Overview
I have an Nginx reverse proxy setup pointing to a SaaS application (BigCommerce). While my configuration works great, I'm not able to ensure the client IP shows in the SaaS backend instead of the Reverse Proxy IP. In the SaaS backend, there's no mechanism to add a list of trusted IP addresses or use set_real_ip_from
and real_ip_header
so instead I've been tasked to implement proxy_protocol
on the Reverse Proxy in order to ensure IP headers are passed through the Reverse Proxy with the client's IP instead of its own.
Configuration
In http
server
context
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
# support http 1.1 persistent connections
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
Issue
When I enable proxy_protocol
on my listen
directive within the http server context (e.g. listen 443 ssl http2 proxy_protocol
), I receive a Broken Header
error in nginx and a connection_reset
in my browser.
Additonally, I've tried enabling proxy_protocol
via the stream
context as it's specified in "Accepting Proxy Protocol" docs by Nginx:
In stream
context
stream {
server {
listen 12345;
proxy_pass example.com:12345;
proxy_protocol on;
}
}
I'm unfamiliar with proxy_protocol
so I don't know what I'm missing to get this working.
Technically speaking, I'm really trying to make my reverse proxy a 'transparent proxy' but it doesn't seem to work that way with the SaaS Backend because it's logging the proxy IP instead of the client IP.
Best Answer
You are looking in the wrong direction:
The
listen 443 ssh http2 proxy_protocol;
directive (cf. listen) enables the parsing of the Proxy Protocol on incoming connections. Your browser does not speak the protocol, hence theBad Header
error. If you want to see a minimal example of Proxy Protocol use:and type:
followed by two blank lines.
The
proxy_protocol on;
directive (cf. proxy_protocol) would enable the Proxy Protocol between Nginx and the SaaS server. The latter, however, does not support the Proxy Protocol as you say. You'll need to implement it.However, you have a third solution, which works if nginx is on the path between the SaaS server and the Internet:
Use the
proxy_bind
directive (cf. proxy_bind) to tell nginx to spoof its source address (to use the one from the client):nginx can easily spoof the source address, but the response packet from the SaaS server will be routed to the client instead of nginx. Therefore you need to intercept the packets coming from the SaaS server. You need a new routing table, let's call it
transparent
by adding:to
/etc/iproute2/rt_tables
. Then you need to accept any address as local:finally you need to add a firewall rule to force the usage of the routing table
transparent
on all traffic coming back from the SaaS server (let's assume its address is10.10.10.10
):