Skip to content

Commit d7f16bc

Browse files
Restrict konnector clusterrole and dynamically provision RBAC per binding
Signed-off-by: alokkumardalei-wq <alokkumardalei2@gmail.com>
1 parent 5d02230 commit d7f16bc

4 files changed

Lines changed: 159 additions & 10 deletions

File tree

backend/kubernetes/resources/konnector.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,33 @@ func NewKonnectorManifests(konnectorImage string, hostAliases []corev1.HostAlias
6060
Namespace: KonnectorNamespace,
6161
},
6262
},
63-
// Broad access is required because the konnector dynamically manages CRDs
64-
// and syncs arbitrary resource types discovered from the provider. Scoping
65-
// down would require knowing the bound resource types in advance, which
66-
// defeats the auto-discovery model.
63+
// The konnector dynamically manages CRDs and syncs arbitrary resource types.
64+
// Wildcard permissions are no longer used here; instead, the kubectl bind
65+
// CLI dynamically creates RBAC for the bound custom resources.
6766
ClusterRole: &rbacv1.ClusterRole{
6867
ObjectMeta: metav1.ObjectMeta{
6968
Name: KonnectorClusterRoleName,
7069
},
7170
Rules: []rbacv1.PolicyRule{
7271
{
73-
APIGroups: []string{"*"},
74-
Resources: []string{"*"},
75-
Verbs: []string{"*"},
72+
APIGroups: []string{""},
73+
Resources: []string{"namespaces", "secrets", "events", "serviceaccounts", "configmaps"},
74+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
75+
},
76+
{
77+
APIGroups: []string{"kube-bind.io"},
78+
Resources: []string{"apiservicebindings", "apiservicebindings/status", "clusterbindings", "clusterbindings/status", "servicebindings", "servicebindings/status", "serviceexports", "serviceexports/status"},
79+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
80+
},
81+
{
82+
APIGroups: []string{"apiextensions.k8s.io"},
83+
Resources: []string{"customresourcedefinitions"},
84+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
85+
},
86+
{
87+
APIGroups: []string{"coordination.k8s.io"},
88+
Resources: []string{"leases"},
89+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
7690
},
7791
},
7892
},

cli/pkg/kubectl/bind-apiservice/plugin/deploy_konnector.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,24 @@ func getKonnectorClusterRole() *rbacv1.ClusterRole {
108108
},
109109
Rules: []rbacv1.PolicyRule{
110110
{
111-
APIGroups: []string{"*"},
112-
Resources: []string{"*"},
113-
Verbs: []string{"*"},
111+
APIGroups: []string{""},
112+
Resources: []string{"namespaces", "secrets", "events", "serviceaccounts", "configmaps"},
113+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
114+
},
115+
{
116+
APIGroups: []string{"kube-bind.io"},
117+
Resources: []string{"apiservicebindings", "apiservicebindings/status", "clusterbindings", "clusterbindings/status", "servicebindings", "servicebindings/status", "serviceexports", "serviceexports/status"},
118+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
119+
},
120+
{
121+
APIGroups: []string{"apiextensions.k8s.io"},
122+
Resources: []string{"customresourcedefinitions"},
123+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
124+
},
125+
{
126+
APIGroups: []string{"coordination.k8s.io"},
127+
Resources: []string{"leases"},
128+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
114129
},
115130
},
116131
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package plugin
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
8+
rbacv1 "k8s.io/api/rbac/v1"
9+
apierrors "k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/client-go/kubernetes"
12+
"k8s.io/client-go/rest"
13+
)
14+
15+
func ensureKonnectorDynamicRBAC(ctx context.Context, config *rest.Config, binding *kubebindv1alpha2.APIServiceBinding, request *kubebindv1alpha2.APIServiceExportRequest) error {
16+
kubeClient, err := kubernetes.NewForConfig(config)
17+
if err != nil {
18+
return err
19+
}
20+
21+
roleName := fmt.Sprintf("kube-bind-konnector-%s", binding.Name)
22+
ownerRef := metav1.OwnerReference{
23+
APIVersion: kubebindv1alpha2.SchemeGroupVersion.String(),
24+
Kind: "APIServiceBinding",
25+
Name: binding.Name,
26+
UID: binding.UID,
27+
}
28+
29+
var rules []rbacv1.PolicyRule
30+
for _, res := range request.Spec.Resources {
31+
rules = append(rules, rbacv1.PolicyRule{
32+
APIGroups: []string{res.Group},
33+
Resources: []string{res.Resource, res.Resource + "/status"},
34+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
35+
})
36+
}
37+
for _, claim := range request.Spec.PermissionClaims {
38+
rules = append(rules, rbacv1.PolicyRule{
39+
APIGroups: []string{claim.Group},
40+
Resources: []string{claim.Resource, claim.Resource + "/status"},
41+
Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"},
42+
})
43+
}
44+
45+
clusterRole := &rbacv1.ClusterRole{
46+
ObjectMeta: metav1.ObjectMeta{
47+
Name: roleName,
48+
OwnerReferences: []metav1.OwnerReference{ownerRef},
49+
},
50+
Rules: rules,
51+
}
52+
53+
_, err = kubeClient.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{})
54+
if err != nil {
55+
if apierrors.IsAlreadyExists(err) {
56+
existing, err := kubeClient.RbacV1().ClusterRoles().Get(ctx, roleName, metav1.GetOptions{})
57+
if err != nil {
58+
return err
59+
}
60+
existing.Rules = rules
61+
existing.OwnerReferences = []metav1.OwnerReference{ownerRef}
62+
_, err = kubeClient.RbacV1().ClusterRoles().Update(ctx, existing, metav1.UpdateOptions{})
63+
if err != nil {
64+
return err
65+
}
66+
} else {
67+
return err
68+
}
69+
}
70+
71+
clusterRoleBinding := &rbacv1.ClusterRoleBinding{
72+
ObjectMeta: metav1.ObjectMeta{
73+
Name: roleName,
74+
OwnerReferences: []metav1.OwnerReference{ownerRef},
75+
},
76+
RoleRef: rbacv1.RoleRef{
77+
APIGroup: "rbac.authorization.k8s.io",
78+
Kind: "ClusterRole",
79+
Name: roleName,
80+
},
81+
Subjects: []rbacv1.Subject{
82+
{
83+
Kind: "ServiceAccount",
84+
Name: KonnectorServiceAccount,
85+
Namespace: KonnectorNamespace,
86+
},
87+
},
88+
}
89+
90+
_, err = kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{})
91+
if err != nil {
92+
if apierrors.IsAlreadyExists(err) {
93+
existing, err := kubeClient.RbacV1().ClusterRoleBindings().Get(ctx, roleName, metav1.GetOptions{})
94+
if err != nil {
95+
return err
96+
}
97+
existing.RoleRef = clusterRoleBinding.RoleRef
98+
existing.Subjects = clusterRoleBinding.Subjects
99+
existing.OwnerReferences = []metav1.OwnerReference{ownerRef}
100+
_, err = kubeClient.RbacV1().ClusterRoleBindings().Update(ctx, existing, metav1.UpdateOptions{})
101+
if err != nil {
102+
return err
103+
}
104+
} else {
105+
return err
106+
}
107+
}
108+
109+
return nil
110+
}

cli/pkg/kubectl/bind-apiservice/plugin/servicebindings.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, co
6868
}
6969
}
7070

71+
// Ensure dynamic RBAC is created for the konnector to access the bound resources
72+
if err := ensureKonnectorDynamicRBAC(ctx, config, existing, request); err != nil {
73+
return nil, fmt.Errorf("failed to create dynamic RBAC for konnector: %w", err)
74+
}
75+
7176
return []*kubebindv1alpha2.APIServiceBinding{existing}, nil
7277
}
7378

@@ -106,6 +111,11 @@ func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, co
106111
return nil, err
107112
}
108113

114+
// Ensure dynamic RBAC is created for the konnector to access the bound resources
115+
if err := ensureKonnectorDynamicRBAC(ctx, config, created, request); err != nil {
116+
return nil, fmt.Errorf("failed to create dynamic RBAC for konnector: %w", err)
117+
}
118+
109119
fmt.Fprintf(b.Options.IOStreams.ErrOut, "✅ Created APIServiceBinding %s for %d resources\n", bindingName, len(request.Spec.Resources))
110120
return []*kubebindv1alpha2.APIServiceBinding{created}, nil
111121
}

0 commit comments

Comments
 (0)