Self-hosted server behind CGNAT and OpenVPN with third party internet access

Hi,

  • Happy self hosting:

I'm throwing a bottle into the sea today.
Although I no longer had the time to do anything other than updates on my little self-hosted server at home, it was well used, notably by my family, whom I had (with difficulty) managed to convince to switch to my matrix server (with a whatsapp bridge in addition).

That's how it worked before the outage:

Each service runs in an independent unprivileged LXC container.
So I know a lot of services were reached directly by their port when it would have been better to go through the reverse proxy. But I didn't manage to do it for all the services but it worked so, so there you go :slight_smile:

  • ISP failure and backup I try to setup:

Unfortunately my internet ADSL line has been cut for 15 days now :hot_face: and the ISP will maybe now not repair before the end of the month :scream:... or later...
A 4G box has been lent to me but obviously it is not possible to use the public IP of this 4G box to reach the server because of the 4G CGNAT.

Since then I've been trying to set up a VPN to use the Internet connection of a third party living nearby. I've managed to install a temporary OpenVPN server at his place and establish the connection from the network behind the 4G box :+1:

The new architecture is this one (what's circled in green is new):

On the left side, my house, the 4G box (A), the previous openWRT router (A1) I want to use as VPN client, the server host (A11), all the services servers inside of LXC unprivilegied containers of this host : for the tests I will use the A112 one on port 3000.
On the right side, the relative house with his Fiber Box (B), a temporary server host (B1), and inside a LXC unprivilegied containers of this host the OpenVPN server (B11).

The idea is to not touch / modify the items under the A1 router so I can easily switch back when my ADSL will be repaired.
The users will connect to the neighbor fiber Box public IP (B) and reach the server on the left side as usual through the VPN tunnel without even knowing it.

I managed to establish a connection between my OpenWRT router (A1) and the OpenVPN server (B11):

  • Problems:

I'm not working in IT and English is not my native language so it's been really hard to get some of this architecture starting to work, and I still don't understand all I have done :confused:
Some already helped me thank you, nothing wouldn't work without you.

I'm now stuck for several days, when I "tcpdump" on the service server container (A112) I see the traffic from a user connecting on the internet from the public IP of the Fiber Box (B) but the user only receive a timeout in return.
I can't access internet on the left side (A1, A11, A112) through the Fiber Box (B) connection. I always go out with the 4G box (A) public IP or loose internet access depending of the configuration I setup.
So I'm guessing the timeout error comes from an asymmetric routing?

  • Setup : In the LXC OpenVPN server container (B11):

The OpenVPN conf file /etc/openvpn/server.conf (I removed certificates lines):

port 1194
proto udp
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
push "route 10.0.4.0 255.255.255.0"
client-config-dir ccd
route 192.168.11.0 255.255.255.0#Not sure if I should do this
#;push "redirect-gateway def1 bypass-dhcp"### If I set this I have no more internet on the left side (A1, A11, A112)
#;push "dhcp-option DNS 208.67.222.222"### Should I uncomment dhcp-option?
#;push "dhcp-option DNS 10.0.4.1"### Should I uncomment dhcp-option and set the temporary server host as DNS server?
client-to-client
keepalive 5 60
cipher AES-256-GCM
max-clients 5
max-routes-per-client 50
stale-routes-check 300
verify-client-cert require
auth SHA512
tls-server
tls-version-min 1.3
remote-cert-tls client
#;push "block-outside-dns"### Should I uncomment this line?
user openvpn
group openvpn
persist-key
persist-tun
verb 3
mute 10
explicit-exit-notify 1

/etc/openvpn/ccd/client :
iroute 192.168.11.0 255.255.255.0

I activated net.ipv4.ip_forward=1
And activated masquerate iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

I don't know if I should do some more? like:

iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -j ACCEPT

  • Setup : In the OpenWRT router (A1):

Network-specific OpenVPN client configuration:

client
dev tun
remote zzz.zzz.zzz.zzz 1194 udp
auth SHA512
cipher AES-256-GCM
tls-client
tls-version-min 1.3
remote-cert-tls server
connect-retry 5 600
resolv-retry infinite
nobind
verb 3
mute 10
explicit-exit-notify 1

I followed parts of this guide https://openwrt.org/docs/guide-user/services/vpn/openvpn/client-luci to setup the VPN tunnel on the openWRT (A1) VPN client.

I tried the both possibilities:

LuCi (config 1st try):

Firewall => Wan => Edit => Advanced Settings => Covered devices => tun0

LuCi (config 2nd try):

Interfaces => Add new interface => OpenVPN, Unmanaged, device tun0, Pas de Bring up on boot

I'm not sure what I should do more like:

Firewall => General settings => Zones add => vpn, Input : accept, output : accept, intra zone forward : accept, no masquerating, covered network : OpenVPN,

Allow forward to dest zones : lan
Allow forward from source zones : lan

The default route still go to the 4G Box (A) so I guess this is why I have trouble getting internet from Fiber Box (B).

  • Configuration to correct/improve:

I tried to resume all configurations on this architecture:

Here's what I'd like to do:

  • The server behind the 4G Box in CGNAT should be accessible from the outside through the VPN.
  • Get internet access on the left network when the VPN is connected (via the 4G Box or via the third-party connection, whatever.)
  • Don't miss anything about the security of the system
  • Understand what I'm doing (yes, because I followed at least 6 tutorials to set up the VPN, with some points still unclear).

Questions:

  1. Do I need to do a "MASQUERADE" anything other than the openVPN server (B11)? (The servers' LXC hosts have apparently already a "MASQUERADE" on everything that isn't respectively in 10.0.3.0 and 10.0.4.0)

  2. Do I need to pass "net.ipv4.ip_forward=1" elsewhere than on the openVPN server container (B11)? (Apparently this is already the case by default on router A1)

  3. On the openVPN server container (B11), does the DNAT on port 3000 have to go to the router (A1) or directly to the host server (A11) or container (A112)? (This seems to work with the server host (A11))

  4. On openVPN server configuration (B11):

  • Should the "redirect-gateway def1" push be enabled? If so, how do I avoid losing Internet access on the client side?
  • Is it necessary to activate the "block-outside-dns" push?
  1. I've tried to follow parts of this page https://openwrt.org/docs/guide-user/services/vpn/openvpn/client-luci to configure the router (A1) as an openVPN client. Is there anything else I need to do on this router (A1), such as:
  • Allow "FORWARD"?
  • a "MASQUERADE"?
  • Other?
  1. Is it possible / recommended to make different paths between input and output? For example, the outside user connects via the fiber box (B), but the server responds via the CGNAT 4G box (A)? (For gain in speed if it doesn't complicate things)

  2. Do I need to manually create routes (with ip route ccc.ccc.ccc.0/24 via ddd.ddd.ddd.ddd dev xxx) when configuring the network (interfaces file or eth0.network) already defines a default gateway with this network ddd.ddd.ddd.ddd?

  3. How can I make the output to the Internet on the CGNAT side (A1, A11 and A112) pass through the box on the fiber side (B)?

  4. What append if the two internet boxes (A) and (B) were both on a 192.168.1.1 (different) network, would it be a problem?

  • Thank you:

Anyway, thank you very much for reading this far ^^.
I hope this will help others too :slight_smile:
(And sorry for my poor English level)

Haven’t had time to crawl through your whole config, but I’d probably just do one of these and call it a day:

Cloudflare tunnel: one command, your service phones home to Cloudflare and is live on a real domain…. no port-forwarding, TLS handled for you.

Tailscale (or any mesh VPN): install it on the router/NAS/Pi, it punches through CGNAT on its own…

WireGuard site-to-site: router dials out to a €3 VPS (or your neighbour’s line) and the vps forwards whatever ports you need back to you.

All three rely on outbound traffic, so CGNAT stops mattering and you avoid the firewall gymnastics.

1 Like

Tailscale is pretty good...

For some - consider ZeroTier

1 Like

Using WireGuard instead of OpenVPN is much easier but zerotier or tailscale from the NAS is also a viable option.

For WireGuard setup see:

Note that for your current OpenVPN you have to route back to your 192.168.11.0/24 network from you neighbor
For the OpenVPN at your neighbour you need route and iroute:

Site-to-site
In a setup where a single server can handle many clients, it is sometimes necessary to set per-client options that overrule the global options, or to add extra options to a particular client. The option client-config-dir is very useful for this. It allows the VPN administrator to assign a specific IP address to a client, in order to push specific options such as a DNS server to a particular client or to temporarily disable a client altogether.
This option is also vital if you want to route a subnet from the server side to the client side.

Add to the OpenVPN servers config file:
#set option for CCD dir in openvpn config:
client-config-dir /etc/openvpn/ccd

This example has the LAN subnet of the server to be 192.168.6.0/24, the LAN subnet of the client is 172.18.18.0/24
Push server side LAN subnet to clients by adding redirect default gateway or
push "route 192.168.6.0 255.255.255.0 vpn_gateway"

Instruct server to add a route to the client-side LAN for all local server side clients:
route 172.18.18.0 255.255.255.0 vpn_gateway

From Command line:
#Make ccd directory
mkdir /etc/openvpn/ccd

#Make DEFAULT file which is used if no named file is used so only suitable if there is just one VPN client, If the server serves multiple clients, certificate authentication must be used with a unique certificate for each client. The CN of the certificate matches the file name for that client in the ccd.
touch /etc/openvpn/ccd/DEFAULT

#Add iroute to DEFAULT ccd file
echo "iroute 172.18.118.0 255.255.255.0" > /etc/openvpn/ccd/DEFAULT

Firewall
The firewall on the Client side must be setup as if it is a OpenVPN Server, so with ACCEPT on INPUT and FORWARD and no Masquerading.

If that is working you can set up a port forward from your neighbor to your server at 192.168.11.x

For your client the easiest is to route all traffic via your neighbor if that is not feasible you need Policy Based Routing

1 Like

Thank you for your answers

I'm a little confuse, the 3 solutions you listed aren't equivalent to the one I'm trying to make work?
I use Openvpn tunnel and an other ISP connection compatible with port routage to go through the 4G CGNAT.
I will still have to do some firewall gymnastics for all solutions, or did I missed something?

Taiscale isn't just wireguard inside with tiers outputs outside? I really don't want to depend on GAFAMs or non trusted cloud/devices for my network.
Wireguard is a solution similar as the one I'm trying to make work with OpenVPN or am I missing something?
Same questions about zero Tiers, would it be better than OpenVPN in my case?

So wireguard, zerotiers and tailscale are in my case equivalents but easier to setup?
I would like to avoid starting from the beginning again if it's possible to stick to OpenVPN.

Can you please check but I think I have already done all this. Did I do it wrong on some particular point?
For example:

push "route 10.0.4.0 255.255.255.0"#inside /etc/openvpn/server.conf
route 192.168.11.0 255.255.255.0#inside /etc/openvpn/server.conf (I tried too instead ip route 192.168.11.0/24 via 10.8.0.3 dev tun0 on the OpenWRT server directly too)
iroute 192.168.11.0 255.255.255.0#inside /etc/openvpn/ccd/client (client name match conf file name)

Already do it too I thing. When an user connect from the internet the good server container see the traffic incoming. But the user get a timeout in return. So I think I'm close no?

WireGuard is the same as you are doing now with OpenVPN with your neighbor but much easier to setup.

zerotier, tailscale and cloudfare use a commercial provider as Man in the Middle, for which you are now using your neigbor.
If you do not want a commercial provider the use your neighbor but use WireGuard instead of OpenVPN if you cannot get that going

For now you have to add to your openvpn client:

redirect-gateway def1

This will route all traffic back to the server, maybe that is not the solution you want for the long run but use it for testing, if that works you can setup PBR

Furthermore your firewall setting should allow incoming traffic for you openvpn client easist is to just add the tun device to the lan zone

Key thing here is that there are options beyond OpenVPN, and those options are more performant and flexible...

ZeroTier and Tailscale - for those on CGNAT and/or 4g/5G Fixed Wireless - these are options that can do the hole-punching and get back to the home network.

there's also algo from trailofbits if one can get a VPS...

Thank you for your help, I think I'm very very close now.

Last week I had had even managed to have the services (on servers containers on the left side) accessible from an user accessing through the Fiber Box (right side)(B) Ip.
I still had internet access on the CGNAT side (left side) and the external ip seen was the ip of the Fiber box (right side).
On the OpenWRT router (A1) I had added the tun0 device to the LAN firewall zone ("covered device" field) as you suggested.

Unfortunately this exact same day my ssl certificates expired and I spend some more days making the new ones, I did some errors like forwarding 80 and 443 ports without specifying the way so it was a big mess and I lose internet for a while on the right side because of that...
It took me time to figure out and has you guess I did have time to try many many things. (For example I tried to disable ipv6 on different host / container / router / box)

Now I have understood and corrected this forwarding errors, renewed my ssl certificates and I managed to get back services from the Fiber Box Ip (right side) (B). But when I connect the VPN tunnel I loose internet on the left side again.

Something strange too, when I start the VPN tunnel, on the openVPN server (B11) it add a wrong route. 192.168.11.0 via 10.8.0.2 instead of 10.8.0.3 and I can't figure out where this error come from.

Where should I start to look, and should I do this on the OpenVPN server (B11)? :

iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -j ACCEPT

Can you install static routes in the fiber box B?

Doing that and using symmetric routing throughout will make it possible to forward a port once in box B and the packet will make it all the way to the server in house A (and back) without further NAT/deNAT.

In box B:
10.0.3.0/24 via 192.168.1.6
10.0.8.0/24 via 192.168.1.6
Port forward(s) as desired from WAN to the appropriate 10.0.3.X IP.
In box B1
10.0.3.0/24 via 10.0.8.3
(the route to 1.0.8.0 via VPN already exists)
In box A1
10.0.3.0/24 via 192.168.11.5
Disable NAT into the VPN tunnel.
That gets the packet there. The quick and dirty way to get the server's reply packets back to house B and out on that public IP is to make the default route in A1 10.0.8.1. This does make any Internet use involving router A1 VPN back to connection B. You can use conditional routing to prevent this. If you do prefer that you will need to include 192.168.11.0 in the routes all the way back for devices on the wifi network and A11 itself to reach the Internet.

The Debian boxes need the kernel IPv4 forward flag set but do not set up any NAT in them.

Note that LTE box A and its 192.168.0.0 network needs no special configuration as it handles only the outgoing connection of encrypted packets and can be treated as part of the Internet. This network does need to be a unique subnet (as it is) so that plain destination-based routing in A1 works.

It is exactly as already noted by me and former speakers
Do not NAT/Masquerade
The server side must have a static route and iroute to site B
OpenVPN can do this all for you
On the Server side A add in the openvpn config add:

push "route 10.0.4.0 255.255.255.0 vpn_gateway" #in /etc/openvpn/server.conf, pushes route to the client for return traffic
route 192.168.11.0 255.255.255.0 vpn_gateway #/etc/openvpn/server.conf, makes static route to the client side via the vpn 
iroute 192.168.11.0 255.255.255.0 vpn_gateway # /etc/openvpn/ccd/client (client name match conf file name) openvpn specific to signal which client to set routes for

This takes care of all the necessary static routes

On the client side add in the OpenvNP config:

redirect-gateway def1

That takes care of the routes

About firewall, both sides have to be setup ad a server e.g. allowing traffic.

On side A add the VPN interface or device to the LAN zone, if you are unsure add device tun+ to the lan firewall zone this will cover all tunX devices of OpenVPN, for testing purposes this is OK

On the server side for firewall you have to allow input and forward traffic on the tun interface these are my setting, my tun interface is tun2 but yours is probably tun0 (if you have specified just device tun in the OpenVPN config, then OpenVPN takes the first available number in case of the first or only one that is tun0, of course better is to hard-code the tun number but that is for the advanced class :wink: )
My OpenVPN port number is 1195 yours might be different
Where it states $(get_wanface) you use the ethernet wan interface
If you do not have IPv6 then those rules are redundant

ip6tables -I INPUT -p udp --dport 1195 -i $(get_wanface) -j ACCEPT
ip6tables -I INPUT -i tun2 -m state --state NEW -j ACCEPT
ip6tables -I FORWARD -i tun2 -m state --state NEW -j ACCEPT
iptables -I INPUT -p udp --dport 1195 -i $(get_wanface) -j ACCEPT
iptables -I INPUT -i tun2 -m state --state NEW -j ACCEPT
iptables -I FORWARD -i tun2 -m state --state NEW -j ACCEPT

1 Like

+1 for Tailscale. Setup is so easy, it almost configures itself. (No good if you want a hard setup ha.)

For privacy, it will always attempt to create a direct connection between nodes first, then fallback to relay thru their servers if unable. And FREE for up to three clients (but no limit when one or more is a router).

Using with two nodes behind CGNAT and one IPV4 for over six months with no issues and great performance.