Kubernetes

This page describes how to install F5 WAF for NGINX using Kubernetes.

It explains the common steps necessary for any Kubernetes-based deployment, then provides details specific to Helm or Manifests.

Before you begin

To complete this guide, you will need the following pre-requisites:

  • A functional Kubernetes cluster
  • An active F5 WAF for NGINX subscription (Purchased or trial)
  • Docker

You will need Helm installed for a Helm-based deployment.

You should read the IP intelligence and Secure traffic using mTLS topics for additional set-up configuration if you want to use them immediately.

There is another optional topic to Add a read-only filesystem for Kubernetes

To review supported operating systems, read the Technical specifications topic.

Download your subscription credentials

  1. Log in to MyF5.
  2. Go to My Products & Plans > Subscriptions to see your active subscriptions.
  3. Find your NGINX subscription, and select the Subscription ID for details.
  4. Download the SSL Certificate and Private Key files from the subscription page.

Create a Dockerfile

In the same folder as your credential files, create a Dockerfile based on your desired operating system image using an example from the following sections.

Alternatively, you may want make your own image based on a Dockerfile using the official NGINX image:

Dockerfile based on official image

This example uses NGINX Open Source as a base: it requires NGINX to be installed as a package from the official repository, instead of being compiled from source.

dockerfile
# syntax=docker/dockerfile:1

# Base image
FROM nginx:1.25.5-bookworm

# Install F5 WAF for NGINX 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
If you are not using using custom_log_format.json or the IP intelligence feature, you should remove any references to them from your Dockerfile.

Alpine Linux

dockerfile
# syntax=docker/dockerfile:1

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

# Base image
FROM alpine:${OS_VER}

# Install NGINX OSS and F5 WAF for NGINX 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;"]
dockerfile
# syntax=docker/dockerfile:1

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

# Base image
FROM alpine:${OS_VER}

# Install NGINX Plus and F5 WAF for NGINX 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;"]

Amazon Linux

dockerfile
# syntax=docker/dockerfile:1

# Base image
FROM amazonlinux:2023

# Install NGINX OSS and F5 WAF for NGINX 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 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/amzn/2023/\$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-oss.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-oss.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-oss/amzn/2023/\$basearch/" >> /etc/yum.repos.d/app-protect-oss.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.crt" >> /etc/yum.repos.d/app-protect-oss.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-oss.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-oss.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-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;"]
dockerfile
# syntax=docker/dockerfile:1

# Base image
FROM amazonlinux:2023

# Install NGINX Plus and F5 WAF for NGINX v5 module
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.cert,mode=0644 \
    --mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
    yum -y install wget ca-certificates shadow-utils \
    && wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/plus-amazonlinux2023.repo \
    && echo "[app-protect-x-plus]" > /etc/yum.repos.d/app-protect-plus.repo \
    && echo "name=nginx-app-protect repo" >> /etc/yum.repos.d/app-protect-plus.repo \
    && echo "baseurl=https://pkgs.nginx.com/app-protect-x-plus/amzn/2023/\$basearch/" >> /etc/yum.repos.d/app-protect-plus.repo \
    && echo "sslclientcert=/etc/ssl/nginx/nginx-repo.cert" >> /etc/yum.repos.d/app-protect-plus.repo \
    && echo "sslclientkey=/etc/ssl/nginx/nginx-repo.key" >> /etc/yum.repos.d/app-protect-plus.repo \
    && echo "gpgcheck=0" >> /etc/yum.repos.d/app-protect-plus.repo \
    && echo "enabled=1" >> /etc/yum.repos.d/app-protect-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;"]

Debian

dockerfile
# 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 F5 WAF for NGINX 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;"]
dockerfile
# 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 F5 WAF for NGINX 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;"]

Oracle Linux

dockerfile
# syntax=docker/dockerfile:1

# Base image
FROM oraclelinux:8

# Install NGINX OSS and F5 WAF for NGINX 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;"]
dockerfile
# syntax=docker/dockerfile:1

# Base image
FROM oraclelinux:8

# Install NGINX Plus and F5 WAF for NGINX v5 module
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.cert,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.cert" >> /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;"]

RHEL 8

dockerfile
# syntax=docker/dockerfile:1

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

# 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 F5 WAF for NGINX 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;"]
dockerfile
# syntax=docker/dockerfile:1

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

# 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 F5 WAF for NGINX v5 module
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.cert,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.cert" >> /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;"]

RHEL 9

dockerfile
# syntax=docker/dockerfile:1

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

# 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 F5 WAF for NGINX 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;"]
dockerfile
# syntax=docker/dockerfile:1

# Base Image
FROM rockylinux:9

# Install NGINX Plus and F5 WAF for NGINX v5 module
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.cert,mode=0644 \
    --mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
    dnf -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.cert" >> /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 \
    && dnf clean all \
    && dnf install -y 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;"]

Rocky Linux 9

dockerfile
# syntax=docker/dockerfile:1

# Base Image
FROM rockylinux:9

# Install NGINX OSS and F5 WAF for NGINX 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/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 \
    && dnf clean all \
    && dnf install -y 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;"]
dockerfile
# syntax=docker/dockerfile:1

# Base Image
FROM rockylinux:9

# Install NGINX Plus and F5 WAF for NGINX v5 module
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.cert,mode=0644 \
    --mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \
    dnf -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.cert" >> /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 \
    && dnf clean all \
    && dnf install -y 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;"]

Ubuntu

dockerfile
# 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 F5 WAF for NGINX 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;"]
dockerfile
# 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 F5 WAF for NGINX v5 module
RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.cert,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 the Docker image

Your folder should contain the following files:

  • nginx-repo.cert
  • nginx-repo.key
  • Dockerfile

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

shell
sudo docker build --no-cache --platform linux/amd64 \
  --secret id=nginx-crt,src=nginx-repo.crt \
  --secret id=nginx-key,src=nginx-repo.key \
  -t <your-image-name> .

Once you have built the image, push it to your private image repository, which should be accessible to your Kubernetes cluster.

From this point, the steps change based on your installation method:

Use Helm to install F5 WAF for NGINX

Download your JSON web token

  1. Log in to MyF5.
  2. Go to My Products & Plans > Subscriptions to see your active subscriptions.
  3. Find your NGINX subscription, and select the Subscription ID for details.
  4. Download the JSON Web Token file from the subscription page.

Get the Helm chart

To get the Helm chart, first configure Docker for the F5 Container Registry.

Create a directory and copy your certificate and key to this directory:

shell
mkdir -p /etc/docker/certs.d/private-registry.nginx.com
cp <path-to-your-nginx-repo.crt> /etc/docker/certs.d/private-registry.nginx.com/client.cert
cp <path-to-your-nginx-repo.key> /etc/docker/certs.d/private-registry.nginx.com/client.key

Then use helm pull to get the chart, replacing <release-version>:

helm pull oci://private-registry.nginx.com/nap/nginx-app-protect --version <release-version> --untar

Change the working directory afterwards:

cd nginx-app-protect

Deploy the Helm chart

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

  • Update appprotect.nginx.image.repository and appprotect.nginx.image.tag with the image name chosen during when building the Docker image.
  • Update appprotect.config.nginxJWT with your JSON web token
  • Update dockerConfigJson to contain the base64 encoded Docker registration credentials

You can encode your credentials with the following command:

shell
echo '{
    "auths": {
        "private-registry.nginx.com": {
            "username": "<JWT Token>",
            "password": "none"
        }
    }
}' | base64 -w 0```

Alternatively, you can use kubectl to create a secret:

shell
kubectl create secret docker-registry regcred -n <namespace> \
    --docker-server=private-registry.nginx.com \
    --docker-username=<JWT Token> \
    --docker-password=none

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

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

helm install <release-name> .

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

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

Helm Chart parameters

This table lists the configurable parameters of the F5 WAF for NGINX Helm chart and their default values.

To understand the mTLS Configuration options, view the Secure traffic using mTLS topic.

Topic Parameter Description Default value
Namespace namespace The target Kubernetes namespace where the Helm chart will be deployed. N/A
F5 WAF for NGINX Configuration appprotect.replicas The number of replicas for the F5 WAF for NGINX deployment. 1
appprotect.readOnlyRootFilesystem Specifies if the root filesystem is read-only. false
appprotect.annotations Custom annotations for the deployment. {}
NGINX Configuration appprotect.nginx.image.repository Docker image repository for NGINX. <your-private-registry>/nginx-app-protect-5
appprotect.nginx.image.tag Docker image tag for NGINX. latest
appprotect.nginx.imagePullPolicy Image pull policy. IfNotPresent
appprotect.nginx.resources The resources of the NGINX container. requests: cpu=10m,memory=16Mi
WAF Config Manager appprotect.wafConfigMgr.image.repository Docker image repository for the WAF Configuration Manager. private-registry.nginx.com/nap/waf-config-mgr
appprotect.wafConfigMgr.image.tag Docker image tag for the WAF Configuration Manager. 5.6.0
appprotect.wafConfigMgr.imagePullPolicy Image pull policy. IfNotPresent
appprotect.wafConfigMgr.resources The resources of the WAF Config Manager container. requests: cpu=10m,memory=16Mi
WAF Enforcer appprotect.wafEnforcer.image.repository Docker image repository for the WAF Enforcer. private-registry.nginx.com/nap/waf-enforcer
appprotect.wafEnforcer.image.tag Docker image tag for the WAF Enforcer. 5.6.0
appprotect.wafEnforcer.imagePullPolicy Image pull policy. IfNotPresent
appprotect.wafEnforcer.env.enforcerPort Port for the WAF Enforcer. 50000
appprotect.wafEnforcer.resources The resources of the WAF Enforcer container. requests: cpu=20m,memory=256Mi
WAF IP Intelligence _appprotect.wafIpIntelligence.enable Enable or disable the use of the IP intelligence container false
appprotect.wafIpIntelligence.image.repository Docker image repository for the WAF IP Intelligence. private-registry.nginx.com/nap/waf-ip-intelligence
appprotect.wafIpIntelligence.image.tag Docker image tag for the WAF Enforcer. 5.6.0
appprotect.wafIpIntelligence.imagePullPolicy Image pull policy. IfNotPresent
appprotect.wafIpIntelligence.resources The resources of the WAF Enforcer container. requests: cpu=10m,memory=256Mi
Config appprotect.config.name The name of the ConfigMap used by the NGINX container. nginx-config
appprotect.config.annotations The annotations of the ConfigMap. {}
appprotect.config.nginxJWT JWT license for NGINX. ""
appprotect.config.nginxConf NGINX configuration file content. See values.yaml
appprotect.config.nginxDefault Default server block configuration for NGINX. {}
appprotect.config.entries Extra entries of the ConfigMap for customizing NGINX configuration. {}
mTLS Configuration appprotect.mTLS.serverCert The base64-encoded TLS certificate for the App Protect Enforcer (server). ""
appprotect.mTLS.serverKey The base64-encoded TLS key for the App Protect Enforcer (server). ""
appprotect.mTLS.serverCACert The base64-encoded TLS CA certificate for the App Protect Enforcer (server). ""
appprotect.mTLS.clientCert The base64-encoded TLS certificate for the NGINX (client). ""
appprotect.mTLS.clientKey The base64-encoded TLS key for the NGINX (client). ""
appprotect.mTLS.clientCACert The base64-encoded TLS CA certificate for the NGINX (client). ""
Extra Volumes appprotect.volumes The extra volumes of the NGINX container. []
Extra Volume Mounts appprotect.volumeMounts The extra volume mounts of the NGINX container. []
Service appprotect.service.nginx.ports.port Service port. 80
appprotect.service.nginx.ports.protocol Protocol used. TCP
appprotect.service.nginx.ports.targetPort Target port inside the container. 80
appprotect.service.nginx.type Service type. NodePort
Storage Configuration appprotect.storage.bundlesPath.name Bundles volume name used by WAF Config Manager container for storing policy bundles app-protect-bundles
appprotect.storage.bundlesPath.mountPath Bundles mount path used by WAF Config Manager container, which is the path to the app_protect_policy_file in nginx.conf. /etc/app_protect/bundles
appprotect.storage.pv.hostPath Host path for persistent volume. /mnt/nap5_bundles_pv_data
appprotect.storage.pvc.bundlesPvc.storageClass Storage class for PVC. manual
appprotect.storage.pvc.bundlesPvc.storageRequest Storage request size. 2Gi
Docker Configuration dockerConfigJson A base64-encoded string representing the Docker registry credentials in JSON format. N/A

Use Manifests to install F5 WAF for NGINX

Update configuration files

Once you have installed F5 WAF for NGINX, you must load it as a module in the main context of your NGINX configuration.

load_module modules/ngx_http_app_protect_module.so;

The Enforcer address must be added at the http context:

app_protect_enforcer_address 127.0.0.1:50000;

And finally, F5 WAF for NGINX can enabled on a http, server or location context:

app_protect_enable on;
You should only enable F5 WAF for NGINX on proxy_pass and grpc_pass locations.

Here are two examples of how these additions could look in configuration files:

Create a Secret

Before you can start the Manifest deployment, you need a Kubernetes secret for the Docker registry.

You can create the secret using kubectl create:

kubectl create secret docker-registry regcred --docker-server=private-registry.nginx.com --docker-username=<JWT Token> --docker-password=none

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

Create Manifest files

The default configuration provided creates two replicas, each hosting NGINX and WAF services together in a single Kubernetes pod.

Create all of these files in a single folder (Such as /manifests).

In each file, replace <your-private-registry>/waf:<your-tag> with your actual image tag.

This configuration uses a hostPath backed persistent volume claim.
yaml
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>/waf:<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: 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
yaml
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>/waf:<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: 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
yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nap5
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: NodePort

Start the Manifest deployment

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

kubectl apply -f manifests/

It will apply all the configuration defined in the files to your Kubernetes cluster.

You can then check the status of the deployment with kubectl get:

shell
kubectl get deployments
kubectl get pods
kubectl get services

You should see output similar to the following:

text
deployment.apps/nap5-deployment created
service/nginx created
persistentvolume/nap5-bundles-pv created
persistentvolumeclaim/nap5-bundles-pvc created
At this stage, you have finished deploying F5 WAF for NGINX and can look at Post-installation checks.

Post-installation checks

The following steps check that F5 WAF for NGINX enforcement is operational.

They should be ran in the environment with the WAF components.

Check that the three processes for F5 WAF for NGINX are running using ps aux:

  • bd-socket-plugin
  • nginx: master process
  • nginx: worker process
shell
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         8  1.3  2.4 3486948 399092 ?      Sl   09:11   0:02 /usr/share/ts/bin/bd-socket-plugin tmm_count 4 proc_cpuinfo_cpu_mhz 2000000 total_xml_memory 307200000 total_umu_max_size 3129344 sys_max_account_id 1024 no_static_config
root        14  0.0  0.1  71060 26680 ?        S    09:11   0:00 nginx: master process /usr/sbin/nginx -c /tmp/policy/test_nginx.conf -g daemon off;
root        26  0.0  0.3  99236 52092 ?        S    09:12   0:00 nginx: worker process
root        28  0.0  0.0  11788  2920 pts/0    Ss   09:12   0:00 bash
root        43  0.0  0.0  47460  3412 pts/0    R+   09:14   0:00 ps aux

Verify there are no errors in the file /var/log/nginx/error.log and that the policy compiled successfully:

2020/05/10 13:21:04 [notice] 402#402: APP_PROTECT { "event": "configuration_load_start", "configSetFile": "/opt/f5waf/config/config_set.json" }
2020/05/10 13:21:04 [notice] 402#402: APP_PROTECT policy 'app_protect_default_policy' from: /etc/app_protect/conf/NginxDefaultPolicy.json compiled successfully
2020/05/10 13:21:04 [notice] 402#402: APP_PROTECT { "event": "configuration_load_success", "software_version": "1.1.1", "attack_signatures_package":{"revision_datetime":"2019-07-16T12:21:31Z"},"completed_successfully":true}
2020/05/10 13:21:04 [notice] 402#402: using the "epoll" event method
2020/05/10 13:21:04 [notice] 402#402: nginx/1.17.6 (nginx-plus-r20)
2020/05/10 13:21:04 [notice] 402#402: built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
2020/05/10 13:21:04 [notice] 402#402: OS: Linux 3.10.0-957.27.2.el7.x86_64
2020/05/10 13:21:04 [notice] 402#402: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2020/05/10 13:21:04 [notice] 406#406: start worker processes
2020/05/10 13:21:04 [notice] 406#406: start worker process 407

Check that sending an attack signature in a request returns a response block page containing a support ID:

shell
Request:
http://10.240.185.211/?a=<script>

Response:
The requested URL was rejected. Please consult with your administrator.

Your support ID is: 9847191526422998597

[Go Back]

If your policy includes JSON/XML profiles, check /var/log/app_protect/bd-socket-plugin.log for possible errors:

grep '|ERR' /var/log/app_protect/bd-socket-plugin.log

Verify that Enforcement functionality is working by checking the following request is rejected:

curl "localhost/<script>"

Or from an external host:

curl "<node-external-ip>:<node-port>/<script>"

Next steps

Once you have successfully installed F5 WAF for NGINX, there are some topics you may want to follow afterwards: