Skip to content

Commit b9c4dfc

Browse files
authored
Merge pull request #3854 from jpbetz/cel-admission-secondary-authz
KEP-3488: Secondary authz for CEL admission control
2 parents 22b952f + d9553e4 commit b9c4dfc

File tree

1 file changed

+104
-18
lines changed
  • keps/sig-api-machinery/3488-cel-admission-control

1 file changed

+104
-18
lines changed

keps/sig-api-machinery/3488-cel-admission-control/README.md

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,8 @@ Matching is performed in quite a few systems across Kubernetes:
695695
| exclude | Audit (level=None) | phase 1 |
696696
| apiVersion + kind | | phase 1 |
697697
| 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 |
700700
| permissions (RBAC verb) | RBAC | phase 2 see "Secondary Authz" section |
701701

702702
WH = Admission webhooks, P&F = Priority and Fairness
@@ -1094,23 +1094,93 @@ repeated evaluations if they are shared across validations.
10941094

10951095
#### Secondary Authz
10961096

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:
10991098

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
11041101
field.
1105-
- Validate that only a controller responsible for a finalizer can remove it from
1106-
the finalizers field.
11071102

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:
11111182

11121183
- PodSecurityPolicy (kube)
1113-
- CertificateApproval (kube)
11141184
- CertificateSigning (kube)
11151185
- OwnerReferencesPermissionEnforcement (kube)
11161186
- network.openshift.io/ExternalIPRanger
@@ -1120,7 +1190,7 @@ Use cases:
11201190
- security.openshift.io/SecurityContextConstraint
11211191
- security.openshift.io/SCCExecRestrictions
11221192

1123-
From deads2k:
1193+
Restricted Service Account use case (from deads2k):
11241194

11251195
> Note that user.Extra in AdmissionReview has pod claims, which are valuable.
11261196

@@ -1136,9 +1206,25 @@ From deads2k:
11361206
> permission, then its probably better to create something specifically for the
11371207
> purpose.
11381208

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.
11421228

11431229
#### Access to namespace metadata
11441230

0 commit comments

Comments
 (0)