Secure traffic using mTLS

This document describes how to secure traffic between NGINX and the F5 WAF enforcer using mTLS.

It explains how to generate the necessary certificates, then update configuration files to use them.

A mutual TLS (mTLS) connection creates authentication between both NGINX (client) and F5 WAF Enforcer (server).

This adds an extra layer of security, ensuring that both parties are who they claim to be.

Generate certificates and keys

To enable mTLS, you must first create certificates.

The following commands will generate self-signed certificates in /etc/ssl/certs/ valid for the default period of 30 days. You can adjust the command to fit your needs.

For instance, to specify a different validity period, add the -days option followed by the number of days you want the certificate to be valid (Such as -days 90).

First, create the certificate authority files:

shell
mkdir -p /etc/ssl/certs
openssl genpkey -algorithm RSA -out /etc/ssl/certs/app_protect_server_ca.key
openssl genpkey -algorithm RSA -out /etc/ssl/certs/app_protect_client_ca.key
openssl req -x509 -new -key /etc/ssl/certs/app_protect_server_ca.key -out /etc/ssl/certs/app_protect_server_ca.crt -subj "/O=F5/OU=app-protect/CN=mTLS Server Root CA"
openssl req -x509 -new -key /etc/ssl/certs/app_protect_client_ca.key -out /etc/ssl/certs/app_protect_client_ca.crt -subj "/O=F5/OU=app-protect/CN=mTLS Client Root CA"

Then, create the server (F5 WAF enforcer) files:

shell
openssl genpkey -algorithm RSA -out /etc/ssl/certs/app_protect_server.key
openssl req -new -key /etc/ssl/certs/app_protect_server.key -out /etc/ssl/certs/app_protect_server_csr.crt -subj "/O=F5/OU=app-protect/CN=mTLS"
openssl x509 -req -in /etc/ssl/certs/app_protect_server_csr.crt -CA /etc/ssl/certs/app_protect_server_ca.crt -CAkey /etc/ssl/certs/app_protect_server_ca.key -out /etc/ssl/certs/app_protect_server.crt -CAcreateserial

Finally, create the client (NGINX) files:

shell
openssl genpkey -algorithm RSA -out /etc/ssl/certs/app_protect_client.key
openssl req -new -key /etc/ssl/certs/app_protect_client.key -out /etc/ssl/certs/app_protect_client_csr.crt -subj "/O=F5/OU=app-protect/CN=mTLS"
openssl x509 -req -in /etc/ssl/certs/app_protect_client_csr.crt -CA /etc/ssl/certs/app_protect_client_ca.crt -CAkey /etc/ssl/certs/app_protect_client_ca.key -out /etc/ssl/certs/app_protect_client.crt -CAcreateserial

Update configuration files

To enable mTLS, you must add F5 WAF enforcer to a stream {} context.

Modify an existing block or create one with the following information:

nginx
stream {
    upstream enforcer {
        server 127.0.0.1:4431;
    }

    server {
        listen 5000;
        proxy_pass enforcer;
        proxy_ssl_server_name on;
        proxy_timeout 60m;
        proxy_ssl on;
        proxy_ssl_certificate /etc/ssl/certs/app_protect_client.crt;
        proxy_ssl_certificate_key /etc/ssl/certs/app_protect_client.key;
        proxy_ssl_trusted_certificate /etc/ssl/certs/app_protect_server_ca.crt;
    }
  • upstream enforcer specifices the server, which listens on port 4431 by default
  • proxy_pass indicates that requests should be routed through the enforcer upstream
  • proxy_ssl_certificate and proxy_ssl_certificate_key are for the client (NGINX) credentials
  • proxy_ssl_trusted_certificate enables the server (enforcer) verification

This stream server should then be used as the app_protect_enforcer_address value:

app_protect_enforcer_address 127.0.0.1:5000;

A fully configured file might look similar to the following example:

nginx
user nginx;
worker_processes auto;
worker_shutdown_timeout 10s; # NGINX gives worker processes 10 seconds to gracefully terminate before it will actively close connections
load_module modules/ngx_http_app_protect_module.so;
error_log /var/log/nginx/error.log notice;

events {
        worker_connections 65536;
    }

stream {
upstream enforcer {
    server 127.0.0.1:4431;
}

server {
    listen 5000;
    proxy_pass enforcer;
    proxy_ssl_server_name on;
    proxy_timeout 60m;
    proxy_ssl on;
    proxy_ssl_certificate /etc/ssl/certs/app_protect_client.crt;
    proxy_ssl_certificate_key /etc/ssl/certs/app_protect_client.key;
    proxy_ssl_trusted_certificate /etc/ssl/certs/app_protect_server_ca.crt;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    app_protect_enforcer_address 127.0.0.1:5000;

    server {
        listen 80;
        server_name localhost;
        proxy_http_version 1.1;

        app_protect_enable on;
        app_protect_policy_file app_protect_default_policy;
        app_protect_security_log_enable on;
        app_protect_security_log log_all syslog:server=127.0.0.1:514;

        location / {
            client_max_body_size 0;
            default_type text/html;
            # Pass traffic to backend
            proxy_pass http://127.0.0.1:8080/;
        }
    }
}
With a Virtual machine or bare metal installation, you have finished all necessary steps.

Modify Docker compose file

This section only applies to installations using Docker.

To use mTLS with Docker, you must modify your compose files.

The certificates folder /etc/ssl/certs/ must be added to the volumes: sections of the NGINX and WAF enforcer services.

And the individual certificate and key files must be added to the environment: section of the WAF enforcer service, using the correct names.

This example highlights the required fields:

yaml
services:
	  nginx:
	    container_name: nginx
	    image: nginx-app-protect-5
	    volumes:
	      - app_protect_bd_config:/opt/app_protect/bd_config
	      - app_protect_config:/opt/app_protect/config
	      - app_protect_etc_config:/etc/app_protect/conf
	      - /conf/nginx.conf:/etc/nginx/nginx.conf
	      - /conf/default.conf:/etc/nginx/conf.d/default.conf 
	      - /path/to/your/certs:/etc/ssl/certs
	    networks:
	      - waf_network
	    ports:
	      - "80:80"

	  waf-enforcer:
	    container_name: waf-enforcer
	    image: "private-registry.nginx.com/nap/waf-enforcer:<version-tag>"
	    environment:
	      - ENFORCER_PORT=4431
	      - ENFORCER_SERVER_CERT=/etc/ssl/certs/app_protect_server.crt
	      - ENFORCER_SERVER_KEY=/etc/ssl/certs/app_protect_server.key
	      - ENFORCER_CA_FILE=/etc/ssl/certs/app_protect_client_ca.crt
	    volumes:
	      - app_protect_bd_config:/opt/app_protect/bd_config
	      - /path/to/your/certs:/etc/ssl/certs
	    networks:
	      - waf_network
	    restart: always

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

	networks:
	  waf_network:
	    driver: bridge

	volumes:
	  app_protect_bd_config:
	  app_protect_config:
	  app_protect_etc_config: