Skip to content

Reworked RBAC machinery for APIServiceExports#451

Merged
mjudeikis merged 7 commits into
kbind-dev:mainfrom
gman0:backend-rbac-controller
Feb 5, 2026
Merged

Reworked RBAC machinery for APIServiceExports#451
mjudeikis merged 7 commits into
kbind-dev:mainfrom
gman0:backend-rbac-controller

Conversation

@gman0

@gman0 gman0 commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Added new APIServiceExportRBAC controller for managing APIServiceExport RBACs.

  • All (Cluster)RoleBindings use the same subject: the <APIServiceExport.Namespace>/kube-binder ServiceAccount.
  • The main ClusterRole kube-binder-exports aggregates all ClusterRoles of all service exports.
  • Each APIServiceExport has its own set of ClusterRoles:
    • kube-binder-resources-<APIServiceExport.Namespace>-<APIServiceExport.Name> ClusterRole for its exported resources,
    • and kube-binder-claims-<APIServiceExport.Namespace>-<APIServiceExport.Name> ClusterRole for its claimed resources.
      • If the informer scope is Cluster: this ClusterRole contains rules for all export's claims.
      • If the informer scope is Namespaced: this ClusterRole contains rules only for cluster-scoped claimed resources.
        • For namespace-scoped claims, a kube-binder-claims-<APIServiceExport.Name> is created in each respective <APIServiceNamespace.Status.Namespace> namespace.

Additionally:

  • kube-binder ClusterRoleBinding creation/update was moved to /bind handler
  • Redundant boundschemas and boundschemas/status rules in kube-binder-<ClusterBinding.Namespace> ClusterRole were removed, because they are already present in kube-binder ClusterRole.

What Type of PR Is This?

/kind feature

Related Issue(s)

Fixes #344

Release Notes

Reworked RBAC machinery for APIServiceExports

@gman0 gman0 requested a review from a team as a code owner January 28, 2026 23:30
// to be able to figure out what to bind.
{
APIGroups: []string{kubebindv1alpha2.GroupName},
Resources: []string{"boundschemas"},

@gman0 gman0 Jan 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

^ These rules are already part of the kube-binder ClusterRole:

https://github.com/kube-bind/kube-bind/blob/d15b44c34d7d5844482cdb59ff6256912acc9c69/backend/kubernetes/resources/rbac.go#L105-L114

Probably no need to re-create them?

Comment thread backend/kubernetes/manager.go Outdated
return nil, err
}

err = kuberesources.EnsureBinderRoleBinding(ctx, c, ns, sa.Name)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Previously, kube-binder RoleBinding was created here:

https://github.com/kube-bind/kube-bind/blob/d15b44c34d7d5844482cdb59ff6256912acc9c69/backend/controllers/servicenamespace/servicenamespace_reconcile.go#L251-L275

IMHO it makes sense to move it to here to the /bind handler, since this is the place where kube-binder ClusterRole as well as the kube-bind-<Cluster> namespace is created.


errs = append(errs,
r.ensureAggregatingClusteRoleAndClusterRoleBinding(ctx, client, cache, kubebinderSubject),
// We always use cluster-scoped RBACs for exported resources.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RBACs for exported resources always use cluster-scoped permissions. This is how it was before too.

We could in theory use namespaced Roles & RoleBindings for resources too when Isolation==Namespaced && Scope==Namespaced.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does this means that if I have prefix isolation, and claim secrets, I get access to all secrets in the cluster?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mjudeikis that decision (whether konnector has cluster- or namespace-scoped access for claimed resources) is based on:

Resource claim scope Informer scope What happens
cluster cluster Creates ClusterRole & ClusterRoleBinding
namespace cluster Creates ClusterRole & ClusterRoleBinding
--- --- ---
cluster namespace Creates ClusterRole & ClusterRoleBinding
namespace namespace Creates Role & RoleBinding

So konnector gets namespaced access to Secrets only if the informer scope itself is namespaced.


And this is the behavior we already have btw:

If c.scope == kubebindv1alpha2.ClusterScope:

https://github.com/kube-bind/kube-bind/blob/d84c3f0bf4b68bdb7038117f0d8a5992949f53a2/backend/controllers/servicenamespace/servicenamespace_reconcile.go#L100-L124

... otherwise (namespaced):

https://github.com/kube-bind/kube-bind/blob/d84c3f0bf4b68bdb7038117f0d8a5992949f53a2/backend/controllers/servicenamespace/servicenamespace_reconcile.go#L175-L189

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My comment above ( #451 (comment) ) was just a suggestion that we could add (yet another :/) mode of operation -- this time for exported resources (APIServiceExport.Spec.Resources), where also backend isolation would be considered.

Right now exported resources always get cluster-scoped access, unconditionally.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we add the table to the docs somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mjudeikis Added in 27f9533

@ntnn ntnn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Otherwise lgtm, but leaving final approve to @mjudeikis

Comment thread backend/kubernetes/manager.go Outdated
Comment on lines +153 to +156
err = kuberesources.EnsureBinderRoleBinding(ctx, c, ns, sa.Name)
if err != nil {
return nil, err
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
err = kuberesources.EnsureBinderRoleBinding(ctx, c, ns, sa.Name)
if err != nil {
return nil, err
}
if err := kuberesources.EnsureBinderRoleBinding(ctx, c, ns, sa.Name); err != nil {
return nil, err
}

func (r *APIServiceExportRBACReconciler) SetupWithManager(mgr mcmanager.Manager) error {
return mcbuilder.ControllerManagedBy(mgr).
For(&kubebindv1alpha2.APIServiceExport{}).
Watches(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fo we need Owns here ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually I've ended up removing Controller: true in the owner reference ( c4472ee ).

I'm not sure Owns() would do much: the owner is the Namespace object, so we wouldn't get notified :/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So that means we don't get notifications on RBACs at all. This is a bit meh, on the other hand we didn't have that before either.

We could add it if it's necessary -- maybe in a follow-up PR? If a care-free admin messes up the RBACs they could trigger a reconcile by nudging the APIServiceExport or APIServiceNamespace so it's not that critical...


errs = append(errs,
r.ensureAggregatingClusteRoleAndClusterRoleBinding(ctx, client, cache, kubebinderSubject),
// We always use cluster-scoped RBACs for exported resources.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does this means that if I have prefix isolation, and claim secrets, I get access to all secrets in the cluster?

}

func compareMap[V any](t *testing.T, kind string, a, b map[string]V) {
require.Equal(t, slices.Sorted(maps.Keys(a)), slices.Sorted(maps.Keys(b)), "got unexpected entries after %s reconciliation", kind)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should call t.Helper().

@gman0 gman0 force-pushed the backend-rbac-controller branch 2 times, most recently from 2b5d05c to b426cb5 Compare February 2, 2026 17:52
}
}

if err := c.cleanupRBACResources(ctx, client, cache, sns, apiServiceExports); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

are we dropping cleanup of RBAC resources completely?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

All created RBAC-related objects have the owner set to kube-binder-<Consumer's cluster name>. So when that namespace is gone, the GC will do the clean up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do you think it's not enough? It's not perfect, I agree: there is one caveat in that if a consumer un-binds an export, they'd retain their RBACs until the ClusterBinding goes away (and with that the whole namespace, which cleans up the RBACs).

The question is if this actually does have any impact, and I'd hazard to say no, because the consumer/konnector could gain these permissions again by simply re-binding the service export.

}
if clusterRole == nil {
err = r.createClusterRole(ctx, client, &expectedClusterRole)
if err != nil && apierrors.IsAlreadyExists(err) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if err != nil && apierrors.IsAlreadyExists(err) {
if err != nil && !apierrors.IsAlreadyExists(err) {

Rules: rules,
}

role, err := r.getRole(ctx, cache, namespace, name)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
role, err := r.getRole(ctx, cache, namespace, name)
role, err := r.getRole(ctx, cache, namespace, name)
if err != nil {
if !apierrors.IsNotFound(err) {
return err
}
if err := r.createRole(ctx, client, &expectedRole); err != nil && !apierrors.IsAlreadyExists(err) {
return err
}
role = &expectedRole
}

let's maybe simplify it to something like

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've moved this whole mess into getOrCreate function: 5d08fa1

gman0 added 7 commits February 3, 2026 16:31
Added new APIServiceExportRBAC controller for managing APIServiceExport RBACs.

Additionally:
* kube-binder ClusterRoleBinding creation/update was moved to /bind handler
* Redundant boundschemas and boundschemas/status rules in
  `kube-binder-<ClusterBinding.Namespace>` ClusterRole were removed, because
  they are already present in `kube-binder` ClusterRole.

On-behalf-of: @SAP robert.vasek@sap.com
Signed-off-by: Robert Vasek <robert.vasek@clyso.com>
@gman0 gman0 force-pushed the backend-rbac-controller branch from b426cb5 to 27f9533 Compare February 4, 2026 09:21
@mjudeikis mjudeikis merged commit 8b330f5 into kbind-dev:main Feb 5, 2026
11 checks passed
olamilekan000 pushed a commit to olamilekan000/kube-bind that referenced this pull request Feb 18, 2026
* Reworked RBAC machinery for APIServiceExports

Added new APIServiceExportRBAC controller for managing APIServiceExport RBACs.

Additionally:
* kube-binder ClusterRoleBinding creation/update was moved to /bind handler
* Redundant boundschemas and boundschemas/status rules in
  `kube-binder-<ClusterBinding.Namespace>` ClusterRole were removed, because
  they are already present in `kube-binder` ClusterRole.
* e2e: adjusted tests for the new APIServiceExport RBAC machinery
* cleanup manager
* serviceexportrbac reconciler: don't set Controller=True in owner refs
* serviceexportrbac reconciler unit test: add missing t.Helper()
* code cleaning
* added docs into developers/architecture.md

On-behalf-of: @SAP robert.vasek@sap.com
Signed-off-by: Robert Vasek <robert.vasek@clyso.com>
@gman0 gman0 deleted the backend-rbac-controller branch February 27, 2026 08:04
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.

Implement ClusterScoped resource permissionClaims RBAC

4 participants