Repository navigation
fix(auth): treat blank bootstrap secrets as unset and refuse passwords under 12 characters - #294
Merged
Merged
Conversation
…ire secrets in the shipped compose files
The shipped compose files passed INPUTLAYER_ADMIN_PASSWORD as
${INPUTLAYER_ADMIN_PASSWORD:-}, so an unset variable reached the engine as
an empty string, and bootstrap created an admin whose password was empty:
anyone could sign in as admin with a blank password on the published port.
Engine:
- An empty or whitespace-only INPUTLAYER_ADMIN_PASSWORD,
http.auth.bootstrap_admin_password or INPUTLAYER_BOOTSTRAP_API_KEY counts
as unset: bootstrap generates the secret and saves it to credentials.toml,
as on a first boot without one.
- A supplied bootstrap secret shorter than 12 characters refuses startup
(Config::validate) with an error naming its source. A short config value
is refused even when the env var overrides it.
- .user create and .user password refuse passwords shorter than 12
characters.
- An empty password never verifies at login, so an admin created with one
by an earlier release cannot sign in with it.
Configs: docker-compose.yml and docker-compose-no-tls.yml require
INPUTLAYER_ADMIN_PASSWORD and INPUTLAYER_BOOTSTRAP_API_KEY (the gateway
uses the key to reach the engine) with ${VAR:?...}; the no-TLS file also
requires GATEWAY_API_KEY, since it publishes the gateway on every
interface. The demo compose passes the optional key through instead of
defaulting it to empty. The Kubernetes manifest already requires the admin
password secret; it now states the length rule.
Tests use passwords of at least 12 characters; the Makefile live-SDK
targets use LIVE_ADMIN_PASSWORD. Docs, .env examples and the regenerated
GUI docs bundle describe the rules.
# Conflicts: # tests/standing_query_tests/harness.rs
This was referenced Oct 5, 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.
Intent
I want you to really hyper focus on making the symbolic engine, you know, crisp and really truly production ready. (Context: the adversarial audit found a P0 security bug: the shipped docker-compose files leave the admin password empty when the variable is unset, and the engine accepts it, so anyone can log in as admin with a blank password on the published port 8080.) The audit finding in substance: docker-compose.yml and docker-compose-no-tls.yml pass INPUTLAYER_ADMIN_PASSWORD as ${INPUTLAYER_ADMIN_PASSWORD:-}, so it reaches the engine as an empty string whenever the operator did not export it, and the no-TLS file publishes port 8080 on all interfaces; bootstrap takes the supplied password with no empty filter, and nothing enforces a minimum password length at bootstrap, .user create or .user password. Scope: P0 empty admin password treated as unset (a blank supplied password or bootstrap API key makes the engine generate and save one, as on first boot); weak secrets (shorter than 12 characters, admin password and bootstrap API key) refused only when newly stored - first-boot bootstrap, .user create, .user password - while an existing deployment whose admin is already stored starts and logs a loud warning to remove the weak value; the compose files require the keys (INPUTLAYER_ADMIN_PASSWORD, INPUTLAYER_BOOTSTRAP_API_KEY and GATEWAY_API_KEY via ${VAR:?...}).
What Changed
INPUTLAYER_ADMIN_PASSWORD,http.auth.bootstrap_admin_passwordorINPUTLAYER_BOOTSTRAP_API_KEYas unset, so it generates the secret and saves it, as on first boot. The newauth::bootstrap_secretsmakes first-boot bootstrap fail before writing anything if a supplied secret it would store is shorter than 12 characters, andserverexits with that error. If the admin or API key already exists, a short supplied value is ignored and the server logs a loud warning to remove it. When the env var is set, the config password is ignored, and the server prints a note saying so.verify_password_or_dummynow always rejects an empty password..user createand.user passwordrefuse passwords shorter than 12 characters through the newauth::check_password_strength, which counts characters rather than bytes.docker-compose.ymlanddocker-compose-no-tls.ymlnow requireINPUTLAYER_ADMIN_PASSWORD,INPUTLAYER_BOOTSTRAP_API_KEYandGATEWAY_API_KEYvia${VAR:?...}, so the gateway in the no-TLS file is no longer open when the key is missing. The k8s statefulset comments,.env.example,config.example.toml, the docs and the bundled GUI docs now describe the 12-character minimum and the env override. The Makefile live targets, demo script, SDK tests/examples and Rust tests use passwords of 12 or more characters. A newtests/bootstrap_secret_tests.rscovers blank, weak and existing-deployment cases.Risk Assessment
✅ Low: The fix round is small and does what the operator asked: when a non-blank INPUTLAYER_ADMIN_PASSWORD is set on first boot, the overridden config password is dropped before the strength check and one NOTE line is logged. Only the value that will actually be stored is checked. The added binary-level test would fail without the fix and checks that the admin authenticates with the env password and not with the config one.
Testing
I built the server at the target commit and at base 8b574ef2. I ran it on disposable data directories under /tmp, signed in and ran commands through a Node WebSocket driver, and checked both shipped compose files withdocker compose configand throwaway env files. What the running server did: - Blank secrets: with a blank admin password and bootstrap API key, the server generates and saves both. A blank-password login is rejected. - Weak secrets on first boot: a short password or API key (env or config) stops startup with an error naming the setting, and nothing is written. Exactly 12 characters is accepted. - Existing deployment: it starts with a WARNING per weak value, and the weak value can't be used to sign in. -.usercommands:.user createand.user passwordreject short passwords and accept 12 or more characters. - Recorded decision: a config password of "admin" overridden by a strong INPUTLAYER_ADMIN_PASSWORD only gets a NOTE log line, and the admin is created with the env password. Without the env password, the same config refuses startup. - Compose files: both refuse to render when any of the three keys is missing or empty. The P0 reproduces on the base engine (blank admin login succeeds). The fixed engine, started on that same data directory, rejects it. The focused bootstrap_secret_tests binary tests also pass. There is no UI change, so no screenshots; the evidence is CLI and WebSocket transcripts. All temporary directories and servers were removed.Evidence: Blank env secrets are generated and saved; blank login rejected
Evidence: Weak secrets refuse first-boot startup, nothing written
Evidence: Existing deployment with weak env starts with warnings
Evidence: .user create/.user password minimum length
Evidence: Config password overridden by env is ignored (decision)
Evidence: Compose files require all three secrets
Evidence: P0 reproduced on base, fixed on target with the same data directory
BASE: login admin/'' -> authenticated role=admin FIXED (same data dir): login admin/'' -> auth_error Invalid credentialsEvidence: Binary-level bootstrap secret tests
Evidence: WebSocket driver used for logins
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
🔧 **Rebase** - 1 issue found → auto-fixed ✅
gui/lib/docs-bundle.ts- merge conflict rebasing onto origin/main🔧 Fix applied.
✅ Re-checked - no issues remain.
🔧 **Review** - 1 issue found → auto-fixed ✅
src/auth.rs:147- The intent says weak secrets are "refused only when newly stored", but on first bootbootstrap_secretschecks env and config passwords with the samepassword_use. A shorthttp.auth.bootstrap_admin_passwordtherefore refuses startup even when a strongINPUTLAYER_ADMIN_PASSWORDis set, although the env value is the one stored (env_password.or(config_password)). Example: config.toml still hasbootstrap_admin_password = "admin"from an older setup, the operator exports a 24-character INPUTLAYER_ADMIN_PASSWORD, and a fresh data dir fails with "http.auth.bootstrap_admin_password is shorter than 12 characters". Commit 9a71ccc dropped the env-override warning branch on purpose, so this needs a decision: keep refusing a config value that is overridden and never stored, or treat it as unused (warn only) when a non-blank env password is present, which is what the intent wording asks for.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
cargo build --bin inputlayer-server(target) and the same build ofgit archive 8b574ef2(base), into a separate temp target directoryLive first boot with INPUTLAYER_ADMIN_PASSWORD='' / INPUTLAYER_BOOTSTRAP_API_KEY='', then WS login with a blank password and with the generated oneLive first boot with admin password 'admin', 'eleven-char', and API key 'short-key' (expect refusal, no credentials.toml)Live first boot with the 12-character 'twelve-chars', then restart the existing deployment with weak env values (expect warnings, start, weak value can't sign in)Live WS.user create alice short viewer,.user create alice password-0123 viewer,.user password alice eleven-char,.user password alice password-0456Live first boot with config.toml bootstrap_admin_password="admin" plus a strong INPUTLAYER_ADMIN_PASSWORD; and the same config without the env passwordBase engine bootstrapped with a blank password (blank login succeeds), then the fixed engine on the same data directory (blank login rejected)docker compose --env-file <tmp> -f docker-compose.yml configand the same for docker-compose-no-tls.yml, with no vars, each var missing, an empty var, and all vars setcargo test --test bootstrap_secret_tests✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.