Ein Laborserver, drei Vertrauensrollen, eine Fehlerdomäne
Ein VPN-Einstieg, private Dienste und CI-Jobs brauchen selbst auf einem einzigen Server getrennte Rechte. Teile die Rollen, isoliere die Netze, sichere den Konsolenzugang.
Alles funktioniert, bis der Build-Runner denselben Host-Socket braucht, den der VPN-Gast nutzt, und dann kann niemand erklären, warum ein Job die Schlüssel des Gateways lesen kann. Alles wurde auf einer Maschine installiert, weil es passte, und jeder Gast bekam weitreichenden Zugriff, weil das der schnellste Weg war, es zum Laufen zu bringen. Das Ergebnis ist ein Host, dessen Vertrauensgrenze das Stromkabel ist. Die Rollen zu trennen ist weniger Arbeit, als es klingt, und sie beginnt damit, sie zu benennen.
Drei Rollen, die derzeit alles teilen
Ein VPN-Einstieg, eine Reihe privater Dienste und ein Build-Runner wollen unterschiedliche Dinge. Der erste braucht einen öffentlichen Port und ein schmales Netz auf der privaten Seite. Der zweite braucht dauerhaften Speicher und Anwendungsgeheimnisse. Der dritte führt Code aus, den jemand anderes geschrieben hat.
Allen dreien denselben Zugriff zu geben, macht das Symptom verwirrend, weil danach jeder die anderen beeinflussen kann. Ein Build-Job, der den Hypervisor-Socket erreicht, ist ein Job, der das VPN anhalten kann. Ein Dienste-Gast mit einer Route ins Verwaltungsnetz kann die Switch-Konfiguration ändern. Die Rechte sind das Problem, und die Prozessliste ist nur der Ort, an dem es sichtbar wird. Die Rollen zuerst zu benennen gibt jedem Recht einen Platz.
Die Skizze unten ist ein Ausgangspunkt für diese Aufteilung und kein Versprechen von Hochverfügbarkeit. Behandle sie als Hypothese, die zu prüfen ist, nicht als fertiges Design.
Drei Rollen trennen, nicht drei Prozesse
Drei Gäste auf einem Host ergeben drei Rechtemengen und drei Netzpositionen. Sie ergeben keine drei Fehlerdomänen, und das Design sollte das laut sagen.
Die Trennung hilft nur, wenn eine Kompromittierung in einem Gast den anderen nicht automatisch Rechte gibt, und das ist eine Hypervisor-Einstellung und keine Namenskonvention. Eine VM Gateway zu nennen hindert keinen anderen Gast daran, den Host zu erreichen. Lies die Konfiguration des virtuellen Switches statt des Hostnamens und bestätige dann, dass jeder Gast dort sitzt, wo du ihn vermutest.
host
├─ vpn-gw 10.60.10.2/24 public 51820/udp only
├─ services 10.60.0.10/24 no route to the management network
└─ ci-runner 10.60.20.2/24 outbound only, no host socket
Schreib die Topologie auf und prüfe sie dann mit den Werkzeugen, denen sie gehört. Ein Gast, der auf der falschen Bridge auftaucht, ist ein Befund und kein Detail. Korrigiere das Netz, bevor du irgendeine Dienstkonfiguration änderst.
Lass den VPN-Gast nur die Türen öffnen, die ihm gehören
Veröffentliche den Tunnelport am Router und sonst nichts. Gastverwaltungsports gehören auf einen Verwaltungspfad und nicht ins Internet, und die Schnittstelle des Hypervisors gehört ebenfalls dorthin.
Das Netz auf der privaten Seite sollte die Dienste erreichen, die entfernte Nutzer brauchen, und dort aufhören. Die Anwendungsauthentifizierung bleibt eingeschaltet, denn ein Tunnel ist Transport und kein Login. Halte die Routing-Tabelle des VPN-Gasts klein genug, um sie in einem Satz zu beschreiben. Wenn du sie nicht so einfach beschreiben kannst, ist die Grenze schon verschwommen.
virsh net-list --all
virsh net-dumpxml lab-private
sudo nft list ruleset | grep -n -E '51820|10.60'
Alles, was auf dem Host lauscht und nicht für die Verwaltung gebraucht wird, sollte überhaupt nicht lauschen. Das schließt die Hypervisor-API ein, die man leicht auf einer öffentlichen Schnittstelle offen lässt.
Build-Jobs brauchen eine andere Art von Grenze
Ein CI-Job führt Befehle aus, die das Repository kontrolliert, also hängt sein Vertrauen davon ab, wer Code pushen darf. Ein Shell-Executor führt diese Befehle als Benutzer des Runners auf dem Host aus, und das ist die weiteste verfügbare Grenze.
Container-Executoren sind enger, und ein privilegierter Container gehört nicht zu den engen. Halte den Runner in einem eigenen Gast mit eigenem Netzbereich und gib ihm nur den Artefakt- und Deployment-Zugriff, den die Jobs brauchen. Wegwerf-Worker verringern den gehaltenen Zustand; sie verringern weder Erreichbarkeit noch Geheimnisoffenlegung. Beides braucht weiterhin ausdrückliche Grenzen.
Der Dienste-Gast besitzt die Daten, also ist sein Backup-Plan der, der funktionieren muss. Halte Repositories, Uploads und die Datenbank im selben Zeitplan. Teste eine Wiederherstellung, statt den Job-Status zu lesen.
Für den Ausfall planen, den alle drei teilen
Ein Host ist eine Fehlerdomäne. Ein Neustart entfernt den Tunnel und jeden privaten Dienst im selben Moment, und genau dann zählt ein Weg hinein am meisten.
Halte Konsolenzugang, der nicht vom VPN-Gast abhängt, sei es eine physische Konsole, eine Out-of-Band-Verwaltungsschnittstelle oder eine Anbieterkonsole. Ein Snapshot auf derselben Platte ist keine unabhängige Kopie, und ein Backup, das im geschützten Gast selbst läuft, auch nicht. Dann teste den Kaltstart als Abfolge und nicht als Checkliste: Host, virtuelle Netze, Tunnelpfad und eine benannte Anwendung unter einem gewöhnlichen Konto.
virsh list --all
systemctl is-active libvirtd
curl -sS -m 5 -o /dev/null -w '%{http_code}\n' https://10.60.0.10/
ss -ltnp | grep -E ':51820|:443'
Die Reihenfolge der Prozessstarts beweist nichts über Routen und DNS, also teste den Pfad und nicht die Unit-Zustände. Der Pfad ist das, was ein entfernter Nutzer tatsächlich braucht.