@@ -695,8 +695,8 @@ Matching is performed in quite a few systems across Kubernetes:
695
695
| exclude | Audit (level=None) | phase 1 |
696
696
| apiVersion + kind | | phase 1 |
697
697
| NonResourceURLs | Audit/RBAC/P&F | No |
698
- | user/userGroup | Audit | phase 2 see "Secondary Authz" section |
699
- | user.Extra | (in WH AdmissionReview) | phase 2 see "Secondary Authz" section |
698
+ | user/userGroup | Audit | phase 1 - request.userInfo.groups |
699
+ | user.Extra | (in WH AdmissionReview) | phase 1 - request.userInfo.extra |
700
700
| permissions (RBAC verb) | RBAC | phase 2 see "Secondary Authz" section |
701
701
702
702
WH = Admission webhooks, P&F = Priority and Fairness
@@ -1094,23 +1094,93 @@ repeated evaluations if they are shared across validations.
1094
1094
1095
1095
# ### Secondary Authz
1096
1096
1097
- We have general agreement to include this as a feature, but need to provide
1098
- a concrete design.
1097
+ We will support admission control use cases requiring permission checks :
1099
1098
1100
- kube-apiserver authorizer checks (aka Secondary-authz checks) have been proposed
1101
- as a way of doing things like :
1102
-
1103
- - Validate that only a user with a specific permission can set a particular
1099
+ - Validate that only a user with a specific permission can set a particular field.
1100
+ - Validate that only a controller responsible for a finalizer can remove it from the finalizers
1104
1101
field.
1105
- - Validate that only a controller responsible for a finalizer can remove it from
1106
- the finalizers field.
1107
1102
1108
- This could be supported by matching criteria, or via CEL expression access, or both.
1109
-
1110
- Use cases :
1103
+ To depend on an authz decision, validation expressions can use the `authorizer`
1104
+ variable, which performs authz checks for the admission request user (the same
1105
+ use as identified by `request.userInfo`), and which will be bound at evaluation
1106
+ time to an Authorizer object supporting receiver-style function overloads :
1107
+
1108
+ | Symbol | Type | Description |
1109
+ |-------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
1110
+ | path | Authorizer.(path string) -> PathCheck | Defines a check for an non-resource request path (e.g. /healthz) |
1111
+ | check | PathCheck.(httpRequestVerb string) -> Decision | Checks if the user is authorized for the HTTP request verb on the path |
1112
+ | resource | Authorizer.(kind string, group string, version string) -> ResourceCheck | Defines a check for API resources |
1113
+ | subresource | ResourceCheck.(subresource string) -> ResourceCheck | Specifies thath the check is for a subresource |
1114
+ | namespace | ResourceCheck.(namespace string) -> ResourceCheck | Specifies that the check is for a namespace (if not called, the check is for the cluster scope) |
1115
+ | name | ResourceCheck.(name string) -> ResourceCheck | Specifies that the check is for a specific resource name |
1116
+ | check | ResourceCheck.(apiVerb string) -> Decision | Checks if the admission request user is authorized for the API verb on the resource |
1117
+ | allowed | Decision.() -> bool | Is the admission request user authorized? |
1118
+ | denied | Decision.() -> bool | Is the admission request user denied authorization? |
1119
+
1120
+ xref : https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes for a details on
1121
+ authorization attributes.
1122
+
1123
+ Example expressions using `authorizer` :
1124
+
1125
+ - ` authorizer.resource('signers', 'certificates.k8s.io', '*').name(oldObject.spec.signerName).check('approve').allowed()`
1126
+ - ` authorizer.path('/metrics').denied('get')`
1127
+
1128
+ Note that this API :
1129
+
1130
+ - Produces errors at compilation time when parameters are missing or improperly combined
1131
+ (e.g. subresource with non-resource path, missing path or resource).
1132
+ - Is open to the addition of future knobs (e.g. impersonation).
1133
+ - Will have a limit on the number of authz checks generated during expression
1134
+ evaluation, this will be enforced by setting a CEL evaluation cost to
1135
+ performing each authz check that is high enough serve as an effective limit to
1136
+ the total authz checks that can be performed. The limit will be picked
1137
+ emperically by evaluating the resource costs (CPU cost primarily) of authz
1138
+ checks. This will need to be set high enough to handle policies like "You must
1139
+ be authorized to read every secret your pod mounts".
1140
+
1141
+ Other considerations :
1142
+
1143
+ - Since authorization decisions depend on an apiserver's authorizer, and the
1144
+ ValidatingAdmissionPolicy plugin is supported on [aggregated API
1145
+ servers](#aggregated-api-servers), the evaluation result of any validation expression involving
1146
+ secondary authz inherently depends on which apiserver evalutes it.
1147
+ - A validation expression could be written such that it allows malicious clients to probe
1148
+ permissions. Policy authors are responsible for careful use of client-provided inputs when
1149
+ constructing authz checks.
1150
+ - Decisions can be stored either as a [variable](#variables) or as part of the implementation of the
1151
+ object bound to the exposed `authorizer` object (i.e. caching decisions for identical checks
1152
+ within a single policy evaluation) in order to prevent duplicate checks across multiple validation
1153
+ expressions.
1154
+ - Authorization checks that require information from resources other than the
1155
+ resource being admitted are possible but will be limited by eventual
1156
+ consistency. Information from other resources can be accumulated by a controller
1157
+ and written to a custom resources which can then be referenced by the `paramSource`
1158
+ of a policy binding and accessed in CEL expressions via the `params` variable.
1159
+
1160
+
1161
+ CertificateApproval use case :
1162
+
1163
+ ` ` ` yaml
1164
+ apiVersion: admissionregistration.k8s.io/v1alpha1
1165
+ kind: ValidatingAdmissionPolicy
1166
+ metadata:
1167
+ name: "certificate-approval-policy.example.com"
1168
+ spec:
1169
+ matchConstraints:
1170
+ resourceRules:
1171
+ - apiGroups: ["certificates.k8s.io"]
1172
+ apiVersions: ["*"]
1173
+ operations: ["UPDATE"]
1174
+ resources: ["certificatesigningrequests/approval"]
1175
+ validations:
1176
+ - expression: "authorizer.resource('signers', 'certificates.k8s.io', '*').name(oldObject.spec.signerName).check('approve').allowed() || authorizer.resource('signers', 'certificates.k8s.io', '*').name([oldObject.spec.signerName.split('/')[0], '*'].join('/')).check('approve').allowed()"
1177
+ reason: Forbidden
1178
+ messageExpression: "user not permitted to approve requests with signerName %s".format([oldObject.spec.signerName])"
1179
+ ` ` `
1180
+
1181
+ Other use cases in existing admission plugins :
1111
1182
1112
1183
- PodSecurityPolicy (kube)
1113
- - CertificateApproval (kube)
1114
1184
- CertificateSigning (kube)
1115
1185
- OwnerReferencesPermissionEnforcement (kube)
1116
1186
- network.openshift.io/ExternalIPRanger
@@ -1120,7 +1190,7 @@ Use cases:
1120
1190
- security.openshift.io/SecurityContextConstraint
1121
1191
- security.openshift.io/SCCExecRestrictions
1122
1192
1123
- From deads2k :
1193
+ Restricted Service Account use case (from deads2k) :
1124
1194
1125
1195
> Note that user.Extra in AdmissionReview has pod claims, which are valuable.
1126
1196
@@ -1136,9 +1206,25 @@ From deads2k:
1136
1206
> permission, then its probably better to create something specifically for the
1137
1207
> purpose.
1138
1208
1139
- Looking up the pod (or any other additional resources) is not something we are
1140
- currently planning to support in this KEP, but the use case is interesting and
1141
- we should investigate with sig-auth.
1209
+ This enhancement makes
1210
+ ` request.userInfo.extra['authentication.kubernetes.io/pod-uid']` available to
1211
+ admission policies. Looking up the pod (or any other additional resources) is
1212
+ makes this use case challenging. A controller that accumulates a `pod-uid ->
1213
+ node-name` map in a custom resource by watching all pods could then make the
1214
+ mapping available in a custom resource for the admission policy to consume using
1215
+ ` paramSource` .
1216
+
1217
+ This would result in a CEL expression like :
1218
+
1219
+ ` ` `
1220
+ object.spec.NodeName == params.nodeNamebyPodUID[request.userInfo.extra['authentication.kubernetes.io/pod-uid']]
1221
+ ` ` `
1222
+
1223
+ But this would not scale well to clusters with large pod counts.
1224
+
1225
+ If we were to offer a way to lookup arbitrary other resources, or even if
1226
+ we provided selective access to just some resources, this might become
1227
+ easier. This can explored as future work.
1142
1228
1143
1229
# ### Access to namespace metadata
1144
1230
0 commit comments