Absicherung: Socket-Eingaben, Geheimnisse in Fehlern, Logparser, Start/Stopp, RCON, Stream-Zugang

- Socket-Nachrichten werden geprüft, Handlerfehler abgefangen, Abfragen pro Verbindung begrenzt
  (vorher beendete stream:subscribe mit null ohne Anmeldung das Backend).
- Fehlermeldungen von Prozessen ohne Befehlszeile und mit geschwärzten Passwörtern/Tokens.
- Logparser wertet nur ganze Systemzeilen nach dem Zeitstempel aus; Chat kann keine Map beenden.
- Verspätet ankommende Starts nach Stopp, Neuanforderung oder Löschen bauen ihren Container ab.
- RCON: kaputte Pakete trennen nur die Verbindung, Befehle ohne Antwort gelten als Fehler.
- Organisatoren bekommen einen Stream-Schlüssel nur für ihr Match statt des globalen STREAM_KEY.
- Rate-Limiter mit regelmäßiger Bereinigung und Obergrenze, alte Admin-Sitzungen werden entfernt.
This commit is contained in:
mregosar committed 2026-11-01 20:25:45 +01:00
1 parent 35bdfc1f67
commit e18b8b4430
13 files changed
+409 -49

No files matched your search

+9 -3
View File
@@ -3,7 +3,7 @@
// leer wird, setzt er sich von selbst zurück. Fällt ein Server aus, startet ihn die App neu.
import { HttpError } from '../matchService.js';
import { randomPassword } from '../security.js';
import { randomPassword, safeErrorMessage } from '../security.js';
const SLOW_POLL_MS = 10_000;
// Nach so vielen Ausfällen in Folge gibt die App auf, damit ein kaputter Host nicht endlos neu startet
@@ -208,6 +208,11 @@ export class KnifeServers {
secrets: { rconPassword: row.rcon_password, serverPassword: '', tvPassword: randomPassword(12) },
serverName: `${prefix ? `${prefix} | ` : ''}${row.name} ${size ? `${size}v${size} ` : ''}(Seitenwahl)`,
});
// Während des Starts entfernt: Container gleich wieder abbauen
if (!this.row(id)) {
await this.adapter.stop(result.handle).catch(() => {});
return;
}
this.update(id, {
status: 'starting',
message: 'Container läuft, Spielserver lädt',
@@ -219,8 +224,9 @@ export class KnifeServers {
started_at: Date.now(),
});
} catch (err) {
this.update(id, { status: 'failed', message: `Start fehlgeschlagen: ${err.message}` });
throw new HttpError(502, `Messer-Server konnte nicht gestartet werden: ${err.message}`, 'start_failed');
const message = safeErrorMessage(err, [row.rcon_password]);
if (this.row(id)) this.update(id, { status: 'failed', message: `Start fehlgeschlagen: ${message}` });
throw new HttpError(502, `Messer-Server konnte nicht gestartet werden: ${message}`, 'start_failed');
} finally {
this.busy.delete(id);
}