The server code learns to distrust strangers: a 64KB WebSocket payload cap (the ws default is 100MB — an easy OOM on a 1GB droplet), a per-connection token-bucket rate limit, caps on concurrent sockets, total rooms, rooms per connection, pending transfer codes, and seats per myGames query. Player names are stripped of control characters and bounded at 24 chars, room codes at 8, serialized commands at 16KB before they touch the append-only log. Catch-up replays — a full game rebuild per request — get a 3-second cooldown. Unexpected exceptions now log server-side and send strangers a bare "internal error" instead of the exception text. One real bug found by the sweep: myGames compared the client's raw seat token against the stored hash, so the lobby ledger silently matched nothing since tokens were hashed at rest — and the comparison wasn't timing-safe either. It now goes through the same timingSafeEqual path as every other seat check, via a new exported seatTokenValid. The droplet tightens too: the game server binds loopback (HOST env) so port 8787 no longer answers the internet — it was reachable directly, plaintext, bypassing Caddy — and ufw now allows only ssh, 80, and 443. The systemd unit gains a sandbox (ProtectSystem=strict, ProtectHome, NoNewPrivileges, PrivateTmp, MemoryMax=700M so a runaway process is killed and restarted before it takes the box down) and execs tsx directly instead of through npx. Caddy adds HSTS, nosniff, frame-denial, and no-referrer headers; setup-droplet.sh records all of it for future rebuilds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
39 lines
1.5 KiB
Bash
Executable File
39 lines
1.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# One-time droplet setup. Run ON the droplet as root:
|
|
# bash setup-droplet.sh wizwar.<droplet-ip>.sslip.io
|
|
set -euo pipefail
|
|
HOST="${1:?usage: setup-droplet.sh <hostname>}"
|
|
|
|
apt-get update -q
|
|
apt-get install -qy curl git rsync
|
|
|
|
# Node 22
|
|
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
|
apt-get install -qy nodejs
|
|
|
|
# Caddy (auto-HTTPS)
|
|
apt-get install -qy debian-keyring debian-archive-keyring apt-transport-https
|
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
|
|
| gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
|
|
| tee /etc/apt/sources.list.d/caddy-stable.list
|
|
apt-get update -q && apt-get install -qy caddy
|
|
|
|
# App user + directories
|
|
id -u wizwar &>/dev/null || useradd -r -m -d /opt/wizwar-home wizwar
|
|
mkdir -p /opt/wizwar /var/lib/wizwar/rooms
|
|
chown -R wizwar:wizwar /opt/wizwar /var/lib/wizwar
|
|
|
|
# Caddy vhost (security headers included; see deploy/Caddyfile for the template)
|
|
printf '%s\n\nheader {\n\tStrict-Transport-Security "max-age=31536000"\n\tX-Content-Type-Options "nosniff"\n\tX-Frame-Options "DENY"\n\tReferrer-Policy "no-referrer"\n}\nreverse_proxy localhost:8787\n' "$HOST" > /etc/caddy/Caddyfile
|
|
systemctl reload caddy
|
|
|
|
# Firewall: ssh + web only. The game server binds loopback and is reached
|
|
# through Caddy; nothing else should answer the internet.
|
|
ufw allow OpenSSH
|
|
ufw allow 80/tcp
|
|
ufw allow 443/tcp
|
|
ufw --force enable
|
|
|
|
echo "droplet ready — now run deploy.sh from your machine"
|