Deploying NGINX App Protect WAF on Kubernetes
Prerequisites
- An active F5 NGINX App Protect WAF subscription in MyF5 (purchased or trial).
- A functional Kubernetes cluster.
kubectl
command-line tool, properly configured.
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
# 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 .
Next, push it to your private image repository, ensuring it’s accessible to your Kubernetes cluster.
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 127.0.0.1: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, the following files are used:
/etc/nginx/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;
}
/etc/nginx/conf.d/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
JWT Token and Docker Config Secret
-
Create a Kubernetes secret:
kubectl create secret docker-registry regcred --docker-server=private-registry.nginx.com --docker-username=<JWT Token> --docker-password=none
It is important that the
--docker-username=<JWT Token>
contains the contents of the token and does not point to the token itself. Ensure that when you copy the contents of the JWT token, there are no additional characters or extra whitespaces. This can invalidate the token and cause 401 errors when trying to authenticate to the registry. Usenone
for password (as the password is not used). -
Verify Secret:
kubectl get secret regcred --output=yaml
-
Use Secret in Deployments:
The Secret is now available for use in manifest deployments.
Protect sensitive data
To protect your system and data, follow these security practices:
JWTs: Treat JSON Web Tokens (JWTs) as sensitive data. Store them securely and delete them after use to prevent unauthorized access.
Shell history: Commands with JWTs or passwords are saved in plain text in your shell history. After running such commands, clear the history to protect credentials. For example:
- Edit your shell history file (such as ~/.bash_history or ~/.zsh_history) to remove specific commands.
- Use
history -c
to clear all shell history in bash or zsh.
Manifest Deployment
In this configuration, two replicas are deployed, with each hosting both NGINX and WAF services together in a single Kubernetes pod.
For simplification in this documentation, we’re using a hostPath backed persistent volume claim nap5-storage.yaml
:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nap5-bundles-pv
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: "/mnt/nap5_bundles_pv_data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nap5-bundles-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
volumeName: nap5-bundles-pv
An example nap5-deployment.yaml
:
Replace the <your-private-registry>/nginx-app-protect-5:<your-tag>
with the actual image tag.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nap5-deployment
spec:
selector:
matchLabels:
app: nap5
replicas: 2
template:
metadata:
labels:
app: nap5
spec:
imagePullSecrets:
- name: regcred
containers:
- name: nginx
image: <your-private-registry>/nginx-app-protect-5:<your-tag>
imagePullPolicy: IfNotPresent
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: waf-enforcer
image: private-registry.nginx.com/nap/waf-enforcer:<version-tag>
imagePullPolicy: IfNotPresent
env:
- name: ENFORCER_PORT
value: "50000"
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: waf-config-mgr
image: private-registry.nginx.com/nap/waf-config-mgr:<version-tag>
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- all
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
volumes:
- name: app-protect-bd-config
emptyDir: {}
- name: app-protect-config
emptyDir: {}
- name: app-protect-bundles
persistentVolumeClaim:
claimName: nap5-bundles-pvc
Finally, nap5-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nap5
ports:
- protocol: TCP
port: 80
targetPort: 80
type: NodePort
Start Deployment
-
Assuming the above manifests are saved in the
/home/user/k8s
directory, you can deploy them by executing the following command:kubectl apply -f /home/user/k8s
Example output:
deployment.apps/nap5-deployment created service/nginx created persistentvolume/nap5-bundles-pv created persistentvolumeclaim/nap5-bundles-pvc created
This command tells
kubectl
to apply the configuration defined in all the files within the/home/user/k8s
directory to your Kubernetes cluster. -
Verify the deployment:
-
Check the status of the deployment using:
kubectl get deployments
-
Verify that the pods are running with:
kubectl get pods
-
Verify services with:
kubectl get services
-
To verify the enforcement functionality, ensure the following request is rejected:
curl "<node-external-ip>:<node-port>/<script>"
-
-
To restart the deployment, use:
kubectl rollout restart deployment.apps/nap5-deployment
-
To remove the deployment, use:
kubectl delete -f /home/user/k8s
Configure read only file systems
NGINX App Protect WAF v5 allows you to enable the readOnlyRootFilesystem
option in your Kubernetes Configuration. This option restricts the root filesystem to read-only mode, which improves security by limiting potential write access in case of compromise.
To enable this feature, you will need a Kubernetes cluster that supports read-only root file systems, and you access to the NGINX and NGINX App Protect WAF configurations.
You may need to identify any extra paths that need to be writable by App Protect during runtime: the following steps assume you are using the defaults path.
Enable readOnlyRootFilesystem
and configure writable paths
The first step is to add the readOnlyRootFilesystem
value (as true) to your Kubernetes pod security context as follows:
containers:
- name: nginx
...
securityContext:
readOnlyRootFilesystem: true
- name: waf-enforcer
...
securityContext:
readOnlyRootFilesystem: true
- name: waf-config-mgr
...
securityContext:
readOnlyRootFilesystem: true
With a read-only root file system, you will likely still require write access for certain directories, such as logs and temporary files. You can add these directories by mounting them as writable volumes in your Kubernetes deployment.
In this example, /tmp
and /var/log/nginx
are writable directories, essential for NGINX and App Protect operations.
containers:
- name: nginx
...
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: tmp-volume
mountPath: /tmp
- name: nginx-log
mountPath: /var/log/nginx
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
...
volumes:
- name: app-protect-bd-config
emptyDir: {}
- name: app-protect-config
emptyDir: {}
- name: nginx-log
emptyDir: {}
- name: tmp-volume
emptyDir: {}
- name: app-protect-bundles
persistentVolumeClaim:
claimName: nap5-bundles-pvc
A full example might look like the following:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nap5-deployment
spec:
selector:
matchLabels:
app: nap5
replicas: 2
template:
metadata:
labels:
app: nap5
spec:
imagePullSecrets:
- name: regcred
containers:
- name: nginx
image: <your-private-registry>/nginx-app-protect-5:<your-tag>
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: tmp-volume
mountPath: /tmp
- name: nginx-log
mountPath: /var/log/nginx
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
- name: waf-enforcer
image: private-registry.nginx.com/nap/waf-enforcer:<version-tag>
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: true
env:
- name: ENFORCER_PORT
value: "50000"
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: waf-config-mgr
image: private-registry.nginx.com/nap/waf-config-mgr:<version-tag>
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- all
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
volumes:
- name: app-protect-bd-config
emptyDir: {}
- name: app-protect-config
emptyDir: {}
- name: nginx-log
emptyDir: {}
- name: tmp-volume
emptyDir: {}
- name: app-protect-bundles
persistentVolumeClaim:
claimName: nap5-bundles-pvc
Update NGINX configuration with writable paths
Once you have created writable paths in your Kubernetes cluster, you should update your NGINX configuration to use these paths.
The following are fields in nginx.conf
you should update, which correspond to writable volumes configured during the last step:
pid /tmp/nginx.pid;
...
http {
...
# Temporary directories for kubernetes "readonlyfilesystem"
client_body_temp_path /tmp/nginx-client-body;
proxy_temp_path /tmp/nginx-proxy;
fastcgi_temp_path /tmp/nginx-fastcgi;
uwsgi_temp_path /tmp/nginx-uwsgi;
scgi_temp_path /tmp/nginx-scgi;
...
}
A full example might look like the following:
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 debug;
pid /tmp/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;
# Temporary directories for kubernetes "readonlyfilesystem"
client_body_temp_path /tmp/nginx-client-body;
proxy_temp_path /tmp/nginx-proxy;
fastcgi_temp_path /tmp/nginx-fastcgi;
uwsgi_temp_path /tmp/nginx-uwsgi;
scgi_temp_path /tmp/nginx-scgi;
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;
}
Remediate possible issues
- Permission denied errors: If you encounter file permission issues, verify that the paths requiring write access are correctly configured as writable volumes in the pod manifest.
- NGINX App Protect WAF initialization errors:
Check the NGINX and NGINX App Protect Logs to ensure that App Protect can write to necessary files like logs and temporary directories.
For general issues, read the Troubleshooting topic.
mTLS Deployment
To secure traffic between NGINX and App Protect Enforcer using mTLS, follow the steps below:
First, create a Kubernetes Secret that contains the certificate and key files:
kubectl create secret generic enforcer-certificates \
--from-file=app_protect_server.crt=/path/to/app_protect_server.crt \
--from-file=app_protect_server.key=/path/to/app_protect_server.key \
--from-file=app_protect_client_ca.crt=/path/to/app_protect_client_ca.crt
Next, update or create the nap5-deployment.yaml
to mount the Secret as a volume and set the environment variables to point to the mounted files:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nap5-deployment
spec:
selector:
matchLabels:
app: nap5
replicas: 2
template:
metadata:
labels:
app: nap5
spec:
imagePullSecrets:
- name: regcred
containers:
- name: nginx
image: <your-private-registry>/nginx-app-protect-5:<your-tag>
imagePullPolicy: IfNotPresent
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: certs
mountPath: /etc/ssl/certs
readOnly: true
- name: waf-enforcer
image: private-registry.nginx.com/nap/waf-enforcer:<version-tag>
imagePullPolicy: IfNotPresent
env:
- name: ENFORCER_PORT
value: "4431"
- name: ENFORCER_SERVER_CERT
value: "/etc/ssl/certs/app_protect_server.crt"
- name: ENFORCER_SERVER_KEY
value: "/etc/ssl/certs/app_protect_server.key"
- name: ENFORCER_CA_FILE
value: "/etc/ssl/certs/app_protect_client_ca.crt"
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: certs
mountPath: /etc/ssl/certs
readOnly: true
- name: waf-config-mgr
image: private-registry.nginx.com/nap/waf-config-mgr:<version-tag>
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- all
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
volumes:
- name: app-protect-bd-config
emptyDir: {}
- name: app-protect-config
emptyDir: {}
- name: app-protect-bundles
persistentVolumeClaim:
claimName: nap5-bundles-pvc
- name: certs
secret:
secretName: enforcer-certificates
Using Compiled Policy and Logging Profile Bundles in NGINX
In this setup, copy your compiled policy and logging profile bundles to /mnt/nap5_bundles_pv_data
on a cluster node. Then, in your NGINX configuration, refer to these files from /etc/app_protect/bundles
.
For example, to apply custom_policy.tgz
that you’ve placed in /mnt/nap5_bundles_pv_data/
, use:
app_protect_policy_file "/etc/app_protect/bundles/custom_policy.tgz";
The NGINX configuration can be integrated using a configmap mount.
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/Air-Gap machine.
- Local registry for the offline/Air-Gap machine
- A functional Kubernetes cluster.
kubectl
command-line tool, properly configured.
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 .
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 imagedocker save -o waf-enforcer.tar waf-enforcer:5.2.0
-
Save the
waf-config-mgr
docker imagedocker save -o waf-config-mgr.tar 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
Local Registry Setup
Set up a local registry for the images on the offline/air-gap machine. For the example deployment we will use Registry.
-
Set up a local registry on the offline/air-gap machine
-
Add nginx-app-protect-5, waf-enforcer, and waf-config-mgr to the local registry
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:
/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;
}
/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
Manifest Deployment
In this configuration, one replicas are deployed, with each hosting both NGINX and WAF services together in a single Kubernetes pod.
For simplification in this documentation, we’re using a hostPath backed persistent volume claim nap5-storage.yaml
:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nap5-bundles-pv
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: "/mnt/nap5_bundles_pv_data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nap5-bundles-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
volumeName: nap5-bundles-pv
An example nap5-deployment.yaml
:
Replace the <your-private-registry>/nginx-app-protect-5:<your-tag>
with the actual image tag.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nap5-deployment
spec:
selector:
matchLabels:
app: nap5
replicas: 1
template:
metadata:
labels:
app: nap5
spec:
containers:
- name: nginx
image: localhost:5000/nginx-app-protect-5:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: nginx-config-volume
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: nginx-default-config-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
- name: waf-enforcer
image: localhost:5000/waf-enforcer:5.2.0
imagePullPolicy: IfNotPresent
env:
- name: ENFORCER_PORT
value: "50000"
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: waf-config-mgr
image: localhost:5000/waf-config-mgr:5.2.0
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- all
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
volumes:
- name: app-protect-bd-config
emptyDir: {}
- name: app-protect-config
emptyDir: {}
- name: app-protect-bundles
persistentVolumeClaim:
claimName: nap5-bundles-pvc
- name: nginx-config-volume
configMap:
name: nginx-config
items:
- key: nginx.conf
path: nginx.conf
- name: nginx-default-config-volume
configMap:
name: nginx-config
items:
- key: default.conf
path: default.conf
Finally, nap5-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nap5
ports:
- protocol: TCP
port: 80
targetPort: 80
type: NodePort
In this configuration, a single replica is deployed. It hosts both NGINX and WAF services. These services run together in a single Kubernetes pod.
Add nginx.conf
and default.conf
to a config map.
kubectl create configmap nginx-config \
--from-file=/nginx.conf \
--from-file=/default.conf
Start Deployment
-
Assuming the above manifests are saved in the
/home/user/k8s
directory, you can deploy them by executing the following command:kubectl apply -f /home/user/k8s
Example output:
deployment.apps/nap5-deployment created service/nginx created persistentvolume/nap5-bundles-pv created persistentvolumeclaim/nap5-bundles-pvc created
This command tells
kubectl
to apply the configuration defined in all the files within the/home/user/k8s
directory to your Kubernetes cluster. -
Verify the deployment:
-
Check the status of the deployment using:
kubectl get deployments
-
Verify that the pods are running with:
kubectl get pods
-
Verify services with:
kubectl get services
-
To verify the enforcement functionality, ensure the following request is rejected:
curl "<node-external-ip>:<node-port>/<script>"
-
-
To restart the deployment, use:
kubectl rollout restart deployment.apps/nap5-deployment
-
To remove the deployment, use:
kubectl delete -f /home/user/k8s
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
Troubleshooting
- Pod Failures: If a pod fails, check its logs using
kubectl logs [pod_name] -c [container_name]
for error messages. The default names for the containers are:nginx
.waf-enforcer
waf-config-mgr
- Connectivity Issues: Verify the service and deployment configurations, especially port mappings and selectors.
- 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.
If you encounter any issues, check the Troubleshooting Guide.
Conclusion
This guide provides the foundational steps for deploying NGINX App Protect WAF v5 on Kubernetes. 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.