Production correct-credential login returned auth_unavailable 503 because Flask's SECRET_KEY was unset: wrong-password probes stopped at 401 before CSRF token issuance, while valid credentials reached _csrf_serializer() and crashed. App factory now rejects absent/short (<32 char) SECRET_KEY at startup, and docker_entrypoint.sh fails fast before migration/services. Bootstrap no longer passes ADMIN_PASSWORD in process arguments; env-only. Tests: app-factory + entrypoint regression (5 focused passed), full backend suite 204 passed. Independent review PASS.
2.0 KiB
2026-09-01 — Login 503 from Missing SECRET_KEY
Status
Root cause proved; fail-fast fix implemented and verified locally; fresh review pending.
Incident
Production login returned:
{"error_code":"auth_unavailable","success":false}
Wrong-password probes returned 401, while the browser's correct credentials returned 503. The distinction was critical: invalid credentials exit before session/CSRF issuance, but successful credential verification continues to issue_csrf_token().
Verified root cause
Config.SECRET_KEY reads the SECRET_KEY environment variable. issue_csrf_token() calls _csrf_serializer(), which raises ApiError("auth_unavailable", 503, ...) when current_app.secret_key is absent. Therefore the browser's 503 indicates that credentials passed but the deployment had no signing secret.
Fix
create_app()now raisesRuntimeError("secret_key_required")when the signing secret is absent.docker_entrypoint.shnow fails before migration/services ifSECRET_KEYis absent or shorter than 32 characters, with a clear error message.- Bootstrap now reads
ADMIN_PASSWORDonly from inherited environment variables; it is no longer exposed in process arguments. - Added RED/GREEN app-factory and entrypoint security regression tests.
Verification
- RED: the missing-secret test failed because
create_app()previously started normally. - GREEN: focused app-factory/entrypoint security tests 5 passed.
- Backend full suite 204 passed, 24 warnings.
- Entry-point Bash syntax passed.
git diff --checkpassed.- Independent reviewer (CEO profile):
{"passed":true,"security_concerns":[...],"logic_errors":[],"suggestions":[...]}.
Operator action
Generate a persistent random signing secret (for example python3 -c "import secrets; print(secrets.token_urlsafe(48))"), save it as the EasyPanel SECRET_KEY environment variable, set SESSION_COOKIE_SECURE=true, and restart/redeploy. Do not rotate SECRET_KEY casually because existing sessions and encrypted platform settings depend on it.