Security review #005 identified critical vulnerabilities in the Unix domain socket admin API (C1 symlink race, C2 no auth, C3 info leak, W1-W7, S1-S6). ADR-028 (already accepted) replaces the socket with an authenticated HTTP admin API on the health check port. This commit adds the remaining spec work: - ADR-029: Config file TOCTOU mitigation (mtime check on reload) - ADR-030: Store cli_allow_wildcard_bind in ConfigReloadHandle for consistent reload validation - Implementation tasks for the admin HTTP migration (fix/admin-http-api), TOCTOU fix (fix/config-reload-toctou), and wildcard flag fix (fix/wildcard-flag-reload) - Updated review #005 status to resolved with per-finding disposition - Resolved OQ-16: POST for state-changing admin endpoints, GET for read-only - Updated all architecture docs to reference new ADRs, use admin_key_path instead of admin_socket_path, and reflect POST method for /admin/reload
3.0 KiB
3.0 KiB
ADR-013: Health Check on Separate Local Port
Status
Accepted
Context
The health check endpoint (/health) needs to be accessible for monitoring
without requiring TLS. Serving it on the main HTTPS listener would mean:
- TLS handshake must succeed for the health check to respond
- External monitoring tools need to handle TLS
- A TLS configuration error would make the health check unreachable, creating a false-negative monitoring signal
- It creates collision with upstream applications that use
/healthfor their own health checks (see ADR-022)
Three options were considered (see OQ-03):
- Separate unencrypted port on localhost (chosen): Simple, works with standard monitoring tools, health checks work even when TLS is misconfigured
- Main HTTPS listener only: Would require TLS for health checks, creating a circular dependency — TLS config errors would make health checks unreachable
- Admin port with its own listener: Most flexible but adds complexity beyond what's needed for a simple health check
Decision
Add a configurable health check port that binds to 127.0.0.1 only (localhost),
serving /health over plain HTTP. This is a separate listener from the main
HTTP and HTTPS listeners.
The port is configurable via health_check_port in StaticConfig. The default
value is 9900 (enabled, localhost only). Setting it to 0 disables the
health check listener entirely — there is no /health route on the main HTTPS
listener (see ADR-022).
Rationale
- A local-only health check port is the standard pattern for reverse proxies and service meshes (envoy, haproxy, k8s health probes all use this pattern)
- Health checks should work even when TLS is misconfigured — that's the whole point of monitoring
- Binding to
127.0.0.1only means the health check is not exposed to the internet — only local monitoring tools (systemd, scripts, load balancers on the same host) can reach it - Configurable port allows different deployment scenarios (some monitoring runs on different ports)
- Disabling via
health_check_port = 0removes the health check entirely — the admin HTTP endpoint's/admin/status(with Bearer token) remains available as an alternative health/status mechanism (ADR-028) - When this project is folded into alknet, the health check will use alknet's existing patterns, making the separate port unnecessary in that context
Consequences
Positive:
- Health checks work even when TLS is misconfigured
- Standard pattern that monitoring tools expect
- Not exposed to the internet (localhost only)
- Configurable — can be disabled if not needed
- systemd can use it for
NotifyAccessreadiness checks
Negative:
- Additional listener to manage (minimal complexity)
References
- operations.md
- ADR-022 — Health check scope (no
/healthon main listener) - ADR-028 — Authenticated HTTP admin API (admin endpoints on health check port)
- OQ-03 (now resolved)