Stoat (Revolt fork) — Lockdown & Org Setup (Sections 1–5)
Context: closed invite-only org, Linux VM on FreeBSD host. Current edge is Caddy on the VM
(confirmed by via: 1.1 Caddy and Caddy listening on 0.0.0.0:80/443).
Minimize attack surface, enforce HTTPS, ensure WebSockets work, and prepare Stoat for a structured Star Citizen org.
1) Decide Your Edge: Caddy-on-VM vs Apache-on-FreeBSD
Right now, your VM is acting as the edge for revolt.yaws.com because Caddy is listening on
:80 and :443 and responses show via: 1.1 Caddy.
Choose one authoritative edge to simplify security and troubleshooting.
Option A (Recommended): FreeBSD Apache is the edge
- Internet → FreeBSD PF → Apache :443 → VM private IP (HTTP)
- VM ports are private; only FreeBSD is publicly exposed
- Single choke point for rate limits, ACLs, logging, WAF, etc.
Option B (Fastest): Keep Caddy as edge on the VM
- Internet → FreeBSD PF (pass-through) → VM Caddy :443 → Stoat services
- Hardening happens on the VM firewall + Caddy config
- Works well if the VM is already stable and you prefer Caddy
Whichever edge you pick, the other component should be non-public (bind to private interface or firewall-restricted).
2) If Keeping Caddy as Edge: Harden Caddy + VM Exposure
2.1 Your current routing map (confirmed)
This is the authoritative request routing for your deployment:
2.2 Enforce HTTPS-only (redirect HTTP → HTTPS)
If you want to keep port 80 open for convenience, ensure it only redirects to HTTPS.
Add a redirect matcher near the top of your site block:
2.3 Reduce exposure at the VM firewall
- Allow inbound: tcp/443 only (and optionally tcp/80 for redirect)
- Ensure internal service ports are not reachable from WAN
- Prefer binding services to private networks, not
0.0.0.0
In
compose.yml, avoid publishing internal ports (e.g., 14702–14706) to the host. They should be internal to the Docker network.3) If Moving Edge to FreeBSD Apache: Exact Proxy Mapping (from your Caddyfile)
If you want Apache to be the edge, the cleanest design is: Apache terminates TLS and forwards to the VM’s Caddy over plain HTTP.
Caddy continues routing /api, /ws, /autumn, /january, /gifbox, and /.
3.1 Port 80: redirect to HTTPS
3.2 Port 443: proxy to VM (HTTP + WebSocket)
This uses your now-loaded proxy_wstunnel module and proxies only the /ws path as WebSocket, matching your Caddyfile.
Everything else proxies as normal HTTP to Caddy on the VM.
If Apache becomes edge, ensure the VM’s Caddy is not public (bind to private interface or firewall so only FreeBSD can reach it).
4) Stoat “Org Mode”: Roles, Categories, and Permissions
4.1 Role hierarchy (recommended)
- Fleet Command
- Executive Officer
- Wing Lead
- Squad Lead
- Veteran
- Member
- Recruit
- Guest
- Bot
4.2 Permission principles (do not skip)
- No self-assigned roles
- No role escalation: only top leadership can grant leadership roles
- Prefer category-level permissions to avoid drift and mistakes
- Keep Recruit restricted (links/uploads/posting scope) until vetted
4.3 Suggested category layout
4.4 Example permission blueprint
- Command: only Fleet Command + XO; hidden from everyone else
- Fleet Ops: read/post for Member+; moderation tools for Wing Lead+
- Recruitment Intake: recruits can post; restrict links/uploads if spam risk is non-trivial
5) Abuse Controls: Closed Invite-Only with 100+ Registered Accounts
With a closed org, most risk comes from: leaked invites, account stuffing, and low-effort spam.
Build controls at both the edge (proxy/firewall) and the app layer (permissions).
5.1 Invite policy (strong default)
- Only admins/mods can create invites
- Invites should expire (24–72 hours)
- Invites should have limited uses (1–5 typical)
- Never publish a “permanent” invite
5.2 Rate limiting and login protection
- Edge throttling: fail2ban / WAF rules for repeated login failures
- App throttling: stricter posting/link/upload rules for Recruit and Guest
- Disable features you don’t use (public discovery, open registration), where supported
5.3 Upload limits at the edge (Apache note)
If Apache is your edge, you can cap request body size using LimitRequestBody.
Keep Apache comments on their own line (no inline #).
Keep recruits contained: limit channels, restrict links/uploads, and promote manually after vetting. This alone prevents most “drive-by” abuse.