Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions charts/substrate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ See `values.yaml` for the full set; the important keys:
| `atelet.nodeSelector` | `{}` | Node labels atelet pods must match; must cover the nodes of every WorkerPool |
| `atelet.imageCredentialProviderBinDir` | `""` | Host path to the kubelet credential provider binaries; both paths are mounted read-only |
| `credentialProvider.enabled` | `true` | Deploy the Kubernetes Secret provider and route `k8s.io` credential URIs to it. With no provider, every credential injection fails closed |
| `credentialProvider.rbac.namespaces` | `[]` | Namespaces the provider reads Secrets in. Empty, its Secret reader ClusterRole (`k8s-credential-provider-secret-reader`, prefixed with the release name unless the release is `substrate`) is bound cluster-wide; set, it is bound by RoleBinding in each listed namespace only, and a namespace added later needs its own RoleBinding to it |
| `credentialProvider.namespacePolicies` | `[]` | Default-deny atespace-to-namespace grants; the chart includes get-only Secret RBAC for the provider |
| `ateApi.actorJWTIssuer` | `https://<idp-service>.<namespace>.svc` | Issuer placed in actor JWTs; set a stable URL reachable by relying parties |
| `objectStorePlugin.extraEnv` | `[]` | Object storage credentials/settings shared by both object-store plugins |
Expand Down
23 changes: 23 additions & 0 deletions charts/substrate/templates/k8s-credential-provider.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
{{- /* Without credentialProvider.rbac.namespaces the reader is bound
cluster-wide. With it, the reader is bound by RoleBinding in each listed
namespace only. */}}
{{- $readerNamespaces := dig "rbac" "namespaces" list .Values.credentialProvider | uniq }}
{{- if not $readerNamespaces }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
Expand All @@ -47,6 +52,24 @@ subjects:
- kind: ServiceAccount
name: {{ include "substrate.fullname" (list "k8s-credential-provider" .) }}
namespace: {{ .Release.Namespace }}
{{- else }}
{{- range $ns := $readerNamespaces }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "substrate.fullname" (list "k8s-credential-provider-secret-reader" $) }}
namespace: {{ $ns }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "substrate.fullname" (list "k8s-credential-provider-secret-reader" $) }}
subjects:
- kind: ServiceAccount
name: {{ include "substrate.fullname" (list "k8s-credential-provider" $) }}
namespace: {{ $.Release.Namespace }}
{{- end }}
{{- end }}
---
apiVersion: apps/v1
kind: Deployment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

suite: k8s credential provider Secret reader binding
templates:
- k8s-credential-provider.yaml
release:
name: substrate
namespace: ate-system
tests:
- it: binds the reader cluster-wide by default
asserts:
- containsDocument:
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
name: k8s-credential-provider-secret-reader
any: true
- containsDocument:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
not: true

- it: binds the reader only in the listed namespaces
set:
credentialProvider.rbac.namespaces: [kagent, agents-acme-x1]
asserts:
- containsDocument:
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
not: true
- containsDocument:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
name: k8s-credential-provider-secret-reader
any: true
- containsDocument:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
name: k8s-credential-provider-secret-reader
namespace: kagent
any: true
- containsDocument:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
name: k8s-credential-provider-secret-reader
namespace: agents-acme-x1
any: true

- it: the RoleBindings name the reader and the provider's ServiceAccount
set:
credentialProvider.rbac.namespaces: [kagent]
documentSelector:
path: kind
value: RoleBinding
asserts:
- equal:
path: roleRef
value: {apiGroup: rbac.authorization.k8s.io, kind: ClusterRole, name: k8s-credential-provider-secret-reader}
- equal:
path: subjects
value: [{kind: ServiceAccount, name: k8s-credential-provider, namespace: ate-system}]

- it: binds the reader cluster-wide with an empty list
set:
credentialProvider.rbac.namespaces: []
asserts:
- containsDocument:
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
name: k8s-credential-provider-secret-reader
any: true
- containsDocument:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
not: true
6 changes: 6 additions & 0 deletions charts/substrate/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ atenetEgress:
credentialProvider:
# Deploy the Kubernetes Secret provider and route k8s.io credential URIs to it.
enabled: true
rbac:
# Namespaces the provider reads Secrets in. Empty (the default), its
# Secret reader ClusterRole is bound cluster-wide. Set, the ClusterRole is
# bound by RoleBinding in each listed namespace only; a namespace added
# later needs its own RoleBinding to it.
namespaces: []
namespacePolicies: []
# - atespace: team-a
# allowedNamespaces: [team-a-secrets]
Expand Down