Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding cel for allow-privilege-escalation #503

Merged
merged 11 commits into from
Jul 8, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 1.1.0
name: k8spspallowprivilegeescalationcontainer
displayName: Allow Privilege Escalation in Container
createdAt: "2024-05-29T23:41:40Z"
description: Controls restricting escalation to root privileges. Corresponds to the `allowPrivilegeEscalation` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privilege-escalation
digest: b88d5016b878fe37a461123069589385e3d082311bcaa93f9c73cedb0201cbbd
license: Apache-2.0
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/allow-privilege-escalation
keywords:
- gatekeeper
- open-policy-agent
- policies
readme: |-
# Allow Privilege Escalation in Container
Controls restricting escalation to root privileges. Corresponds to the `allowPrivilegeEscalation` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privilege-escalation
install: |-
### Usage
```shell
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/allow-privilege-escalation/1.1.0/template.yaml
```
provider:
name: Gatekeeper Library
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowPrivilegeEscalationContainer
metadata:
name: psp-allow-privilege-escalation-container
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
exemptImages: ["nonexistant/*"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-privilege-escalation-disallowed
labels:
app: nginx-privilege-escalation
spec:
ephemeralContainers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-privilege-escalation-allowed
labels:
app: nginx-privilege-escalation
spec:
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-privilege-escalation-disallowed
labels:
app: nginx-privilege-escalation
spec:
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
kind: AdmissionReview
apiVersion: admission.k8s.io/v1beta1
request:
operation: "UPDATE"
object:
apiVersion: v1
kind: Pod
metadata:
name: nginx-privilege-escalation-disallowed
labels:
app: nginx-privilege-escalation
spec:
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
metadata:
name: allow-privilege-escalation
tests:
- name: allow-privilege-escalation
template: template.yaml
constraint: samples/psp-allow-privilege-escalation-container/constraint.yaml
cases:
- name: example-allowed
object: samples/psp-allow-privilege-escalation-container/example_allowed.yaml
assertions:
- violations: no
- name: example-disallowed
object: samples/psp-allow-privilege-escalation-container/example_disallowed.yaml
assertions:
- violations: yes
- name: disallowed-ephemeral
object: samples/psp-allow-privilege-escalation-container/disallowed_ephemeral.yaml
assertions:
- violations: yes
- name: update
object: samples/psp-allow-privilege-escalation-container/update.yaml
assertions:
- violations: no
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspallowprivilegeescalationcontainer
annotations:
metadata.gatekeeper.sh/title: "Allow Privilege Escalation in Container"
metadata.gatekeeper.sh/version: 1.1.0
description: >-
Controls restricting escalation to root privileges. Corresponds to the
`allowPrivilegeEscalation` field in a PodSecurityPolicy. For more
information, see
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privilege-escalation
spec:
crd:
spec:
names:
kind: K8sPSPAllowPrivilegeEscalationContainer
validation:
openAPIV3Schema:
type: object
description: >-
Controls restricting escalation to root privileges. Corresponds to the
`allowPrivilegeEscalation` field in a PodSecurityPolicy. For more
information, see
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privilege-escalation
properties:
exemptImages:
description: >-
Any container that uses an image that matches an entry in this list will be excluded
from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.

It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
in order to avoid unexpectedly exempting images from an untrusted repository.
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
code:
- engine: K8sNativeValidation
source:
variables:
- name: containers
expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []'
- name: initContainers
expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []'
- name: ephemeralContainers
expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []'
- name: exemptImagePrefixes
expression: |
!has(variables.params.exemptImages) ? [] :
variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", ""))
- name: exemptImageExplicit
expression: |
!has(variables.params.exemptImages) ? [] :
variables.params.exemptImages.filter(image, !image.endsWith("*"))
- name: exemptImages
expression: |
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
container.image in variables.exemptImageExplicit ||
variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption))
).map(container, container.image)
- name: badContainers
expression: |
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
!(container.image in variables.exemptImages) && (
!has(container.securityContext) ||
!has(container.securityContext.allowPrivilegeEscalation) ||
container.securityContext.allowPrivilegeEscalation != false
)
)
validations:
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0'
messageExpression: '"Privilege escalation container is not allowed: " + variables.badContainers.map(c, c.image).join(", ")'
- engine: Rego
source:
rego: |
package k8spspallowprivilegeescalationcontainer

import data.lib.exclude_update.is_update
import data.lib.exempt_container.is_exempt

violation[{"msg": msg, "details": {}}] {
# spec.containers.securityContext.allowPrivilegeEscalation field is immutable.
not is_update(input.review)

c := input_containers[_]
not is_exempt(c)
input_allow_privilege_escalation(c)
msg := sprintf("Privilege escalation container is not allowed: %v", [c.name])
}

input_allow_privilege_escalation(c) {
not has_field(c, "securityContext")
}
input_allow_privilege_escalation(c) {
not c.securityContext.allowPrivilegeEscalation == false
}
input_containers[c] {
c := input.review.object.spec.containers[_]
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
}
input_containers[c] {
c := input.review.object.spec.ephemeralContainers[_]
}
# has_field returns whether an object has a field
has_field(object, field) = true {
object[field]
}
libs:
- |
package lib.exclude_update

is_update(review) {
review.operation == "UPDATE"
}
- |
package lib.exempt_container

is_exempt(container) {
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
img := container.image
exemption := exempt_images[_]
_matches_exemption(img, exemption)
}

_matches_exemption(img, exemption) {
not endswith(exemption, "*")
exemption == img
}

_matches_exemption(img, exemption) {
endswith(exemption, "*")
prefix := trim_suffix(exemption, "*")
startswith(img, prefix)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ spec:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
exemptImages: ["nonexistant/*"]
Loading
Loading