ssh layer2 ethernet tunnel einrichten a.k.a. "tunnelservice"

reinhard@finalmedia.de Do 22. Aug 22:45:19 CEST 2019

Konfigurationsbeispiele für 6 Tunnel und zwei Bridges auf den 3 Endpoints, um für jeden endpoint zwei separate ethernet ports des masters als bridge zu mehreren endpoints zu brücken. Der master stellt dabei proaktiv ssh verbindungen zu den endpoints her und tunnelt somit das tap an die gemeinsame bridge. auf den endpoints wiederum wird das tap und ebenso zusammen mit der jeweiligen nic auf die gewünschte dortige bridge aufgelegt.

Ich setze für dieses Howto voraus, dass dir die daemontools und auch envdir bereits bekannt sind.

Das System ist invers konzipiert und kann z.B. innerhalb weitere NAT ssh tunnels gepackt werden, die bereits zuvor aktiv von der Gegenseite zu einem Vermittlungsserver aufgebaut wurden. Sinn und Zweck ist, dass die Endpoints über keine geheimen Schlüssel verfügen müssen und der Master selbst jederzeit Endpoints aussperren kann.


master: server (192.168.111.1) kein sshd aktiv, keine ports nach außen offen. Hardware hat nur NIC1

ep1: endpoint01 (192.168.111.11) sshd aktiv. Hardware hat NIC1, NIC2, NIC3 
ep2: endpoint02 (192.168.111.12) sshd aktiv. Hardware hat NIC1, NIC2, NIC3
ep3: endpoint03 (192.168.111.13) sshd aktiv. Hardware hat NIC1, NIC2, NIC3
..
ep9: endpoint20 (192.168.111.19) sshd aktiv. Hardware hat NIC1, NIC2, NIC3

nic1: an switch hauptnetz (WAN)
nic2: an switch dediziert gebridgtes Netzwerk A (internal)
nic3: an switch dediziert gebridgtes Netzwerk B (guest)

Serverseite (master)

auf der Serverseite muss in der /etc/ssh/sshd_config das hier gesetzt sein

/etc/ssh/sshd_config


GatewayPorts yes

Nun zum dortigen Setup der daemontools und runfiles

idealerweise arbeitest du hierbei auf serverseite mit relativ gesymlinkten daemontool servicenames und envdir, um ein gemeinsames masterscript und tunnelscript zu nutzen und dich nicht zu wiederholen. Konrekt... Nutze auf deinem master server als root das folgende setup:


apt-get install daemontools daemontools-run

mkdir -p /srv/tunnelservice/log/

mkdir -p /srv/tunnelservice/tunnelservice0/config
mkdir -p /srv/tunnelservice/tunnelservice1/config
mkdir -p /srv/tunnelservice/tunnelservice2/config
mkdir -p /srv/tunnelservice/tunnelservice3/config
mkdir -p /srv/tunnelservice/tunnelservice4/config
mkdir -p /srv/tunnelservice/tunnelservice5/config
mkdir -p /srv/tunnelservice/tunnelservice6/config

cat << +++EOF+++ > /srv/tunnelservice/master_run
#!/bin/sh
exec 2>&1
sleep 2
test -d ./config || exit 1
test -f ../tunnelscript || exit 1
exec envdir ./config/ ../tunnelscript
+++EOF+++

cat << +++EOF+++ > /srv/tunnelservice/log/run
#!/bin/sh
exec 2>&1
exec logger -t "$(pwd)"
+++EOF+++

chmod +x /srv/tunnelservice/master_run
chmod +x /srv/tunnelservice/log/run

ln -s ../master_run /srv/tunnelservice/tunnelservice0/run
ln -s ../master_run /srv/tunnelservice/tunnelservice1/run
ln -s ../master_run /srv/tunnelservice/tunnelservice2/run
ln -s ../master_run /srv/tunnelservice/tunnelservice3/run
ln -s ../master_run /srv/tunnelservice/tunnelservice4/run
ln -s ../master_run /srv/tunnelservice/tunnelservice5/run
ln -s ../master_run /srv/tunnelservice/tunnelservice6/run

ln -s ../log/ /srv/tunnelservice/tunnelservice0/log
ln -s ../log/ /srv/tunnelservice/tunnelservice1/log
ln -s ../log/ /srv/tunnelservice/tunnelservice2/log
ln -s ../log/ /srv/tunnelservice/tunnelservice3/log
ln -s ../log/ /srv/tunnelservice/tunnelservice4/log
ln -s ../log/ /srv/tunnelservice/tunnelservice5/log
ln -s ../log/ /srv/tunnelservice/tunnelservice6/log

#  services starten
ln -s /srv/tunnelservice/tunnelservice0 /etc/service/
ln -s /srv/tunnelservice/tunnelservice1 /etc/service/
ln -s /srv/tunnelservice/tunnelservice2 /etc/service/
ln -s /srv/tunnelservice/tunnelservice3 /etc/service/
ln -s /srv/tunnelservice/tunnelservice4 /etc/service/
ln -s /srv/tunnelservice/tunnelservice5 /etc/service/
ln -s /srv/tunnelservice/tunnelservice6 /etc/service/

wobei das eigentliche /srv/tunnelservice/tunnelscript dann so aus sieht

/srv/tunnelservice/tunnelscript


#!/bin/sh
# reinhard@finalmedia.de

exec 2>&1

ip tuntap add tap${local_tap_id} mode tap
brctl addbr br${bridge_id}
brctl addif br${bridge_id} tap{local_tap_id}
ip link set tap${local_tap_id} up
ip link set br${bridge_id} up

ip addr add ${local_bridge_ip_and_netmask} dev br0

exec ssh -C -N -o Tunnel=ethernet \
-c "chacha20-poly1305@openssh.com" \
-m "hmac-sha2-512" \
-o KexAlgorithms="curve25519-sha256" \
-o HostKeyAlgorithms="ssh-ed25519" \
-o ServerAliveInterval=10 \
-w ${local_tap_id}:${remote_tap_id} \
-i /root/.ssh/id_ed25519 \
root@${endpoint}


Beachte: Das Script setzt einen existierenden ssh privatekey voraus, hier in /root/.ssh/id_ed25519. Ich nehme also bereits an, dass du diesen mit

ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -C "tunnelmaster"
erzeugt hast und den zugehörigen publickey /root/.ssh/id_ed25519.pub dann auf allen endpoint services in dortiger /root/.ssh/authorized_keys hinterlegt hast. Die Services müssen auch als root laufen, um an die taps binden zu können. Du solltest zuvor auch bereits einmal manuell eine ssh Verbindung allen endpoints aufgebaut haben, damit es einen known hosts eintrag gibt (TOFU).

Konfigurationsbeispiele für 6 Tunnel zu 3 Endpoints für 2 Bridges

In die Datei /etc/hosts auf dem Master Server die IPs der endpoints eintragen. Hier sind die IPs anzugeben, über die der masterserver alle endpoints von außen erreichen kann, um dorthin eine ssh Verbindung aufzubauen:


192.168.111.1  master
192.168.111.11 endpoint01
192.168.111.12 endpoint02
192.168.111.13 endpoint03

Hier Konfigurationsbeispiele für die env zum obigen Script, die man dann als env für die envdir configdir anlegen muss.

Jedem tunnelservice weist man dazu auch einen separaten daemontools service zu. Wir haben zwei services pro endpoint.

Pro tunnelservice hast du also je eine configdir als envdir. Gesamt also dieses setup:


echo endpoint01 >  /srv/tunnelservice/tunnelservice0/config/endpoint
echo endpoint02 >  /srv/tunnelservice/tunnelservice1/config/endpoint
echo endpoint03 >  /srv/tunnelservice/tunnelservice2/config/endpoint

echo 192.168.90.1./24 > /srv/tunnelservice/tunnelservice0/config/local_bridge_ip_and_netmask
echo 192.168.91.1./24 > /srv/tunnelservice/tunnelservice1/config/local_bridge_ip_and_netmask

echo 192.168.90.2./24 > /srv/tunnelservice/tunnelservice2/config/local_bridge_ip_and_netmask
echo 192.168.91.2./24 > /srv/tunnelservice/tunnelservice3/config/local_bridge_ip_and_netmask

echo 192.168.90.3./24 > /srv/tunnelservice/tunnelservice4/config/local_bridge_ip_and_netmask
echo 192.168.91.3./24 > /srv/tunnelservice/tunnelservice5/config/local_bridge_ip_and_netmask

echo 0 > /srv/tunnelservice/tunnelservice0/config/bridge_id
echo 0 > /srv/tunnelservice/tunnelservice0/config/local_tap_id
echo 0 > /srv/tunnelservice/tunnelservice0/config/remote_tap_id

echo 1 > /srv/tunnelservice/tunnelservice1/config/bridge_id
echo 1 > /srv/tunnelservice/tunnelservice1/config/local_tap_id
echo 1 > /srv/tunnelservice/tunnelservice1/config/remote_tap_id

echo 0 > /srv/tunnelservice/tunnelservice2/config/bridge_id
echo 2 > /srv/tunnelservice/tunnelservice2/config/local_tap_id
echo 0 > /srv/tunnelservice/tunnelservice2/config/remote_tap_id

echo 1 > /srv/tunnelservice/tunnelservice3/config/bridge_id
echo 3 > /srv/tunnelservice/tunnelservice3/config/local_tap_id
echo 1 > /srv/tunnelservice/tunnelservice3/config/remote_tap_id

echo 0 > /srv/tunnelservice/tunnelservice4/config/bridge_id
echo 4 > /srv/tunnelservice/tunnelservice4/config/local_tap_id
echo 0 > /srv/tunnelservice/tunnelservice4/config/remote_tap_id

echo 1 > /srv/tunnelservice/tunnelservice5/config/bridge_id
echo 5 > /srv/tunnelservice/tunnelservice5/config/local_tap_id
echo 1 > /srv/tunnelservice/tunnelservice5/config/remote_tap_id


Du kannst dir im Anschluss den gesamten Config-Baum auf diese Weise ansehen:


find /srv/tunnelservice/*/config/ -type f -exec head -v -n 1 "{}"

Zusammenfassung/Listing aller Configs für dich zur Übersicht


# tunnelservice 0
bridge_id=0
local_tap_id=0
remote_tap_id=0
endpoint="endpoint01"
local_bridge_ip_and_netmask="192.168.90.1/24"

# tunnelservice 1
bridge_id=1
local_tap_id=1
remote_tap_id=1
endpoint="endpoint01"
local_bridge_ip_and_netmask="192.168.91.1/24"

# tunnelservice 2
# bindet auf endpoint02 (um an endpoint 3 das tap0 zu binden)
bridge_id=0
local_tap_id=2
remote_tap_id=0
endpoint="endpoint02"
local_bridge_ip_and_netmask="192.168.90.2/24"

# tunnelservice 3
# bindet auf endpoint02 (um an endpoint 3 das tap1 zu binden)
bridge_id=1
local_tap_id=3
remote_tap_id=1
endpoint="endpoint02"
local_bridge_ip_and_netmask="192.168.91.2/24"

# tunnelservice 4
bridge_id=0
local_tap_id=4
remote_tap_id=0
endpoint="endpoint03"
local_bridge_ip_and_netmask="192.168.90.3/24"

# tunnelservice 5
bridge_id=1
local_tap_id=5
remote_tap_id=1
endpoint="endpoint03"
local_bridge_ip_and_netmask="192.168.91.3/24"



clientseite (endpoint)

Auf der endpoint seite, muss nur in der /root/.ssh/authorized_keys der publickey des masters hinterlegt sein, sowie in der /etc/sshd/sshd_config folgendes Setting. Damit erlauben wir nur noch ssh login als root und erlauben generell Tunnel. Auf einem Multiuser System muss das mit Match User gelöst werden. Der Endpoint sollte aber kein multiuser system sein.

/etc/ssh/sshd_config


AllowUsers root
PermitTunnel yes
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin prohibit-password

Die Datei /etc/network/interfaces muss dann einfach nur alle bridge und tap Regeln enthalten. Das sieht so aus beim Beispiel des endpoint01. Der Endpoint spannt einen wlan AP auf auf wlan0 und hat eine weitere nic eth1. an eth0 ist die "WAN" Seite, an die der master einen Verbindungsaufbau durchführt und seine tunnel "pushed".

/etc/network/interfaces


# WAN eth0
auto eth0
iface eth0 inet static
        address 192.168.111.11
        netmask 255.255.255.0

# WLAN AP
auto wlan0
iface wlan0 inet manual

auto br0
iface br0 inet static
        address 192.168.90.251
        netmask 255.255.255.0
        bridge_ports wlan0
        post-up ip tuntap add tap0 mode tap
        post-up brctl addif br0 tap0
        post-up ip link set tap0 up

# LAN eth1
auto eth1
iface eth1 inet manual

auto br1
iface br1 inet static
        address 192.168.91.251
        netmask 255.255.255.0
        bridge_ports eth1
        post-up ip tuntap add tap1 mode tap
        post-up brctl addif br1 tap1
        post-up ip link set tap1 up


ACHTUNG: Die IP-Adressen 192.168.90.x und 192.168.91.x natürlich auf jedem endpoint anders setzen! also z.B. 251,251,253. Und auch die statische IP-Adresse der WAN Seite ist natürlich bei jedem Endpoint anders. Die 192.168.111.11 aus obigem Beispiel ist dies die IP von endpoint01! Auf dem endpoint02 würdest du 192.168.111.12 setzen.

FAQ

Warum setzt du auf der master seite noch eine ip für die bridge?

Zu debugzwecken, weil man dann von der clientseite auch gut testen kann, ob eine Verbindung durch den Tunnel zur Remotebridge besteht. Es ist aber kein Muss. Wenn du reines L2 Briding machst, brauchst du das nicht zwingend. Im /srv/tunnelservice/tunnelscript auf dem master server kannst du daher die betreffende Zeile einfach mit Kommentarzeichen versehen, um sie nicht auszuführen


# ip addr add ${local_bridge_ip_and_netmask} dev br0