Kubernetes with L3 mitigation
This page describes how to install F5 DOS for NGINX using Kubernetes.
It explains the common steps necessary for any Kubernetes-based deployment, then provides details specific to Helm or Manifests.
To complete this guide, you will need the following pre-requisites:
- A functional Kubernetes cluster
- An active F5 DOS for NGINX subscription (Purchased or trial)
- Docker
To review supported operating systems, read the Releases topic.
Download the SSL certificate, private key, and the JWT license file associated with your NGINX Plus subscription from the MyF5 Customer Portal:
- Log in to MyF5.
- Go to My Products & Plans > Subscriptions to see your active subscriptions.
- Find your NGINX products or services subscription, and select the Subscription ID for details.
- Download the nginx-repo.crt and nginx-repo.key from the subscription page.
- Download the JSON Web Token (JWT) from the subscription page.
Starting from NGINX Plus Release 33, a JWT file is required for each NGINX Plus instance. For more information, see About Subscription Licenses.
In the same folder as your credential files, create a Dockerfile based on your desired operating system image using an example from the following sections.
# syntax=docker/dockerfile:1
# Supported OS_VER's are 3.21/3.22
ARG OS_VER="3.22"
# Base image
FROM alpine:${OS_VER}
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/apk/cert.pem,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/apk/cert.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
wget -O /etc/apk/keys/nginx_signing.rsa.pub https://cs.nginx.com/static/keys/nginx_signing.rsa.pub \
&& printf "https://pkgs.nginx.com/plus/alpine/v`egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release`/main\n" | tee -a /etc/apk/repositories \
&& printf "https://pkgs.nginx.com/app-protect-dos/alpine/v`egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release`/main\n" | tee -a /etc/apk/repositories \
&& apk update \
&& apk add app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
&& rm -rf /var/cache/apk/*
RUN --mount=type=secret,id=nginx_license_secret \
sh -c 'cat /run/secrets/nginx_license_secret | base64 -d > /etc/nginx/license.jwt' && \
chmod 600 /etc/nginx/license.jwt
RUN nginx -v && admd -v
# Copy configuration files:
COPY nginx.conf custom_log_format.json /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# syntax=docker/dockerfile:1
# Supported OS_VER's are 3.21/3.22
ARG OS_VER="3.22"
# Base image
FROM alpine:${OS_VER}
# Install F5 DoS ebpf manager for NGINX and create required nginx user
RUN --mount=type=secret,id=nginx-crt,dst=/etc/apk/cert.pem,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/apk/cert.key,mode=0644 \
wget -O /etc/apk/keys/nginx_signing.rsa.pub https://cs.nginx.com/static/keys/nginx_signing.rsa.pub \
&& printf "https://pkgs.nginx.com/app-protect-dos/alpine/v`egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release`/main\n" | tee -a /etc/apk/repositories \
&& apk update \
&& apk add app-protect-dos-ebpf-manager \
&& rm -rf /var/cache/apk/* \
&& adduser -D -S -H -s /sbin/nologin nginx
RUN ebpf_manager_dos -v
STOPSIGNAL SIGQUIT
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]# For AmazonLinux 2023:
FROM amazonlinux:2023
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
dnf -y install ca-certificates \
&& curl -o /etc/yum.repos.d/plus-amazonlinux2023.repo https://cs.nginx.com/static/files/plus-amazonlinux2023.repo \
&& curl -o /etc/yum.repos.d/app-protect-dos-amazonlinux2023.repo https://cs.nginx.com/static/files/app-protect-dos-amazonlinux2023.repo \
&& dnf install -y app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& dnf clean all \
&& rm -rf /var/cache/dnf \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
RUN --mount=type=secret,id=nginx_license_secret \
sh -c 'cat /run/secrets/nginx_license_secret | base64 -d > /etc/nginx/license.jwt' && \
chmod 600 /etc/nginx/license.jwt
RUN nginx -v && admd -v
# Copy configuration files:
COPY nginx.conf custom_log_format.json /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# For AmazonLinux 2023:
FROM amazonlinux:2023
# Install F5 DoS ebpf manager for NGINX and create required nginx user
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
dnf -y install ca-certificates shadow-utils \
&& curl -o /etc/yum.repos.d/app-protect-dos-amazonlinux2023.repo https://cs.nginx.com/static/files/app-protect-dos-amazonlinux2023.repo \
&& dnf install -y app-protect-dos-ebpf-manager \
&& dnf clean all \
&& rm -rf /var/cache/dnf \
&& useradd -r -s /usr/sbin/nologin nginx
RUN ebpf_manager_dos -v
STOPSIGNAL SIGQUIT
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]# Where can be bullseye/bookworm
FROM debian:bullseye
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
apt-get update \
&& apt-get install -y --no-install-recommends \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
debian-archive-keyring \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key \
| gpg --dearmor \
| tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/plus/debian $(lsb_release -cs) nginx-plus\n" > /etc/apt/sources.list.d/nginx-plus.list \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/app-protect-dos/debian $(lsb_release -cs) nginx-plus" \
> /etc/apt/sources.list.d/nginx-app-protect-dos.list \
&& wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
RUN --mount=type=secret,id=nginx_license_secret \
sh -c 'cat /run/secrets/nginx_license_secret | base64 -d > /etc/nginx/license.jwt' && \
chmod 600 /etc/nginx/license.jwt
RUN nginx -v && admd -v
COPY nginx.conf /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# Where can be bullseye/bookworm
FROM debian:bullseye
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
apt-get update \
&& apt-get install -y --no-install-recommends \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
debian-archive-keyring \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key \
| gpg --dearmor \
| tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/app-protect-dos/debian $(lsb_release -cs) nginx-plus" \
> /etc/apt/sources.list.d/nginx-app-protect-dos.list \
&& wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y app-protect-dos-ebpf-manager \
&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/*
RUN ebpf_manager_dos -v
STOPSIGNAL SIGQUIT
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]# For UBI 8
FROM registry.access.redhat.com/ubi8
ARG RHEL_ORG
ARG RHEL_ACTIVATION_KEY
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
subscription-manager register --org=${RHEL_ORG} --activationkey=${RHEL_ACTIVATION_KEY} \
&& subscription-manager refresh \
&& subscription-manager repos --enable=rhel-8-for-x86_64-baseos-rpms \
&& subscription-manager repos --enable=rhel-8-for-x86_64-appstream-rpms \
&& dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \
&& dnf -y install ca-certificates \
&& curl -o /etc/yum.repos.d/plus-8.repo https://cs.nginx.com/static/files/plus-8.repo \
&& curl -o /etc/yum.repos.d/app-protect-dos-8.repo https://cs.nginx.com/static/files/app-protect-dos-8.repo \
&& dnf -y install app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& rm /etc/yum.repos.d/plus-8.repo \
&& rm /etc/yum.repos.d/app-protect-dos-8.repo \
&& dnf clean all \
&& rm -rf /var/cache/yum \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
&& subscription-manager unregister
# Copy configuration files:
RUN --mount=type=secret,id=nginx_license_secret \
sh -c 'cat /run/secrets/nginx_license_secret | base64 -d > /etc/nginx/license.jwt' && \
chmod 600 /etc/nginx/license.jwt
RUN nginx -v && admd -v
COPY nginx.conf custom_log_format.json /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# For UBI 8
FROM registry.access.redhat.com/ubi8
ARG RHEL_ORG
ARG RHEL_ACTIVATION_KEY
# Install F5 DoS ebpf manager for NGINX and create required nginx user
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \
&& dnf -y install ca-certificates \
&& curl -o /etc/yum.repos.d/app-protect-dos-8.repo https://cs.nginx.com/static/files/app-protect-dos-8.repo \
&& dnf -y install app-protect-dos-ebpf-manager \
&& rm /etc/yum.repos.d/app-protect-dos-8.repo \
&& dnf clean all \
&& rm -rf /var/cache/yum \
&& useradd -r -s /usr/sbin/nologin nginx
RUN ebpf_manager_dos -v
STOPSIGNAL SIGQUIT
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]# For UBI 9
FROM registry.access.redhat.com/ubi9
ARG RHEL_ORG
ARG RHEL_ACTIVATION_KEY
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
subscription-manager register --org=${RHEL_ORG} --activationkey=${RHEL_ACTIVATION_KEY} \
&& subscription-manager refresh \
&& subscription-manager repos --enable=rhel-9-for-x86_64-baseos-rpms \
&& subscription-manager repos --enable=rhel-9-for-x86_64-appstream-rpms \
&& dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm \
&& dnf -y install ca-certificates \
&& curl -o /etc/yum.repos.d/plus-9.repo https://cs.nginx.com/static/files/plus-9.repo \
&& curl -o /etc/yum.repos.d/app-protect-dos-9.repo https://cs.nginx.com/static/files/app-protect-dos-9.repo \
&& dnf -y install app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& rm /etc/yum.repos.d/plus-9.repo \
&& rm /etc/yum.repos.d/app-protect-dos-9.repo \
&& dnf clean all \
&& rm -rf /var/cache/yum \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
&& subscription-manager unregister
# Copy configuration files:
COPY nginx.conf custom_log_format.json /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# For UBI 9
FROM registry.access.redhat.com/ubi9
ARG RHEL_ORG
ARG RHEL_ACTIVATION_KEY
# Install F5 DoS ebpf manager for NGINX and create required nginx user
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm \
&& dnf -y install ca-certificates \
&& curl -o /etc/yum.repos.d/app-protect-dos-9.repo https://cs.nginx.com/static/files/app-protect-dos-9.repo \
&& dnf -y install app-protect-dos-ebpf-manager \
&& rm /etc/yum.repos.d/app-protect-dos-9.repo \
&& dnf clean all \
&& rm -rf /var/cache/yum \
&& useradd -r -s /usr/sbin/nologin nginx
RUN ebpf_manager_dos -v
STOPSIGNAL SIGQUIT
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]# syntax=docker/dockerfile:1
# For Rocky Linux 9
FROM rockylinux:9
# Install F5 DoS for NGINX:
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
dnf -y install ca-certificates epel-release 'dnf-command(config-manager)' \
&& curl -o /etc/yum.repos.d/plus-9.repo https://cs.nginx.com/static/files/plus-9.repo \
&& curl -o /etc/yum.repos.d/app-protect-dos-9.repo https://cs.nginx.com/static/files/app-protect-dos-9.repo \
&& dnf config-manager --set-enabled crb \
&& dnf install -y app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& dnf clean all \
&& rm -rf /var/cache/dnf \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
# Copy configuration files:
COPY nginx.conf custom_log_format.json /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# syntax=docker/dockerfile:1
# For Rocky Linux 9
FROM rockylinux:9
# Install F5 DoS ebpf manager for NGINX and create required nginx user
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
dnf -y install ca-certificates epel-release 'dnf-command(config-manager)' \
&& curl -o /etc/yum.repos.d/app-protect-dos-9.repo https://cs.nginx.com/static/files/app-protect-dos-9.repo \
&& dnf config-manager --set-enabled crb \
&& dnf install -y app-protect-dos-ebpf-manager \
&& dnf clean all \
&& rm -rf /var/cache/dnf \
&& useradd -r -s /usr/sbin/nologin nginx
RUN ebpf_manager_dos -v
STOPSIGNAL SIGQUIT
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]# syntax=docker/dockerfile:1
# For Ubuntu
# Where version can be: jammy/noble
FROM ubuntu:noble
# Install F5 DoS for NGINX
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
--mount=type=secret,id=license-jwt,dst=license.jwt,mode=0644 \
apt-get update \
&& apt-get install -y --no-install-recommends \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
ubuntu-keyring \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key \
| gpg --dearmor \
| tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/plus/ubuntu $(lsb_release -cs) nginx-plus\n" > /etc/apt/sources.list.d/nginx-plus.list \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/app-protect-dos/ubuntu $(lsb_release -cs) nginx-plus" \
> /etc/apt/sources.list.d/nginx-app-protect-dos.list \
&& wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y app-protect-dos \
&& cat license.jwt > /etc/nginx/license.jwt \
&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
RUN --mount=type=secret,id=nginx_license_secret \
sh -c 'cat /run/secrets/nginx_license_secret | base64 -d > /etc/nginx/license.jwt' && \
chmod 600 /etc/nginx/license.jwt
RUN nginx -v && admd -v
COPY nginx.conf /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["sh", "/root/entrypoint.sh"]# syntax=docker/dockerfile:1
# For Ubuntu
# Where version can be: jammy/noble
FROM ubuntu:noble
# Install F5 DoS ebpf manager for NGINX and create required nginx user
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \
--mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
apt-get update \
&& apt-get install -y --no-install-recommends \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
ubuntu-keyring \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key \
| gpg --dearmor \
| tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/app-protect-dos/ubuntu $(lsb_release -cs) nginx-plus" \
> /etc/apt/sources.list.d/nginx-app-protect-dos.list \
&& wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y app-protect-dos-ebpf-manager \
&& apt-get remove --purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -r -s /usr/sbin/nologin nginx
STOPSIGNAL SIGQUIT
RUN ebpf_manager_dos -v
# Idle forever
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]Docker startup script which spins up all App Protect DoS processes, must have executable permissions
#!/usr/bin/env bash
USER=nginx
LOGDIR=/var/log/adm
# prepare environment
mkdir -p /var/run/adm /tmp/cores ${LOGDIR}
chmod 755 /var/run/adm /tmp/cores ${LOGDIR}
chown ${USER}:${USER} /var/run/adm /tmp/cores ${LOGDIR}
# run processes
/bin/su -s /bin/bash -c "/usr/bin/adminstall > ${LOGDIR}/adminstall.log 2>&1" ${USER}
/bin/su -s /bin/bash -c "/usr/bin/admd -d --log info > ${LOGDIR}/admd.log 2>&1 &" ${USER}
/usr/sbin/nginx -g 'daemon off;'Your folder should contain the following files:
- nginx-repo.crt
- nginx-repo.key
- license.jwt
- entrypoint.sh
- Dockerfile
To build an image, use the following command, replacing <your-nginx-dos-image-name> as appropriate:
sudo docker build --no-cache --platform linux/amd64 \
--secret id=nginx-crt,src=nginx-repo.crt \
--secret id=nginx-key,src=nginx-repo.key \
--secret id=license-jwt,src=license.jwt \
-t <your-nginx-dos-image-name> .Your folder should contain the following files:
- nginx-repo.crt
- nginx-repo.key
- Dockerfile
To build an image, use the following command, replacing <your-ebpf-manager-image-nam> as appropriate:
sudo docker build --no-cache --platform linux/amd64 \
--secret id=nginx-crt,src=nginx-repo.crt \
--secret id=nginx-key,src=nginx-repo.key \
-t <your-ebpf-manager-image-name> .Once you have built the DOS and EBPF images, push them to your private image repository, which should be accessible to your Kubernetes cluster.
From this point, the steps change based on your installation method:
Before you can start the Manifest deployment, you need a Kubernetes secret for the Docker registry.
You can create the secret using kubectl create:
kubectl create secret docker-registry regcred --docker-server=private-registry.nginx.com --docker-username=<JWT Token> --docker-password=noneThe <JWT Token> argument should be the contents of the file, not the file itself. Ensure there are no additional characters such as extra whitespace.
The default configuration provided creates two replicas, each hosting NGINX and DOS services together in a single Kubernetes pod.
Create all of these files in a single folder (Such as /manifests).
In each file, replace
<your-private-registry>/<your-nginx-dos-image-name>:<your-tag> with your actual nginx-dos image tag.
<your-private-registry>/<your-ebpf-manager-image-name>:<your-tag> with your actual ebpf-manager image tag.
apiVersion: v1
kind: Namespace
metadata:
name: app-protect-dosapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-app-protect-dos-shared
namespace: app-protect-dos
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1GiapiVersion: v1
kind: ConfigMap
metadata:
name: dos-nginx-conf
namespace: app-protect-dos
data:
nginx.conf: |
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log error;
worker_rlimit_nofile 65535;
load_module modules/ngx_http_app_protect_dos_module.so;
working_directory /tmp/cores;
events {
worker_connections 65535;
}
http {
app_protect_dos_arb_fqdn svc-appprotect-dos-arb.arb.svc.cluster.local;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
log_format log_dos
', vs_name_al=$app_protect_dos_vs_name, ip=$remote_addr, tls_fp=$app_protect_dos_tls_fp, '
'outcome=$app_protect_dos_outcome, reason=$app_protect_dos_outcome_reason, '
'ip_tls=$remote_addr:$app_protect_dos_tls_fp, ';
app_protect_dos_accelerated_mitigation on syn_drop=on;
upstream backend {
server svc-backend-nginx:8080;
}
# Health endpoints for probes
server {
listen 8090;
location /app_protect_dos_liveness { return 200; }
location /app_protect_dos_readiness { return 200; }
}
server {
listen 80 reuseport;
server_name serv;
access_log /var/log/nginx/access.log log_dos if=$loggable;
app_protect_dos_security_log_enable on;
app_protect_dos_security_log "/etc/app_protect_dos/log-default.json" syslog:server=10.197.30.219:5261;
app_protect_dos_policy_file "/etc/app_protect_dos/BADOSDefaultPolicy.json";
location / {
app_protect_dos_enable on;
app_protect_dos_name "main_serv";
app_protect_dos_monitor uri=http://serv:80/ protocol=http1;
proxy_pass http://backend;
}
}
}apiVersion: v1
kind: ConfigMap
metadata:
name: dos-log-default
namespace: app-protect-dos
data:
log-default.json: |
{
"filter": {
"traffic-mitigation-stats": "all",
"bad-actors": "all",
"attack-signatures": "all"
}
}apiVersion: apps/v1
kind: Deployment
metadata:
name: app-protect-dos
namespace: app-protect-dos
labels:
app: app-protect-dos
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: app-protect-dos
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 1
template:
metadata:
labels:
app: app-protect-dos
spec:
containers:
- name: nginx-app-protect-dos
image: <your-private-registry>/<your-nginx-app-protect-dos-image-name>:<your-tag>
imagePullPolicy: Always
command: ["/bin/bash", "-c"]
args:
- |
/root/entrypoint.sh
resources:
requests:
cpu: "200m"
memory: "500Mi"
limits:
cpu: "900m"
memory: "800Mi"
ports:
- containerPort: 80
name: web
- containerPort: 8090
name: probe
- containerPort: 8091
name: probe500
livenessProbe:
httpGet:
path: /app_protect_dos_liveness
port: 8090
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /app_protect_dos_readiness
port: 8090
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: shared-dir
mountPath: /shared/
- name: bpf
mountPath: /sys/fs/bpf
- name: conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: log-default
mountPath: /etc/app_protect_dos/log-default.json
subPath: log-default.json
- name: dos-ebpf-manager
image: <your-private-registry>/<your-ebpf-manager-image-name>:<your-tag>
securityContext:
privileged: true
volumeMounts:
- name: shared-dir
mountPath: /shared
- name: bpf
mountPath: /sys/fs/bpf
volumes:
- name: shared-dir
persistentVolumeClaim:
claimName: pvc-app-protect-dos-shared
- name: bpf
hostPath:
path: /sys/fs/bpf
type: DirectoryOrCreate
- name: conf
configMap:
name: dos-nginx-conf
items:
- key: nginx.conf
path: nginx.conf
- name: log-default
configMap:
name: dos-log-default
defaultMode: 0644
items:
- key: log-default.json
path: log-default.jsonapiVersion: v1
kind: Service
metadata:
name: nap-dos
namespace: app-protect-dos
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: app-protect-dosFrom the folder containing the YAML files from the previous step (Suggested as /manifests), deploy F5 DOS for NGINX using kubectl:
kubectl apply -f manifests/dos-namespace.yaml
kubectl apply -f manifests/dos-storage.yaml
kubectl apply -f manifests/dos-nginx-conf-configmap.yam
kubectl apply -f manifests/dos-log-default-configmap.yaml
kubectl apply -f manifests/dos-deployment.yaml
kubectl apply -f manifests/dos-service.yamlIt will apply all the configuration defined in the files to your Kubernetes cluster.
You can then check the status of the deployment with kubectl get:
kubectl -n app-protect-dos get deployments
kubectl -n app-protect-dos get pods
kubectl -n app-protect-dos get servicesYou should see output similar to the following:
~$ kubectl -n app-protect-dos get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
app-protect-dos 1/1 1 1 33s
~$ kubectl -n app-protect-dos get pods
NAME READY STATUS RESTARTS AGE
app-protect-dos-7f9798654c-7ncbl 2/2 Running 0 68s
$ kubectl -n app-protect-dos get pods -o jsonpath='{range .items[*]}Pod: {.metadata.name} -> Containers: {.spec.containers[*].name}{"\n"}{end}'
Pod: app-protect-dos-7f9798654c-7ncbl -> Containers: dos-ebpf-manager nginx-app-protect-dos
~$ kubectl -n app-protect-dos get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nap-dos LoadBalancer 10.43.212.232 <pending> 80:32586/TCP 93sAt this stage, you have finished deploying F5 DOS for NGINX with EBPF L3 mitigation enabled You can login to dos-ebpf-manager container like following command
kubectl exec -it app-protect-dos-586fb94947-8sjnc -n app-protect-dos -c nginx-app-protect-dos -- bash
kubectl exec -it app-protect-dos-586fb94947-8sjnc -n app-protect-dos -c dos-ebpf-manager -- bashand can look at .
You can run the following commands to ensure that F5 DoS for NGINX enforcement is operational.
-
Check that the three processes needed for F5 DoS for NGINX are running using
ps aux:- admd
- nginx: master process
- nginx: worker process
shell USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND nginx 7759 0.0 0.0 113120 1200 ? Ss Sep06 0:00 /bin/sh -c /usr/bin/admd -d --log info > /var/log/adm/admd.log 2>&1 root 7765 0.0 0.0 87964 1464 ? Ss Sep06 0:00 nginx: master process /usr/sbin/nginx -g daemon off; nginx 7767 0.0 0.1 615868 8188 ? Sl Sep06 0:04 nginx: worker process -
Verify that there are no NGINX errors in the
/var/log/nginx/error.logand that the policy compiled successfully:shell 2020/09/07 15:33:44 [notice] 9307#9307: using the "epoll" event method 2020/09/07 15:33:44 [notice] 9307#9307: nginx/1.19.0 (nginx-plus-r22) 2020/09/07 15:33:44 [notice] 9307#9307: built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) 2020/09/07 15:33:44 [notice] 9307#9307: OS: Linux 3.10.0-327.28.3.el7.x86_64 2020/09/07 15:33:44 [notice] 9307#9307: getrlimit(RLIMIT_NOFILE): 1024:4096 2020/09/07 15:33:44 [notice] 9310#9310: start worker processes 2020/09/07 15:33:44 [notice] 9310#9310: start worker process 9311 PID <9311>, WORKER <0>, Function adm_ngx_init_process, line 684, version: 22+1.19.4-1.el7.ngx -
Check that by applying an attack, the attacker IP addresses are blocked while the good traffic pass through:
a. Simulate good traffic:
shell echo "Start Good Traffic 2" while true; do curl ${VS}/good1 & curl ${VS}/good2 & curl ${VS}/good3 & curl ${VS}/good4 sleep 0.1 done &b. After 7 minutes start the attack:
shell while [ true ] do ab -B ${BAD_IP1} -l -r -n 1000000 -c 150 -d -H "Host: evil.net" -H "Pragma: no-cache" -H "Cache-Control: no-cache" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: WireXBot" -H "x-requested-with:" -H "Referer: http://10.0.2.1/none.html" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: en-US" http://${VS}/ & ab -B ${BAD_IP2} -l -r -n 1000000 -c 150 -d -H "Host: evil.net" -H "Pragma: no-cache" -H "Cache-Control: no-cache" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: WireXBot" -H "x-requested-with:" -H "Referer: http://10.0.2.1/none.html" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: en-US" http://${VS}/ & ab -B ${BAD_IP3} -l -r -n 1000000 -c 150 -d -s 10 -H "Host: evil.net" -H "Pragma: no-cache" -H "Cache-Control: no-cache" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" -H "Upgrade-Insecure-Requests: 1" -H "User-Agent: WireXBot" -H "x-requested-with:" -H "Referer: http://10.0.2.1/none.html" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: en-US" http://${VS}/ killall ab donec. See that the good traffic continue as usual while the attackers receive denial of service.
-
For DOS with L3 mitigation enabled
Check that the ebpf_manager_dos process needed for F5 DoS for NGINX is running using ps aux | grep /usr/bin/ebpf_manager_dos:
root 1 0.0 0.0 4324 3072 ? Ss 19:32 0:00 bash -c /usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log
root 7 0.2 0.0 1722732 14208 ? Sl 19:32 0:01 /usr/bin/ebpf_manager_dos
root 46 0.0 0.0 3528 1792 pts/0 S+ 19:44 0:00 grep --color=auto /usr/bin/ebpf_manager_dosVerify that there are no errors in the /shared/ebpf_dos.log and that the XDP program uploaded successfully:
[2025-12-02 19:32:13] INFO: Install eBPF maps and XDP program
[2025-12-02 19:32:13] INFO: Start ebpf manager
[2025-12-02 19:32:13] INFO: Version: 36+4.8.3-1~noble
[2025-12-02 19:32:13] INFO: Start Periodic task for update time
[2025-12-02 19:32:13] INFO: Owner of the UDS has been changed to user nginx and group nginx.
[2025-12-02 19:32:13] INFO: Permissions of the UDS have been changed successfully for user nginx and group nginx.
[2025-12-02 19:32:13] INFO: Async Callback Server listening on unix:/shared/ebpf_manager_dos_udsTo check F5 WAF for NGINX alongside F5 DoS for NGINX, just perform the normal tests as specified at Admin Guide
F5 DoS for NGINX arbitrator orchestrates all the running F5 DoS for NGINX instances to synchronize local/global attack start/stop.
F5 DoS for NGINX arbitrator serves as a central coordinating component for managing multiple instances of App Protect DoS in a network. It is needed when there are more than one F5 DoS for NGINX instances. Its primary function is to ensure that all instances are aware of and share the same state for each protected object. Here’s a clearer breakdown of how it works and why it’s necessary:
How F5 DoS for NGINX Arbitrator Works:
- Collecting State Periodically: The arbitrator regularly collects the state information from all running instances of App Protect DoS. This collection occurs at set intervals, typically every 10 seconds.
- State Initialization for New Instances: When a new App Protect DoS instance is created, it doesn’t start with a blank or uninitialized state for a protected object. Instead, it retrieves the initial state for the protected object from the arbitrator.
- Updating State in Case of an Attack: If an attack is detected by one of the App Protect DoS instances, that instance sends an attack notification to the arbitrator. The arbitrator then updates the state of the affected protected object to indicate that it is under attack. Importantly, this updated state is propagated to all other instances.
F5 DoS for NGINX Arbitrator is essential for several reasons:
- Global State Management: Without the arbitrator, each individual instance of App Protect DoS would manage its own isolated state for each protected object. This isolation could lead to inconsistencies. For example, if instance A declared an attack on a protected object named "PO-Example," instance B would remain unaware of this attack, potentially leaving the object vulnerable.
- Uniform Attack Detection: With the arbitrator in place, when instance A detects an attack on "PO-Example" and reports it to the arbitrator, the state of "PO-Example" is immediately updated to indicate an attack. This means that all instances, including instance B, are aware of the attack and can take appropriate measures to mitigate it.
In summary, F5 DoS for NGINX Arbitrator acts as a central coordinator to maintain a consistent and up-to-date global state for protected objects across multiple instances of App Protect DoS. This coordination helps ensure that attacks are properly detected and mitigated, and that knowledge gained by one instance is efficiently shared with others, enhancing the overall security of the network.
-
Pull the official F5 DoS for NGINX Arbitrator image with the command:
docker pull docker-registry.nginx.com/nap-dos/app_protect_dos_arb:latest -
Create a container based on this image, for example,
app-protect-dos-arbcontainer:docker run --name app_protect_dos_arb -p 3000:3000 -d docker-registry.nginx.com/nap-dos/app_protect_dos_arb -
Verify that the
app-protect-dos-arbcontainer is up and running with thedocker pscommand. -
DNS records are required for F5 DoS for NGINX Arbitrator to work properly and be accessible by F5 DoS for NGINX servers. Ensure that the
svc-appprotect-dos-arbor configured Arbitrator FQDN (withapp_protect_dos_arb_fqdndirective) has a valid DNS resolution. This step is necessary only for VM/Docker deployments with arbitrator. When the arbitrator is in the same Kubernetes namespace as F5 DoS for NGINX, this step is not needed.
The Arbitrator service is standalone. Once it is down, it can be seamlessly re-started. It will immediately recover all the needed information from F5 DoS for NGINX instances that communicate to it every 10 sec. It’s downtime is around 10-20 seconds which will not affect the F5 DoS for NGINX working.
F5 DoS for NGINX Arbitrator service connects to port 3000 and can be seen under App Protect DoS instances. All modules try to connect to this service automatically. If it’s not accessible, each instance works in standalone mode.
There is no such option for authentications between F5 DoS for NGINX servers and Arbitrator service like MTLS or password . Currently Arbitrator service is not exposed outside of the namespace. It is customers responsibility to isolate it from outside. It is applicable to any deployment of Arbitrator, not only to multi-VM.