-
Notifications
You must be signed in to change notification settings - Fork 321
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CEL to K8sPSPCapabilities template (#535)
* Add CEL to K8sPSPCapabilities template Signed-off-by: Max Smythe <[email protected]> * bump minor version Signed-off-by: Max Smythe <[email protected]> --------- Signed-off-by: Max Smythe <[email protected]> Co-authored-by: Jaydipkumar Arvindbhai Gabani <[email protected]> Co-authored-by: Sertaç Özercan <[email protected]>
- Loading branch information
1 parent
7983a1d
commit d59972f
Showing
13 changed files
with
802 additions
and
232 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
artifacthub/library/pod-security-policy/capabilities/1.1.0/artifacthub-pkg.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
version: 1.1.0 | ||
name: k8spspcapabilities | ||
displayName: Capabilities | ||
createdAt: "2024-05-29T23:37:22Z" | ||
description: Controls Linux capabilities on containers. Corresponds to the `allowedCapabilities` and `requiredDropCapabilities` fields in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#capabilities | ||
digest: 1b837e4add0952bb782bf0d0bc5e12ac0b0543ee4d23f88e9a282b531bfb8ff5 | ||
license: Apache-2.0 | ||
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/capabilities | ||
keywords: | ||
- gatekeeper | ||
- open-policy-agent | ||
- policies | ||
readme: |- | ||
# Capabilities | ||
Controls Linux capabilities on containers. Corresponds to the `allowedCapabilities` and `requiredDropCapabilities` fields in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#capabilities | ||
install: |- | ||
### Usage | ||
```shell | ||
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/capabilities/1.1.0/template.yaml | ||
``` | ||
provider: | ||
name: Gatekeeper Library |
2 changes: 2 additions & 0 deletions
2
artifacthub/library/pod-security-policy/capabilities/1.1.0/kustomization.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
resources: | ||
- template.yaml |
14 changes: 14 additions & 0 deletions
14
.../library/pod-security-policy/capabilities/1.1.0/samples/capabilities-demo/constraint.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
apiVersion: constraints.gatekeeper.sh/v1beta1 | ||
kind: K8sPSPCapabilities | ||
metadata: | ||
name: capabilities-demo | ||
spec: | ||
match: | ||
kinds: | ||
- apiGroups: [""] | ||
kinds: ["Pod"] | ||
namespaces: | ||
- "default" | ||
parameters: | ||
allowedCapabilities: ["something"] | ||
requiredDropCapabilities: ["must_drop"] |
21 changes: 21 additions & 0 deletions
21
...od-security-policy/capabilities/1.1.0/samples/capabilities-demo/disallowed_ephemeral.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: opa-disallowed | ||
labels: | ||
owner: me.agilebank.demo | ||
spec: | ||
ephemeralContainers: | ||
- name: opa | ||
image: openpolicyagent/opa:0.9.2 | ||
args: | ||
- "run" | ||
- "--server" | ||
- "--addr=localhost:8080" | ||
securityContext: | ||
capabilities: | ||
add: ["disallowedcapability"] | ||
resources: | ||
limits: | ||
cpu: "100m" | ||
memory: "30Mi" |
22 changes: 22 additions & 0 deletions
22
...ary/pod-security-policy/capabilities/1.1.0/samples/capabilities-demo/example_allowed.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: opa-allowed | ||
labels: | ||
owner: me.agilebank.demo | ||
spec: | ||
containers: | ||
- name: opa | ||
image: openpolicyagent/opa:0.9.2 | ||
args: | ||
- "run" | ||
- "--server" | ||
- "--addr=localhost:8080" | ||
securityContext: | ||
capabilities: | ||
add: ["something"] | ||
drop: ["must_drop", "another_one"] | ||
resources: | ||
limits: | ||
cpu: "100m" | ||
memory: "30Mi" |
21 changes: 21 additions & 0 deletions
21
.../pod-security-policy/capabilities/1.1.0/samples/capabilities-demo/example_disallowed.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: opa-disallowed | ||
labels: | ||
owner: me.agilebank.demo | ||
spec: | ||
containers: | ||
- name: opa | ||
image: openpolicyagent/opa:0.9.2 | ||
args: | ||
- "run" | ||
- "--server" | ||
- "--addr=localhost:8080" | ||
securityContext: | ||
capabilities: | ||
add: ["disallowedcapability"] | ||
resources: | ||
limits: | ||
cpu: "100m" | ||
memory: "30Mi" |
26 changes: 26 additions & 0 deletions
26
...thub/library/pod-security-policy/capabilities/1.1.0/samples/capabilities-demo/update.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
kind: AdmissionReview | ||
apiVersion: admission.k8s.io/v1beta1 | ||
request: | ||
operation: "UPDATE" | ||
object: | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: opa-disallowed | ||
labels: | ||
owner: me.agilebank.demo | ||
spec: | ||
containers: | ||
- name: opa | ||
image: openpolicyagent/opa:0.9.2 | ||
args: | ||
- "run" | ||
- "--server" | ||
- "--addr=localhost:8080" | ||
securityContext: | ||
capabilities: | ||
add: ["disallowedcapability"] | ||
resources: | ||
limits: | ||
cpu: "100m" | ||
memory: "30Mi" |
25 changes: 25 additions & 0 deletions
25
artifacthub/library/pod-security-policy/capabilities/1.1.0/suite.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
kind: Suite | ||
apiVersion: test.gatekeeper.sh/v1alpha1 | ||
metadata: | ||
name: capabilities | ||
tests: | ||
- name: capabilities | ||
template: template.yaml | ||
constraint: samples/capabilities-demo/constraint.yaml | ||
cases: | ||
- name: example-disallowed | ||
object: samples/capabilities-demo/example_disallowed.yaml | ||
assertions: | ||
- violations: yes | ||
- name: example-allowed | ||
object: samples/capabilities-demo/example_allowed.yaml | ||
assertions: | ||
- violations: no | ||
- name: disallowed-ephemeral | ||
object: samples/capabilities-demo/disallowed_ephemeral.yaml | ||
assertions: | ||
- violations: yes | ||
- name: update | ||
object: samples/capabilities-demo/update.yaml | ||
assertions: | ||
- violations: no |
224 changes: 224 additions & 0 deletions
224
artifacthub/library/pod-security-policy/capabilities/1.1.0/template.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
apiVersion: templates.gatekeeper.sh/v1 | ||
kind: ConstraintTemplate | ||
metadata: | ||
name: k8spspcapabilities | ||
annotations: | ||
metadata.gatekeeper.sh/title: "Capabilities" | ||
metadata.gatekeeper.sh/version: 1.1.0 | ||
description: >- | ||
Controls Linux capabilities on containers. Corresponds to the | ||
`allowedCapabilities` and `requiredDropCapabilities` fields in a | ||
PodSecurityPolicy. For more information, see | ||
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#capabilities | ||
spec: | ||
crd: | ||
spec: | ||
names: | ||
kind: K8sPSPCapabilities | ||
validation: | ||
# Schema for the `parameters` field | ||
openAPIV3Schema: | ||
type: object | ||
description: >- | ||
Controls Linux capabilities on containers. Corresponds to the | ||
`allowedCapabilities` and `requiredDropCapabilities` fields in a | ||
PodSecurityPolicy. For more information, see | ||
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#capabilities | ||
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 | ||
allowedCapabilities: | ||
type: array | ||
description: "A list of Linux capabilities that can be added to a container." | ||
items: | ||
type: string | ||
requiredDropCapabilities: | ||
type: array | ||
description: "A list of Linux capabilities that are required to be dropped from a container." | ||
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: allContainers | ||
expression: 'variables.containers + variables.initContainers + variables.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: allowedCapabilities | ||
expression: 'has(variables.params.allowedCapabilities) ? variables.params.allowedCapabilities : []' | ||
- name: allCapabilitiesAllowed | ||
expression: '"*" in variables.allowedCapabilities' | ||
- name: disallowedCapabilitiesByContainer | ||
expression: | | ||
variables.allContainers.map(container, !(container.image in variables.exemptImages) && | ||
!variables.allCapabilitiesAllowed && has(container.securityContext) && has(container.securityContext.capabilities) && has(container.securityContext.capabilities.add) && | ||
container.securityContext.capabilities.add.exists(capability, !(capability in variables.allowedCapabilities)), | ||
[container.name, dyn(container.securityContext.capabilities.add.filter(capability, !(capability in variables.allowedCapabilities)).join(", "))] | ||
) | ||
- name: requiredDropCapabilities | ||
expression: 'has(variables.params.requiredDropCapabilities) ? variables.params.requiredDropCapabilities : []' | ||
- name: missingDropCapabilitiesByContainer | ||
expression: | | ||
variables.allContainers.map(container, !(container.image in variables.exemptImages) && | ||
size(variables.requiredDropCapabilities) > 0 && ( | ||
!has(container.securityContext) || !has(container.securityContext.capabilities) || !has(container.securityContext.capabilities.drop) || ( | ||
!("all" in container.securityContext.capabilities.drop) && | ||
variables.requiredDropCapabilities.exists(capability, !(capability in container.securityContext.capabilities.drop)) | ||
) | ||
), | ||
[container.name, | ||
!has(container.securityContext) ? variables.requiredDropCapabilities : | ||
!has(container.securityContext.capabilities) ? variables.requiredDropCapabilities : | ||
!has(container.securityContext.capabilities.drop) ? variables.requiredDropCapabilities : | ||
variables.requiredDropCapabilities.filter(capability, !(capability in container.securityContext.capabilities.drop)) | ||
] | ||
) | ||
validations: | ||
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.disallowedCapabilitiesByContainer) == 0' | ||
messageExpression: | | ||
"containers have disallowed capabilities: " + variables.disallowedCapabilitiesByContainer.map(pair, "{container: " + pair[0] + ", capabilities: [" + pair[1] + "]}").join(", ") | ||
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.missingDropCapabilitiesByContainer) == 0' | ||
messageExpression: | | ||
"containers are not dropping all required capabilities: " + variables.missingDropCapabilitiesByContainer.map(pair, "{container: " + pair[0] + ", capabilities: [" + pair[1].join(", ") + "]}").join(", ") | ||
- engine: Rego | ||
source: | ||
rego: | | ||
package capabilities | ||
import data.lib.exclude_update.is_update | ||
import data.lib.exempt_container.is_exempt | ||
violation[{"msg": msg}] { | ||
# spec.containers.securityContext.capabilities field is immutable. | ||
not is_update(input.review) | ||
container := input.review.object.spec.containers[_] | ||
not is_exempt(container) | ||
has_disallowed_capabilities(container) | ||
msg := sprintf("container <%v> has a disallowed capability. Allowed capabilities are %v", [container.name, get_default(input.parameters, "allowedCapabilities", "NONE")]) | ||
} | ||
violation[{"msg": msg}] { | ||
not is_update(input.review) | ||
container := input.review.object.spec.containers[_] | ||
not is_exempt(container) | ||
missing_drop_capabilities(container) | ||
msg := sprintf("container <%v> is not dropping all required capabilities. Container must drop all of %v or \"ALL\"", [container.name, input.parameters.requiredDropCapabilities]) | ||
} | ||
violation[{"msg": msg}] { | ||
not is_update(input.review) | ||
container := input.review.object.spec.initContainers[_] | ||
not is_exempt(container) | ||
has_disallowed_capabilities(container) | ||
msg := sprintf("init container <%v> has a disallowed capability. Allowed capabilities are %v", [container.name, get_default(input.parameters, "allowedCapabilities", "NONE")]) | ||
} | ||
violation[{"msg": msg}] { | ||
not is_update(input.review) | ||
container := input.review.object.spec.initContainers[_] | ||
not is_exempt(container) | ||
missing_drop_capabilities(container) | ||
msg := sprintf("init container <%v> is not dropping all required capabilities. Container must drop all of %v or \"ALL\"", [container.name, input.parameters.requiredDropCapabilities]) | ||
} | ||
violation[{"msg": msg}] { | ||
not is_update(input.review) | ||
container := input.review.object.spec.ephemeralContainers[_] | ||
not is_exempt(container) | ||
has_disallowed_capabilities(container) | ||
msg := sprintf("ephemeral container <%v> has a disallowed capability. Allowed capabilities are %v", [container.name, get_default(input.parameters, "allowedCapabilities", "NONE")]) | ||
} | ||
violation[{"msg": msg}] { | ||
not is_update(input.review) | ||
container := input.review.object.spec.ephemeralContainers[_] | ||
not is_exempt(container) | ||
missing_drop_capabilities(container) | ||
msg := sprintf("ephemeral container <%v> is not dropping all required capabilities. Container must drop all of %v or \"ALL\"", [container.name, input.parameters.requiredDropCapabilities]) | ||
} | ||
has_disallowed_capabilities(container) { | ||
allowed := {c | c := lower(input.parameters.allowedCapabilities[_])} | ||
not allowed["*"] | ||
capabilities := {c | c := lower(container.securityContext.capabilities.add[_])} | ||
count(capabilities - allowed) > 0 | ||
} | ||
missing_drop_capabilities(container) { | ||
must_drop := {c | c := lower(input.parameters.requiredDropCapabilities[_])} | ||
all := {"all"} | ||
dropped := {c | c := lower(container.securityContext.capabilities.drop[_])} | ||
count(must_drop - dropped) > 0 | ||
count(all - dropped) > 0 | ||
} | ||
get_default(obj, param, _) := obj[param] | ||
get_default(obj, param, _default) := _default { | ||
not obj[param] | ||
not obj[param] == false | ||
} | ||
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) | ||
} |
Oops, something went wrong.