SSH Port Forwarding ⤳ aka SSH Tunneling —Local Port Forwarding. (part 1)
SSH port forwarding (SSH Tunneling) is a method for safely transmitting data over an encrypted SSH; tunneling application ports from a connection between a local and distant server or vice versa.
Understanding the Basics
What is SSH Tunneling?
SSH tunneling is a powerful feature widely used by system administrators and developers to securely access services across different networks or to bypass restrictive firewall rules.
At a high level, SSH tunneling allows you to forward any TCP/IP port through an encrypted SSH connection. Instead of application traffic traveling directly over the network, it is encapsulated inside the SSH session. This ensures that the data remains confidential and protected from eavesdropping, interception, or manipulation while in transit.
Because the traffic is encrypted end-to-end, SSH tunnels are commonly used to:
- Access internal services from untrusted networks
- Secure legacy or unencrypted protocols
- Reach restricted resources behind firewalls or NAT devices
- Safely manage remote systems over insecure networks
⚠️ Security Considerations
While SSH tunneling is a legitimate and essential administrative tool, it can also be misused. Attackers and malware may abuse SSH tunnels to:
- Create covert backdoors from the Internet into internal networks
- Exfiltrate data through encrypted channels that evade inspection
- Obscure their activity by routing attacks through multiple intermediate hosts that allow unrestricted tunneling
For this reason, SSH tunneling should be carefully monitored, controlled, and restricted to trusted users and well-defined use cases, especially in enterprise or production environments.
Let Us Explore: How SSH Port Forwarding Works
SSH tunneling enables secure port forwarding and can be configured in three primary modes: local port forwarding (covered in Part 1), remote port forwarding (Part 2), and dynamic port forwarding (Part 3). Each mode serves a different purpose, depending on where the traffic originates and its intended destination.
- Local port forwarding: Redirects traffic from a local port on the client machine to a specified port on a remote server via an SSH connection.
- Remote port forwarding: Redirects traffic from a port on the remote server to a specified port on the client machine.
- Dynamic port forwarding: Creates a SOCKS proxy on the client machine, enabling the forwarding of traffic from various applications through the SSH connection.
Clarifying Some Key Concepts
Let's take a moment to clarify a few fundamental notions. If you’re already familiar with them, feel free to skip this section.
What is a localhost?
Localhost refers to the computer or device you're currently using. It's commonly associated with the loopback IP address 127.0.0.1, which allows your computer to send network traffic to itself.
Why it matters:
- Test network applications locally without exposing them to the broader network
- Access services running only on your own machine, like a local web server or database
Think of localhost as a private network within your own computer—a way for applications to communicate with each other without leaving your device.
What is a Bind-address?
A bind address specifies the network interface (IP address) and port that an application listens on for incoming connections. In simple terms, it tells the application: "Accept traffic coming to this IP address and port combination."
Key points:
- When a socket is linked to an IP address and port, it is said to be bound to that address
- The process of assigning a port to a socket is called binding
- Only applications bound to an address can receive data sent to that specific address
Why it matters:
This concept is essential when configuring servers or SSH tunnels, as the bind address determines which clients can reach your service and from which networks.
What is a Jump Server (Bastion Host)?
A jump server (also called a bastion host) is a specially secured server that acts as a single entry point to access other servers on a private network. Think of it as a secure gateway or checkpoint that you must pass through to reach internal systems.
Key Points:
- A jump server is the only server exposed to the public internet
- All other servers remain hidden in a private network
- You must connect through the jump server first before reaching any internal server
- It's typically hardened with extra security measures
Why it matters:
A jump server keeps all your other servers hidden and safe by being the only one exposed to the internet.
1. Local Port Forwarding
Local port forwarding is the most commonly used type of SSH tunneling. It allows you to access a service on a remote machine that is otherwise not directly accessible from your local device—all over a secure, encrypted connection.
Typical Use Cases
Local port forwarding is useful in scenarios such as:
- Accessing a database (MySQL, MariaDB, Redis, Postgres, etc.) from your local machine (
127.0.0.1:3914). - Accessing a container or VM port without exposing it on the server’s public interface
- Using a browser to access a web application that is restricted to a private network
How It Works
Consider a remote application that listens on localhost of the remote server (127.0.0.1:3914).
From your local machine, there is no direct way to connect to this port because it is not publicly exposed.
This is where SSH local port forwarding comes into play: you can forward a local port to the remote service over SSH, making it accessible on your machine as if it were local.
The Basic Command
Using OpenSSH, local port forwarding is initiated with the -L flag. The -N flag prevents starting an interactive shell session, which is useful if you only want to forward ports.
$ ssh -N -L local_port:localhost:[REMOTE_USER]@[REMOTE-SERVER_IP]
Password:Let's break this down:
-N: Don't start a shell session (just create the tunnel)-L: We're doing local port forwardinglocal_port: The port on YOUR computerlocalhost:remote_port: The service on the remote serveruser@remote_server: Your SSH login details
Part #A: Accessing a Web Application
Scenario: Your team has a web application running on a development server at port 3000. You need to test it from your laptop.
# Step 1: Create the tunnel
$ ssh -N -L 8080:localhost:3000 devuser@192.168.1.100
devuser@192.168.1.100's password:
# The terminal will just sit there (that means it's working!)
# Don't close this terminal windowWhat is happening?
- You connected to the remote server (192.168.1.100)
- You told SSH: "Take anything sent to my port 8080 and forward it to the server's port 3000"
Now test it:
- Open your web browser
- Go to
http://localhost:8080 - You should see the web application!
Part #B: Accessing a Remote Database
Scenario: You need to check something in the MySQL database on your staging server.
# Terminal 1 - Create the tunnel
$ ssh -N -L 3306:localhost:3306 dbadmin@staging-server.com
dbadmin@staging-server.com's password:
# Terminal 2 (open a new terminal window) - Connect to MySQL
$ mysql -h 127.0.0.1 -P 3306 -u myappuser -p
Enter password:
Welcome to the MySQL monitor...
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| myapp_db |
| test_db |
+--------------------+
2 rows in set (0.02 sec)
mysql> exitNotice: You're connecting to 127.0.0.1 (your own machine), but you're actually talking to the remote database!
Part #C: Making It Smart
Using Different Port Numbers
You don't have to match the remote port. This is useful when:
- The remote port is already in use on your machine
- You want to use a more memorable port number
# Remote app uses port 3000, but you want it on port 80
# Note: sudo might be needed for ports below 1024* <-
$ sudo ssh -N -L 80:localhost:3000 devuser@staging-server.com
# Now visit http://localhost (no port number needed!)
Multiple Services, One Tunnel
Scenario: Your application needs both MySQL (3306) and Redis (6379) from the same server.
$ ssh -N \
-L 3306:localhost:3306 \
-L 6379:localhost:6379 \
appuser@application-server.com
# Now you can connect to both:
# MySQL: localhost:3306
# Redis: localhost:6379
Running Tunnels in the Background
Sometimes you don't want a terminal window sitting open forever:
# Add -f to run in background
$ ssh -f -N -L 3306:localhost:3306 user@staging-server.com
# Find your tunnel later
$ ps aux | grep ssh
user 12345 0.0 0.1 ... ssh -f -N -L 3306:localhost:3306 user@staging-server.com
# Stop it when done
$ kill 12345Part #D: Security Basics
The Most Important Security Rule
Always bind to localhost unless you have a good reason not to!
# ❌ BAD - Anyone on your network can use your tunnel
$ ssh -N -L 3306:localhost:3306 user@staging-server.com
# ✅ GOOD - Only you can use it
$ ssh -N -L 127.0.0.1:3306:localhost:3306 user@staging-server.comWhy does this matter?
- Without
127.0.0.1:, your tunnel is visible to others on the same network - In a coffee shop (WI-FI), that means strangers could potentially connect to your database tunnel!
- Always add
127.0.0.1:before your local port
Part #E: Making Tunnels More Reliable
Using AutoSSH ("Set It and Forget It")
AutoSSH automatically restarts your tunnel if it dies:
# Install autossh
# On Mac
$ brew install autossh
# On Ubuntu/Debian
$ sudo apt-get install autossh
# On CentOS/RHEL
$ sudo yum install autossh
# Use it just like ssh
$ autossh -M 0 -N \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
-L 3306:localhost:3306 \
user@staging-server.comThe -M 0 tells autossh to use SSH's built-in keepalive instead of its own monitoring port.
Common Real-World Scenarios
Scenario 1: Accessing a Work Database from Home
The situation: You're working from home and need to check something in the production database.
# Create the tunnel
$ ssh -N -L 127.0.0.1:3306:localhost:3306 dbuser@staging-server.com
# In your database GUI tool (like TablePlus, Sequel Pro, etc.)
# Host: 127.0.0.1
# Port: 3306
# Username: your_db_user
# Password: your_db_password
# You're connected securely!
Scenario 2: Testing a Web App on Different Devices
The situation: You need to test your web app on your phone, but it's running on your work server.
# First, find your laptop's IP address on your home network
$ ip addr show | grep inet # On Linux
$ ifconfig | grep inet # On Mac
# You might see something like 192.168.1.66
# Create the tunnel (note: no 127.0.0.1 this time!)
$ ssh -N -L 8080:localhost:3000 devuser@server.com
# On your phone, connect to same Wi-Fi and visit:
# http://192.168.1.66:8080Warning: This exposes the tunnel to your whole home network. Only do this on trusted networks!
Scenario 3: Multiple Services - Simple Examples
The situation: Instead of opening multiple terminal windows with separate tunnels, you can forward several ports at once with a single SSH command!
ie. #1: Two Services, One Command
# Forward both MySQL (3306) and Redis (6379)
$ ssh -N -L 3306:localhost:3306 -L 6379:localhost:6379 user@staging-server.comThat's it! One command, one password prompt, one terminal window.
Result:
The remote MySQL and Redis services are securely accessible on your local machine via:
- MySQL available at
localhost:3306 - Redis available at
localhost:6379
ie. #2: Three Common Services
# Forward database, cache, and web app
$ ssh -N \
-L 3306:localhost:3306 \ # MySQL
-L 6379:localhost:6379 \ # Redis
-L 8080:localhost:3000 \ # Web app (remote port 3000)
user@staging-server.comNow you access them at:
- MySQL:
mysql -h 127.0.0.1 -P 3307 - Redis:
redis-cli -h 127.0.0.1 -p 6380 - API:
http://localhost:8080
ie. #3: The Secure Way (Bind to Localhost)
Always add 127.0.0.1: to keep it secure:
$ ssh -N \
-L 127.0.0.1:3306:localhost:3306 \
-L 127.0.0.1:6379:localhost:6379 \
-L 127.0.0.1:8080:localhost:3000 \
developer@staging-server.comNow only you can access these services from your machine. Nobody else on your network can sneak in!
ie. #4: Mixed Services from Different Hosts
The Situation: Imagine your company has this setup:
- Database server =
db-server(port 3306) - Redis cache server =
redis-server(port 6379) - Web application server =
app-server(port 3000)
These are all internal servers that you can't directly access from the internet. But there's one server you CAN access: a jump server (also called a bastion host) at jump-server.com.
$ ssh -N \
-L 3306:db-server:3306 \
-L 6379:redis-server:6379 \
-L 8080:app-server:3000 \
user@jump-server.comLet's break this down:
Step-by-step:
- You SSH to
jump-server.com(the only server publicly accessible) - You tell SSH: "When someone connects to my local port 3306, forward that traffic through this SSH connection to
db-server:3306" - The jump server acts as a middleman - it receives the forwarded traffic and sends it to the right internal server
- From your perspective, it feels like all three services are running on your own machine!
BONUS: Quick Reference Card
Commands You'll Use Most Often
# Basic tunnel (web app)
ssh -N -L 8080:localhost:3000 user@server
# Secure tunnel (localhost only)
ssh -N -L 127.0.0.1:3306:localhost:3306 user@server
# Multiple tunnels
ssh -N -L 3306:localhost:3306 -L 5432:localhost:5432 user@server
# Background tunnel
ssh -f -N -L 3306:localhost:3306 user@server
# Reliable tunnel
autossh -M 0 -N -L 3306:localhost:3306 user@server
# Debug mode (when things go wrong)
ssh -vvv -N -L 3306:localhost:3306 user@server
Security Concerns of Port Forwarding
While port forwarding can be a useful technique for accessing remote devices and services, if not implemented correctly, it can pose security risks.
Below are the primary concerns associated with port forwarding:
- Exposure of Services
Port forwarding exposes internal services to the internet, potentially allowing unauthorized access to those services if not properly secured. - Target for Attacks
Open ports created by port forwarding can become targets for attackers who may attempt to exploit vulnerabilities in the exposed services. - Misconfiguration
Incorrectly configured port forwarding rules may inadvertently expose sensitive data or services, leading to data breaches or unauthorized access. - Increased Attack Surface
Port forwarding increases the attack surface of a network by providing more entry points for potential attackers.
Best Practices to Reduce Risk
To reduce the security risks associated with port forwarding, implement the following best practices:
- Limit Exposure
Only forward ports that are absolutely necessary for your specific use case. Remove or disable unused forwarding rules to minimize the network’s attack surface. - Implement Access Controls
Restrict access to forwarded ports by using firewall rules or access control lists (ACLs) to allow only authorized IP addresses or networks to connect. - Keep Software Patched and Updated
Keep all software and services running on forwarded ports up to date with the latest security patches to address known vulnerabilities. - Enable Logging and Monitoring
Enable logging and monitoring for forwarded ports to detect and respond to suspicious activities or unauthorized access attempts. - Employ Intrusion Detection/Prevention Systems (IDS/IPS)
Use IDS/IPS solutions to monitor network traffic for signs of malicious activity and block or alert on suspicious behavior. - Implement Two-Factor Authentication (2FA)
Require two-factor authentication for accessing sensitive services or systems exposed through port forwarding to add an extra layer of security. - Avoid Direct Exposure When Possible
Consider safer alternatives to direct port forwarding when feasible, such as:
- VPN access
- Bastion (jump) hosts
- Zero-trust network access solutions
- Identity-aware proxies
These approaches reduce direct exposure of internal services and provide stronger centralized access control.
That's a wrap for Part 1: Local Port Forwarding.
Keep experimenting, testing scenarios, and exploring SSH tunneling in your own lab.
Stay tuned for:
- Part 2: Remote Port Forwarding
- Part 3: Dynamic Port Forwarding
Thanks for following along — stay curious, stay sharp, stay secure.