Skip to content

security: Harden server, auth, rate limiting, and proxy config#78

Merged
0xFelix merged 8 commits intomainfrom
security-hardening
Apr 18, 2026
Merged

security: Harden server, auth, rate limiting, and proxy config#78
0xFelix merged 8 commits intomainfrom
security-hardening

Conversation

@0xFelix
Copy link
Copy Markdown
Owner

@0xFelix 0xFelix commented Apr 18, 2026

Summary

  • container: Run as non-root UID 65532 in the scratch image
  • server: Add HTTP ReadTimeout (30s), WriteTimeout (30s), and IdleTimeout (120s)
  • ratelimit: Cap in-memory bucket/entry maps to 65536 entries each to prevent unbounded memory growth under IP spoofing; add periodic sweep to both limiter and lockout
  • middleware: Limit debug log body reads via MaxBytesReader; redact Authorization, X-Api-User, X-Api-Key headers before printing
  • middleware: Apply lockout and constant-time comparison to CMD_API_SHOW_DOMAINS user auth
  • config: Parse trustedProxies once at startup into []netip.Prefix, accepting both bare IPs and CIDR ranges; use prefix.Contains for matching
  • chore: Remove stale //nolint:gosec directives now flagged by nolintlint
  • docs: Security callout, wildcard subdomain auth, DynDNS2 lockout/nohost note, caller-supplied IP trust model, updated TRUSTED_PROXIES description

Test plan

  • go test ./... passes
  • golangci-lint run ./... reports 0 issues
  • Verify container runs as UID 65532 (docker run --rm ... id)
  • Confirm TRUSTED_PROXIES=10.0.0.0/8 accepts a forwarded header from 10.1.2.3 and ignores one from 192.168.1.1
  • Confirm rate limit returns 429 after burst is exhausted
  • Confirm lockout triggers after repeated auth failures

🤖 Generated with Claude Code

0xFelix and others added 8 commits April 17, 2026 15:53
Assign a fixed system UID (65532) to the service user so the final
scratch image runs under a predictable non-root identity regardless of
host defaults.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Felix Matouschek <felix@matouschek.org>
Set ReadTimeout, WriteTimeout, and IdleTimeout on the HTTP server to
limit resource exhaustion from slow or stalled clients.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
Add a maxBuckets cap (default 65536) to the per-IP token-bucket limiter.
When the map is full, an eager idle sweep is attempted before inserting a
new entry; if the map is still full after the sweep the request is
rejected with 429. This prevents unbounded memory growth under IP
spoofing or a large number of unique clients.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
Add a maxEntries cap (default 65536) to the per-IP lockout tracker.
When full, an eager stale-entry sweep is run; if the map is still full
an arbitrary entry is evicted before inserting the new key. Also adds
a periodic sweep on IsBlocked and RecordFailure to age out stale entries
even when the cap is never reached.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
Limit request body reads to maxRequestBodySize via MaxBytesReader and
return 413 on overflow. Redact Authorization, X-Api-User, and X-Api-Key
headers before printing to prevent credential leakage in debug logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
Apply lockout tracking to CMD_API_SHOW_DOMAINS and use constant-time
string comparison for username/password checks to prevent timing-based
credential enumeration. Return 401 on auth failure and skip credentials
check when both username and password are empty.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
Parse trustedProxies entries once at startup into []netip.Prefix,
accepting both bare IPs (promoted to /32 or /128) and CIDR notation.
The middleware now uses prefix.Contains for matching, making subnet-level
proxy trust straightforward to configure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
Add security callout covering plaintext HTTP and config file permissions.
Document wildcard subdomain authorization, DynDNS2 nohost/lockout
interaction, caller-supplied IP trust model, and updated TRUSTED_PROXIES
description to reflect CIDR support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: 0xFelix <felix@matouschek.org>
@0xFelix 0xFelix force-pushed the security-hardening branch from 5c8a877 to 6fd5279 Compare April 18, 2026 16:02
@0xFelix 0xFelix merged commit 7d58394 into main Apr 18, 2026
5 checks passed
@0xFelix 0xFelix deleted the security-hardening branch April 18, 2026 16:06
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