Skip to content

feat(deploy): add host firewall with per-IP connection limits#51

Merged
jonathandeamer merged 1 commit into
mainfrom
feat/host-firewall-per-ip-limits
Jul 1, 2026
Merged

feat(deploy): add host firewall with per-IP connection limits#51
jonathandeamer merged 1 commit into
mainfrom
feat/host-firewall-per-ip-limits

Conversation

@jonathandeamer

Copy link
Copy Markdown
Owner

Closes the single-source worker-exhaustion / slowloris gap in #29 at the firewall layer.

Context

Verified on the box (SSH): there is no host firewalliptables/ip6tables are at default ACCEPT with zero rules, and nftables/ufw aren't installed. So only the Lightsail cloud firewall sits in front of tcp/1900, and it structurally can't per-IP cap. The box is raw-exposed for #29.

What this adds

  • contrib/buffetcar.nftables — a surgical ruleset (same contrib/ + deploy-installed pattern as buffetcar.service):
    • Input policy stays acceptcannot lock out SSH or block other services; Lightsail handles port filtering.
    • Per-source-IP concurrent-connection cap (ct count over 16) — the resource-cap sibling of the --workers pool.
    • Per-source-IP new-connection rate limit (30/min, small burst).
    • Excess is dropped silently (no RST) → preserves the no-leakage contract.
  • deploy.yml step — installs nftables, validates with nft -c -f before installing (syntax error fails the deploy rather than half-applying), then applies atomically with nft -f.
  • Docs updated (CLAUDE.md deploy description + Abuse resistance: per-IP connection/rate limits #29 roadmap note).

Staged approach (why this is only half of #29)

This is the firewall layer SECURITY.md already scopes rate limiting to. The in-app per-IP concurrent cap stays open in #29, justified narrowly as making the binary safe-by-default when deployed without a firewall.

Human-merge + human first-apply, please

Touches the deploy path and the threat model. Merging triggers a deploy that installs+applies the firewall on the live box. The nft -c -f gate makes a syntax error fail safe (policy accept, nothing applied), but I'd rather you watch the first apply. The exact per-source meter { … ct count over … } statement is validated by that check-mode step on first apply.

Adds contrib/buffetcar.nftables, a surgical nftables ruleset installed by the
Deploy workflow. The box currently has no host firewall (verified: iptables at
default ACCEPT, no nftables/ufw), so only the Lightsail cloud firewall sits in
front of tcp/1900 and it cannot per-IP cap. This closes the single-source
worker-exhaustion / slowloris gap (#29) at the firewall layer.

Design notes:
- Surgical, not a perimeter firewall: input policy stays 'accept' so it can
  never lock out SSH or block other services. Lightsail handles port filtering.
- Rules cap per-source-IP concurrent connections (ct count over 16) and
  new-connection rate (30/min) on tcp/1900; excess is dropped silently (no RST),
  preserving the no-leakage contract.
- Deploy step installs nftables, validates the file with 'nft -c -f' before
  applying (a syntax error fails the deploy rather than half-applying), then
  applies atomically with 'nft -f'.

Staged approach: this is the firewall layer SECURITY.md already scopes rate
limiting to. The in-app per-IP concurrent cap remains open in #29, justified
narrowly as making the binary safe-by-default when deployed without a firewall.
@jonathandeamer jonathandeamer force-pushed the feat/host-firewall-per-ip-limits branch from cf05da3 to 6a34366 Compare July 1, 2026 18:32
@jonathandeamer jonathandeamer merged commit f8917b4 into main Jul 1, 2026
6 checks passed
@jonathandeamer jonathandeamer deleted the feat/host-firewall-per-ip-limits branch July 1, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant