Rezept MM-103a: Layer-2 VPN mit ssh


Disclaimer: 


1. Dies ist eine Bastelanleitung! Für ein Fertiggerät guckst Du bitte z.B. hier.


2. Ich habe es nur zwischen Macs getestet; es funktioniert ziemlich sicher auch auf *ix und (mit viel Enthusiasmus) auch irgendwie auf Win*, aber fragt um Himmels willen nicht mich! Ich bitte auch, mir Diskussionen über die Plattformauswahl zu ersparen - der Podcast heißt schließlich MobileMacs und nicht WasAuchAllesUnterLinuxGeht oder so ähnlich...


3. YMMV 



Zutaten:




So - geht los:


Zunächst sollte man sicherstellen, daß man überhaupt mit ssh Tunnel aufbauen kann. Dazu auf dem Zielsystem (server-Seite) mal in /etc/sshd_config schauen und sicherstellen, daß da irgendwo 


PermitTunnel yes


steht! Unter OS X muß man nix weiter machen, da neue sshd Instanzen über den launchd erzeugt werden und dadurch immer die aktuelle Version des sshd_config benutzen.


Jetzt kann man mal einen ersten Versuch wagen mit:

sudo ssh -v -w 0:0 -o Tunnel=ethernet root@<server>


wobei die Magie in den beiden lilanen Optionen lieg (Details weiter unten). Das "-v" macht man nur beim ersten Mal an, um zu gucken, daß auch wirklich der Tunnel korrekt erzeugt wird. In dem Debug-Output sollte sich dann sowas finden:


debug1: Requesting tun unit 0 in mode 2

debug1: sys_tun_open: /dev/tap0 mode 2 fd 6

debug1: channel 0: new [tun]

debug1: channel 1: new [client-session]


Das muß erst mal klappen. Wenn das da nicht steht, hat man z.B. den TUN/TAP Treiber nicht installiert oder ist eben nicht root oder darf keine Tunnel erzeugen (s.o.) oder oder oder ...


Die anderen Optionen: Das "-w 0:0" sagt an, daß man hüben, wie drüben jeweils eine "0"-Instanz des jew. TUN/TAP-Interfaces erzeugen möchte. Wenn man das häufiger machen will (also mit mehr als einer gleichzeitigen Verbindung auf einer oder beiden Seiten), z.B. wenn man sich eine "Dreiecksverbindung" zwischen drei Sites oder gar noch komplexere Setups basteln möchte, sollte man sich ein System ausdenken, wie man die Dinger durchnummeriert. Der Effekt von "0:0" jedenfalls ist, daß beidseitig ein tap0 Interface entsteht. Dafür sorgt auch die Option "-o Tunnel=ethernet", denn wenn man diese Option weg läßt bekommt man ein tun0 Interface (das ist für Layer-3 Tunneling, also IP, aber das wollen wir hier nicht...).


Wenn das geklappt hat (Gratulation!), haben wir erfolgreich ein virtuelles Ethernetkabel zwischen beiden Systemen erzeugt. Das steckt (wie gesagt) zwischen tap0 auf dem client und tap0 auf dem server. Gleich mal mit ifconfig -a gucken - da sollte sich dann z.B. sowas finden:


tap0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500

ether 47:11:23:42:08:15 

open (pid 4242)


Fein!


Da könnte man sich jetzt manuell lustige IPv6/4-Adressen auf beiden Seiten drauf konfigurieren und mit der jew. anderen Seite "reden", aber sowas banales wollen wir ja nicht - da hätten wir auch gleich einen Layer-3 Tunnel (s.o.) nehmen können.


Wir wollen ja das virtuelle Kabel auf der server-Seite mit dem/einem dortigen LAN verbinden. Achtung: Das geht auch irgendwie mit WLAN, ist aber u.U. viel kniffliger, weswegen ich das hier nicht näher besprechen möchte - andere Baustelle.


Dazu brauchen wir einen virtuellen Switch, in welchen wir (virtuell) das bereits existierende LAN-Kabel und unser frisch verlegtes virtuelles Kabel hineinstecken. Ein Switches hießen früher "Bridges" und die erzeugt man (als root) unter OS X z.B. so:


ifconfig bridge0 create


... und dann stecken wir auch gleich mal die beiden Strippen rein:


ifconfig bridge0 addm en0

ifconfig bridge0 addm tap0


... und machen das ganze Ding mal an: (is'n Klassiker, genau das zu vergessen ...)


ifconfig bridge0 up


... und dann schauen wir auch gleich mal nach, was da so passiert ist:


ifconfig bridge0


Da sieht man dann beispielsweise sowas:


        bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500

                ether XX:XX:XX:XX:XX:bc

                Configuration:

                        priority 32768 hellotime 2 fwddelay 15 maxage 20

                        ipfilter disabled flags 0x2

                member: en0 flags=143<LEARNING,DISCOVER,AUTOEDGE,AUTOPTP>

                         port 4 priority 128 path cost 2000000

                member: tap0 flags=143<LEARNING,DISCOVER,AUTOEDGE,AUTOPTP>

                         port 9 priority 128 path cost 55

                Address cache (max cache: 100, timeout: 1200):

                        XX:XX:XX:XX:XX:42 en0 1014 flags=0<>

                        XX:XX:XX:XX:XX:eb en0 1077 flags=0<>

                        XX:XX:XX:XX:XX:61 en0 1077 flags=0<>

                        XX:XX:XX:XX:XX:9c en0 844 flags=0<>

                        XX:XX:XX:XX:XX:44 en0 814 flags=0<>

                        XX:XX:XX:XX:XX:76 en0 1161 flags=0<>

                        XX:XX:XX:XX:XX:e en0 1163 flags=0<>

                        XX:XX:XX:XX:XX:56 en0 1144 flags=0<>

                        XX:XX:XX:XX:XX:f8 en0 1166 flags=0<>

                        XX:XX:XX:XX:XX:b9 en0 1159 flags=0<>

                        XX:XX:XX:XX:XX:33 en0 1163 flags=0<>

                        XX:XX:XX:XX:XX:9a en0 1199 flags=0<>

                        XX:XX:XX:XX:XX:cb en0 1199 flags=0<>

                        XX:XX:XX:XX:XX:9 en0 1187 flags=0<>

                        XX:XX:XX:XX:XX:7e en0 1198 flags=0<>

                        XX:XX:XX:XX:XX:76 en0 1200 flags=0<>

                        XX:XX:XX:XX:XX:98 en0 1198 flags=0<>

                        XX:XX:XX:XX:XX:75 en0 1199 flags=0<>

                        XX:XX:XX:XX:XX:74 en0 1200 flags=0<>

                        XX:XX:XX:XX:XX:54 en0 1200 flags=0<>

                        XX:XX:XX:XX:XX:d7 en0 1199 flags=0<>

                        XX:XX:XX:XX:XX:e en0 1200 flags=0<>


Statt der XXe stehen da bei Euch natürlich richtige Mac-Adressen, aber welche ich davon in meinem Setup habe, geht Euch mal nix an! 😉


Wenn das also π×👍 bei Euch so aussieht, dann hat die Bridge erfolgreich gelernt, welche Geräte/Mac-Adressen sich im LAN befinden. Damit ist die Arbeit auf der server-Seite dann auch schon beendet. Die obigen Schritte sind ja immer die gleichen, man kann sich das also leicht in einem kleinen Scriptchen verankern. Die Bridge bekommt man übrigens mit


ifconfig bridge0 destroy


wieder weg und muß sie vorher nicht mal ausschalten oder die "Kabel" herausziehen.


So, jetzt muß man nur noch mit dem "Kabel" auf der client-Seite was anstellen. Achtung: Hier muß dringenst darauf geachtet werden, keine Tunnel-Schleifen zu bilden, d.h. wenn man die ssh-Verbindung z.B. mit IPv4 aufgebaut hat, darf man keinesfalls einfach so das gesamte IPv4-Ziel-Netz drüber routen, denn sonst geht der ssh-Verkehr durch seinen eigenen Tunnel und das ist sowas, wie Google in Google eingeben und führt zu schwarzen Löchern oder schlimmerem! Gleiches, klar, für IPv6, weshalb es u.U. ganz praktisch ist, daß OS X das tap0 Interface auf der client-Seite noch nicht entdeckt und "automatisch konfiguriert" hat. 


Um dieser Problematik zu entfliehen, hilft es in der Regel, eine explizite statische Host-Route (/128 bzw. /32) zum server-System über den aktuell verwendeten Gateway einzurichten. Wer einen multi-homed Server benutzt, kann natürlich auch einfach dessen IP-Adresse von einem anderen Interface als en0 benutzen, vorausgesetzt es steckt nicht im gleichen LAN Segment. Oder man ist erst mal feige und baut z.B. die ssh-Verbindung mit IPv6 auf und tunnelt erst mal nur IPv4 oder andersherum.


Man kann sich jetzt z.B. (auf der client-Seite) mit dem Kommando


ipconfig set tap0 DHCP


automatisch eine Adresse aus dem server-LAN zuweisen lassen (wenn das dort unterstützt ist) oder mit dem üblichen


ifconfig tap0 ...


selbst IPv4/IPv6 Adressen setzen. Bei IPv6 wird automatisch eine (zwingend notwendige) link-local Adresse erzeugt.


Anstatt das Kabel im client-System zu terminieren, kann man es natürlich auch dort wieder in eine entspr. (virtuelle) Bridge stöpseln und mit dem lokalen Ethernet verbinden - voliá: Schon haben wir ein "site-to-site VPN". Das ganze kann man (theoretisch) mit beliebig vielen Systemen/Sites machen und auch die Topologie dieses Netzes beliebig wählen. Es ist aber ab einem gewissen Komplexitätsgrad genauso tricky, wie im "richtigen Leben" mit echten Switches & Kabeln, d.h. wer hier nicht aufpaßt wird irgendwann vom Spanning-Tree (ja, das kann die virtuelle Mac OS X Bridge auch alles!) gebissen!


Zum Schluß noch ein Wort zu "VPN über TCP", was ja den einen oder anderen zu erregen scheint. TCP Verbindungen erzeugen ein super-zuverlässiges "Kabel". Das sind IPvX-Stacks so nicht gewohnt. Denen ist es lieber, wenn da mal ein Paket verschwindet, als daß sich jemand irre Mühe gibt, ein Paket auf-Teufel-komm-raus zu übertragen. D.h. wenn die äußere IP-Strecke, d.h. die, auf der die ssh-Session läuft, irgendwie hakelt, dann betrifft das (natürlich) auch den inneren, getunnelten Verkehr. Da es, im Gegensatz zu SCTP, bei TCP nur einen Stream gibt, blockiert der gesamte Verkehr, wenn dieser Stream, z.B. wegen eines lost-packet-retries blockiert ist. Da kann man jetzt noch stundenlang philosophieren, aber dieser Zettel ist ja kein Transskript einer MobileMacs Sendung, also machen wir das mal irgendwann und woanders ...


So: Viel Spaß beim Basteln - ich würde mich freuen, von Erfolgreichen, spannenden Installationen auf Basis o.g. Technik zu hören.


Clemens <clemens.schrimpe@gmail.com>