fix: fail container startup when JWT_SECRET is unset instead of falling back to 'change-me' in nginx config - #15
Merged
Conversation
…ng back to 'change-me' in nginx config Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RttwcxBgsnXH2TwQGQsrp
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.
What
Follow-up to PR #14. That PR fixed the silent auth bypass on the Python/JWT-validation side (
api/auth.pynow refuses to start ifAUTH_ENABLED=trueandJWT_SECRETis unset). This PR fixes the same class of problem in the architecturally separate Dockerfile/nginx-config-generation path.Where and when this happens
Dockerfile'sCMDis abash -c "..."script that runs at container start time (not build time — env vars likeJWT_SECRETaren't available duringdocker buildat all; this is plain shell parameter expansion evaluated when the container process actually launches). It previously did:If
JWT_SECRETwas unset, this silently wrote the literal string"change-me"into nginx's config as the internal UI-proxy trust header, then continued straight into starting nginx and serving traffic — no error, no warning, nothing.The fix
Added a fail-fast check as the very first thing the CMD script does — before the Ollama-readiness wait, before the index build, and before nginx starts:
Also dropped the
:-change-mefallback entirely from theprintfline — it's now"${JWT_SECRET:-}", so if this check somehow doesn't fire (i.e.AUTH_ENABLEDisn'ttrue), an unset secret becomes an empty string in the config rather than a plausible-looking fake credential.Design choice — gated on
AUTH_ENABLED, matching PR #14, not unconditional: I considered making this unconditional (always requireJWT_SECRET), but that would break every existing local/dev deployment that runs withAUTH_ENABLEDunset/false and never setsJWT_SECRET(the documented default, per README's Authentication section) — the internal header is provably irrelevant in that case sincerequire_auth()short-circuits to"system"before ever looking at it. Gating onAUTH_ENABLED=truekeeps this consistent with PR #14's exact same condition and avoids an unnecessary breaking change. Flagging this choice explicitly in case you intended unconditional enforcement.Before / after
Before —
AUTH_ENABLED=true,JWT_SECRETunset: container started, nginx served traffic withX-Devhub-Internal: change-mebaked in, looked fully operational.After — same misconfiguration: container refuses to start.
No "Waiting for Ollama..." line even printed — the check fires before any other startup work, and confirmed via
docker inspect: container is not running, exited 1. nginx never started.Correctly configured —
AUTH_ENABLED=true,JWT_SECRET=a-real-shared-secret— starts normally, works end-to-end through the nginx proxy:(Note: a request through nginx succeeds without a client-supplied JWT even in the "no token" case — that's pre-existing, correct behavior, not something this PR changes: nginx unconditionally attaches the trusted internal header to every
/rag/request it proxies, since that's how same-origin browser UI traffic authenticates. Only requests that bypass nginx entirely need a real JWT, confirmed above.)pytest results
pytest -q→ 199 passed — unaffected, as expected (no Python code touched,Dockerfile-only change).Scope
1 file:
Dockerfile(CMDstep only).This changes container startup behavior. Per instructions, not merged automatically — results reported above; merge only after explicit confirmation.