Skip to content

The Kubernetes credential provider can grant tenant namespaces selected by label - #2877

Open
Quentin Bisson (QuentinBisson) wants to merge 2 commits into
agent-substrate:mainfrom
QuentinBisson:credential-provider-namespace-selector
Open

Quentin Bisson (QuentinBisson) wants to merge 2 commits into
agent-substrate:mainfrom
QuentinBisson:credential-provider-namespace-selector

Conversation

@QuentinBisson

@QuentinBisson Quentin Bisson (QuentinBisson) commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

The Kubernetes secrets provider grants each atespace a fixed namespace list read at startup, so a platform that creates one namespace and one atespace per tenant at run time needs a policy edit and a restart for every tenant.

A namespace policy can now carry ownNamespaceSelector, a label selector, in place of atespace and allowedNamespaces: each namespace it matches is readable by the atespace of the same name and no other. The provider watches namespaces only when a policy has a selector, matches each request against current labels, and secretSelector narrows a selected grant as it does a listed one. The provider's ClusterRole gains list/watch on namespaces. ownNamespaceSelector is a full metav1.LabelSelector, unlike secretSelector, because the tenant case needs the Exists operator.

Fixes #2876

  • Tests pass
  • Appropriate changes to documentation are included in the PR

AI use: AI-assisted, Claude Code wrote the change and tests; I reviewed and tested it.

@google-cla

google-cla Bot commented Oct 19, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

The Kubernetes credential provider grants each atespace a fixed list of
namespaces, read once at startup. A platform that creates a namespace
per tenant at run time, and runs each tenant's actors in the atespace
named after it, cannot follow those namespaces with a fixed list.

A namespace policy can now carry an ownNamespaceSelector, a Kubernetes
label selector, in place of atespace and allowedNamespaces. Each
namespace the selector matches may be read by the atespace of the same
name and by no other, so one tenant's actors never read another
tenant's Secrets.

The provider watches namespaces and matches each request against the
namespace's labels at that moment: a namespace created, labeled,
unlabeled or deleted after startup is granted or refused without a
restart. A secretSelector narrows a selected grant as it narrows a
listed one. An empty selector, or an ownNamespaceSelector combined with
atespace or allowedNamespaces, fails loading.

The provider starts the namespace watch only when a policy selects
namespaces, so a policy file without one parses and behaves as before.
The provider's ClusterRole gains list and watch on namespaces, and
/statusz lists the namespaces the selectors match now.

Signed-off-by: Jose Armesto <github@armesto.net>
@QuentinBisson
Quentin Bisson (QuentinBisson) force-pushed the credential-provider-namespace-selector branch from fba26c5 to 49ee2fe Compare October 19, 2026 10:20
Pin that combining static and selector grants never writes into the
policy's shared slice.
@QuentinBisson
Quentin Bisson (QuentinBisson) marked this pull request as ready for review October 19, 2026 10:52
@bowei Bowei Du (bowei) added area/network area/security Security related issue/pr kind/feature An enhancement / feature request or implementation labels Oct 19, 2026

@EItanya Eitan Yarmush (EItanya) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that something like this is useful, but I also think it's a bit overly specific. What do you think about starting with a filewatch + reload on the configmap, (assuming this doesn't exist today), and then we can ideate on how to improve the API going forward. This feels to me like the perfect use of a simple policy language like CEL.

Bowei Du (@bowei) I'm a little worried that more requirements like this will come up so we should decide our stance on this component.

@QuentinBisson

Copy link
Copy Markdown
Contributor Author

Reload doesn't exist today; the policy is read once at startup. I'm happy to start there, I'll open a small PR for it.

It won't cover our case on its own: we create a namespace and an atespace per tenant at run time, so something would still have to edit the shared policy for every tenant, and whatever can do that can grant any atespace any namespace.

On CEL: it would cover our case as long as rules can see the namespace's current labels, which means keeping the namespace watch from this PR as the input. One alternative to weigh before adding a policy language: have the provider ask Kubernetes RBAC with a SubjectAccessReview (can substrate:atespace: get this Secret in this namespace?), so a grant is just a RoleBinding in the tenant namespace. Happy to write both up in #2876 and close this PR once we pick one

@QuentinBisson

Copy link
Copy Markdown
Contributor Author

Reload PR #2959

@EItanya

Copy link
Copy Markdown
Collaborator

I'll let Taahir Ahmed (@ahmedtd) confirm on the security side of the RBAC approach.

Watching the CM for changes seems like a good temporary middle ground to me.

As I mentioned, I think the question here is more about what the project as a whole sees for this component. On the one hand, addressing k8s secrets is clearly an important base case, but maintaining this also means we're on the hook for this API forever etc. The credential API is extendable exactly to make sure that folks can bring whatever implementation they want it the upstream one doesn't match what's needed.

@QuentinBisson

Copy link
Copy Markdown
Contributor Author

Thanks, that's clear. We'll wait for the direction on the component before proposing anything further on the policy API; #2959 stands as the reload middle ground, and I'll close this PR once it merges.

One thing that helps either way: nsauthz.go and the decision functions are package main, so an out-of-tree provider can only copy them today. If the answer is "bring your own provider", would you take a pure move of the policy loader and authorizer into an importable package (no behaviour change), so a custom provider can reuse the same policy file and semantics?

@bowei

Copy link
Copy Markdown
Collaborator

@bowei

Copy link
Copy Markdown
Collaborator

It's a good question how feature-rich this "example cred provider" should become. The biggest concern is whether or not we accidentally open a can of CVEs (ref: ingress-nginx) that can be hard to close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/network area/security Security related issue/pr kind/feature An enhancement / feature request or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-tenant credential isolation when atespaces are created at run time

4 participants