1 Punkte von GN⁺ 2024-05-13 | 1 Kommentare | Auf WhatsApp teilen
  • Wag ist ein Projekt, das WireGuard um Multi-Faktor-Authentifizierung, Routenbeschränkungen und Geräteregistrierung erweitert. Es kann zwischen Routen, die MFA erfordern, und jederzeit erreichbaren öffentlichen Routen unterscheiden
  • Es bietet eine API zur Registrierung neuer Clients, Hochverfügbarkeit, Echtzeit-Updates und Benachrichtigungen für Nutzer sowie mehrere MFA-Integrationen wie Security Key, SSO, PAM und TOTP
  • Für den Serverbetrieb muss IP-Forwarding aktiviert sein; bei manueller Ausführung sind die Installation von iptables und libpam sowie die Ausführung als root zur Verwaltung von iptables und WireGuard-Geräten erforderlich
  • Die Verwaltung ist über Web-UI und CLI möglich; die CLI nutzt die Unterbefehle start, registration, devices, users und webadmin für Registrierungstoken, Gerätesperren, MFA-Reset und Web-Admin-Konten
  • Zu den Einschränkungen gehört, dass nur ein AllowedIP pro Client unterstützt wird; das System ist hauptsächlich für Linux gedacht, Windows kann mit einigen zusätzlichen Schritten funktionieren

Von Wag hinzugefügte WireGuard-Funktionen

  • Wag erweitert WireGuard um MFA, Routenbeschränkungen und Geräteregistrierung
  • Routen können in Pfade, die eine MFA-Authentifizierung erfordern, und jederzeit erreichbare öffentliche Routen unterteilt werden
  • Es bietet eine einfache API zur Registrierung neuer Clients
  • Es unterstützt Hochverfügbarkeit, Echtzeit-Updates und Benachrichtigungen für Nutzer
  • MFA-Integrationen umfassen folgende Verfahren
    • Security Key
    • SSO
    • PAM
    • TOTP
  • Die Dokumentation ist unter Documentation verfügbar

Installation und Voraussetzungen für die Ausführung

  • Auf dem Server muss Forwarding aktiviert sein
    • Für IPv4 wird die Einstellung net.ipv4.ip_forward=1 verwendet
    • Für IPv6 werden entsprechende sysctl-Einstellungen wie net.ipv6.conf.all.forwarding=1 verwendet
  • Das Docker-Compose-Beispiel verwendet das Image wagvpn/wag:latest
    • Beispielport für die Verwaltungsseite: 4433/tcp
    • Beispielport für die öffentliche Registrierungsseite: 8081/tcp
    • Beispielport für WireGuard: 53230/udp
    • Das Gerät /dev/net/tun wird in den Container eingebunden
  • Für eine manuelle Installation werden iptables und libpam benötigt
  • Wag muss als root ausgeführt werden, um iptables und das WireGuard-Gerät zu verwalten
  • Binäre Releases benötigen glibc 2.31+
  • Builds aus dem Quellcode benötigen go1.23.1 und npm

Verwaltungsarten

  • Wenn die Verwaltungs-UI aktiviert und Wag konfiguriert wird, erstellt es den ersten Administrator und gibt das Passwort auf STDOUT aus
  • Anschließend kann man sich in der Web-UI anmelden und Nutzer verwalten
  • Der root-Benutzer kann den Wag-Server per CLI verwalten
  • Das CLI-Format lautet wag subcommand [-options]
  • Unterstützte Unterbefehle sind:
    • start: startet den Wag-Server und daemonisiert ihn nicht
    • registration: erstellt, löscht und listet Registrierungstoken auf
    • devices: listet WireGuard-Geräte auf, löscht sie, sperrt und entsperrt sie und zeigt aktive MFA-Sitzungen an
    • users: verwaltet Nutzer-MFA, löscht Nutzer, sperrt Konten und setzt MFA zurück
    • webadmin: fügt Web-UI-Administratornutzer hinzu, löscht und listet sie auf sowie sperrt und entsperrt Konten
    • version und firewall gehören ebenfalls zu den unterstützten Befehlen

Registrierungstoken und MFA-Ablauf

  • Für die Registrierung eines neuen Geräts wird zunächst mit einem Befehl wie wag registration -add -username tester ein Registrierungstoken erstellt
  • Wird das erzeugte Token an den öffentlichen Registrierungs-Endpunkt übermittelt, erhält man als Antwort eine WireGuard-Konfiguration
  • Die zurückgegebene Konfiguration enthält Einträge wie Interface, PrivateKey, Address, Peer, Endpoint, PublicKey, AllowedIPs und PersistentKeepAlive
  • Der Nutzer verbindet sich mit der VPN-Adresse des Servers und gibt den 2FA-Code ein
  • Wie lange die Sitzung bis zum Ablauf bestehen bleibt, wird in der Konfigurationsdatei festgelegt

Web-Verwaltungskonsole

  • Um sich an der Verwaltungskonsole anzumelden, muss Webserver.Management.Enabled auf true gesetzt werden
  • In der Konsole wird mit sudo ./wag webadmin -add -username <your_username> -password <your-password-here> ein Web-Admin-Konto hinzugefügt
  • Danach verbindet man sich mit der Verwaltungs-Listen-Adresse und gibt die Zugangsdaten ein
  • Die Weboberfläche selbst kann keine Administratornutzer hinzufügen
  • Es wird empfohlen, das Verwaltungsportal nicht nach außen offenzulegen; empfohlen ist, ListenAddress auf 127.0.0.1 oder localhost zu setzen und es per SSH-Forwarding bereitzustellen

Wichtige Konfigurationselemente

  • NumberProxies gibt die Anzahl vertrauenswürdiger Reverse Proxies vor dem Client an und lässt Wag die Client-IP unter Berücksichtigung von X-Forward-For parsen
  • Socket ist der Wag-Steuerungs-Socket; durch Änderung können mehrere Wag-Instanzen auf derselben Maschine ausgeführt werden
  • NAT schaltet Masquerading ein oder aus; wenn es aktiviert ist, wirkt sämtlicher Traffic so, als käme er vom VPN-Server
  • NATExcludeRanges legt CIDR-Bereiche fest, die bei NAT=true von NAT ausgenommen werden
  • ExposePorts macht Ports des VPN-Servers für Clients verfügbar und fügt iptables-Regeln hinzu
  • CheckUpdates ist standardmäßig deaktiviert; bei Aktivierung zeigt die Verwaltungs-UI Benachrichtigungen über neue Wag-Versionen an und greift auf api.github.com zu
  • Acls definiert Gruppen und Policies, wird aber nur beim ersten Start übernommen; zur Laufzeit erfolgt die Bearbeitung über die Web-UI
  • Webserver enthält Einstellungen für den öffentlichen Registrierungs-Endpunkt, das Tunnel-MFA-Portal und das Verwaltungsportal
  • Wireguard konfiguriert Gerätenamen, Listen-Port, privaten Schlüssel, das vom VPN verwaltete Subnetz, MTU und DNS-Server
  • Clustering umfasst Einstellungen für Cluster-Name, Status des etcd-Clusters, Log-Level, Witness-Node, Datenbankposition und Cluster-Zertifikate

Verhalten von ACL-Policies

  • Policies definieren die vom VPN zu erfassenden Routen sowie Ports und Protokolle, die Wag passieren dürfen
  • Die Regelanwendung nutzt die Subnetz-Prefix-Länge; der spezifischste Treffer bestimmt die Zugriffsstufe der Route
  • Wenn beispielsweise ein /16 als MFA definiert und darin ein bestimmtes /32 als Allow definiert ist, hat das spezifischere /32 Vorrang und ist ohne MFA erreichbar
  • Dieses Verhalten wurde in v6.0.0 geändert; zuvor hatten MFA-Routen immer Vorrang
  • Werden für eine Route mehrere Policies definiert, werden die Policies zusammengeführt; MFA-Regeln haben Vorrang
  • Ab einer noch nicht veröffentlichten Version kann der Routenzugriff mit Deny-Regeln blockiert werden
  • Die spezifischste Regel erzeugt einen neuen Regel-„Bucket“; wenn ein /32-Bucket nur Deny enthält, kann daher auch der Zugriff auf andere Ports desselben /32 nicht erlaubt sein

Port- und Protokollregeln

  • Der Zugriff auf Dienste kann über Port- und Protokollregeln definiert werden
  • Es gibt drei unterstützte Regeltypen
    • Any: Wenn keine eigene Regel vorhanden ist oder das Schlüsselwort any verwendet wird, sind alle Dienst- und Portkombinationen erlaubt
    • Single Service: erlaubt bestimmte TCP- und UDP-Ports eines Hosts, z. B. 192.168.1.1 22/tcp 53/udp
    • Ranges: legt Portbereiche fest, z. B. 192.168.1.1 22-1024/tcp 23-53/any
  • Bei Portbereichen muss der niedrigere Port zuerst angegeben werden
  • ICMP hat keine Ports und kann daher ohne Port angegeben werden, etwa 1.1.1.1 icmp

Einschränkungen und Entwicklung

  • Wag unterstützt nur ein AllowedIP pro Client
  • Diese Einschränkung passt zu einer Architektur, bei der Clients mit dem Server verbunden sind
  • Es ist hauptsächlich nur für Linux gedacht; Windows kann mit einigen zusätzlichen Schritten funktionieren
  • Im Entwicklungsmodus kann eine Umgebungsvariable verwendet werden, um die IP eingehender Tunnel-Anfragen auf die Client-IP zu setzen
  • Ein Testbeispiel führt sudo go test -v . in internal/router aus
  • Externe Beiträge werden gebeten, bei Feature-Ergänzungen oder Bugfixes nach Möglichkeit Tests zu schreiben und einen Pull Request zu öffnen

1 Kommentare

 
GN⁺ 2024-05-13
Hacker-News-Kommentare
  • Sieht auf den ersten Blick gut aus, aber ein paar Dinge geben mir zu denken
    Wenn man sich das Beispiel curl [http://public.server.address:8080/register_device?key=e83253...](<http://public.server.address/register_device/…;) und die Beschreibung „der Dienst liefert eine vollständig templatisierte Antwort zurück“ ansieht, wirkt es so, als würde beim Registrierungsprozess nicht der Client den privaten Schlüssel erzeugen und den öffentlichen Schlüssel an den Server senden, sondern der Server den privaten Schlüssel erzeugen und an den Client schicken
    Außerdem ist das Beispiel HTTP, daher wäre es besser, zumindest das zu ändern, damit niemand denkt, HTTP sei hier auch eine akzeptable Option
    Ich frage mich auch, ob der Client überhaupt erkennen kann, wenn die Sitzung abläuft. Oder hängt dann einfach etwa eine SSH-Sitzung?
    Ich habe gelegentlich nach einem WireGuard-Client gesucht, der ähnlich wie die Captive-Portal-Erkennung bei Wi‑Fi funktioniert. Idealerweise wäre das einfach eine Zeile wie persistentkeepalive in der Konfigurationsdatei, die eine URL abruft und regelmäßig prüft. Wenn OK zurückkommt, ist alles in Ordnung, wenn keine Antwort kommt, ist es ein Netzwerkproblem, und wenn ein Location-Header kommt, öffnet man den Browser an dieser Stelle, damit die Sitzung erneut authentifiziert werden kann usw.
    Einen solchen Client habe ich bisher noch nicht gefunden

    • Die Registrierungs-URL kann optional auch einen pubkey-Parameter annehmen, man ist also nicht darauf angewiesen, dass der Server den privaten Schlüssel erzeugt. Die Dokumentation ist nur etwas dünn, daher ist die Verwirrung verständlich
      Um die letzte Frage zu beantworten: Das von mir verwendete eBPF XDP kann nur PASS, DROP und REDIRECT. Deshalb behandle ich das mit dem einfachsten Ergebnis PASS/DROP, und Verbindungen hängen dann einfach
      Wenn man allerdings eine Captive-Portal-Erkennungsseite zur wag-MFA-Liste hinzufügt, kann man die Erkennung selbst konfigurieren, und danach übernimmt der Browser den Rest
      Ich habe nicht vor, in wag Funktionen zu implementieren, die wie Interception oder ein Proxy arbeiten. Das würde zwar Dinge wie Auth-Ablauf oder Logout einfacher machen, ist aber nicht die Richtung, in die ich gehen will
    • So eine Funktion wäre wirklich großartig, und ich hoffe, der Autor dieses Projekts zieht sie in Betracht
    • Ich habe einmal einen ähnlichen Server gebaut. Dafür waren Client-Zertifikate pro Gerät nötig, mit denen man sich per mTLS bei einer Login-Seite anmeldete, dann wurde der Benutzer per OIDC authentifiziert und der Tunnel aktiviert — der schwierige Teil war der Client
      Ich habe einen Go-Client für den Mac geschrieben und über das Brew-Kommandozeilenprogramm wg auch die Schlüsselerzeugung abgewickelt, aber das war grob und erforderte sudo
      Eine ordentliche native App mit Netzwerkberechtigungen wäre schön, liegt aber außerhalb meiner Fähigkeiten
  • Ich frage mich, ob das Problem des Sitzungsmanagements bereits behandelt wurde oder noch behandelt werden soll
    Im Kern sind WireGuard-Schlüssel so etwas wie permanente Sitzungsschlüssel
    Wenn Software, die die WireGuard-Transportschicht implementiert, eine richtige VPN-Server-Lösung sein soll, dann sollte sie auch Sitzungsmanagement implementieren. Also das regelmäßige Rotieren von Sitzungsschlüsseln über einen zweiten Kanal zum Server, das Beenden von Sitzungen, das Ändern von IP-Adressen, das Einrichten neuer Routen und bei Bedarf erneute Authentifizierung

    • Für so einen Anwendungsfall würde ich Firezone verwenden. Es hat eine Option, Nutzer zu regelmäßigen Anmeldungen auf der Plattform zu zwingen, und in Kombination mit OIDC an einen externen ID-Provider gekoppelt ist das eine sehr robuste und einfache Lösung für Sitzungsmanagement
    • Im Kontext von wag ist mir nicht ganz klar, was mit „permanente Sitzungsschlüssel“ genau gemeint ist
      WireGuard-Schlüssel ermöglichen die Kommunikation mit dem wag-Server, aber die eigentliche Sitzung wird über eine eBPF-Map verwaltet, die enthält, ob der Benutzer authentifiziert ist
      Deshalb kann jemand, der Material des privaten Schlüssels stiehlt, trotzdem nicht auf MFA-beschränkte Routen zugreifen
    • Wenn man einen VPN-Client wie GlobalProtect mit WireGuard bauen würde, hätte man vermutlich pro Client einen dauerhaften Authentifizierungsschlüssel, würde damit einen initialen Tunnel zum VPN-Controller aufbauen, darin die Authentifizierung durchführen und dann einen separaten Sitzungsschlüssel erhalten. Der erste Tunnel würde sofort beendet, sobald die Authentifizierung abgeschlossen ist und der eigentliche Sitzungsschlüssel vorliegt
    • Wenn es um einen zweiten Kanal für periodische Sitzungsschlüsselrotation, Sitzungsbeendigung, IP-Adressänderungen, neue Routen und Re-Authentifizierung geht, ist das dann nicht im Grunde das IKE-Protokoll von IPsec? Warum nicht einfach IPsec verwenden?
  • Ich frage mich, ob ihr Brute-Force auf TOTP-Codes verhindert, etwa durch Rate Limiting oder eine Begrenzung der Wiederholungsversuche
    Ich habe den Code kurz überflogen, konnte so etwas aber nicht finden
    Das Szenario, das ich mir vorstelle, ist, dass jemand im Browser die TOTP-Eingabeoberfläche öffnet, die Entwicklertools startet und dann alle möglichen TOTP-Codes wiederholt ausprobiert

    • Es gibt Schutz gegen Brute-Force bei TOTP-Codes. Für jede Authentifizierung gibt es eine begrenzte Zahl von Versuchen, und wenn diese überschritten wird, wird das Konto gesperrt und muss von einem Administrator entsperrt werden
      Die Absicht ist insbesondere auch, den Nutzer zum Nachdenken zu bringen, warum das Gerät überhaupt versucht, Authentifizierung zu erzwingen. Denn so eine Situation kann auf eine kompromittierte Endpoint-Umgebung hindeuten
    • Vermutlich ist es hier: https://github.com/NHAS/wag/blob/cdbdbec3393fa86bf6c823117c8...
    • Ich kenne die Details dieser Implementierung nicht, aber wenn jemand bereits über die Login-Daten verfügt, um überhaupt bis zum TOTP-Schritt zu kommen — also Benutzername und Passwort — dann ist dieser Benutzer im Grunde ohnehin schon kompromittiert
  • Klingt Headscale oder Tailscale sehr ähnlich. Es ist gut, Alternativen für die Verwaltung von WireGuard-Netzwerken zu sehen
    Ich frage mich, ob es Vergleichsmaterial gibt, aus dem hervorgeht, wie weit sich die Funktionen überschneiden, was zusätzlich vorhanden ist, was anders ist und was auch künftig nicht implementiert werden soll

    • In dem Sinne, dass WireGuard verwendet wird, ist es definitiv ähnlich
      Ich habe keinen direkten Vergleich in die Dokumentation aufgenommen, weil das aktuell nicht die Richtung ist, in die ich gehen will. Dieses Projekt passt zu meinen Anforderungen und macht ziemlich viel Spaß
      Wag eignet sich eher für eine Hub-and-Spoke-Struktur, wenn man harte Grenzen möchte, statt für ein Tailscale-artiges Mesh, in dem alles miteinander verbunden ist und die Regeln das Overlay definieren
      Sowohl wag als auch Tailscale fügen SSO-Integration und praktisch 2FA zum Schutz der Benutzer hinzu
      Beide haben Methoden zur Registrierung und ein Web-UI für die Verwaltung, aber ich bin ein Solo-Entwickler, der keine Webentwicklung mag, daher ist Tailscale sicher deutlich polierter
      Was ich definitiv nicht implementieren werde, ist die Richtung Interception oder TLS-Proxy, um Benutzer nach einem Sitzungs-Logout umzuleiten. Der Hauptgrund ist, dass mir das mit eBPF im Moment etwas zu viel ist und ich die DNAT/SNAT-Komponenten, die ich dafür vermutlich schreiben müsste, nicht verwenden möchte, nur damit es funktioniert
  • Nur IPv4? Bei einem Setup, das sich für WireGuard entscheidet, könnte ich erwarten, dass es moderner aufgestellt ist und viele eigene ULA-as-a-Service nutzt

    • Ich plane, bald IPv6-Unterstützung hinzuzufügen, und überlege auch, die IPv4-Adressen der Nutzer in privaten IPv6-Adressraum abzubilden, um das Risiko von Konflikten mit ihren tatsächlichen lokalen Netzwerken zu verringern
      Hattest du beim Erwähnen von ULA etwas Bestimmtes im Sinn?
    • Mich würde interessieren, welchen Vorteil von ULA du hier konkret im Kopf hast