Configure SSH port forwarding and tunneling for secure connections

Intermediate 35 min Apr 15, 2026 176 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Learn to configure SSH port forwarding, remote tunnels, and SOCKS proxy for secure network connections. Set up local, remote, and dynamic port forwarding with automated monitoring and security hardening.

Prerequisites

  • Root or sudo access
  • SSH server installed
  • Basic understanding of networking concepts
  • SSH key pair configured

What this solves

SSH port forwarding creates secure tunnels through encrypted SSH connections, allowing you to access remote services, bypass firewalls, and route traffic securely through intermediate servers. This technique is essential for accessing services on private networks, securing unencrypted protocols, and creating secure pathways for database connections, web services, and administrative tasks.

SSH configuration and security hardening

Update system and install OpenSSH

Ensure you have the latest SSH server and client packages installed on your system.

sudo apt update && sudo apt upgrade -y
sudo apt install -y openssh-server openssh-client
sudo dnf update -y
sudo dnf install -y openssh-server openssh-clients

Configure SSH server for port forwarding

Edit the SSH daemon configuration to enable port forwarding and configure security settings.

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo nano /etc/ssh/sshd_config
# SSH Port Forwarding Configuration
Port 22
Protocol 2

Enable port forwarding

AllowTcpForwarding yes GatewayPorts no X11Forwarding no

Security hardening

PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes AuthenticationMethods publickey

Connection limits

MaxAuthTries 3 MaxSessions 10 ClientAliveInterval 300 ClientAliveCountMax 2

Restrict users (optional)

AllowUsers username1 username2

AllowGroups ssh-users

Enable and restart SSH service

Apply the configuration changes by restarting the SSH daemon and enabling it for automatic startup.

sudo systemctl enable ssh
sudo systemctl restart ssh
sudo systemctl status ssh

Configure SSH client settings

Create a client configuration file to store connection settings and tunnel definitions.

mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/config
chmod 600 ~/.ssh/config
# SSH Client Configuration
Host jumpserver
    HostName 203.0.113.10
    User admin
    Port 22
    IdentityFile ~/.ssh/id_rsa
    ServerAliveInterval 60
    ServerAliveCountMax 3

Host database-tunnel
    HostName 203.0.113.20
    User dbuser
    Port 22
    IdentityFile ~/.ssh/id_rsa
    LocalForward 5432 localhost:5432
    ServerAliveInterval 60

Local port forwarding setup

Configure basic local port forwarding

Local port forwarding redirects traffic from a local port through the SSH tunnel to a destination server.

# Forward local port 8080 to remote web server port 80
ssh -L 8080:localhost:80 user@203.0.113.10

Forward local port 5432 to remote database server

ssh -L 5432:database.internal:5432 user@jumpserver.example.com

Forward local port 3306 to MySQL server through jump host

ssh -L 3306:mysql.internal:3306 -N -f user@jumpserver.example.com

Create persistent local tunnels

Set up background tunnels that automatically reconnect and persist across system restarts.

sudo nano /etc/systemd/system/ssh-tunnel-database.service
[Unit]
Description=SSH Tunnel to Database Server
After=network.target

[Service]
Type=simple
User=tunneluser
ExecStart=/usr/bin/ssh -N -L 5432:database.internal:5432 user@jumpserver.example.com
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable ssh-tunnel-database
sudo systemctl start ssh-tunnel-database

Configure multi-port forwarding

Forward multiple ports simultaneously through a single SSH connection for complex service access.

# Forward multiple services through one connection
ssh -L 8080:web.internal:80 \
    -L 5432:db.internal:5432 \
    -L 6379:redis.internal:6379 \
    -L 9200:elasticsearch.internal:9200 \
    -N -f user@jumpserver.example.com

Remote port forwarding configuration

Set up basic remote port forwarding

Remote port forwarding allows external connections to reach services on your local machine through the remote server.

# Allow remote server to forward connections to local service
ssh -R 8080:localhost:80 user@203.0.113.10

Forward remote port to local development server

ssh -R 3000:localhost:3000 -N -f user@development.example.com

Forward remote port to local database for remote access

ssh -R 5432:localhost:5432 user@remote.example.com

Configure gateway ports for external access

Enable external connections to reach forwarded ports by configuring gateway ports on the remote server.

sudo nano /etc/ssh/sshd_config
# Allow remote port forwarding from external IPs
GatewayPorts yes

Or restrict to specific interfaces

GatewayPorts clientspecified

sudo systemctl restart ssh

Now bind to all interfaces on remote server

ssh -R 0.0.0.0:8080:localhost:80 user@remote.example.com

Create reverse tunnel service

Set up a persistent reverse tunnel service for continuous remote access to local services.

sudo nano /etc/systemd/system/reverse-tunnel-web.service
[Unit]
Description=Reverse SSH Tunnel for Web Service
After=network.target

[Service]
Type=simple
User=tunneluser
ExecStart=/usr/bin/ssh -N -R 8080:localhost:80 user@public.example.com
Restart=always
RestartSec=15
StandardOutput=journal
StandardError=journal

Auto-restart on failure

StartLimitInterval=0 StartLimitBurst=0 [Install] WantedBy=multi-user.target
sudo systemctl enable reverse-tunnel-web
sudo systemctl start reverse-tunnel-web

Dynamic port forwarding and SOCKS proxy

Create SOCKS proxy tunnel

Dynamic port forwarding creates a SOCKS proxy that routes traffic through the SSH connection for any destination.

# Create SOCKS5 proxy on local port 8080
ssh -D 8080 user@proxy.example.com

Background SOCKS proxy with auto-reconnect

ssh -D 8080 -N -f user@proxy.example.com

SOCKS proxy with specific bind address

ssh -D 127.0.0.1:8080 -N user@proxy.example.com

Configure applications to use SOCKS proxy

Configure web browsers and applications to route traffic through the SOCKS proxy tunnel.

# Test SOCKS proxy with curl
curl --socks5 127.0.0.1:8080 http://httpbin.org/ip

Configure git to use SOCKS proxy

git config --global http.proxy socks5://127.0.0.1:8080 git config --global https.proxy socks5://127.0.0.1:8080

Remove proxy configuration

git config --global --unset http.proxy git config --global --unset https.proxy

Set up persistent SOCKS proxy service

Create a system service for continuous SOCKS proxy availability with automatic reconnection.

sudo nano /etc/systemd/system/socks-proxy.service
[Unit]
Description=SSH SOCKS Proxy
After=network.target

[Service]
Type=simple
User=proxyuser
ExecStart=/usr/bin/ssh -N -D 127.0.0.1:8080 user@proxy.example.com
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

Environment variables

Environment=SSH_AUTH_SOCK=/run/user/1001/ssh-agent.socket [Install] WantedBy=multi-user.target
sudo systemctl enable socks-proxy
sudo systemctl start socks-proxy

SSH tunnel automation and monitoring

Create tunnel monitoring script

Develop a monitoring script to check tunnel health and automatically restart failed connections.

sudo nano /usr/local/bin/monitor-ssh-tunnels.sh
#!/bin/bash

SSH Tunnel Monitoring Script

LOG_FILE="/var/log/ssh-tunnel-monitor.log" DATE=$(date '+%Y-%m-%d %H:%M:%S')

Function to log messages

log_message() { echo "[$DATE] $1" | sudo tee -a "$LOG_FILE" }

Check if tunnel port is responsive

check_tunnel() { local port=$1 local service_name=$2 if nc -z localhost "$port" 2>/dev/null; then log_message "SUCCESS: $service_name tunnel on port $port is active" return 0 else log_message "ERROR: $service_name tunnel on port $port is down" return 1 fi }

Restart tunnel service

restart_service() { local service_name=$1 log_message "Restarting service: $service_name" sudo systemctl restart "$service_name" sleep 5 }

Monitor database tunnel

if ! check_tunnel 5432 "Database"; then restart_service "ssh-tunnel-database" fi

Monitor SOCKS proxy

if ! check_tunnel 8080 "SOCKS Proxy"; then restart_service "socks-proxy" fi

Monitor web tunnel

if ! check_tunnel 8081 "Web Service"; then restart_service "reverse-tunnel-web" fi
sudo chmod +x /usr/local/bin/monitor-ssh-tunnels.sh

Schedule tunnel monitoring

Set up automated monitoring using systemd timers to regularly check tunnel health.

sudo nano /etc/systemd/system/ssh-tunnel-monitor.service
[Unit]
Description=SSH Tunnel Health Monitor

[Service]
Type=oneshot
ExecStart=/usr/local/bin/monitor-ssh-tunnels.sh
User=root
sudo nano /etc/systemd/system/ssh-tunnel-monitor.timer
[Unit]
Description=Run SSH Tunnel Monitor every 5 minutes
Requires=ssh-tunnel-monitor.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable ssh-tunnel-monitor.timer
sudo systemctl start ssh-tunnel-monitor.timer

Configure connection multiplexing

Enable SSH connection multiplexing to reuse connections and improve tunnel reliability.

nano ~/.ssh/config
# SSH Connection Multiplexing
Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600
    ServerAliveInterval 60
    ServerAliveCountMax 3
    

Create socket directory

Host * ProxyCommand none
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets

Set up tunnel logging and alerting

Configure comprehensive logging and integrate with monitoring systems for tunnel visibility.

sudo nano /etc/rsyslog.d/50-ssh-tunnels.conf
# SSH Tunnel Logging Configuration
:programname, isequal, "ssh" /var/log/ssh-tunnels.log
:programname, isequal, "ssh" stop
sudo systemctl restart rsyslog

Create log rotation configuration

sudo nano /etc/logrotate.d/ssh-tunnels
/var/log/ssh-tunnels.log {
    daily
    missingok
    rotate 30
    compress
    notifempty
    create 644 root root
    postrotate
        systemctl reload rsyslog
    endscript
}

Verify your setup

Test your SSH tunnels and monitoring configuration to ensure everything works correctly.

# Check SSH service status
sudo systemctl status ssh

List active SSH connections

ss -tuln | grep :22

Test local port forwarding

netstat -tlnp | grep :8080 curl -I http://localhost:8080

Verify SOCKS proxy

curl --socks5 127.0.0.1:8080 http://httpbin.org/ip

Check tunnel services

sudo systemctl status ssh-tunnel-database sudo systemctl status socks-proxy sudo systemctl status reverse-tunnel-web

Monitor tunnel health

sudo /usr/local/bin/monitor-ssh-tunnels.sh tail -f /var/log/ssh-tunnel-monitor.log

Verify monitoring timer

sudo systemctl status ssh-tunnel-monitor.timer sudo systemctl list-timers ssh-tunnel-monitor.timer

Common issues

SymptomCauseFix
Connection refused on forwarded portService not running on target serverVerify target service with systemctl status service-name
Tunnel drops frequentlyNetwork instability or timeoutAdd ServerAliveInterval 60 to SSH config
Cannot bind to portPort already in useCheck with netstat -tlnp | grep port and change port
Remote forwarding failsGatewayPorts disabledSet GatewayPorts yes in /etc/ssh/sshd_config
SOCKS proxy not workingApplication not configuredConfigure application proxy settings to use 127.0.0.1:8080
Permission denied for tunnelSSH key not configuredCopy SSH key with ssh-copy-id user@server
Service won't startUser doesn't existCreate tunnel user with sudo useradd -r -s /bin/false tunneluser

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.