Zum Hauptinhalt springen

Einen Multi-Tenant Wireguard VPN Server mit iptables absichern

Die ZERO AMPS Nodes verfügen zwar nicht per default über eine Internetverbindung, doch in einigen Fällen statten wir sie mit einem Mobilfunkmodul aus, sodass wir sie in Absprache mit dem Kunden aus der Ferne aktualisieren, warten oder Fehler beheben können.

Um eine sichere Verbindung zu unserer eigenen Infrastruktur herzustellen, nutzen wir größtenteils Wireguard VPNs. Wireguard VPN sind sehr leichtgewichtig, performant und sind sich erfahrungsgemäß sehr robust - vor allem in Kombination mit Mobilfunkverbindungen. Der Wireguard Client auf den AMPS Nodes verbindet sich zu unserem zentralen VPN-Server. Über diesen stellen auch unsere Entwickler eine Verbindung her, sodass sie sich letztendlich zu dem jeweiligen AMPS Node verbinden können.

Da wir allerdings verschiedene Kunden mit verschieden großen Projekten haben und bei der Datensicherheit keine Kompromisse eingehen wollen, haben wir unsere VPN-Netze nach Kunden und Projekten aufgetrennt:

Kunde A wird so mit seinen Geräten niemals mit Geräten des Kunden B in Kontakt kommen können - dafür sorgen die individuellen VPN-Netze und unsere Firewallregeln, die den Datenverkehr innerhalb der VPNs und darüber hinaus beschränken. Es muss unbedingt verhindert werden, dass ein kompromittiertes Gerät die Kontrolle über sämtliche - auch kundenfremde - Nodes erhält.

In der Regel kann also von einem Node aus nicht aus einem Kunden-VPN heraus kommuniziert werden. Eine Ausnahme gibt es dann aber doch: Ein eigenes “Master VPN” (mastervpn) erlaubt unseren Entwicklern, über dieses VPN Geräte aller anderen VPNs zu erreichen. Statt sich also mit vielen verschiedenen Wireguard-Profilen herumschlagen zu müssen, genügt es, wenn unsere Entwickler eine Verbindung mit diesem einen Master VPN herstellen.

Unsere Netzwerktopologie sieht also in etwa so aus:

Networks Graphics

Die Firewallregeln auf dem VPN-Server vor der Implementierung der Sicherheitsmaßnahmen:

root@vpnserver:~# iptables -L -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination                 

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination 

(Grund für die gähnende Leere: eine weitere Firewall befindet sich außerhalb des VPN-Servers und filtert eingehende Verbindungen bereits)

Diese Regeln galt es nun so auszubauen, dass folgende Anforderungen erfüllt sind:

  • Ein AMPS Node in einem der Wireguard-Netz darf nur mit anderen AMPS Nodes (oder dem Server-Interface) seines eigenen VPN-Subnetzes kommunizieren.
  • Ein Gerät im mastervpn Wiregard Netz darf mit beliebigen AMPS Nodes in allen Wireguard-Netzen kommunizieren.

Damit überhaupt eine Kommunikation / ein Paket-Forwarding über Netzgrenzen (z.B. mastervpn => customer1-vpn oder mastervpn => customer2-vpn) hinweg möglich ist, muss im Linux Kernel zuerst das IPv4-Forwarding aktiviert werden. Temporär via

sysctl -w net.ipv4.ip_forward=1

… oder dauerhaft durch die Anpassung der Datei /etc/sysctl.conf:

net.ipv4.ip_forward=1

Gefolgt durch ein

sysctl -p

Per default kann nun jedes Netzwerkinterface an jedes andere Datenpakete weiterleiten. Clients verschiedener Wireguard-Netze könnten also - wenn sie clientseitig passend konfiguriert sind - miteinander sprechen. Doch dieses Verhalten soll bei uns normalerweise nicht erlaubt sein. Daher wird die Default-Firewallregel für die “FORWARD” Chain der “Filter” Tabelle auf “DROP” gesetzt:

iptables -P FORWARD DROP

Für einen Fall müssen wir das Forwarding allerdings erlauben - nämlich für den Fall, dass eine Anfrage aus dem mastervpn heraus in eines der Kunden-VPNs geschickt wird. Für diesen Fall wurde das Forwarding ja zuvor aktiviert:

iptables -A FORWARD --in-interface mastervpn --out-interface customer1-vpn -j ACCEPT
iptables -A FORWARD --in-interface mastervpn --out-interface customer2-vpn -j ACCEPT

Damit Geräte aus dem betreffenden Kunden-VPN antworten können, ist eine weitere Firewallregel wichtig, die dafür sorgt, dass AMPS Nodes aus dem Kunden-VPN in einem Fall eben doch Pakete in ein anderes Wireguard-Netz (mastervpn) schicken dürfen. Nämlich dann, wenn es sich um eine Antwort auf eine zuvor eingegangene Anfrage aus mastervpn handelt:

iptables -A FORWARD --out-interface mastervpn -m state --state ESTABLISHED,RELATED -j ACCEPT

Bisher haben wir nur die FORWARD Regeln betrachtet. Dabei fällt aber ein Fall durch’s Raster: Was, wenn ein Paket nicht weitergeleitet werden muss, sondern sein Ziel schon erreicht hat? Das ist beispielsweise der Fall, wenn von customer3-vpn aus die Wireguard Server-IP von customer2-vpn angesprochen wird. Da hier - trotz anders lautender IP-Adresse der Server selbst angesprochen wird, kommt kein Forwarding zum Tragen und derartige Kommunikation wird von den bisherigen Regeln nicht verhindert.

Wenn wir solche Anfragen verhindern wollen, muss eine INPUT Regel für jedes der VPNs erzeugt werden, z.B.:

iptables -A INPUT -d 10.4.0.1 ! --in-interface mastervpn -j REJECT
iptables -A INPUT -d 10.2.0.1 ! --in-interface customer1-vpn -j REJECT
iptables -A INPUT -d 10.3.0.1 ! --in-interface customer2-vpn -j REJECT

10.4.0.1 ist hier beispielsweise die IP-Adresse, die innerhalb des mastervpn für den Server selbst genutzt wird. Genauso verhält es sich bei den anderen beiden Regeln für die customer VPNs.

Firewallregeln nach der Implementierung der Sicherheitsmaßnahmen:

root@vpnserver:~# iptables -L -v
Chain INPUT (policy ACCEPT 5621 packets, 1148K bytes)
 pkts bytes target     prot opt in     out     source               destination         
    7   588 REJECT     all  --  !mastervpn any     anywhere             10.4.0.1             
    0   0   REJECT     all  --  !customer1-vpn any     anywhere             10.2.0.1            
    0   0   REJECT     all  --  !customer2-vpn any     anywhere             10.3.0.1                
reject-with icmp-port-unreachable

Chain FORWARD (policy DROP 17 packets, 1428 bytes)
 pkts bytes target     prot opt in     out     source               destination                   
    2   168 ACCEPT     all  --  mastervpn customer1-vpn  anywhere             anywhere            
  207 17201 ACCEPT     all  --  mastervpn customer2-vpn  anywhere             anywhere            
   38  4692 ACCEPT     all  --  any    mastervpn  anywhere             anywhere             state RELATED,ESTABLISHED

Chain OUTPUT (policy ACCEPT 6708 packets, 1425K bytes)
 pkts bytes target     prot opt in     out     source               destination

Der Weg durch die Firewall wäre hiermit für unsere AMPS Kundengeräte geebnet.

Nur eines fehlt noch: Eine Routinginformation für den Fall, dass jemand aus dem mastervpn heraus auf eines der AMPS Nodes in den customer VPNs zugreift. Es muss spezifiziert werden, wohin Antwortpakete geschickt werden sollen. Schließlich sehen die Geräte nur eine IP-Adresse aus dem mastervpn-Bereich, z.B. 10.4.0.0/24. Da standardmäßig nur Routinginformationen für ihr eigenes Subnetz vorliegen, müssen wir ihnen den Weg zu 10.4.0.0/24 zeigen. Dazu wird in der jeweiligen Wireguard-Konfiguration auf dem AMPS Node ein Eintrag für AllowedIPs hinzugefügt:

Aus

AllowedIPs = 10.3.0.0/16

wird beispielsweise

AllowedIPs = 10.3.0.0/16,10.4.0.0/24

Somit ist für den betroffenen Wireguard-Client aus dem Kundennetz klar, dass nicht nur Pakete für das eigene SUbnetz über den Server geroutet werden sollen, sondern auch (Antwort-)Pakete an das mastervpn Netz 10.4.0.0/24.

Nach einem Neuladen der Clientkonfiguration mittels systemctl restart wg-quick@customer1-vpn ist die Änderung aktiv und die Routinginformationen sollten vorliegen:

ip route

Hinweis: Die oben genannten iptables Einstellungen sind nicht persistent! Um sie nach einem Reboot wiederherzustellen, empfehlen wir, iptables Regeln mittels netfilter-persistent zu persistieren.

Mastodon