-
Notifications
You must be signed in to change notification settings - Fork 0
Sicherheit und Hardening
← Home
Was dieses Repo an Sicherheitsmechanik mitbringt, welche Defaults schwach sind und wo die Härtung zusätzlich zur Repo-Stand notwendig ist. Vier Bereiche: Secrets-Handhabung, Netz/Ports, RCON und Audit-Checkliste.
Vertieft in Repo: docs/05-wartung.md, docs/07-troubleshooting.md;
Detailseiten ENV-Variablen, Ports-und-Netzwerk,
Wartung-und-Automatik, Konventionen-und-Quellen,
Cheatsheet-Tagesbetrieb.
Die folgenden vier Werte stehen in .env.example (1:1 übernommen,
Repo-Default) und sind die einzigen Geheimnisse, die das Repo aktiv
verwaltet:
| Variable | Wert in .env.example
|
Wirkung |
|---|---|---|
ADMIN_PASSWORD |
CHANGE_ME |
In-Game-Admin-Login; fließt in servertest.ini → AdminPassword= (Repo-Default) |
SERVER_PASSWORD |
CHANGE_ME |
Join-Passwort für Spieler; fließt in servertest.ini → Password= (Repo-Default) |
RCON_PASSWORD |
CHANGE_ME |
Passwort für RCON (mcrcon -p); genutzt von scripts/announce.sh (Repo-Default) |
DISCORD_WEBHOOK_URL |
leer | Webhook-URL für notify() aus scripts/lib/common.sh (Repo-Default) |
Alle vier Default-Werte sind nicht-sicher: CHANGE_ME ist explizit ein
Platzhalter, das leere Webhook schaltet Discord-Alerts still ab
(notify() macht dann sofort return, ohne zu senden, 1:1 aus
scripts/lib/common.sh).
-
.envniemals committen. Repo-Regel, dokumentiert in der.env.example-Vorlage selbst (Zeile 3, 1:1: „Werte lokal ausfuellen, NIE committen"). Die.gitignoredeckt.env,.env.localunddocker/.envexplizit ab (1:1). - Dateirechte
chmod 600 .envsetzen (Heuristik; Standard für Secret-Dateien). Skripte lesen die Datei als der ausführende Benutzer; lesbar nur für Eigentümer ist Standard. - Nach jeder Änderung an
.enveinmalmake renderlaufen lassen — die Templates werden perenvsubstneu geschrieben, Werte landen inservertest.iniundservertest_SandboxVars.lua(Repo-Default). -
.envmuss beim Skript-Aufruf im Repo-Root liegen — Skripte gehen vom aktuellen Verzeichnis aus (Repo-Default auslib/common.sh).
Detailübersicht zu allen Variablen: ENV-Variablen.
Im CI läuft ein einfacher grep über Shell-, YAML- und Template-Dateien
(1:1 aus .github/workflows/ci.yml):
! grep -rInE "(PASSWORD|TOKEN|SECRET)=[^C][^H][^A][^N][^G][^E]" \
--include="*.sh" --include="*.yml" --include="*.tmpl" . \
|| (echo "Moegliches Secret im Repo!" && exit 1)Was er fängt (Beispiele):
| Beispiel | Erkannt? |
|---|---|
FOO=bar |
Nein (kein Schlüsselwort) |
PASSWORD=actualSecretValue |
Ja (matcht das negative Lookahead [^C][^H]… nicht) |
PASSWORD=CHANGE_ME |
Nein (das ist genau der Default, der erlaubt sein soll) |
DISCORD_WEBHOOK_URL= (leer) |
Nein |
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... |
Ja (matcht SECRET ist zwar nicht im Schlüssel, aber PASSWORD/TOKEN/SECRET als Variablenname würde matchen — eine Webhook-URL selbst enthält keinen dieser Namen) |
Was er NICHT fängt (Heuristik, aus der Mechanik abgeleitet):
- Secret-Werte in
.env— die Datei liegt per.gitignorenie im Repo und dergrepläuft nur über committed files. - Base64-kodierte oder anderweitig verschleierte Werte.
- Geheime Werte in Kommentaren oder Commit-Messages.
- Hex/UUID-Werte ohne
PASSWORD/TOKEN/SECRETim Variablennamen. - Secrets in
.example-Dateien (z. B.RCON_PASSWORD=CHANGE_ME) — die sind per Design erlaubt.
Der Scan ist ein grobes Sieb, kein Audit-Ersatz (Heuristik).
Konsistent zu Ports-und-Netzwerk (dort ausführlich mit Quellen-
Konflikt). Reihenfolge 1:1 aus pdf/content.json Kapitel „Linux-Grundsetup"
(Community-Quelle: PDF-Handbuch; in der Repo-Doku docs/02 ohne den
TCP-Zusatz).
| Regel | Zweck | Quelle |
|---|---|---|
sudo ufw default deny incoming |
Eingehend grundsätzlich blocken | Community-Quelle (PDF-Handbuch) |
sudo ufw default allow outgoing |
Ausgehend erlauben | Community-Quelle (PDF-Handbuch) |
sudo ufw allow OpenSSH |
SSH für Administration | Community-Quelle (PDF-Handbuch) |
sudo ufw allow 16261/tcp |
Hauptport TCP (Quellen-Konflikt, Heuristik: beides freigeben) | Heuristik (PDF-Handbuch) |
sudo ufw allow 16261/udp |
Hauptport UDP (pzwiki) | Community-Quelle (pzwiki) |
sudo ufw allow 16262/udp |
Datenverkehrs-Port (UDP) | Community-Quelle (pzwiki) |
sudo ufw enable |
Firewall scharf schalten | Community-Quelle (PDF-Handbuch) |
sudo ufw status verbose |
Kontrolle nach enable
|
Community-Quelle (PDF-Handbuch) |
RCON-Port (27015/TCP per Repo-Default) bleibt geschlossen für
eingehende Verbindungen aus dem Internet — siehe nächster Abschnitt.
Drei harte Repo-Fakten (1:1 aus scripts/announce.sh):
-
Host ist hardkodiert
127.0.0.1: Eine VariableRCON_HOSTgibt es im Repo nicht (Repo-Befund). Das Skript verbindet sich ausschließlich loopback. -
RCON_ENABLEDals Guard: Beifalseoder leer machtannounce.shstill exit 0, ohne irgendetwas zu senden (1:1 aus dem Skript). -
mcrcon -pPasswort kommt aus.env(RCON_PASSWORD, Repo-DefaultCHANGE_ME).
Empfehlungen (Heuristik, abgeleitet aus dem Repo-Stand):
| Maßnahme | Warum |
|---|---|
RCON-Port (27015/TCP per Repo-Default) nicht öffentlich freigeben |
Nur Loopback ist im Repo vorgesehen |
| Kein Port-Forwarding auf RCON im Router | Versehentlich geöffnet = volle Server-Konsole für jeden im Internet |
| SSH-Tunnel statt Remote-RCON | Wer RCON remote braucht, tunneliert via SSH |
RCON_ENABLED=false wenn keine In-Game-Ansagen nötig |
Reduziert die Angriffsfläche auf das lokale Loopback |
RCON_PASSWORD ändern (nicht CHANGE_ME) |
Standard-Härtung |
Hinweis (allgemeine Best Practice, nicht PZ-/Repo-dokumentiert): Die folgenden Empfehlungen stammen aus generischer Linux-Systemadministration und werden hier nur kurz erwähnt. Das Repo selbst äußert sich nicht zu SSH-Konfiguration.
| Maßnahme | Zweck |
|---|---|
| SSH-Login nur mit Schlüsseln | Passwort-Login brute-force-resistent |
PasswordAuthentication no in sshd_config
|
Erzwingt Schlüssel-Login |
PermitRootLogin no (oder prohibit-password) |
SSH-Login als root ausschließen |
| Fail2ban o. ä. | Reduziert Login-Spam |
Konfiguration siehe Distribution-Doku und man sshd_config.
Der Server-Bezug läuft komplett ohne Steam-Account (Community-Quelle:
pzwiki, Repo-Konsens). Repo-Beleg (1:1 aus scripts/install.sh /
Repo-Doku): die Installation verwendet +login anonymous. Damit:
| Vorteil | Anmerkung |
|---|---|
| Kein Steam-Account auf dem Server-Host nötig | Reduziert Secret-Anforderungen |
| Funktioniert trotzdem für App 380870 und Workshop-Download | Community-Quelle (Workshop-Download mit +workshop_download_item 108600 <ID>) |
Wer Workshop-Mods manuell ziehen will, funktioniert das ebenfalls mit
+login anonymous (Community-Quelle: SteamCMD-Doku).
- Webhook-URLs haben die Form
https://discord.com/api/webhooks/<id>/<token>— das<token>ist der eigentliche Secret-Teil (Community-Quelle: Discord-Doku). -
DISCORD_WEBHOOK_URLsteht in.env, niemals ins Repo oder in Skript-Logs schreiben. Dasnotify()ausscripts/lib/common.shsendet percurlan genau diese URL. - Wenn das Token einmal geleakt wurde: im Discord-Server den alten Webhook
löschen und einen neuen anlegen, dann
.envaktualisieren (Heuristik, Standard-Verfahren).
Quelle: pdf/content.json, Audit-Tabelle C — Security (1:1 übernommene
Prüfpunkte). Spalte „Befehl zum Prüfen" aus dem Repo-Pfad/Standard-Linux
abgeleitet.
| # | Prüfpunkt | Befehl zum Prüfen | Erwartetes Ergebnis |
|---|---|---|---|
| C1 | Läuft als Nicht-Root | ps -o user= -p $(pgrep -fi zomboid) |
Prozess gehört pzserver, niemals root (Repo-Default aus .env.example: PZ_USER=pzserver) |
| C2 | UFW aktiv und minimal | sudo ufw status verbose |
Aktiv; erlaubt nur 16261/tcp+udp, 16262/udp und OpenSSH (Community-Quelle: PDF-Handbuch) |
| C3 | SSH gehärtet | ssh -o PreferredAuthentications=publickey user@host |
Login mit Key möglich; Passwort-Login deaktiviert (allgemeine Best Practice, nicht PZ-dokumentiert) |
| C4 | System-Updates stehen | apt list --upgradable |
Keine relevanten offenen Sicherheitsupdates |
| C5 | Backups vorhanden + getestet | Restore-Protokoll der letzten Sicherung | Backup existiert und wurde mindestens einmal testweise zurückgesichert (Heuristik) |
Die Original-Reihenfolge und der genaue Wortlaut stehen in
pdf/content.json unter „Audit C — Security" (1:1 übernommen).
-
Webhook-URL in Skript-Logs: Wenn
notify()percurl -voder in einem Debug-Wrapper aufgerufen wird, kann die URL im Log landen. Skripte im Repo rufennotify()ohne-vauf (1:1 auslib/common.sh), trotzdem: Logs nicht öffentlich posten (Heuristik). -
.envin Docker-Mounts kopiert: Wenndocker/.envoder die Repo-.envin ein Container-Mount wandert, das später committed wird, ist das Secret geleakt. Repo-Default:docker/.envliegt in.gitignore(siehedocker/.env.docker.exampleals Vorlage). -
RCON-Port versehentlich geöffnet: Wenn beim UFW-Setup
allow 27015stattfrom <eigene-ip>läuft, ist RCON weltweit offen. Symptom: injournalctl -u zomboidtauchen fremdemcrcon-Verbindungsversuche auf (Heuristik). -
PASSWORD=CHANGE_MEbelassen: Fünf Variablen tragen denCHANGE_ME-Platzhalter —ADMIN_PASSWORD,SERVER_PASSWORDundRCON_PASSWORDfür den laufenden Betrieb sowieGCP_PROJECTundGCP_BUCKET(nur für den Failover-Betrieb relevant; die drei ersteren sind die vom Repo aktiv verwalteten Geheimnisse, siehe oben). Wer den Platzhalter produktiv lässt, hat faktisch kein Passwort. CI-Scan grep't die Platzhalter explizit nicht (siehe oben). -
make rendervergessen: Änderungen in.envlanden erst nachmake renderin den ini-Dateien. Wer den Schritt überspringt, wundert sich, warumADMIN_PASSWORDweiter leer ist (Heuristik). -
Backups nie getestet: Ein Backup, das nie zurückgespielt wurde, ist
kein Backup.
audit-checklisteC5 verlangt explizit einen Restore-Test (1:1 auspdf/content.json). -
Repo-Verhalten
update.shsetzt KEIN.planned-stop: Während des Update-Fensters kann der minütliche Cron-Watchdog den Server mitten im Stop wieder hochziehen. Heuristik-Empfehlung: vor manuell angestoßenen Updatestouch .planned-stopsetzen (siehe Monitoring-und-Alerts).
Konkrete Schrittfolge nach git clone (Heuristik, abgeleitet aus Repo-
Defaults und Standard-Praxis):
cp .env.example .env
chmod 600 .env
$EDITOR .env # mindestens die vier CHANGE_ME-Platzhalter setzen
make render # envsubst -> servertest.ini + Sandbox-Lua
make validate # sanity-check (shellcheck wenn vorhanden)Danach nie wieder .env committen; .gitignore deckt die Datei bereits
ab (1:1 im Repo).
Das Repo-Setup erzeugt an mehreren Stellen Logs, in denen theoretisch Secrets landen könnten:
| Log | Quelle | Risiko (Heuristik) |
|---|---|---|
${REPO_ROOT}/logs/server.log |
Skript-Logging aus lib/common.sh
|
Skripte loggen Passwörter nicht aktiv; Risiko gering |
sudo journalctl -u zomboid |
Java-Prozess-Logs | Bei Fehlern können Stacktraces Konfig-Werte ausgeben |
| Discord-Notification |
notify() via curl
|
Wenn Webhook-URL als Argument übergeben wird, kann sie im Process-Listing erscheinen |
| Backup-Filenames |
backup.sh enthält Label + Timestamp |
Keine Secrets; aber Backup-Pfade verraten Verzeichnisstruktur |
Standard-Empfehlung (allgemeine Best Practice, nicht PZ-dokumentiert):
Log-Dateien mit chmod 640 oder restriktiver, kein world-readable. Bei
zentraler Log-Sammlung (Graylog, Loki etc.) Filter für bekannte
Secret-Muster konfigurieren.
scripts/announce.sh baut die RCON-Verbindung mit mcrcon -p auf und
schluckt Fehler per || true (1:1 aus dem Skript). Drei Punkte:
- Wenn
mcrconaus einer alten Version stammt, kann das Passwort im Klartext über die Leitung gehen — bei127.0.0.1und SSH-Tunnel kein Problem, aber nie ohne Tunnel nach außen (Heuristik). -
mcrconcached das Passwort nicht; jeder Aufruf liest es neu aus der.env. NachRCON_PASSWORD-Rotation: kein Cache-Leeren nötig (Heuristik, abgeleitet aus dem Repo-Aufrufmuster). - Die
mcrcon-Binary sollte aus derselben Distribution wie der Server kommen (Heuristik). Versions-Drift zwischen Client- und Server-Binary ist in der Praxis unkritisch, aber dokumentiert.
Das Repo-Backup ist ein einfaches tar.gz ohne Verschlüsselung (1:1 aus
scripts/backup.sh):
tar -czf "$DEST" -C "$PZ_DATA_DIR" Saves ServerWer das Backup auf einen Cloud-Bucket spiegelt (make failover-sync →
gcp/sync-backups.sh), transportiert damit Klartext-Saves in einen
externen Bucket. Heuristik-Empfehlungen:
| Maßnahme | Zweck |
|---|---|
Bucket-ACL auf pzserver-Service-Account beschränken |
Nur eigene Maschinen sehen die Backups |
| Object-Lock / Retention-Policy im Bucket | Versehentliches Löschen verhindern |
| Separater Backup-Bucket für Tests | Test-Restores nicht im produktiven Bucket |
Backup vor gsutil rsync lokal verschlüsseln (z. B. gpg) |
Bei kompromittiertem Bucket bleibt Save verschlüsselt |
Diese Maßnahmen sind nicht im Repo implementiert; sie sind generische Cloud-Backup-Praxis (Heuristik).
systemd/zomboid.service läuft mit User=__PZ_USER__ (1:1 aus dem
Service-File). Das Templating füllt __PZ_USER__ aus .env (PZ_USER,
Repo-Default pzserver). Folgerungen:
- Der Java-Prozess hat keine Root-Rechte (Repo-Default).
- Installation und Updates laufen weiterhin als root via
sudo make install/sudo make update(Repo-Default, Makefile 1:1: allesystemctl-Targets sindsudo-Aufrufe). - Watchdog (
monitor.sh) und Cron laufen typischerweise als root oder als Service-User mitsudo-fähigkeit — siehesystemd/README.md.
Wer den Server versehentlich als root startet, fällt durch Audit C1 (siehe oben) sofort auf.
Kurzfassung zum Abhaken bei Erstinstallation (Heuristik, abgeleitet aus Repo-Defaults und Audit C):
| # | Punkt | Status |
|---|---|---|
| 1 |
.env aus .env.example kopiert, Rechte 600, alle CHANGE_ME-Platzhalter gesetzt |
[ ] |
| 2 |
DISCORD_WEBHOOK_URL gesetzt oder bewusst leer gelassen |
[ ] |
| 3 | UFW aktiv, Regeln wie oben, RCON-Port nicht öffentlich | [ ] |
| 4 | SSH-Passwort-Login deaktiviert (allgemeine Best Practice) | [ ] |
| 5 | Server läuft als pzserver, nicht als root
|
[ ] |
| 6 | Ein Test-Restore wurde durchgeführt und im Protokoll dokumentiert | [ ] |
| 7 |
.gitignore deckt .env und docker/.env ab (Repo-Default) |
[ ] |
| 8 | Logs laufen nicht world-readable (Heuristik) | [ ] |
| 9 | CI-Secret-Scan ist im grünen Bereich nach git push (1:1 aus .github/workflows/ci.yml) |
[ ] |
Diese Liste ist Selbstzweck — die formale Audit-Tabelle mit Server- Antworten steht oben (Audit C, 5 Prüfpunkte).
Heuristik (dieses Wikis): Das Audit nach jedem größeren Eingriff wiederholen — neuem Mod, Heap-Änderung, Storage-Umzug oder Branch-Update. Ein einmal bestandenes Audit veraltet; die Backup-Prüfung (Audit C5) gehört in jeden Auditlauf.
Konsistent zum Original aus pdf/content.json (Audit-C-Abschluss).
Weiter: ENV-Variablen · Ports-und-Netzwerk · Wartung-und-Automatik · Konventionen-und-Quellen · Cheatsheet-Tagesbetrieb
Labels: Community-Quelle = extern belegt (Herkunft in Klammern, z. B. pzwiki, Steam-Diskussionen, GCP-Preisliste) · Repo-Default = steht genau so im Repo (Template, Skript oder .env.example) · Heuristik = begründete Empfehlung dieses Projekts, nicht extern verifiziert.
The Indie Stone (TIS) publiziert keine offiziellen Storage-/IOPS-/Performance-Specs — Zahlen in diesem Wiki nie als TIS-Anforderung lesen.
Inhalte zielen auf den B42-Stable-Branch: seit B42.20 der Default-Zweig in Steam, kein -beta-Opt-in nötig (Community-Quelle).
Details zu Labels, Quellen und Redaktionsregeln: Konventionen-und-Quellen.
Einstieg
Referenz
- Makefile-Referenz
- Cheatsheet-Tagesbetrieb
- ENV-Variablen
- Server-Konfiguration
- Admin-Befehle
- Mods-Referenz
- Spawn-System
- Skripte-Referenz
- systemd-Referenz
- Ports-und-Netzwerk
- SteamCMD-Referenz
Betrieb
- Wartung-und-Automatik
- Backup-und-Restore
- Updates-und-Rollback
- Monitoring-und-Alerts
- Sicherheit-und-Hardening
- Docker-Betrieb
- GCP-Failover
Tiefenwissen
- Modding-Workflow
- Performance-Guide
- Storage-und-Map-Streaming
- Hardware-Empfehlungen
- Troubleshooting
- Runbook-Raven-Creek
Meta