-
Notifications
You must be signed in to change notification settings - Fork 0
worker credentials
The worker bridges platform-mounted Kubernetes Secret values into the in-process keychain map that tools' ctx.get_secret(alias) calls read. Shipped in noetl-worker 5.7.0 (noetl/worker#35); closes noetl/ai-meta#34.
Per agents/rules/execution-model.md, business-logic credentials (bearer tokens, API keys, DSNs, signing keys) live in the NoETL keychain and are referenced by alias from playbook config. This page describes how the worker honours that convention end-to-end for the env-mounted case (the typical k8s deployment shape).
# Worker pod spec
env:
- name: NOETL_KEYCHAIN_ENV_VARS
value: NOETL_FLIGHT_BEARER_TOKEN,DUFFEL_API_KEY,SLACK_BOT_TOKEN
envFrom:
- secretRef:
name: noetl-worker-credentials # provides the three vars above# Playbook step references each by env-var name as the keychain alias
- step: fetch_secure
tool:
kind: result_fetch
bearer_token: NOETL_FLIGHT_BEARER_TOKENAt worker startup, CommandExecutor::new reads the comma-separated allow-list + lifts each named env var into the per-executor keychain map. Each command's ExecutionContext.secrets gets seeded from that map before tool dispatch. Tools call ctx.get_secret("NOETL_FLIGHT_BEARER_TOKEN") and get the env-mounted value back.
Two design alternatives:
| Approach | Shape | Trade-off |
|---|---|---|
Prefix-strip (NOETL_SECRET_* → secrets[*]) |
Operator names env vars with a fixed prefix; worker scans + strips. | Forces a renaming convention; playbook references the stripped name (mismatch with the Secret key name). |
Allow-list (NOETL_KEYCHAIN_ENV_VARS=A,B,C → secrets[A]+secrets[B]+secrets[C]) |
Operator explicitly lists which env vars are credentials. | Two env vars to manage (allow-list + the value vars), but env-var name matches the alias verbatim. |
The worker uses the allow-list approach because:
- The operator owns naming. A k8s Secret with key
NOETL_FLIGHT_BEARER_TOKENbecomes an env var with the same name, which is the exact alias the playbook references. No prefix-strip transformation, no mismatch to debug. - Operator-controlled scope. Only the listed vars become secrets; everything else (
PATH,HOSTNAME,NATS_URL, runtime config) stays out of the secrets map. No accidental dumping. - Empty / unset allow-list ⇒ pre-#35 deployments keep working unchanged.
| Var | Type | Meaning |
|---|---|---|
NOETL_KEYCHAIN_ENV_VARS |
Comma-separated list of env-var names | Allow-list. Empty / unset ⇒ no env vars get lifted (keychain map is empty). |
| Each listed name | String | The credential value. Typically populated via envFrom: secretRef so the literal stays in k8s, never in deployment manifests. |
Parsing tolerates:
-
Whitespace —
" A , B"parses to{A, B}. -
Empty entries —
"A,,B,"parses to{A, B}. - Allow-listed but unset env vars — silently skipped. An operator can stage rollouts (define the allow-list ahead of mounting the Secret, or vice versa) without startup spam.
- Empty string values — rejected. Distinguishing "unset" from "set to empty string" is a common deployment-time surprise; both shapes are skipped. Otherwise a Secret with a blank field would silently authenticate as an empty token, which is worse than failing closed.
At startup, when the allow-list is non-empty, the worker logs the key names (not values):
INFO Loaded keychain credentials from NOETL_KEYCHAIN_ENV_VARS
count=2 aliases=["NOETL_FLIGHT_BEARER_TOKEN", "DUFFEL_API_KEY"]
Per agents/rules/observability.md Principle 3 — never log credential values; log enough that an operator can verify the allow-list took effect via kubectl logs.
The keychain map seeded at worker startup acts as a default. Per-command secrets from the playbook step (auth: block, future per-step credential injection) layer on top — env-mounted entries can be overridden. This matches the postgres-tool credential pattern that's been in noetl-tools since R-1.x.
For GKE / production, the typical shape is:
- k8s Secret (created via secret manager, cert-manager, or static manifest) carries the credential values.
-
Deployment mounts the Secret via
envFrom. -
Deployment sets
NOETL_KEYCHAIN_ENV_VARSenv var pointing at the names the Secret carries. - Playbook references each credential by its env-var name as the alias.
The Phase C2 kind validation rig (automation/development/generate-flight-tls.sh + validate-flight-tls.sh in noetl/ops) is the worked example — it generates a fresh CA + server cert + client cert + bearer token, creates the Secrets, patches the deployments to add NOETL_KEYCHAIN_ENV_VARS=NOETL_FLIGHT_BEARER_TOKEN, and the result_fetch.bearer_token: NOETL_FLIGHT_BEARER_TOKEN playbook field resolves to the actual token at runtime.
-
src/executor/command.rs—KEYCHAIN_ENV_ALLOWLIST_VARconst,load_keychain_env_allowlist()helper,CommandExecutor::keychain_envfield, per-commandctx.set_secret(...)seed loop.
-
noetl-executor adoption — how the worker dispatches to
noetl-tools::ToolRegistry;ctx.get_secretis the consumer side of the credential map. -
agents/rules/execution-model.md— secrets + credentials rule (the boundary discipline this page implements).