guest@6admin.io:~$

SSH: tunneling

SSH does not only transfer files: it makes any TCP protocol cross the encrypted channel. A VNC connection, a MySQL database, an internal web server, an SMB share: everything that is TCP can be tunneled through SSH, without VPN, without firewall configuration, with one command.

The local tunnel (local forward)

ssh -L 3306:localhost:3306 user@server

This command opens port 3306 on your local machine and everything that arrives there goes to port 3306 on server (as seen from server). An SSH session is open, the tunnel works as long as the session lasts.

To access MySQL on server:

mysql -h 127.0.0.1 -P 3306 -u root -p

The -h 127.0.0.1 is crucial: the tunnel is local, MySQL sees a connection from localhost on server, which bypasses bind-address restrictions.

The remote tunnel (remote forward)

ssh -R 8080:localhost:80 user@server

This command opens port 8080 on server and everything that arrives there goes to port 80 on your local machine. The reverse of the local forward: you expose a local service on a remote server.

Use case: your machine is behind a NAT/firewall and you want to expose a local web server. The remote tunnel exposes it via server:8080.

The dynamic tunnel (SOCKS proxy)

ssh -D 1080 user@server

This command opens a SOCKS5 proxy on port 1080 of your machine. Configure Firefox to use 127.0.0.1:1080 as SOCKS5 proxy and all web traffic goes through server: DNS, HTTP, HTTPS. This is the poor man’s VPN: no network config, no VPN client, just SSH and a browser proxy.

Use case: accessing an internal network from outside. The SSH server is in the network, the SOCKS proxy projects you into it.

Multi-hop tunneling

To reach a target behind a jump host, you chain the connections with ProxyCommand and nc on the intermediate:

ssh -o ProxyCommand="ssh user@intermediate nc final 22" user@final

The intermediate opens a TCP connection to port 22 on the final with nc and the local SSH negotiates its end-to-end encryption through that pipe: the jump host only ever sees encrypted traffic it cannot read. This is the bastion pattern and its merit is that no key is stored on the jump host, the key and the session stay on the originating machine.

[edit: 2026] The three forwards (local, remote, dynamic) have not moved an inch. The ProxyCommand multi-hop has since been simplified by ProxyJump, the -J option that appeared with OpenSSH 7.3, which I cover in a dedicated post, SSH: ProxyJump and modern multi-hop . WireGuard has meanwhile taken over as the serious VPN, but for a one-off connection to an internal service, SSH tunneling remains simpler: one command, no network config.

[edit: 2026] The Ansible implementation of all this: the ssh and wireguard roles.