First check what you have
Run these on the machine you want to reach, or on your router's status page:
curl -4 https://ifconfig.me # the public IPv4 others see
curl -6 https://ifconfig.me # the public IPv6, if you have one
ip -6 addr show scope global # IPv6 addresses on this machine
Then find the WAN address on your router's status page. If it differs from what the first command shows, your provider has another NAT in front of you. An address from 100.64.0.0 to 100.127.255.255 on the WAN side is a strong sign of CGNAT, because RFC 6598 reserves that range for it.
The options
| Needs | Works behind CGNAT | Limits | |
|---|---|---|---|
| VPN or overlay | Software on both ends. A reachable peer or a coordination service | Yes | Software must be installed on every device you connect from |
| Reverse SSH tunnel | A server with a public address | Yes | The extra server carries the traffic |
| IPv6 | Global IPv6 on the machine, and on the network you connect from | Yes, if the provider gives IPv6 | Fails from IPv4-only networks. The firewall must allow it |
| Router port forwarding | A public IPv4 on the router, and access to its settings | No | Port is open to the internet. Address can change |
VPN or overlay network
WireGuard
WireGuard is a VPN you run yourself. Behind CGNAT, the hidden machine connects out to a peer that has a public address, and the keepalive holds the NAT mapping open. WireGuard's quick start suggests 25 seconds. On the hidden machine, /etc/wireguard/wg0.conf:
[Interface]
Address = 10.0.0.2/24
PrivateKey = HIDDEN_MACHINE_PRIVATE_KEY
[Peer]
PublicKey = PUBLIC_PEER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25
On the peer with the public address:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = PUBLIC_PEER_PRIVATE_KEY
[Peer]
PublicKey = HIDDEN_MACHINE_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
Create the keys with wg genkey | tee priv | wg pubkey > pub, start each side with sudo wg-quick up wg0, and connect with ssh user@10.0.0.2 from the peer. UDP 51820 must be open on it. We ran these two files between containers, and the SSH login over 10.0.0.2 worked. Only the peer needs a public address.
Tailscale
Tailscale is an overlay network built on WireGuard, with a hosted coordination service and an account. On Linux, per its documentation:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
Do the same on your own device, and then SSH to the address it assigns, which tailscale ip -4 shows on the hidden machine. Tailscale first tries a direct connection between devices, and its article on NAT traversal says this is harder behind CGNAT. When direct paths fail, traffic goes through its DERP relays, encrypted end to end.
Reverse SSH tunnel
This needs only SSH and one server with a public address, such as 203.0.113.10. On the hidden machine:
ssh -N -o ExitOnForwardFailure=yes -R 2222:localhost:22 user@203.0.113.10
From your laptop:
ssh -J user@203.0.113.10 -p 2222 hiddenuser@localhost
Details are in reverse SSH tunnels. To restart it automatically after a drop, and restrict the key it uses, see autossh and systemd.
The -J option is explained in ProxyJump and jump hosts.
IPv6
CGNAT applies to IPv4, and some providers that use it also hand out global IPv6 addresses. If ip -6 addr show scope global shows one, try from a network that has IPv6:
ssh user@2001:db8::10
The router or provider may block incoming IPv6, so a timeout may mean the firewall needs a rule for port 22. See Connection timed out. The network you connect from also has to have IPv6, and some office and hotel networks do not. If your address changes, a dynamic DNS name helps.
Router port forwarding
This works when the router has a real public IPv4 address. In its settings, forward an outside TCP port to port 22 of the machine, then connect to the router's public address:
ssh -p 2222 user@203.0.113.10
Behind CGNAT this does nothing, because the public address belongs to your provider. Some providers will give you a public IPv4 on request, sometimes for a fee, so it can be worth asking. An open port also receives constant login attempts from the internet, so use keys only. See how to disable password login and fail2ban.
Questions
How do I know if I am behind CGNAT?
Compare the WAN address on your router with the address that sites such as ifconfig.me see. If they differ, or the WAN address is between 100.64.0.0 and 100.127.255.255, your provider uses CGNAT.
Can I SSH to a machine behind CGNAT without a VPN?
Yes, with a reverse SSH tunnel to a server that has a public address. The hidden machine connects out, and you reach it through that server.
Does router port forwarding work behind CGNAT?
No. The public address belongs to your provider, not to your router, so there is nothing to forward from. Ask the provider for a public IPv4, or use one of the other options.
Sources
- RFC 6598, shared address space
- WireGuard quick start
- Install Tailscale on Linux
- How NAT traversal works, Tailscale
- ssh(1), OpenBSD manual