How I Use SSH and Port Forwarding in My Setup

Close-up of a computer monitor displaying cyber security data and code, indicative of system hacking or programming.
Close-up of a computer monitor displaying cyber security data and code, indicative of system hacking or programming.

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:

  1. Local Port Forwarding (-L): Connects a local port to a remote host/port through the SSH server.
  2. Remote Port Forwarding (-R): Connects a remote port to a local host/port through the SSH server.
  3. 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 port 2222. Any connection to my-public-vps.com:2222 will be forwarded back to localhost: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 enable GatewayPorts yes on your SSH server if you intend for the remote forwarded ports to be accessible from outside localhost. Otherwise, keep it as no (the default).
  • Minimal Privileges: Use dedicated SSH users for specific tunnels if possible, or restrict forwarded ports using PermitOpen in sshd_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.

Last updated on