diff --git a/charts/substrate/README.md b/charts/substrate/README.md index 35d7bc96c8..9436f6ab31 100644 --- a/charts/substrate/README.md +++ b/charts/substrate/README.md @@ -86,7 +86,7 @@ See `values.yaml` for the full set; the important keys: | `.resources` | see `values.yaml` | Requests and limits for each container, e.g. `ateApi.resources` and `atenetRouter.agentgateway.resources` | | `atenetRouter.requestParking.budget` | `60s` | Total budget for actor resolution and resume retries | | `atenetRouter.requestTimeout` | `60s` | Timeout for each Substrate ingress API call, including actor resolution | -| `atenetEgress.extraCredentialProviders` | `[]` | Credential providers you deploy, added after the bundled `k8s.io` provider, each `{name, address}`. `name` is the host of the `ate-secret://` URIs it serves; `address` is the `host:port` the egress gateway dials over mTLS. Its serving certificate must chain to the servicedns CA. See [egress credential injection](../../docs/guides/egress-credential-injection.md#the-plugin-api) | +| `atenetEgress.extraCredentialProviders` | `[]` | Credential providers you deploy, added after the bundled `k8s.io` provider, each `{name, address, cacheTtl}`. `name` is the host of the `ate-secret://` URIs it serves; `address` is the `host:port` the egress gateway dials over mTLS. Its serving certificate must chain to the servicedns CA. Optional `cacheTtl` sets how long the gateway reuses a fetched credential (default `5m`; `0s` disables caching). See [egress credential injection](../../docs/guides/egress-credential-injection.md#the-plugin-api) | | `atenetEgress.agentgateway.logLevel` | `info` | Egress proxy `RUST_LOG` filter, e.g. `debug` or `info,agentgateway=debug` when troubleshooting | | `rustfs.enabled` | `true` | Deploy an in-cluster S3-compatible RustFS bucket for snapshots | | `ateApi.snapshotLocation` | `""` | Cluster-wide snapshot URI; defaults to `gs:///` with bundled RustFS | diff --git a/charts/substrate/templates/_helpers.tpl b/charts/substrate/templates/_helpers.tpl index 35d435d350..9155f315ad 100644 --- a/charts/substrate/templates/_helpers.tpl +++ b/charts/substrate/templates/_helpers.tpl @@ -275,6 +275,10 @@ nothing when there are none, so every injection fails closed. {{- if not .name -}} {{- fail "atenetEgress.extraCredentialProviders: each provider needs a name" -}} {{- end -}} +{{- $unknown := omit . "name" "address" "cacheTtl" -}} +{{- if $unknown -}} +{{- fail (printf "atenetEgress.extraCredentialProviders: %s has unknown keys %s; allowed keys are name, address and cacheTtl" .name (keys $unknown | sortAlpha | join ", ")) -}} +{{- end -}} {{- if eq .name "k8s.io" -}} {{- fail "atenetEgress.extraCredentialProviders: k8s.io is the bundled provider; set credentialProvider.enabled instead" -}} {{- end -}} @@ -284,13 +288,19 @@ nothing when there are none, so every injection fails closed. {{- if not (regexMatch "^[^:]+:[0-9]+$" (.address | default "")) -}} {{- fail (printf "atenetEgress.extraCredentialProviders: %s needs an address as host:port" .name) -}} {{- end -}} +{{- if and (hasKey . "cacheTtl") (not (regexMatch "^([0-9]+(ns|us|ms|s|m|h))+$" (toString .cacheTtl))) -}} +{{- fail (printf "atenetEgress.extraCredentialProviders: %s needs cacheTtl in whole ns, us, ms, s, m or h, like 30s or 0s" .name) -}} +{{- end -}} {{- $_ := set $seen .name true -}} -{{- $providers = append $providers (dict "name" .name "address" .address) -}} +{{- $providers = append $providers (pick . "name" "address" "cacheTtl") -}} {{- end -}} {{- if $providers -}} credentialProviders: {{- range $providers }} - uriAuthority: {{ .name | quote }} + {{- if hasKey . "cacheTtl" }} + cacheTtl: {{ .cacheTtl | quote }} + {{- end }} target: host: {{ .address | quote }} policies: diff --git a/charts/substrate/tests/control_plane_test.yaml b/charts/substrate/tests/control_plane_test.yaml index 87646b96fc..98540e75a9 100644 --- a/charts/substrate/tests/control_plane_test.yaml +++ b/charts/substrate/tests/control_plane_test.yaml @@ -600,3 +600,37 @@ tests: asserts: - failedTemplate: errorMessage: "atenetEgress.extraCredentialProviders: vault.example.com needs an address as host:port" +- it: passes an extra provider's cacheTtl to both listeners + template: atenet-egress.yaml + set: + atenetEgress.extraCredentialProviders: + - name: vault.example.com + address: vault-provider.vault.svc:50051 + cacheTtl: 0s + documentSelector: + path: kind + value: ConfigMap + asserts: + - matchRegex: + path: data["config.yaml"] + pattern: '(?s)uriAuthority: "vault.example.com"\n\s+cacheTtl: "0s"\n\s+target:.*uriAuthority: "vault.example.com"\n\s+cacheTtl: "0s"\n\s+target:' +- it: rejects an extra provider with a misspelled key + template: atenet-egress.yaml + set: + atenetEgress.extraCredentialProviders: + - name: vault.example.com + address: vault-provider.vault.svc:50051 + cacheTTL: 0s + asserts: + - failedTemplate: + errorMessage: "atenetEgress.extraCredentialProviders: vault.example.com has unknown keys cacheTTL; allowed keys are name, address and cacheTtl" +- it: rejects an extra provider cacheTtl that is not a duration + template: atenet-egress.yaml + set: + atenetEgress.extraCredentialProviders: + - name: vault.example.com + address: vault-provider.vault.svc:50051 + cacheTtl: 5 minutes + asserts: + - failedTemplate: + errorMessage: "atenetEgress.extraCredentialProviders: vault.example.com needs cacheTtl in whole ns, us, ms, s, m or h, like 30s or 0s" diff --git a/charts/substrate/values.yaml b/charts/substrate/values.yaml index 5d0f7a4dce..ec3255ff65 100644 --- a/charts/substrate/values.yaml +++ b/charts/substrate/values.yaml @@ -172,10 +172,13 @@ atenetEgress: # when credentialProvider.enabled. name is the host of the ate-secret:// URIs # the provider serves, and address is the host:port the gateway dials over # mTLS. The provider's serving certificate must chain to the servicedns CA - # and name the address host. + # and name the address host. Optional cacheTtl sets how long the gateway + # reuses a fetched credential, 5m by default. A grant the provider revokes + # keeps working until then. 0s sends every request to the provider. extraCredentialProviders: [] # - name: vault.example.com # address: vault-provider.vault.svc:50051 + # cacheTtl: 30s agentgateway: logLevel: info resources: diff --git a/docs/guides/egress-credential-injection.md b/docs/guides/egress-credential-injection.md index b95a55faf9..9ce9c9087e 100644 --- a/docs/guides/egress-credential-injection.md +++ b/docs/guides/egress-credential-injection.md @@ -313,6 +313,13 @@ name. An actor that is deleted and recreated under the same name can get its predecessor's token until that token's refresh point, up to two thirds of its lifetime, and that token's UID claim names the old actor. +**Credentials are cached on agentgateway.** By default the agentgateway +dataplane reuses a fetched credential per actor and URI for 5 minutes. A grant +the provider revokes keeps working until the cached credential expires. To +send every request to the provider, set `cacheTtl` to `0s` on its +`atenetEgress.extraCredentialProviders` entry in the Helm chart. The Envoy +dataplane fetches a credential on every request. + **Scope of the reference provider's RBAC.** The sample deployment grants read on Secrets cluster-wide so the policy may name any namespace; a production deployment should scope this to the namespaces the provider is allowed to