Node-Hardening
Für nicht vertrauenswürdige Tenants (das Hosting-Profil). Auch ohne die folgenden Schritte laufen Gameserver-Container bereits:
- ohne alle Capabilities und mit
no-new-privileges - mit dem Seccomp-Profil von Docker
- mit Limits für Arbeitsspeicher (ohne Swap), CPU und Prozesse
- mit einem Netzwerk pro Tenant: Tenants erreichen sich gegenseitig nicht


Installations- und Update-Jobs laufen als root (Installer schreiben jede Datei des Servers), behalten aber nur die Capabilities dafür: Besitzer und Rechte von Dateien ändern und den Benutzer wechseln. Keine Raw-Sockets, keine Gerätedateien, kein chroot.
Die Seite des Nodes zeigt unter Isolation, was sein Docker bietet; der Agent meldet es bei jeder Verbindung. Der Agent selbst braucht nie mehr als den Docker-Socket: Alles Folgende richtet der Operator einmal ein. Änderungen gelten für jeden Server ab seinem nächsten Start.
1. User Namespaces
Abschnitt betitelt „1. User Namespaces“Root in einem Container ist dann nicht Root auf dem Node. In /etc/docker/daemon.json:
{ "userns-remap": "default" }Danach systemctl restart docker und den Agent neu erstellen. Der Agent muss Dateien als Root des Nodes verwalten und läuft deshalb mit userns_mode: host (deploy/compose.agent.yml und der Befehl unter Add node setzen das bereits).
Was sich ändert:
- Docker hält Images, Container und Volumes pro Remapping getrennt (
/var/lib/docker/100000.100000): Images werden neu heruntergeladen, und Steam-Anmeldungen müssen neu gemacht werden. - Server-Dateien gehören auf dem Node dem verschobenen Benutzer (988 → 100988). Der Agent erkennt das beim Start und übergibt bestehende Server einmal dem neuen Besitzer; er merkt sich den Besitzer in
/var/lib/panel-agent/file-owner.
2. Schreibgeschützte Container
Abschnitt betitelt „2. Schreibgeschützte Container“Ein Schalter auf der Seite des Nodes; bei neuen Nodes mit dem Hosting-Profil eingeschaltet.
Gameserver-Container bekommen ein schreibgeschütztes Root-Dateisystem. Ihre Dateien, /tmp (256 MB) und /var/tmp bleiben beschreibbar. Images, die anderswo schreiben, starten so nicht: Schalte es für diesen Node aus, oder korrigiere das Image.
3. gVisor
Abschnitt betitelt „3. gVisor“Optional, ein Schalter auf der Seite des Nodes. Gameserver laufen dann auf dem eigenen Kernel von gVisor: die stärkste Isolation, mit etwas Mehraufwand bei CPU und Netzwerk. Installiere es auf dem Node (Debian, Ubuntu):
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpgecho "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.listsudo apt-get update && sudo apt-get install -y runscsudo runsc install # trägt die Runtime "runsc" in /etc/docker/daemon.json einsudo systemctl restart dockerStarte den Agent neu, damit er die neue Runtime meldet; solange der Node keine hat, wird der Schalter abgelehnt.
gVisor kann in den Netzwerken der Tenants den Nameserver von Docker nicht nutzen; der Agent gibt gVisor-Containern deshalb eigene: 1.1.1.1 und 9.9.9.9, oder was AGENT_SANDBOX_DNS festlegt (z. B. deinen Router).
4. Firewall
Abschnitt betitelt „4. Firewall“Gameserver-Container sollen weder die Services des Nodes selbst erreichen (SSH, Datenbanken, andere veröffentlichte Ports) noch die Netzwerke um ihn herum (LAN, VPN, den Metadaten-Service der Cloud).
Tenant-Netzwerke verwenden Bridge-Interfaces mit Namen pnl…. deploy/node-isolation.nft sperrt diesen Verkehr und lässt Spieler und Antworten durch:
sudo install -m 644 deploy/node-isolation.nft /etc/nftables.d/panel-isolation.nftsudo nft -f /etc/nftables.d/panel-isolation.nft # und für Neustarts in /etc/nftables.conf einbinden- Steht der DNS-Resolver des Nodes im LAN (z. B. der Router), erlaube ihn zuerst in der Datei (die auskommentierte Zeile), sonst können Gameserver keine Namen auflösen.
- Vorher erstellte Netzwerke behalten die Docker-Namen
br-…, bis kein Server sie mehr nutzt; entferne sie dann mitdocker network rm.