aboutsummaryrefslogtreecommitdiffstats
path: root/content/blog/ssh-tunnel-and-postfix.md
blob: 2f09db8e3854c1cfc86ecc41afd23b09184139bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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.
Category: admin

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