Title: SSH tunnel with single hop, using systemd network and autossh Date: 2015-02-01 20:00 Modified: 2015-02-01 20:00 Tags: archlinux, autossh, ssh, tunnel, systemd, systemd.network, postfix, TUN Slug: ssh-tunnel-with-single-hop-using-systemd-network-and-autossh Authors: David Runge Summary: Howto on setting up a SSH tunnel with the help of a systemd .network file between two machines, with no direct access to each other and modifying Postfix to use that tunnel. Recently I had the pleasure of setting up a SSH tunnel between two virtual machines that share no route and are located in two different subnets. They can however reach each other via SSH, hopping their host. Let's assume the following setup: * **client1** (Arch Linux) has *10.0.5.2/24* * **client2** (Arch Linux) has *10.0.6.2/24* * **host** (Debian) is *10.0.5.1/24* to **client1** and *10.0.6.1/24* to **client2** As I needed the two clients to be able to send mail to each other and reach each others' services, I did some digging and opted for a SSH connection using TUN devices (aka. *"poor man's VPN"*). The following is needed to set this up: * root access on both virtual machines (**client1** & **client2**) * a user account on the **host** system * SSH ([OpenSSH](http://www.openssh.com/) assumed) installed on all three machines # Connect the clients ## Change sshd_config The following two settings have to be made in each clients */etc/ssh/sshd_config* (to allow root login and the creation of TUN devices): #!aconf PermitRootLogin yes PermitTunnel yes I hope it is needless to say, that permitting root access via SSH has its caveats. You should make sure to set a very secure password, or only allow SSH keys for login. ## Generate and exchange keys Generate SSH keys on **client1** (you can of course use other key types, if your OpenSSH installation allows and supports it): :::bash ssh-keygen -t rsa -b 4096 -C "$(whoami)@$(hostname)-$(date -I)" Here you can choose between setting a password for the key (to unlock the key with *ssh-add* yourself) or not setting one (to be able to use the key on system boot with an automated service). Add them to your user at **host** like this: :::bash ssh-copy-id -i .ssh/id_rsa user@host Also add it to */root/.ssh/authorized_keys* on **client2**. ## Use ProxyCommand to connect To make a first connection between the clients, one can use the following settings in */root/.ssh/config* of **client1** to hop **host** and connect to **client2**: #!aconf Host client2 ProxyCommand ssh user@10.0.5.1 -W 10.0.6.2:%p ForwardAgent yes User root ServerAliveInterval 120 Compression yes ControlMaster auto ControlPath ~/.ssh/socket-%r@%h:%p The *ForwardAgent yes* setting here is especially interesting, as it forwards the SSH key of **client1** to **client2**. On **client1** a simple :::bash ssh client2 -v should now directly connect to **client2** by hopping **host**. # Tunneling ## Start the tunnel Now to the fun part: Creating the tunnel. OpenSSH supports a VPN-like feature, that creates a TUN device on both ends of the connection. As the "direct" (hopping **host**) connection between **client1** and **client2** has been setup already, let's try the tunnel: :::bash ssh -w5:5 client2 -v The *-w* switch will create a TUN device (*tun5* to be exact) on each client. Now, to start the tunnel without executing a remote command (*-N*), compression of the data (*-C*) and disabling pseudo-tty allocation (*-T*), one can use the following: :::bash ssh -NCTv -w5:5 client2 ## Setting up the TUN devices A short :::bash ip a s on **client1** and **client2** shows, that the *tun5* devices have been created on both clients. However they don't feature a link yet. This can be achieved by setting up a [systemd.network](http://www.freedesktop.org/software/systemd/man/systemd.network.html) with the help of [systemd-networkd](http://www.freedesktop.org/software/systemd/man/systemd-networkd.service.html). By placing a *.network* file in */etc/systemd/network/*, the TUN device will be configured as soon as it shows up. Here I chose the *10.0.10.0/24* subnet, but you could use any other private subnet (that's still available in your setup). On **client1** (*/etc/systemd/network/client1-tun.network*): #!ini [Match] Name=tun5 Host=client1 [Network] Address=10.0.10.1/24 [Address] Address=10.0.10.1/24 Peer=10.0.10.2/24 On **client2** (*/etc/systemd/network/client2-tun.network*): #!ini [Match] Name=tun5 Host=client2 [Network] Address=10.0.10.2/24 [Address] Address=10.0.10.2/24 Peer=10.0.10.1/24 After adding the files a restart of the **systemd-networkd** service on both machines is necessary. :::bash systemctl restart systemd-networkd Now starting the tunnel again should give a fully working point-to-point TCP connection between the two (virtual) machines using the TUN devices. If you need a more complex setup (i.e. to access the other clients' subnet), you will have to apply some routes (either using [Netfilter](http://www.netfilter.org/) or [systemd.network](http://www.freedesktop.org/software/systemd/man/systemd.network.html)), depending on your individual setup. # Hosts To make both hosts know about each other by hostname (and domain, if any), too, those can be added to the clients' */etc/hosts* files. On **client1** (*/etc/hosts*): 10.0.10.2 client2.org client2 On **client2** (*/etc/hosts*): 10.0.10.1 client1.org client1 # Postfix If using [postfix](http://www.postfix.org/) as MTA, the service has to be configured to use */etc/hosts* before resolving to your networks DNS resolving. On **client1** and **client2** (*/etc/postfix/main.cf*): #!ini lmtp_host_lookup = native smtp_host_lookup = native ignore_mx_lookup_error = yes # Autossh and system boot Wrapping it all up, it's usually intended to have a tunnel service be started on system boot. SSH tunnels are supposedly known for their poor connectivity. One way to get around this issue is to manage them with [autossh](http://www.harding.motd.ca/autossh/). A simple [systemd service](http://www.freedesktop.org/software/systemd/man/systemd.service.html) file can then be used to manage this behavior. On **client1** (*/etc/systemd/system/tunnel@.service*): #!ini [Unit] Description=AutoSSH tunnel to a host After=network.target [Service] Environment="AUTOSSH_GATETIME=0" ExecStart=/usr/bin/autossh -M 0 -NCTv -o ServerAliveInterval=45 -o ServerAliveCountMax=2 -o TCPKeepAlive=yes -w 5:5 %I [Install] WantedBy=multi-user.target Enable the service with :::bash systemctl enable tunnel@client2 Start the service with :::bash systemctl start tunnel@client2 *[SSH]: Secure Shell *[MTA]: Message Transfer Agent *[TCP]: Transmission Control Protocol *[TUN]: network TUNnel (virtual-network kernel devices) *[VPN]: Virtual Private Network