security: enforce real plugin secret, fix proxy auth bypass, loopback DB ports, nightly backups
- SHARED_SECRET now read from env and fail-closed: unset/placeholder refuses ALL plugin connections (constant-time compare). The old hardcoded 'your_shared_secret' in this public repo was no auth at all. Dockerfile default removed; generate_data.py reads the env var. - SECRET_KEY fails closed at startup (main.py and agent/auth.py) instead of falling back to a publicly-known signing key; agent systemd unit now requires /etc/overlord/agent.env (no '-' prefix). - AuthMiddleware + /ws/live: replace the 172.x source-IP trust (which every nginx-proxied internet request satisfied via docker-proxy — full session bypass and unauthenticated in-game command injection) with private-source AND no X-Forwarded-For, i.e. only genuinely internal callers (overlord-agent on the host, compose-network services). Invariant documented in nginx/overlord.conf: every tracker-bound location must set X-Forwarded-For. - /character-stats/test endpoints gated behind admin (they upsert real rows). - docker-compose: bind 5432/5433 to 127.0.0.1 (both DBs were internet- reachable; active brute-force observed in dereth-db logs). - discord-rare-monitor: drop dead SHARED_SECRET constant. - scripts/backup-databases.sh + docs/backups.md: nightly pg_dump of both DBs (telemetry/spawn hypertable data excluded), 10MB canary, umask 077, TimescaleDB restore procedure. - Remove stray mangled-path css file from repo root. Adversarially reviewed pre-deploy (3-lens workflow): ship verdict; deploy- sequencing blockers addressed (secret staged before enforcement, exec bit set, cron uses bash). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c6a1af0c39
commit
a28b61511c
12 changed files with 261 additions and 2579 deletions
53
scripts/backup-databases.sh
Executable file
53
scripts/backup-databases.sh
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env bash
|
||||
# Nightly logical backups for both MosswartOverlord databases.
|
||||
# Install as a cron job on the live host (see docs/backups.md). Note `bash`
|
||||
# in the cron line (survives a lost executable bit) and that /home/erik/backups
|
||||
# must exist BEFORE the first run (cron sets up the >> redirection before this
|
||||
# script's mkdir runs):
|
||||
# 15 3 * * * bash /home/erik/MosswartOverlord/scripts/backup-databases.sh >> /home/erik/backups/backup.log 2>&1
|
||||
#
|
||||
# What is backed up:
|
||||
# - dereth (TimescaleDB): full schema + all data EXCEPT the raw
|
||||
# telemetry_events/spawn_events hypertable chunks. Those tables hold
|
||||
# ~12 GB of data that expires via retention policies in 7-30 days
|
||||
# anyway; the irreplaceable rows (users, char_stats, rare_stats,
|
||||
# rare_events, combat_stats*, portals, character_stats, server_status)
|
||||
# are all included.
|
||||
# - inventory_db (postgres): full dump (~1 GB raw, much smaller compressed).
|
||||
#
|
||||
# Restore procedure: docs/backups.md (TimescaleDB needs pre/post restore calls).
|
||||
set -euo pipefail
|
||||
# Dumps contain the users table (bcrypt hashes) — keep them owner-only.
|
||||
umask 077
|
||||
|
||||
BACKUP_DIR="${BACKUP_DIR:-/home/erik/backups/postgres}"
|
||||
KEEP_DAYS="${KEEP_DAYS:-7}"
|
||||
STAMP="$(date -u +%Y%m%d-%H%M)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# dereth: -Fc is compressed; exclude hypertable chunk DATA (schema kept so a
|
||||
# restore recreates the tables empty and retention/compression jobs reattach).
|
||||
docker exec dereth-db pg_dump -U postgres -Fc \
|
||||
--exclude-table-data='public.telemetry_events' \
|
||||
--exclude-table-data='public.spawn_events' \
|
||||
--exclude-table-data='_timescaledb_internal._hyper_*' \
|
||||
dereth > "$BACKUP_DIR/dereth-$STAMP.dump.tmp"
|
||||
# Canary: a healthy dereth dump is ~50 MB; a tiny one means pg_dump silently
|
||||
# produced garbage (fail the run so the old dumps are kept and cron logs it).
|
||||
if [ "$(stat -c%s "$BACKUP_DIR/dereth-$STAMP.dump.tmp")" -lt 10000000 ]; then
|
||||
echo "$(date -u +%FT%TZ) FAIL dereth dump under 10MB — keeping old backups" >&2
|
||||
exit 1
|
||||
fi
|
||||
mv "$BACKUP_DIR/dereth-$STAMP.dump.tmp" "$BACKUP_DIR/dereth-$STAMP.dump"
|
||||
|
||||
docker exec inventory-db pg_dump -U inventory_user -Fc inventory_db \
|
||||
> "$BACKUP_DIR/inventory-$STAMP.dump.tmp"
|
||||
mv "$BACKUP_DIR/inventory-$STAMP.dump.tmp" "$BACKUP_DIR/inventory-$STAMP.dump"
|
||||
|
||||
# Retention: keep KEEP_DAYS days of dailies.
|
||||
find "$BACKUP_DIR" -name 'dereth-*.dump' -mtime +"$KEEP_DAYS" -delete
|
||||
find "$BACKUP_DIR" -name 'inventory-*.dump' -mtime +"$KEEP_DAYS" -delete
|
||||
# Clean up aborted runs older than a day.
|
||||
find "$BACKUP_DIR" -name '*.dump.tmp' -mtime +1 -delete
|
||||
|
||||
echo "$(date -u +%FT%TZ) OK dereth=$(du -h "$BACKUP_DIR/dereth-$STAMP.dump" | cut -f1) inventory=$(du -h "$BACKUP_DIR/inventory-$STAMP.dump" | cut -f1)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue