Deploying NGINX App Protect WAF on Docker

This guide explains how to deploy NGINX App Protect WAF v5 release using Docker Compose.

Prerequisites

  • Active NGINX App Protect WAF subscription in MyF5 (purchased or trial).
  • Docker (with Docker Compose) is installed and running.

Build the NGINX Image

Follow the instructions below to build a Docker image containing the NGINX and the NGINX App Protect module.

Download Certificates

Log in to the My F5 and download the following two files from your active NGINX App Protect WAF subscription:

nginx-repo.key
nginx-repo.crt

Proceed, by creating a Dockerfile using one of the examples provided below.

Dockerfile Based on the Official NGINX Image

Note:
While this example utilizes the official NGINX Open Source image as a base, the crucial requirement is that NGINX must be installed as a package from the official NGINX repository, rather than being compiled from source.
# syntax=docker/dockerfile:1

# Base image
FROM nginx:1.25.4-bookworm

# Install NGINX App Protect WAF v5 module
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 --no-install-recommends --no-install-suggests -y \
       apt-transport-https \
       lsb-release \
       ca-certificates \
       wget \
       gnupg \
    && wget https://cs.nginx.com/static/keys/nginx_signing.key \
    && gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/nginx.gpg \
       --import nginx_signing.key \
    && chmod 644 /etc/apt/trusted.gpg.d/nginx.gpg \
    && printf "deb https://pkgs.nginx.com/app-protect-x-oss/debian `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-app-protect.list \
    && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
    && apt-get update \
    && apt-get install --no-install-recommends --no-install-suggests -y nginx=1.25.4-1~bookworm app-protect-module-oss  \
    && apt-get remove --purge --auto-remove -y apt-transport-https lsb-release gnupg wget \
    && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx-app-protect.list

NGINX Open Source Dockerfile

Choose the appropriate Dockerfile example based on your Operating System (OS).

# syntax=docker/dockerfile:1

# Supported OS_VER's are 3.16/3.17
ARG OS_VER="3.17"

# Base image
FROM alpine:${OS_VER}

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 \
    apk add openssl curl ca-certificates \
    && printf "%s%s%s%s\n" \
        "http://nginx.org/packages/mainline/alpine/v" \
        `egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release` \
        "/main" \
        | tee -a /etc/apk/repositories \
    && 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-x-oss/alpine/v`egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release`/main\n" | \
        tee -a /etc/apk/repositories \
    && apk update \
    && apk add app-protect-module-oss \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && rm -rf /var/cache/apk/*

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Base image
FROM amazonlinux:2

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 \
    amazon-linux-extras enable epel \
    && yum clean metadata \
    && yum -y install wget ca-certificates epel-release shadow-utils yum-utils \
    && echo "[nginx-mainline]" > /etc/yum.repos.d/nginx.repo \
    && echo "name=nginx mainline repo" >> /etc/yum.repos.d/nginx.repo \
    && echo "baseurl=http://nginx.org/packages/mainline/amzn2/\$releasever/\$basearch/" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgcheck=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgkey=https://nginx.org/keys/nginx_signing.key" >> /etc/yum.repos.d/nginx.repo \
    && echo "module_hotfixes=true" >> /etc/yum.repos.d/nginx.repo \
    && echo "priority=9" >> /etc/yum.repos.d/nginx.repo \
    && echo "[app-protect-x-oss]" > /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-oss/centos/7/\$basearch/" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && yum -y install app-protect-module-oss \
    && yum 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

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Base image
FROM centos:7

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 \
    yum -y install wget ca-certificates epel-release yum-utils \
    && echo "[nginx-mainline]" > /etc/yum.repos.d/nginx.repo \
    && echo "name=nginx mainline repo" >> /etc/yum.repos.d/nginx.repo \
    && echo "baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgcheck=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgkey=https://nginx.org/keys/nginx_signing.key" >> /etc/yum.repos.d/nginx.repo \
    && echo "module_hotfixes=true" >> /etc/yum.repos.d/nginx.repo \
    && echo "[app-protect-x-oss]" > /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-oss/centos/7/\$basearch/" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-7-x-oss.repo \
    && yum -y install app-protect-module-oss \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && yum clean all \
    && rm -rf /var/cache/yum

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Base image
FROM oraclelinux:8

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 wget ca-certificates yum-utils \
    && echo "[nginx-mainline]" > /etc/yum.repos.d/nginx.repo \
    && echo "name=nginx mainline repo" >> /etc/yum.repos.d/nginx.repo \
    && echo "baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgcheck=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgkey=https://nginx.org/keys/nginx_signing.key" >> /etc/yum.repos.d/nginx.repo \
    && echo "module_hotfixes=true" >> /etc/yum.repos.d/nginx.repo \
    && echo "[app-protect-x-oss]" > /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-oss/centos/8/\$basearch/" >> /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-8-x-oss.repo \
    && dnf clean all \
    && dnf -y install app-protect-module-oss \
    && 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

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Supported OS_CODENAME's are: bullseye/bookworm
ARG OS_CODENAME=bookworm

# Base image
FROM debian:${OS_CODENAME}

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 curl \
       apt-transport-https \
       lsb-release \
       ca-certificates \
       wget \
       gnupg2 \
       debian-archive-keyring \
    && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
    && gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg \
    && echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
    http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" \
    | tee /etc/apt/sources.list.d/nginx.list \
    && wget https://cs.nginx.com/static/keys/nginx_signing.key \
    && apt-key add nginx_signing.key \
    && printf "deb https://pkgs.nginx.com/app-protect-x-oss/debian `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-app-protect.list \
    && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
    && apt-get update \
    && DEBIAN_FRONTEND="noninteractive" apt-get install -y nginx=1.25.4-1~`lsb_release -cs` app-protect-module-oss \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Supported UBI_VERSION's are 7/8/9
ARG UBI_VERSION=9

# Base Image
FROM registry.access.redhat.com/ubi${UBI_VERSION}/ubi

# Define the ARG again after FROM to use it in this stage
ARG UBI_VERSION

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 \
    PKG_MANAGER=dnf; \
    if [ "${UBI_VERSION}" = "7" ]; then \
        PKG_MANAGER=yum; \
    fi \
    && $PKG_MANAGER -y install wget ca-certificates yum-utils \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/dependencies.repo \
    && echo "[nginx-mainline]" > /etc/yum.repos.d/nginx.repo \
    && echo "name=nginx mainline repo" >> /etc/yum.repos.d/nginx.repo \
    && echo "baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgcheck=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/nginx.repo \
    && echo "gpgkey=https://nginx.org/keys/nginx_signing.key" >> /etc/yum.repos.d/nginx.repo \
    && echo "module_hotfixes=true" >> /etc/yum.repos.d/nginx.repo \
    && echo "[app-protect-x-oss]" > /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-oss/centos/${UBI_VERSION}/\$basearch/" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-oss.repo \
    && $PKG_MANAGER clean all \
    && $PKG_MANAGER install -y app-protect-module-oss \
    && $PKG_MANAGER clean all \
    && rm -rf /var/cache/$PKG_MANAGER \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Supported OS_CODENAME's are: focal/jammy
ARG OS_CODENAME=jammy

# Base image
FROM ubuntu:${OS_CODENAME}

# Install NGINX OSS and NGINX App Protect WAF v5 module
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 curl \
       apt-transport-https \
       lsb-release \
       ca-certificates \
       wget \
       gnupg2 \
       ubuntu-keyring \
    && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null \
    && gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg \
    && echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
    http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \
    | tee /etc/apt/sources.list.d/nginx.list \
    && wget https://cs.nginx.com/static/keys/nginx_signing.key \
    && apt-key add nginx_signing.key \
    && printf "deb https://pkgs.nginx.com/app-protect-x-oss/ubuntu `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-app-protect.list \
    && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
    && apt-get update \
    && DEBIAN_FRONTEND="noninteractive" apt-get install -y nginx=1.25.4-1~`lsb_release -cs` app-protect-module-oss \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

You are ready to Build the image

NGINX Plus Dockerfile

Choose the appropriate Dockerfile example based on your Operating System (OS).

# syntax=docker/dockerfile:1

# Supported OS_VER's are 3.16/3.17
ARG OS_VER="3.17"

# Base image
FROM alpine:${OS_VER}

# Install NGINX Plus and NGINX App Protect WAF v5 module
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/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-x-plus/alpine/v`egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release`/main\n" | \
       tee -a /etc/apk/repositories \
    && apk update \
    && apk add app-protect-module-plus \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && rm -rf /var/cache/apk/*

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Base image
FROM amazonlinux:2

# Install NGINX Plus and NGINX App Protect WAF v5 module
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 \
    amazon-linux-extras enable epel \
    && yum clean metadata \
    && yum -y install wget ca-certificates epel-release shadow-utils \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/nginx-plus-amazon2.repo \
    && echo "[app-protect-x-plus]" > /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-plus/centos/7/\$basearch/" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && yum -y install app-protect-module-plus \
    && yum 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

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Base image
FROM centos:7

# Install NGINX Plus and NGINX App Protect WAF v5 module
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 \
    yum -y install wget ca-certificates epel-release \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/nginx-plus-7.4.repo \
    && echo "[app-protect-x-plus]" > /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-plus/centos/7/\$basearch/" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-7-x-plus.repo \
    && yum -y install app-protect-module-plus \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && yum clean all \
    && rm -rf /var/cache/yum

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Base image
FROM oraclelinux:8

# Install NGINX Plus and NGINX App Protect WAF v5 module
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 wget ca-certificates yum-utils \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/nginx-plus-8.repo \
    && echo "[app-protect-x-plus]" > /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-plus/centos/8/\$basearch/" >> /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-8-x-plus.repo \
    && dnf clean all \
    && dnf -y install app-protect-module-plus \
    && 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

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Supported OS_CODENAME's are: bullseye/bookworm
ARG OS_CODENAME=bookworm

# Base image
FROM debian:${OS_CODENAME}

# Install NGINX Plus and NGINX App Protect WAF v5 module
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 \
       apt-transport-https \
       lsb-release \
       ca-certificates \
       wget \
       gnupg2 \
    && wget https://cs.nginx.com/static/keys/nginx_signing.key \
    && apt-key add nginx_signing.key \
    && printf "deb https://pkgs.nginx.com/plus/debian `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-plus.list \
    && printf "deb https://pkgs.nginx.com/app-protect-x-plus/debian `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-app-protect.list \
    && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
    && apt-get update \
    && DEBIAN_FRONTEND="noninteractive" apt-get install -y app-protect-module-plus \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Supported UBI_VERSION's are 7/8/9
ARG UBI_VERSION=9

# Base Image
FROM registry.access.redhat.com/ubi${UBI_VERSION}/ubi

# Define the ARG again after FROM to use it in this stage
ARG UBI_VERSION

# Install NGINX Plus and NGINX App Protect WAF v5 module
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 \
    PKG_MANAGER=dnf; \
    if [ "${UBI_VERSION}" = "7" ]; then \
        PKG_MANAGER=yum; \
        NGINX_PLUS_REPO="nginx-plus-7.4.repo"; \
    elif [ "${UBI_VERSION}" = "9" ]; then \
        NGINX_PLUS_REPO="plus-${UBI_VERSION}.repo"; \
    else \
        NGINX_PLUS_REPO="nginx-plus-${UBI_VERSION}.repo"; \
    fi \
    && $PKG_MANAGER -y install wget ca-certificates \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/dependencies.repo \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/${NGINX_PLUS_REPO} \
    && echo "[app-protect-x-plus]" > /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-plus/centos/${UBI_VERSION}/\$basearch/" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-${UBI_VERSION}-x-plus.repo \
    && $PKG_MANAGER clean all \
    && $PKG_MANAGER install -y app-protect-module-plus \
    && $PKG_MANAGER clean all \
    && rm -rf /var/cache/$PKG_MANAGER \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

# syntax=docker/dockerfile:1

# Supported OS_CODENAME's are: focal/jammy
ARG OS_CODENAME=jammy

# Base image
FROM ubuntu:${OS_CODENAME}

# Install NGINX Plus and NGINX App Protect WAF v5 module
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 \
       apt-transport-https \
       lsb-release \
       ca-certificates \
       wget \
       gnupg2 \
    && wget https://cs.nginx.com/static/keys/nginx_signing.key \
    && apt-key add nginx_signing.key \
    && printf "deb https://pkgs.nginx.com/plus/ubuntu `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-plus.list \
    && printf "deb https://pkgs.nginx.com/app-protect-x-plus/ubuntu `lsb_release -cs` nginx-plus\n" | \
       tee /etc/apt/sources.list.d/nginx-app-protect.list \
    && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \
    && apt-get update \
    && DEBIAN_FRONTEND="noninteractive" apt-get install -y app-protect-module-plus \
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Expose port
EXPOSE 80

# Define stop signal
STOPSIGNAL SIGQUIT

# Set default command
CMD ["nginx", "-g", "daemon off;"]

Build image

Note:
Never upload your NGINX App Protect WAF v5 images to a public container registry such as Docker Hub. Doing so violates your license agreement.

To build the image, execute the following command in the directory containing the nginx-repo.crt, nginx-repo.key, and Dockerfile. Here, nginx-app-protect-5 is an example image tag.

sudo docker build --no-cache \
  --secret id=nginx-crt,src=nginx-repo.crt \
  --secret id=nginx-key,src=nginx-repo.key \
  -t nginx-app-protect-5 .

NGINX Configuration

In your nginx configuration:

  1. Load the NGINX App Protect WAF v5 module at the main context:

    load_module modules/ngx_http_app_protect_module.so;
    
  2. Configure the Enforcer address at the http context:

    app_protect_enforcer_address waf-enforcer:50000;
    
  3. Enable NGINX App Protect WAF on an http/server/location context (make sure you only enable NGINX App Protect WAF with proxy_pass/grpc_pass locations):

    app_protect_enable on;
    

In this guide, we’ve created the following files under /conf/ directory:

/conf/nginx.conf

user  nginx;
worker_processes  auto;

# NGINX App Protect WAF
load_module modules/ngx_http_app_protect_module.so;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    # NGINX App Protect WAF
    app_protect_enforcer_address 127.0.0.1:50000;

    include /etc/nginx/conf.d/*.conf;
}

/conf/default.conf

server {
    listen 80;
    server_name domain.com;

    proxy_http_version 1.1;

    location / {

        # NGINX App Protect WAF
        app_protect_enable on;

        client_max_body_size 0;
        default_type text/html;
        proxy_pass http://127.0.0.1:8080/;
    }
}

server {
    listen 8080;
    server_name localhost;


    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

WAF Services Configuration

Set up Docker for F5 Container Registry

Configure Docker to interact with the F5 Container Registry at private-registry.nginx.com:

sudo mkdir -p /etc/docker/certs.d/private-registry.nginx.com
sudo cp <path-to-your-nginx-repo.crt> /etc/docker/certs.d/private-registry.nginx.com/client.cert
sudo cp <path-to-your-nginx-repo.key> /etc/docker/certs.d/private-registry.nginx.com/client.key
Note:
Please note that the file extension for the certificate file has changed from .crt to .cert

Docker Compose File

Create a docker-compose.yml with the following configuration:

version: "3.9"

services:
  nginx:
    container_name: nginx
    image: nginx-app-protect-5
    volumes:
      - app_protect_bd_config:/opt/app_protect/bd_config
      - app_protect_config:/opt/app_protect/config
      - app_protect_etc_config:/etc/app_protect/conf
      - /conf/nginx.conf:/etc/nginx/nginx.conf # based on the provided example
      - /conf/default.conf:/etc/nginx/conf.d/default.conf # based on the provided example
    networks:
      - waf_network
    ports:
      - "80:80"

  waf-enforcer:
    container_name: waf-enforcer
    image: "private-registry.nginx.com/nap/waf-enforcer:1.0.0"
    environment:
      - ENFORCER_PORT=50000
    volumes:
      - app_protect_bd_config:/opt/app_protect/bd_config
    networks:
      - waf_network
    restart: always

  waf-config-mgr:
    container_name: waf-config-mgr
    image: "private-registry.nginx.com/nap/waf-config-mgr:1.0.0"
    volumes:
      - app_protect_bd_config:/opt/app_protect/bd_config
      - app_protect_config:/opt/app_protect/config
      - app_protect_etc_config:/etc/app_protect/conf
    restart: always
    network_mode: none
    depends_on:
      waf-enforcer:
        condition: service_started

networks:
  waf_network:
    driver: bridge

volumes:
  app_protect_bd_config:
  app_protect_config:
  app_protect_etc_config:

Start Deployment

  1. To start the NGINX and WAF services, navigate to the directory that contains the docker-compose.yml file and run:

    sudo docker compose up -d
    
  2. To verify the enforcement functionality, ensure the following request is rejected:

    curl "localhost/<script>"
    
  3. (Optionally) To reload the NGINX, run:

    sudo docker exec nginx  nginx -s reload
    

Using Policy and Logging Profile Bundles

To work with custom policy and logging profile bundles, these files must be accessible to the waf-config-mgr container.

  1. Assumption: Your bundle files are in /bundles on the host machine.
  2. Docker Compose Configuration: In your docker-compose.yml, add a volume mount under the waf-config-mgr service to link the host directory /bundles to the same path inside the container.
...
waf-config-mgr:
  container_name: waf-config-mgr
  image: "private-registry.nginx.com/nap/waf-config-mgr:1.0.0"
  volumes:
    - /bundles:/bundles # Mounting the host directory to the container
    - ... #existing volume mounts

By setting up this volume mount, the bundle files can be referenced within your NGINX configuration using the /bundles directory.

For instance:

app_protect_policy_file /bundles/custom_policy.tgz;
app_protect_security_log /bundles/custom_logging_profile.tgz syslog:server=localhost:5514;

Troubleshooting

  • Container Failures: If a container fails, restarts, or stops running, examine its logs for any error messages by executing sudo docker logs [container_name] for error messages. The default names for the containers are:
    • nginx.
    • waf-enforcer
    • waf-config-mgr
  • Permissions Issues: By default, the containers waf-config-mgr and waf-enforcer operate with the user and group IDs set to 101:101. Ensure that the bundle files are accessible to these IDs.

Sometimes, simply restarting the services can resolve transient issues. Use sudo docker compose down -v followed by sudo docker compose up -d to restart all services.

If you encounter any other issues, check the Troubleshooting Guide.

Conclusion

This guide provides the foundational steps for deploying NGINX App Protect WAF v5 using Docker Compose. You may need to adjust the deployment to fit your specific requirements.

For more detailed configuration options and advanced deployment strategies, refer to the NGINX App Protect WAF v5 configuration guide.


This documentation applies to the following versions of NGINX App Protect WAF: 5.0.