I have recently moved to a new flat and I love it, though unfortunately, not all is perfect. One would expect central London (literally 500m away from the actual geographical centre) would have the best internet connection London has to offer. Well, one would be wrong; I am no longer able to get anything better than lousy ADSL2+, and this amazing offering comes at a high price point, a long contract and a month's wait for the installation.
This has led me to choose a 4G internet provider. The connection is mostly better than what I would have expected to get with any of the landline providers, is much cheaper and I had it up and running less than 24hrs after I joined.
There is a problem though: they use a carrier-grade NAT which means I was no longer able to access my home server from outside my home network. Luckily I have a VPS that is not behind a NAT, so a solution was obvious, tunnel everything through there.
One easy solution to my problem would be to just run a VPN, connect my home server to it, and then connect to the VPN every time I want to access my home server. This solution works, but it is very annoying to have to connect to the VPN every time, and even more annoying to have to give access to my VPN to anyone who needs access to my private Git server.
This made me choose a slightly more complex solution that solves the above issues: port forwarding some ports from another member of the VPN that has a unique external IP to my home server.
Setting up OpenVPN
From now on we will call the externally available server EXT and the one we would like to expose: HOME.
With that being said, there are a few important OpenVPN configurations you should make sure to have in your OpenVPN server (EXT):
# ... SNIP ... # Allow client to client communication client-to-client # Send pings to keep connection alive keepalive 15 60 ping-timer-rem # Persistent ip addresses ifconfig-pool-persist /etc/openvpn/ipp
At this point you should make sure you can connect to the VPN and access services running on HOME from EXT.
Setting up port forwarding
The first thing to do when setting up port forwarding, is enabling it in the kernel (on EXT). Since we sadly still live in an IPv4 world (this is why we have CGNAT in the first place), we will only deal with the IPv4 case.
# # Enable on the running system (temporary) # sysctl -w net.ipv4.ip_forward=1 # # Enable on boot (depends on your distribution) # echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/51-ip.conf
Now we just need to tell
iptables which ports we would like to forward, and we should be done. Keep in mind: like
iptables commands only last until the next boot, so do not forget to make them persistent in whatever way is recommended for your distribution.
Now we need to decide which ports you would like to forward. For the sake of the example, we will forward TCP port 2222 for
ssh and UDP ports 61001-62000 for
First we need to check HOME's address inside the VPN. This depends on your VPN setup, but it usually should be obvious from the
As root, assuming HOME's ip address is 192.168.87.10:
# # Allow IP forwarding # iptables -A POSTROUTING -j MASQUERADE # # Forward the relevant ports to HOME # iptables -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 192.168.87.10:22 # iptables -A PREROUTING -p udp --dport 61001 -j DNAT --to-destination 192.168.87.10:61001-62000 # # Enable connections to and from the machine (in case restricted) # iptables -A FORWARD -d 192.168.87.10 -p tcp --dport 22 -j ACCEPT # iptables -A FORWARD -s 192.168.87.10 -p tcp --sport 22 -j ACCEPT # iptables -A FORWARD -s 192.168.87.10 -p udp -m state --state RELATED,ESTABLISHED --sport 61001:62000 -j ACCEPT # iptables -A FORWARD -d 192.168.87.10 -p udp --dport 61001:62000 -j ACCEPT
Another reminder: make sure to make the
iptables rules persistent.
Verifying it works
Connect to a different network than the one your home server is on and try to connect to
ssh using port 2222 with EXT's ip/hostname.
$ ssh -p 2222 user@EXT
You should now be successfully logged into HOME.
One additional trick
One annoyance that comes with this method is that now all of your traffic to HOME is passed through EXT, even if you have direct access (same LAN) to HOME from your computer. This is due to the fact that the DNS name/ip address you are now using to connect to HOME is actually EXT's.
One simple solution would be to use a different DNS name depending if you have direct access or not. This is awfully annoying and manual in general, and even more annoying when using git.
A better solution, which is what I chose for my setup, is to configure your DNS to resolve differently inside your home network (configure your router if it supports it, it should). So for example, I use git.stosb.com for my git server. The public DNS is a CNAME to stosb.com, but inside my network, it resolves to HOME's local address, which means everything will just connect to it directly.
I would love to hear if you have any corrections or better ideas on how to implement this. I would especially love to hear if you have any comments on how to harden this setup even further. I have a feeling the
iptables rules could be a bit more strict. Please let me know.