SSH Tunnels in simple words

At some point, I had a need to figure out simple SSH tunnels: how to launch them and which tunnels could help me (an ordinary web developer). I managed to figure this out and I decided to share the explanations in a simple, understandable form.

Immediately an important thought: I absorbed the basis for this post in my head from this article, for which I thank the author very much! This article is a little more hardcore to understand, but much more comprehensive than mine. My option is for those who understand almost nothing about this and want to understand the pictures and details.

If the video format is more convenient for you, you can watch it at YouTube.

Why did the need for tunnels arise?

One way or another, I use SSH tunnels regularly in my work. Sometimes I need to access a resource, database or service that is on a closed network, but I have SSH access to one of the servers on that network.

Everything described below is purely an interpretation of my experience and study of various articles on the topic. I would be very grateful if you find a mistake and let me know via the comments. Thank you!

My first acquaintance with tunnels occurred when I tried to deploy a developed project on a client’s server. I arrived at the customer’s office, having previously saved in notes links to our repositories, names of Docker images, etc.

But a surprise awaited me. The client simply gave me a keyboard, showed me the monitor and said “here, unfold it.” Due to the client's security policy, I could not gain remote access to this server in order to conveniently upload everything from my office. Therefore, I had to manually deploy everything on this server, entering all the links and names by letter.

I imagined with horror how I would manually enter each link for the repository and realized that all this would take more than an hour.

I decided not to waste a minute on this and spent enough time connecting this server to our openvpn network, so that I could then easily access this server remotely. It was quite tedious, but I achieved my goal and then released it remotely.

Having solved this problem, I began to study other, more simplified ways to solve this problem. This is how I became acquainted with tunnels.

The basic idea of ​​tunnels

If you can’t do it directly, then why not go around it?

If you can’t do it directly, then why not go around it?

In general, the essence of the tunnel is clearly depicted in the animation above. But I’ll add some text:

  1. You have a server that you want to access, but you can't go to it directly. Let's call it server 1;

  2. Between you and the target server there is a server that has access to the target and to which you have SSH access. Let's call it server 2;

  3. You can log in via SSH to server 2 and, through the console, send any requests to server 1. You can also log in via SSH to server 1 and move on.

This is roughly how SSH tunnels work. But we don’t always have to go to one server and send requests from it to the next. To simplify this process we can use port forwarding (Port Forwarding).

Port forwarding on fingers

It took me some mental fuel to understand how port forwarding works.

We write port 81, 80 in our minds

We write port 81, 80 in our minds

Let's expand the situation described above:

  1. We have an S1 server that we can access on any port;

  2. There is a server S2, whose port 80 we want to access through our S1;

  3. With one command we can open port 80 on S1 so that it forwards these requests to port 80 of S2 and then sends us the response;

  4. Also, for example, with one command, we can open port 81, which will forward the request to port 80 of another S3 server.

It is this “forwarding” of ports that is called port forwarding or Port Forwarding. Now let's move on to real cases of using tunnels.

Real examples from practice

Case with nginx

Let's consider a simple case. We have a server that is closed to the world, but we have SSH access to it. The server is running on this server nginxwhich listens on port 80.

In order to access what is published on the server's port 80, we can open a tunnel that connects to the server and publish on local port 80. That is, we will knock on localhost:80 and see exactly what is published on port 80 of the remote server. To do this, enter the following command:

ssh -L 80:127.0.0.1:80 remote.server.address

After entering this command, we will connect to the server via SSH and, what is important, while this connection is active, we will have this tunnel active. Now we can open localhost:80 in the browser and what is published on port 80 of the remote server will open. More details about what's in this command:

  1. The first parameter is the “-L” flag, which indicates that we need to open a tunnel and start listening to the port.

  2. Next, we indicate the interface on which we want to listen (by default, localhost and in this example it is not specified). In our case, we indicate port 80 (in the figure “local port”).

  3. Next, we indicate where we will need to connect on the remote server. In our example, 127.0.0.1, which means that the tunnel will be forwarded to the remote server itself. What this value can be replaced with can be read below.

  4. Port on the remote server – in our case it is 80. If nginx, for example, saw it on 8080, then we would indicate 8080 in this place.

  5. Remote server address. We specify it in the same way as when connecting to SSH normally.

Now you can go to localhost in the browser and see what is on the server on port 80. In my example, you can only see the nginx error that nginx-proxy produces on the server.

Case with Postgres database in Docker

Now to another useful case that we encounter during development, one way or another, regularly.

  1. There's me and my laptop

  2. There is code for my backend application on the laptop

  3. Basically I work with a local copy of the database during development

  4. For some reason I need to connect to a database that is located on a server (test or production) in an isolated Docker network.

    In a roundabout way directly to the base

    In a roundabout way directly to the base

When we talk about working through an IDE to work with a database, we can easily use the built-in tunnel mechanism (product instructions from JetBrains here). But difficulties arise if we want to connect to this database from our application code.

By the way, this point is relevant not only for the database. Sometimes we just need to connect to different services that are located on an isolated network that we can access via SSH. So, this example is only illustrative and the scope is extremely wide.

To solve this issue, just enter the following command:

ssh -L 5432:postgres-db:5432 remote.server.address

Here the situation is similar to the previous one. Only instead of the local IP address of the server itself, we indicate the IP address (in this case, the host name postgres-db) as the target address.

In the case of working with Docker, I use this containerto know the addresses of all containers on the server. After startup, the hostname of each container can be obtained in the file /etc/hosts. And after a restart, the ip address changes, but the host remains

I can already point out further localhost:5432 as a postgres server in my application and connect directly to the database in the docker container on that server.

You can run as many such tunnels as you like. All are launched by analogy. For example, you can build a diagram like this:

multiple tunnels via one server

multiple tunnels via one server

  1. There are servers S1, S2, S3.

  2. S1 has access to S2 and S2

  3. I only have access to S1 via SSH on port 22

  4. Servers S2 and S3 run Postgres on 5432 and HTTP on 80 ports, respectively.

  5. I launch 2 tunnels and work with these servers as if they were local ones. The commands to start the tunnels are below:

ssh -p 22 -l 5432:s2:5432 s1
ssh -p 22 -l 80:s3:80 s1

Reverse tunnels

Ok, we figured out how to access a remote server that is located on the same network as the server available to us. But sometimes there are situations opposite to this. For example:

When the client wants to see "right now"and you’re sitting in a cafe coding...

When a client wants to see “right now”, and you’re sitting in a cafe coding…

  1. I'm working on my laptop and I have a copy of the client's website running on port 3000

  2. I’m in a cafe with free wifi

  3. The client is in his office on a closed network

  4. I want to show the current version of the client's website directly from my laptop

  5. Both the client and I have access to the same server on the external network (for example, my VPS)

There are already ready-made solutions for such a case – localtunnel or ngrok. I use them myself when I need to quickly pick them up and show them. But sometimes I need to connect to a remote server, for example, to my local database to repeat some special case.

To solve this issue, a reverse tunnel (Reverse port forwarding) can help us. In order for the client to be able to see the site on his PC, I need to run the following command:

ssh -R 0.0.0.0:3001:127.0.0.1:3000 server.ru

Let's understand each parameter in this command:

From right to left:

  1. Server address that is accessible to both me and the client

  2. Local address and port that I want to make accessible (localhost:3000)

  3. Address and port on a shared server where my site will be displayed on port 3000

  4. A flag telling the SSH server that I intend to open a return tunnel to my local port

What will the client see

What will the client see

The client, after launching such a tunnel, can see our website at server.ru:3001.

True, doing this may be a little more difficult than “just typing in one command.” The fact is that out of the box the sshd daemon does not allow connections to the external interface.

In other words, the fact that we specified “0.0.0.0” as the listening address tells the server the following: “accept any connections on any interface on port 3001, even if the request comes from outside.” But this setting is usually disabled. In order for external connections to work, it is necessary in the file /etc/ssh/sshd_config add line GatewayPorts yes

You should be extremely careful and careful with this setting. Putting it into meaning yes you expose a port to the world and this can be a perfect security hole for attackers. ALWAYS CLOSE TUNNELS after finishing working through them.

What else can you use a return tunnel for?

I've also used reverse tunnels when debugging PHP projects. In the case when the code is located on a remote server and we work with the code via SFTP, there is a need to debug the code that is running on the server, but through local XDebug. Here the SSH tunnel also comes to the rescue, which starts and gives the remote server access to our PC.

Instead of a conclusion

That's all. This was a short summary about the tunnels that I use in my work. Thank you for attention! I will be glad if you subscribe to me on YouTube And Telegram

Similar Posts

Leave a Reply

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