How I Use SSH and Port Forwarding in My Setup
For anyone deeply involved in technology, the Secure Shell (SSH) protocol is an indispensable tool. It’s the bedrock for secure remote access, file transfers, and command execution. But what truly unlocks its power and versatility, transforming it from a mere remote terminal into a comprehensive networking Swiss Army knife, is port forwarding.
In my daily setup, port forwarding isn’t just a niche trick; it’s a fundamental capability that underpins various workflows, from managing remote servers to developing applications against segmented environments. This post will detail how I leverage SSH’s port forwarding features, not just conceptually, but with practical, real-world examples from my own setup. My goal is to illustrate the depth, accuracy, and utility of these often-underestimated features.
The Foundation: A Quick Refresher on SSH and Port Forwarding
Before diving into my specific use cases, let’s briefly touch upon the core concepts.
SSH (Secure Shell) At its heart, SSH is a cryptographic network protocol for operating network services securely over an unsecured network. It provides a secure channel over an unsecured network in a client-server architecture, connecting an SSH client with an SSH server. Beyond just shell access, SSH can tunnel arbitrary network ports, create encrypted FTP sessions, and even forward X Window System sessions.
Port Forwarding Port forwarding, in the context of SSH, means creating an encrypted tunnel through an SSH connection. This tunnel allows network traffic destined for one port (and potentially one host) to be securely redirected to another port (and another host), bypassing firewalls or insecure public networks. It’s like building a secure, private pipeline for your network traffic.
There are three primary types of SSH port forwarding, and I use all of them extensively:
- Local Port Forwarding (
-L
): Connects a local port to a remote host/port through the SSH server. - Remote Port Forwarding (
-R
): Connects a remote port to a local host/port through the SSH server. - Dynamic Port Forwarding (
-D
): Creates a SOCKS proxy on your local machine, allowing applications configured to use the proxy to tunnel their traffic through the SSH server.
Let’s explore each with my real-world applications.
Local Port Forwarding (-L
): Accessing Remote Services Securely
Local port forwarding is perhaps the most commonly used form. It lets you connect to a service on a remote network as if it were running on your local machine.
The syntax is typically ssh -L [local_port]:[remote_host]:[remote_port] [user]@[ssh_server]
.
My Use Cases for -L
:
1. Developing Against Staging or Production Databases
This is an absolute staple in my development workflow. Often, staging or production database servers (like PostgreSQL, MySQL, or MongoDB) are not directly accessible from the public internet. They’re locked down within a private network or behind a strict firewall for security reasons.
Instead of deploying my local application code just to test a database query, I use local port forwarding. I can tunnel a remote database port to a local port.
Example:
Suppose a PostgreSQL database is running on db-server.internal
on port 5432
and is only accessible from jumpbox.mycompany.com
.
ssh -L 5432:db-server.internal:5432 myuser@jumpbox.mycompany.com
Now, my local application, my IDE’s database client (like DBeaver or VS Code’s database extensions), or even psql
running on my laptop can connect to localhost:5432
. All that traffic is securely tunneled through jumpbox.mycompany.com
to db-server.internal
.
This is incredibly powerful for debugging, running migrations, or just peeking at data without exposing the database to the internet or requiring a full VPN connection.
2. Accessing Internal Web Services or Admin Dashboards
Many organizations have internal web applications, monitoring dashboards (Grafana, Prometheus), or administrative interfaces (Jenkins, Kubernetes dashboards, cloud provider internal UIs) that are not publicly exposed.
If these services are only accessible from specific internal IP ranges or via a jump host, -L
comes to the rescue.
Example:
Accessing an internal Jenkins server running on jenkins.internal.net:8080
via a jump host dev-ssh.mycompany.com
.
ssh -L 8000:jenkins.internal.net:8080 myuser@dev-ssh.mycompany.com
Then, I just open my browser to http://localhost:8000
, and I’m securely connected to the Jenkins dashboard. The 8000
here is arbitrary; I pick one that’s unlikely to conflict with local services.
3. Bypassing Restrictive Client Networks
When working on-site at a client with extremely restrictive network policies (e.g., no direct internet access for dev machines, or only specific outbound ports allowed), I sometimes use a public cloud instance (my VPS) as an intermediary.
If the client network allows outbound SSH (port 22), I can set up a tunnel to my VPS, and then from my VPS, access external resources. This is a bit more complex and often involves a Dynamic Port Forwarding
setup, but a specific -L
tunnel could also be used for a particular service.
Note: While this is technically feasible, always be mindful of client security policies and never bypass them without explicit permission.
Remote Port Forwarding (-R
): Exposing Local Services to the World (or just a remote host)
Remote port forwarding is where things get really interesting, especially for home lab users or those behind restrictive NATs. It allows a remote SSH server to listen on a specified port and forward any incoming connections on that port back to a specific port on your local machine.
The syntax is ssh -R [remote_port]:[local_host]:[local_port] [user]@[ssh_server]
.
My Use Cases for -R
:
1. Accessing My Home Lab Behind a NAT
This is probably my most critical use of -R
. My home network uses a standard consumer router with NAT (Network Address Translation), meaning my internal machines don’t have public IP addresses and are not directly reachable from the internet. I don’t want to mess with port forwarding on my router or rely on dynamic DNS if I can avoid it.
Instead, I have a small, cheap public VPS (Virtual Private Server). My home server initiates an SSH connection to this VPS, creating a persistent reverse tunnel.
From my home server:
ssh -N -R 2222:localhost:22 myuser@my-public-vps.com
Here:
-N
: Do not execute a remote command (just forward ports).-R 2222:localhost:22
: The VPS will listen on port2222
. Any connection tomy-public-vps.com:2222
will be forwarded back tolocalhost:22
on my home server.
Now, from anywhere in the world, I can SSH into my home server like this:
From my laptop (anywhere):
ssh -p 2222 myuser@my-public-vps.com
This effectively connects me through my public VPS directly to my home server’s SSH daemon.
For reliability, I use autossh
to manage this connection. autossh
automatically monitors the SSH connection and restarts it if it drops, ensuring the tunnel is always active.
Home server’s autossh
command (often in a systemd
service):
autossh -M 0 -N -T -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -R 2222:localhost:22 myuser@my-public-vps.com
-M 0
: Disable the monitoring port (autossh checks connection health via SSH’s built-in mechanisms).-T
: Disable pseudo-terminal allocation.-o "ServerAliveInterval 30"
and-o "ServerAliveCountMax 3"
: Keep the connection alive.
Crucial Server-Side Configuration: For my-public-vps.com
to allow other machines to connect to port 2222
and forward to your home server, you might need to enable GatewayPorts
in /etc/ssh/sshd_config
on the VPS:
GatewayPorts yes
After changing sshd_config
, remember to restart the SSH daemon (sudo systemctl restart sshd
). Without GatewayPorts yes
, the VPS would only allow connections to localhost:2222
, not from external IPs.
2. Sharing a Local Development Server
Sometimes, I’m working on a local web application or API that isn’t ready for full deployment but needs to be accessible by a colleague for testing, or by a remote webhook service.
I can spin up my local dev server (e.g., a React app on localhost:3000
, a Flask API on localhost:5000
), then create a remote tunnel from my local machine to my public VPS.
From my local machine:
ssh -R 8000:localhost:3000 myuser@my-public-vps.com
Now, my colleague can access http://my-public-vps.com:8000
from their browser, and their requests will be forwarded through the VPS to my local dev server on localhost:3000
. This is fantastic for quick demos or integration testing without needing to deploy to a staging environment.
Dynamic Port Forwarding (-D
): The SOCKS Proxy Powerhouse
Dynamic port forwarding creates a SOCKS proxy on your local machine. Applications configured to use this SOCKS proxy will then send all their traffic through the SSH tunnel to the SSH server, which then acts as an intermediary to reach the final destination.
The syntax is ssh -D [local_port] [user]@[ssh_server]
.
My Use Cases for -D
:
1. Secure Browsing on Untrusted Networks
When I’m on public Wi-Fi (e.g., coffee shops, airports), I’m always cautious about security. While HTTPS encrypts application-layer data, my DNS queries and general network traffic might still be exposed.
I use dynamic port forwarding to tunnel all my browser traffic through my trusted public VPS.
From my laptop:
ssh -D 8080 myuser@my-public-vps.com
Then, I configure my browser (or a SOCKS-aware application) to use localhost:8080
as a SOCKS5 proxy. This effectively routes all my web traffic through my encrypted SSH tunnel, making it appear as if I’m browsing from the VPS’s location. This protects against snooping on the local network and can also help in bypassing certain network restrictions.
2. Accessing Geo-Restricted Content (Responsibly)
Sometimes, I need to access services that are geographically restricted or perform region-specific testing. By connecting to an SSH server located in a specific country and using dynamic port forwarding, my traffic appears to originate from that country.
Note: Always use this responsibly and ethically. Do not violate terms of service or local laws.
3. General Network Access in a Restricted Environment
If I’m on a remote server that has restricted outbound internet access (e.g., only certain ports or IPs are allowed), but that server can SSH out to my public VPS, I can create a reverse dynamic tunnel.
This is less common for typical desktop use, but can be useful for server-to-server communication where a specific server acts as a bastion.
Advanced SSH Configuration for Port Forwarding
For repeated or complex port forwarding setups, direct command-line execution becomes cumbersome. The ~/.ssh/config
file is your best friend.
~/.ssh/config
Examples:
For a Local Forward:
Host my_db_tunnel
HostName jumpbox.mycompany.com
User myuser
LocalForward 5432 db-server.internal:5432
LocalForward 8000 jenkins.internal.net:8080
ServerAliveInterval 60
ServerAliveCountMax 3
Now, to establish both tunnels, I just type ssh my_db_tunnel
. The ServerAliveInterval
and ServerAliveCountMax
options help keep the tunnel alive by sending keep-alive messages.
For a Remote Forward (from the home server to the VPS):
Host my_vps_tunnel
HostName my-public-vps.com
User myuser
RemoteForward 2222 localhost:22
ExitOnForwardFailure yes # Important for automated tunnels
Then, ssh my_vps_tunnel
from the home server sets up the reverse tunnel.
For a Dynamic Forward:
Host socks_proxy
HostName my-public-vps.com
User myuser
DynamicForward 8080
ServerAliveInterval 60
ServerAliveCountMax 3
Then, ssh socks_proxy
establishes the SOCKS proxy.
Persistence with autossh
and systemd
As mentioned, for long-running tunnels (especially -R
tunnels from home servers), autossh
is invaluable. It wraps the ssh
command and intelligently restarts it if the connection drops.
For ultimate robustness, I combine autossh
with systemd
on my Linux servers. This ensures the tunnel starts automatically on boot and is managed as a proper service.
Example systemd
service unit (/etc/systemd/system/ssh-tunnel.service
):
[Unit]
Description=AutoSSH tunnel to my_public_vps.com
After=network-online.target
[Service]
ExecStart=/usr/bin/autossh -M 0 -N -T -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o "ExitOnForwardFailure yes" -R 2222:localhost:22 myuser@my-public-vps.com
RestartSec=5
Restart=always
User=myuser # Or root, depending on your setup
Group=myuser # Or root, depending on your setup
[Install]
WantedBy=multi-user.target
After creating this file, run sudo systemctl daemon-reload
, sudo systemctl enable ssh-tunnel
, and sudo systemctl start ssh-tunnel
.
Security Considerations
While incredibly useful, port forwarding also introduces potential security risks if not handled carefully:
- Restrict
GatewayPorts
: Only enableGatewayPorts yes
on your SSH server if you intend for the remote forwarded ports to be accessible from outsidelocalhost
. Otherwise, keep it asno
(the default). - Minimal Privileges: Use dedicated SSH users for specific tunnels if possible, or restrict forwarded ports using
PermitOpen
insshd_config
on the server-side. - Key-Based Authentication: Always use SSH keys for authentication, ideally with strong passphrases, instead of passwords.
- Firewalls: Ensure your SSH server has a firewall (e.g.,
ufw
,firewalld
) that only allows necessary inbound connections (typically just port 22).
Conclusion: SSH Port Forwarding as a Core Utility
SSH and its port forwarding capabilities are not just advanced features; they are fundamental tools that dramatically enhance productivity, security, and flexibility in any modern tech setup. From securely accessing internal company resources for development to maintaining access to my personal home lab from anywhere in the world, these techniques are a daily staple.
The ability to create secure, encrypted tunnels for almost any network traffic is a testament to the robust design of the SSH protocol. By understanding and utilizing local, remote, and dynamic port forwarding, combined with ~/.ssh/config
, autossh
, and systemd
, you can build incredibly resilient and convenient network access solutions.
I encourage you to experiment with these features in your own setup. You might be surprised at how much they simplify complex networking challenges.
References:
man ssh
: For comprehensive details on SSH client options.man sshd_config
: For details on SSH server configuration.- OpenSSH Project: The official source for the SSH suite.
- autossh GitHub Repository: For keeping your SSH tunnels alive.
- SOCKS Protocol: Wikipedia entry on the SOCKS proxy protocol.