1 Punkte von GN⁺ 2024-03-14 | 1 Kommentare | Auf WhatsApp teilen
  • Fly.io hat die Vorgehensweise geändert, um die Zustandslast der WireGuard-Gateways zu reduzieren, während die direkte Kommunikation zwischen flyctl und Fly Machines erhalten bleibt: Peers werden nicht mehr vorab installiert, sondern erst beim Verbindungsaufbau dem Kernel hinzugefügt.
  • Im bisherigen Ablauf übergab die GraphQL-API die Peer-Konfiguration per NATS-RPC, wggwd registrierte sie in SQLite und im Linux-Kernel-WireGuard, danach stellte flyctl die Verbindung her.
  • Verlorene NATS-Nachrichten und einmalig erzeugte Peers in CI-Jobs führten zusammen dazu, dass sich auf den Gateways Hunderttausende nicht wiederverwendeter Peers ansammelten und Kernel-Operationen sowie das Laden beim Neustart langsam wurden.
  • Der neue Ansatz fängt handshake initiation-Pakete über einen BPF-Filter oder den WebSockets-Empfangspfad ab, entschlüsselt einen Teil des Noise-Handshakes, um den öffentlichen Schlüssel zu identifizieren, und holt dann nur die benötigten Peers über eine interne HTTP-API.
  • Nach einigen Wochen im Betrieb war die Zahl alter Peers nahezu verschwunden; die Gateways können nun mit weniger Zustand schnellere Peer-Einrichtung und Neustarts bewältigen.

Wie Fly.io WireGuard nutzt

  • Fly.io führt Container als Firecracker-basierte VMs aus und nutzt WireGuard an mehreren Stellen wie einen Teil der Kunden-API.
  • flyctl erstellt beim Start einen TCP/IP-Stack mit eigener IPv6-Adresse und kommuniziert direkt mit Fly Machines im Fly.io-Netzwerk.
  • Dieser Ansatz macht es einfacher, Funktionen wie Remote-Docker-Builder so darzustellen, als befänden sie sich im selben LAN; sie dauerhaft zuverlässig zu betreiben, ist jedoch schwieriger.
  • Fly.io stellte den Standardpfad schließlich auf WireGuard-over-WebSockets um.

Bisheriger Provisioning-Ablauf der Gateways

  • Fly.io verbindet eingehende WireGuard-Verbindungen über mehrere Gateway-Server weltweit mit dem passenden privaten Netzwerk.
  • Wenn flyctl für Container-Builds, SSH-Konsolen, Dateikopien oder Service-Proxys mit einer Fly Machine kommunizieren muss, startet oder verbindet es einen Agent-Prozess im Hintergrund.
  • Beim ersten Start erzeugt der Agent über die GraphQL-API eine neue WireGuard-Peer-Konfiguration.
    • Die Peer-Konfiguration besteht aus einem öffentlichen Schlüssel und der zu verbindenden Adresse.
  • Die API übermittelt diese Konfiguration per RPC im NATS-Messaging-System an das passende Gateway.
  • wggwd auf dem Gateway nimmt die Konfiguration entgegen, speichert sie in SQLite, fügt sie über die WireGuard-Go-Bibliothek dem Kernel hinzu und meldet der API die abgeschlossene Installation.
  • Wenn die API die Konfiguration in der GraphQL-Anfrage zurückgibt, verbindet sich flyctl mit dem bereits auf dem Gateway installierten WireGuard-Peer.

Warum die bisherige Architektur langsam wurde

  • NATS ist schnell, garantiert aber keine Zustellung, wodurch es als Basis für eine zuverlässige API schwierig zu nutzen war.
    • Fly.io reduzierte intern die Nutzung von NATS; so wurde etwa die interne flyd-API von NATS-basiert auf HTTP-basiert umgestellt.
    • Die Reduzierung der NATS-Nutzung verbesserte die WireGuard-Gateways, reichte aber nicht aus.
  • Nach dem Beenden von flyctl blieben die erzeugten WireGuard-Peers auf dem Gateway erhalten, und es gab keinen Prozess zum Aufräumen alter Peers.
    • Die Entscheidung, Peers nicht zu entfernen, beruhte auf der Möglichkeit, am nächsten Tag erneut zu deployen oder per fly ssh console zu debuggen.
    • Die meisten Peers entstanden jedoch in CI-Jobs ohne persistenten Storage; beim nächsten Lauf konnten sie sich nicht mit demselben Peer erneut verbinden, sodass jedes Mal ein neuer Peer erzeugt wurde.
  • In der Folge hielten Gateways Hunderttausende Peers vor, die möglicherweise nie wiederverwendet wurden.
    • Mit der wachsenden Zahl alter Peers wurden Kernel-WireGuard-Operationen sehr langsam.
    • Besonders langsam war es, nach einem Neustart eines Gateway-Servers alle Peers wieder in den Kernel zu laden.
    • Es traten auch einige Kernel Panics auf.

Design: Peers nur bei Bedarf im Kernel installieren

  • Die gesamte Historie aller WireGuard-Peers in einer einzelnen SQLite-Datenbank zu speichern, ist nicht besonders schwierig; alle Peers im Linux-Kernel vorzuhalten, wird jedoch zum Flaschenhals.
  • Statt Konfigurationen an Gateways zu pushen, entschied sich Fly.io dafür, dass Gateways die benötigten Peers on demand aus der API abrufen.
  • Wenn Peers erst dann dem Kernel hinzugefügt werden, wenn ein Client eine Verbindung aufbauen will, können alte Peers jederzeit aus dem Kernel entfernt werden.
  • Entfernte Peers können beim nächsten Verbindungsversuch wieder abgerufen und installiert werden, sodass Gateways weniger langfristigen Zustand halten müssen.
  • Allerdings bietet Linux-Kernel-WireGuard keine API, um ein Ereignis wie „incoming connection attempt“ zu abonnieren.

Umsetzung von JIT-WireGuard-Peers

  • Die Konfigurationsschnittstelle von WireGuard im Linux-Kernel ist Netlink, und die Go-Steuerbibliothek für WireGuard verwendet wgctrl-go.
  • Fly.io nutzt aus, dass WireGuard-Verbindungsanfragen als erkennbare Pakete vorliegen, und erzeugt mit BPF-Filtern und einem packet socket direkt eigene Events.
  • Beim WebSockets-WireGuard-Pfad lassen sich rohe WireGuard-Pakete einfacher erhalten.
    • Dieser Pfad tauscht als UDP-Pakete geframte Daten über eine nicht authentifizierte WebSockets-Verbindung mit der Gateway-Schnittstelle aus.
    • Da Fly.io den Code dieses Daemons besitzt, kann es einen Hook in die Paketempfangsfunktion einbauen.
  • WireGuard kennt keine Konzepte von „Client“ und „Server“, sondern ist ein Point-to-Point-Protokoll, bei dem Peers miteinander verbinden, sobald Traffic gesendet wird.
    • Die Seite, die zuerst verbindet, ist der Initiator, die Gegenseite der Responder.
    • Bei Fly.io ist normalerweise flyctl der Initiator und das Gateway der Responder.
  • Das erste UDP-Paket ist gemäß dem WireGuard-Paper eine handshake initiation; der Pakettyp steht in einem Klartext-Byte.
    • Fly.io erfasst eingehende Verbindungen mit dem BPF-Filter udp and dst port 51820 and udp[8] = 1.

Peers im Noise-Handshake identifizieren

  • WireGuard basiert auf dem Noise Protocol Framework, und Noise verbirgt Identifikatoren während des Handshakes für identity hiding.
  • Daher kann man nicht einfach einen Wert wie einen Benutzernamen aus dem Paket lesen und damit direkt die Konfiguration finden.
  • Fly.io führt einen Teil der Noise-Verschlüsselung aus, um die Identität zu entschlüsseln und eingehende Anfragen zu identifizieren.
    • Dieser Code ist anspruchsvoll, umfasst aber nur rund 200 Zeilen.
    • Die Kernel-Netlink-Schnittstelle kann einem privilegierten Prozess den privaten Schlüssel der Schnittstelle bereitstellen, sodass die nötigen geheimen Werte verfügbar sind.
    • Der zugehörige Code ist als Gist veröffentlicht.
  • Nach diesem Schritt erhält man einen Event-Feed der öffentlichen Schlüssel von Nutzern, die versuchen, eine WireGuard-Verbindung zum Gateway aufzubauen.

Optimierungen für Installation, Cache und Retries

  • Das Gateway hält in SQLite einen Rate-Limit-Cache vor und ruft bei Entdeckung eines neuen Peers die zugehörigen Peer-Informationen per interner HTTP-API ab, um sie zu installieren.
  • Diese Logik passte gut in den kleinen Daemon, der auf dem Gateway bereits WireGuard verwaltete.
  • Alte Peers können nun per cron-Job aktiv entfernt werden.
  • API-Lookups für neue Peers sind möglicherweise nicht schnell genug, um sofort auf die erste handshake initiation-Nachricht zu reagieren.
    • Da WireGuard schnell erneut versucht, ist das für die Funktion an sich kein Problem.
  • Mithilfe einer von Jason Donenfeld erläuterten Linux-WireGuard-Netlink-Funktion kann die Verbindung schneller hergestellt werden.
    • Aus der eingehenden Initiation-Nachricht wird die 4-Tupel-Adresse einschließlich des temporären Source-Ports von flyctl gewonnen.
    • Das Gateway installiert den Peer so, als wäre es selbst der Initiator und flyctl der Responder.
    • Der Linux-Kernel startet die WireGuard-Verbindung in Richtung flyctl; das Protokoll hängt nicht stark von Server- und Client-Rollen ab.
    • Neue Verbindungen werden nahezu mit der Geschwindigkeit aufgebaut, mit der sie installiert werden können.

Ergebnisse im Betrieb

  • Dieser Ansatz läuft seit einigen Wochen in Produktion.
  • Die Zahl alter WireGuard-Peers, die je nach Gateway von Tausenden bis Hunderttausenden reichte, ist auf nahezu null gefallen.
  • Gateways müssen weniger Zustand vorhalten.
  • Peer-Konfigurationen erfolgen schneller.
  • Beim Neustart müssen weniger ungenutzte Peers erneut in den Kernel geladen werden.

1 Kommentare

 
GN⁺ 2024-03-14
Hacker-News-Kommentare
  • Ich verstehe nicht ganz die Aussage, dass WireGuard im Linux-Kernel keine Funktion hat, Peers bei Bedarf zu installieren. Es scheint möglich zu sein, Peers auch zur Laufzeit hinzuzufügen: https://serverfault.com/questions/1101002/wireguard-client-a...
    Wenn ich das richtig verstehe, ist dieser Schritt bereits zu spät, und man möchte offenbar authentifizieren, bevor man den Peer hinzufügt, damit keine veralteten Einträge im Interface verbleiben.
    Daher sieht die Architektur so aus, dass vor das Interface ein eBPF-Filter gesetzt wird, der auf Basis von Cryptokey-Routing prüft, ob die Gegenstelle autorisiert ist, dann bei Erfolg den Peer dem Interface hinzufügt und ihn nach einem Timeout wieder entfernt.

    • Letztlich ist gewünscht, dass Kernel-WireGuard eine Netlink-API bereitstellt, die die Liste der Public Keys weitergibt, die es in Initiator-Nachrichten sieht. Mittelfristig scheint Jason so eine Funktion ebenfalls anbieten zu wollen; mit diesem Feed müsste man überhaupt keine WireGuard-Peers vorab installieren.
      Die Peers lägen alle etwa in SQLite und würden erst bei Bedarf installiert, wenn ein Client versucht, eine Verbindung aufzubauen.
      Aus Sicht eines VPN-Anbieters ist die aktuelle API etwas grob. Zwar ist in der Praxis zu jedem Zeitpunkt nur ein Teil der Peers aktiv, aber wenn die Zahl der Peers von Hunderttausenden auf Zehnmillionen wächst, wird es unmöglich, sie alle in einer einzelnen Kernel-Instanz zu speichern.
      Wenn Peers vorab installiert werden müssen, bindet man sie letztlich an eine bestimmte Servermaschine.
      Wie der Artikel sagt, kann man schon heute mit einfachem Packet Capture etwas Ähnliches wie das benötigte Interface bauen, und weil Jason die API gut entworfen hat, lässt sich die Initiator-Richtung zwischen Server und Client sehr leicht umdrehen. Selbst wenn der Kernel die erste Initiator-Nachricht verworfen hat, fühlt es sich für den Nutzer wie eine nahtlose Verbindung an.
      Jann Horn meinte, man könne noch einen Schritt weitergehen und das erfasste Initiator-Paket aufbewahren und nach der Peer-Installation wieder in den Kernel injizieren; auch das ist eine ziemlich gute Idee.
      Ich würde nicht sagen, dass dieser Artikel lebensverändernd ist, eher eine Sammlung sauberer Tricks, die Leute kennen sollten.
      Der nächste Schritt wäre, darauf aufbauend floating peers zu bauen und Peers vollständig von Regionen zu entkoppeln. Dann müssten Nutzer sich nicht darum kümmern, in welcher Region ein Peer konfiguriert wurde, und das hätte wohl echte Produktvorteile, über reinen Nerd-Spaß hinaus.
    • Es wirkt so, als sei das gemacht worden, um die Alternative zu vermeiden, WireGuard außerhalb des Kernels laufen zu lassen. Linux hat im Kernel keine Funktion, zuerst nach kryptografischer Adresse zu routen, aber man wollte den Kernel nicht verlassen und hat es deshalb per Hack eingebaut, so scheint es mir.
      Der Ausdruck JIT WireGuard fühlt sich etwas seltsam an. Mein erster Gedanke war: „Warum? Der Performance-Flaschenhals ist die Verschlüsselung, und ein client-spezifisches JIT hilft dort nicht.“
      Ich wäre wohl einfach in den Userspace gegangen. Mit tokio-uring oder glommio ließe sich Performance herausholen.
      Wenn man weiter alles im Kernel erzwingt, wird man ständig an Grenzen stoßen, weil Linux nicht dafür gebaut wurde, Millionen aktiver Tunnel zu verarbeiten. Schon Millionen TCP-Verbindungen in einem Kernel können mitunter knifflig sein.
      Für jede Grenze braucht man einen Hack, und jeder Hack bringt Systemeinstellungen mit sich, die angewendet und verwaltet werden müssen. Die Toolchain für die Provisionierung physischer Linux-Server liegt weit hinter Werkzeugen für App- und Service-Entwicklung sowie Konfigurationsmanagement zurück.
      Oder bin ich einfach dumm und habe etwas falsch verstanden?
  • Wenn man in einer Go-App einen Userspace-WireGuard-Peer bauen möchte, lohnt sich ein Blick auf mein aktuelles Experimentierprojekt https://github.com/dpeckett/noisysockets
    Es basiert auf der hervorragenden Arbeit von wireguard-go, soll aber einfacher als Library nutzbar und idiomatischer für Go sein.
    Damit ein Service Mesh zu bauen, wäre interessant. Mehrere Sprachen zu unterstützen wäre schwierig, aber man könnte vielleicht eine Socket-API implementieren.
    Allerdings habe ich für WireGuard-Verschlüsselung noch keine Hardwarebeschleunigung gesehen, daher könnte es bei der Performance schwer werden, mit mTLS zu konkurrieren.
    Nebenbei: Ich suche derzeit Freelance-Arbeit; wenn jemand einen Golang-Freelancer für schnelles, sicheres Networking braucht, kann er sich gern melden.

    • Ich habe den Traum, ein Userspace-WireGuard-Projekt zu nehmen, auf einem Relay davor die WireGuard-Keys per PAKE auszutauschen und anschließend per Hole Punching einen direkten Tunnel aufzubauen.
      So etwas wie Magic Wormhole für beliebige Tunnel; ich hoffe, dass es auch das Problem deutlich verbessert, dass Dateiübertragungen über lange High-Bandwidth-Netzwerke bei 20–30 MB/s einbrechen.
    • Ich frage mich, ob Noisy Transport in gewisser Weise Slacks Nebula [0] ähnelt, oder ob ich da etwas verwechsle.
      0 - https://github.com/slackhq/nebula
  • Ich stimme grundsätzlich zu, dass für einzelne Punkt-zu-Punkt-Nachrichten direkte HTTP-Requests zuverlässiger sein können als der Weg über eine Message Queue, aber ich bin etwas überrascht, dass bei NATS so viele Nachrichten verloren gegangen sind, dass es den Dienst stark beeinträchtigt hat.
    Wenn Nachrichten verloren gehen, sendet NATS sie dann nicht erneut, bis es erfolgreich ist? Weiß jemand, warum eine spürbare Instabilität aufgetreten sein könnte?

    • Ich wäre sehr an mehr Details interessiert. Den NATS-Maintainern dürfte es genauso gehen.
      Die Architektur von NATS ist intuitiv und attraktiv, daher würde mich interessieren, wo es schiefgelaufen ist. JetStream hat viele einstellbare Parameter.
      Zum Beispiel sind Memory-Streams mit zeitbasiertem Deduplizierungsfenster, Push/Pull-Modi sowie Einstellungen für Redelivery- und Ack-Policies möglich.
      Allerdings könnte es nicht gut zu einmaligen Verbindungen mit einzelnen Nachrichten passen. So oder so wären konkretere Details sehr hilfreich.
    • Ich will NATS nicht schlechtmachen. Wahrscheinlich haben wir es schlicht falsch genutzt.
      Am Ende brauchten wir es aber nicht. Die Messaging-Schicht hat uns nicht mehr Ausdruckskraft gegeben, sondern Tests und Monitoring nur schwieriger gemacht.
    • Wenn ihr core NATS verwendet habt, dann war es meines Wissens nicht JetStream, daher dürfte es überhaupt keine Redelivery-Option gegeben haben.
  • Der Teil „wir richten den Peer so ein, als wären wir der Initiator, und lassen flyctl der Responder sein. Der Linux-Kernel startet die WireGuard-Verbindung zu flyctl neu“ fügt dem Handshake im Grunde eine halbe Round-Trip-Latenz hinzu?
    Ich frage mich zum Beispiel, ob der Ablauf so ist: 1) flyctl sendet eine Initiation, 2) per netlink wird ein Peer hinzugefügt und eine neue Initiation gesendet, 3) flyctl sendet eine Response

    • So wie ich es lese, „denken“ beide Peers, sie hätten selbst gestartet, aber in der Praxis scheint das egal zu sein
      Also gibt es Schritt 3 entweder nicht oder man muss nicht darauf warten; und wenn man die neue Initiation aus Schritt 2 verhindert, wäre das wohl definitiv nicht der Fall
    • Im Großen und Ganzen stimmt das. Wenn man sich vorstellt, dass „Bob“ die Policy hat, nur mit Nummern telefonieren zu können, die in seinem Adressbuch stehen, kann man es so sehen:
      1. Alice ruft Bob an
        1.a) Bob nimmt den Anruf nicht an, fügt aber die Nummer aus der Anrufer-ID seinem Adressbuch hinzu
      2. Bob ruft diese Nummer, also Alice, zurück
      3. Alice nimmt ab, und beide unterhalten sich glücklich
  • Ich verstehe nicht, was mit „jedes Mal, wenn man flyctl ausführt, erschafft unsere liebenswerte, riesige CLI aus dem Nichts einen TCP/IP-Stack, hat ihre eigene IPv6-Adresse und spricht direkt mit den in unserem Netzwerk laufenden Fly Machines“ gemeint ist

    • Im Grunde heißt das, dass eine Userspace-WireGuard-Implementierung wie die in Go verwendet wird. Das ist der Gegenentwurf zu WireGuard im Kernel
      Die Formulierung „erschafft aus dem Nichts einen TCP/IP-Stack“ kommt daher, dass normalerweise das Betriebssystem den TCP/IP-Stack als Teil des Kernels bereitstellt
      Bei wireguard-go läuft der TCP/IP-Stack im Userspace, daher kann man ihn innerhalb eines normalen Userspace-Prozesses wie dem flyctl-Kommandozeileninterface erzeugen
      Für Leute, die schon lange mit Systemen arbeiten, kann das ziemlich magisch wirken. Wirklich brauchbare prozessinterne TCP/IP-Stacks im Userspace sind relativ neu und originell
    • Dazu habe ich einen eigenen ganzen Artikel geschrieben: https://fly.io/blog/our-user-mode-wireguard-year/
    • Es heißt, dass WireGuard verwendet wird
    • Eine liebenswerte riesige CLI kann ich mir schwer vorstellen
  • Ich frage mich, was einen daran hindert, das erste Handshake-Paket wieder in den Netzwerk-Stack einzuspeisen. Dann gäbe es anscheinend keinen Paketverlust
    Außerdem frage ich mich, wozu im eBPF-Filter udp[8] = 1 geprüft wird

    • Nichts hindert einen daran. Gute Idee
      Wie im Nachbarkommentar gesagt, fischt der BPF-Filter nur Initiation-Pakete heraus, und genau das ist das gewünschte Verhalten. Es ist die WireGuard-Version davon, bei TCP nach SYN zu sniffen, um den Verbindungsaufbau zu sehen
    • udp[8] = 1 filtert nur Handshake-Pakete. Ohne das würden auch Datenpakete an den Userspace-Daemon geschickt
      Ich bin nicht sicher, ob man den ersten Handshake wieder abspielen kann, aber da WireGuard unbekannte Clients ignoriert, könnte es möglich sein
    • Klingt wie ein NFQUEUE-Helper, der das Paket freigibt, nachdem der Key hinzugefügt wurde
  • Interessant, dass WireGuard standardmäßig über WebSocket getunnelt wird. Für die Performance ist das nicht gut, aber für DevOps-artige Aufgaben, bei denen flyctl genutzt wird, dürfte es in Ordnung sein
    Daran musste ich auch denken, wenn es um die Zukunft von QUIC/HTTP3 geht. Die Wahrscheinlichkeit ist nicht null, dass Netzwerkbetreiber UDP auf Port 443 eher komplett blockieren, statt ihn korrekt zu behandeln

    • Natürlich kann man auch natives WireGuard verwenden, und in flyctl gibt es dafür ebenfalls eine Konfigurationsoption
      Wenn UDP nicht funktioniert, funktioniert es gar nicht, und das Debugging ist schwierig; deshalb haben wir den Default auf das gesetzt, von dem wir wissen, dass es zuverlässig funktioniert
      Dass ich die Debatte darüber, welchen Default man wählen sollte, verloren habe, ist bitter
  • Mein Startup hat Fly fast ein Jahr lang genutzt. Die Kernfunktion, Code in unter einer Minute zu deployed Code zu machen, ist wirklich schön
    Neue Nodes für Backfills hoch- und wieder herunterzufahren dauerte ebenfalls nur Sekunden
    Aber das Unternehmen selbst wirkte ein wenig unausgereift. Einmal war unser API-Server auf Fly 48 Stunden lang nicht erreichbar, und ich war mir nicht sicher, ob es ein Konfigurationsfehler von mir war oder ein weiterer „stiller“ Ausfall
    Es gibt ein „db“-Produkt, aber es heißt dann etwa „kein Managed Postgres“, und auch dort kam es regelmäßig zu Unterbrechungen
    Es fühlte sich seltsam an, Postgres in der CLI als Top-Level-Nomen aufzunehmen und dann den Umfang der unterstützten Funktionen einzuschränken
    Auch der Zugriff auf die zentrale Service-API fiel häufig aus, sodass man warten musste, bevor man Änderungen an neuen Services deployen konnte
    Die Deployment-Erfahrung vermisse ich, aber ehrlich gesagt bin ich inzwischen mit GCPs Cloud Run zufriedener. Es gibt deutlich weniger „Überraschungen“, und die Dokumentation ist wesentlich ausgereifter

    • Die Deployment-Erfahrung ist großartig, aber für mich sind die Killer-Features von Fly.io das Anycast-Netzwerk sowie Funktionen wie FLY_REPLAY und LiteFS. Damit wird Clustering sehr einfach
      Ich finde es erstaunlich, dass VPS-Anbieter kaum Unterstützung dafür bieten, die Latenz von Backend-Services für Nutzer zu verringern. Niemand unterstützt Anycast, und es gibt nur sehr wenige GeoDNS-Optionen
      GeoDNS bringt allerdings zusätzliche Komplexität mit sich
      Ich wünschte, die Datenübertragungskosten bei Fly.io wären niedriger. Im Moment muss ich in einem ngrok-ähnlichen Dienst, an dem ich arbeite, einen großen Teil der Fly.io-Funktionalität unbeholfen neu implementieren
      [0]: https://lastlogin.io
      [1]: Der Fly-spezifische Code, der nötig ist, um LastLogin weltweit verteilt auszuführen, ist ungefähr so viel: https://github.com/lastlogin-io/obligator/blob/37f75cc861f1b...
    • Fly sieht gut aus, aber ich hatte noch keine Gelegenheit, es selbst auszuprobieren. Allerdings gehört Cloud Run von GCP zu meinen drei liebsten Infrastruktur- und Deployment-Tools, die Messlatte liegt also ziemlich hoch
    • Ich hatte fast dieselbe Erfahrung. Nach einem Jahr mit Fly sind wir vor ein, zwei Monaten zu GCP gewechselt, und in unserem Fall haben wir uns aus bestimmten Gründen für GKE entschieden
      Wenn es gut funktionierte, war es wirklich reibungslos, aber das passierte nicht oft genug
  • Ich möchte die Gelegenheit nutzen, Netmaker[0] vorzustellen.
    Ich habe nichts damit zu tun, sondern nutze es nur zufrieden, weil ich privaten Zugriff auf AWS-VPCs über mehrere Accounts hinweg brauche. Ich würde mich freuen, wenn es breiter genutzt würde.
    [0] https://www.netmaker.io/

    • Ist Netmaker so etwas wie Tailscale? Nur anhand der Website ist mir nicht ganz klar, worin das Unterscheidungsmerkmal besteht.
    • Netmaker oder ähnliche Tools scheinen die Schlüsselverwaltung zu übernehmen, und das dürfte die Verwaltung deutlich einfacher machen.
      In meinem früheren Job habe ich mit Ansible wg auf einigen Windows- und Linux-Rechnern eingerichtet und verwaltet; das war zwar okay, wurde am Ende aber etwas unübersichtlich.
    • Kann man das nicht AWS-nativ mit PrivateLink oder VPC Peering machen? Ich kenne mich in dem Bereich nicht gut aus und verstehe daher den Vorteil von Netmaker nicht.
    • Ist das eine allgemeine VPN-Plattform? Ich frage mich, ob es etwas Ähnliches wie Tailscale ist.
      Die Website ist zu vage.
  • Der Teil mit „Gateways mit Hunderttausenden Peers, von denen viele nie wieder verwendet werden“ war genau der Gedanke, der mir beim Lesen der ersten Absätze kam.
    Auch die Idee „Es gibt keinen API-Aufruf, um Events für eingehende Verbindungsversuche zu abonnieren. Schon gut. Dann erzeugen wir die Events eben selbst. WireGuard-Verbindungsanfragen sind Pakete und leicht zu identifizieren, sodass man sie mit einem BPF-Filter und Packet Sockets effizient abfangen kann“ ist gut.
    Wenn eine eingehende Initiierungsnachricht eintrifft, bekommt man die 4-Tupel-Adresse der gewünschten Verbindung, einschließlich des temporären Source-Ports, den flyctl verwendet; dann wird der Peer so installiert, als wären wir der Initiator und flyctl der Responder. Ich frage mich, ob das auch hinter NAT funktioniert.

    • Funktioniert. UDP-NAT kennt nämlich nur das 4-Tupel. Zum Beispiel in der Form {wggwd.fly.io, 12345, clientIP, 23456}.
      Ob es ein neues „Initiator“-UDP-Paket ist oder eine Antwort auf eine ausgehende Initiierungsnachricht: Für das UDP-NAT auf dem Pfad sieht es exakt gleich aus.
      Die Entscheidungsgrundlage ist nur das 4-Tupel, und dieses 4-Tupel ist identisch.
    • Wenn Pakete zur selben IP/zum selben Port zurückgehen und von derselben IP/demselben Port erzeugt werden, funktioniert es durch NAT hindurch.