VPN over SSH
SSH is an awesome protocol and it can do a lot of things. I found out that OpenSSH has native TUN device support. Unfortunately I was not able to set this up properly. On my Linux boxes this failed with interesting errors. I ended up using strace
to find out that there is some permission idiosyncracy.
As I love using pipes in my terminal, I had an idea. What about using socat
and piping IP traffic through the SSH tunnel? tldr; it works.
$ sudo socat TUN:192.168.255.1/24,up EXEC:'ssh -l root HOST "socat TUN:192.168.255.2/24,up"'
Let's break this monster down.
socat
allocates two entities and connects them via a bidirectional channel (e.g. two pipes for stdin and stdout respectively). Let's have a look at the “ADDRESS TYPES” section in the manpage here: socat(1)
. Any of these address types can be used. So let's create a TCP listener and print everything to the terminal.
$ socat TCP-LISTEN:1234 -
This command creates a TCP listener at port 1234 and connects it to stdin/stdout of the socat process. Eventually, this is a stupid replacement of nc -l -p 1234
.
One step further, socat
enables creating arbitrary processes and piping data into them. With the TCP listener example we can pipe data from the network in a subprocess like grep
[^1].
$ socat -u TCP-LISTEN:1234 EXEC:'grep foo' &
$ echo "foobar" | nc 127.0.0.1 1234
The VPN stuff works like the very same. So, let's start slowly[^2]:
$ sudo socat TUN:192.168.255.1/24,up - | hexdump
This command allocates a TUN device, assings the ip address 192.168.255.1
, sets the device into the up
state (= enables it), and relays everything to the terminal into hexdump
. When you ping the subnet via ping 192.168.255.2
then the IP packets show up. So now, let's send this data over an SSH tunnel.
ssh
is awesome as it provides a bidirectional connection (via two pipes) to a remote process. Everything which is written to ssh
's stdin is written to the remote process' stdin tunneled via the SSH connection! Well, let's spawn a socat
an the remote side as well:
$ sudo socat TUN:192.168.255.1/24,up EXEC:'ssh -l root HOST "socat TUN:192.168.255.2/24,up -"'
The right argument (EXEC:"…"
) spaws ssh
logs into HOST
and starts a socat
which is connected to a tunnel device. The remote socat
needs to write everything to -
(stdio) because -
is connected to the SSH tunnel.
It works! The only problem left is that root
is needed on both sides.
But this can be solved somehow…
[^1]: Using unidirectional mode with -u
to avoid blocking.
[^2]: sudo
is needed to allocate a TUN device. There are tricks to do this as a usual user, but I am too lazy to find out…