Essential guide to SSH, SSH tunnel and PuTTY

The basics

In Linux, to generate a keypair (a pair of private key and public key), use ssh-keygen. The private key is for you to keep secret, while the public key is to added any instance you want connect to.

root@ip-10-17-1-106:~/workspace# ssh-keygen -f nexus
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in nexus
Your public key has been saved in nexus.pub
The key fingerprint is:
SHA256:3sYCJJlyAKlE92Km8J8u9skHmMlIr3DBurA4WlCcauQ root@ip-10-17-1-106
The key's randomart image is:
+---[RSA 3072]----+
|o+..             |
|.o.o.o           |
|+o=+=..          |
|==*o.o           |
|+Eo=  . S        |
|*.*o.. o o       |
|++o o.  o +      |
|=ooo ..  o       |
|oo o=.           |
+----[SHA256]-----+
root@ip-10-17-1-106:~/workspace# ls
nexus  nexus.pub

To make it more secure, specify a passphrase, this is like a password that you need to enter before you can use the private key.

The file nexus is you private key and nexus.pub is you public key. Usually you put an .pem extension for you private key. And make sure it is only readable to you, do a chmod 400 on it. Some SSH clients insist on this file permission.

mv nexus nexus.pem
chmod 400 nexus.pem

The nexus.pub looks like this

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCmX80rNtRCYVK3XVQO31ZRMKHA0uYD31WoYTRLVZ3Gd27eKFYZYYfdMktvDiPFI+Xzy2QjnvwikuPEQVcQtMnBdTcWZ1xrAGEXUugKeT2eIkA61aAxshXqSW0tErFl++P3Dk6hRjWC0DkRIzVG1E5d8eSVwK6iU6aNSgjdQbLi/hVUrsQlwSABO4VSylk1Vdofgy8maFHBAfh4EY4wFUSYzQ+OXZk1rlGaEIgce+CHlfAzDDz6N0/TbXV2h2kkC1kxTHnQecevsdmc9MtNVuQU0eXGZIz7Dy0K+dFmzCTTa/QOyT9W4mEik9icbDWOWhUk65lGhZhTmdV9KX+64t1SqIqDePp/vv68d0zdoWfvmX5IaBYesCcEXeMVtQf/SgZ1VPLGk+pKT+4S6+tpLOsjEW+HneKCvWBDntdSTL2Lw6/OcGZZupz/VoU3PLYH+DlR1koEQfLD8q0OeMdf1Io+VKZ32K1+Y8JcucQRK8vSWb2W8rvXlz47f36dD0y1G/E= root@ip-10-17-1-106 <- you should change this name to be something more meaningful

Let’s say you want to connect to a server as the ubuntu user. Connect to the server and find the ubuntu user’s home directory, i.e. /home/ubuntu, then go to the .ssh/ folder and append the public key to the authorized_keys file (or create one if it doesn’t exist). If you want to connect as root then because root’s home directory is /root, you should add to /root/.ssh/authorized_keys.

There are several formats that you can choose, e.g. RSA, DSA. Common formats nowadays are RSA and ED25519 (using common formats mean you can use them in most cloud providers i.e. AWS, GCP, Azure).

In Windows, to generate a keypair, use PuTTY Key Generator (puttygen.exe).

The highlighted text is what you need to paste to authorized_keys. Save this as nexus.ppk

A .ppk file contains both public and private key.

If you are on Windows and you have passphrases on your keys, then it is recommended to use pageant.exe. This is a Windows software that stores your keys in RAM. You add nexus.ppk to Pageant once, enter your passphrase, then we you use PuTTY to connect to a server that requires nexus.ppk, you don’t need to enter the passphrase anymore. Once your Windows restarts, you need to re-enter again.

Import and export from .pem format to .ppk format

To convert between .pem and .ppk, use puttygen.exe’s Conversions menu

SSH Tunnel

Now that we’ve got the basics covered, let’s get to the exciting stuff. There is concept of SSH tunnels. This basically means you can have other connections inside your SSH session. And they are encrypted too (except in a few cases explained below).

In this example, you have an SSH connection from a Windows machine to a Linux machine. Inside this SSH session there are 2 tunnels, one that connects Chrome browser to a httpd process running on port 80 on the Linux machine and a HeidiSQL application connected to a MySQL server on the Linux machine running on port 3306.

The Linux machine itself only has only port 22 open to the outside world (i.e. it is firewalled/has AWS security group that only has port 22 open to the outside world).

There are 3 types of SSH tunnels.

In this scenario, you are in a Windows machine, which an SSH session to a Linux server called andromeda.

Local port forwarding

Don’t forget to add before you open the session

This means when an SSH session is established, there will be a tunnel, at one end (your end), it is listening at localhost port 9900, at the other end it is connected to the server’s localhost port 3306 (remember that is localhost from the server’s perspective, so a process listening at port 3306 at andromeda).

If you start HeidiSQL and point it to 127.0.0.1 (i.e. localhost) and port 9900, then you will be connected to whatever is listening on port 3306 on the server’s localhost, which in my example here is a MySQL server. We use this method to have 1 single staging database server where all developers share.

You could also do something like this

This means if you connect HeidiSQL to localhost port 9900, it will arrive at the server’s end (i.e. andromeda), then the server will connect it to the server with IP address 10.17.1.101 port 3306 (which it is not andromeda, but another machine reachable from andromeda). In this case andromeda acts as a jump host/jump machine. This is a very useful method to use in AWS, GCP, Azure, usually to reach a database server which is not accessible from the internet but only accessible from the application server/backend server. So you use the application server as a jump host.

Remote port forwarding

Remote port forwarding is like the opposite to local port forwarding. The difference is, with local port forwarding, the tunnel is listening at the local part (i.e. you home computer). With remote port forwarding, the tunnel is listening at the remote part (i.e. andromeda), whatever is connected to the remote part, it will be forwarded.

In this example below, whatever is connected to andromeda’s port 9090, will be forwarded to my home machine’s localhost port 8080.

In this example below, whatever is connected to andromeda’s port 9090, it will forwarded to 192.168.1.127 port 80 (think this as, a packet goes into the tunnel at andromeda port 9090, it appears at the end of the tunnel at my home machine, then the packet gets sent to a computer with IP address 192.168.1.127 port 80).

Typical use case for this is, if you have home network that is not reachable from the internet and you want to allow a machine to access your home network. Kind of inviting someone to have access to your neighborhood.

Combining local port forwarding and remote port forwarding to pierce through firewalls or NAT gateways

An interesting method is to combine both local port forwarding and remote port forwarding to what I like to call a rendezvous meeting in the middle.

To make it simple to imagine, let’s imagine a typical home network, behind a Wi-Fi router that is doing NAT. You are on a that kind of home network. You have a friend in the same kind of home network. Both of you are running MySQL servers on port 3306 on your home machines. Normally, you can’t connect to your friend’s machine’s MySQL server (unless you have configured some sort port forwarding on the Wi-Fi router, but let’s assume you don’t have this configuration, because it means other people can also connect thus less secure). Your friend also can’t connect to your machine’s MySQL server.

You both have SSH access to a “server in the middle”, let’s call it sunflower.

In order for you to connect to your friend’s MySQL server, steps to achieve this are

  • Your friend to open a remote port forwarding tunnel listening on sunflower’s localhost port 8080 (or any other port number) and connected to localhost:3306 (i.e. friend’s MySQL server).
  • You to open a local port forwarding tunnel listening on your home machine’s localhost port 9090 (or choose any other port number) and connect it to sunflower’s port 8080.
  • When you then connect HeidiSQL to port 9090, the packets will “appear” on sunflower’s port 8080, which instantly go inside another tunnel and “appear” at your friend’s port 3306, which is MySQL.

If your friend wants to connect to your MySQL, then do the reverse

  • You open a remote port forwarding listening on sunflower’s port 8080 and connect it to your localhost port 3306.
  • Friend to open a local port forwarding listening on his localhost port 9090 and connect it to sunflower’s port 8080.
  • When your friend connects his HeidiSQL to his localhost port 9090, it will be connected to your MySQL.

Dynamic port forwarding

Dynamic port forwarding is basically a SOCKS proxy. If you enable it in a SSH connection to a server with IP address 1.2.5.6 and you set up your browser to use the SOCKS proxy, then all your browser connections will appear to come from the server’s IP address 1.2.5.6.

You only need to specify source port, then click add.
Point your browser to use SOCKS v5 on localhost port 8181. I am using Firefox here.
Doing a Google search for “what is my IP address” resulted in the server’s IP address (the server that I am connected to in SSH)

In a way this is like having a browser only VPN.

Leave a Reply

Your email address will not be published. Required fields are marked *