Welcome! Log In Create A New Profile

Advanced

Preserving Source IP using SSL Preread + OpenVPN

Posted by ilikeweb 
Preserving Source IP using SSL Preread + OpenVPN
April 11, 2017 05:16AM
Please if anyone could help. I am trying to setup an OpenVPN server running on 443 tcp behind an Nginx 1.11.13 proxy while preserving the source IP.

I have searched long and far and my nginx.conf is currently as follows:

stream {

map $ssl_preread_server_name $name {
mydomain.com backend;
default default-backend;
}

upstream backend {
server 10.100.0.102:443; <-- Backend nginx server
}

upstream default-backend {
server 192.168.1.154:443; <-- OpenVPN server
}

proxy_protocol on;

server {
listen 443;
proxy_pass $name;
ssl_preread on;
}

Leaving proxy_protocol off (or commented out) I am able to connect to my OpenVPN server on 443 without any issues. Only problem is all forwarded requests show the IP address of this nginx server 10.100.0.102. I am unable to filter or deny requests based on IP since its all from myself.

Upon turning proxy_protocol on I can now see all requests from source IP using real_ip_header proxy_protocol. Only problem is now OpenVPN is unable to connect due to TRANSPORT_ERROR "NETWORK_EOF_ERROR".


Does anybody know a way to allow connections to an OpenVPN server behind an Nginx proxy while preserving source IP?
Please help.
Re: Preserving Source IP using SSL Preread + OpenVPN
April 11, 2017 05:42AM
Define an extra server {} block, enable proxy protocol there, move proxy_pass to server block, call server block from map.

---
nginx for Windows http://nginx-win.ecsds.eu/
Re: Preserving Source IP using SSL Preread + OpenVPN
April 12, 2017 03:56AM
Thanks for the reply. I have created a new $ssl_preread_server called $test and a new server block with proxy_pass $test and proxy_protocol set to on but I am unable to set it to listen on 443 it says port in use. So I attempted to listen on 4443 but then it never gets any requests that come from https on the mydomain.com specification.

So I figured I set mydomain.com and then its upstream back to the same server on 4443 and then from there have my $test ssl preread forward to the backend (10.100.0.102) server using proxy_protocol on but I am left with the original issue of not seeing a source IP because of the initial non proxy_protocoled forward :|

So what can I do here? How exactly do I get both my server blocks with the two different proxy_pass' to listen on 443 so that it can forward source ip requests to backend NGINX + use OpenVPN on 443?

Thank you



Edited 1 time(s). Last edit at 04/12/2017 03:57AM by ilikeweb.
Re: Preserving Source IP using SSL Preread + OpenVPN
April 12, 2017 05:07AM
https always has a name, vpn/ssh does not so make the map(ping) work this way.
internal passing on (443->map->1443->proxy_pass with proxy protocol...)

---
nginx for Windows http://nginx-win.ecsds.eu/
Re: Preserving Source IP using SSL Preread + OpenVPN
May 19, 2017 06:09PM
I am trying to accomplish a similar if not same task. I am running nginx 1.12.0 and am trying to maintain the source IP for logging purposes so that fail2ban can block the nefarious characters trying to compromise my system. I have a single server where I am trying to host a vpn server and a web server. To prevent my VPN from being blocked/filtered I want to run it on port 443 in tandem with a secure website.

Based on the last post I have two servers listening, port 443 and 1443. port 443 will forward to my vpn or 1443 for web traffic. This supports OpenVPN and my web server, but I have lost the source IP. Am I missing something? Here is my current config:

stream {
log_format basic '$remote_addr [$time_local] '
'protocol: $protocol Status: $status bytes sent: bytes_sent bytes received: $bytes_received '
'session duration: $session_time';

map $ssl_preread_server_name $name {
www.example.com pre_www_server;
example.com pre_www_server;
default vpn_server;
}

upstream vpn_server {
hash $remote_addr consistent;
server localhost:1194;
}

upstream pre_www_server {
server localhost:1443;
}

upstream www_server {
server localhost:8443;
}

server {
listen 1443;
proxy_pass www_server;
proxy_protocol on;
}

server {
listen 443 so_keepalive=on;
access_log /var/log/nginx/stream-access.log basic buffer=32k;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_pass $name;
ssl_preread on;
}
}

Again the goal is to capture the source IP so that I can filter IPs that appear as a threat while hosting an https website and an OpenVPN server from port 443 on one IP.

Thanks,
Phil
Re: Preserving Source IP using SSL Preread + OpenVPN
May 20, 2017 01:34AM
The proxy_protocol 'passing' needs to be supported by the vpn/sshd service, not many can handle this (yet).

---
nginx for Windows http://nginx-win.ecsds.eu/
Re: Preserving Source IP using SSL Preread + OpenVPN
June 16, 2017 02:17PM
I have attempted this as well but so far no luck. I feel it is close but something may be missing.
Currently I have it listening on multiple ports in the stream block, and passing it back to the same server locally on another port.
I am able to view the source IP successfully on the website request, however not the VPN request.
Currently the only error I am experiencing is the one below.
This is a windows RAAS VPN Server.

10.0.0.55 is the NGINX server handling all of the request.
I have attached a screenshot of both the error and access logs.
For some reason the website request are preserving the IP but the VPN request are not.

Here is my web server block.
server {
listen 9999 ssl proxy_protocol;
ssl_certificate *;
ssl_certificate_key *;
server_name webserver;
set $upstream 10.0.0.62;

location / {
proxy_pass_header Authorization;
proxy_pass https://$upstream;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
client_max_body_size 0;
proxy_read_timeout 300;
proxy_connect_timeout 300;
index index.html index.htm;
include /etc/nginx/mime.types;
}


Here is the VPN server block
server {
listen 9997 ssl proxy_protocol;
ssl_certificate *;
ssl_certificate_key *;
server_name vpn;
location / {
proxy_pass https://192.168.2.3:443;
proxy_pass_header Authorization;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
proxy_set_header Connection "";
proxy_buffering off;
proxy_connect_timeout 10;
}
}

Here is my stream block
stream {
map $ssl_preread_server_name $name {
server1.com server1;
server2.com server2;
server3.com server3;
}

upstream server1 {
server 10.0.0.55:9997;
}

upstream server2 {
server 10.0.0.55:9999;
}

upstream server3 {
server 10.0.0.55:9998;
}
server {
listen 443;
proxy_protocol on;
proxy_pass $name;
ssl_preread on;
}
}
Attachments:
open | download - error1.png (34 KB)
open | download - access1.PNG (49.7 KB)
Re: Preserving Source IP using SSL Preread + OpenVPN
July 16, 2017 01:39AM
I am still looking to get this to work as well. Would love if someone posted a working example config. I am still trying with OpenVPN on 443
Re: Preserving Source IP using SSL Preread + OpenVPN
July 01, 2018 03:26AM
i managed to do this, but not with nginx alone:

My setup:

Single Server with OpenVPN and HTTPS both on Port 443.
Additionally: since many firewalls with deep-packet-inspection (DPI) will block the OpenVPN Traffic even when on Port 443 i wanted a solution for OpenVPN where all packets _can_ be layered within regular SSL Traffic, which will look like Plain HTTPS to a DPI-Firewall. This special scenario requires that the client-device will layer all packets within SSL, this can be done using "stunnel" or "ssldroid" but is not covered here.


So my Port 443 will go by these rules:

Input on Port 443 -> is it OpenVPN Traffic? -> Keep real remote IP -> Forward to Port 1194 [OpenVPN Daemon]
Input on Port 443 -> is it non OpenVPN Traffic? -> is the requested Domain "sslvpn.mydomain.com"? -> Unpack (remove SSL-Layer) Traffic -> Forward to Port 1194 [OpenVPN Daemon]
Input on Port 443 -> is it non OpenVPN Traffic? -> is the requested Domain NOT "sslvpn.mydomain.com"? -> Keep real remote IP -> Forward to Port 4443 [Nginx Vhosts]

I managed to keep the real remote IPs in all cases but the one where i need the layering of OpenVPN-Packets within SSL. This is the only flaw, but i can live with it since usually i only use OpenVPN on Port 443 directly and need my Webroots up and running.

Tools i used:
OpenVPN 2.4.6
Nginx 1.12.2
sslh 1.18

While Nginx has the ability of forwarding the realip, this is only true for the proxy_protocol, which many applications don't support.
sslh can transparently forward traffic while keeping the realip using netfilter.

Important: in the version i used, sslh has to be configured on IP-Addresses bound to Interfaces other than localhost. Do not use 127.0.0.1, this will not work! In my setup, the server has the IP 192.168.1.251.

sslh is started with these params:
# sslh -p 192.168.1.251:443 --openvpn 192.168.1.251:1194 --anyprot 192.168.1.251:9443 --transparent

This means, sslh will listen on port 443 and redirect openvpn traffic to 1194 (OpenVPN Daemon) and everything else to 9443 (one of the nginx listening-ports).
The transparent parameter will initiate the netfilter/caps module which is important.

For this to work we need some rules for iptables:

# iptables -t mangle -N SSLH
# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 1194 --jump SSLH
# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 9443 --jump SSLH
# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
# iptables -t mangle -A SSLH --jump ACCEPT
# ip rule add fwmark 0x1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100

Replace "eth0" with whatever your interface is called.

The OpenVPN-Server config is not special, it's only required that it is running on Port 1194 (or you have to adjust the config above) and that it is listeing in TCP-Mode.

Now to the nginx part (snippets):

stream {
map $ssl_preread_server_name $name {
sslvpn.mydomain.com sslvpn_backend;
default https_backend;
}

server {
listen 9443;
proxy_pass $name;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_protocol on;

ssl_preread on;
}

upstream sslvpn_backend {
server 127.0.0.1:8443;
}

server {
listen 127.0.0.1:8443 ssl proxy_protocol so_keepalive=on;

ssl_certificate /etc/letsencrypt/live/sslvpn.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sslvpn.mydomain.com/privkey.pem;

proxy_protocol off;
proxy_connect_timeout 300s;
proxy_timeout 300s;
proxy_pass openvpn-ssltunnel-inet-only;
}

upstream openvpn-ssltunnel-inet-only {
# route traffic back to openvpn
server 127.0.0.1:1194;
}

upstream https_backend {
server 127.0.0.1:4443;
}
}


The first block will initiate listening on port 9443. There i turn on the proxy_protocol (important to keep the real-ip) and also start "ssl_preread", to inspect the requested domain.
The mapping is quite easy: if the domain was "sslvpn.mydomain.com" use the "sslvpn_backend", in all other cases use the default "https_backend".

The sslvpn_backend just redirects the traffic to port 8443 where the server is a simple stream server with ssl-layering, but also disables the proxy protocol. afterwards the traffic is routed back to port 1194, where the OpenVPN-Daemon can now parse the unpacked traffic.

The "https_backend" is just routing all traffic to the destination 4443 where all "real" https-vhosts of nginx reside.

A typical vhost looks like this:

http {
server {
listen 127.0.0.1:4443 ssl http2 proxy_protocol;

set_real_ip_from 127.0.0.1;
real_ip_recursive on;
real_ip_header proxy_protocol;

include includes/ssl.conf;
ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;

server_name www.mydomain.com;

access_log /var/log/nginx/www_mydomain_com.access_log main;
error_log /var/log/nginx/www_mydomain_com.error_log info;

root /var/www/mydomain/htdocs;

try_files $uri $uri/ /index.php?$args;

include includes/expires.conf;

include includes/php-fpm.conf;
}

}

The vhost will listen on 4443, activate the submodules ssl and proxy_protocol.
Within the vhost the realip will be decoded (available via proxy_protocol).
That way, within the logs (access and error) the real-remote-ip will be visible and even within PHP in the $_SERVER['REMOTE_ADDR''] variable.

I hope this setup can help someone like me.

Best regards,
David
Re: Preserving Source IP using SSL Preread + OpenVPN
August 20, 2018 12:20PM
Thank you for sharing this solution. I will try it out.
Sorry, only registered users may post in this forum.

Click here to login

Online Users

Guests: 177
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready