*edited 5apr25 to add sshtunnel user
Some cell carriers and ISPs do carrier-grade network address translation (CGNAT) so you don’t get a public IPv4. So to connect to a computer (or network) behind CGNAT, you need a virtual private server (VPS) or some other machine that is reachable directly. You can then create a tunnel between the CGNAT’d machine to the exposed machine and use that tunnel to connect directly. There are many ways to skin a cat (Cloudflare, Tailscale, Headscale, RealVNC) but my method uses SSH and OpenVPN since it doesn’t rely on cloud services. I used to use autossh (of which there are various guides), but systemd renders it unnecessary, and was more complicated (some solutions use old initd methods). I use the guide here, but will recreate it here in case it disappears. (credit: https://blog.stigok.com). Options used:
-g Allows remote hosts to connect to local forwarded ports
-N Do not execute a remote command
-T Disable pseudo-terminal allocation
-o Used to give options in the format used in the configuration file (man ssh_config)
ServerAliveInterval Interval in seconds to ping the server while connection has been inactive
ExitOnForwardFailure Whether to terminate the connection if it cannot set up all requested port forwards
-R Forward given remote TCP port (22221) to the local port (22)
-v Verbose mode. More v's increase verbosity.
Create a service file in /lib/systemd/system or /etc/systemd/system called ssh-reverse.service sudo nano /etc/systemd/system/ssh-reverse.service
and put in the following:
[Unit]
Description=Reverse SSH connection
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/ssh -vvv -g -N -T -o "ServerAliveInterval 10" -o "ExitOnForwardFailure yes" -o StrictHostKeyChecking=no -R 21194:localhost:1194 -R 20022:localhost:22 sshtunnel@12.34.56.78
Restart=always
RestartSec=5s
[Install]
WantedBy=default.target
Just add the ports you want to forward (in my case ssh and openvpn) and put in the ip address of the VPS. You will need to adjust the /etc/ssh/sshd_config settings on the VPS to allow port forwarding as follows:
AllowAgentForwarding yes
AllowTcpForwarding yes
GatewayPorts yes
X11Forwarding yes
TCPKeepAlive yes
ClientAliveInterval 300
ClientAliveCountMax 2
PermitTunnel yes
Not sure if all those are strictly necessary for it to work, but at a minimum GatewayPorts yes is. For OpenVPN to work, a few more have to be on, like AllowTcpForwarding, PermitTunnel, and TCPKeepAlive.
You’ll then want to create a sshtunnel user on the VPS that has privileges to port forward but nothing else. Credit from here.
useradd sshtunnel -m -d /home/sshtunnel -s /bin/true
For the initial SSH connection from the host to the VPS, it needs to be done manually to add the fingerprint to known_hosts (on the host machine) (in /root/.ssh/known_hosts). And need to use ssh-keygen to generate the public/private key pair to allow password-less connection. Once the id_rsa.pub is generated on the host, it can be copied to the VPS manually to /sshtunnel/.ssh/authorized_keys, or you can use ssh-copy-id $target_host if you can login with a password. I’ve heard that allowing remote root ssh login via password is not a good idea, so you should probably turn that off afterwards. Make sure /etc/ssh/sshd_config on the VPS has the following lines:
PermitRootLogin prohibit-password #or without-password, which is deprecated
PubkeyAuthentication yes
I’ve run into issues with the fingerprint adding, and altering the service file to include -o StrictHostKeyChecking=no
in the ssh line takes care of it.
Final step is to save the service file, then do sudo systemctl daemon-reload
, sudo systemctl enable ssh-reverse
, sudo systemctl start ssh-reverse