Introduction / Why You Need This
SSH tunneling allows you to securely transmit unencrypted traffic over an existing SSH connection. It provides secure access to internal databases, admin panels, or network resources without exposing them directly to the internet. This is a standard tool for system administrators and developers to establish temporary secure access, bypass corporate network restrictions, and test services.
Requirements / Preparation
Before running the commands, ensure the following baseline conditions are met:
- The
openssh-clientandopenssh-serverpackages are installed on both your local machine and the remote host. - You have working SSH access to the server (key-based authentication is preferred over passwords).
- You know the server's IP address or domain name, as well as the ports of the target internal services.
- Your local firewall does not block the ports you choose, and port 22 is open on the server.
Step 1: Local Port Forwarding
A local tunnel accepts connections on your computer, encrypts them, and forwards them to the remote server, which then delivers them to the target host. Use this mode when the required service is only accessible within the server's local network or bound to 127.0.0.1.
ssh -L 9090:10.0.0.5:3306 user@remote-server.com -N -f
Parameter breakdown:
-L 9090:10.0.0.5:3306— binds your local port9090to10.0.0.5:3306on the server's network.-N— prevents opening a remote shell, keeping only the tunnel active.-f— sends the process to the background after password entry or key verification.
You can now connect to the remote database using mysql -u root -p -h 127.0.0.1 -P 9090. All traffic will be encrypted.
Step 2: Remote Port Forwarding
The reverse scenario: you need to make a port on your local machine accessible to other users via an external server. This is useful for quickly demonstrating a local web application or API.
ssh -R 8888:localhost:3000 user@remote-server.com -N -f
By default, the server will bind port 8888 only to 127.0.0.1. To make the tunnel accessible via the server's external IP, add the line GatewayPorts clientspecified to /etc/ssh/sshd_config on the remote server, then restart the daemon: sudo systemctl restart sshd.
⚠️ Important: Before running the command, ensure that port
8888is free on the server. Otherwise, you will get abind: Address already in useerror and the tunnel will fail to create.
Step 3: Dynamic Proxy (SOCKS5)
A dynamic tunnel turns your SSH client into a SOCKS5 proxy. You can route web traffic through it to bypass local network restrictions without manually specifying each destination address.
ssh -D 1080 user@remote-server.com -N -f
To use it, configure your client application:
- In Chromium/Firefox browsers, go to network settings and specify
127.0.0.1port1080, typeSOCKS v5. Be sure to enable the "Proxy DNS" or "Remote DNS" option, otherwise DNS leaks will compromise your security. - For terminal usage, wrap your commands with
proxychainsortsocks, after configuring/etc/proxychains.confaccordingly.
Verifying the Setup
You can verify that the tunnels are working correctly with a single command:
ss -tulpn | grep -E '9090|8888|1080'
The output will show the processes listening on the specified ports and confirm they are bound to 127.0.0.1 or ::1. To test data transmission, run curl -v http://127.0.0.1:9090 or open the target resource in your browser.
To make the tunnel survive network drops, use autossh:
autossh -M 0 -N -f -L 9090:127.0.0.1:80 user@remote-server.com
The -M 0 flag disables autossh's built-in monitoring and delegates connection health checks to SSH keepalives.
Troubleshooting
- Connection refused when connecting to the local port. Check if the background SSH process has terminated. Ensure you are connecting to
127.0.0.1and notlocalhost(some systems resolvelocalhostto::1, where the port isn't bound). - Tunnel drops during long periods of inactivity. Add
ServerAliveInterval 60andServerAliveCountMax 3to your~/.ssh/config. The client will start sending keepalive pings every 60 seconds and will attempt to reconnect if it stops receiving responses. - IPv6 binding causes conflicts. If your network heavily relies on IPv6, explicitly specify the protocol version in your command:
ssh -4 -L ...or addAddressFamily inetto your SSH configuration file.