feat(boot_guards): refuse to start in web mode with insecure config (#441) [stacked on #475] - #476
Open
olegbrok wants to merge 1 commit into
Open
Conversation
…441) PR-5 of the #433 web-exposed hardening track. Loud-failure boot-time checks for the obviously-unsafe configurations an internet-exposed deployment must not run with. Stacked on #475 → #474 → #473 → #472. Scope of this PR ================ Ships the guards that don't depend on unshipped pieces of the track. The remaining guards land alongside their owning PRs (admin user, legacy UI password → #435; multi-worker rate-limit backend → #438; audit emission for overrides → #440). Each deferral is marked with a TODO referencing the owning PR. Guards (fatal, web mode only) ----------------------------- * SESSION_SECRET — PINKY_SESSION_SECRET unset, < 32 chars, one of a small placeholder set (changeme/secret/pinky/...), or trivially low-entropy (≤ 2 distinct chars). * PUBLIC_BIND_NO_TLS — bound to 0.0.0.0 / a non-loopback host without PINKY_BEHIND_TLS_PROXY=true to acknowledge upstream TLS termination. * MISSING_TRUSTED_PROXIES — PINKY_BEHIND_TLS_PROXY=true with PINKY_TRUSTED_PROXIES empty; otherwise audit + rate-limit attribution silently flatten to 127.0.0.1 because #442's resolver refuses to honor XFF from an untrusted peer. * DB_PERMISSIONS — DB file or its parent directory has loose permissions (group/other bits set). No-op on Windows. Warnings (non-fatal, all modes) ------------------------------- * RUNNING_AS_ROOT — daemon process UID == 0. Override mechanism ------------------ Per-guard PINKY_ALLOW_INSECURE_<NAME>=true env vars. **No global escape hatch by design** — operators who need to bypass a guard for an emergency must do so per-guard, and each override is logged loudly at startup. Once #440 lands, overrides will also emit an audit event. Wiring ------ * assert_web_mode_safe(mode, host, db_path) called from pinky_daemon.__main__._run_api after argparse + mode resolution and before uvicorn.run. * Failure → preformatted rejection block to stderr; sys.exit(3). Distinct exit code from invalid-mode (#434 uses 2) so wrapper scripts can route on it. Tests (34 new) -------------- * check_session_secret: unset / short / placeholder (parametrised) / long-random-passes / low-entropy / override. * check_public_bind_requires_tls_marker: loopback variants pass / public bind no marker fails / public bind with marker passes / specific public address fails / override. * check_trusted_proxies_when_behind_tls: no marker passes / marker with proxies passes / marker without proxies fails. * check_db_permissions: nonexistent skips / secure dir + file pass / world-readable dir + file fail. * run_warning_guards: empty list when nothing to warn / running as root warning / silenced by override. * assert_web_mode_safe: trusted/lan skip all / web all-pass / web aggregates failures / overrides let boot proceed. * format_guard_error: contains all expected sections. Closes part of #433. Refs #441. 🤖 Authored by Barsik
This was referenced May 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR-5 of the #433 web-exposed hardening track. Loud-failure boot-time checks for the obviously-unsafe configurations an internet-exposed deployment must not run with.
Stacked on #475 → #474 → #473 → #472.
Closes part of #433. Refs #441.
Scope of this PR
Ships the guards that don't depend on unshipped pieces of the track. Remaining guards land alongside their owning PRs (admin user + legacy UI password → #435; multi-worker rate-limit backend → #438; audit emission for overrides → #440). Each deferral is marked with a TODO referencing the owning PR.
Guards (fatal, web mode only)
Warnings (non-fatal, all modes)
Override mechanism
Per-guard `PINKY_ALLOW_INSECURE_=true` env vars. No global escape hatch by design — operators that need to bypass a guard for an emergency must do so per-guard, and each override is logged loudly at startup. Once #440 lands, overrides will also emit an audit event.
Wiring
`assert_web_mode_safe(mode, host, db_path)` called from `pinky_daemon.main._run_api` after argparse + mode resolution and before `uvicorn.run`. Failure → preformatted rejection block to stderr; `sys.exit(3)`. Distinct exit code from invalid-mode (#434 uses 2) so wrapper scripts can route on it.
Tests (34 new)
`tests/test_boot_guards.py` covers each guard, the override mechanism, the warning runner, the aggregator, and the message formatter. All scenarios: trusted/lan skip, web-all-pass, web-aggregates-failures, web-overrides-let-boot-proceed.
286 existing api tests + all earlier hardening-track tests still green.
Test plan
Out of scope (deferred to owning PRs)
🤖 Opened by Barsik