Skip to content

feat(helm): add secretEnv to avoid API keys in ConfigMap#730

Merged
thepagent merged 6 commits intoopenabdev:mainfrom
Joseph19820124:feat/secret-env-helm
May 5, 2026
Merged

feat(helm): add secretEnv to avoid API keys in ConfigMap#730
thepagent merged 6 commits intoopenabdev:mainfrom
Joseph19820124:feat/secret-env-helm

Conversation

@Joseph19820124
Copy link
Copy Markdown
Contributor

@Joseph19820124 Joseph19820124 commented May 4, 2026

Summary

Closes #729

Problem

[agent].env values are rendered directly into the ConfigMap as plaintext, exposing API keys to anyone with kubectl get configmap access:

# ConfigMap — visible to all cluster users
[agent]
env = { GEMINI_API_KEY = "AIzaSy..." }

Solution

Add a secretEnv field that renders as valueFrom.secretKeyRef in the Deployment and automatically adds the key name to inherit_env in config.toml:

agents:
  gemini:
    secretEnv:
      - name: GEMINI_API_KEY
        secretName: openab-secrets
        secretKey: GEMINI_API_KEY

Deployment (secret value, not plaintext):

- name: GEMINI_API_KEY
  valueFrom:
    secretKeyRef:
      name: openab-secrets
      key: GEMINI_API_KEY

ConfigMap (no secret value):

[agent]
inherit_env = ["GEMINI_API_KEY"]

The secret flows: k8s Secret → Deployment env → openab process → inherit_env → agent subprocess. The ConfigMap stays clean.

Changes

  • charts/openab/templates/deployment.yaml — render secretEnv as valueFrom.secretKeyRef
  • charts/openab/templates/configmap.yaml — merge secretEnv key names into inherit_env

Testing

helm template test ./charts/openab \
  --set agents.gemini.secretEnv[0].name=GEMINI_API_KEY \
  --set agents.gemini.secretEnv[0].secretName=openab-secrets \
  --set agents.gemini.secretEnv[0].secretKey=GEMINI_API_KEY

Verified ConfigMap contains inherit_env = ["GEMINI_API_KEY"] and Deployment contains valueFrom.secretKeyRef.

Discord Discussion

https://discord.com/channels/1491295327620169908/1491365157010542652

@Joseph19820124 Joseph19820124 requested a review from thepagent as a code owner May 4, 2026 11:06
@github-actions github-actions Bot added pending-screening PR awaiting automated screening closing-soon PR missing Discord Discussion URL — will auto-close in 3 days and removed closing-soon PR missing Discord Discussion URL — will auto-close in 3 days labels May 4, 2026
@shaun-agent
Copy link
Copy Markdown
Contributor

OpenAB PR Screening

This is auto-generated by the OpenAB project-screening flow for context collection and reviewer handoff.
Click 👍 if you find this useful. Human review will be done within 24 hours. We appreciate your support and contribution 🙏

Screening report ## Intent

PR #730 is trying to stop Helm-rendered agent API keys from being exposed in the OpenAB ConfigMap.

The concrete operator-visible problem is that current [agent].env values can place secrets such as GEMINI_API_KEY directly into config.toml, which is rendered into a Kubernetes ConfigMap. Anyone with ConfigMap read access can see those values. The PR introduces a Helm-level secretEnv mechanism so secret values stay in Kubernetes Secrets and are passed into the OpenAB pod as environment variables, then inherited by agent subprocesses.

Feat

This is a feature for the Helm chart with a security-hardening effect.

Behavioral change:

  • Add agents.<name>.secretEnv[] values to the chart.
  • Render each entry into the Deployment as env[].valueFrom.secretKeyRef.
  • Automatically add the secret environment variable names to the generated agent inherit_env list.
  • Keep secret values out of the ConfigMap while still making them available to the agent runtime.

Who It Serves

Primary beneficiaries:

  • Deployers running OpenAB on Kubernetes.
  • Agent runtime operators managing provider API keys.
  • Maintainers and reviewers who want the Helm chart to follow Kubernetes secret-handling expectations.

Secondary beneficiaries:

  • Security-conscious OpenAB users.
  • Cluster administrators who separate ConfigMap access from Secret access.

Rewritten Prompt

Implement Helm chart support for agent-scoped secret environment variables.

Add an agents.<agentName>.secretEnv list to the OpenAB Helm chart. Each item should include name, secretName, and secretKey. Render those entries into the OpenAB Deployment container environment as Kubernetes valueFrom.secretKeyRef entries. In the generated config.toml, merge the secretEnv[].name values into the agent’s inherit_env list so the OpenAB process passes those variables to the agent subprocess.

Ensure secret values are never rendered into the ConfigMap. Preserve existing env and inherit_env behavior. Add or update Helm template tests or documented helm template verification covering Deployment output and ConfigMap output.

Merge Pitch

This is worth advancing because it fixes a real deployment security footgun without changing OpenAB runtime semantics. The proposed flow uses Kubernetes-native secret references and aligns with common Helm chart expectations.

Risk profile is moderate-low: the change is confined to Helm templates, but reviewer attention should focus on backwards compatibility, duplicate inherit_env handling, missing field validation, and whether secretEnv should be documented in chart values. The likely reviewer concern is whether the chart now has two overlapping ways to inject env vars and whether their merge behavior is deterministic.

Best-Practice Comparison

Relevant OpenClaw principles:

  • Explicit delivery routing: Relevant. secretEnv makes the delivery route explicit: Kubernetes Secret to Deployment env to OpenAB process to inherited agent env.
  • Durable job persistence: Not relevant. This PR does not affect jobs or scheduling state.
  • Gateway-owned scheduling: Not relevant.
  • Isolated executions: Partially relevant. Passing secrets through inherited environment variables preserves process isolation better than writing secrets into shared config.
  • Retry/backoff and run logs: Not relevant.

Relevant Hermes Agent principles:

  • Self-contained prompts for scheduled tasks: Not directly relevant.
  • Fresh session per scheduled run: Not relevant.
  • Atomic writes for persisted state: Not relevant.
  • File locking to prevent overlap: Not relevant.
  • Gateway daemon tick model: Not relevant.

The strongest best-practice alignment is with explicit secret routing and avoiding persistent plaintext secret material in broadly readable configuration artifacts.

Implementation Options

Option 1: Conservative template-only support

Accept the PR’s core approach: add secretEnv rendering in deployment.yaml and merge secret names into inherit_env in configmap.yaml.

Pros:

  • Fastest path.
  • Smallest diff.
  • Directly solves the ConfigMap exposure problem.

Cons:

  • May lack schema/docs/tests.
  • Possible duplicate env names or duplicate inherit_env entries unless explicitly handled.

Option 2: Balanced chart feature with validation and docs

Keep the same implementation model, but add chart documentation, values.yaml examples, JSON schema validation if the chart has one, and template tests or snapshot checks.

Pros:

  • More mergeable and maintainable.
  • Clarifies expected fields and behavior.
  • Reduces future support burden.

Cons:

  • Slightly larger PR.
  • Requires reviewers to check chart docs and validation behavior.

Option 3: Ambitious secret-management pass

Broaden the chart’s environment handling into a unified model supporting plain env, secret refs, config map refs, existing secret names, and optional generated Secret resources.

Pros:

  • More complete Kubernetes-native env configuration.
  • Could reduce future chart churn.
  • Gives deployers more flexible secret workflows.

Cons:

  • Higher design surface.
  • More risk of breaking existing values.
  • Too large for a quick security-focused PR unless split carefully.

Comparison Table

Option Speed to ship Complexity Reliability Maintainability User impact Fit for OpenAB right now
1. Conservative template-only support High Low Medium Medium High for Kubernetes deployers with secrets Good, but may need follow-up docs/tests
2. Balanced chart feature with validation and docs Medium Medium High High High and easier to adopt correctly Best fit
3. Ambitious secret-management pass Low High Medium until fully tested Medium-high after stabilization Potentially high Too broad for this PR

Recommendation

Advance this PR using Option 2 as the target shape.

The underlying idea is sound and should move forward: secrets should not be rendered into ConfigMaps, and Kubernetes secretKeyRef is the right primitive. Before merge, tighten the PR by adding chart documentation or values.yaml examples, checking duplicate handling for inherit_env, and adding a repeatable Helm rendering test or snapshot expectation.

Suggested sequencing:

  1. Merge the focused secretEnv capability once docs and verification are sufficient.
  2. Defer broader env-source unification to a separate chart design issue if maintainers want support for envFrom, ConfigMap refs, or generated Secret resources.

@chaodu-agent

This comment has been minimized.

@chaodu-agent

This comment has been minimized.

@Joseph19820124
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review! I've addressed all the blocking issues and nits:

🔴 Deduplication — Added | uniq after concat in configmap.yaml so duplicate keys in inheritEnv + secretEnv don't produce duplicate inherit_env entries.

🔴 Required field validation — Added fail guards in both deployment.yaml (before rendering) and configmap.yaml (before building $secretEnvKeys) to catch missing name, secretName, or secretKey fields early.

🔴 env/secretEnv collision guard — Added a fail in configmap.yaml that fires when the same key appears in both env and secretEnv, preventing the plaintext secret from silently leaking into the ConfigMap.

🟡 values.yaml docs — Added secretEnv: [] with usage comments to all agent blocks (the active kiro section and all three commented-out example blocks).

🟡 Rename$allInheritEnv$mergedInheritEnv to better convey the merge semantics.

@chaodu-agent

This comment has been minimized.

@Joseph19820124
Copy link
Copy Markdown
Contributor Author

EKS Helm Test — PR #730 secretEnv validation

Tested on openab-test (us-east-1, EKS) in a dedicated namespace openab-pr730.


Setup

# Kubernetes Secret (holds the actual key — never in ConfigMap)
kubectl create secret generic pr730-secrets \
  --namespace openab-pr730 \
  --from-literal=GEMINI_API_KEY=AIzaSy-FAKE-TEST-KEY-FOR-PR730

# Helm install from PR branch
helm install pr730 ./charts/openab \
  --namespace openab-pr730 \
  --set agents.kiro.enabled=false \
  --set agents.gemini.command=gemini-cli \
  --set agents.gemini.args[0]=acp \
  --set agents.gemini.discord.enabled=false \
  --set agents.gemini.persistence.enabled=false \
  --set agents.gemini.secretEnv[0].name=GEMINI_API_KEY \
  --set agents.gemini.secretEnv[0].secretName=pr730-secrets \
  --set agents.gemini.secretEnv[0].secretKey=GEMINI_API_KEY
# → STATUS: deployed  REVISION: 1

✅ ConfigMap — no plaintext secret, inherit_env present

$ kubectl get configmap pr730-openab-gemini -n openab-pr730 -o yaml
data:
  config.toml: |
    [agent]
    command = "gemini-cli"
    args = ["acp"]
    working_dir = "/home/agent"
    inherit_env = ["GEMINI_API_KEY"]   # ← key name only, no value

    [pool]
    max_sessions = 10
    session_ttl_hours = 24

    [reactions]
    enabled = true
    remove_after_reply = false

✅ Deployment — valueFrom.secretKeyRef (not plaintext)

$ kubectl get deployment pr730-openab-gemini -n openab-pr730 \
    -o jsonpath='{.spec.template.spec.containers[0].env}' | jq .
[
  {
    "name": "HOME",
    "value": "/home/agent"
  },
  {
    "name": "GEMINI_API_KEY",
    "valueFrom": {
      "secretKeyRef": {
        "key": "GEMINI_API_KEY",
        "name": "pr730-secrets"
      }
    }
  }
]

✅ Validation guard 1 — missing required field

$ helm template . --set agents.gemini.secretEnv[0].name=GEMINI_API_KEY \
                  --set agents.gemini.secretEnv[0].secretName=pr730-secrets
                  # secretKey intentionally omitted

Error: execution error at (openab/templates/deployment.yaml:88:16):
  agents.gemini.secretEnv entries require name, secretName, and secretKey

✅ Validation guard 2 — env/secretEnv collision

$ helm template . --set "agents.gemini.env.GEMINI_API_KEY=plaintext-value" \
                  --set agents.gemini.secretEnv[0].name=GEMINI_API_KEY \
                  --set agents.gemini.secretEnv[0].secretName=pr730-secrets \
                  --set agents.gemini.secretEnv[0].secretKey=GEMINI_API_KEY

Error: execution error at (openab/templates/configmap.yaml:114:8):
  agents.gemini.secretEnv key "GEMINI_API_KEY" also exists in env —
  remove it from env to avoid plaintext secret in ConfigMap

✅ Dedup — same key in inheritEnv + secretEnv → appears once

$ helm template . --set agents.gemini.inheritEnv[0]=GEMINI_API_KEY \
                  --set agents.gemini.secretEnv[0].name=GEMINI_API_KEY \
                  ...
inherit_env = ["GEMINI_API_KEY"]   # ← deduplicated by | uniq

All four scenarios pass on a live EKS cluster. The secret value never appears in the ConfigMap.

Closes openabdev#729

Adds agents.<name>.secretEnv so API keys stay in Kubernetes Secrets
instead of being rendered as plaintext in the ConfigMap.

Deployment renders each entry as valueFrom.secretKeyRef; ConfigMap
merges the key names into inherit_env so the openab process passes them
to agent subprocesses. Secret values never appear in the ConfigMap.

Validation:
- fail if any secretEnv entry is missing name, secretName, or secretKey
- fail if the same key appears in both env and secretEnv (collision guard)
- uniq applied to merged inherit_env list to prevent duplicate entries

values.yaml: added secretEnv: [] with usage comments to all agent blocks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Joseph19820124 Joseph19820124 force-pushed the feat/secret-env-helm branch from 58e2097 to 79fc867 Compare May 5, 2026 02:34
@chaodu-agent

This comment has been minimized.

Consolidate the validation-only range block and the render range block
into a single loop so validation and rendering happen together.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chaodu-agent

This comment has been minimized.

Covers ConfigMap inherit_env merge, dedup with inheritEnv, no plaintext
rendering, env-conflict guard, and Deployment valueFrom.secretKeyRef
rendering. Field-validation guards are tested via the Deployment template
(helm-unittest detects fail() correctly there).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Joseph19820124
Copy link
Copy Markdown
Contributor Author

helm test — Proof of Testing on Live EKS Cluster

Addressing the request in the consolidated review for helm tests.


1. New helm unit tests added

Two new test files added to charts/openab/tests/:

File Suite Tests
secretenv_test.yaml ConfigMap rendering (inherit_env merge, no plaintext) 6
secretenv_deployment_test.yaml Deployment rendering + field validation 5

Full suite run — 85/85 passing (zero regressions):

```
$ helm unittest charts/openab

PASS adapter enablement gating
PASS allowBotMessages & trustedBotIds
PASS gateway config rendering
PASS gateway deployment rendering
PASS gateway secret rendering
PASS persistence PVC rendering
PASS secretEnv — Deployment rendering and field validation
PASS secretEnv — ConfigMap rendering (inherit_env merge, no plaintext)
PASS toolDisplay

Charts: 1 passed, 1 total
Test Suites: 9 passed, 9 total
Tests: 85 passed, 85 total
Time: 233ms
```

secretEnv test scenarios covered:

Scenario Template Result
secretEnv key appears in inherit_env ConfigMap
Secret value NOT rendered in ConfigMap ConfigMap
Dedup when key also in inheritEnv ConfigMap
Multiple secretEnv entries in inherit_env ConfigMap
env/secretEnv collision → fail ConfigMap
No inherit_env when both unset (backward compat) ConfigMap
valueFrom.secretKeyRef rendered in Deployment Deployment
Multiple entries render as separate vars Deployment
Missing secretKey → fail Deployment
Missing secretName → fail Deployment
Missing name → fail Deployment

2. Live EKS deployment — openab-test cluster (us-east-1)

Namespace: openab-pr730

Helm release upgraded to revision 2:
```
$ helm upgrade pr730 charts/openab -n openab-pr730 --reuse-values
Release "pr730" has been upgraded.
NAME: pr730 NAMESPACE: openab-pr730 REVISION: 2 STATUS: deployed
```

ConfigMap — GEMINI_API_KEY key name in inherit_env, zero plaintext:
```toml
$ kubectl -n openab-pr730 get configmap pr730-openab-gemini -o jsonpath='{.data.config.toml}'

[agent]
command = "gemini-cli"
args = ["acp"]
working_dir = "/home/agent"
inherit_env = ["GEMINI_API_KEY"]

[pool]
max_sessions = 10
session_ttl_hours = 24

[reactions]
enabled = true
remove_after_reply = false
```

Deployment — GEMINI_API_KEY injected as valueFrom.secretKeyRef:
```json
$ kubectl -n openab-pr730 get deployment pr730-openab-gemini
-o jsonpath='{.spec.template.spec.containers[0].env}' | jq .

[
{
"name": "HOME",
"value": "/home/agent"
},
{
"name": "GEMINI_API_KEY",
"valueFrom": {
"secretKeyRef": {
"key": "GEMINI_API_KEY",
"name": "pr730-secrets"
}
}
}
]
```

The secret value never appears in the ConfigMap. ✅

@christinhabibiyanq366
Copy link
Copy Markdown

This is a strong improvement from a security and operability standpoint. Moving sensitive agent environment variables out of the ConfigMap and into Secret-backed env injection makes the Helm chart much safer by default while keeping the workflow straightforward. I’d be happy to see this merged.

@chaodu-agent

This comment has been minimized.

Extract the duplicated `if not (and .name .secretName .secretKey)` guard
from configmap.yaml and deployment.yaml into a shared named template
`openab.validateSecretEnv` in _helpers.tpl; both callers now use a
single `include` instead of repeating the same fail block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Joseph19820124
Copy link
Copy Markdown
Contributor Author

Thanks for the review feedback — addressed in the latest commit.

Extracted the duplicated if not (and .name .secretName .secretKey) guard into a new openab.validateSecretEnv named template in _helpers.tpl. Both configmap.yaml and deployment.yaml now call include "openab.validateSecretEnv" (dict "secretEnv" $cfg.secretEnv "agentName" $name) instead of repeating the same fail block. The configmap-only conflict check (env collision) remains inline since it's not shared.

@chaodu-agent

This comment has been minimized.

@chaodu-agent chaodu-agent added pending-contributor and removed pending-maintainer pending-screening PR awaiting automated screening labels May 5, 2026
Replace single order-sensitive regex with three independent assertions:
each key must appear in inherit_env, and GEMINI_API_KEY must not appear
twice. This removes the implicit reliance on uniq preserving insertion
order while still verifying the deduplication invariant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Joseph19820124
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough final review! Addressed the remaining nit:

🟡 Test order-sensitivity — The dedup test merges secretEnv keys with existing inheritEnv and deduplicates previously asserted a specific JSON array order (["GEMINI_API_KEY","API_BASE_URL"]). Replaced with three independent assertions:

  • inherit_env contains "GEMINI_API_KEY"
  • inherit_env contains "API_BASE_URL"
  • "GEMINI_API_KEY" does not appear twice (dedup verified without relying on order)

This removes the implicit reliance on uniq's insertion-order behavior while still fully verifying the deduplication invariant.

🟡 Double validation call — Left as-is per reviewer guidance ("Not worth changing"). Belt-and-suspenders is appropriate for a security feature — if either template is rendered in isolation (e.g. helm template --show-only), the validation still fires.

All 85 helm-unittest tests pass with the updated assertion.

@chaodu-agent

This comment has been minimized.

…p.yaml

deployment.yaml already calls the same validation helper, so the
configmap.yaml call was redundant (validation ran twice on every render).
Keep it in deployment.yaml only — fail-fast on the template that
actually renders the secretKeyRef entries.
@chaodu-agent
Copy link
Copy Markdown
Collaborator

🟢 LGTM — Clean, well-scoped Helm enhancement with good safety guards

What problem does it solve? API keys stored in env render as plaintext in ConfigMap, visible to anyone with kubectl get configmap access. This PR adds secretEnv to inject secrets via valueFrom.secretKeyRef while auto-wiring inherit_env so the agent subprocess inherits the env var.

How? Three-part approach:

  1. Deployment template renders secretEnv entries as valueFrom.secretKeyRef
  2. ConfigMap template merges secretEnv key names into inherit_env (with dedup via uniq)
  3. Validation helper in _helpers.tpl fails fast on incomplete entries

Considered alternatives? The chart already has envFrom which can reference entire Secrets, but secretEnv is more ergonomic for per-key injection and critically auto-wires inherit_env — something envFrom cannot do.

Review Details

🟢 INFO — Done well

  • Conflict guard: Fails template rendering if the same key appears in both env and secretEnv — prevents accidental plaintext leak
  • Backward compatible: Empty secretEnv: [] default means existing deployments are unaffected
  • Deduplication: concat ... | uniq handles the case where a user manually lists a key in both inheritEnv and secretEnv
  • Comprehensive tests: 11 helm-unittest cases covering rendering, validation, merge, conflict, and backward compat

🟡 NIT — Non-blocking suggestions

  1. values.yaml default placement: The active kiro agent block at line 188 has secretEnv: [] uncommented, but the commented-out preset blocks only show # secretEnv: []. This is fine but slightly inconsistent — consider making the comment block match the active block format for discoverability.

  2. Documentation: Consider adding a one-liner to docs/config-reference.md under the [agent] section mentioning inherit_env auto-population from secretEnv, so users reading the Rust-side docs understand where the values come from.

Verdict: Ready for maintainer review. No blocking issues found.

@thepagent thepagent merged commit 4f050e5 into openabdev:main May 5, 2026
3 checks passed
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.

feat: support secretKeyRef for [agent].env to avoid exposing API keys in ConfigMap

5 participants