Skip to content

fix(auth): treat blank bootstrap secrets as unset and refuse passwords under 12 characters - #294

Merged
jsam merged 7 commits into
mainfrom
fm/fix-empty-admin-pw-v2
Oct 5, 2026
Merged

jsam merged 7 commits into
mainfrom
fm/fix-empty-admin-pw-v2

Conversation

@jsam

@jsam jsam commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

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

  • Bootstrap: the engine now treats a blank INPUTLAYER_ADMIN_PASSWORD, http.auth.bootstrap_admin_password or INPUTLAYER_BOOTSTRAP_API_KEY as unset, so it generates the secret and saves it, as on first boot. The new auth::bootstrap_secrets makes first-boot bootstrap fail before writing anything if a supplied secret it would store is shorter than 12 characters, and server exits 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_dummy now always rejects an empty password.
  • User commands: .user create and .user password refuse passwords shorter than 12 characters through the new auth::check_password_strength, which counts characters rather than bytes.
  • Deploy and docs: docker-compose.yml and docker-compose-no-tls.yml now require INPUTLAYER_ADMIN_PASSWORD, INPUTLAYER_BOOTSTRAP_API_KEY and GATEWAY_API_KEY via ${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 new tests/bootstrap_secret_tests.rs covers 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 with docker compose config and 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. - .user commands: .user create and .user password reject 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.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Operator boots a fresh engine with blank INPUTLAYER_ADMIN_PASSWORD and INPUTLAYER_BOOTSTRAP_API_KEY: random 64-hex secrets are generated and saved to credentials.toml, and admin login with a blank pas… ✅ pass live s1-blank-env.txt
Adversarial: the base engine accepts blank admin login (P0 reproduced); after upgrading that same data directory to the fixed engine, it starts and rejects blank admin login ✅ pass live s7-legacy-blank-admin-upgrade.txt
Fresh engine with a short INPUTLAYER_ADMIN_PASSWORD ('admin', 'eleven-char') or short INPUTLAYER_BOOTSTRAP_API_KEY refuses to start, the error names the setting, and no credentials are written ✅ pass live s2-weak-first-boot.txt
Boundary: a first boot with the exactly-12-character password 'twelve-chars' starts, and admin can sign in with it ✅ pass live s3-existing-deployment-weak.txt
An existing deployment restarted with weak env secrets still starts, logs a WARNING for each weak value, and the weak value can't be used to sign in ✅ pass live s3-existing-deployment-weak.txt
Admin runs .user create / .user password with a short password: rejected with 'Password too short: use at least 12 characters'; 12+ characters succeeds ✅ pass live s4-user-commands.txt
Recorded decision: config bootstrap_admin_password="admin" plus a strong env password on a fresh data dir starts with a NOTE log line, admin uses the env password, and 'admin' is rejected; without the… ✅ pass live s5-config-overridden-by-env.txt
docker compose refuses to render docker-compose.yml and docker-compose-no-tls.yml when INPUTLAYER_ADMIN_PASSWORD, INPUTLAYER_BOOTSTRAP_API_KEY or GATEWAY_API_KEY is unset or empty, and renders them wh… ✅ pass live s6-compose-required-vars.txt
Evidence: Blank env secrets are generated and saved; blank login rejected
\### S1: first boot with INPUTLAYER_ADMIN_PASSWORD='' and INPUTLAYER_BOOTSTRAP_API_KEY='' (what ${VAR:-} used to pass)
start rc=0
--- stderr (secrets redacted):
=== INITIAL ADMIN CREDENTIALS CREATED ===
Admin API key: ****e20e
==========================================
Generated credentials saved to: /tmp/ilt-ErPJ/s1/data/credentials.toml
Retrieve them with:  cat /tmp/ilt-ErPJ/s1/data/credentials.toml
Delete this file to generate new credentials on next boot.
--- credentials.toml keys:
admin_password = "<64-hex generated>"
api_key = "<64-hex generated>"
--- login admin with blank password:
<- {"type":"auth_error","message":"Invalid credentials"}
<- {"type":"notice","code":"auth_timeout","message":"Authentication timeout (5s)"}
--- login admin with generated password, then whoami:
<- {"type":"authenticated","session_id":"bd9d5dcf-c4e8-4cae-aac3-a4361b30e45a","knowledge_graph":"default","version":"0.1.0","role":"admin","protocol_version":3,"stream_epoch":"91a131801c73fe6d"}
-> .status
<- {"type":"result","columns":["message"],"rows":[["Server Status"],["  Health: healthy"],["  Version: 0.1.0"],["  Uptime: 7 seconds"],["  Total queries: 1"],["  Knowledge graphs: 2"]],"row_count":6,"total_count":6,"truncated":false,"execution_time_ms":0,"row_provenance":["unknown","unknown","unknown",
Evidence: Weak secrets refuse first-boot startup, nothing written
\### s2a: first boot with INPUTLAYER_ADMIN_PASSWORD=admin INPUTLAYER_BOOTSTRAP_API_KEY=
EXITED code=1
launcher rc=1
--- stderr:
ERROR: INPUTLAYER_ADMIN_PASSWORD is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml.
Error: "INPUTLAYER_ADMIN_PASSWORD is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml."
--- data dir credentials.toml exists? no
\### s2b: first boot with INPUTLAYER_ADMIN_PASSWORD=eleven-char INPUTLAYER_BOOTSTRAP_API_KEY=a-strong-enough-key
EXITED code=1
launcher rc=1
--- stderr:
ERROR: INPUTLAYER_ADMIN_PASSWORD is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml.
Error: "INPUTLAYER_ADMIN_PASSWORD is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml."
--- data dir credentials.toml exists? no
\### s2c: first boot with INPUTLAYER_ADMIN_PASSWORD=a-strong-enough-pw INPUTLAYER_BOOTSTRAP_API_KEY=short-key
EXITED code=1
launcher rc=1
--- stderr:
ERROR: INPUTLAYER_BOOTSTRAP_API_KEY is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml.
Error: "INPUTLAYER_BOOTSTRAP_API_KEY is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml."
--- data dir credentials.toml exists? no
Evidence: Existing deployment with weak env starts with warnings
\### S3: same data dir as s2a (refused earlier) - first boot with exactly-12-char password 'twelve-chars' succeeds
launcher rc=0
<- {"type":"authenticated","session_id":"da955d4e-0f80-4dcf-9478-629ed4ae7c5a","knowledge_graph":"default","version":"0.1.0","role":"admin","protocol_version":3,"stream_epoch":"50163ce76bec7094"}

\### S3b: restart the existing deployment with weak INPUTLAYER_ADMIN_PASSWORD=admin and INPUTLAYER_BOOTSTRAP_API_KEY=short
launcher rc=0 (0 = server started)
--- stderr:
WARNING: INPUTLAYER_ADMIN_PASSWORD is shorter than 12 characters and is ignored because the admin user already exists. Remove it from the environment or config.
WARNING: INPUTLAYER_BOOTSTRAP_API_KEY is shorter than 12 characters and is ignored because the admin user already exists. Remove it from the environment or config.
--- login with the weak env value 'admin' (must fail, it is never stored):
<- {"type":"auth_error","message":"Invalid credentials"}
--- login with the stored password:
<- {"type":"authenticated","session_id":"8c9bbcfa-2039-4d84-b479-16eb06214d12","knowledge_graph":"default","version":"0.1.0","role":"admin","protocol_version":3,"stream_epoch":"013230245651103a"}
Evidence: .user create/.user password minimum length
\### S4: as admin, .user create / .user password with short and 12+ char passwords
<- {"type":"authenticated","session_id":"cb156905-2257-45f6-86a6-a0e576bfe1a1","knowledge_graph":"default","version":"0.1.0","role":"admin","protocol_version":3,"stream_epoch":"be17da4dd5844c55"}
-> .user create alice short viewer
<- {"type":"error","message":"Password too short: use at least 12 characters"}
-> .user create alice password-0123 viewer
<- {"type":"result","columns":["message"],"rows":[["User 'alice' created with role 'viewer'."]],"row_count":1,"total_count":1,"truncated":false,"execution_time_ms":0,"row_provenance":["unknown"],"errors":[]}
-> .user password alice eleven-char
<- {"type":"error","message":"Password too short: use at least 12 characters"}
-> .user password alice password-0456
<- {"type":"result","columns":["message"],"rows":[["Password updated for 'alice'."]],"row_count":1,"total_count":1,"truncated":false,"execution_time_ms":0,"row_provenance":["unknown"],"errors":[]}
--- login alice with rejected short password eleven-char:
<- {"type":"auth_error","message":"Invalid credentials"}
--- login alice with accepted password-0456:
<- {"type":"auth_error","message":"Access denied"}
Evidence: Config password overridden by env is ignored (decision)
\### S5: fresh data dir, config.toml bootstrap_admin_password="admin" + strong INPUTLAYER_ADMIN_PASSWORD
[http.auth]
bootstrap_admin_password = "admin"
launcher rc=0 (0 = started)
--- stderr:
NOTE: http.auth.bootstrap_admin_password is ignored because INPUTLAYER_ADMIN_PASSWORD is set.
=== INITIAL ADMIN CREDENTIALS CREATED ===
Admin API key: ****b594
==========================================
Generated credentials saved to: /tmp/ilt-ErPJ/s5/data/credentials.toml
Retrieve them with:  cat /tmp/ilt-ErPJ/s5/data/credentials.toml
Delete this file to generate new credentials on next boot.
--- login admin/admin (config value, must fail):
<- {"type":"auth_error","message":"Invalid credentials"}
--- login admin with env password:
<- {"type":"authenticated","session_id":"f1effb39-2cf0-4fe5-9ff4-2ef637d0f878","knowledge_graph":"default","version":"0.1.0","role":"admin","protocol_version":3,"stream_epoch":"a3bf4fa6f3ddaf93"}

\### S5b (adversarial): same config, NO env password -> config value would be stored, so refused
EXITED code=1
launcher rc=1
ERROR: http.auth.bootstrap_admin_password is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml.
Error: "http.auth.bootstrap_admin_password is shorter than 12 characters. Set a longer value, or unset it to have the server generate one on first boot and save it to credentials.toml."
credentials.toml exists? no
Evidence: Compose files require all three secrets
\### docker-compose.yml with no variables set
error while interpolating services.inputlayer.environment.INPUTLAYER_ADMIN_PASSWORD: required variable INPUTLAYER_ADMIN_PASSWORD is missing a value: Set INPUTLAYER_ADMIN_PASSWORD (at least 12 characters) in .env
exit=1
\### docker-compose.yml with only INPUTLAYER_ADMIN_PASSWORD missing
error while interpolating services.inputlayer.environment.INPUTLAYER_ADMIN_PASSWORD: required variable INPUTLAYER_ADMIN_PASSWORD is missing a value: Set INPUTLAYER_ADMIN_PASSWORD (at least 12 characters) in .env
exit=1
\### docker-compose.yml with only INPUTLAYER_BOOTSTRAP_API_KEY missing
error while interpolating services.inputlayer.environment.INPUTLAYER_BOOTSTRAP_API_KEY: required variable INPUTLAYER_BOOTSTRAP_API_KEY is missing a value: Set INPUTLAYER_BOOTSTRAP_API_KEY (at least 12 characters) in .env
exit=1
\### docker-compose.yml with only GATEWAY_API_KEY missing
error while interpolating services.gateway.environment.GATEWAY_API_KEY: required variable GATEWAY_API_KEY is missing a value: GATEWAY_API_KEY is required for the public TLS deployment - clients authenticate with Authorization Bearer
exit=1
\### docker-compose.yml with GATEWAY_API_KEY set to empty string
error while interpolating services.inputlayer.environment.INPUTLAYER_ADMIN_PASSWORD: required variable INPUTLAYER_ADMIN_PASSWORD is missing a value: Set INPUTLAYER_ADMIN_PASSWORD (at least 12 characters) in .env
exit=1
\### docker-compose.yml with all three set
      GATEWAY_API_KEY: gw-0123456789ab
      INPUTLAYER_API_KEY: key-0123456789ab
      INPUTLAYER_ADMIN_PASSWORD: pw-0123456789ab
      INPUTLAYER_BOOTSTRAP_API_KEY: key-0123456789ab
exit=0
\### docker-compose-no-tls.yml with no variables set
error while interpolating services.inputlayer.environment.INPUTLAYER_ADMIN_PASSWORD: required variable INPUTLAYER_ADMIN_PASSWORD is missing a value: Set INPUTLAYER_ADMIN_PASSWORD (at least 12 characters) in .env
exit=1
\### docker-compose-no-tls.yml with only INPUTLAYER_ADMIN_PASSWORD missing
error while interpolating services.inputlayer.environment.INPUTLAYER_ADMIN_PASSWORD: required variable INPUTLAYER_ADMIN_PASSWORD is missing a value: Set INPUTLAYER_ADMIN_PASSWORD (at least 12 characters) in .env
exit=1
\### docker-compose-no-tls.yml with only INPUTLAYER_BOOTSTRAP_API_KEY missing
error while interpolating services.inputlayer.environment.INPUTLAYER_BOOTSTRAP_API_KEY: required variable INPUTLAYER_BOOTSTRAP_API_KEY is missing a value: Set INPUTLAYER_BOOTSTRAP_API_KEY (at least 12 characters) in .env
exit=1
\### docker-compose-no-tls.yml with only GATEWAY_API_KEY missing
error while interpolating services.gateway.environment.GATEWAY_API_KEY: required variable GATEWAY_API_KEY is missing a value: GATEWAY_API_KEY is required - clients authenticate with Authorization Bearer
exit=1
\### docker-compose-no-tls.yml with GATEWAY_API_KEY set to empty string
error while interpolating services.inputlayer.environment.INPUTLAYER_ADMIN_PASSWORD: required variable INPUTLAYER_ADMIN_PASSWORD is missing a value: Set INPUTLAYER_ADMIN_PASSWORD (at least 12 characters) in .env
exit=1
\### docker-compose-no-tls.yml with all three set
      GATEWAY_API_KEY: gw-0123456789ab
      INPUTLAYER_API_KEY: key-0123456789ab
      INPUTLAYER_ADMIN_PASSWORD: pw-0123456789ab
      INPUTLAYER_BOOTSTRAP_API_KEY: key-0123456789ab
exit=0
(note: the two 'GATEWAY_API_KEY set to empty string' headings above are mislabelled: that run exported INPUTLAYER_ADMIN_PASSWORD='' alongside a full .env, proving an explicitly empty value is refused too)
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 credentials

\### S7a: BASE engine (8b574ef2), fresh data dir, INPUTLAYER_ADMIN_PASSWORD='' as ${VAR:-} delivered it
launcher rc=0
--- login admin with BLANK password on base engine:
<- {"type":"authenticated","session_id":"b5f6ddb3-634b-4d24-b17f-be0c014e73d1","knowledge_graph":"default","version":"0.1.0","role":"admin","protocol_version":3,"stream_epoch":"521d52f8054c2a31"}

\### S7b: upgrade the same data dir to the FIXED engine (admin already stored with blank password)
launcher rc=0 (0 = started)
--- login admin with BLANK password on fixed engine:
<- {"type":"auth_error","message":"Invalid credentials"}
Evidence: Binary-level bootstrap secret tests
test short_supplied_secret_refuses_startup ... ok
test short_supplied_secret_is_ignored_with_a_warning_once_admin_exists ... ok
test blank_supplied_secrets_are_generated_and_saved ... ok
test config_password_overridden_by_env_is_ignored_on_first_boot ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.77s
Evidence: WebSocket driver used for logins
// usage: node ws.mjs PORT USER PASSWORD [program...]
const [port, user, password, ...programs] = process.argv.slice(2);
const ws = new WebSocket(`ws://127.0.0.1:${port}/ws`);
const out = (m) => console.log(m);
let step = 0;
ws.onopen = () => ws.send(JSON.stringify({ type: "login", username: user, password }));
ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  out(`<- ${JSON.stringify(msg).slice(0, 300)}`);
  if (msg.type === "error" && step === 0) { ws.close(); return; }
  if (msg.type === "authenticated" || (step > 0 && msg.type !== "hello")) {
    if (step < programs.length) { out(`-> ${programs[step]}`); ws.send(JSON.stringify({ type: "execute", program: programs[step] })); step++; }
    else ws.close();
  }
};
ws.onclose = () => process.exit(0);
setTimeout(() => { out("timeout"); process.exit(1); }, 15000);

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 boot bootstrap_secrets checks env and config passwords with the same password_use. A short http.auth.bootstrap_admin_password therefore refuses startup even when a strong INPUTLAYER_ADMIN_PASSWORD is set, although the env value is the one stored (env_password.or(config_password)). Example: config.toml still has bootstrap_admin_password = &#34;admin&#34; 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.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Operator boots a fresh engine with blank INPUTLAYER_ADMIN_PASSWORD and INPUTLAYER_BOOTSTRAP_API_KEY: random 64-hex secrets are generated and saved to credentials.toml, and admin login with a blank pas… ✅ pass live s1-blank-env.txt
Adversarial: the base engine accepts blank admin login (P0 reproduced); after upgrading that same data directory to the fixed engine, it starts and rejects blank admin login ✅ pass live s7-legacy-blank-admin-upgrade.txt
Fresh engine with a short INPUTLAYER_ADMIN_PASSWORD ('admin', 'eleven-char') or short INPUTLAYER_BOOTSTRAP_API_KEY refuses to start, the error names the setting, and no credentials are written ✅ pass live s2-weak-first-boot.txt
Boundary: a first boot with the exactly-12-character password 'twelve-chars' starts, and admin can sign in with it ✅ pass live s3-existing-deployment-weak.txt
An existing deployment restarted with weak env secrets still starts, logs a WARNING for each weak value, and the weak value can't be used to sign in ✅ pass live s3-existing-deployment-weak.txt
Admin runs .user create / .user password with a short password: rejected with 'Password too short: use at least 12 characters'; 12+ characters succeeds ✅ pass live s4-user-commands.txt
Recorded decision: config bootstrap_admin_password="admin" plus a strong env password on a fresh data dir starts with a NOTE log line, admin uses the env password, and 'admin' is rejected; without the… ✅ pass live s5-config-overridden-by-env.txt
docker compose refuses to render docker-compose.yml and docker-compose-no-tls.yml when INPUTLAYER_ADMIN_PASSWORD, INPUTLAYER_BOOTSTRAP_API_KEY or GATEWAY_API_KEY is unset or empty, and renders them wh… ✅ pass live s6-compose-required-vars.txt
  • cargo build --bin inputlayer-server (target) and the same build of git archive 8b574ef2 (base), into a separate temp target directory
  • Live first boot with INPUTLAYER_ADMIN_PASSWORD='' / INPUTLAYER_BOOTSTRAP_API_KEY='', then WS login with a blank password and with the generated one
  • Live 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-0456
  • Live first boot with config.toml bootstrap_admin_password="admin" plus a strong INPUTLAYER_ADMIN_PASSWORD; and the same config without the env password
  • Base 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 &lt;tmp&gt; -f docker-compose.yml config and the same for docker-compose-no-tls.yml, with no vars, each var missing, an empty var, and all vars set
  • cargo test --test bootstrap_secret_tests
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

jsam added 6 commits October 5, 2026 06:15
…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
@jsam
jsam merged commit edb8bc3 into main Oct 5, 2026
21 checks passed
@jsam
jsam deleted the fm/fix-empty-admin-pw-v2 branch October 5, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant