There are two types of SSH tunnel: backward tunnel and forward tunnel

Backward tunnel

A backward tunnel connection can be initialized with -R flag in a ssh command.

From PC client, run ssh -R \*:2222:firewalled:2200 server.

Reserve tunnel

Note: firewalled is looked up from client. Hence, if firewalled is localhost, it will become the client itself.

From now, any access (send/receive) to server:2222 from anywhere, is equivalent to access to firewalled:2200 from client. This is extremely useful if firewalled is closed from public and only accessible from the client machine.

More interestingly, the firewalled can be localhost, then, with this tunnel connection, you can connect to any port of the client machine without access permission to the NAT router. For example, I can expose SSH connection from any machine which has internet connection, or be able to reach the server machine.

Full real-life example

autossh -o "ServerAliveInterval 30" -o "ServerAliveCountMax 100" -R \*:2222:localhost:22 -fNT
  • autossh: same command interface as ssh, except that autossh automatically restarts the ssh command if terminated. autossh needs to manually be installed (sudo apt install autossh). This command can be replaced with ssh.
  • o "ServerAliveInterval 30": for each 30 seconds, send a ping package to server in order to prevent the ssh connection from being terminated due to inactivity.
  • -o "ServerAliveCountMax 100": number of server alive messages sent when the server does not response, before terminating the connection.
  • -R: reverse tunnel
  • localhost: the firewalled, this can be localhost or replaced with IP address of any machine in the same LAN network of the client PC (i.e. the PC where this command is executed)
  • the center machine (the server machine), which should be public and must be reachable from the client machine. This machine is latter used to connect to the localhost( firewalled) machine specified above.
  • -C: compress data
  • -q: quite mode, no warning
  • -N: open SSH connection with no command to be executed
  • -f: run background
  • -T: do not allocate a TTY-terminal save a tiny amount of memory.

Use the tunnel

After running the above command, access to the server (i.e. server). From the server PC command line, run ssh username_at_firewalled@localhost -p 2222.

Note: localhost here refers to the server machine itself, and its meaning is different from the localhost ( firewalled)

Direct access to firewalled

By default, Ubuntu blocks forwarding packing if you access directly to server:2222. Unless config following configuration, you have to login to the server, and access to localhost:2200. Edit /etc/ssh/sshd_config, append following content

Match User root
   GatewayPorts yes
   AllowTcpForwarding yes

Restart sshd via sudo systemctl restart sshd.

Disable firewall at port 2222/TCP with ufw allow 2222/tcp && ufw reload.

For example, with the above real-life example, now you can do ssh -p 2222 -l username_at_server.

Forward tunnel

The mechanism is mostly same as backward tunnel

In backward tunnel, the ssh tunnel command runs in client and target to server. In forward tunnel, the tunnel command runs in the server and target to the client.

ssh -L \*:2222:firewalled:2200 client

This command is useful when server is the unique machine is allowed to access to the client and its local network.

Startup script

You can create a script containing those above commands, execute the script whenever the client PC startups to enable these tunnel connections forever.

Refer to this blog post to know how to create a startup script in general.

Create systemd unit tunnel.service in /etc/systemd/system

sudo cat <<EOF >/etc/systemd/system/tunnel.service
Description=SSH tunnel service sshd.service

ExecStart=/usr/bin/autossh -i /home/transang/.ssh/id_rsa -R "*:2222:localhost:22" -NT username_at_server@server


Reduce power save level to prevent NetworkManager from randomly disconnect wifi:
In /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf change wifi.powersave = 3 to wifi.powersave = 2. Restart NetworkManager service in order for the change to takes effect sudo systemctl restart NetworkManager.

From nm-setting-wireless.h

* NMSettingWirelessPowersave:
* @NM_SETTING_WIRELESS_POWERSAVE_IGNORE: don't touch existing setting
* These flags indicate whether wireless powersave must be enabled.
typedef enum {
} NMSettingWirelessPowersave;

Enable and start the service with sudo systemctl enable --now tunnel.