Skip to content

BotGuard Ingress Controller

This guide describes how to deploy a virtual machine instance with a minimal Kubernetes setup, web application, BotGuard ingress controller, and certificate manager.

As a result, you will get a botguard-protected website running in the Kubernetes environment.

About BotGuard Ingress Controller

The BotGuard Ingress Controller is a fork of the ingress-nginx, the popular ingress controller for Kubernetes. It fully compatible with ingress-nginx, but provides additional options. Moreover, both ingress-nginx and BotGuard Ingress Controller can be used in the same cluster simultaneously.

The differences from ingress-nginx are:

  • ModSecurity is disabled due to the possible BotGuard incompatibility. The configuration options for ModSecurity are ignored when used.
  • It uses patched version of the Nginx
  • It uses patched latest version of the OpenSSL library
  • It contains BotGuard module for Nginx
  • There are additional configuration options to enable BotGuard protection

The additional configuration options are (per cluster):

controller:
  config:
    botguard-primary-server: "xxx.botguard.net"
    botguard-secondary-server: "yyy.botguard.net"
    botguard-skip: "regexp to skip urls"

botguard-primary-server and botguard-secondary-server are BotGuard servers assigned to your domain in the BotGuard dashboard. The optional botguard-skip parameter is the regular expression applied to URL to ignore some patterns when matched. It is used to bypass static files analysis.

If those options are not set, the protection is disabled. To enable the protection per domain, the Ingress annotation is used:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/enable-botguard: "true"

Virtual machine setup

Deploy a virtual machine in the DigitalOcean environment with the following specs:

  • AlmaLinux 8
  • 2 CPU
  • 2 Gb RAM
  • 60 Gb disk
  • Additional reserved IPv4 address

Assign a reserved IPv4 address to the domain (botguard.example for example) with DNS manager (record type A).

As a result, the machine will be accessible by both of IPv4 addresses (primary and reserved):

ssh root@botguard.example

Minikube setup

Minikube provides a minimal implementation of the Kubernetes environment. It supports different implementations ("drivers"). We will use podman (RedHat's Docker replacement).

Install required packages:

yum update
yum install podman podman-docker conntrack-tools
rpm -ihv https://storage.googleapis.com/minikube/releases/latest/minikube-latest.x86_64.rpm

Deploy Kubernetes single-node cluster:

minikube start --driver=podman --force

To avoid extra typing, create an alias for kubectl in the shell config.

echo 'alias kubectl="minikube kubectl --"' >> ~/.bashrc
source ~/.bashrc

As a result, the kubectl get nodes should display:

NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   84s   v1.27.4
and kubectl get all --all-namespaces should display:
NAMESPACE     NAME                                   READY   STATUS    RESTARTS      AGE
kube-system   pod/coredns-5d78c9869d-hz5fc           1/1     Running   0             2m5s
kube-system   pod/etcd-minikube                      1/1     Running   0             2m16s
kube-system   pod/kube-apiserver-minikube            1/1     Running   0             2m16s
kube-system   pod/kube-controller-manager-minikube   1/1     Running   0             2m16s
kube-system   pod/kube-proxy-mdq6w                   1/1     Running   0             2m5s
kube-system   pod/kube-scheduler-minikube            1/1     Running   0             2m16s
kube-system   pod/storage-provisioner                1/1     Running   1 (94s ago)   2m14s

NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP                  2m19s
kube-system   service/kube-dns     ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   2m17s

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system   daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   2m17s

NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/coredns   1/1     1            1           2m17s

NAMESPACE     NAME                                 DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/coredns-5d78c9869d   1         1         1       2m5s

Web application

We will deploy a sample web application, that just shows the original request for demo purposes. It contains a Kubernetes deployment and a service:

echo "
apiVersion: apps/v1
kind: Deployment
metadata:
  name: meow
spec:
  replicas: 1
  selector:
    matchLabels:
      app: meow
  template:
    metadata:
      labels:
        app: meow
    spec:
      containers:
      - name: meow
        image: gcr.io/kubernetes-e2e-test-images/echoserver:2.1
        ports:
        - containerPort: 8080
" | kubectl apply -f -
echo "
apiVersion: v1
kind: Service
metadata:
  name: meow-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: meow
" | kubectl apply -f -

As a result of the application deployment, the kubectl get all should display:

NAME                        READY   STATUS    RESTARTS   AGE
pod/meow-75d6b9f5c8-kpw8d   1/1     Running   0          25s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP   40m
service/meow-svc     ClusterIP   10.110.23.94   <none>        80/TCP    7s

NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/meow   1/1     1            1           25s

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.apps/meow-75d6b9f5c8   1         1         1       25s

BotGuard Ingress Controller

To install BotGuard Ingress Controller, first we need to install Helm package manager:

curl https://get.helm.sh/helm-v3.12.3-linux-amd64.tar.gz | tar -zx
mv linux-amd64/helm /usr/local/bin/
rm -rf linux-amd64

Then add the Helm repositories:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add botguard https://repo.botguard.net/modules/ingress-nginx
helm repo add jetstack https://charts.jetstack.io
helm repo update

And finally install BotGuard Ingress Controller. Replace "xxx.botguard.net" and "yyy.botguard.net" with values provided by BotGuard dashboard for the registered domain (botguard.example).

helm install botguard botguard/ingress-nginx \
  --set controller.config.botguard-primary-server=xxx.botguard.net \
  --set controller.config.botguard-secondary-server=yyy.botguard.net \
  --set controller.service.type=NodePort \
  --set controller.service.nodePorts.http=32080 \
  --set controller.service.nodePorts.https=32443 \
  --set controller.service.externalTrafficPolicy=Local

To enable external access to the ingress controller, add the iptables rules. Replace 10.19.0.11 with the external private ip address ("anchor ip address") of the machine assigned by DigitalOcean:

iptables -t nat -I PREROUTING 1 -d 10.19.0.11 -p tcp -m tcp --dport 80 -j DNAT --to-destination `minikube ip`:32080
iptables -t nat -I PREROUTING 2 -d 10.19.0.11 -p tcp -m tcp --dport 443 -j DNAT --to-destination `minikube ip`:32443

At this point, navigating to the http://botguard.example should display standard 404 Not Found page.

Certificate Manager

Install cert-manager with the Helm:

helm install cert-manager jetstack/cert-manager \
  --version v1.12.3 \
  --set installCRDs=true

And then apply the ACME certificate issuer to obtain a certificate from LetsEncrypt (replace email address with your one):

echo "
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    email: meow@botguard.example
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
        ingress:
          ingressClassName: botguard
" | kubectl apply -f -

Protected Ingress

To issue the certificate and provide access to the web application, create Kubernetes ingress:

echo "
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/enable-botguard: "true"
  name: meow-ingress
spec:
  ingressClassName: botguard
  tls:
    - hosts:
        - botguard.example
      secretName: meow-tls
  rules:
    - host: botguard.example
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: meow-svc
                port:
                  number: 80
" | kubectl apply -f -

In a couple of minutes, navigating to the http://botguard.example and https://botguard.example should display web application page.

Final tests

Check the BotGuard protection is worked as expected:

curl -v -H 'BotGuard-Ping: request' https://botguard.example

should display:

> GET / HTTP/2
> Host: botguard.example
> User-Agent: curl/8.1.2
> Accept: */*
> BotGuard-Ping: request
>
< HTTP/2 403
< date: Thu, 17 Aug 2023 19:11:34 GMT
< content-type: text/html
< botguard-pong: response