Connecting via reverse SSH tunnel
This is a continuation of the reverse ssh article. There we configured the server side — autossh service that opens a reverse tunnel from an internal server to a public VPS (workspace). Here we configure the client side — how to connect from your laptop to those internal servers through the workspace.
How it works
The internal server has already established a reverse tunnel to the workspace. For example, it forwarded its local port 22 to port 2222 on the workspace. So if you SSH to localhost:2222 on the workspace, you end up on the internal server.
Instead of doing this manually in two steps, we use ProxyCommand in ~/.ssh/config to make it a single command.
Workspace host
First, define the workspace (your public VPS that all tunnels connect to):
Connecting to an internal server
Each internal server has a reverse tunnel to the workspace on a unique port. To connect through it:
Host internal-server-1
HostName localhost
Port 2222
User serveradmin
ProxyCommand ssh -W %h:%p workspace
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
What happens when you run ssh internal-server-1:
- SSH reads the config and sees
ProxyCommand ssh -W %h:%p workspace. - It first connects to
workspace(your public VPS). - From there it connects to
localhost:2222— which is the reverse tunnel endpoint. - You land on the internal server.
Multiple servers
Add one block per server, each with its own port:
Host internal-server-1
HostName localhost
Port 2222
User serveradmin
ProxyCommand ssh -W %h:%p workspace
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host internal-server-2
HostName localhost
Port 2223
User anotheradmin
ProxyCommand ssh -W %h:%p workspace
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host internal-db
HostName localhost
Port 2224
User dbadmin
ProxyCommand ssh -W %h:%p workspace
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Now you can simply run:
Each command is a single step from your laptop, but under the hood SSH jumps through the workspace.
Useful global defaults
Put these at the top of ~/.ssh/config to make all connections more stable:
Host *
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
ServerAliveInterval 15
ServerAliveCountMax 3
- ControlMaster/ControlPath/ControlPersist — reuses one TCP connection for multiple SSH sessions to the same host. The first
ssh workspaceopens the connection, subsequent ones piggyback on it instantly. - ServerAliveInterval/ServerAliveCountMax — sends keepalive packets every 15 seconds; drops the connection if 3 in a row fail (45 seconds total). Prevents zombie sessions.
Port convention
When you manage many servers, it helps to assign ports systematically:
| Port | Server |
|---|---|
| 2222 | internal-server-1 |
| 2223 | internal-server-2 |
| 2224 | internal-db |
| 2225 | internal-app |
| ... | ... |
This way you always know which port belongs to which server, and the autossh services on each server use the matching port in their -R flag.
Quick test
# Test workspace connectivity
ssh workspace echo "workspace OK"
# Test internal server through tunnel
ssh internal-server-1 hostname
If the first command works but the second fails, the reverse tunnel on the internal server is likely down — check the autossh service there.