Kubernetes with L4 accelerated mitigation

This guide explains how to install F5 DoS for NGINX on Kubernetes with L4 accelerated mitigation. By enabling the app_protect_dos_accelerated_mitigation directive and running the DoS eBPF (Extended Berkeley Packet Filter) Manager as a sidecar container alongside the NGINX container, you can offload Layer 4 DoS mitigation to eBPF programs in the Linux kernel. This improves mitigation performance and reduces CPU usage on the NGINX container.

Deployments with L4 accelerated mitigation require the NGINX and DoS containers to run with elevated privileges and additional Linux capabilities. This guide assumes you have a good understanding of Kubernetes security best practices and have secured your cluster accordingly.

F5 DoS for NGINX requires the service to run with externalTrafficPolicy set to Local to preserve the client source IP address for accurate DoS mitigation:

yaml
spec:
  externalTrafficPolicy: Local

It covers the common steps for any Kubernetes-based deployment, then provides details specific to Helm or manifests.

Before you begin

Before you start, make sure you have:

  • 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 your subscription credentials

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.

Create a Dockerfile

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.

Alpine Linux

dockerfile
# 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/*

# 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"]
dockerfile
# 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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && addgroup -S -g 101 nginx \
    && adduser -S -u 101 -G nginx -h /nonexistent -s /sbin/nologin nginx \
    && 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/*

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

Amazon Linux

dockerfile

# 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 

# 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"]
dockerfile
# 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 \
     set -x \
     && dnf -y install ca-certificates shadow-utils \
     && groupadd --system --gid 101 nginx \
     && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
     && 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

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

Debian

dockerfile

# Where can be bullseye/bookworm/trixie
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 \
    && DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends apt-transport-https lsb-release ca-certificates wget gnupg2 debian-archive-keyring \
    && mkdir -p /etc/ssl/nginx/ /etc/nginx/ \
    && wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
    && printf "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 \
    && printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/app-protect-dos/debian $(lsb_release -cs) nginx-plus\n" > /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 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

COPY nginx.conf /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh

EXPOSE 80

STOPSIGNAL SIGQUIT

CMD ["sh", "/root/entrypoint.sh"]
dockerfile
# 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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && groupadd --system --gid 101 nginx \
    && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
    && DEBIAN_FRONTEND=noninteractive apt-get update \
    && DEBIAN_FRONTEND=noninteractive 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 clean \
    && rm -rf /var/lib/apt/lists/*

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

RHEL 8

dockerfile
# 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 attach --auto || true \
    && 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

# 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"]
dockerfile
# 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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && groupadd --system --gid 101 nginx \
    && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
    && 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

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

RHEL 9

dockerfile
# 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 attach --auto || true \
    && 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

# 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"]
dockerfile
# 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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && groupadd --system --gid 101 nginx \
    && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
    && 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

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

RHEL 10

dockerfile
# For UBI 10
FROM registry.access.redhat.com/ubi10

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 attach --auto || true \
    && subscription-manager repos --enable=rhel-10-for-x86_64-baseos-rpms \
    && subscription-manager repos --enable=rhel-10-for-x86_64-appstream-rpms \
    && dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm \
    && dnf -y install ca-certificates \
    && curl -o /etc/yum.repos.d/plus-10.repo https://cs.nginx.com/static/files/plus-10.repo \
    && curl -o /etc/yum.repos.d/app-protect-dos-10.repo https://cs.nginx.com/static/files/app-protect-dos-10.repo \
    && dnf -y install app-protect-dos \
    && cat license.jwt > /etc/nginx/license.jwt \
    && rm /etc/yum.repos.d/plus-10.repo \
    && rm /etc/yum.repos.d/app-protect-dos-10.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

# 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"]
dockerfile
# For UBI 10
FROM registry.access.redhat.com/ubi10

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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && groupadd --system --gid 101 nginx \
    && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
    && dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm \
    && dnf -y install ca-certificates \
    && curl -o /etc/yum.repos.d/app-protect-dos-10.repo https://cs.nginx.com/static/files/app-protect-dos-10.repo \
    && dnf -y install app-protect-dos-ebpf-manager \
    && rm /etc/yum.repos.d/app-protect-dos-10.repo \
    && dnf clean all \
    && rm -rf /var/cache/yum

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

Rocky Linux 9

dockerfile

# 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"]
dockerfile
# 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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && groupadd --system --gid 101 nginx \
    && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
    && 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

STOPSIGNAL SIGQUIT

CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

Ubuntu

dockerfile

# syntax=docker/dockerfile:1
# For Ubuntu 

# Where version can be: jammy/noble
FROM ubuntu:noble

# Setup repository keys
RUN apt-get update && \

# 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 \
    && printf "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 \
    && printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://pkgs.nginx.com/app-protect-dos/ubuntu $(lsb_release -cs) nginx-plus\n" > /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 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

COPY nginx.conf /etc/nginx/
COPY entrypoint.sh /root/
RUN chmod +x /root/entrypoint.sh

EXPOSE 80

STOPSIGNAL SIGQUIT

CMD ["sh", "/root/entrypoint.sh"]
dockerfile
# 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 \
    set -x \
    # Create nginx user/group first, to be consistent throughout Docker variants \
    && groupadd --system --gid 101 nginx \
    && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
    && DEBIAN_FRONTEND=noninteractive apt-get update \
    && DEBIAN_FRONTEND=noninteractive 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 clean \
    && rm -rf /var/lib/apt/lists/*

STOPSIGNAL SIGQUIT

# Idle forever
CMD ["bash", "-c", "/usr/bin/ebpf_manager_dos 2>&1 | tee /shared/ebpf_dos.log"]

Create DOS entrypoint.sh

Docker startup script which spins up all App Protect DoS processes, must have executable permissions

shell
    #!/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;'

Build the DOS Docker image

Your folder should contain the following files:

  • nginx-repo.crt
  • nginx-repo.key
  • license.jwt
  • entrypoint.sh
  • nginx.conf
  • Dockerfile

To build an image, use the following command, replacing <your-nginx-dos-image-name> as appropriate:

shell
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-nginx-dos-image-name> .

Build the EBPF Manager Docker image

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-name> as appropriate:

shell
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:

Use Helm to install F5 DOS for NGINX

You will need to edit the values.yaml file for a few changes:

  • Update appprotectdos.nginxImage.repository and appprotectdos.nginxImage.tag with the image name chosen during when building the Docker image.

The <JWT Token> argument should be the contents of the file, not the file itself. Ensure there are no additional characters such as extra whitespace.

On helm deployment environment variables need to be set for image repository and tag. set enviorment variable DOS_IMAGE_REPOSITORY with your actual nginx-dos image anmae. set enviorment variable DOS_IMAGE_TAG with your actual nginx-dos image tag. set enviorment variable EBPF_IMAGE_REPOSITORY with your actual ebpf-manager image name. set enviorment variable EBPF_IMAGE_TAG with your actual ebpf-manager image tag.

Once you have updated values.yaml, you can install F5 WAF for NGINX using helm install:

shell
export DOS_IMAGE_REPOSITORY=<your-nginx-dos-image-name>
export DOS_IMAGE_TAG=<your-nginx-dos-image-tag>
export EBPF_IMAGE_REPOSITORY=<your-ebpf-manager-image-name>
export EBPF_IMAGE_TAG=<your-ebpf-manager-image-tag>

kubectl create namespace <namespace> --dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic license-token \ 
        --from-file=license.jwt=${PWD}/license.jwt --type=nginx.com/license --namespace <namespace>

# Install DOS Arbitrator
helm repo add nginx-stable https://helm.nginx.com/stable && helm repo update
helm install dos-arbitrator nginx-stable/nginx-appprotect-dos-arbitrator --namespace <namespace>

# Install DOS with EBPF Manager
# release-version example: 4.8.3
helm pull oci://private-registry.nginx.com/nap-dos/nginx-app-protect-ebpf --version <release-version> --untar
cd nginx-app-protect-dos-ebpf

helm install nginx-app-protect-dos-ebpf --namespace <namespace> \
      --set namespace.create=false --set service.type=NodePort \
      --set appProtectDos.image.repository=${DOS_IMAGE_REPOSITORY} \
      --set appProtectDos.image.tag=${DOS_IMAGE_TAG} \
      --set appProtectDos.ebpfManagerImage.repository=$EBPF_IMAGE_REPOSITORY} \
      --set appProtectDos.ebpfManagerImage.tag=${EBPF_IMAGE_TAG} .
       
kubectl wait --for=condition=available --timeout=300s deployment/app-protect-dos -n <namespace>

You can verify the deployment is successful with kubectl get, replacing namespace accordingly:

shell
kubectl get pods --namespac <namespace>
kubectl get svc --namespac <namespace>
At this stage, you have finished deploying F5 DOS for NGINX and can look at Post-installation checks.

Use Manifests to install F5 DOS for NGINX

Create Manifest files

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).

On manifest deployment environment variables need to be set for image repository and tag. set enviorment variable DOS_IMAGE_REPOSITORY with your actual nginx-dos image anmae. set enviorment variable DOS_IMAGE_TAG with your actual nginx-dos image tag. set enviorment variable EBPF_IMAGE_REPOSITORY with your actual ebpf-manager image name. set enviorment variable EBPF_IMAGE_TAG with your actual ebpf-manager image tag.

dos-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app-protect-dos
dos-nginx-conf-configmap.yaml
apiVersion: 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;
        
        # Health endpoints for probes
        app_protect_dos_liveness on; # uri:/app_protect_dos_liveness port:8090
        app_protect_dos_readiness on; # uri:/app_protect_dos_readiness port:8090
    
        server {
            listen 8090;
            server_name probe;
    
            location / {
                proxy_pass http://localhost:8091;
            }
        }
    
        server {
            listen 8091;
            return 503;
        }

        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=<SYSLOG_SERVER_IP>: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://127.0.0.1/proxy$request_uri;
            }
    
            location /proxy {
                app_protect_dos_enable off;
                client_max_body_size 0;
                default_type text/html;
                return 200 "Hello! I got your URI request - $request_uri\n";
            }
       }
    }
dos-log-default-configmap.yaml
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"
      }
    }
dos-deployment.yaml
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: ${DOS_IMAGE_REPOSITORY}:${DOS_IMAGE_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: license-token-volume
              mountPath: /etc/nginx/license.jwt
              subPath: license.jwt
              readOnly: true

        - name: dos-ebpf-manager
          image: ${EBPF_IMAGE_REPOSITORY}:${EBPF_IMAGE_TAG}
          securityContext:
              privileged: true
          env:
              - name: POD_NAME
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.name # This is unique for every Pod
          volumeMounts:
              - name: shared-dir
                mountPath: /shared/
              - name: bpf
                mountPath: /sys/fs/bpf

      volumes:
        - name: shared-dir
          emptyDir: {}
        - 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.json
        - name: license-token-volume
          secret:
            secretName: license-token
            items:
              - key: license.jwt
                path: license.jwt
dos-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nap-dos
  namespace: app-protect-dos
spec:
  externalTrafficPolicy: Local
  ports:
    - name: app
      port: 80
      protocol: TCP
  selector:
    app: app-protect-dos
  type: LoadBalancer

Start the Manifest deployment

From the folder containing the YAML files from the previous step (Suggested as /manifests), deploy F5 DOS for NGINX using kubectl:

shell
export DOS_IMAGE_REPOSITORY=<your-nginx-dos-image-name>
export DOS_IMAGE_TAG=<your-nginx-dos-image-tag>
export EBPF_IMAGE_REPOSITORY=<your-ebpf-manager-image-name>
export EBPF_IMAGE_TAG=<your-ebpf-manager-image-tag>
kubectl apply -f manifests/dos-namespace.yaml
kubectl apply -f manifests/dos-nginx-conf-configmap.yaml
kubectl apply -f manifests/dos-log-default-configmap.yaml
kubectl apply -f manifests/dos-deployment.yaml
kubectl apply -f manifests/dos-service.yaml

It 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:

shell
kubectl -n app-protect-dos get deployments
kubectl -n app-protect-dos get pods
kubectl -n app-protect-dos get services

You should see output similar to the following:

text
~$ 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   93s

Post-Installation Checks

At this stage, you have finished deploying F5 DOS for NGINX with EBPF L4 accelerated mitigation enabled You can login to dos-ebpf-manager container like following command

text
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 -- bash

and can look at .

You can run the following commands to ensure that F5 DoS for NGINX enforcement is operational.

  1. 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
  2. Verify that there are no NGINX errors in the /var/log/nginx/error.log and that the policy compiled successfully:

    shell
    2025/12/07 09:14:34 [notice] 675#675: APP_PROTECT_DOS { "event": "shared_memory_connected", "worker_pid": 675, "mode": "operational", "mode_changed": true }
    2025/12/07 09:14:34 [notice] 675#675: using the "epoll" event method
    2025/12/07 09:14:34 [notice] 675#675: APP_PROTECT_DOS { "event": "configuration_load_success", "software_version": "36+4.8.3-1.el8.ngx"}
    2025/12/07 09:14:34 [notice] 675#675: nginx/1.29.3 (nginx-plus-r36)
    2025/12/07 09:14:34 [notice] 675#675: built by gcc 8.5.0 20210514 (Red Hat 8.5.0-28) (GCC)
    2025/12/07 09:14:34 [notice] 675#675: OS: Linux 6.8.0-88-generic
    2025/12/07 09:14:34 [notice] 675#675: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2025/12/07 09:14:34 [notice] 675#675: start worker processes
    2025/12/07 09:14:34 [notice] 675#675: start worker process 679
    2025/12/07 09:14:34 [notice] 679#679: APP_PROTECT_DOS { "event": "shared_memory_connected", "worker_pid": 679, "mode": "operational", "mode_changed": true }
  3. Verify that when you simulate an attack, attacker IP addresses are blocked while legitimate traffic passes 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
     done

    c. Verify that legitimate traffic continues as usual while the attack traffic is blocked.

  4. For DOS with L4 accelerated 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_dos

Verify that there are no errors in the /shared/ebpf_dos.log and that the XDP program uploaded successfully:

[2025-12-02
[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_uds

To 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

F5 DoS for NGINX Arbitrator

Overview

F5 DoS for NGINX Arbitrator orchestrates all running F5 DoS for NGINX instances to synchronize local and global attack start and stop.

F5 DoS for NGINX Arbitrator is a central coordinating component for managing multiple F5 DoS for NGINX instances in a network. It is needed when there is more than one F5 DoS for NGINX instance. Its primary function is to ensure that all instances are aware of and share the same state for each protected object.

How the Arbitrator works

  • Collecting state periodically: The Arbitrator regularly collects state information from all running F5 DoS for NGINX instances. This collection occurs at set intervals, typically every 10 seconds.
  • State initialization for new instances: When a new F5 DoS for NGINX instance starts, it retrieves the initial state for each protected object from the Arbitrator rather than starting with an empty state.
  • Updating state during an attack: When an F5 DoS for NGINX instance detects an attack, it sends a notification to the Arbitrator. The Arbitrator updates the state of the affected protected object and propagates that state to all other instances.

Why F5 DoS for NGINX Arbitrator is necessary

F5 DoS for NGINX Arbitrator is essential for several reasons:

  • Global state management: Without the Arbitrator, each F5 DoS for NGINX instance manages its own isolated state for each protected object. This can lead to inconsistencies. For example, if instance A declares an attack on a protected object named "PO-Example," instance B remains unaware of it, potentially leaving the object vulnerable.
  • Uniform attack detection: With the Arbitrator, when instance A detects an attack on "PO-Example" and reports it, the Arbitrator updates the state of "PO-Example" and propagates it to all instances, including instance B.

F5 DoS for NGINX Arbitrator maintains a consistent global state for protected objects across all F5 DoS for NGINX instances. This ensures attacks are detected and mitigated uniformly across your deployment.

F5 DoS for NGINX Arbitrator Deployment

  1. 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
  2. Create a container based on this image, for example, app-protect-dos-arb container:

    docker run --name app_protect_dos_arb -p 3000:3000 -d docker-registry.nginx.com/nap-dos/app_protect_dos_arb
  3. Verify that the app-protect-dos-arb container is up and running with the docker ps command.

  4. 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-arb or configured Arbitrator FQDN (with app_protect_dos_arb_fqdn directive) 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.

Multi-VM Deployment

The Arbitrator service is standalone. If it goes down, it can be restarted and immediately recovers all required information from F5 DoS for NGINX instances, which report to it every 10 seconds. Its downtime is around 10 to 20 seconds, which does not affect F5 DoS for NGINX operation.

F5 DoS for NGINX Arbitrator connects to port 3000. All modules try to connect to it automatically. If it’s not accessible, each instance operates in standalone mode.

F5 DoS for NGINX does not support mutual TLS (mTLS) or password authentication between DoS servers and the Arbitrator. Arbitrator is not exposed outside the namespace. It is the customer’s responsibility to isolate it from external access. This applies to all Arbitrator deployments, not only multi-VM.

Next steps