You are on page 1of 20

5/27/2019 An Illustrated Guide to SSH Tunnels

HOME (HTTPS://SOLITUM.NET/) ABOUT (HTTPS://SOLITUM.NET/ABOUT/)

F E AT U R E D ( H T T P S : / / S O L I T U M . N E T / TA G / F E AT U R E D / )

S T O R I E S ( H T T P S : / / S O L I T U M . N E T / TA G / S T O R I E S / ) TUBBING (HTTP://TUBBING.COM/)

15 Aug 2015

An Illustrated Guide to SSH Tunnels


Scott Wiersdorf (/author/scott/) on system administration (/tag/system-administration/), ssh (/tag/ssh/),

security (/tag/security/), featured (/tag/featured/)

SSH tunnels can provide secure connections through insecure or untrusted networks and may
also be used to securely route through rewalls.

About This Guide


This guide began as a personal document to help me learn and remember how SSH tunnels work
and has been several years incubating. If you nd errors or think of additional examples that you
believe would be helpful, I'd be delighted to know about (/about) them.

Terminology
Throughout this guide we use "SSH" to refer to the SSH protocol or the world of SSH things and
use ssh to refer to the ssh(1) program itself. "We use ssh and sshd to make SSH
connections." The examples in this tutorial are based on OpenSSH 0.9.8 and later.

As a preview of what's to come, here is an illustration of a local SSH tunnel that allows client-
host to connect to www on port 80, tunneled through tunnel-host over SSH:
https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 1/20
5/27/2019 An Illustrated Guide to SSH Tunnels

This tunnel is created with this shell command:

client-host: $ ssh -L 8080:www:80 tunnel-host -N

Client Host, Tunnel Host


SSH tunnels always have at least two hosts involved1. The rst host is the client host, or the host
from which we run ssh to de ne the tunnel. The second host is the tunnel host where the
tunnel connections are created. To build an SSH tunnel, the client host must be able to reach the
tunnel host via SSH.

Ingress, Egress
In the physical world of plumbing we have pipes, and one end of a pipe is usually as good as the
other. With SSH tunnels, the ends of the tunnel are not interchangeable as connections may be
initiated at only one end.

We will call the initiating side, entrance, or "listening" end of the tunnel ingress and the
terminating or exit end of the tunnel egress.

In ssh syntax, the bind-address:port pair is always the ingress and the host:hostport
pair is always the egress. The ingress bind-address is nearly always localhost 2 and since this
is the default in ssh , it is often omitted and we have only the ingress port .

The tunnel egress can be a little confusing. The SSH portion of the tunnel ends at the host where
the nal SSH connection is made, though the tunnel host will forward the TCP connection to the
speci ed host:hostport if host is not localhost . TCP traf c past the tunnel host is not
secured by SSH3 and any security would be determined by the protocol being tunneled (e.g.,
HTTPS, IMAPS, etc.), if any.

Using Tunnels
Any service using the tunnel must connect to the ingress port and the connection will come
out at the egress host:hostport . To create an SSH tunnel, the tunnel host must be able to
reach the egress host:hostport via TCP.

We may nd that our way is sometimes blocked by rewall rules or network topologies4. Some
complex tunneling involves multiple client hosts, tunnel hosts and ingress/egress pairs.

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 2/20
5/27/2019 An Illustrated Guide to SSH Tunnels

Syntax
We'll cover the basics of tunnel syntax here, copied more or less from the ssh(1) man page.
We'll also cover some general ssh options that are often used in SSH tunnels:

-N indicates that we will not be sending any commands over SSH and that ssh should not
open stdin or execute any commands on the tunnel host.

-A enables agent forwarding. You should never enable forwarding on hosts you do not trust
or otherwise do not wish to use agent forwarding on. See An Illustrated Guide to SSH Agent
Fowarding (http://www.unixwiz.net/techtips/ssh-agent-forwarding.html) and Using SSH
Agent Forwarding (https://developer.github.com/guides/using-ssh-agent-forwarding/) for
more information5.

Local tunnels
A local tunnel is a tunnel whose ingress is located on the client host. Local tunnels are created
with the -L option. The basic syntax of a local tunnel looks like this:

client-host: $ ssh -L port:host:hostport tunnel-host -N

If our client host were named you , we might make a connection to a web server through a
bastion host:

you: $ ssh -L 8080:www.example.com:80 (http://www.example.com:80)


bastion-host -N

And to use the tunnel we connect our web client to the tunnel's ingress at localhost:8080 :

you: $ curl http://localhost:8080/some-file.txt (http://localhost:8080/some-

The HTTP request goes through the tunnel host bastion-host ; the tunnel egress is at
www.example.com:80 (http://www.example.com:80) .

Keep in mind that the SSH portion of the tunnel is between the client host and the tunnel host;
traf c between the tunnel host and www.example.com (http://www.example.com) is not
protected by SSH.
https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 3/20
5/27/2019 An Illustrated Guide to SSH Tunnels

Remote tunnels
A remote tunnel is a tunnel whose ingress is located on the tunnel host. Remote tunnels are
created with the -R option. The basic syntax of a remote tunnel is identical to local tunnel
syntax except for the -R option:

client-host: $ ssh -R port:host:hostport tunnel-host -N

If we wanted to make www.example.com:80 (http://www.example.com:80) available to a


remote server via the remote server's localhost:8080 , we might do something like this:

you: $ ssh -R 8080:www.example.com:80 (http://www.example.com:80)


bastion-host -N

The tunnel's ingress is on bastion-host 's localhost:8080 and the egress points to
www.example.com:80 (http://www.example.com:80) :

bastion-host: $ curl http://localhost:8080/some-file.txt (http://localhost:8

This is a somewhat contrived remote tunnel example, since we might just as easily have made a
local tunnel routed from 8080 to port 80 over localhost on the tunnel host itself. A more
realistic use for remote tunnels will be given below in the examples.

Dynamic Proxies
The third kind of tunnel we'll cover is a dynamic proxy. Dynamic SSH proxies forward
application connections using the SOCKS4 or SOCKS5 protocol. This means that any application
wishing to use a dynamic SSH tunnel must know how to communicate via one of these protocols
and be specially con gured to do so. Con guring applications to use SOCKS-based protocols is
beyond the scope of this guide.

The basic syntax of a dynamic proxy looks like this:

client-host: $ ssh -D port tunnel-host -N

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 4/20
5/27/2019 An Illustrated Guide to SSH Tunnels
Dynamic proxies are different from local and remote tunnels in that they de ne only a tunnel
ingress on the client host; the tunnel egress is determined dynamically using the SOCKS protocol
as the connection passes through the tunnel host.

If we wanted to route all of our web client's traf c through a remote host via SSH, we might start
a dynamic tunnel like this:

you: $ ssh -D 8080 remote-host -N

and then we can make HTTP requests that are now encrypted through to the tunnel host:

you: $ curl -x socks5://localhost:8080 http://www.example.com (http://www.ex

Our web client makes a SOCKS connection to the tunnel ingress on you at localhost:8080 ;
ssh uses the SOCKS protocol to determine that the egress should be at www.example.com:80
(http://www.example.com:80) . When the connection arrives at remote-host , ssh
forwards the TCP connection to the egress. Any SOCKS compatible application may use this sort
of proxy.

This is a powerful method to secure otherwise vulnerable application traf c.

Examples
Now that we've covered the basic syntax of local, remote, and dynamic tunnels, we'll show a few
illustrated examples of how these might be used. The following image may be used as a reference
to understand the colors and symbols in the subsequent examples:

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 5/20
5/27/2019 An Illustrated Guide to SSH Tunnels

Securing Email
We're connected to an untrusted network and we want to check our email via an
unencrypted IMAP connection6.

If we were to check our email over this untrusted network, we would be sending our credentials
in the clear and anyone else on the network could capture our credentials in transit:

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 6/20
5/27/2019 An Illustrated Guide to SSH Tunnels
If we rst make an SSH tunnel to a trusted host, then our traf c passing over the untrusted
network will be encrypted all the way through to the trusted host, out of harm's way:

This command creates the SSH tunnel:

you: $ ssh -L 2143:mail-server:143 trusted-host -N

We create a local tunnel ( -L ) whose ingress is on you at localhost:2143 and whose egress is
at mail-server:143 . The tunnel is secured via SSH between the client host you and the
tunnel host trusted-host .

To use this tunnel, we con gure our mail client to use localhost:2143 , rather than mail-
server:143 . The tunnel will forward all traf c it receives on port 2143 to mail-server port 143
via trusted-host .

Note that our network traf c leaving the tunnel egress at trusted-host for mail-server will
not be protected by SSH.

Poor Man's VPN

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 7/20
5/27/2019 An Illustrated Guide to SSH Tunnels

We have an internal company web server we need to access at web-server ports 80 and
443. We are outside of the corporate network but the network administrator has con gured
a bastion host for SSH connections.

We'll use the bastion host as our tunnel host to forward our web traf c into the trusted network:

Here is the command to create this kind of tunnel:

you: $ ssh -L 8080:web-server:80 -L 8443:web-server:443 bastion-host -N

We create one local tunnel whose ingress is on localhost:8080 and whose egress is at web-
server:80 . We create a second local tunnel whose ingress is at localhost:8443 and whose
egress is at web-server:443 . Both tunnels pass through bastion-host .

We can make HTTP and HTTPS requests to localhost ports 8080 and 8443 which will be
forwarded to web-server ports 80 and 443 respectively:

you: $ curl https://localhost:8443/secure.txt (https://localhost:8443/secure

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 8/20
5/27/2019 An Illustrated Guide to SSH Tunnels

Double Tunnel
This example illustrates a double tunnel to get around two rewalls.

We are connected to VLAN 1 and want to check out a Git repository in VLAN 3 .

The bastion host spanning VLAN 1 and VLAN 2 cannot access to the Git server, but a test
server that spans VLAN 2 and VLAN 3 can. The Git service listens on port 7999.

Here is a picture of our network:

We need to rst tunnel into VLAN 2 , using bastion-host as the rst tunnel host. We create a
local tunnel to bastion-host with an ingress on you 's localhost:7999 and an egress on
bastion-host 's localhost:2999 :

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 9/20
5/27/2019 An Illustrated Guide to SSH Tunnels

This command creates the tunnel:

you: $ ssh -L 7999:localhost:2999 bastion-host

Notice how we did not use -N with this tunnel. The reason is that we can use this SSH
connection to make our second tunnel, using test-server for the tunnel host:

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 10/20
5/27/2019 An Illustrated Guide to SSH Tunnels

This second tunnel is created from bastion-host :

bastion-host: $ ssh -L 2999:git-server:7999 test-server -N

In this second tunnel, bastion-host is acting as the client host with ingress on bastion-
host 's localhost:2999 . The connection goes through the tunnel host test-server with
egress at git-server:7999 .

This double tunnel is possible because bastion-host is hosting the rst tunnel's egress at
localhost:2999 and the second tunnel's ingress at localhost:2999 , thereby linking the two
tunnels.

Chaining tunnels through multi-homed hosts can get you far inside of networks.

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 11/20
5/27/2019 An Illustrated Guide to SSH Tunnels

Now we can connect to git-server through the tunnels:

you: $ git clone ssh://git@localhost:7999/some-repo.git

For existing repos, we can add a new remote:

you: $ git remote add tunnel ssh://git@localhost:7999/some-repo.git


you: $ git pull tunnel master

Local HTTP Proxy


Remote tunnels are less common than local tunnels, but are no less useful in the right situation.
In this example we will use a remote tunnel to create a connection back to our client host which
will be acting as an HTTP proxy.

We need to fetch a remote le via HTTP from our beta server, but the beta server
connected to VLAN 2 does not have access to the public internet, nor can it connect to
other hosts in VLAN 2 . The only access permitted to the beta server is through a bridge
host that sits on VLAN 1 and VLAN 2 .

Our own host ( you ) may connect to the bridge host as well as the public internet.

Here is the network:

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 12/20
5/27/2019 An Illustrated Guide to SSH Tunnels

Our rst step is to run an HTTP proxy on you . Selection and installation of an HTTP proxy is
outside of the scope of this document, but for the sake of this illustration, we'll use tinyproxy
which is freely available and requires little to no con guration. We'll assume our tinyproxy is
con gured to listen on all interfaces (including localhost ) at port 8888 and we'll run it in the
foreground so we don't forget about it7:

you: $ tinyproxy -d

Now we have a means for hosts connected to you:8888 to access the public internet:

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 13/20
5/27/2019 An Illustrated Guide to SSH Tunnels

However, because beta-server cannot access you directly to access the HTTP proxy, we need
to create a tunnel from the bridge host back to us:

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 14/20
5/27/2019 An Illustrated Guide to SSH Tunnels
This commands creates the tunnel:

you: $ ssh -R 8080:localhost:8888 bridge-host

This creates a remote tunnel whose ingress is on bridge-host 's localhost:8080 and whose
egress is at you 's localhost:8888 , which is where our tinyproxy daemon is listening.

Pay close attention to where the ingress and egress are: they are in opposite positions to where
they are in a local ( -L ) tunnel, meaning the tunnel is entered on the tunnel host rather than on
the client host. This is the de nition of a remote tunnel.

With this tunnel as it is, we could access the public internet from the bridge host like so:

bridge: $ curl -x http://localhost:8080 (http://localhost:8080) http://www.e

While this is neat, we're only halfway done. As we did in the double local tunnel, we'll use this
SSH connection to make our second remote tunnel:

To create this tunnel:


https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 15/20
5/27/2019 An Illustrated Guide to SSH Tunnels

you: $ ssh -R 8080:localhost:8888 bridge-host


bridge: $ ssh -R 9999:localhost:8080 beta-server -N

The second remote tunnel has ingress at beta-server 's localhost:9999 and egress is at
bridge-host 's localhost:8080 , which is where the rst tunnel's ingress is. The tunnels join
on bridge-host so that HTTP connections made on beta-server:9999 (the effective ingress)
are forwarded to tinyproxy running on you:8888 (the effective egress):

beta: $ curl -x http://localhost:9999 (http://localhost:9999) https://www.ex

Troubleshooting
As mentioned at the beginning of this document, before you can create an SSH tunnel, the client
host must be able to connect to the tunnel host via SSH. This is the rst thing you should test
when troubleshooting.

If that works properly, you might check to see if rewall rules prohibit opening any of the ports
you are trying to forward. This may require a little experimentation or co-opting of existing open
ports.

Further, many SSH installations are customized. Some administrators require public key
authentication, others require password authentication8. Is agent forwarding permitted?
Prohibited? Are you using the same keypair for every connection or do some connections require
one keypair and others another?

Many of these con guration differences can be put into ~/.ssh/config les on the hosts you
use as clients and tunnels, allowing you to use aliases and simplifying your SSH connections.

Try ssh in verbose mode; did you know ssh accepts up to three -v ags? Finally, there is no
substitute for the manual. OpenSSH and other SSH variants have extensive documentation that's
worth a gander.

Caveats
SSH tunnels can provide strong security for data in transit and can compensate for baroque
network topologies. SSH tunnels can also be used to circumvent reasonable security measures
designed to prevent certain kinds of network access.

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 16/20
5/27/2019 An Illustrated Guide to SSH Tunnels
Knowledge is a two-edged sword: I hope that you will use this information to do good, not evil. I
neither encourage nor condone using SSH tunnels to bypass your network administrator's
intentions to protect access to speci c hosts on the network.

Be wise, be good!

1. A third possible host is de ned in the egress de nition, but if the host is set to "localhost", then
either the client host or tunnel host will be used for egress (depending on if the tunnel is a
remote tunnel or a local tunnel, respectively). ↩

2. There are legitimate, unusual cases for binding to other interfaces. See the "GatewayPorts"
setting in "ssh_con g(5)". ↩

3. Except for tunnels that are built to forward SSH connections. ↩

4. Yes, sometimes this is deliberate and I encourage you to never do anything unlawful,
unethical, or imprudent. ↩

5. This tutorial will never explicitly use agent forwarding, though the author makes heavy use of
it in quotidian use. ↩

6. We're going to ignore the fact that this isn't a secure way to set up a mail server. ↩

7. Running an open HTTP proxy on a whim is asking for trouble. ↩

8. The author once worked for a company whose administrators insisted that password
authentication was more secure than public key authentication because you could simply
change the password when somebody left the company. ↩

(http://twitter.com/share? 
text=An%20Illustrated%20Guide%20to%20SSH%20Tunnels&url=https://solitum.net/an-

illustrated-guide-to-ssh-tunnels/)  (https://www.facebook.com/sharer/sharer.php?

u=https://solitum.net/an-illustrated-guide-to-ssh-tunnels/) 
(https://plus.google.com/share?
url=https://solitum.net/an-illustrated-guide-to-ssh-tunnels/)

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 17/20
5/27/2019 An Illustrated Guide to SSH Tunnels

(/author/scott/)
Scott Wiersdorf (/author/scott/)
()

I'm…reluctant.

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 18/20
5/27/2019 An Illustrated Guide to SSH Tunnels

2 Comments solitum 
1 Login

 Recommend 4 t Tweet f Share Sort by Best

Join the discussion…

LOG IN WITH
OR SIGN UP WITH DISQUS ?

Name

Rafael Bianco Nacif • a year ago


I know you wrote this a long time ago. But this guide helped me understand SSH tunnels and I
just wanted to say thank you! =D
Cheers!
10 △ ▽ • Reply • Share ›

scottw Mod > Rafael Bianco Nacif • a year ago


Hey—you’re welcome; glad you found it useful :)
△ ▽ • Reply • Share ›

ALSO ON SOLITUM

NameCheap Review OpenSSL, OS X "El Capitan" and Brew


2 comments • 2 years ago 6 comments • 2 years ago
scottw — Hello Jean-Philippe, my advice would scottw — Thanks Robert. I've updated my post
be to contact Namecheap and explain that you to make it clear that my instructions are meant
want to transfer *one* of your domains from … only for cases where the canonical ways of …

Vivint Wireless vs Comcast/Xfinity Update: alpine-perl:5.24.1 Docker Image


2 comments • 2 years ago 2 comments • 2 years ago
scottw — Sorry friend, the speeds are not fake. I Thayne Trevenen — Well yeah but don't we all?
measure using `iperf3` at regular intervals (ask
me what that means if you're not into …

✉ Subscribe d Add Disqus to your siteAdd DisqusAdd 🔒 Disqus' Privacy PolicyPrivacy PolicyPrivacy

PREVIOUS POST

Alto's Adventure (done)


I'm always happy when I can master a game and put it away for a while. I don't play…

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 19/20
5/27/2019 An Illustrated Guide to SSH Tunnels

NEXT POST

ContainerCon 2015, Seattle


Lot's of smart people doing clever things! I talked with Brian Dorsey and Deepak Singh
for a few minutes…


(https://solitum.net/rss/)   
the usual (https://solitum.net) © 2019
Proudly published with Ghost (http://ghost.org)

https://solitum.net/an-illustrated-guide-to-ssh-tunnels/#fnref:8 20/20

You might also like