博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

SSH Tunneling (TCP port forwarding)

Posted on 2013-11-25 09:25  bw_0927  阅读(618)  评论(0)    收藏  举报

http://www.spencerstirling.com/computergeek/sshtunnel.html

http://www.vdomck.org/2005/11/reversing-ssh-connection.html

 

tip: April 2009. I have found that the tunnels "collapse" (i.e. don't die, but no longer function properly) if the option "TCPKeepAlive yes" is used in the /etc/ssh/sshd_config file. Instead I set this option to "no" and add the lines "ClientAliveCountMax 3" and "ClientAliveInterval 15". Check the manpages for sshd_config for details.

SSH Tunneling is probably one of the coolest things that you can learn how to do. Basically, an SSH tunnel is an internet "pipeline" through which data can be sent. If used appropriately then your data is always encrypted, which prevents eavesdropping. But that's NOT ALL!!! SSH tunnels can also be used to connect machines on opposite sites of a firewall(s). More precisely, an SSH tunnel forwards a *local* TCP port to a *remote* TCP port through a firewall!!! This can be useful in any of the following situations:

1) You need to "talk directly" (to a specific TCP port) to a machine that is separated from you by a firewall/gateway (assuming that you can *at least* SSH into the gateway).

2) You can send data directly to a machine, but you don't like the fact that the data is sent unencrypted.

3) Both (1) and (2).

First application: Bypassing a Gateway
Consider the following situation: you are sitting on "mymachine", and you have a program that needs to send data to a TCP port (say 5900) on "remotemachine". If the two machines can talk directly then there is no problem, but what if "remotemachine" is behind "firewall"? In that case you cannot even refer to "remotemachine" directly - as far the outside world is concerned "remotemachine" doesn't even exist. All communication between "remotemachine" and the internet is done through "firewall".

If you *happen* to be able to SSH into the "firewall", however, then you are in luck. In that case, "firewall" knows about "remotemachine", and "mymachine" knows about "firewall", so maybe a menage a trois can be rigged up. The following SSH command (run on "mymachine") sets up a tunnel:

ssh -N -L 33642:remotemachine:5900 user@firewall

Then you could telnet (for example) to "remotemachine" port 5900 by running the command on "mymachine"

telnet localhost 33642

Let's dissect this a little. The main piece is "ssh user@firewall" - you are literally SSHing into the firewall. So when it asks for a password, give the one for your account on the firewall. The "-N" is not important - this just keeps the pipe open.

The "-L 33642:remotemachine:5900" piece is the RELAY part. This tells SSH that you don't want to actually OPEN UP A SHELL on firewall, but instead that you are merely using it as a relay. Notice that "remotemachine" doesn't necessarily need to be addressable from the outside - it is only necessary that "firewall" knows how to find it!!! The piece "-L" tells SSH to listen to port 33642 on "mymachine" (listen locally) and redirect any received data THROUGH "firewall" and on to port 5900 on "remotemachine". So, in essence, telnet'ing to "localhost 33642" is *like* telnet'ing to "remotemachine 5900".

There are a few things to note here:

1) port 33642 was chosen at random. You merely need a free port on "mymachine" to use and abuse (try "netstat" to list ports in use).

2) The session is encrypted between "mymachine" and "firewall", but in between "firewall" and "remotemachine" the data is sent IN THE CLEAR (i.e. as usual), so be aware (there is a way to fix this - see below).

3) It IS possible to "chain" up SSH tunnels to go through MULTIPLE gateways if necessary!

4) There is a "-R" switch that does the OPPOSITE - it captures traffic on the remote side and forwards it to the local machine.

Now that we know how to go through one firewall, let's learn how to chain up SSH tunnels to punch through multiple firewall levels! So I want to implement the diagram:

mymachine ---> Afirewall ---> Bfirewall ---> remotemachine

This is easy, as long as you have SSH accounts on both firewalls! Let me mention here that this is the "lazy man's" way - meaning that data is encrypted all of the way through EXCEPT (as usual) at the last level between "Bfirewall" and "remotemachine". Again, see below for a way to remedy this.

First, SSH into "Afirewall" (like you've ALWAYS known how to do, i.e. log in to a shell) and run "netstat" to find an open port there (say 4652 for our example). Then, while logged into "Afirewall" set up the last "hop" with the command:

ssh -N -L 4652:remotemachine:5900 user@Bfirewall

This command picks up port 4652 on "Afirewall" and forwards it THROUGH "Bfirewall" to its final destination at "remotemachine" port 5900.

Now, back on "mymachine" execute

ssh -N -L 33642:localhost:4652 user@Afirewall

This opens up a tunnel from port 33642 on "mymachine" and forwards THROUGH "Afirewall" to ITSELF on port 4652 (remember... when using the -L option the machine name that appears between the :: is resolved from the point-of-view of "Afirewall", so localhost is ITSELF).

You can obviously go through any number of firewalls this way - just run an SSH tunnel between each firewall IN TURN, always forwarding to the relaying firewall ITSELF except at the last "hop" (however, see below for greater security).

Using an SSH Tunnel to encrypt your session
SSH tunnels are not only useful for punching through firewalls, but they are also useful if you want an encrypted channel from source to destination. Let's suppose that you can talk directly to "remotemachine", then you could just directly

telnet remotemachine 5900

But you MIGHT not want to do that! You data goes over the internet in the open! There is a better way, that is if you have to ability to SSH in "remotemachine". If so then consider these commands (run on "mymachine"):

ssh -N -L 33642:localhost:5900 user@remotemachine
telnet localhost 33642

The first command creates an encrypted pipe from "mymachine" to "remotemachine" that - guess what - forwards data to ITSELF (see how the word "localhost" shows up between :: - that's "remotemachine" referring to ITSELF)! The second command opens up a telnet session on "mymachine" to port 33642. This session is then FORWARDED over the pipe to "remotemachine", which then forwards the session to ITSELF on port 5900! Pretty cool, huh? So your data is not running around in the open. Obviously, when SSH asks for a login here you should give your login information for "remotemachine".

You can combine this technique while you are punching through firewalls, too! The strategy is this: follow the technique for forwarding through multiple firewalls as outlined above, but at the last firewall don't "wimp out" and just "hop" over it. Instead, use the "forwarding to itself" method at EVERY step (this will require one extra step to the final "remotemachine").

Let's see how this goes: first, get an SSH *shell* session on each firewall (even the last one) going - probably by SSHing through each firewall successively. Find yourself a free port on each firewall. For simplicity I'll use an example with 4 firewalls, and the free ports will be 4651, 4652, 4653, and 4654 on each firewall, respectively (they could be the SAME numbers... obviously ports on different machines have nothing to do with each other). Then consider the sequence:

1) On "Dfirewall" run:
ssh -N -L 4654:localhost:5900 user@remotemachine

This sets up that final secure link between "Dfirewall" and "remotemachine" - using the technique that I just described, i.e. "remotemachine" forwards the final bit of traffic to itself, so there is NEVER a time when the data is out in the open.

2) On "Cfirewall" run:
ssh -N -L 4653:localhost:4654 user@Dfirewall

3) On "Bfirewall" run:
ssh -N -L 4652:localhost:4653 user@Cfirewall

4) On "Afirewall" run:
ssh -N -L 4651:localhost:4652 user@Bfirewall

5) On "mymachine" run:
ssh -N -L 33642:localhost:4651 user@Afirewall

That's IT! Now, on "mymachine" you could telnet into "remotemachine" with the command

telnet localhost 33642

Important TIP: in all of these examples I've been using "telnet", but really any program that needs to send to a port works. There IS ONE caveat! What if you forward to port 22 and try to use SSH as your generic "program"! In other words, after setting up the tunnel to port 22, you could *try* to SSH to "remotemachine" with the command

ssh -p 33642 localhost

Maybe this seems like a stupid thing to do (setting up an SSH tunnel just to get an SSH session), but many programs use SSH under the hood, so we'd better talk about it. The problem is that SSH will give you the very scary "WATCH OUT PEOPLE ARE WATCHING YOU" and "MAN-IN-THE-MIDDLE ATTACK" messages - and probably won't let you proceed. This is because SSH keeps a certificate around from every machine to which it connects. If this certificate ever changes then SSH assumes that something went wrong. So, in the above example, SSH looks up the certificate for "localhost" - which will definitely not match the certificate that it's *actually getting* from "remotemachine" over the SSH tunnel. The way around this is to use the MODIFIED SSH command

ssh -p 33642 -o HostKeyAlias="remotemachine" localhost

This will tell SSH to not check the "localhost" certificate, but rather that for "remotemachine".

If you are going to hook up with sketchy women than you should Double-Bag IT!!!
There is one obvious point of weakness here - what if you don't FULLY trust those firewalls? It would be relatively easy for a user on any firewall to sniff your traffic (and modify IT, if they want!!!). This is because as traffic comes into the firewall it is decrypted. Then it is sent to the appropriate port, where ANOTHER copy of SSH picks it up, encrypts it again, and sends it on to the next firewall.

The solution is to run another SSH tunnel INSIDE of the SSH tunnel. The price is this: you'll have to use up another port on your local machine (but who cares?). So, using the previous example (with 4 firewalls) you should execute:

1) On "Dfirewall" run:
ssh -N -L 4654:localhost:22 user@remotemachine

Notice the main difference HERE from the previous example. I am forwarding to port 22 (the SSH port) instead of the final destination port 5900! This is so that I can (later) set up an SSH tunnel within this SSH tunnel.

2) On "Cfirewall" run:
ssh -N -L 4653:localhost:4654 user@Dfirewall

3) On "Bfirewall" run:
ssh -N -L 4652:localhost:4653 user@Cfirewall

4) On "Afirewall" run:
ssh -N -L 4651:localhost:4652 user@Bfirewall

5) On "mymachine" run:
ssh -N -L 43642:localhost:4651 user@Afirewall

 

At this point we have an SSH tunnel from port 43642 (ANOTHER random port that was chosen) to the SSH server listening on port 22 on "remotemachine".

Now, to set up the "tunnel within the tunnel" I could execute the following command on "mymachine":

ssh -p 43642 -N -L 33642:localhost:5900 -o HostKeyAlias="remotemachine" localhost

This sets up the tunnel from port 33642 on "mymachine" to port 5900 on "remotemachine" INSIDE the previous tunnel that had been established from port 43642 on "mymachine" to port 22 on "remotemachine".

The "-o HostKeyAlias=" statement is VERY important. See the discussion above for an explanation.

An example where the "-R" switch can be very useful
Now let's suppose that you DON'T have an SSH account on the firewall! What can you do? You still want to forward port 33642 on "mymachine" to port "5900" on "remotemachine". The reason that we're dealing with any of this at all is that "remotemachine" is hidden behind a firewall. However, "mymachine" is not (or, at least, we'll assume for simplicity that "mymachine" is public). So "remotemachine" can actually see "mymachine", just not the other way around.

If you can ever get PHYSICAL access to "remotemachine" then you're in luck (say, you go into work every day). On "remotemachine" you could run the following command:

ssh -N -R 33642:localhost:5900 "mymachine"

That sets up a tunnel that forwards all traffic from the REMOTE port (in this case "mymachine" port 33642) to port 5900 on localhost (i.e. "remotemachine"). After running this at work you could then set up whatever program to listen to port 5900 and go home.

Now, at home on "mymachine" it would be easy to telnet into "remotemachine" port 5900. Just do the usual

telnet localhost 33642

Now THAT'S COOL! Consider all of the neat things you can do with this. Personally I'd set up a forward to port 22 - then you can have full SSH access from home and set anything else up that you could ever want remotely!

This page has been visited times since March 26, 2006

=====================================

Update: I have now created a script to automate setting up a persistent reverse ssh tunnel.

One of the nice features of VNC is that you can reverse a connection. If you are behind a firewall (or a router doing NAT) that doesn't allow incoming connections, VNC allows you to connect to a "listening server". As soon as a connection is established to the listening server, the "listener" gets a regular VNC connection back to the originating box.

Last Friday, I figured out how to do the same thing and reverse a connection with ssh.

I'll lay out the process first, and then talk about the applications and implications afterwards.

The simple approach:

Assume we have two machines. A firewalled server at work and your laptop. You can't connect to the server at work because all incoming connections are blocked by the firewall. So you ask Pete (who is at work and behind the firewall) to login to the server and then ssh to your laptop with the following command:

ssh -f -N -R 10000:localhost:22 username@ip_address_of_laptop

This creates an ssh connection from the work machine to the laptop. the -f and -N are basically cosmetic options. The -R 10000:localhost:22 option causes the laptop to listen on port 10000 and forward any requests on that port to the work machine (this is basically ssh tunneling).

So now, you can ssh to port 10000 on your laptop and you will actually be sshing to port 22 on the firewalled server:

ssh username@localhost -p 10000

(you will have to use your username and password for the server - despite it looking like you are logging onto localhost)

An ugly but effective hack to get rid of Pete:

To get rid of Pete you could setup a cron job on the server that attempts to connect to your laptop every hour. You would have to setup passwordless logins (using public_key authentication), but then you would know that if you needed to get on the server you could just make sure your laptop was on and willing to receive an ssh connection at the right time. Using www.dyndns.org would help you handle changing IP addresses on your laptop.

A better approach:

If you have access to a machine that is always on and outside of the firewall then you can use it as a middleman. The idea is to log onto middleman from behind the firewall, setup forwarded ports as above and just leave it connected all the time. Anytime you need to get behind the firewall you just go through the middleman. Here are the steps:

Setup your middleman to do gateways and stay connected without timing out. Edit /etc/sshd_config and make sure the following options are set:

TCPKeepAlive yes
ClientAliveInterval 30
ClientAliveCountMax 99999
GatewayPorts yes

You have to restart sshd to make these changes.

ps -aux | grep sshd

locate the ssh daemon

kill -hup

This will force sshd to rehash the config. (thanks Phil Vell!)


Then from the firewalled machine run:

nohup ssh -f -N -R 10000:localhost:22 username@middleman

The only difference from before is that this is now connecting the middleman and not the laptop to the firewalled machine, and we are using nohup to make sure the process doesn't die when the user logs out.

Then with your laptop you log onto port 10000 on the middleman which will forward your requests to the firewalled machine:

ssh usernameAtFirewalledMachine@middleman -p 10000

If you were unable to edit the sshd_config then you can still do this, you just have to jump through two hoops instead of one. First you log into middleman, then you log into the firewalled machine:

ssh usernameAtMiddleman@middleman
ssh usernameAtFirewalledMachine@localhost -p 10000

Applications and Implications

You can do a lot with this. It eliminates the need for a VPN. Ssh allows you to forward any port so you can connect to the intranet, the email server, anything that was hiding behind the firewall. This post is already too long, so I will leave it to the comments section for some examples of this sort of thing.

It also makes it pretty easy for social engineers to get behind your firewall. ("Could you run the Necessary Operation Help User Protocol for me, just type nohup...")

But I am a Windows luser!

Windows users can do this as well. Just install ssh using Cygwin. All of the ssh goodies including a client, server and scripts to set it up are there.
 
 
================
http://www.revsys.com/writings/quicktips/ssh-tunnel.html

I was surprised at how long it took me to find a good HOWTO on setting up a simple SSH tunnel that I wanted to write up this Quick-Tip.

Using OpenSSH on a Linux/Unix system you can tunnel all of the traffic from your local box to a remote box that you have an account on.

For example I tunnel all of my outbound E-mail traffic back to my personal server to avoid having to change SMTP servers, use SMTP-AUTH, etc. when I am behind firewalls. I find that hotel firewalls, wireless access points, and the other various NATing devices you end up behind while traveling often do not play nice.

 

To do this I use the following:

ssh -f user@personal-server.com -L 2000:personal-server.com:25 -N

The -f tells ssh to go into the background just before it executes the command. This is followed by the username and server you are logging into. The -L 2000:personal-server.com:25 is in the form of -L local-port:host:remote-port. Finally the -N instructs OpenSSH to not execute a command on the remote system.

This essentially forwards the local port 2000 to port 25 on personal-server.com over, with nice benefit of being encrypted. I then simply point my E-mail client to use localhost:2000 as the SMTP server and we're off to the races.

Another useful feature of port forwarding is for getting around pesky firewall restrictions. For example, a firewall I was behind recently did not allow outbound Jabber protocol traffic to talk.google.com. With this command:

ssh -f -L 3000:talk.google.com:5222 home -N

I was able to send my Google Talk traffic encrypted through the firewall back to my server at home and then out to Google. 'home' here is just an SSH alias to my server at home. All I had to do was reconfigure my Jabber client to use localhost as the server and the port 3000 that I had configured.

Hopefully this helps you to better understand SSH tunneling. If you found this page useful, you may also be interested in how to make yourSSH connections faster. If you find any errors or have any suggestions regarding this please feel free to E-mail me atfrank@revsys.com.

Books on SSH

Here are some helpful books if you need further help with SSH.