fix: kube-apiserver authorizers order

Fixes handling of `kube-apiserver` authorization config authorizers.
order.

Fixes: #10110

Signed-off-by: Noel Georgi <git@frezbo.dev>
This commit is contained in:
Noel Georgi 2025-01-14 10:22:33 +05:30
parent db4ca5668a
commit e41a995253
No known key found for this signature in database
GPG Key ID: 21A9F444075C9E36
3 changed files with 138 additions and 9 deletions

View File

@ -24,6 +24,7 @@ preface = """
Talos is built with Go 1.23.4.
"""
[notes.driver-rebind]
title = "Driver Rebind"
description = """\
@ -44,6 +45,35 @@ The kernel argument `talos.unified_cgroup_hierarchy` is now ignored.
Kernel parameter `talos.auditd.disabled=1` can be used to disable Talos built-in `auditd` service.
"""
[notes.kube-apiserver-authorization-config]
title = "kube-apiserver Authorization Config"
description = """\
When using `.cluster.apiServer.authorizationConfig` the user provided order for the authorizers is honoured and `Node` and `RBAC` authorizers are always added to the end if not explicitly specified.
Eg: If user provides only `Webhook` authorizer, the final order will be `Webhook`, `Node`, `RBAC`.
To provide a specific order for `Node` or `RBAC` explicitly, user can provide the authorizer in the order they want.
Eg:
```yaml
cluster:
apiServer:
authorizationConfig:
- type: Node
name: Node
- type: Webhook
name: Webhook
webhook:
connectionInfo:
type: InClusterConfig
...
- type: RBAC
name: rbac
```
Usage of `authorization-mode` CLI argument will not support this form of customization.
[make_deps]
[make_deps.tools]

View File

@ -131,11 +131,6 @@ func NewControlPlaneAuthorizationController() *ControlPlaneAuthorizationControll
var authorizers []k8s.AuthorizationAuthorizersSpec
for _, authorizer := range cfgProvider.Cluster().APIServer().AuthorizationConfig() {
// skip Node and RBAC authorizers as we add them by default later on.
if authorizer.Type() == "Node" || authorizer.Type() == "RBAC" {
continue
}
authorizers = slices.Concat(authorizers, []k8s.AuthorizationAuthorizersSpec{
{
Type: authorizer.Type(),
@ -145,7 +140,25 @@ func NewControlPlaneAuthorizationController() *ControlPlaneAuthorizationControll
})
}
res.TypedSpec().Config = slices.Concat(v1alpha1.APIServerDefaultAuthorizationConfigAuthorizers, authorizers)
if !slices.ContainsFunc(authorizers, func(a k8s.AuthorizationAuthorizersSpec) bool {
return a.Type == "Node"
}) {
authorizers = slices.Insert(authorizers, 0, k8s.AuthorizationAuthorizersSpec{
Type: "Node",
Name: "node",
})
}
if !slices.ContainsFunc(authorizers, func(a k8s.AuthorizationAuthorizersSpec) bool {
return a.Type == "RBAC"
}) {
authorizers = slices.Insert(authorizers, 1, k8s.AuthorizationAuthorizersSpec{
Type: "RBAC",
Name: "rbac",
})
}
res.TypedSpec().Config = authorizers
return nil
},

View File

@ -272,7 +272,7 @@ func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAut
},
{
AuthorizerType: "Node",
AuthorizerName: "foo",
AuthorizerName: "bar",
},
},
},
@ -283,7 +283,11 @@ func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAut
suite.setupMachine(cfg)
expectedAuthorizers := slices.Concat(v1alpha1.APIServerDefaultAuthorizationConfigAuthorizers, []k8s.AuthorizationAuthorizersSpec{
expectedAuthorizers := []k8s.AuthorizationAuthorizersSpec{
{
Type: "RBAC",
Name: "foo",
},
{
Type: "Webhook",
Name: "webhook",
@ -297,7 +301,89 @@ func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAut
},
},
},
})
{
Type: "Node",
Name: "bar",
},
}
rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID},
func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) {
assert.Equal(expectedAuthorizers, authorizationConfig.TypedSpec().Config)
},
)
}
func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAuthorizersWithOnlyNodeSet() {
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)
cfg := config.NewMachineConfig(
container.NewV1Alpha1(
&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineType: "controlplane",
},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: u,
},
},
APIServerConfig: &v1alpha1.APIServerConfig{
AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{
{
AuthorizerType: "Node",
AuthorizerName: "foo",
},
{
AuthorizerType: "Webhook",
AuthorizerName: "webhook",
AuthorizerWebhook: v1alpha1.Unstructured{
Object: map[string]any{
"timeout": "3s",
"subjectAccessReviewVersion": "v1",
"matchConditionSubjectAccessReviewVersion": "v1",
"failurePolicy": "NoOpinion",
"connectionInfo": map[string]any{
"type": "InClusterConfig",
},
},
},
},
},
},
},
},
),
)
suite.setupMachine(cfg)
expectedAuthorizers := []k8s.AuthorizationAuthorizersSpec{
{
Type: "Node",
Name: "foo",
},
{
Type: "RBAC",
Name: "rbac",
},
{
Type: "Webhook",
Name: "webhook",
Webhook: map[string]any{
"timeout": "3s",
"subjectAccessReviewVersion": "v1",
"matchConditionSubjectAccessReviewVersion": "v1",
"failurePolicy": "NoOpinion",
"connectionInfo": map[string]any{
"type": "InClusterConfig",
},
},
},
}
rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID},
func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) {