Skip to content

Commit 2d7f797

Browse files
committed
authorizer: unify authorizer chain constructors
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@gmail.com>
1 parent 2325423 commit 2d7f797

File tree

6 files changed

+87
-75
lines changed

6 files changed

+87
-75
lines changed

pkg/authorization/maximal_permission_policy_authorizer.go

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ const (
4444

4545
// NewMaximalPermissionPolicyAuthorizer returns an authorizer that first checks if the request is for a
4646
// bound resource or not. If the resource is bound it checks the maximal permission policy of the underlying API export.
47-
func NewMaximalPermissionPolicyAuthorizer(kubeInformers, globalKubeInformers kcpkubernetesinformers.SharedInformerFactory, kcpInformers, globalKcpInformers kcpinformers.SharedInformerFactory, delegate authorizer.Authorizer) authorizer.Authorizer {
47+
func NewMaximalPermissionPolicyAuthorizer(
48+
kubeInformers, globalKubeInformers kcpkubernetesinformers.SharedInformerFactory,
49+
kcpInformers, globalKcpInformers kcpinformers.SharedInformerFactory,
50+
) func(delegate authorizer.Authorizer) authorizer.Authorizer {
4851
// Make sure informer knows what to watch
4952
kubeInformers.Rbac().V1().Roles().Lister()
5053
kubeInformers.Rbac().V1().RoleBindings().Lister()
@@ -64,37 +67,39 @@ func NewMaximalPermissionPolicyAuthorizer(kubeInformers, globalKubeInformers kcp
6467
indexers.ByLogicalClusterPathAndName: indexers.IndexByLogicalClusterPathAndName,
6568
})
6669

67-
return &MaximalPermissionPolicyAuthorizer{
68-
getAPIBindings: func(clusterName logicalcluster.Name) ([]*apisv1alpha1.APIBinding, error) {
69-
return kcpInformers.Apis().V1alpha1().APIBindings().Lister().Cluster(clusterName).List(labels.Everything())
70-
},
71-
getAPIExport: func(path logicalcluster.Path, name string) (*apisv1alpha1.APIExport, error) {
72-
return indexers.ByPathAndNameWithFallback[*apisv1alpha1.APIExport](apisv1alpha1.Resource("apiexports"), kcpInformers.Apis().V1alpha1().APIExports().Informer().GetIndexer(), globalKcpInformers.Apis().V1alpha1().APIExports().Informer().GetIndexer(), path, name)
73-
},
74-
newAuthorizer: func(clusterName logicalcluster.Name) authorizer.Authorizer {
75-
return rbac.New(
76-
&rbac.RoleGetter{Lister: rbacwrapper.NewMergedRoleLister(
77-
kubeInformers.Rbac().V1().Roles().Lister().Cluster(clusterName),
78-
globalKubeInformers.Rbac().V1().Roles().Lister().Cluster(clusterName),
79-
kubeInformers.Rbac().V1().Roles().Lister().Cluster(controlplaneapiserver.LocalAdminCluster),
80-
)},
81-
&rbac.RoleBindingLister{Lister: rbacwrapper.NewMergedRoleBindingLister(
82-
kubeInformers.Rbac().V1().RoleBindings().Lister().Cluster(clusterName),
83-
globalKubeInformers.Rbac().V1().RoleBindings().Lister().Cluster(clusterName),
84-
)},
85-
&rbac.ClusterRoleGetter{Lister: rbacwrapper.NewMergedClusterRoleLister(
86-
kubeInformers.Rbac().V1().ClusterRoles().Lister().Cluster(clusterName),
87-
globalKubeInformers.Rbac().V1().ClusterRoles().Lister().Cluster(clusterName),
88-
kubeInformers.Rbac().V1().ClusterRoles().Lister().Cluster(controlplaneapiserver.LocalAdminCluster),
89-
)},
90-
&rbac.ClusterRoleBindingLister{Lister: rbacwrapper.NewMergedClusterRoleBindingLister(
91-
kubeInformers.Rbac().V1().ClusterRoleBindings().Lister().Cluster(clusterName),
92-
globalKubeInformers.Rbac().V1().ClusterRoleBindings().Lister().Cluster(clusterName),
93-
kubeInformers.Rbac().V1().ClusterRoleBindings().Lister().Cluster(controlplaneapiserver.LocalAdminCluster),
94-
)},
95-
)
96-
},
97-
delegate: delegate,
70+
return func(delegate authorizer.Authorizer) authorizer.Authorizer {
71+
return &MaximalPermissionPolicyAuthorizer{
72+
getAPIBindings: func(clusterName logicalcluster.Name) ([]*apisv1alpha1.APIBinding, error) {
73+
return kcpInformers.Apis().V1alpha1().APIBindings().Lister().Cluster(clusterName).List(labels.Everything())
74+
},
75+
getAPIExport: func(path logicalcluster.Path, name string) (*apisv1alpha1.APIExport, error) {
76+
return indexers.ByPathAndNameWithFallback[*apisv1alpha1.APIExport](apisv1alpha1.Resource("apiexports"), kcpInformers.Apis().V1alpha1().APIExports().Informer().GetIndexer(), globalKcpInformers.Apis().V1alpha1().APIExports().Informer().GetIndexer(), path, name)
77+
},
78+
newAuthorizer: func(clusterName logicalcluster.Name) authorizer.Authorizer {
79+
return rbac.New(
80+
&rbac.RoleGetter{Lister: rbacwrapper.NewMergedRoleLister(
81+
kubeInformers.Rbac().V1().Roles().Lister().Cluster(clusterName),
82+
globalKubeInformers.Rbac().V1().Roles().Lister().Cluster(clusterName),
83+
kubeInformers.Rbac().V1().Roles().Lister().Cluster(controlplaneapiserver.LocalAdminCluster),
84+
)},
85+
&rbac.RoleBindingLister{Lister: rbacwrapper.NewMergedRoleBindingLister(
86+
kubeInformers.Rbac().V1().RoleBindings().Lister().Cluster(clusterName),
87+
globalKubeInformers.Rbac().V1().RoleBindings().Lister().Cluster(clusterName),
88+
)},
89+
&rbac.ClusterRoleGetter{Lister: rbacwrapper.NewMergedClusterRoleLister(
90+
kubeInformers.Rbac().V1().ClusterRoles().Lister().Cluster(clusterName),
91+
globalKubeInformers.Rbac().V1().ClusterRoles().Lister().Cluster(clusterName),
92+
kubeInformers.Rbac().V1().ClusterRoles().Lister().Cluster(controlplaneapiserver.LocalAdminCluster),
93+
)},
94+
&rbac.ClusterRoleBindingLister{Lister: rbacwrapper.NewMergedClusterRoleBindingLister(
95+
kubeInformers.Rbac().V1().ClusterRoleBindings().Lister().Cluster(clusterName),
96+
globalKubeInformers.Rbac().V1().ClusterRoleBindings().Lister().Cluster(clusterName),
97+
kubeInformers.Rbac().V1().ClusterRoleBindings().Lister().Cluster(controlplaneapiserver.LocalAdminCluster),
98+
)},
99+
)
100+
},
101+
delegate: delegate,
102+
}
98103
}
99104
}
100105

pkg/authorization/requiredgroups_authorizer.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,20 @@ const (
4242

4343
// NewRequiredGroupsAuthorizer returns an authorizer that a set of groups stored
4444
// on the LogicalCluster object. Service account by-pass this.
45-
func NewRequiredGroupsAuthorizer(local, global corev1alpha1listers.LogicalClusterClusterLister, delegate authorizer.Authorizer) authorizer.Authorizer {
46-
return &requiredGroupsAuthorizer{
47-
getLogicalCluster: func(logicalCluster logicalcluster.Name) (*corev1alpha1.LogicalCluster, error) {
48-
obj, err := local.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
49-
if err != nil && !errors.IsNotFound(err) {
50-
return nil, err
51-
} else if errors.IsNotFound(err) {
52-
return global.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
53-
}
54-
return obj, nil
55-
},
56-
delegate: delegate,
45+
func NewRequiredGroupsAuthorizer(local, global corev1alpha1listers.LogicalClusterClusterLister) func(delegate authorizer.Authorizer) authorizer.Authorizer {
46+
return func(delegate authorizer.Authorizer) authorizer.Authorizer {
47+
return &requiredGroupsAuthorizer{
48+
getLogicalCluster: func(logicalCluster logicalcluster.Name) (*corev1alpha1.LogicalCluster, error) {
49+
obj, err := local.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
50+
if err != nil && !errors.IsNotFound(err) {
51+
return nil, err
52+
} else if errors.IsNotFound(err) {
53+
return global.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
54+
}
55+
return obj, nil
56+
},
57+
delegate: delegate,
58+
}
5759
}
5860
}
5961

pkg/authorization/workspace_content_authorizer.go

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,27 @@ const (
4444
WorkspaceAccessNotPermittedReason = "workspace access not permitted"
4545
)
4646

47-
func NewWorkspaceContentAuthorizer(localInformers, globalInformers kcpkubernetesinformers.SharedInformerFactory, localLogicalClusterLister, globalLogicalClusterLister corev1alpha1listers.LogicalClusterClusterLister, delegate authorizer.Authorizer) authorizer.Authorizer {
48-
return &workspaceContentAuthorizer{
49-
localClusterRoleLister: localInformers.Rbac().V1().ClusterRoles().Lister(),
50-
localClusterRoleBindingLister: localInformers.Rbac().V1().ClusterRoleBindings().Lister(),
51-
52-
globalClusterRoleLister: globalInformers.Rbac().V1().ClusterRoles().Lister(),
53-
globalClusterRoleBindingLister: globalInformers.Rbac().V1().ClusterRoleBindings().Lister(),
54-
55-
getLogicalCluster: func(logicalCluster logicalcluster.Name) (*corev1alpha1.LogicalCluster, error) {
56-
obj, err := localLogicalClusterLister.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
57-
if err != nil && !errors.IsNotFound(err) {
58-
return nil, err
59-
} else if errors.IsNotFound(err) {
60-
return globalLogicalClusterLister.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
61-
}
62-
return obj, nil
63-
},
64-
65-
delegate: delegate,
47+
func NewWorkspaceContentAuthorizer(localInformers, globalInformers kcpkubernetesinformers.SharedInformerFactory, localLogicalClusterLister, globalLogicalClusterLister corev1alpha1listers.LogicalClusterClusterLister) func(delegate authorizer.Authorizer) authorizer.Authorizer {
48+
return func(delegate authorizer.Authorizer) authorizer.Authorizer {
49+
return &workspaceContentAuthorizer{
50+
localClusterRoleLister: localInformers.Rbac().V1().ClusterRoles().Lister(),
51+
localClusterRoleBindingLister: localInformers.Rbac().V1().ClusterRoleBindings().Lister(),
52+
53+
globalClusterRoleLister: globalInformers.Rbac().V1().ClusterRoles().Lister(),
54+
globalClusterRoleBindingLister: globalInformers.Rbac().V1().ClusterRoleBindings().Lister(),
55+
56+
getLogicalCluster: func(logicalCluster logicalcluster.Name) (*corev1alpha1.LogicalCluster, error) {
57+
obj, err := localLogicalClusterLister.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
58+
if err != nil && !errors.IsNotFound(err) {
59+
return nil, err
60+
} else if errors.IsNotFound(err) {
61+
return globalLogicalClusterLister.Cluster(logicalCluster).Get(corev1alpha1.LogicalClusterName)
62+
}
63+
return obj, nil
64+
},
65+
66+
delegate: delegate,
67+
}
6668
}
6769
}
6870

pkg/authorization/workspace_content_authorizer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ func TestWorkspaceContentAuthorizer(t *testing.T) {
369369
globalLogicalClusters := corev1alpha1listers.NewLogicalClusterClusterLister(globalIndexer)
370370

371371
recordingAuthorizer := &recordingAuthorizer{decision: authorizer.DecisionAllow, reason: "allowed"}
372-
w := NewWorkspaceContentAuthorizer(local, global, localLogicalClusters, globalLogicalClusters, recordingAuthorizer)
372+
w := NewWorkspaceContentAuthorizer(local, global, localLogicalClusters, globalLogicalClusters)(recordingAuthorizer)
373373

374374
requestedCluster := request.Cluster{
375375
Name: logicalcluster.Name(tt.requestedWorkspace),

pkg/server/options/authorization.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ func (s *Authorization) ApplyTo(ctx context.Context, config *genericapiserver.Co
136136

137137
// group authorizer
138138
if len(s.AlwaysAllowGroups) > 0 {
139-
authorizers = append(authorizers, authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...))
139+
privGroups := authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...)
140+
authorizers = append(authorizers, privGroups)
140141
}
141142

142143
// path authorizer
@@ -190,31 +191,33 @@ func (s *Authorization) ApplyTo(ctx context.Context, config *genericapiserver.Co
190191
globalAuth, _ := authz.NewGlobalAuthorizer(kubeInformers, globalKubeInformers)
191192
globalAuth = authz.NewDecorator("05-global", globalAuth).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
192193

194+
chain := union.New(bootstrapAuth, localAuth, globalAuth)
195+
193196
// everything below - skipped for Deep SAR
194197

195198
// enforce maximal permission policy
196-
maxPermissionPolicyAuth := authz.NewMaximalPermissionPolicyAuthorizer(kubeInformers, globalKubeInformers, kcpInformers, globalKcpInformers, union.New(bootstrapAuth, localAuth, globalAuth))
197-
maxPermissionPolicyAuth = authz.NewDecorator("04-maxpermissionpolicy", maxPermissionPolicyAuth).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
199+
chain = authz.NewMaximalPermissionPolicyAuthorizer(kubeInformers, globalKubeInformers, kcpInformers, globalKcpInformers)(chain)
200+
chain = authz.NewDecorator("04-maxpermissionpolicy", chain).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
198201

199202
// protect status updates to apiexport and apibinding
200-
systemCRDAuth := authz.NewSystemCRDAuthorizer(maxPermissionPolicyAuth)
201-
systemCRDAuth = authz.NewDecorator("03-systemcrd", systemCRDAuth).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
203+
chain = authz.NewSystemCRDAuthorizer(chain)
204+
chain = authz.NewDecorator("03-systemcrd", chain).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
202205

203206
// content auth deteremines if users have access to the workspace itself - by default, in Kube there is a set
204207
// of default permissions given even to system:authenticated (like access to discovery) - this authorizer allows
205208
// kcp to make workspaces entirely invisible to users that have not been given access, by making system:authenticated
206209
// mean nothing unless they also have `verb=access` on `/`
207-
contentAuth := authz.NewWorkspaceContentAuthorizer(kubeInformers, globalKubeInformers, localLogicalClusterLister, globalLogicalClusterLister, systemCRDAuth)
208-
contentAuth = authz.NewDecorator("02-content", contentAuth).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
210+
chain = authz.NewWorkspaceContentAuthorizer(kubeInformers, globalKubeInformers, localLogicalClusterLister, globalLogicalClusterLister)(chain)
211+
chain = authz.NewDecorator("02-content", chain).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
209212

210213
// workspaces are annotated to list the groups required on users wishing to access the workspace -
211214
// this is mostly useful when adding a core set of groups to an org workspace and having them inherited
212215
// by child workspaces; this gives administrators of an org control over which users can be given access
213216
// to content in sub-workspaces
214-
requiredGroupsAuth := authz.NewRequiredGroupsAuthorizer(localLogicalClusterLister, globalLogicalClusterLister, contentAuth)
215-
requiredGroupsAuth = authz.NewDecorator("01-requiredgroups", requiredGroupsAuth).AddAuditLogging().AddAnonymization()
217+
chain = authz.NewRequiredGroupsAuthorizer(localLogicalClusterLister, globalLogicalClusterLister)(chain)
218+
chain = authz.NewDecorator("01-requiredgroups", chain).AddAuditLogging().AddAnonymization()
216219

217-
authorizers = append(authorizers, requiredGroupsAuth)
220+
authorizers = append(authorizers, chain)
218221

219222
config.RuleResolver = union.NewRuleResolvers(bootstrapRules, localResolver)
220223
config.Authorization.Authorizer = union.New(authorizers...)

test/e2e/authorizer/serviceaccounts_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func TestServiceAccounts(t *testing.T) {
253253
}, metav1.CreateOptions{})
254254
require.NoError(t, err, "failed to create role")
255255

256-
t.Log("Verifying if service account is allowed to escalate")
256+
t.Log("Verifying if service account is allowed to delegate")
257257
framework.Eventually(t, func() (bool, string) { // authz makes this eventually succeed
258258
_, err = saKubeClusterClient.Cluster(wsPath).RbacV1().ClusterRoles().Create(ctx, &rbacv1.ClusterRole{
259259
ObjectMeta: metav1.ObjectMeta{

0 commit comments

Comments
 (0)