From 386428f9ff6ca3421c21f93a5adaefb61cc82372 Mon Sep 17 00:00:00 2001 From: Rita Zhang Date: Fri, 5 Jul 2024 17:07:38 -0700 Subject: [PATCH] Add cel for forbidden-sysctls Signed-off-by: Rita Zhang --- .../forbidden-sysctls/1.2.0/README.md | 36 ++ .../1.2.0/artifacthub-pkg.yml | 22 ++ .../1.2.0/kustomization.yaml | 2 + .../psp-forbidden-sysctls/constraint.yaml | 15 + .../psp-forbidden-sysctls/constraint2.yaml | 14 + .../psp-forbidden-sysctls/constraint3.yaml | 15 + .../example_allowed.yaml | 14 + .../example_disallowed.yaml | 16 + .../samples/psp-forbidden-sysctls/update.yaml | 21 ++ .../forbidden-sysctls/1.2.0/suite.yaml | 53 +++ .../forbidden-sysctls/1.2.0/template.yaml | 135 +++++++ .../psp-forbidden-sysctls/constraint2.yaml | 14 + .../psp-forbidden-sysctls/constraint3.yaml | 15 + .../forbidden-sysctls/suite.yaml | 32 ++ .../forbidden-sysctls/template.yaml | 133 ++++--- .../forbidden-sysctls/constraint.tmpl | 18 +- .../forbidden-sysctls/src.cel | 30 ++ website/docs/validation/forbidden-sysctls.md | 340 ++++++++++++++---- 18 files changed, 810 insertions(+), 115 deletions(-) create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/README.md create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/kustomization.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint2.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint3.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_allowed.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_disallowed.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/update.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/suite.yaml create mode 100644 artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/template.yaml create mode 100644 library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint2.yaml create mode 100644 library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint3.yaml create mode 100644 src/pod-security-policy/forbidden-sysctls/src.cel diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/README.md b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/README.md new file mode 100644 index 000000000..d8a40937d --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/README.md @@ -0,0 +1,36 @@ +# Forbidden Sysctls security context policy + +The forbidden sysctls constraint allows one to limit the set of kernel parameters that can be modified by pods. This is accomplished by specifying a combination of allowed and forbidden sysctls using either of two parameters: `allowedSysctls` and `forbiddenSysctls`. + +## Parameters + +`allowedSysctls`: A list of explicitly allowed sysctls. Any sysctl not in this list will be considered forbidden. '*' and trailing wildcards are supported. If unspecified, no limitations are made by this parameter. + +`forbiddenSysctls`: A list of explicitly denied sysctls. Any sysctl in this list will be considered forbidden. '*' and trailing wildcards are supported. If unspecified, no limitations are made by this parameter. + +## Examples + +```yaml +parameters: + allowedSysctls: ['*'] + forbiddenSysctls: + - kernel.msg* + - net.core.somaxconn +``` + +```yaml +parameters: + allowedSysctls: + - kernel.shm_rmid_forced + - net.ipv4.ip_local_port_range + - net.ipv4.tcp_syncookies + - net.ipv4.ping_group_range + forbiddenSysctls: [] +``` + +*Note*: `forbiddenSysctls` takes precedence, such that an explicitly forbidden sysctl is still forbidden even if it appears in `allowedSysctls` as well. However in practice, such overlap between the rules should be avoided. + +## References + +* [Using sysctls in a Kubernetes Cluster](https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/) +* [Kubernetes API Reference - Sysctl](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#sysctl-v1-core) \ No newline at end of file diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/artifacthub-pkg.yml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/artifacthub-pkg.yml new file mode 100644 index 000000000..aae5e46c3 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.2.0 +name: k8spspforbiddensysctls +displayName: Forbidden Sysctls +createdAt: "2024-07-05T17:47:31Z" +description: Controls the `sysctl` profile used by containers. Corresponds to the `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. When specified, any sysctl not in the `allowedSysctls` parameter is considered to be forbidden. The `forbiddenSysctls` parameter takes precedence over the `allowedSysctls` parameter. For more information, see https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +digest: c34167b32bcd5b55b48a68614ad4b20cd26294d17be559738f01735ab719f621 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/forbidden-sysctls +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Forbidden Sysctls + Controls the `sysctl` profile used by containers. Corresponds to the `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. When specified, any sysctl not in the `allowedSysctls` parameter is considered to be forbidden. The `forbiddenSysctls` parameter takes precedence over the `allowedSysctls` parameter. For more information, see https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/kustomization.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint.yaml new file mode 100644 index 000000000..39abf4b23 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint.yaml @@ -0,0 +1,15 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + # - "*" # * may be used to forbid all sysctls + - kernel.* + allowedSysctls: + - "*" # allows all sysctls. allowedSysctls is optional. diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint2.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint2.yaml new file mode 100644 index 000000000..4306dff41 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint2.yaml @@ -0,0 +1,14 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + - "*" # * forbid all sysctls + allowedSysctls: + - "*" # allows all sysctls. allowedSysctls is optional. diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint3.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint3.yaml new file mode 100644 index 000000000..24f0d3905 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/constraint3.yaml @@ -0,0 +1,15 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + # - "*" # * may be used to forbid all sysctls + - kernel.* + allowedSysctls: + - "net.*" # allows all sysctls. allowedSysctls is optional. diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_allowed.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_allowed.yaml new file mode 100644 index 000000000..4b6cc4b66 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_allowed.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls +spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: net.core.somaxconn + value: "1024" diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_disallowed.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_disallowed.yaml new file mode 100644 index 000000000..34ab8f344 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/example_disallowed.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls +spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: kernel.msgmax + value: "65536" + - name: net.core.somaxconn + value: "1024" diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/update.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/update.yaml new file mode 100644 index 000000000..e4e732be9 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/samples/psp-forbidden-sysctls/update.yaml @@ -0,0 +1,21 @@ +kind: AdmissionReview +apiVersion: admission.k8s.io/v1beta1 +request: + operation: "UPDATE" + object: + apiVersion: v1 + kind: Pod + metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls + spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: kernel.msgmax + value: "65536" + - name: net.core.somaxconn + value: "1024" diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/suite.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/suite.yaml new file mode 100644 index 000000000..fe394ddd8 --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/suite.yaml @@ -0,0 +1,53 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: forbidden-sysctls +tests: + - name: forbidden-sysctls + template: template.yaml + constraint: samples/psp-forbidden-sysctls/constraint.yaml + cases: + - name: example-disallowed + object: samples/psp-forbidden-sysctls/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-forbidden-sysctls/example_allowed.yaml + assertions: + - violations: no + - name: update + object: samples/psp-forbidden-sysctls/update.yaml + assertions: + - violations: no + - name: forbidden-sysctls2 + template: template.yaml + constraint: samples/psp-forbidden-sysctls/constraint2.yaml + cases: + - name: example-disallowed + object: samples/psp-forbidden-sysctls/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-forbidden-sysctls/example_allowed.yaml + assertions: + - violations: yes + - name: update + object: samples/psp-forbidden-sysctls/update.yaml + assertions: + - violations: no + - name: forbidden-sysctls3 + template: template.yaml + constraint: samples/psp-forbidden-sysctls/constraint3.yaml + cases: + - name: example-disallowed + object: samples/psp-forbidden-sysctls/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-forbidden-sysctls/example_allowed.yaml + assertions: + - violations: no + - name: update + object: samples/psp-forbidden-sysctls/update.yaml + assertions: + - violations: no diff --git a/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/template.yaml b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/template.yaml new file mode 100644 index 000000000..db4ea623a --- /dev/null +++ b/artifacthub/library/pod-security-policy/forbidden-sysctls/1.2.0/template.yaml @@ -0,0 +1,135 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8spspforbiddensysctls + annotations: + metadata.gatekeeper.sh/title: "Forbidden Sysctls" + metadata.gatekeeper.sh/version: 1.2.0 + description: >- + Controls the `sysctl` profile used by containers. Corresponds to the + `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. + When specified, any sysctl not in the `allowedSysctls` parameter is considered to be forbidden. + The `forbiddenSysctls` parameter takes precedence over the `allowedSysctls` parameter. + For more information, see https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +spec: + crd: + spec: + names: + kind: K8sPSPForbiddenSysctls + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls the `sysctl` profile used by containers. Corresponds to the + `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. + When specified, any sysctl not in the `allowedSysctls` parameter is considered to be forbidden. + The `forbiddenSysctls` parameter takes precedence over the `allowedSysctls` parameter. + For more information, see https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ + properties: + allowedSysctls: + type: array + description: "An allow-list of sysctls. `*` allows all sysctls not listed in the `forbiddenSysctls` parameter." + items: + type: string + forbiddenSysctls: + type: array + description: "A disallow-list of sysctls. `*` forbids all sysctls." + items: + type: string + targets: + - target: admission.k8s.gatekeeper.sh + code: + - engine: K8sNativeValidation + source: + variables: + - name: sysctls + expression: '!has(variables.anyObject.spec.securityContext) ? [] : !has(variables.anyObject.spec.securityContext.sysctls) ? [] : variables.anyObject.spec.securityContext.sysctls' + - name: allowedSysctlPrefixes + expression: | + !has(variables.params.allowedSysctls) ? [] : variables.params.allowedSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) + - name: allowedSysctlExplicit + expression: | + !has(variables.params.allowedSysctls) ? [] : + variables.params.allowedSysctls.filter(sysctl, !sysctl.endsWith("*")) + - name: forbiddenSysctlPrefixes + expression: | + !has(variables.params.forbiddenSysctls) ? [] : variables.params.forbiddenSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) + - name: forbiddenSysctlExplicit + expression: | + !has(variables.params.forbiddenSysctls) ? [] : + variables.params.forbiddenSysctls.filter(sysctl, !sysctl.endsWith("*")) + - name: allAllowed + expression: '!has(variables.params.allowedSysctls) ? true : false' + - name: violatingSysctls + expression: | + (variables.sysctls.filter(sysctl, + (sysctl.name in variables.forbiddenSysctlExplicit || + variables.forbiddenSysctlPrefixes.exists(fsp, string(sysctl.name).startsWith(fsp))) || + (!variables.allAllowed && + !(sysctl.name in variables.allowedSysctlExplicit) && + !variables.allowedSysctlPrefixes.exists(asp, string(sysctl.name).startsWith(asp))))) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.violatingSysctls) == 0' + messageExpression: '"The sysctl is not allowed for pod: " + variables.anyObject.metadata.name + ", forbidden: " + variables.forbiddenSysctls.map(c, c).join(", ") + ", allowed: " + variables.allowedSysctls.map(c, c).join(", ")' + - engine: Rego + source: + rego: | + package k8spspforbiddensysctls + + import data.lib.exclude_update.is_update + + # Block if forbidden + violation[{"msg": msg, "details": {}}] { + # spec.securityContext.sysctls field is immutable. + not is_update(input.review) + + sysctl := input.review.object.spec.securityContext.sysctls[_].name + forbidden_sysctl(sysctl) + msg := sprintf("The sysctl %v is not allowed, pod: %v. Forbidden sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.forbiddenSysctls]) + } + + # Block if not explicitly allowed + violation[{"msg": msg, "details": {}}] { + not is_update(input.review) + sysctl := input.review.object.spec.securityContext.sysctls[_].name + not allowed_sysctl(sysctl) + msg := sprintf("The sysctl %v is not explicitly allowed, pod: %v. Allowed sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.allowedSysctls]) + } + + # * may be used to forbid all sysctls + forbidden_sysctl(_) { + input.parameters.forbiddenSysctls[_] == "*" + } + + forbidden_sysctl(sysctl) { + input.parameters.forbiddenSysctls[_] == sysctl + } + + forbidden_sysctl(sysctl) { + forbidden := input.parameters.forbiddenSysctls[_] + endswith(forbidden, "*") + startswith(sysctl, trim_suffix(forbidden, "*")) + } + + # * may be used to allow all sysctls + allowed_sysctl(_) { + input.parameters.allowedSysctls[_] == "*" + } + + allowed_sysctl(sysctl) { + input.parameters.allowedSysctls[_] == sysctl + } + + allowed_sysctl(sysctl) { + allowed := input.parameters.allowedSysctls[_] + endswith(allowed, "*") + startswith(sysctl, trim_suffix(allowed, "*")) + } + libs: + - | + package lib.exclude_update + + is_update(review) { + review.operation == "UPDATE" + } diff --git a/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint2.yaml b/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint2.yaml new file mode 100644 index 000000000..4306dff41 --- /dev/null +++ b/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint2.yaml @@ -0,0 +1,14 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + - "*" # * forbid all sysctls + allowedSysctls: + - "*" # allows all sysctls. allowedSysctls is optional. diff --git a/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint3.yaml b/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint3.yaml new file mode 100644 index 000000000..24f0d3905 --- /dev/null +++ b/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint3.yaml @@ -0,0 +1,15 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + # - "*" # * may be used to forbid all sysctls + - kernel.* + allowedSysctls: + - "net.*" # allows all sysctls. allowedSysctls is optional. diff --git a/library/pod-security-policy/forbidden-sysctls/suite.yaml b/library/pod-security-policy/forbidden-sysctls/suite.yaml index d00f85b8b..fe394ddd8 100644 --- a/library/pod-security-policy/forbidden-sysctls/suite.yaml +++ b/library/pod-security-policy/forbidden-sysctls/suite.yaml @@ -19,3 +19,35 @@ tests: object: samples/psp-forbidden-sysctls/update.yaml assertions: - violations: no + - name: forbidden-sysctls2 + template: template.yaml + constraint: samples/psp-forbidden-sysctls/constraint2.yaml + cases: + - name: example-disallowed + object: samples/psp-forbidden-sysctls/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-forbidden-sysctls/example_allowed.yaml + assertions: + - violations: yes + - name: update + object: samples/psp-forbidden-sysctls/update.yaml + assertions: + - violations: no + - name: forbidden-sysctls3 + template: template.yaml + constraint: samples/psp-forbidden-sysctls/constraint3.yaml + cases: + - name: example-disallowed + object: samples/psp-forbidden-sysctls/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-forbidden-sysctls/example_allowed.yaml + assertions: + - violations: no + - name: update + object: samples/psp-forbidden-sysctls/update.yaml + assertions: + - violations: no diff --git a/library/pod-security-policy/forbidden-sysctls/template.yaml b/library/pod-security-policy/forbidden-sysctls/template.yaml index 053c0eae5..db4ea623a 100644 --- a/library/pod-security-policy/forbidden-sysctls/template.yaml +++ b/library/pod-security-policy/forbidden-sysctls/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8spspforbiddensysctls annotations: metadata.gatekeeper.sh/title: "Forbidden Sysctls" - metadata.gatekeeper.sh/version: 1.1.3 + metadata.gatekeeper.sh/version: 1.2.0 description: >- Controls the `sysctl` profile used by containers. Corresponds to the `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. @@ -39,62 +39,97 @@ spec: type: string targets: - target: admission.k8s.gatekeeper.sh - rego: | - package k8spspforbiddensysctls + code: + - engine: K8sNativeValidation + source: + variables: + - name: sysctls + expression: '!has(variables.anyObject.spec.securityContext) ? [] : !has(variables.anyObject.spec.securityContext.sysctls) ? [] : variables.anyObject.spec.securityContext.sysctls' + - name: allowedSysctlPrefixes + expression: | + !has(variables.params.allowedSysctls) ? [] : variables.params.allowedSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) + - name: allowedSysctlExplicit + expression: | + !has(variables.params.allowedSysctls) ? [] : + variables.params.allowedSysctls.filter(sysctl, !sysctl.endsWith("*")) + - name: forbiddenSysctlPrefixes + expression: | + !has(variables.params.forbiddenSysctls) ? [] : variables.params.forbiddenSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) + - name: forbiddenSysctlExplicit + expression: | + !has(variables.params.forbiddenSysctls) ? [] : + variables.params.forbiddenSysctls.filter(sysctl, !sysctl.endsWith("*")) + - name: allAllowed + expression: '!has(variables.params.allowedSysctls) ? true : false' + - name: violatingSysctls + expression: | + (variables.sysctls.filter(sysctl, + (sysctl.name in variables.forbiddenSysctlExplicit || + variables.forbiddenSysctlPrefixes.exists(fsp, string(sysctl.name).startsWith(fsp))) || + (!variables.allAllowed && + !(sysctl.name in variables.allowedSysctlExplicit) && + !variables.allowedSysctlPrefixes.exists(asp, string(sysctl.name).startsWith(asp))))) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.violatingSysctls) == 0' + messageExpression: '"The sysctl is not allowed for pod: " + variables.anyObject.metadata.name + ", forbidden: " + variables.forbiddenSysctls.map(c, c).join(", ") + ", allowed: " + variables.allowedSysctls.map(c, c).join(", ")' + - engine: Rego + source: + rego: | + package k8spspforbiddensysctls - import data.lib.exclude_update.is_update + import data.lib.exclude_update.is_update - # Block if forbidden - violation[{"msg": msg, "details": {}}] { - # spec.securityContext.sysctls field is immutable. - not is_update(input.review) + # Block if forbidden + violation[{"msg": msg, "details": {}}] { + # spec.securityContext.sysctls field is immutable. + not is_update(input.review) - sysctl := input.review.object.spec.securityContext.sysctls[_].name - forbidden_sysctl(sysctl) - msg := sprintf("The sysctl %v is not allowed, pod: %v. Forbidden sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.forbiddenSysctls]) - } + sysctl := input.review.object.spec.securityContext.sysctls[_].name + forbidden_sysctl(sysctl) + msg := sprintf("The sysctl %v is not allowed, pod: %v. Forbidden sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.forbiddenSysctls]) + } - # Block if not explicitly allowed - violation[{"msg": msg, "details": {}}] { - not is_update(input.review) - sysctl := input.review.object.spec.securityContext.sysctls[_].name - not allowed_sysctl(sysctl) - msg := sprintf("The sysctl %v is not explicitly allowed, pod: %v. Allowed sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.allowedSysctls]) - } + # Block if not explicitly allowed + violation[{"msg": msg, "details": {}}] { + not is_update(input.review) + sysctl := input.review.object.spec.securityContext.sysctls[_].name + not allowed_sysctl(sysctl) + msg := sprintf("The sysctl %v is not explicitly allowed, pod: %v. Allowed sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.allowedSysctls]) + } - # * may be used to forbid all sysctls - forbidden_sysctl(_) { - input.parameters.forbiddenSysctls[_] == "*" - } + # * may be used to forbid all sysctls + forbidden_sysctl(_) { + input.parameters.forbiddenSysctls[_] == "*" + } - forbidden_sysctl(sysctl) { - input.parameters.forbiddenSysctls[_] == sysctl - } + forbidden_sysctl(sysctl) { + input.parameters.forbiddenSysctls[_] == sysctl + } - forbidden_sysctl(sysctl) { - forbidden := input.parameters.forbiddenSysctls[_] - endswith(forbidden, "*") - startswith(sysctl, trim_suffix(forbidden, "*")) - } + forbidden_sysctl(sysctl) { + forbidden := input.parameters.forbiddenSysctls[_] + endswith(forbidden, "*") + startswith(sysctl, trim_suffix(forbidden, "*")) + } - # * may be used to allow all sysctls - allowed_sysctl(_) { - input.parameters.allowedSysctls[_] == "*" - } + # * may be used to allow all sysctls + allowed_sysctl(_) { + input.parameters.allowedSysctls[_] == "*" + } - allowed_sysctl(sysctl) { - input.parameters.allowedSysctls[_] == sysctl - } + allowed_sysctl(sysctl) { + input.parameters.allowedSysctls[_] == sysctl + } - allowed_sysctl(sysctl) { - allowed := input.parameters.allowedSysctls[_] - endswith(allowed, "*") - startswith(sysctl, trim_suffix(allowed, "*")) - } - libs: - - | - package lib.exclude_update + allowed_sysctl(sysctl) { + allowed := input.parameters.allowedSysctls[_] + endswith(allowed, "*") + startswith(sysctl, trim_suffix(allowed, "*")) + } + libs: + - | + package lib.exclude_update - is_update(review) { - review.operation == "UPDATE" - } + is_update(review) { + review.operation == "UPDATE" + } diff --git a/src/pod-security-policy/forbidden-sysctls/constraint.tmpl b/src/pod-security-policy/forbidden-sysctls/constraint.tmpl index 87e4d23a9..28444f130 100644 --- a/src/pod-security-policy/forbidden-sysctls/constraint.tmpl +++ b/src/pod-security-policy/forbidden-sysctls/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8spspforbiddensysctls annotations: metadata.gatekeeper.sh/title: "Forbidden Sysctls" - metadata.gatekeeper.sh/version: 1.1.3 + metadata.gatekeeper.sh/version: 1.2.0 description: >- Controls the `sysctl` profile used by containers. Corresponds to the `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. @@ -39,8 +39,14 @@ spec: type: string targets: - target: admission.k8s.gatekeeper.sh - rego: | -{{ file.Read "src/pod-security-policy/forbidden-sysctls/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} - libs: - - | -{{ file.Read "src/pod-security-policy/forbidden-sysctls/lib_exclude_update.rego" | strings.Indent 10 | strings.TrimSuffix "\n" }} + code: + - engine: K8sNativeValidation + source: +{{ file.Read "src/pod-security-policy/forbidden-sysctls/src.cel" | strings.Indent 10 | strings.TrimSuffix "\n" }} + - engine: Rego + source: + rego: | +{{ file.Read "src/pod-security-policy/forbidden-sysctls/src.rego" | strings.Indent 12 | strings.TrimSuffix "\n" }} + libs: + - | +{{ file.Read "src/pod-security-policy/forbidden-sysctls/lib_exclude_update.rego" | strings.Indent 14 | strings.TrimSuffix "\n" }} diff --git a/src/pod-security-policy/forbidden-sysctls/src.cel b/src/pod-security-policy/forbidden-sysctls/src.cel new file mode 100644 index 000000000..17f2a52fc --- /dev/null +++ b/src/pod-security-policy/forbidden-sysctls/src.cel @@ -0,0 +1,30 @@ +variables: +- name: sysctls + expression: '!has(variables.anyObject.spec.securityContext) ? [] : !has(variables.anyObject.spec.securityContext.sysctls) ? [] : variables.anyObject.spec.securityContext.sysctls' +- name: allowedSysctlPrefixes + expression: | + !has(variables.params.allowedSysctls) ? [] : variables.params.allowedSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) +- name: allowedSysctlExplicit + expression: | + !has(variables.params.allowedSysctls) ? [] : + variables.params.allowedSysctls.filter(sysctl, !sysctl.endsWith("*")) +- name: forbiddenSysctlPrefixes + expression: | + !has(variables.params.forbiddenSysctls) ? [] : variables.params.forbiddenSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) +- name: forbiddenSysctlExplicit + expression: | + !has(variables.params.forbiddenSysctls) ? [] : + variables.params.forbiddenSysctls.filter(sysctl, !sysctl.endsWith("*")) +- name: allAllowed + expression: '!has(variables.params.allowedSysctls) ? true : false' +- name: violatingSysctls + expression: | + (variables.sysctls.filter(sysctl, + (sysctl.name in variables.forbiddenSysctlExplicit || + variables.forbiddenSysctlPrefixes.exists(fsp, string(sysctl.name).startsWith(fsp))) || + (!variables.allAllowed && + !(sysctl.name in variables.allowedSysctlExplicit) && + !variables.allowedSysctlPrefixes.exists(asp, string(sysctl.name).startsWith(asp))))) +validations: +- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.violatingSysctls) == 0' + messageExpression: '"The sysctl is not allowed for pod: " + variables.anyObject.metadata.name + ", forbidden: " + variables.forbiddenSysctls.map(c, c).join(", ") + ", allowed: " + variables.allowedSysctls.map(c, c).join(", ")' diff --git a/website/docs/validation/forbidden-sysctls.md b/website/docs/validation/forbidden-sysctls.md index 279b406a8..63ef43610 100644 --- a/website/docs/validation/forbidden-sysctls.md +++ b/website/docs/validation/forbidden-sysctls.md @@ -16,7 +16,7 @@ metadata: name: k8spspforbiddensysctls annotations: metadata.gatekeeper.sh/title: "Forbidden Sysctls" - metadata.gatekeeper.sh/version: 1.1.3 + metadata.gatekeeper.sh/version: 1.2.0 description: >- Controls the `sysctl` profile used by containers. Corresponds to the `allowedUnsafeSysctls` and `forbiddenSysctls` fields in a PodSecurityPolicy. @@ -51,65 +51,100 @@ spec: type: string targets: - target: admission.k8s.gatekeeper.sh - rego: | - package k8spspforbiddensysctls - - import data.lib.exclude_update.is_update - - # Block if forbidden - violation[{"msg": msg, "details": {}}] { - # spec.securityContext.sysctls field is immutable. - not is_update(input.review) - - sysctl := input.review.object.spec.securityContext.sysctls[_].name - forbidden_sysctl(sysctl) - msg := sprintf("The sysctl %v is not allowed, pod: %v. Forbidden sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.forbiddenSysctls]) - } - - # Block if not explicitly allowed - violation[{"msg": msg, "details": {}}] { - not is_update(input.review) - sysctl := input.review.object.spec.securityContext.sysctls[_].name - not allowed_sysctl(sysctl) - msg := sprintf("The sysctl %v is not explicitly allowed, pod: %v. Allowed sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.allowedSysctls]) - } - - # * may be used to forbid all sysctls - forbidden_sysctl(_) { - input.parameters.forbiddenSysctls[_] == "*" - } - - forbidden_sysctl(sysctl) { - input.parameters.forbiddenSysctls[_] == sysctl - } - - forbidden_sysctl(sysctl) { - forbidden := input.parameters.forbiddenSysctls[_] - endswith(forbidden, "*") - startswith(sysctl, trim_suffix(forbidden, "*")) - } - - # * may be used to allow all sysctls - allowed_sysctl(_) { - input.parameters.allowedSysctls[_] == "*" - } - - allowed_sysctl(sysctl) { - input.parameters.allowedSysctls[_] == sysctl - } - - allowed_sysctl(sysctl) { - allowed := input.parameters.allowedSysctls[_] - endswith(allowed, "*") - startswith(sysctl, trim_suffix(allowed, "*")) - } - libs: - - | - package lib.exclude_update - - is_update(review) { - review.operation == "UPDATE" - } + code: + - engine: K8sNativeValidation + source: + variables: + - name: sysctls + expression: '!has(variables.anyObject.spec.securityContext) ? [] : !has(variables.anyObject.spec.securityContext.sysctls) ? [] : variables.anyObject.spec.securityContext.sysctls' + - name: allowedSysctlPrefixes + expression: | + !has(variables.params.allowedSysctls) ? [] : variables.params.allowedSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) + - name: allowedSysctlExplicit + expression: | + !has(variables.params.allowedSysctls) ? [] : + variables.params.allowedSysctls.filter(sysctl, !sysctl.endsWith("*")) + - name: forbiddenSysctlPrefixes + expression: | + !has(variables.params.forbiddenSysctls) ? [] : variables.params.forbiddenSysctls.filter(sysctl, sysctl.endsWith("*")).map(sysctl, string(sysctl).replace("*", "")) + - name: forbiddenSysctlExplicit + expression: | + !has(variables.params.forbiddenSysctls) ? [] : + variables.params.forbiddenSysctls.filter(sysctl, !sysctl.endsWith("*")) + - name: allAllowed + expression: '!has(variables.params.allowedSysctls) ? true : false' + - name: violatingSysctls + expression: | + (variables.sysctls.filter(sysctl, + (sysctl.name in variables.forbiddenSysctlExplicit || + variables.forbiddenSysctlPrefixes.exists(fsp, string(sysctl.name).startsWith(fsp))) || + (!variables.allAllowed && + !(sysctl.name in variables.allowedSysctlExplicit) && + !variables.allowedSysctlPrefixes.exists(asp, string(sysctl.name).startsWith(asp))))) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.violatingSysctls) == 0' + messageExpression: '"The sysctl is not allowed for pod: " + variables.anyObject.metadata.name + ", forbidden: " + variables.forbiddenSysctls.map(c, c).join(", ") + ", allowed: " + variables.allowedSysctls.map(c, c).join(", ")' + - engine: Rego + source: + rego: | + package k8spspforbiddensysctls + + import data.lib.exclude_update.is_update + + # Block if forbidden + violation[{"msg": msg, "details": {}}] { + # spec.securityContext.sysctls field is immutable. + not is_update(input.review) + + sysctl := input.review.object.spec.securityContext.sysctls[_].name + forbidden_sysctl(sysctl) + msg := sprintf("The sysctl %v is not allowed, pod: %v. Forbidden sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.forbiddenSysctls]) + } + + # Block if not explicitly allowed + violation[{"msg": msg, "details": {}}] { + not is_update(input.review) + sysctl := input.review.object.spec.securityContext.sysctls[_].name + not allowed_sysctl(sysctl) + msg := sprintf("The sysctl %v is not explicitly allowed, pod: %v. Allowed sysctls: %v", [sysctl, input.review.object.metadata.name, input.parameters.allowedSysctls]) + } + + # * may be used to forbid all sysctls + forbidden_sysctl(_) { + input.parameters.forbiddenSysctls[_] == "*" + } + + forbidden_sysctl(sysctl) { + input.parameters.forbiddenSysctls[_] == sysctl + } + + forbidden_sysctl(sysctl) { + forbidden := input.parameters.forbiddenSysctls[_] + endswith(forbidden, "*") + startswith(sysctl, trim_suffix(forbidden, "*")) + } + + # * may be used to allow all sysctls + allowed_sysctl(_) { + input.parameters.allowedSysctls[_] == "*" + } + + allowed_sysctl(sysctl) { + input.parameters.allowedSysctls[_] == sysctl + } + + allowed_sysctl(sysctl) { + allowed := input.parameters.allowedSysctls[_] + endswith(allowed, "*") + startswith(sysctl, trim_suffix(allowed, "*")) + } + libs: + - | + package lib.exclude_update + + is_update(review) { + review.operation == "UPDATE" + } ``` @@ -211,4 +246,189 @@ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper- +
+forbidden-sysctls2 + +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + - "*" # * forbid all sysctls + allowedSysctls: + - "*" # allows all sysctls. allowedSysctls is optional. + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint2.yaml +``` + +
+ +
+example-disallowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls +spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: kernel.msgmax + value: "65536" + - name: net.core.somaxconn + value: "1024" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/example_disallowed.yaml +``` + +
+
+example-allowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls +spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: net.core.somaxconn + value: "1024" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/example_allowed.yaml +``` + +
+ + +
+forbidden-sysctls3 + +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPForbiddenSysctls +metadata: + name: psp-forbidden-sysctls +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + forbiddenSysctls: + # - "*" # * may be used to forbid all sysctls + - kernel.* + allowedSysctls: + - "net.*" # allows all sysctls. allowedSysctls is optional. + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/constraint3.yaml +``` + +
+ +
+example-disallowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls +spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: kernel.msgmax + value: "65536" + - name: net.core.somaxconn + value: "1024" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/example_disallowed.yaml +``` + +
+
+example-allowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-forbidden-sysctls-disallowed + labels: + app: nginx-forbidden-sysctls +spec: + containers: + - name: nginx + image: nginx + securityContext: + sysctls: + - name: net.core.somaxconn + value: "1024" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/forbidden-sysctls/samples/psp-forbidden-sysctls/example_allowed.yaml +``` + +
+ +
\ No newline at end of file