networking

tailnet-operator: Verbindungen deklarieren statt Proxys verkabeln

Kubernetes und Headscale sprechen nicht automatisch dieselbe Sprache. Mein tailnet-operator verbindet beide Welten: Services für entfernte Ziele, private HTTPS-Eingänge, abgeleitete Zugriffsregeln und Statusmeldungen, die mehr sagen als ‚Pod läuft‘. Eine Vorstellung anhand des realen Lab-Einsatzes.

Die Datenbank ist erreichbar. Jedenfalls vom Laptop. Aus dem Pod nicht. Der Tunnel steht, die Route ist angekündigt, der Proxy läuft, und trotzdem bleibt die Verbindung hängen. Irgendwo zwischen Kubernetes-Service, Headscale-Policy, Routenfreigabe und DNS fehlt ein Stück — nur gehört dieser Zusammenhang keinem einzelnen System.

Genau für diese Lücke entwickle ich tailnet-operator. Er macht aus Verbindungen zwischen Kubernetes und einem Headscale-Tailnet eigene Ressourcen: Ein Namespace bekommt einen stabilen Service für ein entferntes Ziel. Eine Anwendung bekommt einen privaten HTTPS-Eingang. Und die dazugehörigen Identitäten, Freigaben und Statusinformationen entstehen aus derselben Deklaration.

Nicht noch ein VPN. Sondern die fehlende Betriebslogik zwischen zwei bereits vorhandenen Welten.

Das Lab wird multi-cluster — wiederholbar föderiert und evaluiert

Aus einem handverdrahteten Cluster plus Wegwerf-Test wurde ein wiederholbares Muster: ein Cluster pro Befehl, Apps im cluster-scope, Flux-Tenants, eine aufgeräumte Headscale-Domain — und Föderation statt Node-Mesh. Inklusive der Vorgeschichte (warum ClusterMesh rausflog), einer nüchternen Stabilitäts-Bewertung und dem, was als Nächstes fällig ist: Monitoring, Alerting, Backup und HA für die Föderations-Wurzel.

Die letzten Tage im Lab waren ein Strukturschnitt. Lange gab es genau einen ernsthaften Cluster — hydra, bare-metal — und daneben einen Wegwerf-Test, von dem die ClusterMesh-Episode erzählte. Inzwischen ist daraus ein wiederholbares Muster geworden: ein Cluster pro Befehl, Anwendungen im cluster-scope, Flux-Tenants, eine aufgeräumte Tailnet-Domain — und eine Föderation, die bewusst kein Pod-Mesh ist. Dieser Beitrag fasst die Änderungen zusammen, spricht ehrlich über die Probleme dahinter und bewertet, wie produktionstauglich das Ganze wirklich ist.

Cilium ClusterMesh durchs Tailnet — und warum ich es wieder rausgerissen habe

Zwei Talos-Cluster mit Cilium ClusterMesh über das Headscale-Tailnet verbinden — eine Sammlung der Fallstricke, der Erkenntnis, die die Datenebene zu retten schien, und des Outages, der mich am Ende zur Föderation ohne Node-Mesh zurückbrachte.

Im vorigen Beitrag habe ich das tailnet-gateway gebaut — die kleine Brücke, die API, Talos und DNS eines Clusters ins Headscale-Tailnet bringt. Das hier ist die Fortsetzung. Mein Lab hat inzwischen einen zweiten Cluster (cosmos bei Hetzner, neben dem bare-metal hydra), und beide sollten föderiert werden: Pods und Services clusterübergreifend, mit Cilium ClusterMesh .

Die offizielle Anleitung sagt: clustermesh-apiserver per LoadBalancer exponieren, dafür sorgen, dass sich die Knoten beider Cluster direkt sehen, fertig. Genau das wollte ich nicht. Ich habe schon ein verschlüsseltes WireGuard-Fabric über Headscale — die Föderation soll da durch, nicht über öffentliche IPs. Diese eine Entscheidung hat mich durch ein halbes Dutzend Fallstricke geführt. Hier sind sie.

Headscale aus dem Single Point of Failure holen

Headscale ist die Kontrollebene der ganzen Föderation — und heute bewusst eine Single-Replica auf SQLite. Der geplante Weg zu HA: das Image festnageln, SQLite gegen CloudNativePG tauschen, den öffentlichen Endpunkt failover-fähig machen und das gebündelte tailnet-gateway entflechten.
Stand: Design/Roadmap. Das hier ist der geplante Weg, kein Live-Setup. Heute läuft Headscale im Lab bewusst als Single-Replica; die einzelnen Schritte leben als Issues im Backlog (rad issue list: b404b7a , 5f36156 , 231c25c , 8b9e4e1 , fa3985f ). Ich schreibe das auf, bevor ich es baue — die Reihenfolge ist der eigentliche Inhalt.

Im tailnet-gateway-Beitrag habe ich Headscale zur Kontrollebene des Labs gemacht: ein selbst gehosteter Coordination-Server, über den jeder Cluster ins Tailnet kommt. Die ClusterMesh-Episode hat denselben Strang weitergesponnen. Was beide stillschweigend voraussetzen: Es gibt genau eine Headscale-Instanz, auf hydra, und wenn die wegfällt, hat die Föderation kein Gehirn mehr. Genau diesen Single Point of Failure will ich auflösen — und der Weg dahin ist überraschend gestuft.

Den Tailscale-Operator für Headscale nachbauen: Das tailnet-gateway

Warum der offizielle Tailscale-Kubernetes-Operator nicht zu meinem selbstgehosteten Headscale passt — und wie ich die Teile, die ich brauche, als ein einziges StatefulSet nachgebaut habe

Mein Lab hängt an einem selbstgehosteten Headscale — einer quelloffenen Reimplementierung der Tailscale-Control-Plane. Das funktioniert wunderbar für Menschen und für die Talos-Knoten. Sobald ich aber wollte, dass auch die Cluster-Dienste sauber im Tailnet auftauchen — die Kubernetes-API, die Talos-API, die clusterinterne Namensauflösung —, stieß ich auf eine Lücke: Der offizielle Tailscale-Kubernetes-Operator ist für die Tailscale-SaaS gebaut, nicht für Headscale. Also habe ich die Teile, die ich tatsächlich brauche, selbst nachgebaut. Das Ergebnis ist ein einziges, gut lesbares StatefulSet: das tailnet-gateway.