Reverse proxy SSL connections and retain the originating IP address without terminating SSL at the mid-point. This makes use of the PROXY protocol.
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.
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.
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.
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.