Ssh To Device Behind Router

Ssh To Device Behind Router

Reading time1 min
#SSH#Networking#Security#ReverseTunneling#PortForwarding#NATTraversal

Mastering SSH Access to Devices Behind Routers Using Reverse Tunneling Techniques

Traditional port forwarding for remote SSH access breaks down in real-world deployments: ISPs often block inbound requests, end users rarely have admin access to consumer routers, and sensitive network perimeter rules make exposed ports a persistent attack surface.

Reverse SSH tunneling sidesteps these barriers entirely. Instead of opening arbitrary ports on an edge firewall, the device inside the NAT makes an outbound SSH connection to a publicly reachable system under your control (jump server, relay, or VPS). This outbound session pre-establishes a secure tunnel, exposing a port on the jump host that forwards traffic back to the interior device.

Consider a deployment scenario:

  • Test Raspberry Pi at a customer site, reachable only on the LAN (192.168.1.100).
  • Engineer needs to SSH in from headquarters, but can’t change router/firewall rules at the site.
  • Public cloud VM (jump.example.com, Ubuntu 22.04, OpenSSH_8.9p1) is available as a meeting point.

Reverse SSH Tunneling: Practical Mechanics

Diagram:

[engineer-laptop] <--ssh--> [jump.example.com]:2222 <-- tunneled --> [target-device behind NAT]:22

Outbound Tunnel Creation (On the Interior Device)

From the device behind NAT, run:

ssh -N -R 2222:localhost:22 user@jump.example.com
  • -N: Do not execute any remote command (just forwarding).
  • -R 2222:localhost:22: Opens port 2222 on jump, sends all traffic to device's SSH (localhost:22).
  • user@jump.example.com: login credential for the jump box.

Ports above 1024 (e.g., 2222) avoid requiring root on jump server — unless strict privilege separation is enabled or sshd’s GatewayPorts is restricted.

Note: For persistent tunnels, autossh is strongly recommended:

autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -R 2222:localhost:22 user@jump.example.com
  • -M 0: disables autossh monitoring port (avoid conflicts if only one port open on jump).
  • -f: background the process.

Systemd units are more robust than cron for launching autossh. Cron is fine for quick tests, but long-term production use calls for unit files with proper restart policies.

systemd Example (Unit File: /etc/systemd/system/reverse-ssh-tunnel.service)

[Unit]
Description=Persistent reverse SSH tunnel to jump.example.com

[Service]
User=deploy
ExecStart=/usr/bin/autossh -M 0 -N -R 2222:localhost:22 user@jump.example.com
Restart=always

[Install]
WantedBy=multi-user.target

After modifying, reload systemd and enable:

sudo systemctl daemon-reload
sudo systemctl enable --now reverse-ssh-tunnel

Connecting Inbound

Engineer initiates:

ssh -p 2222 user@jump.example.com

This authenticates to the jump box’s SSH daemon, which receives the incoming connection on port 2222 and routes traffic to the hidden device’s port 22 — even if it’s deep behind NAT/firewall.

Non-obvious pitfall: If you see

Warning: remote port forwarding failed for listen port 2222

this usually means:

  • The port is already in use (or previous process is still active)
  • User permissions or GatewayPorts settings restrict binding

SSHD Configuration: Allowing Reverse Tunnels

Verify on the jump server (/etc/ssh/sshd_config):

GatewayPorts clientspecified
AllowTcpForwarding yes
PermitOpen any

Reload the sshd daemon:

sudo systemctl reload sshd

Some cloud providers (AWS EC2, GCP) require explicit inbound rule for nonstandard ports in the VPC firewall — don’t overlook this.

Multiple Concurrent Devices

Assign unique ports per device, managing them judiciously:

DeviceSSH Tunnel CommandJump Host Port
raspberrypi-site1-R 2222:localhost:222222
workstation-site2-R 2223:localhost:222223

Port conflicts trigger “remote port forwarding failed” error on tunnel creation.

Tip: Document port allocations in a shared team wiki to avoid accidental reuse.

Security Posture

  • Use SSH keys (never passwords) for all endpoints.
  • Jump/bastion server must be routinely patched and monitored.
  • Limit which users may forward ports (Match block in sshd_config).
  • Firewall all open tunnel ports except necessary ones (never leave 0.0.0.0:2222 open to the world unless intended).

Side note: Monitor for idle tunnels. Reverse tunnels left up for weeks can accumulate and consume ephemeral ports or expose dormant attack surfaces.

Troubleshooting

  • Use ssh -vvv for verbose mode. Example symptom:
debug1: remote forward success for: listen 0.0.0.0 port 2222

If missing, diagnose sshd_config and confirm no process is already bound to the port.

  • Confirm IPs with ss -tlnp | grep 2222 on jump.
  • On cloud VMs, use nc -vz jump.example.com 2222 from the engineer workstation to test if the port is reachable.

Alternatives & Limitations

  • For larger fleets, tools like frp or Remote.it can automate NAT traversal with richer access control — but build/ops complexity rises.
  • For one-off emergency access, reverse SSH remains simple, fast, and transparent.

Final Notes

Reverse SSH tunnels provide a reliable, audit-friendly pathway into devices with no router/NAT admin required. They are neither a panacea nor completely invisible to advanced adversaries, but used judiciously, they remove the logistical headache from remote management.

Production environments must treat relay endpoints as critical infrastructure: hardened, monitored, and operationally owned. For ad-hoc lab and field scenarios, nothing is faster.

Gotcha: Non-root jump sessions cannot bind ports <1024. Not all ISPs permit outbound TCP/22; in those scenarios, try -p 443 or -p 993 for the (outbound) SSH session to the jump.

If you need guidance creating multi-user shared tunnels with ACLs, or want hardening tips for SSH in zero-trust deployments, leave a technical note below.