Skip to content

worker credentials

Kadyapam edited this page Jun 1, 2026 · 3 revisions

Worker Credentials — keychain env-var allow-list

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).

TL;DR

# 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_TOKEN

At 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.

Why an allow-list instead of a prefix

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,Csecrets[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_TOKEN becomes 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.

Env-var contract

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.

Observability

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.

Layering with playbook-step credentials

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.

Production deployment shape

For GKE / production, the typical shape is:

  1. k8s Secret (created via secret manager, cert-manager, or static manifest) carries the credential values.
  2. Deployment mounts the Secret via envFrom.
  3. Deployment sets NOETL_KEYCHAIN_ENV_VARS env var pointing at the names the Secret carries.
  4. 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.

Source

  • src/executor/command.rsKEYCHAIN_ENV_ALLOWLIST_VAR const, load_keychain_env_allowlist() helper, CommandExecutor::keychain_env field, per-command ctx.set_secret(...) seed loop.

Related

Clone this wiki locally