Deploying NGINX App Protect WAF on Docker
Prerequisites
- Active F5 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 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.5-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.5-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
# 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 \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
debian-archive-keyring \
&& wget -qO - 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 \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx\n" | \
tee /etc/apt/sources.list.d/nginx.list \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key | gpg --dearmor | \
tee /usr/share/keyrings/nginx-static-archive-keyring.gpg >/dev/null \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-static-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-static-archive-keyring.gpg] \
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.5-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
# 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 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 \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
ubuntu-keyring \
&& wget -qO - 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 \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx\n" | \
tee /etc/apt/sources.list.d/nginx.list \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key | gpg --dearmor | \
tee /usr/share/keyrings/nginx-static-archive-keyring.gpg >/dev/null \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-static-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-static-archive-keyring.gpg] \
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.5-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
# 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
# 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 \
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 \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://pkgs.nginx.com/plus/debian `lsb_release -cs` nginx-plus\n" | \
tee /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-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
# 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 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 \
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 \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://pkgs.nginx.com/plus/ubuntu `lsb_release -cs` nginx-plus\n" | \
tee /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-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:
-
Load the NGINX App Protect WAF v5 module at the main context:
load_module modules/ngx_http_app_protect_module.so;
-
Configure the Enforcer address at the
http
context:app_protect_enforcer_address waf-enforcer:50000;
-
Enable NGINX App Protect WAF on an
http/server/location
context (make sure you only enable NGINX App Protect WAF withproxy_pass
/grpc_pass
locations):app_protect_enable on;
In this guide, we have 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 waf-enforcer: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: Replace <version-tag>
with the actual release version you are deploying.
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:<version-tag>"
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:<version-tag>"
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:
Docker Compose File with mTLS
To secure traffic between NGINX and App Protect Enforcer using mTLS, create a docker-compose.yml
with the following configuration:
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
- /path/to/your/certs:/etc/ssl/certs # mount certificates directory
networks:
- waf_network
ports:
- "80:80"
waf-enforcer:
container_name: waf-enforcer
image: "private-registry.nginx.com/nap/waf-enforcer:<version-tag>"
environment:
- ENFORCER_PORT=4431
- ENFORCER_SERVER_CERT=/etc/ssl/certs/app_protect_server.crt
- ENFORCER_SERVER_KEY=/etc/ssl/certs/app_protect_server.key
- ENFORCER_CA_FILE=/etc/ssl/certs/app_protect_client_ca.crt
volumes:
- app_protect_bd_config:/opt/app_protect/bd_config
- /path/to/your/certs:/etc/ssl/certs # mount certificates directory
networks:
- waf_network
restart: always
waf-config-mgr:
container_name: waf-config-mgr
image: "private-registry.nginx.com/nap/waf-config-mgr:<version-tag>"
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
-
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
-
To verify the enforcement functionality, ensure the following request is rejected:
curl "localhost/<script>"
-
(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.
- Assumption: Your bundle files are in
/bundles
on the host machine. - Docker Compose Configuration: In your
docker-compose.yml
, add a volume mount under thewaf-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
andwaf-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.
Air-Gap Install: Secure Offline Deployment
Prerequisites
- Active NGINX App Protect WAF subscription in MyF5 (purchased or trial) on the online machine.
- Docker (with Docker Compose) is installed and running on both the online and offline machine.
Build the NGINX Image
Follow the instructions below to build a Docker image containing the NGINX and the NGINX App Protect module on the machine connected to the internet.
Download Certificates
Log in to 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.5-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.5-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
# 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
# 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 \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
debian-archive-keyring \
&& wget -qO - 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 \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx\n" | \
tee /etc/apt/sources.list.d/nginx.list \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key | gpg --dearmor | \
tee /usr/share/keyrings/nginx-static-archive-keyring.gpg >/dev/null \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-static-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-static-archive-keyring.gpg] \
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.5-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
# 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 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 \
apt-transport-https \
lsb-release \
ca-certificates \
wget \
gnupg2 \
ubuntu-keyring \
&& wget -qO - 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 \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx\n" | \
tee /etc/apt/sources.list.d/nginx.list \
&& wget -qO - https://cs.nginx.com/static/keys/nginx_signing.key | gpg --dearmor | \
tee /usr/share/keyrings/nginx-static-archive-keyring.gpg >/dev/null \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-static-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-static-archive-keyring.gpg] \
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.5-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
# 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
# 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 \
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 \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://pkgs.nginx.com/plus/debian `lsb_release -cs` nginx-plus\n" | \
tee /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-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
# 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 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 \
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 \
&& gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg \
&& printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://pkgs.nginx.com/plus/ubuntu `lsb_release -cs` nginx-plus\n" | \
tee /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-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 .
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
Download Waf-Enforcer and Waf-Config-mgr Images
Pull the waf-enforcer
and waf-config-mgr
images. Replace 5.2.0
with the actual release version you are deploying.
docker pull private-registry.nginx.com/nap/waf-enforcer:5.2.0
docker pull private-registry.nginx.com/nap/waf-config-mgr:5.2.0
Saving and Transferring Images
-
Save the NGINX Open Source or NGINX Plus image you built earlier.
docker save -o nginx-app-protect-5.tar nginx-app-protect-5
-
Save the
waf-enforcer
docker image:docker save -o waf-enforcer.tar private-registry.nginx.com/nap/waf-enforcer:5.2.0
-
Save the
waf-config-mgr
docker image:docker save -o waf-config-mgr.tar private-registry.nginx.com/nap/waf-config-mgr:5.2.0
-
Transfer the tar files from the online machine to the offline/air-gapped machine:
-
On the offline machine load the docker images:
docker load -i nginx-app-protect-5.tar docker load -i waf-enforcer.tar docker load -i waf-config-mgr.tar
NGINX Configuration
In your nginx configuration:
-
Load the NGINX App Protect WAF v5 module at the main context:
load_module modules/ngx_http_app_protect_module.so;
-
Configure the Enforcer address at the
http
context:app_protect_enforcer_address waf-enforcer:50000;
-
Enable NGINX App Protect WAF on an
http/server/location
context (make sure you only enable NGINX App Protect WAF withproxy_pass
/grpc_pass
locations):app_protect_enable on;
In this guide, we have created the following files under /conf/
directory on the offline machine:
/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 waf-enforcer: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;
}
}
Docker Compose File
Create a docker-compose.yml
with the following configuration on the offline machine: Replace 5.2.0
with the actual release version you are deploying.
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:5.2.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:5.2.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
-
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
-
To verify the enforcement functionality, ensure the following request is rejected:
curl "localhost/<script>"
-
(Optionally) To reload the NGINX, run:
sudo docker exec nginx nginx -s reload
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.