sec(env): refuse to start master API with footgun public-binding config

Add validate_public_binding() called from the master API lifespan: when
DECNET_API_HOST is non-loopback, refuse to start if DECNET_CORS_ORIGINS
still contains a loopback origin (catches the "operator flipped to
0.0.0.0 to make it work and forgot to update CORS" footgun) or if
DECNET_CANARY_HTTP_BASE is plaintext http:// to a non-loopback host.
Log CRITICAL when DECNET_LIMITER_ENABLED=false on a public binding.
The validator no-ops under pytest so unrelated suites don't trip on it.

Add DECNET_VERIFY_HOSTNAME env knob; AgentClient and UpdaterClient
consult it when verify_hostname is None, giving production deploys
TLS hostname verification on top of the existing CA + fingerprint pin.
Default off so dev enrollments with mismatched SANs keep working.
This commit is contained in:
2026-04-27 21:15:15 -04:00
parent 28e2a93355
commit 1a7da33375
4 changed files with 200 additions and 5 deletions

View File

@@ -24,6 +24,7 @@ from decnet.env import (
DECNET_INGEST_LOG_FILE,
DECNET_PROFILE_DIR,
DECNET_PROFILE_REQUESTS,
validate_public_binding,
)
from decnet.logging import get_logger
from decnet.web.dependencies import repo
@@ -69,6 +70,11 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
soft,
)
# Refuse to come up with a footgun config on a public binding (loopback
# CORS origin while bound to 0.0.0.0, plaintext canary base, etc.).
# Raises ValueError with an actionable message; uvicorn surfaces it.
validate_public_binding()
log.info("API startup initialising database")
for attempt in range(1, 6):
try: