Bypassing CG-NAT

Last week I dumped Vodafone Internet + TV bundle for a dirt cheap DIGI unlimited 5G data plan for my home. I have zero interest on TV… so it felt kind of dumb to be paying for something that I really don’t use or care.

Everything working fine except that I couldn’t access my cloud server from outside my home network… I traced the problem to DIGI using CG-NAT. Carrier Grade Network Address Translation is a technology that shares the same public IP address between multiple customers. The upside: it saves IPV4 addresses, the downside: it doesn’t allow for inbound connections.

CG-NAT blocks outside connections because many devices share one public internet address. The router managing this can’t tell which device an outside request is for, so it blocks them to keep things secure and organized.

A solution is top set up a reverse SSH tunnel. You establish a secure connection to an outside device with a public IP address that forwards internet requests through it to your CGNATed device via the encrypted tunnel.

Great news this software tunnel is very easy to set up. First check for port availability on the outside device. Let’s say you want to use port 8080. Check if the port is available

lsof -i :8080

or

netstat -tuln | grep 8080

make sure nothing is using the port you will assign to your service.

Set up ssh keys login with no password between your home network device and the outside device. You should be able to login like

ssh outsidedevice

remember the -v option can be very helpful debugging ssh troubles. On the outside server adjust GatewayPorts in sshd configuration:

sudo nano /etc/ssh/sshd_config
GatewayPorts yes

and restart the sshd service

sudo systemctl restart sshd

now actually dig the tunnel. From your home device connect to the outside server, replace 192.168.1.100 with your internal device IP and adjust the ports of your service.

ssh -R 8080:192.168.1.100:8080 outsideserver -v

For simplicity sake, probably you want the same port on both machines. But if for some reason you want different ports the syntax is:

ssh -R outside_port:inside_ip:inside_port outside -v

Now you should be able to access from anywhere via outside, lets say http://outside:8080 the service running inside your home network at lets say http://192.168.1.100:8080 bypassing CGNAT.

If your tunnel silently freezes up when data is transmitted trough it, please try lowering the MTU in the local network device. I was having this kind of problem and lowering the interface MTU from 1500 to 1400 solved it.

ifconfig eth0 mtu 1400

replace eth0 with your interface, and if it works in stabilizing your ssh tunnel connection, don’t forget to update /etc/network/interfaces to make this change permanent across reboots.

At this point probably you are happy with the setup and want to auto magically connect the tunnel at startup and re-connect in case of unexpected tunnel closing. The tool for that is autossh. Install it:

sudo apt install autossh

test it manually first

autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -R 8081:192.168.1.100:8081 outsideserver -v

If all good make a systemd script to control it

sudo nano /etc/systemd/system/autossh-tunnel.service

adjust according

[Unit]
Description=AutoSSH tunnel to Outside Server
After=network-online.target

[Service]
User=root
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o ExitOnForwardFailure=yes -NR 8080:192.168.1.100:8080 outside
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Then reload the service daemon, enable your autossh tunnel service, start it, and confirm its status.

sudo systemctl daemon-reload
sudo systemctl enable autossh-tunnel.service
sudo systemctl start autossh-tunnel.service
sudo systemctl status autossh-tunnel.service

Boom, there you go your home network service again accessible trough the internet.

Leave a Reply