Nginx access list for a local service behind VPN connection is not filtering by VPN IP but by remote IP

We have a Pritunl VPN server running on 192.168.0.201. It uses OpenVPN and has one network, 192.168.243.0/24.
On 192.168.0.251, an nginx reverse proxy is running to expose some websites running in Docker.

Our goal is to be able to connect to these services only when on the local network (192.168.0.0/16). Hence, in Nginx, an access control list is configured to only allow connections from 192.168.0.0/16. This works for a device on the local network.

However, when a remote device connects through the Pritunl VPN, Nginx does not see the server ip 192.168.0.201 nor a local ip 192.168.243.0/24 as the Client IP, but rather it sees the original device’s IP:

[30/Nov/2024:13:06:46 +0000] - - 403 - GET https [...] "/" [Client 188.65....]

As such, nginx is returning 403 Forbidden responses as the IP is not allowed by access lists. But then, if I google “what is my ip” when connected through the VPN, it does show the VPN server’s public IP address.

Now we could solve this by not proxying the local service through nginx and instead just publishing the port through Docker, but we’d like to be able to use a domain name to connect rather than an IP address, which is made much easier with nginx.

So the question is, is this behaviour configurable? Can we make it so that any request going through the VPN has Client IP 192.168.0.201 or 192.168.243.0/24?

EDIT: FYI, I’m using a generic OpenVPN client to connect, not the pritunl client.

The local network needs to be included in the routes, the 0.0.0.0/0 route should be removed unless it is required.

The web server will need to either be accessed with the correct local IP address or with a DNS name with the correct IP address. If the DNS name has a public IP either a different domain or a custom DNS server to remap it will need to be configured.

Hey Zach!
Thanks for getting back and sorry for my late response.

I removed the default 0.0.0.0/0 route and added the appropriate route for the local network, but I’m still getting the same behaviour - nginx reports the remote IP as the client, even though I am definitely connecting through the VPN (nginx is only accessible from the local network).

What do you mean by saying “accessed with the correct local IP address”?

Can you access or curl websites from the Pritunl VPN server (192.168.0.201)? Then please show the nginx logs.

Sure!

Curl from the 192.168.0.201 returns the html for the webpage and causes the log message:

[17/Dec/2024:11:20:41 +0000] - 200 200 - GET https [...] "/" [Client 192.168.0.1] [Length 41922] [Gzip -] [Sent-to links] "curl/7.74.0" "-"

Curl from my machine over the VPN (IP 192.168.243.2 according to Windows’ ipconfig) gives a simple ‘403 Forbidden’ and corresponding log:

[17/Dec/2024:11:21:45 +0000] - - 403 - GET https [...] "/" [Client 80.1...] [Length 150] [Gzip -] [Sent-to links] "curl/7.88.1" "-"

Perhaps a bit curiously, curl from the Pritunl VPN host (192.168.0.201) puts the client as the gateway?
I don’t know that much about networking, unsure if that’s weird or expected behaviour.

It’s likely not the clients public IP, it would be the Pritunl servers public IP or local IP if the Nginx server is being access on a local IP. To get the client VPN virtual IP instead would require a non-NAT configuration. The server needs an enterprise subscription and the NAT option for the route that contains the nginx server needs to be disabled. Then the VPN virtual subnet needs to be routed on the local routing table.

I had to double-check, but it actually is the public IP of the client. Note also how in my original post it was 188.65.... and in yesterday’s post it was 80.1... - these are the public IP addresses of the different networks I was testing this out from.

That being said, I guess what you say about NAT configuration being an enterprise-only feature still holds true?

When the routes use NAT it is going to use the IP address of the server either the public or private one.