Transparent proxy with nginx and pf on FreeBSD

I want to ensure that communication on ports other than 80 is valid HTTP. Since nginx can also be used as a transparent forward proxy, I installed it on a firewall running pf on FreeBSD 12.

Steps

  1. Configure and install nginx:
    # make -C /usr/ports/www/nginx config install clean
    
    Uncheck any options but these:
  2. Edit /usr/local/etc/nginx/nginx.conf and add a new server block to the http context:
    http {
        ...
        server {
            listen             127.0.0.1:8011 accept_filter=httpready reuseport;
            proxy_pass_header  server;
            proxy_pass_header  date;
            resolver           127.0.0.1;
            location / {
                proxy_pass  $scheme://$http_host$request_uri;
            }
        }
        ...
    }
    
    Using this configuration, nginx will preserve the Server: and Date: header fields in the response from the origin server. Additionally, it will make use of the accf_http kernel module which buffers new connections until it has detected a complete HTTP request.
    This also assumes that you run a dns resolver on localhost.
  3. Activate accf_http during system boot:
    # echo 'accf_http_load="YES"' >> /boot/loader.conf
    
  4. Start nginx when the system boots, too:
    # sysrc nginx_enable="YES"
    
  5. Edit /etc/pf.conf. Redirect any requests from your clients to port 8080 to port 8011 on the firewall, and allow outgoing communication to port 8080 if initiated by the user www under which nginx runs:
    rdr on $LANIF inet proto tcp from port >= 1024 to ! ($LANIF) port 8080 -> (lo0) port 8011
    pass out quick on $WANIF inet proto tcp from port >= 1024 to port 8080 user www
    
    Replace $LANIF by the name of the interface where your local network is connected to, i.e. where your clients reside on. Likewise, replace $WANIF by the name of your external interface.
  6. Start pf when the system boots:
    # sysrc pf_enable="YES"
    
  7. Now either reboot, or start everything manually:
    # kldload accf_http
    # service nginx start
    # service pf start
    

If you now use this firewall as your default gateway, you can open webpages that are served on port 8080 whilst non HTTP traffic to port 8080 is rejected by nginx.