Unlocking Network Access: A Guide to SSH Port Forwarding
SSH (Secure Shell) is renowned for providing secure command-line access to remote servers. Its capabilities extend far beyond simple terminal sessions. One of its most useful features is port forwarding (also known as SSH tunneling), which allows you to securely tunnel network connections from one machine to another through an encrypted SSH link.
Understanding port forwarding opens solutions for various networking challenges, from bypassing firewalls to securely accessing internal services or even exposing local development environments to the internet temporarily.
There are three main types of SSH port forwarding:
- Local Port Forwarding: Forwards connections from your local machine to a destination server, via an intermediary SSH server.
- Remote Port Forwarding: Forwards connections from a remote SSH server back to your local machine (or another destination reachable from your local machine).
- Dynamic Port Forwarding: Creates a SOCKS proxy on your local machine that tunnels traffic through the SSH server to various destinations.
Let’s explore each type.
1. Local Port Forwarding (-L)
This is the most common type. It allows you to access a resource (like a web server or database) on a remote network as if it were running locally on your machine. The connection path is:
Your Local Machine -> SSH Server -> Final Destination Server
Use Cases:
- Accessing internal company resources (wikis, databases) from outside the company network via a bastion host (jump server).
- Bypassing simple firewalls that might block direct access to a service but allow SSH connections.
Command Structure:
ssh -L <LocalPort>:<DestinationHost>:<DestinationPort> <SSHServerUser>@<SSHServerHost>
<LocalPort>: The port number you will connect to on your local machine.<DestinationHost>: The hostname or IP address of the final destination server, reachable from the SSH server.<DestinationPort>: The port number on the final destination server you want to reach.<SSHServerUser>@<SSHServerHost>: The credentials for the intermediary SSH server you are connecting through.
Example: Accessing www.ubuntuforums.org (port 80) via your local port 8080, tunneling through <host>:
# Make sure nothing else is running on localhost:8080
ssh -L 8080:www.ubuntuforums.org:80 <user>@<host>
While this command is running, open your web browser and navigate to http://localhost:8080. Your request will be securely forwarded through <host> to www.ubuntuforums.org on port 80.
2. Remote Port Forwarding (-R)
Remote port forwarding does the opposite of local forwarding. It allows you to expose a service running on your local machine (or a machine reachable from your local machine) so that it can be accessed via a port on the remote SSH server. The connection path is:
External Client -> SSH Server -> Your Local Machine -> Local Destination Service
Use Cases:
- Temporarily exposing a local web development server to the internet for demos or testing.
- Allowing external services (like webhooks from GitHub, Stripe, etc.) to reach an application running only on your local machine.
- Providing temporary remote access to a local service for collaboration.
Command Structure:
ssh -R <RemotePort>:<LocalDestinationHost>:<LocalDestinationPort> <SSHServerUser>@<SSHServerHost>
<RemotePort>: The port number the remote SSH server will listen on.<LocalDestinationHost>: The hostname or IP address the connection should be forwarded to from your local machine (oftenlocalhost).<LocalDestinationPort>: The port number of the service running on your<LocalDestinationHost>.<SSHServerUser>@<SSHServerHost>: The credentials for the remote SSH server where the<RemotePort>will be opened.
Example: Expose your local web server running on localhost:8000 so it’s accessible via port 9999 on my-remote-host.com:
# While this SSH session is active...
ssh -R 9999:localhost:8000 my-remote-host.com
Now, connections made to my-remote-host.com on port 9999 will be tunneled back through the SSH connection to your local machine and directed to localhost:8000.
Running in the Background:
To keep the tunnel open without an active shell session, use the -f (fork to background) and -N (do not execute remote command) flags:
# Runs in the background. The SSH process must be killed manually later.
ssh -f -N -R 9999:localhost:8000 my-remote-host.com
Verification (netstat & curl):
After running the -R command, you can check on the remote server if the port is listening:
# On the remote server (my-remote-host.com)
netstat -ntl | grep 9999
You might see tcp 0 0 127.0.0.1:9999 0.0.0.0:* LISTEN or tcp 0 0 0.0.0.0:9999 0.0.0.0:* LISTEN. The first (127.0.0.1) means only connections originating from the remote server itself can use the tunnel. The second (0.0.0.0) means connections from anywhere can use it.
You can test the tunnel locally on the remote server:
# On the remote server
curl localhost:9999
This should return the output from your service running on localhost:8000 on your local machine.
Exposing the Forwarded Port Publicly (GatewayPorts):
To allow external connections (not just localhost on the remote server) to use the forwarded port, you need to configure the SSH server (on my-remote-host.com).
- Edit the SSH server configuration file (usually
/etc/ssh/sshd_config) withsudo:sudo nano /etc/ssh/sshd_config - Find or add the
GatewayPortsdirective and set it toyes:GatewayPorts yes - Save the file and restart the SSH service:
sudo systemctl restart sshd
Security Warning: Setting GatewayPorts yes makes the service running on your local machine accessible to the internet via the remote server’s IP and port. Use this with extreme caution! Ensure your local application is secure or use firewall rules on the remote server to restrict access if needed.
Using SSH Config for Remote Forwarding:
You can simplify this by adding it to your ~/.ssh/config file:
# ~/.ssh/config
Host mysandbox
HostName my-sandbox.com # Replace with your actual remote host
User myusername # Replace with your username
# RemoteForward <RemotePort> <LocalDestinationHost>:<LocalDestinationPort>
RemoteForward 9999 localhost:8000
Then you can establish the background tunnel with:
ssh -f -N mysandbox
3. Dynamic Port Forwarding (-D)
Dynamic port forwarding turns your SSH client into a SOCKS proxy server. Applications on your local machine configured to use this proxy will have their traffic tunneled through the SSH server. Unlike local/remote forwarding which map specific ports, dynamic forwarding works for various destinations accessed through the proxy.
Use Cases:
- Securely browsing the web from an untrusted network by tunneling your browser traffic through a trusted SSH server.
- Accessing multiple different services on a remote network without setting up individual local forwards for each.
Command Structure:
ssh -D <LocalSocksPort> <SSHServerUser>@<SSHServerHost>
<LocalSocksPort>: The port number your local machine will listen on for SOCKS proxy connections.<SSHServerUser>@<SSHServerHost>: The SSH server to tunnel traffic through.
Example: Create a SOCKS proxy on local port 1080 via <host>:
ssh -D 1080 <user>@<host>
You would then configure your application (e.g., web browser, other tools) to use localhost:1080 as its SOCKS5 proxy. All traffic from that application will then be securely tunneled through <host>. Dynamic forwarding is flexible but generally less commonly used than local or remote forwarding for specific port mappings.
Alternatives for Exposing Local Services (Remote Forwarding Use Case)
While SSH remote forwarding works well, setting it up, especially with GatewayPorts, requires server configuration and careful security consideration. Several third-party services and tools simplify exposing local ports:
- ngrok: A popular programmable network edge service. Easy setup, provides secure tunnels with public URLs (e.g.,
https://<random-string>.ngrok.io). Has free and paid tiers. - LocalXpose: Commercial service with free options, terminal and GUI clients available.
- localhost.run: Free service using SSH; no client download or signup needed. Simple command-line usage.
- localtunnel: Open-source Node.js client. Free, no signup. (
lt --port 8000) - JPRQ: Open-source Python client. Free, no signup.
- sish: Open-source, self-hostable alternative using Docker. Mimics ngrok functionality via SSH.
These tools often handle the complexities of creating public URLs and securing tunnels, making them excellent alternatives for the remote forwarding use case.
Conclusion
SSH port forwarding is a versatile feature baked into the SSH protocol. Local forwarding helps access remote services securely, remote forwarding exposes local services (use with caution!), and dynamic forwarding provides a SOCKS proxy for general traffic tunneling. Understanding these options allows you to overcome network barriers and securely manage connections in various development and operational scenarios. Always prioritize security, especially when exposing services using remote forwarding and GatewayPorts.
(References: Ubuntu Community SSH Port Forwarding, DevDungeon Expose Local Port, SitePoint Using Ngrok, Nerderati SSH Config)