The SSH Tunnelling Cheatsheet

This is going to be the Ultimate Wi-Fi Hitchhiker’s Guide to Tunnelling.

I’m going to start with the basics, and end up presenting my own scripts and my own setup to create an Ultimate, almost 100% transparent tunnelling setup, without root permissions on the gateway.

What SSH Provides

Situation 1: A –> B –> C

If you are A and you want to connect to an HTTP proxy running on C on port number 3128, but you have access only to B, and B has access to C, this is what you would do:
arnold@A:~$ ssh -L 8000:C:3128 B -N

(B/C are IP address or host names.) This creates a port 8000 locally which maps to port 3128 on C.

Situation 2: C —> A, but A –/–> C

Ok, suppose C is behind a complete firewall, and you can’t even access B, or C directly from A. But C can access A. Well, let’s assume now that you have physical access to C (or ssh access via some long tedious route), well, then connect to C and do:
arnold@C:~$ ssh -R 8000:A:3128 A

Very useful if somebody behind a firewall wants you to help out with system administration remotely.

Situation 3: You just want a “transparent” proxy, and you already have a connection from A –> C

What we do here, is create a SOCKS proxy, which is a kind of super-proxy, that can proxy just about any kind of connection, as long as your client program supports SOCKS. Typically this is what you’d do:
arnold@A~$ ssh -D 9000 arnold@C -N

Now go to your Gnome Network Proxy setting and set your proxy to use SOCKS, with localhost:9000. You might have to manually set it for applications that don’t lookup the Gnome proxy settings. If you’ve done this right you should be able to do almost everything like chatting, browsing etc.

ssh -D is useful, but it’s not convenient when you want a “permanent” transparent solution. For one thing many command line applications don’t support SOCKS by default. We can sort of fix this: Install tsocks, edit the proxy settings in /etc/tsocks.conf, and run tsocks bash. Any command you run now will transparently use the SOCKS proxy you set up.

But that’s not good enough.

Situation 4: A –> C, and you have root access on both A and C

… and you’re running Linux/BSD etc.

arnold@A~$ sudo ssh -w 0 root@C -N

This creates a “virtual” networking device (tun0) on both ends, and connects them, basically creating a VPN. But this step just creates the connection, it does not set up the routing. I can’t show you all the steps here, but this is basically what you do:

arnold@A~$ ifconfig tun0 10.0.0.1 pointtopoint 10.0.0.2
arnold@C~$ ifconfig tun0 10.0.0.2 pointtopoint 10.0.0.1

You should now be able to ping 10.0.0.2. Now set up iptables and NAT on C. (Sigh, I can’t explain those here, and really I don’t know much of it. But you’ll see a few examples of iptables further down.) Once this is set up, you need to set the route on A:

arnold@A~$ sudo route del default dev eth0
arnold@A~$ sudo route add dev eth0 # you still need your SSH to run
arnold@A~$ sudo route add default gw 10.0.0.2

Too messy? Of course it is. Plus, you need root access on the server. We can do better.

The trick

There are multiple “solutions” to the problem. The first one that came to my mind, was to create a tun0 virtual device locally, which maps each connection to a local SOCKS proxy. Fairly simple idea, turned out messy to code because I didn’t know how to easily write a userspace TCP stack that changed the raw packets from the tun device to a stream. I googled a lot but found just one program that claimed to do this, and that was written in C#, and had some GUI code in it, and I definitely didn’t feel comfortable running it. (for the record, socks-tun on Google code.)

So here’s what I finally did: Let’s use iptables to reroute all outgoing TCP streams to a local daemon. This daemon at this point does not know the destination of this stream, but with a little effort it can find that out by looking at /proc/net/ip_conntrack. Now it just uses tsocks to finally route this connection via SOCKS. Simple enough, the code is here (and git clone git://github.com/tdrhq/tunnel-scripts.git), and here’s what I did:

Build it: arnold@laptop:~/tunnel$ gcc lcat.c io_loop.c my_socket.c -g -o lcat.

For the following example, I shall use 61.12.4.27 as an example, since it’s CMI’s IP address, although I would hope that the rest of you reaching here via Google don’t just copy and paste this.

Enable ip_forward: (I’m not sure if you need this step, but still…)
sysctl -w net.ipv4.ip_forward=1

Reroute all connections to destinations other than 61.12.4.27 to a local port:
sudo iptables -t nat -A OUTPUT -p tcp -d \! 61.12.4.27 -o wlan0 -j DNAT --to-destination 127.0.0.1:9000

(Change wlan0 to your interface.)

Now create your SSH socks proxy:
ssh -D 3128 61.12.4.27 -N

Now edit /etc/tsocks.conf with the proxy as localhost and port as 3128. Also set the SOCKS version to 4. We’re almost done:

arnold@laptop:~/tunnel$ sudo tsocks bash
root@laptop:/home/arnold/tunnel# ./lcat -p 9000 -t

And from now on all TCP connections are 100% tunnelled.

Caveats: DNS lookup is not TCP based. The iptables command above routes only TCP connections. It should be possible to route UDP via SOCKS too, however I don’t have support for that in my code yet. So, right now, you better have a valid nameserver to use. Also, because of this same reason, ping won’t work, and so don’t expect to use it to test connectivity during any of the steps.

More notes: SSH tunnels time out if inactive.

Being a responsible Tunneller

As a tunneller, you don’t want to be misusing your gateway’s bandwidth. Let’s all just be resonsible tunnellers. Also, what’s your reason for tunnelling? Right now, mine is that I’m using my neighbor’s wireless, and I don’t want to get caught. (While watching videos, you don’t really need the full bandwidth, but it uses it anyway in pointless buffering.) My lcat script can be used for bandwidth throtling too. You need to run an additional lcat, before the ssh step. To limit the speed to 100 kbps:

arnold@laptop:~/tunnel$ ./lcat -p 6000 -h 61.12.4.27 -s 100
arnold@laptop:~/tunnel$ ssh -D 3128 -p 6000 arnold@localhost

While lcat is running, you can also type in a new speed to change the speed on-the-fly.

And for completeness…

… let me mention Corkscrew for tunnelling an SSH session over an HTTP proxy.

Advertisements

8 Responses to The SSH Tunnelling Cheatsheet

  1. haru says:

    lolnoob

  2. Arnie says:

    (As I seem to have convinced APA later, this is not entirely “lolnoob” content.)

  3. Preyas says:

    But you might be lying for all we know. I want apa to admit that

  4. Arnie says:

    @preyas, thank you for being so supportive.

    @APA, you better come back here. I expect an apology. If somebody comes here via Google, I really don’t want them to see the first comment as “lolnoob”.

    And, few additional remarks:
    1. tsocks can be used for setting up a system-wide socksification, but you need to restart your system. (And btw, this was the solution APA was referring to when he called me a noob.) Using tsocks or any other socksification program also kinda changes the way the program itself runs, so I wouldn’t rely on it for running my entire system.

    2. In my solution, you can skip the sudo tsocks bash step, by compiling the code differently: gcc -g lcat.c io_loop.c my_socket.c -ltsocks. Then you can directly run it as sudo ./a.out. It will still use /etc/tsocks.conf.

    3. I made a mistake in the throttling part, you don’t need a separate lcat process below SSH. You can directly set the throttling speed in the lcat process you used for the tunnel.

  5. Arnie says:

    Pretty depressing, but I just found that http://transocks.sourceforge.net/ already does this, and does a few things better and more elegantly. Still, mine would perform much better since it does not fork processes for each connection, nor use any form of threading.

  6. darkk says:

    Arnie,

    Yeah, I disliked process-per-connection model of transocks too, so I reinvented the wheel too: http://darkk.net.ru/redsocks

  7. oyvindh says:

    This is awesome! :D

  8. Thireus says:

    The trick section will help me a lot! But I think your code can be improved.

    Regarding ssh timeout, just add these two options: -o ServerAliveInterval=15 -o ConnectTimeout=15

    Regards,
    Thi.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: