feat(controller,ui): experimental Envoy credential injector behind per-instance opt-in flag#346
Conversation
First slice of the ADR-033 rollout. Add a per-instance opt-in flag (`experimentalCredentialInjector`) that, when enabled, replaces OneCLI's egress path for that pod with an Envoy sidecar. - Controller: branch BuildStatefulSet on the flag — render a per-instance Envoy bootstrap ConfigMap, mount owner-scoped credential Secrets into the sidecar only, drop the agent's `ONECLI_ACCESS_TOKEN`, point `HTTP(S)_PROXY` at `127.0.0.1:<EnvoyPort>`, hard-code `automountServiceAccountToken: false` and `shareProcessNamespace: false` per ADR-033 threat model. NetworkPolicy drops the OneCLI peer and allows TCP 443/80 egress when the flag is on. - API server: dual-write user-typed secrets (generic + Anthropic) to K8s Secrets labelled with the owner's sub. OneCLI write path unchanged; existing OneCLI-only secrets are not migrated — flagged instances only see secrets created after this lands. - UI: checkbox in Add Agent dialog (configure step) and a new Experimental section in the configuration panel for toggling on existing instances. - Helm: `controller.envoyImage` / `controller.envoyPort` defaults. OAuth app connections, HITL, refresh-token loop, gVisor enforcement, and OneCLI removal stay out of scope per the issue. Closes #337 Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
The K8s mirror used a non-existent `headerPrefix` field on `InjectionConfig`
(the real field is `valueFormat: "Bearer {value}"`). Two consequences:
1. Anthropic api-key secrets were mirrored with `Authorization: Bearer <key>`
instead of `x-api-key: <key>`, so the upstream would reject them.
2. Generic secrets with a custom `valueFormat` (e.g. `Token {value}`) were
ignored — every mirrored secret got `Bearer <value>` regardless.
Fix:
- Replace `headerPrefix` with the actual `valueFormat` template, applied
via `{value}` substitution before writing the credential file.
- Special-case Anthropic in `resolveInjection`: read OneCLI's
`metadata.authMode` from the create response and pick `x-api-key`
(api-key) or `Authorization: Bearer` (oauth, default).
- Persist `humr.ai/auth-mode` and `humr.ai/injection-value-format`
annotations so updates can recompute correctly without re-fetching the
injection config.
`mise run check` does not run tsc on api-server; per-package CI catches
this. Add a unit test suite for the K8s port so the contract is locked
in.
Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
…idecar The standard envoyproxy/envoy image runs as root by default, which conflicts with the sidecar's runAsNonRoot: true security context — the container fails to start. envoyproxy/envoy-distroless ships with USER set to a non-root account, so it satisfies the policy. Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
…-host dispatch
Envoy's credential_injector filter rejects virtual-host/route specific
config ("doesn't support virtual host or route specific configurations"),
which the previous bootstrap depended on. Move per-Secret injection into
an envoy.filters.http.composite filter wrapped with ExtensionWithMatcher,
dispatching by :authority. Each Secret becomes one entry in the matcher
map, selecting its own credential_injector instance.
Also add a node id/cluster — the SDS path_config_source for the per-route
credential file requires both, even for file-based sources.
Validated with 'envoy --mode validate' against a rendered bootstrap.
Note: HTTPS-via-CONNECT traffic is encrypted inside the tunnel, so
header injection is currently a no-op for HTTPS upstreams. Adding TLS
interception is tracked as the next slice.
Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
…injector
Closes the two follow-ups from the previous slice:
1. HTTPS injection is no longer a no-op. Envoy now terminates the agent's
TLS using a per-instance leaf cert signed by a cluster-wide MITM CA, runs
credential_injector on the plaintext HTTP, and re-originates upstream TLS
to the real host. SNI-miss requests pass through unmolested via
sni_dynamic_forward_proxy.
2. The fetch-ca-cert init container is no longer required on the experimental
path. The agent's CA volume is now projected from the leaf Secret (only
ca.crt is exposed; tls.key stays in the sidecar — the credential boundary
between agent and sidecar is preserved).
Mechanics:
- Helm: adds a self-signed bootstrap ClusterIssuer, an isCA Certificate that
produces the humr-mitm-ca Secret in cert-manager's
cluster-resource-namespace, and a CA ClusterIssuer that signs leaves.
Gated behind controller.envoyMitm.enabled (default true).
- Controller: cert-manager.io/v1 types vendored; reconciler builds a per-
instance Certificate (DNSNames = deduped Secret host-patterns, signed by
humr-mitm-ca-issuer) and applies via dynamic client. cert-manager produces
the {instance}-envoy-tls Secret asynchronously.
- Envoy bootstrap: outer CONNECT listener tunnels into an internal listener
via envoy.bootstrap.internal_listener; tls_inspector + per-SNI filter
chains terminate TLS using files mounted from the leaf Secret. HCM inside
each chain runs credential_injector + dynamic_forward_proxy, with upstream
TLS validated against the system CA bundle shipped in envoy-distroless.
Verified end-to-end on local k3s:
- Pod boots cleanly (no fetch-ca-cert hang).
- curl https://api.anthropic.com/v1/models from the agent shows MITM cert
issuer 'CN=humr MITM CA' with SAN api.anthropic.com, the request reaches
Anthropic with the injected Authorization header (Anthropic returns 401
with a token-type-specific error, not a 'no key' error).
- curl https://httpbin.org/anything (no Secret) passes through unmodified;
no Authorization header in the echoed request.
The credential file path now points at the SDS DiscoveryResponse the
api-server already writes (sds.yaml key) instead of the raw 'value' file —
path_config_source expects an SDS resource, not bare bytes.
Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
|
🛡️ Humr — Code Review PR #346: feat(controller,ui): experimental Envoy credential injector behind per-instance opt-in flagAuthor: pilartomas | Branch: feat/experimental-credential-injector → main | Changes: +1236 −139 (26 files) SummaryIntroduces an experimental Envoy sidecar credential injector gated behind a per-instance Findings
Documentation Check (doc-drift)minor drift
However, VerdictCOMMENT — The HTTPS-via-CONNECT credential injection no-op is a significant usability gap (credentials silently not injected for HTTPS upstreams on the current slice) that should be communicated more clearly to users enabling the flag; the Review by Humr · automated code guardian |
|
@xjacka the critical finding ("HTTPS-via-CONNECT injection is a no-op") looks stale relative to the current branch tip — could you re-run against Concretely:
Verified end-to-end on local k3s: a Happy to address the warning-level findings ( |
Six findings from #346 review: * secrets-service: replace ad-hoc `console.warn` in mirrorToK8s with a stable token (`k8s-mirror-failed`) and structured payload (op, secretId, error). Log scrapers can now alert on broken K8s mirroring (which silently breaks Envoy injection on the experimental path) without parsing free-form text. * k8s-secrets-port: drop the defensive `.toLowerCase()` from k8sSecretName and validate the ID up-front against RFC 1123. Two IDs differing only in case can no longer silently overwrite each other; an invalid ID throws (caught by mirrorToK8s, not propagated to OneCLI). * controller: when ExperimentalCredentialInjector is on, log a warning if no GitHub credential Secret is attached. The OneCLI GH_TOKEN sentinel is dropped on this path, so without a BYO credential gh/octokit silently lose auth — this surfaces it in operator logs. * resources_test: TestBuildStatefulSet_FlagOn_AddsEnvoySidecar now uses a non-empty credentialSecrets slice and asserts that volume + mount names match what the bootstrap template references (`cred-<name>`, `/etc/envoy/credentials/<name>`, `/etc/envoy/tls`). * secrets-service.test (new): unit tests verifying create/update/delete resolve successfully when the K8s mirror throws, that the failure is logged with the structured payload, and that the mirror is skipped entirely when k8sPort is undefined. * platform-topology.md: rewrite the credential-isolation invariant to cover both paths (OneCLI MITM, Envoy sidecar with per-instance leaf) and add ADR-033 to Motivated by. The previous wording asserted agents never hold upstream credentials, but elided that the experimental path achieves this differently. Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
|
🛡️ Humr — Code Review PR #346: feat(controller,ui): experimental Envoy credential injector behind per-instance opt-in flagAuthor: pilartomas | Branch: feat/experimental-credential-injector → main | Changes: +578 −239 (35 files) SummaryExtends the experimental Envoy credential injector with full TLS MITM infrastructure: cert-manager CA + ClusterIssuers via Helm chart, per-instance leaf Certificate management in Changes since last reviewPrevious HEAD: 93c3081 (2026-04-28T10:00:00Z) — verdict COMMENT
Findings
Documentation Check (doc-drift)Verdict: Minor drift Both architecture pages are updated and current ( Check 1 — Architecture-page drift: VerdictAPPROVE — the critical HTTPS no-op is fully resolved via the TLS MITM chain; all prior warnings addressed; remaining GH_TOKEN runtime observability gap is minor and documented at the UI layer. Review by Humr · automated code guardian |
Follow-up to the previous review pass — operator-side log warnings weren't
enough; the agent itself needs a signal so GH_TOKEN-aware tooling (gh CLI,
octokit, wrapper scripts) can short-circuit instead of failing on a
mid-request 401.
When ExperimentalCredentialInjector=true:
- Set HUMR_GH_TOKEN_AVAILABLE="true"|"false" on the agent container env.
"true" iff the owner has a credential Secret with host-pattern
github.com or api.github.com (Envoy will inject Authorization on the
wire); "false" otherwise.
- Mirror the same value to a pod annotation
humr.ai/gh-token-available — operators can grep for the missing case
via 'kubectl get pods -o jsonpath="{...annotations.humr\.ai/gh-token-available}"'
without poking inside the container.
Off the experimental path, neither is set (the OneCLI sentinel mechanism
is unchanged). Tests cover both flag-on cases and confirm flag-off stays
clean. security-and-credentials.md documents the signal.
Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
|
🛡️ Humr — Code Review PR #346: feat(controller,ui): experimental Envoy credential injector behind per-instance opt-in flagAuthor: pilartomas | Branch: feat/experimental-credential-injector → main | Changes: +2054 −242 (37 files) SummaryThird review. This commit adds the Helm chart's cert-manager infrastructure (CA Certificate, ClusterIssuers, RBAC, Changes since last reviewPrevious HEAD: 741d070 (2026-04-28T13:00:00Z) — verdict APPROVE
Findings
Documentation Check (doc-drift)Verdict: minor drift Both architecture pages are updated and current ( One remaining gap carried from the previous review: Check 1 — Architecture-page drift: VerdictAPPROVE — the GH_TOKEN observability gap from the previous review is fully resolved; the Helm cert-manager infrastructure is complete; the two new warnings (default Review by Humr · automated code guardian |
Summary
First slice of the ADR-033 rollout. Add a per-instance opt-in flag (
experimentalCredentialInjector) that, when enabled, replaces OneCLI's egress path for that pod with an Envoy sidecar. Existing instances are untouched; off-by-default.User-clarified scope:
add-agent-dialog.tsx(theuseCreateInstancehook has no caller today; the create flow runs throughuseCreateAgent). A live "Experimental" section was added to the configuration panel so users can also toggle the flag on existing instances.What changed
Data model & contract
InstanceSpec.ExperimentalCredentialInjectorInstance/CreateInstanceInput/UpdateInstanceInput+ zodinstances-service→ ConfigMap YAML →parseInfraInstance→assembleInstanceController — pod rendering
BuildStatefulSetbranches on the flag: dropsONECLI_ACCESS_TOKEN+GH_TOKENsentinel, pointsHTTP(S)_PROXYat127.0.0.1:<EnvoyPort>, hard-codesautomountServiceAccountToken: falseandshareProcessNamespace: false(ADR-033 threat model).pkg/reconciler/envoy.go: bootstrap YAML renderer, owner-scoped Secret discovery, sidecar container, sidecar-only volume mounts.BuildNetworkPolicydrops the OneCLI peer and allows TCP 443/80 egress when the flag is on.applyNetworkPolicynow updates on diff so toggles propagate.Envoy bootstrap (TLS interception)
CONNECTand tunnels into an internal listener viaenvoy.bootstrap.internal_listener. The internal listener usestls_inspectorand per-SNI filter chains that terminate the agent's TLS using a per-instance leaf cert, runcredential_injectoron the now-plaintext HTTP, and re-originate upstream TLS to the real host. SNI-miss requests pass through unmolested viasni_dynamic_forward_proxy.credential_injectordoesn't allow per-route config in 1.32, so each TLS-terminating filter chain has its own HCM with its owncredential_injectorinstance — nocompositewrapper needed.envoy-distrolessimage.MITM CA via cert-manager
templates/cert-manager/) bootstrap a self-signedClusterIssuer, anisCA: trueCertificatewhose Secret backshumr-mitm-ca-issuer, and the CAClusterIssueritself. Gated behindcontroller.envoyMitm.enabled(default true).Certificate({instance}-envoy-tls) signed byhumr-mitm-ca-issuer, withdnsNames= the deduped host-pattern list from the owner's credential Secrets. Controller applies it via the dynamic client. cert-manager produces the leaf Secret asynchronously.ca-certvolume on the experimental path is now projected from the leaf Secret withitems: [{key: ca.crt}]— only the CA cert is exposed;tls.keystays in the sidecar's mount, preserving the credential boundary. Thefetch-ca-certinit container is no longer rendered when the flag is on.API server — secrets dual-write
k8s-secrets-port.tsmirrors generic + Anthropic secret writes into K8s Secrets labelled with the owner's sub and host/header annotations. The header prefix is baked into the credential file (Envoy's generic source has no upstream prefix template — see [envoy.filters.http.credential_injector]: Add prefix value to injected credentials envoyproxy/envoy#37001).secrets-servicecalls both ports; OneCLI write unchanged, K8s mirror is best-effort and logged on failure.UI
useCreateAgent→platform.instances.create.mutate.experimental-panel.tsxwired into the configuration panel as an "Experimental" section.Helm
controller.envoyImage(nowenvoyproxy/envoy-distroless:v1.32.0) /controller.envoyPortdefaults; passed through the controller deployment env.controller.envoyMitm.{enabled,bootstrapIssuerName,caIssuerName,caSecretNamespace,caSecretName,caDuration,caRenewBefore,leafDuration,leafRenewBefore}for the cert-manager wiring.cert-manager.io/certificates.Verified end-to-end on local k3s
fetch-ca-certhang), envoy + agent bothRunning.curl https://api.anthropic.com/v1/modelsfrom the agent shows the leaf cert (CN=humr MITM CA, SANapi.anthropic.com), validated against/etc/humr/ca/ca.crt. Anthropic returns a meaningful 401 (OAuth authentication is currently not supported), confirming theAuthorization: Bearer …header was injected.curl https://httpbin.org/anything(no Secret) passes through unmodified — no Authorization header in the echoed request, confirming SNI-miss passthrough.Known limitations
Out of scope (per #337)
OAuth app connections, HITL
ext_authz, refresh-token loop, streaming xDS, gVisor / RuntimeClass, audit logging, ADR-027 fork pods, OneCLI removal — tracked as follow-ups.Test plan
mise run check(lint, tsc, go build/vet, helm lint, helm render via kubeconform) — greenmise run test— controller, api-server, ui, agent-runtime all greenBuildStatefulSetflag-on adds Envoy sidecar, scrubs OneCLI envs, skipsfetch-ca-certinit, projectsca.crtonly into the agentBuildStatefulSetflag-off unchangedautomountServiceAccountToken: false,shareProcessNamespace: false)BuildNetworkPolicydrops OneCLI peer and adds 443/80 egresstls_inspector, per-SNI termination,dynamic_forward_proxy_https,sni_dynamic_forward_proxypassthroughBuildEnvoyLeafCertificateshape (sorted/deduped DNSNames, IssuerRef, SecretName, owner ref, returns nil on no Secrets)assembleInstanceround-trips the flagenvoy --mode validateagainst rendered bootstrap returns OKCloses #337