Skip to content

Instantly share code, notes, and snippets.

@0xdade
Created February 13, 2020 04:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save 0xdade/13bd0c0156a3a1979cac82221f137444 to your computer and use it in GitHub Desktop.
Save 0xdade/13bd0c0156a3a1979cac82221f137444 to your computer and use it in GitHub Desktop.
Reverse Proxying with PROXY PROTOCOL in Nginx

Summary

Reverse proxy SSL connections and retain the originating IP address without terminating SSL at the mid-point. This makes use of the PROXY protocol.

Testing Configuration

This example setup uses nginx version: nginx/1.14.0 (Ubuntu) as it ships out of the box with ubuntu 18.04.4 LTS. It was last tested on 2020-02-12.

Example Configuration to log originating IP

DNS example.com points to 192.168.10.10 192.168.10.10 has nginx installed with this in /etc/nginx/nginx.conf OUTSIDE OF the http block

[...]
stream {
    server {
        listen 443;
        proxy_pass 192.168.11.11:443;
        proxy_protocol on;
    }
}
[...]

Host 192.168.11.11 has nginx installed with the SSL certificate and key for example.com. The configuration file might look like this:

/etc/nginx/sites-enabled/example.com.conf

server {
    listen 80 proxy_protocol;
    listen [::]:80 proxy_protocol;

    root /var/www/html;
    index index.html;
    server_name example.com;

    listen [::]:443 ssl ipv6only=on proxy_protocol; # managed by Certbot    
    listen 443 ssl proxy_protocol; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

In order to see the originating client IP address, we have to modify the access log format:

/etc/nginx/nginx.conf

[...]
http {
[...]
    ##
    # Logging Settings
    ##
    log_format proxy '$proxy_protocol_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent "$http_referer" ' '"$http_user_agent"';        
    access_log /var/log/nginx/access.log proxy;
    error_log /var/log/nginx/error.log;
[...]
}

The key element here is $proxy_protocol_addr which contains the originating client IP address.

nginx SSL termination for an application

Another example might be to have nginx terminating the SSL for your application server and you want your application server to know the IP address without having to make your application aware of the PROXY protocol.

/etc/nginx/sites-enabled/example.com.conf

server {
    root /var/www/html;
    index index.html;
    server_name example.com;

    listen [::]:443 ssl ipv6only=on proxy_protocol; # managed by Certbot    
    listen 443 ssl proxy_protocol; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    location / {
        proxy_pass 127.0.0.1:5000;
        proxy_set_header X-Forwarded-For $proxy_protocol_addr;
    }
}

Now your application can read the originating client IP address via the X-Forwarded-For header.

Advanced Usage: nginx real_ip module

I don't have an example of this ready yet, but if you are dealing with a backend application that doesn't know about proxy addresses then you may need to set the real client IP address to that of the pre-proxy origination address. This can be achieved with nginx's real_ip module, which does not ship with nginx out of the box on ubuntu and requires a custom build of nginx. An example configuration, however, might look like so:

/etc/nginx/sites-enabled/example.com.conf

server {
    root /var/www/html;
    index index.html;
    server_name example.com;
    set_real_ip_from 192.168.10.10/32;
    real_ip_header proxy_protocol;
    
    listen [::]:443 ssl ipv6only=on proxy_protocol; # managed by Certbot    
    listen 443 ssl proxy_protocol; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    location / {
        proxy_pass 127.0.0.1:5000;
    }
}

This configuration should replace the client address with the address provided by the proxy_protocol, which is equivalent to $proxy_protocol_addr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment