From ba4e29fb3bb1e6efabbfb1393294d2dd760befd4 Mon Sep 17 00:00:00 2001 From: Rita Zhang Date: Mon, 8 Jul 2024 21:13:03 -0700 Subject: [PATCH] Add cel for fsgroup Signed-off-by: Rita Zhang --- .../samples/psp-fsgroup/constraint2.yaml | 13 + .../fsgroup/1.1.0/README.md | 7 + .../fsgroup/1.1.0/artifacthub-pkg.yml | 22 ++ .../fsgroup/1.1.0/kustomization.yaml | 2 + .../1.1.0/samples/psp-fsgroup/constraint.yaml | 14 ++ .../samples/psp-fsgroup/constraint2.yaml | 13 + .../samples/psp-fsgroup/example_allowed.yaml | 17 ++ .../psp-fsgroup/example_disallowed.yaml | 17 ++ .../1.1.0/samples/psp-fsgroup/update.yaml | 22 ++ .../fsgroup/1.1.0/suite.yaml | 37 +++ .../fsgroup/1.1.0/template.yaml | 119 +++++++++ .../samples/psp-fsgroup/constraint2.yaml | 13 + .../pod-security-policy/fsgroup/suite.yaml | 16 ++ .../pod-security-policy/fsgroup/template.yaml | 126 +++++----- .../fsgroup/constraint.tmpl | 18 +- src/pod-security-policy/fsgroup/src.cel | 9 + src/pod-security-policy/fsgroup/src.rego | 2 +- website/docs/validation/fsgroup.md | 229 +++++++++++++----- 18 files changed, 573 insertions(+), 123 deletions(-) create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.0.2/samples/psp-fsgroup/constraint2.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/README.md create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/kustomization.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint2.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_allowed.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_disallowed.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/update.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/suite.yaml create mode 100644 artifacthub/library/pod-security-policy/fsgroup/1.1.0/template.yaml create mode 100644 library/pod-security-policy/fsgroup/samples/psp-fsgroup/constraint2.yaml create mode 100644 src/pod-security-policy/fsgroup/src.cel diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.0.2/samples/psp-fsgroup/constraint2.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.0.2/samples/psp-fsgroup/constraint2.yaml new file mode 100644 index 0000000000..64739ab946 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.0.2/samples/psp-fsgroup/constraint2.yaml @@ -0,0 +1,13 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPFSGroup +metadata: + name: psp-fsgroup +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + ranges: + - min: 1 + max: 1000 diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/README.md b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/README.md new file mode 100644 index 0000000000..b70d94d454 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/README.md @@ -0,0 +1,7 @@ +# Deprecated + +**This Policy is deprecated** + +Please use the FSGroup settings on the users policy to enforce FSGroup Settings. + +[Users Policy](../users) diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/artifacthub-pkg.yml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/artifacthub-pkg.yml new file mode 100644 index 0000000000..cf13202239 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.1.0 +name: k8spspfsgroup +displayName: FS Group +createdAt: "2024-07-08T22:14:40Z" +description: Controls allocating an FSGroup that owns the Pod's volumes. Corresponds to the `fsGroup` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems +digest: a9a225993a5a5cb56c9fc4ee947b7756fde6025498a7e9dd66fb67718b59d24a +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/fsgroup +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # FS Group + Controls allocating an FSGroup that owns the Pod's volumes. Corresponds to the `fsGroup` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/fsgroup/1.1.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/kustomization.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/kustomization.yaml new file mode 100644 index 0000000000..7d70d11b71 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint.yaml new file mode 100644 index 0000000000..4eb14fe3c2 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint.yaml @@ -0,0 +1,14 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPFSGroup +metadata: + name: psp-fsgroup +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + rule: "MayRunAs" #"MustRunAs" #"MayRunAs", "RunAsAny" + ranges: + - min: 1 + max: 1000 diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint2.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint2.yaml new file mode 100644 index 0000000000..64739ab946 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/constraint2.yaml @@ -0,0 +1,13 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPFSGroup +metadata: + name: psp-fsgroup +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + ranges: + - min: 1 + max: 1000 diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_allowed.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_allowed.yaml new file mode 100644 index 0000000000..17d3274c32 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_allowed.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: + name: fsgroup-disallowed +spec: + securityContext: + fsGroup: 500 # directory will have group ID 500 + volumes: + - name: fsgroup-demo-vol + emptyDir: {} + containers: + - name: fsgroup-demo + image: busybox + command: ["sh", "-c", "sleep 1h"] + volumeMounts: + - name: fsgroup-demo-vol + mountPath: /data/demo diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_disallowed.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_disallowed.yaml new file mode 100644 index 0000000000..9caf7c0a31 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/example_disallowed.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: + name: fsgroup-disallowed +spec: + securityContext: + fsGroup: 2000 # directory will have group ID 2000 + volumes: + - name: fsgroup-demo-vol + emptyDir: {} + containers: + - name: fsgroup-demo + image: busybox + command: [ "sh", "-c", "sleep 1h" ] + volumeMounts: + - name: fsgroup-demo-vol + mountPath: /data/demo diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/update.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/update.yaml new file mode 100644 index 0000000000..c0de7258a1 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/samples/psp-fsgroup/update.yaml @@ -0,0 +1,22 @@ +kind: AdmissionReview +apiVersion: admission.k8s.io/v1beta1 +request: + operation: "UPDATE" + object: + apiVersion: v1 + kind: Pod + metadata: + name: fsgroup-disallowed + spec: + securityContext: + fsGroup: 2000 # directory will have group ID 2000 + volumes: + - name: fsgroup-demo-vol + emptyDir: {} + containers: + - name: fsgroup-demo + image: busybox + command: [ "sh", "-c", "sleep 1h" ] + volumeMounts: + - name: fsgroup-demo-vol + mountPath: /data/demo diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/suite.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/suite.yaml new file mode 100644 index 0000000000..2b9f62d903 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/suite.yaml @@ -0,0 +1,37 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: fsgroup +tests: + - name: fsgroup + template: template.yaml + constraint: samples/psp-fsgroup/constraint.yaml + cases: + - name: example-disallowed + object: samples/psp-fsgroup/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-fsgroup/example_allowed.yaml + assertions: + - violations: no + - name: update + object: samples/psp-fsgroup/update.yaml + assertions: + - violations: no + - name: fsgroup2 + template: template.yaml + constraint: samples/psp-fsgroup/constraint2.yaml + cases: + - name: example-disallowed + object: samples/psp-fsgroup/example_disallowed.yaml + assertions: + - violations: no + - name: example-allowed + object: samples/psp-fsgroup/example_allowed.yaml + assertions: + - violations: no + - name: update + object: samples/psp-fsgroup/update.yaml + assertions: + - violations: no diff --git a/artifacthub/library/pod-security-policy/fsgroup/1.1.0/template.yaml b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/template.yaml new file mode 100644 index 0000000000..31cdcc0376 --- /dev/null +++ b/artifacthub/library/pod-security-policy/fsgroup/1.1.0/template.yaml @@ -0,0 +1,119 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8spspfsgroup + annotations: + metadata.gatekeeper.sh/title: "FS Group" + metadata.gatekeeper.sh/version: 1.1.0 + description: >- + Controls allocating an FSGroup that owns the Pod's volumes. Corresponds + to the `fsGroup` field in a PodSecurityPolicy. For more information, see + https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems +spec: + crd: + spec: + names: + kind: K8sPSPFSGroup + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls allocating an FSGroup that owns the Pod's volumes. Corresponds + to the `fsGroup` field in a PodSecurityPolicy. For more information, see + https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems + properties: + rule: + description: "An FSGroup rule name." + enum: + - MayRunAs + - MustRunAs + - RunAsAny + type: string + ranges: + type: array + description: "GID ranges affected by the rule." + items: + type: object + properties: + min: + description: "The minimum GID in the range, inclusive." + type: integer + max: + description: "The maximum GID in the range, inclusive." + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + code: + - engine: K8sNativeValidation + source: + variables: + - name: fsGroup + expression: '!has(variables.anyObject.spec.securityContext) ? "" : !has(variables.anyObject.spec.securityContext.fsGroup) ? "" : variables.anyObject.spec.securityContext.fsGroup' + - name: input_fsGroup_allowed + expression: | + !has(variables.params.rule) ? true : variables.params.rule == "RunAsAny" ? true : variables.params.rule == "MayRunAs" && variables.fsGroup == "" ? true : (variables.params.rule == "MayRunAs" || variables.params.rule == "MustRunAs") && has(variables.params.ranges) && size(variables.params.ranges) > 0 ? variables.params.ranges.all(range, range.min <= variables.fsGroup && range.max >= variables.fsGroup) : false + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || variables.input_fsGroup_allowed' + messageExpression: '"The provided pod spec fsGroup is not allowed, pod: " + variables.anyObject.metadata.name + ". Allowed fsGroup: " + variables.params.rule' + - engine: Rego + source: + rego: | + package k8spspfsgroup + + import data.lib.exclude_update.is_update + + violation[{"msg": msg, "details": {}}] { + # spec.securityContext.fsGroup field is immutable. + not is_update(input.review) + has_field(input.parameters, "rule") + spec := input.review.object.spec + not input_fsGroup_allowed(spec) + msg := sprintf("The provided pod spec fsGroup is not allowed, pod: %v. Allowed fsGroup: %v", [input.review.object.metadata.name, input.parameters]) + } + + input_fsGroup_allowed(_) { + # RunAsAny - No range is required. Allows any fsGroup ID to be specified. + input.parameters.rule == "RunAsAny" + } + input_fsGroup_allowed(spec) { + # MustRunAs - Validates pod spec fsgroup against all ranges + input.parameters.rule == "MustRunAs" + fg := spec.securityContext.fsGroup + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, fg) + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + not has_field(spec, "securityContext") + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + not spec.securityContext.fsGroup + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + fg := spec.securityContext.fsGroup + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, fg) + } + value_within_range(range, value) { + range.min <= value + range.max >= value + } + # 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" + } diff --git a/library/pod-security-policy/fsgroup/samples/psp-fsgroup/constraint2.yaml b/library/pod-security-policy/fsgroup/samples/psp-fsgroup/constraint2.yaml new file mode 100644 index 0000000000..64739ab946 --- /dev/null +++ b/library/pod-security-policy/fsgroup/samples/psp-fsgroup/constraint2.yaml @@ -0,0 +1,13 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPFSGroup +metadata: + name: psp-fsgroup +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + ranges: + - min: 1 + max: 1000 diff --git a/library/pod-security-policy/fsgroup/suite.yaml b/library/pod-security-policy/fsgroup/suite.yaml index cb102e785e..2b9f62d903 100644 --- a/library/pod-security-policy/fsgroup/suite.yaml +++ b/library/pod-security-policy/fsgroup/suite.yaml @@ -19,3 +19,19 @@ tests: object: samples/psp-fsgroup/update.yaml assertions: - violations: no + - name: fsgroup2 + template: template.yaml + constraint: samples/psp-fsgroup/constraint2.yaml + cases: + - name: example-disallowed + object: samples/psp-fsgroup/example_disallowed.yaml + assertions: + - violations: no + - name: example-allowed + object: samples/psp-fsgroup/example_allowed.yaml + assertions: + - violations: no + - name: update + object: samples/psp-fsgroup/update.yaml + assertions: + - violations: no diff --git a/library/pod-security-policy/fsgroup/template.yaml b/library/pod-security-policy/fsgroup/template.yaml index 4e1b9449af..31cdcc0376 100644 --- a/library/pod-security-policy/fsgroup/template.yaml +++ b/library/pod-security-policy/fsgroup/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8spspfsgroup annotations: metadata.gatekeeper.sh/title: "FS Group" - metadata.gatekeeper.sh/version: 1.0.2 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Controls allocating an FSGroup that owns the Pod's volumes. Corresponds to the `fsGroup` field in a PodSecurityPolicy. For more information, see @@ -44,62 +44,76 @@ spec: type: integer targets: - target: admission.k8s.gatekeeper.sh - rego: | - package k8spspfsgroup + code: + - engine: K8sNativeValidation + source: + variables: + - name: fsGroup + expression: '!has(variables.anyObject.spec.securityContext) ? "" : !has(variables.anyObject.spec.securityContext.fsGroup) ? "" : variables.anyObject.spec.securityContext.fsGroup' + - name: input_fsGroup_allowed + expression: | + !has(variables.params.rule) ? true : variables.params.rule == "RunAsAny" ? true : variables.params.rule == "MayRunAs" && variables.fsGroup == "" ? true : (variables.params.rule == "MayRunAs" || variables.params.rule == "MustRunAs") && has(variables.params.ranges) && size(variables.params.ranges) > 0 ? variables.params.ranges.all(range, range.min <= variables.fsGroup && range.max >= variables.fsGroup) : false + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || variables.input_fsGroup_allowed' + messageExpression: '"The provided pod spec fsGroup is not allowed, pod: " + variables.anyObject.metadata.name + ". Allowed fsGroup: " + variables.params.rule' + - engine: Rego + source: + rego: | + package k8spspfsgroup - import data.lib.exclude_update.is_update + import data.lib.exclude_update.is_update - violation[{"msg": msg, "details": {}}] { - # spec.securityContext.fsGroup field is immutable. - not is_update(input.review) + violation[{"msg": msg, "details": {}}] { + # spec.securityContext.fsGroup field is immutable. + not is_update(input.review) + has_field(input.parameters, "rule") + spec := input.review.object.spec + not input_fsGroup_allowed(spec) + msg := sprintf("The provided pod spec fsGroup is not allowed, pod: %v. Allowed fsGroup: %v", [input.review.object.metadata.name, input.parameters]) + } - spec := input.review.object.spec - not input_fsGroup_allowed(spec) - msg := sprintf("The provided pod spec fsGroup is not allowed, pod: %v. Allowed fsGroup: %v", [input.review.object.metadata.name, input.parameters]) - } + input_fsGroup_allowed(_) { + # RunAsAny - No range is required. Allows any fsGroup ID to be specified. + input.parameters.rule == "RunAsAny" + } + input_fsGroup_allowed(spec) { + # MustRunAs - Validates pod spec fsgroup against all ranges + input.parameters.rule == "MustRunAs" + fg := spec.securityContext.fsGroup + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, fg) + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + not has_field(spec, "securityContext") + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + not spec.securityContext.fsGroup + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + fg := spec.securityContext.fsGroup + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, fg) + } + value_within_range(range, value) { + range.min <= value + range.max >= value + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } + libs: + - | + package lib.exclude_update - input_fsGroup_allowed(_) { - # RunAsAny - No range is required. Allows any fsGroup ID to be specified. - input.parameters.rule == "RunAsAny" - } - input_fsGroup_allowed(spec) { - # MustRunAs - Validates pod spec fsgroup against all ranges - input.parameters.rule == "MustRunAs" - fg := spec.securityContext.fsGroup - count(input.parameters.ranges) > 0 - range := input.parameters.ranges[_] - value_within_range(range, fg) - } - input_fsGroup_allowed(spec) { - # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset - input.parameters.rule == "MayRunAs" - not has_field(spec, "securityContext") - } - input_fsGroup_allowed(spec) { - # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset - input.parameters.rule == "MayRunAs" - not spec.securityContext.fsGroup - } - input_fsGroup_allowed(spec) { - # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset - input.parameters.rule == "MayRunAs" - fg := spec.securityContext.fsGroup - count(input.parameters.ranges) > 0 - range := input.parameters.ranges[_] - value_within_range(range, fg) - } - value_within_range(range, value) { - range.min <= value - range.max >= value - } - # 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" - } + is_update(review) { + review.operation == "UPDATE" + } diff --git a/src/pod-security-policy/fsgroup/constraint.tmpl b/src/pod-security-policy/fsgroup/constraint.tmpl index 01e9f415d5..783cb8efc1 100644 --- a/src/pod-security-policy/fsgroup/constraint.tmpl +++ b/src/pod-security-policy/fsgroup/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8spspfsgroup annotations: metadata.gatekeeper.sh/title: "FS Group" - metadata.gatekeeper.sh/version: 1.0.2 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Controls allocating an FSGroup that owns the Pod's volumes. Corresponds to the `fsGroup` field in a PodSecurityPolicy. For more information, see @@ -44,8 +44,14 @@ spec: type: integer targets: - target: admission.k8s.gatekeeper.sh - rego: | -{{ file.Read "src/pod-security-policy/fsgroup/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} - libs: - - | -{{ file.Read "src/pod-security-policy/fsgroup/lib_exclude_update.rego" | strings.Indent 10 | strings.TrimSuffix "\n" }} + code: + - engine: K8sNativeValidation + source: +{{ file.Read "src/pod-security-policy/fsgroup/src.cel" | strings.Indent 10 | strings.TrimSuffix "\n" }} + - engine: Rego + source: + rego: | +{{ file.Read "src/pod-security-policy/fsgroup/src.rego" | strings.Indent 12 | strings.TrimSuffix "\n" }} + libs: + - | +{{ file.Read "src/pod-security-policy/fsgroup/lib_exclude_update.rego" | strings.Indent 14 | strings.TrimSuffix "\n" }} diff --git a/src/pod-security-policy/fsgroup/src.cel b/src/pod-security-policy/fsgroup/src.cel new file mode 100644 index 0000000000..c37686a34b --- /dev/null +++ b/src/pod-security-policy/fsgroup/src.cel @@ -0,0 +1,9 @@ +variables: +- name: fsGroup + expression: '!has(variables.anyObject.spec.securityContext) ? "" : !has(variables.anyObject.spec.securityContext.fsGroup) ? "" : variables.anyObject.spec.securityContext.fsGroup' +- name: input_fsGroup_allowed + expression: | + !has(variables.params.rule) ? true : variables.params.rule == "RunAsAny" ? true : variables.params.rule == "MayRunAs" && variables.fsGroup == "" ? true : (variables.params.rule == "MayRunAs" || variables.params.rule == "MustRunAs") && has(variables.params.ranges) && size(variables.params.ranges) > 0 ? variables.params.ranges.all(range, range.min <= variables.fsGroup && range.max >= variables.fsGroup) : false +validations: +- expression: '(has(request.operation) && request.operation == "UPDATE") || variables.input_fsGroup_allowed' + messageExpression: '"The provided pod spec fsGroup is not allowed, pod: " + variables.anyObject.metadata.name + ". Allowed fsGroup: " + variables.params.rule' diff --git a/src/pod-security-policy/fsgroup/src.rego b/src/pod-security-policy/fsgroup/src.rego index 2eb95cab7d..fcb19f10a8 100644 --- a/src/pod-security-policy/fsgroup/src.rego +++ b/src/pod-security-policy/fsgroup/src.rego @@ -5,7 +5,7 @@ import data.lib.exclude_update.is_update violation[{"msg": msg, "details": {}}] { # spec.securityContext.fsGroup field is immutable. not is_update(input.review) - + has_field(input.parameters, "rule") spec := input.review.object.spec not input_fsGroup_allowed(spec) msg := sprintf("The provided pod spec fsGroup is not allowed, pod: %v. Allowed fsGroup: %v", [input.review.object.metadata.name, input.parameters]) diff --git a/website/docs/validation/fsgroup.md b/website/docs/validation/fsgroup.md index 35e1b035e0..766741d1df 100644 --- a/website/docs/validation/fsgroup.md +++ b/website/docs/validation/fsgroup.md @@ -16,7 +16,7 @@ metadata: name: k8spspfsgroup annotations: metadata.gatekeeper.sh/title: "FS Group" - metadata.gatekeeper.sh/version: 1.0.2 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Controls allocating an FSGroup that owns the Pod's volumes. Corresponds to the `fsGroup` field in a PodSecurityPolicy. For more information, see @@ -56,65 +56,79 @@ spec: type: integer targets: - target: admission.k8s.gatekeeper.sh - rego: | - package k8spspfsgroup - - import data.lib.exclude_update.is_update - - violation[{"msg": msg, "details": {}}] { - # spec.securityContext.fsGroup field is immutable. - not is_update(input.review) - - spec := input.review.object.spec - not input_fsGroup_allowed(spec) - msg := sprintf("The provided pod spec fsGroup is not allowed, pod: %v. Allowed fsGroup: %v", [input.review.object.metadata.name, input.parameters]) - } - - input_fsGroup_allowed(_) { - # RunAsAny - No range is required. Allows any fsGroup ID to be specified. - input.parameters.rule == "RunAsAny" - } - input_fsGroup_allowed(spec) { - # MustRunAs - Validates pod spec fsgroup against all ranges - input.parameters.rule == "MustRunAs" - fg := spec.securityContext.fsGroup - count(input.parameters.ranges) > 0 - range := input.parameters.ranges[_] - value_within_range(range, fg) - } - input_fsGroup_allowed(spec) { - # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset - input.parameters.rule == "MayRunAs" - not has_field(spec, "securityContext") - } - input_fsGroup_allowed(spec) { - # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset - input.parameters.rule == "MayRunAs" - not spec.securityContext.fsGroup - } - input_fsGroup_allowed(spec) { - # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset - input.parameters.rule == "MayRunAs" - fg := spec.securityContext.fsGroup - count(input.parameters.ranges) > 0 - range := input.parameters.ranges[_] - value_within_range(range, fg) - } - value_within_range(range, value) { - range.min <= value - range.max >= value - } - # 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" - } + code: + - engine: K8sNativeValidation + source: + variables: + - name: fsGroup + expression: '!has(variables.anyObject.spec.securityContext) ? "" : !has(variables.anyObject.spec.securityContext.fsGroup) ? "" : variables.anyObject.spec.securityContext.fsGroup' + - name: input_fsGroup_allowed + expression: | + !has(variables.params.rule) ? true : variables.params.rule == "RunAsAny" ? true : variables.params.rule == "MayRunAs" && variables.fsGroup == "" ? true : (variables.params.rule == "MayRunAs" || variables.params.rule == "MustRunAs") && has(variables.params.ranges) && size(variables.params.ranges) > 0 ? variables.params.ranges.all(range, range.min <= variables.fsGroup && range.max >= variables.fsGroup) : false + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || variables.input_fsGroup_allowed' + messageExpression: '"The provided pod spec fsGroup is not allowed, pod: " + variables.anyObject.metadata.name + ". Allowed fsGroup: " + variables.params.rule' + - engine: Rego + source: + rego: | + package k8spspfsgroup + + import data.lib.exclude_update.is_update + + violation[{"msg": msg, "details": {}}] { + # spec.securityContext.fsGroup field is immutable. + not is_update(input.review) + has_field(input.parameters, "rule") + spec := input.review.object.spec + not input_fsGroup_allowed(spec) + msg := sprintf("The provided pod spec fsGroup is not allowed, pod: %v. Allowed fsGroup: %v", [input.review.object.metadata.name, input.parameters]) + } + + input_fsGroup_allowed(_) { + # RunAsAny - No range is required. Allows any fsGroup ID to be specified. + input.parameters.rule == "RunAsAny" + } + input_fsGroup_allowed(spec) { + # MustRunAs - Validates pod spec fsgroup against all ranges + input.parameters.rule == "MustRunAs" + fg := spec.securityContext.fsGroup + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, fg) + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + not has_field(spec, "securityContext") + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + not spec.securityContext.fsGroup + } + input_fsGroup_allowed(spec) { + # MayRunAs - Validates pod spec fsgroup against all ranges or allow pod spec fsgroup to be left unset + input.parameters.rule == "MayRunAs" + fg := spec.securityContext.fsGroup + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, fg) + } + value_within_range(range, value) { + range.min <= value + range.max >= value + } + # 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" + } ``` @@ -219,4 +233,99 @@ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper- +
+fsgroup2 + +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPFSGroup +metadata: + name: psp-fsgroup +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + ranges: + - min: 1 + max: 1000 + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/fsgroup/samples/psp-fsgroup/constraint2.yaml +``` + +
+ +
+example-disallowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: fsgroup-disallowed +spec: + securityContext: + fsGroup: 2000 # directory will have group ID 2000 + volumes: + - name: fsgroup-demo-vol + emptyDir: {} + containers: + - name: fsgroup-demo + image: busybox + command: [ "sh", "-c", "sleep 1h" ] + volumeMounts: + - name: fsgroup-demo-vol + mountPath: /data/demo + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/fsgroup/samples/psp-fsgroup/example_disallowed.yaml +``` + +
+
+example-allowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: fsgroup-disallowed +spec: + securityContext: + fsGroup: 500 # directory will have group ID 500 + volumes: + - name: fsgroup-demo-vol + emptyDir: {} + containers: + - name: fsgroup-demo + image: busybox + command: ["sh", "-c", "sleep 1h"] + volumeMounts: + - name: fsgroup-demo-vol + mountPath: /data/demo + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/fsgroup/samples/psp-fsgroup/example_allowed.yaml +``` + +
+ +
\ No newline at end of file