Show HN: Wag – Multi-Faktor-Authentifizierung und Registrierungssystem für WireGuard
(github.com/NHAS)- 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
iptablesundlibpamsowie die Ausführung als root zur Verwaltung voniptablesund WireGuard-Geräten erforderlich - Die Verwaltung ist über Web-UI und CLI möglich; die CLI nutzt die Unterbefehle
start,registration,devices,usersundwebadminfür Registrierungstoken, Gerätesperren, MFA-Reset und Web-Admin-Konten - Zu den Einschränkungen gehört, dass nur ein
AllowedIPpro 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=1verwendet - Für IPv6 werden entsprechende
sysctl-Einstellungen wienet.ipv6.conf.all.forwarding=1verwendet
- Für IPv4 wird die Einstellung
- 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/tunwird in den Container eingebunden
- Beispielport für die Verwaltungsseite:
- Für eine manuelle Installation werden
iptablesundlibpambenötigt - Wag muss als root ausgeführt werden, um
iptablesund das WireGuard-Gerät zu verwalten - Binäre Releases benötigen
glibc 2.31+ - Builds aus dem Quellcode benötigen
go1.23.1undnpm
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 nichtregistration: erstellt, löscht und listet Registrierungstoken aufdevices: listet WireGuard-Geräte auf, löscht sie, sperrt und entsperrt sie und zeigt aktive MFA-Sitzungen anusers: verwaltet Nutzer-MFA, löscht Nutzer, sperrt Konten und setzt MFA zurückwebadmin: fügt Web-UI-Administratornutzer hinzu, löscht und listet sie auf sowie sperrt und entsperrt Kontenversionundfirewallgehö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 testerein 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,AllowedIPsundPersistentKeepAlive - 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.Enabledauftruegesetzt 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,
ListenAddressauf127.0.0.1oderlocalhostzu setzen und es per SSH-Forwarding bereitzustellen
Wichtige Konfigurationselemente
NumberProxiesgibt die Anzahl vertrauenswürdiger Reverse Proxies vor dem Client an und lässt Wag die Client-IP unter Berücksichtigung vonX-Forward-ForparsenSocketist der Wag-Steuerungs-Socket; durch Änderung können mehrere Wag-Instanzen auf derselben Maschine ausgeführt werdenNATschaltet Masquerading ein oder aus; wenn es aktiviert ist, wirkt sämtlicher Traffic so, als käme er vom VPN-ServerNATExcludeRangeslegt CIDR-Bereiche fest, die beiNAT=truevon NAT ausgenommen werdenExposePortsmacht Ports des VPN-Servers für Clients verfügbar und fügtiptables-Regeln hinzuCheckUpdatesist standardmäßig deaktiviert; bei Aktivierung zeigt die Verwaltungs-UI Benachrichtigungen über neue Wag-Versionen an und greift aufapi.github.comzuAclsdefiniert Gruppen und Policies, wird aber nur beim ersten Start übernommen; zur Laufzeit erfolgt die Bearbeitung über die Web-UIWebserverenthält Einstellungen für den öffentlichen Registrierungs-Endpunkt, das Tunnel-MFA-Portal und das VerwaltungsportalWireguardkonfiguriert Gerätenamen, Listen-Port, privaten Schlüssel, das vom VPN verwaltete Subnetz, MTU und DNS-ServerClusteringumfasst Einstellungen für Cluster-Name, Status des etcd-Clusters, Log-Level, Witness-Node, Datenbankposition und Cluster-Zertifikate
Verhalten von ACL-Policies
Policiesdefinieren 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
/16als MFA definiert und darin ein bestimmtes/32als Allow definiert ist, hat das spezifischere/32Vorrang und ist ohne MFA erreichbar - Dieses Verhalten wurde in
v6.0.0geä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/32nicht 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
anyverwendet 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
- Any: Wenn keine eigene Regel vorhanden ist oder das Schlüsselwort
- 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
AllowedIPpro 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 .ininternal/routeraus - 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
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 schickenAuß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
persistentkeepalivein der Konfigurationsdatei, die eine URL abruft und regelmäßig prüft. WennOKzurückkommt, ist alles in Ordnung, wenn keine Antwort kommt, ist es ein Netzwerkproblem, und wenn einLocation-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
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ändlichUm die letzte Frage zu beantworten: Das von mir verwendete eBPF XDP kann nur
PASS,DROPundREDIRECT. Deshalb behandle ich das mit dem einfachsten ErgebnisPASS/DROP, und Verbindungen hängen dann einfachWenn 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
Ich habe einen Go-Client für den Mac geschrieben und über das Brew-Kommandozeilenprogramm
wgauch die Schlüsselerzeugung abgewickelt, aber das war grob und erfordertesudoEine 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
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
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
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
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
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
Hattest du beim Erwähnen von ULA etwas Bestimmtes im Sinn?