Blackwall Ingress Controller
This guide is retained for customers who operate an existing Blackwall Ingress Controller deployment. Blackwall Ingress Controller is no longer actively developed or maintained. Do not use this guide to create a new production deployment.
Blackwall Ingress Controller is no longer maintained
Blackwall Ingress Controller is no longer actively developed or maintained and is retained only to support existing deployments.
The upstream Ingress NGINX project was retired on 24 March 2026. There will be no further upstream releases, bug fixes, or security updates. Although existing deployments and installation artifacts remain available, you should not use this controller for new production deployments.Existing users should assess the security risks of continuing to use the controller and plan migration to a supported alternative.
About Blackwall Ingress Controller
Blackwall Ingress Controller is a fork of the now-retired Ingress NGINX controller for Kubernetes. It was designed to maintain compatibility with Ingress NGINX while providing additional Blackwall protection options. The main differences from the original Ingress NGINX controller are:
- ModSecurity is disabled due to the possible Blackwall 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 Blackwall module for Nginx
- There are additional configuration options to enable Blackwall 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 Blackwall servers assigned to your domain in the Blackwall 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):
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:
To avoid extra typing, create an alias for kubectl in the shell config.
As a result, the kubectl get nodes should display:
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.x.x.x <none> 443/TCP 2m19s
kube-system service/kube-dns ClusterIP 10.x.x.x <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.x.x.x <none> 443/TCP 40m
service/meow-svc ClusterIP 10.x.x.x <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
Blackwall Ingress Controller
To install Blackwall 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 Blackwall Ingress Controller. Replace "xxx.botguard.net" and "yyy.botguard.net" with values provided by Blackwall 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:
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 that the Blackwall protection worked as expected:
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