Skip to content

Commit 237252a

Browse files
committed
change position of webhook authorizer in authz chain so that it cannot override alwaysAllowPaths/Groups
On-behalf-of: @SAP [email protected]
1 parent 5850a76 commit 237252a

File tree

2 files changed

+62
-45
lines changed

2 files changed

+62
-45
lines changed

docs/content/concepts/authorization/authorizers.md

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,41 @@ They are related in the following way:
1616

1717
``` mermaid
1818
graph TD
19-
start(Request):::state --> main_alt[/one of\]:::or
20-
main_alt --> aapa[Always Allow Paths Auth]
21-
main_alt --> aaga[Always Allow Groups Auth]
22-
main_alt --> wa[Webhook Auth]
23-
main_alt --> rga[Required Groups Auth]
19+
start(Request):::state --> main_alt[/one of\]:::or
20+
main_alt --> aapa[Always Allow Paths Auth]
21+
main_alt --> aaga[Always Allow Groups Auth]
2422
25-
aapa --> decision(Decision):::state
26-
aaga --> decision
27-
wa --> decision
23+
aapa --> decision(Decision):::state
24+
aaga --> decision
25+
26+
main_alt --> kcp_alt[/one of\]:::or
27+
28+
subgraph "main authorizers"
29+
kcp_alt --> rga[Required Groups Auth]
30+
kcp_alt --> wa[Webhook Auth]
2831
2932
subgraph "RBAC"
30-
rga --> wca[Workspace Content Auth]
31-
wca --> scrda[System CRD Auth]
32-
scrda --> mppa[Max. Permission Policy Auth]
33-
34-
mppa --- mppa_alt[/one of\]:::or
35-
mppa_alt --> lpa[Local Policy Auth]
36-
mppa_alt --> gpa[Global Policy Auth]
37-
mppa_alt --> bpa[Bootstrap Policy Auth]
33+
rga --> wca[Workspace Content Auth]
34+
wca --> scrda[System CRD Auth]
35+
scrda --> mppa[Max. Permission Policy Auth]
36+
37+
mppa --- mppa_alt[/one of\]:::or
38+
mppa_alt --> lpa[Local Policy Auth]
39+
mppa_alt --> gpa[Global Policy Auth]
40+
mppa_alt --> bpa[Bootstrap Policy Auth]
3841
end
42+
end
3943
40-
lpa --> decision
41-
gpa --> decision
42-
bpa --> decision
44+
wa --> decision
45+
lpa --> decision
46+
gpa --> decision
47+
bpa --> decision
4348
44-
classDef state color:#F77
45-
classDef or fill:none,stroke:none
49+
classDef state color:#F77
50+
classDef or fill:none,stroke:none
4651
```
4752

48-
[View graph on Kroki](https://kroki.io/mermaid/svg/eNqFkkFrwzAMhe_7Faa7dLBux0IOg7ZhvWxQusEOWRmKoyambuTZDln__RQnG02bUp-E3mfx9OzcginEe3wj-DgP1o_X-F2h83dRFHHDo5hMnsQeVPkF2iePVKKg7eeGZbLh2p8WQAADyUzXcHBipjXVYgW-4LryxWYIz0_wpaXKXORrSD4wLYh2lwjLA5sVlMWsPyywjb_AZSiVU1SO4674X7jj8j4XuvVJr42tSvMQ42g9ny1GoWe727Vkw2R3zoBEsaDSY-mPrLMeOCdtBsnbwXnci8U6PkKC1D6C4Wxf4edBrNDulWssiBVpJQ_HKzYY85NQXHy0TguDNc99IQm6P-2My5lbakqvgimDcyLvPAdzzmKZtVa1GQg5H2qmZih6qcG5GLei_amSNNno9nk67atkxVZpHZWcwz17oh2G-he4ue-L)
53+
[View graph on Kroki](https://kroki.io/mermaid/svg/eNqNkk1PwzAMhu_7Fda4DInBcVIPSGMTu4A0DSQOZUJumrVVs7okqcr49TgpHf3YgUtrxY9fv7GTaCxTeF1PAIxFbWc7-VlJY6-DIOADK2E-v4cjZsUHKhveUSGBDu97TpPmojbjMcQSw6Wq8WRgqRTVsEWbclzZdD-GkwG80VSVLc24k_NoLEVmMipm69_g7M5TSZ-aDDvlorxk3l25ihI_gKkrAOTOpLNvqc2Us9BWehXNdt1wMi3jvtUhWWP4JqOUKP-7S7fX7mG5avTBqTY1gotI56ZEIWFFhZWF7eiDIzxphI4xfDkZK4-w2q17kE82Kyt5F8_4dQtbqY-ZcZOBLalMnLquwINcMffB5SW32PmGirWfSKDqK14gEyY3iqJ_oBGjD0TWWB7TmJZFPGm-_KsHKwdnaXiUjI-icvxWhEJj1vIAzXsXpEgHV4-LRTdHGg6ZUkHBs7lhh5RLH_8Amf8GPg==)
4954

5055
### Always Allow Paths Authorizer
5156

@@ -247,9 +252,14 @@ contexts:
247252
cluster: webhook
248253
```
249254

250-
The webhook will receive every authorization request made in kcp, including internal ones. This means if the webhook
251-
is badly configured, it can even prevent kcp from starting up successfully, but on the other hand this allows
252-
a lot of influence over the authorization in kcp.
255+
The webhook will receive every authorization request made in kcp and is therefore able to bypass traditional
256+
RBAC. However it cannot overrule the Always Allow Paths/Groups authorizers as these are required for core
257+
functionality in kcp like health checks.
258+
259+
!!! note
260+
However webhooks still have tremendous influence and a webhook that always denies every request will
261+
block workspace creation, for example, potentially preventing kcp from even starting up because
262+
the `root` workspace cannot be created.
253263

254264
The webhook will receive JSON-marshalled `SubjectAccessReview` objects, that (compared to vanilla Kubernetes) include the name of target logical cluster as an `extra` field, like so:
255265

pkg/server/options/authorization.go

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,25 @@ func (s *Authorization) AddFlags(fs *pflag.FlagSet) {
131131
func (s *Authorization) ApplyTo(ctx context.Context, config *genericapiserver.Config, kubeInformers, globalKubeInformers kcpkubernetesinformers.SharedInformerFactory, kcpInformers, globalKcpInformers kcpinformers.SharedInformerFactory) error {
132132
var authorizers []authorizer.Authorizer
133133

134+
localLogicalClusterLister := kcpInformers.Core().V1alpha1().LogicalClusters().Lister()
135+
globalLogicalClusterLister := globalKcpInformers.Core().V1alpha1().LogicalClusters().Lister()
136+
137+
// group authorizer
138+
if len(s.AlwaysAllowGroups) > 0 {
139+
authorizers = append(authorizers, authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...))
140+
}
141+
142+
// path authorizer
143+
if len(s.AlwaysAllowPaths) > 0 {
144+
a, err := path.NewAuthorizer(s.AlwaysAllowPaths)
145+
if err != nil {
146+
return err
147+
}
148+
authorizers = append(authorizers, a)
149+
}
150+
134151
// re-use the authorizer from the generic control plane (this is only set for webhooks)
152+
var webhookAuthorizer authorizer.Authorizer
135153
if webhook := s.Webhook; webhook != nil && webhook.WebhookConfigFile != "" {
136154
authorizationConfig, err := webhook.ToAuthorizationConfig(informerfactoryhack.Wrap(kubeInformers))
137155
if err != nil {
@@ -146,29 +164,12 @@ func (s *Authorization) ApplyTo(ctx context.Context, config *genericapiserver.Co
146164
authorizationConfig.CustomDial = egressDialer
147165
}
148166

149-
authorizer, _, err := authorizationConfig.New(ctx, config.APIServerID)
167+
webhookAuthorizer, _, err = authorizationConfig.New(ctx, config.APIServerID)
150168
if err != nil {
151169
return err
152170
}
153171

154-
authorizers = append(authorizers, authorizer)
155-
}
156-
157-
localLogicalClusterLister := kcpInformers.Core().V1alpha1().LogicalClusters().Lister()
158-
globalLogicalClusterLister := globalKcpInformers.Core().V1alpha1().LogicalClusters().Lister()
159-
160-
// group authorizer
161-
if len(s.AlwaysAllowGroups) > 0 {
162-
authorizers = append(authorizers, authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...))
163-
}
164-
165-
// path authorizer
166-
if len(s.AlwaysAllowPaths) > 0 {
167-
a, err := path.NewAuthorizer(s.AlwaysAllowPaths)
168-
if err != nil {
169-
return err
170-
}
171-
authorizers = append(authorizers, a)
172+
webhookAuthorizer = authz.NewDecorator("10-webhook", webhookAuthorizer).AddAuditLogging().AddAnonymization().AddReasonAnnotation()
172173
}
173174

174175
// kcp authorizers, these are evaluated in reverse order
@@ -209,7 +210,13 @@ func (s *Authorization) ApplyTo(ctx context.Context, config *genericapiserver.Co
209210
requiredGroupsAuth := authz.NewRequiredGroupsAuthorizer(localLogicalClusterLister, globalLogicalClusterLister, contentAuth)
210211
requiredGroupsAuth = authz.NewDecorator("01-requiredgroups", requiredGroupsAuth).AddAuditLogging().AddAnonymization()
211212

212-
authorizers = append(authorizers, requiredGroupsAuth)
213+
// The kcp authorizer chain and the webhook form a union of themselves, so that a faulty webhook
214+
// cannot prevent the health checks or system tasks from succeeding.
215+
if webhookAuthorizer != nil {
216+
authorizers = append(authorizers, union.New(webhookAuthorizer, requiredGroupsAuth))
217+
} else {
218+
authorizers = append(authorizers, requiredGroupsAuth)
219+
}
213220

214221
config.RuleResolver = union.NewRuleResolvers(bootstrapRules, localResolver)
215222
config.Authorization.Authorizer = union.New(authorizers...)

0 commit comments

Comments
 (0)