Skip to content

Commit e252349

Browse files
author
Massimiliano Giovagnoli
committed
wip
Signed-off-by: Massimiliano Giovagnoli <[email protected]>
1 parent 5a9c25b commit e252349

File tree

6 files changed

+143
-5
lines changed

6 files changed

+143
-5
lines changed

api/v1beta1/owner_role.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,30 @@ func (in OwnerSpec) GetRoles(tenant Tenant, index int) []string {
4040

4141
roles := []string{"admin", "capsule-namespace-deleter"}
4242

43+
if tenant.Spec.GitOpsReady {
44+
roles = append(roles, in.getGitOpsRoles(tenant)...)
45+
}
46+
4347
return roles
4448
}
4549

4650
func (in OwnerSpec) GetClusterRoles(tenant Tenant) []string {
4751
if tenant.Spec.GitOpsReady {
48-
return in.getGitOpsRoles(tenant)
52+
return in.getGitOpsClusterRoles(tenant)
4953
}
5054

5155
return []string{}
5256
}
5357

58+
func (in OwnerSpec) getGitOpsClusterRoles(tenant Tenant) []string {
59+
return []string{
60+
"capsule-tenant-impersonator-" + tenant.Name + "-" + in.Name,
61+
}
62+
}
63+
5464
func (in OwnerSpec) getGitOpsRoles(tenant Tenant) []string {
5565
return []string{
5666
"cluster-admin",
57-
"capsule-tenant-impersonator-" + tenant.Name + "-" + in.Name,
5867
}
5968
}
6069

api/v1beta1/tenant_labels.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ func GetTypeLabel(t runtime.Object) (label string, err error) {
2424
return "capsule.clastix.io/resource-quota", nil
2525
case *rbacv1.RoleBinding:
2626
return "capsule.clastix.io/role-binding", nil
27+
case *rbacv1.ClusterRoleBinding:
28+
return "capsule.clastix.io/cluster-role-binding", nil
2729
default:
2830
err = fmt.Errorf("type %T is not mapped as Capsule label recognized", v)
2931
}

config/crd/bases/capsule.clastix.io_tenants.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,9 @@ spec:
654654
allowedRegex:
655655
type: string
656656
type: object
657+
gitOpsReady:
658+
description: Configured RBAC for machine owners tailored for GitOps controllers.
659+
type: boolean
657660
imagePullPolicies:
658661
description: Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
659662
items:
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package tenant
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"hash/fnv"
7+
8+
"golang.org/x/sync/errgroup"
9+
rbacv1 "k8s.io/api/rbac/v1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
12+
13+
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
14+
)
15+
16+
// Sync the dynamic Tenant Owner specific cluster-roles and additional ClusterRole Bindings, which can be used in many ways:
17+
// applying Pod Security Policies or giving access to CRDs or specific API groups.
18+
func (r *Manager) syncClusterRoleBindings(ctx context.Context, tenant *capsulev1beta1.Tenant) (err error) {
19+
// hashing the ClusterRoleBinding name due to DNS RFC-1123 applied to Kubernetes labels
20+
hashFn := func(binding capsulev1beta1.AdditionalRoleBindingsSpec) string {
21+
h := fnv.New64a()
22+
23+
_, _ = h.Write([]byte(binding.ClusterRoleName))
24+
25+
for _, sub := range binding.Subjects {
26+
_, _ = h.Write([]byte(sub.Kind + sub.Name))
27+
}
28+
29+
return fmt.Sprintf("%x", h.Sum64())
30+
}
31+
// getting requested Role Binding keys
32+
keys := make([]string, 0, len(tenant.Spec.Owners))
33+
// Generating for dynamic tenant owners cluster roles
34+
for _, owner := range tenant.Spec.Owners {
35+
for _, clusterRoleName := range owner.GetClusterRoles(*tenant) {
36+
37+
cr := r.ownerClusterRoleBindings(owner, clusterRoleName)
38+
39+
keys = append(keys, hashFn(cr))
40+
}
41+
}
42+
43+
group := new(errgroup.Group)
44+
45+
group.Go(func() error {
46+
return r.syncAdditionalClusterRoleBinding(ctx, tenant, keys, hashFn)
47+
})
48+
49+
return group.Wait()
50+
}
51+
52+
func (r *Manager) syncAdditionalClusterRoleBinding(ctx context.Context, tenant *capsulev1beta1.Tenant, keys []string, hashFn func(binding capsulev1beta1.AdditionalRoleBindingsSpec) string) (err error) {
53+
54+
var tenantLabel string
55+
56+
var clusterRoleBindingLabel string
57+
58+
if tenantLabel, err = capsulev1beta1.GetTypeLabel(&capsulev1beta1.Tenant{}); err != nil {
59+
return
60+
}
61+
62+
if clusterRoleBindingLabel, err = capsulev1beta1.GetTypeLabel(&rbacv1.ClusterRoleBinding{}); err != nil {
63+
return
64+
}
65+
66+
if err = r.pruningResources(ctx, "capsule-system", keys, &rbacv1.ClusterRoleBinding{}); err != nil {
67+
return
68+
}
69+
70+
var clusterRoleBindings []capsulev1beta1.AdditionalRoleBindingsSpec
71+
72+
for _, owner := range tenant.Spec.Owners {
73+
for _, clusterRoleName := range owner.GetClusterRoles(*tenant) {
74+
clusterRoleBindings = append(clusterRoleBindings, r.ownerClusterRoleBindings(owner, clusterRoleName))
75+
}
76+
}
77+
78+
for i, clusterRoleBinding := range clusterRoleBindings {
79+
80+
clusterRoleBindingHashLabel := hashFn(clusterRoleBinding)
81+
82+
target := &rbacv1.ClusterRoleBinding{
83+
ObjectMeta: metav1.ObjectMeta{
84+
Name: fmt.Sprintf("capsule-%s-%d-%s", tenant.Name, i, clusterRoleBinding.ClusterRoleName),
85+
},
86+
}
87+
88+
var res controllerutil.OperationResult
89+
res, err = controllerutil.CreateOrUpdate(ctx, r.Client, target, func() error {
90+
target.ObjectMeta.Labels = map[string]string{
91+
tenantLabel: tenant.Name,
92+
93+
clusterRoleBindingLabel: clusterRoleBindingHashLabel,
94+
}
95+
target.RoleRef = rbacv1.RoleRef{
96+
APIGroup: rbacv1.GroupName,
97+
Kind: "ClusterRole",
98+
Name: clusterRoleBinding.ClusterRoleName,
99+
}
100+
target.Subjects = clusterRoleBinding.Subjects
101+
102+
return controllerutil.SetControllerReference(tenant, target, r.Client.Scheme())
103+
})
104+
105+
r.emitEvent(tenant, target.GetNamespace(), res, fmt.Sprintf("Ensuring ClusterRoleBinding %s", target.GetName()), err)
106+
107+
if err != nil {
108+
r.Log.Error(err, "Cannot sync ClusterRoleBinding")
109+
}
110+
111+
r.Log.Info(fmt.Sprintf("ClusterRoleBinding sync result: %s", string(res)), "name", target.Name, "namespace", target.Namespace)
112+
113+
if err != nil {
114+
return
115+
}
116+
}
117+
118+
return nil
119+
}

controllers/tenant/clusterroles.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ func (r *Manager) ensureOwnerClusterRole(ctx context.Context, tenant *capsulev1b
4747
}
4848

4949
resourceName := owner.Name
50-
if owner.Kind == capsulev1beta1.ServiceAccountOwner {
51-
resourceName = owner.Name
52-
}
5350

5451
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, clusterRole, func() error {
5552
clusterRole.Rules = []rbacv1.PolicyRule{

controllers/tenant/manager.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ func (r Manager) Reconcile(ctx context.Context, request ctrl.Request) (result ct
113113

114114
return
115115
}
116+
// Ensuring ClusterRoleBindings resources
117+
r.Log.Info("Ensuring ClusterRoleBindings for Owners and Tenant")
118+
119+
if err = r.syncClusterRoleBindings(ctx, instance); err != nil {
120+
r.Log.Error(err, "Cannot sync ClusterRoleBindings items")
121+
122+
return
123+
}
116124
// Ensuring RoleBinding resources
117125
r.Log.Info("Ensuring RoleBindings for Owners and Tenant")
118126

0 commit comments

Comments
 (0)