SSH, aka Secure Shell Protocol, is a network protocol that can be used to perform network services. We use it mainly for accessing remote servers to perform activities in the terminal shell. It is a cryptographic protocol and helps in accessing remote servers securely.
A notable feature of SSH is port-forwarding/tunneling. This feature enables us to forward our traffic via an SSH connection between an SSH client machine and an SSH server machine.
SSH Port Forwarding
SSH forwarding comes in quite handy when we need to transport network data for unencrypted services, bypass firewalls, or even circumvent the lack of a proper static IPV4 Address (by using servers in the cloud). Using this, we can forward TCP-based traffic via different ports to the SSH machine server. The types of port forwarding available are:
Local
In local port forwarding, the SSH connection of the SSH client is forwarded to the destination server. For example, let's assume we have a website running at port 80
in our local system. If we forward port 80
using local port forwarding, then the website at port 80
can also be accessed by the destination server.
Before port forwarding, the website was only accessible from our local system.
Remote
In remote port forwarding, the SSH connection is forwarded from the SSH server to the destination server.
Let's say, in a remote server, we have a database service running at port 5432
. If we perform remote port forwarding for that particular port, we can also access the database service locally.
Previously it was accessible from the remote server only, but now it can be accessed by us in our own system.
Dynamic
In dynamic port forwarding, the traffic of different applications can be forwarded to the destination server. Instead of forwarding certain ports only, we can forward the traffic of our entire system to the destination server. Whatever we have running at a particular port in our system will be accessible at the remote destination server as well.
Let's see how we can perform SSH remote port forwarding. Our goal will be publicly exposing app traffic from an internal network to bypass the lack of a static IPV4 address.
Service Setup
Let’s set up a service file on our local machine. Doing this will make you never have to manually run the SSH port forwarding, as the service file will take care of that.
Write the file below into the /etc/systemd/system
directory.
Key components from the contents of the system file specified above are:
remote-tunnel
: This is the name of the SSH config specified in~/.ssh/config
. It can be anything you want to use.User=example
: The user directive is very important as it specifies the user ID to be used when running the service. For example, if the user you specified does not contain the SSH configuration forremote-tunnel
in their/home/example/.ssh
directory, then the service will fail.ExecStart=/usr/bin/ssh -o ServerAliveInterval=120 -R {remote_server_port}:localhost:{local_server_port} -NT remote-tunnel -v
: This is the command that will perform remote SSH port forwarding.ExecStart
executes the command and its parameters.SyslogIdentifier=reverse_proxy
: This will prepend the linereverse_proxy
in the system log file for logs generated by the service, making it easy to identify the logs originating from this system service. To view the logs, you can usejournalctl -u service_name.service -f
.
Now let's make the system aware of the new service file, enable it, and start the service. For that, you can use the following commands.
To make the systemctl
daemon aware of changes:
To enable and start the service:
To check the service status:
SSH Config Setup
As you can see in the service file provided in a few sections above, I used the user example. Replace the user example with your local or root user and set up the SSH configuration in the user's home directory.
- Generate an SSH key
ssh-keygen -t ed25519 -C “ssh@example.local”
2. Create an SSH config file
Add the following details into the config file at ~/.ssh/config
. Create the file if it does not exist.
Here we've specified,
i. Host
= Identifying name of the config. For example, you can now run ssh remote-tunnel
instead of using remote-user-name@192.168.1.99
for SSH connections.
ii. HostName
= IP address of the remote server.
iii. User
= Username of the user account we will use to SSH into the remote server.
iv. IdentityFile
= Path to the SSH private key.
Finally, we’re ready to perform remote port forwarding.
255
.Further details can be gained by viewing logs of the service file using
systemctl status
or journalctl -f
commands.Exposing the Service Publicly
Set up a domain – A record to point towards the remote server's IP address.
I’ll be using the example.com.local
domain which I've pointed to 192.168.2.199
.
22
, 80
, 443
. This is just a security practice to make sure other services in your remote server are not accessible publicly unless you want them to be publicly accessible.
Now set up an Nginx configuration file in the sites-available
directory. We will route web traffic that is received by Nginx to the local port 8980
.
This is also known as reverse proxying, where requests received at one server are then forwarded to another server either in a remote location or locally available in our system.
Now symlink it to /etc/nginx/sites-enabled
using the command below:
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Finally, we'll generate an SSL certificate. First, let's install Certbot.
sudo apt update && sudo apt install certbot python3-certbot-nginx
Now we can generate the certificate using the command:
certbot --nginx
The Nginx plugin of Certbot will detect the Nginx configuration available in the sites-enabled directory and then give us options to generate a certificate for that particular server name. Once generated, it will also write the SSL configuration in the specific Nginx configuration, making it an easy, one-stop solution for setting up SSL/TLS encryption for our websites.
Finally, if all goes well, we should be able to access the service hosted internally in our local server from the public internet on the domain you've assigned it to.
Please comment below if you have any queries, I try to regularly update my articles to ensure legibility.