diff --git a/artifacthub/library/pod-security-policy/seccompv2/1.0.0/artifacthub-pkg.yml b/artifacthub/library/pod-security-policy/seccompv2/1.0.0/artifacthub-pkg.yml index fc35f3bcd..d8bdf417b 100644 --- a/artifacthub/library/pod-security-policy/seccompv2/1.0.0/artifacthub-pkg.yml +++ b/artifacthub/library/pod-security-policy/seccompv2/1.0.0/artifacthub-pkg.yml @@ -3,7 +3,7 @@ name: k8spspseccompv2 displayName: Seccomp V2 createdAt: "2024-09-05T01:36:31Z" description: Controls the seccomp profile used by containers. Corresponds to the `securityContext.seccompProfile` field. -digest: 82f2c64bae8f6984ac24a71793134a385ec0761a2c44be43d16f25283bac4d9b +digest: 7586bb9d19280fe3e019415023e00db4194d8f64698bb54d075f7942310d4c6c license: Apache-2.0 homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/seccompv2 keywords: diff --git a/artifacthub/library/pod-security-policy/seccompv2/1.0.0/template.yaml b/artifacthub/library/pod-security-policy/seccompv2/1.0.0/template.yaml index fe948ae10..73ab76d52 100644 --- a/artifacthub/library/pod-security-policy/seccompv2/1.0.0/template.yaml +++ b/artifacthub/library/pod-security-policy/seccompv2/1.0.0/template.yaml @@ -36,10 +36,7 @@ spec: description: >- An array of allowed profile values for seccomp on Pods/Containers. - Can use the annotation naming scheme: `runtime/default`, `docker/default`, `unconfined` and/or - `localhost/some-profile.json`. The item `localhost/*` will allow any localhost based profile. - - Can also use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` + Can use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` and/or `Localhost`. For securityContext `Localhost`, use the parameter `allowedLocalhostProfiles` to list the allowed profile JSON files. @@ -94,25 +91,22 @@ spec: (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, !variables.allowAllProfiles && !(container.image in variables.exemptImages)) - - name: inputAllowedProfiles - expression: | - !has(variables.params.allowedProfiles) ? [] : variables.params.allowedProfiles - - name: derivedAllowedLocalhostFiles + - name: inputNonLocalHostProfiles expression: | - variables.inputAllowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, "Localhost/" + file) : [] - - name: allowedProfiles + variables.params.allowedProfiles.filter(profile, profile != "Localhost").map(profile, {"type": profile}) + - name: inputLocalHostProfiles expression: | - variables.inputAllowedProfiles + variables.derivedAllowedLocalhostFiles - - name: allowAllLocalhostFiles + variables.params.allowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, {"type": "Localhost", "localHostProfile": string(file)}) : [] + - name: inputAllowedProfiles expression: | - variables.allowedProfiles.exists(profile, profile == "Localhost/*") + variables.inputNonLocalHostProfiles + variables.inputLocalHostProfiles - name: hasPodSeccomp expression: | has(variables.anyObject.spec.securityContext) && has(variables.anyObject.spec.securityContext.seccompProfile) - name: podLocalHostProfile expression: | variables.hasPodSeccomp && has(variables.anyObject.spec.securityContext.seccompProfile.localhostProfile) ? variables.anyObject.spec.securityContext.seccompProfile.localhostProfile : "" - - name: podSecurityContextProfile + - name: podSecurityContextProfileType expression: | has(variables.hasPodSeccomp) && has(variables.anyObject.spec.securityContext.seccompProfile.type) ? variables.anyObject.spec.securityContext.seccompProfile.type : "" @@ -123,7 +117,7 @@ spec: variables.hasPodSeccomp ).map(container, { "container" : container.name, - "profile" : dyn(variables.podSecurityContextProfile), + "profile" : dyn(variables.podSecurityContextProfileType), "file" : variables.podLocalHostProfile, "location" : dyn("pod securityContext"), }) @@ -153,24 +147,23 @@ spec: variables.podSecurityContextProfiles + variables.containerSecurityContextProfiles + variables.containerProfilesMissing - name: badContainerProfilesWithoutFiles expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile != "Localhost" && - !(badContainerProfile.profile in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) + variables.allContainerProfiles.filter(container, + container.profile != "Localhost" && + !variables.inputAllowedProfiles.exists(profile, profile.type == container.profile) + ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.inputAllowedProfiles.map(profile, "{\"type\": \"" + profile.type + "\"" + (has(profile.localHostProfile) ? ", \"localHostProfile\": \"" + profile.localHostProfile + "\"}" : "}")).join(", ")) - name: badContainerProfilesWithFiles expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile == "Localhost" && - !variables.allowAllLocalhostFiles && - !((badContainerProfile.profile + "/" + badContainerProfile.file) in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) + variables.allContainerProfiles.filter(container, + container.profile == "Localhost" && + !variables.inputAllowedProfiles.exists(profile, profile.type == "Localhost" && (has(profile.localHostProfile) && (profile.localHostProfile == container.file || profile.localHostProfile == "*"))) + ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.inputAllowedProfiles.map(profile, "{\"type\": \"" + profile.type + "\"" + (has(profile.localHostProfile) ? ", \"localHostProfile\": \"" + profile.localHostProfile + "\"}" : "}")).join(", ")) validations: - expression: 'size(variables.badContainerProfilesWithoutFiles) == 0' messageExpression: | - variables.badContainerProfilesWithoutFiles.join("\n") + variables.badContainerProfilesWithoutFiles.join(", ") - expression: 'size(variables.badContainerProfilesWithFiles) == 0' messageExpression: | - variables.badContainerProfilesWithFiles.join("\n") + variables.badContainerProfilesWithFiles.join(", ") - engine: Rego source: rego: | @@ -218,25 +211,30 @@ spec: # Simple allowed Profiles allowed_profile(profile, _, allowed) { profile != "Localhost" - profile == allowed[_] + temp = allowed[_] + profile == temp.type } # annotation localhost without wildcard allowed_profile(profile, file, allowed) { profile == "Localhost" - allowed[_] == sprintf("Localhost/%s", [file]) + temp = allowed[_] + temp.type == "Localhost" + file == temp.localHostProfile } # The profiles explicitly in the list get_allowed_profiles[allowed] { - allowed := input.parameters.allowedProfiles[_] + profile := input.parameters.allowedProfiles[_] + profile != "Localhost" + allowed := {"type": profile} } get_allowed_profiles[allowed] { profile := input.parameters.allowedProfiles[_] profile == "Localhost" - file := object.get(input.parameters, "allowedLocalhostFiles", [])[_] - allowed := sprintf("Localhost/%s", [file]) + file := object.get(input.parameters, "allowedLocalhostFiles", [""])[_] + allowed := {"type": "Localhost", "localHostProfile": file} } # Container profile as defined in containers securityContext diff --git a/library/pod-security-policy/seccompv2/template.yaml b/library/pod-security-policy/seccompv2/template.yaml index fe948ae10..52a1c06c2 100644 --- a/library/pod-security-policy/seccompv2/template.yaml +++ b/library/pod-security-policy/seccompv2/template.yaml @@ -36,10 +36,7 @@ spec: description: >- An array of allowed profile values for seccomp on Pods/Containers. - Can use the annotation naming scheme: `runtime/default`, `docker/default`, `unconfined` and/or - `localhost/some-profile.json`. The item `localhost/*` will allow any localhost based profile. - - Can also use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` + Can use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` and/or `Localhost`. For securityContext `Localhost`, use the parameter `allowedLocalhostProfiles` to list the allowed profile JSON files. @@ -64,113 +61,6 @@ spec: 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: allowAllProfiles - expression: | - has(variables.params.allowedProfiles) && variables.params.allowedProfiles.exists(profile, profile == "*") - - 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))) - - name: unverifiedContainers - expression: | - (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, - !variables.allowAllProfiles && - !(container.image in variables.exemptImages)) - - name: inputAllowedProfiles - expression: | - !has(variables.params.allowedProfiles) ? [] : variables.params.allowedProfiles - - name: derivedAllowedLocalhostFiles - expression: | - variables.inputAllowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, "Localhost/" + file) : [] - - name: allowedProfiles - expression: | - variables.inputAllowedProfiles + variables.derivedAllowedLocalhostFiles - - name: allowAllLocalhostFiles - expression: | - variables.allowedProfiles.exists(profile, profile == "Localhost/*") - - name: hasPodSeccomp - expression: | - has(variables.anyObject.spec.securityContext) && has(variables.anyObject.spec.securityContext.seccompProfile) - - name: podLocalHostProfile - expression: | - variables.hasPodSeccomp && has(variables.anyObject.spec.securityContext.seccompProfile.localhostProfile) ? variables.anyObject.spec.securityContext.seccompProfile.localhostProfile : "" - - name: podSecurityContextProfile - expression: | - has(variables.hasPodSeccomp) && has(variables.anyObject.spec.securityContext.seccompProfile.type) ? variables.anyObject.spec.securityContext.seccompProfile.type - : "" - - name: podSecurityContextProfiles - expression: | - variables.unverifiedContainers.filter(container, - !(has(container.securityContext) && has(container.securityContext.seccompProfile)) && - variables.hasPodSeccomp - ).map(container, { - "container" : container.name, - "profile" : dyn(variables.podSecurityContextProfile), - "file" : variables.podLocalHostProfile, - "location" : dyn("pod securityContext"), - }) - - name: containerSecurityContextProfiles - expression: | - variables.unverifiedContainers.filter(container, - has(container.securityContext) && has(container.securityContext.seccompProfile) - ).map(container, { - "container" : container.name, - "profile" : dyn(container.securityContext.seccompProfile.type), - "file" : has(container.securityContext.seccompProfile.localhostProfile) ? container.securityContext.seccompProfile.localhostProfile : dyn(""), - "location" : dyn("container securityContext"), - }) - - name: containerProfilesMissing - expression: | - variables.unverifiedContainers.filter(container, - !(has(container.securityContext) && has(container.securityContext.seccompProfile)) && - !variables.hasPodSeccomp - ).map(container, { - "container" : container.name, - "profile" : dyn("not configured"), - "file" : dyn(""), - "location" : dyn("no explicit profile found"), - }) - - name: allContainerProfiles - expression: | - variables.podSecurityContextProfiles + variables.containerSecurityContextProfiles + variables.containerProfilesMissing - - name: badContainerProfilesWithoutFiles - expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile != "Localhost" && - !(badContainerProfile.profile in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) - - name: badContainerProfilesWithFiles - expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile == "Localhost" && - !variables.allowAllLocalhostFiles && - !((badContainerProfile.profile + "/" + badContainerProfile.file) in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) - validations: - - expression: 'size(variables.badContainerProfilesWithoutFiles) == 0' - messageExpression: | - variables.badContainerProfilesWithoutFiles.join("\n") - - expression: 'size(variables.badContainerProfilesWithFiles) == 0' - messageExpression: | - variables.badContainerProfilesWithFiles.join("\n") - engine: Rego source: rego: | @@ -218,25 +108,30 @@ spec: # Simple allowed Profiles allowed_profile(profile, _, allowed) { profile != "Localhost" - profile == allowed[_] + temp = allowed[_] + profile == temp.type } # annotation localhost without wildcard allowed_profile(profile, file, allowed) { profile == "Localhost" - allowed[_] == sprintf("Localhost/%s", [file]) + temp = allowed[_] + temp.type == "Localhost" + file == temp.localHostProfile } # The profiles explicitly in the list get_allowed_profiles[allowed] { - allowed := input.parameters.allowedProfiles[_] + profile := input.parameters.allowedProfiles[_] + profile != "Localhost" + allowed := {"type": profile} } get_allowed_profiles[allowed] { profile := input.parameters.allowedProfiles[_] profile == "Localhost" - file := object.get(input.parameters, "allowedLocalhostFiles", [])[_] - allowed := sprintf("Localhost/%s", [file]) + file := object.get(input.parameters, "allowedLocalhostFiles", [""])[_] + allowed := {"type": "Localhost", "localHostProfile": file} } # Container profile as defined in containers securityContext diff --git a/src/pod-security-policy/seccompv2/constraint.tmpl b/src/pod-security-policy/seccompv2/constraint.tmpl index db9ad0ba5..2db2ea162 100644 --- a/src/pod-security-policy/seccompv2/constraint.tmpl +++ b/src/pod-security-policy/seccompv2/constraint.tmpl @@ -36,10 +36,7 @@ spec: description: >- An array of allowed profile values for seccomp on Pods/Containers. - Can use the annotation naming scheme: `runtime/default`, `docker/default`, `unconfined` and/or - `localhost/some-profile.json`. The item `localhost/*` will allow any localhost based profile. - - Can also use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` + Can use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` and/or `Localhost`. For securityContext `Localhost`, use the parameter `allowedLocalhostProfiles` to list the allowed profile JSON files. diff --git a/src/pod-security-policy/seccompv2/src.cel b/src/pod-security-policy/seccompv2/src.cel index 75d97e859..3c5768487 100644 --- a/src/pod-security-policy/seccompv2/src.cel +++ b/src/pod-security-policy/seccompv2/src.cel @@ -26,25 +26,22 @@ variables: (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, !variables.allowAllProfiles && !(container.image in variables.exemptImages)) -- name: inputAllowedProfiles - expression: | - !has(variables.params.allowedProfiles) ? [] : variables.params.allowedProfiles -- name: derivedAllowedLocalhostFiles +- name: inputNonLocalHostProfiles expression: | - variables.inputAllowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, "Localhost/" + file) : [] -- name: allowedProfiles + variables.params.allowedProfiles.filter(profile, profile != "Localhost").map(profile, {"type": profile}) +- name: inputLocalHostProfiles expression: | - variables.inputAllowedProfiles + variables.derivedAllowedLocalhostFiles -- name: allowAllLocalhostFiles + variables.params.allowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, {"type": "Localhost", "localHostProfile": string(file)}) : [] +- name: inputAllowedProfiles expression: | - variables.allowedProfiles.exists(profile, profile == "Localhost/*") + variables.inputNonLocalHostProfiles + variables.inputLocalHostProfiles - name: hasPodSeccomp expression: | has(variables.anyObject.spec.securityContext) && has(variables.anyObject.spec.securityContext.seccompProfile) - name: podLocalHostProfile expression: | variables.hasPodSeccomp && has(variables.anyObject.spec.securityContext.seccompProfile.localhostProfile) ? variables.anyObject.spec.securityContext.seccompProfile.localhostProfile : "" -- name: podSecurityContextProfile +- name: podSecurityContextProfileType expression: | has(variables.hasPodSeccomp) && has(variables.anyObject.spec.securityContext.seccompProfile.type) ? variables.anyObject.spec.securityContext.seccompProfile.type : "" @@ -55,7 +52,7 @@ variables: variables.hasPodSeccomp ).map(container, { "container" : container.name, - "profile" : dyn(variables.podSecurityContextProfile), + "profile" : dyn(variables.podSecurityContextProfileType), "file" : variables.podLocalHostProfile, "location" : dyn("pod securityContext"), }) @@ -85,21 +82,20 @@ variables: variables.podSecurityContextProfiles + variables.containerSecurityContextProfiles + variables.containerProfilesMissing - name: badContainerProfilesWithoutFiles expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile != "Localhost" && - !(badContainerProfile.profile in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) + variables.allContainerProfiles.filter(container, + container.profile != "Localhost" && + !variables.inputAllowedProfiles.exists(profile, profile.type == container.profile) + ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.inputAllowedProfiles.map(profile, "{\"type\": \"" + profile.type + "\"" + (has(profile.localHostProfile) ? ", \"localHostProfile\": \"" + profile.localHostProfile + "\"}" : "}")).join(", ")) - name: badContainerProfilesWithFiles expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile == "Localhost" && - !variables.allowAllLocalhostFiles && - !((badContainerProfile.profile + "/" + badContainerProfile.file) in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) + variables.allContainerProfiles.filter(container, + container.profile == "Localhost" && + !variables.inputAllowedProfiles.exists(profile, profile.type == "Localhost" && (has(profile.localHostProfile) && (profile.localHostProfile == container.file || profile.localHostProfile == "*"))) + ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.inputAllowedProfiles.map(profile, "{\"type\": \"" + profile.type + "\"" + (has(profile.localHostProfile) ? ", \"localHostProfile\": \"" + profile.localHostProfile + "\"}" : "}")).join(", ")) validations: - expression: 'size(variables.badContainerProfilesWithoutFiles) == 0' messageExpression: | - variables.badContainerProfilesWithoutFiles.join("\n") + variables.badContainerProfilesWithoutFiles.join(", ") - expression: 'size(variables.badContainerProfilesWithFiles) == 0' messageExpression: | - variables.badContainerProfilesWithFiles.join("\n") + variables.badContainerProfilesWithFiles.join(", ") diff --git a/src/pod-security-policy/seccompv2/src.rego b/src/pod-security-policy/seccompv2/src.rego index c00c40801..bb10f4744 100644 --- a/src/pod-security-policy/seccompv2/src.rego +++ b/src/pod-security-policy/seccompv2/src.rego @@ -42,25 +42,30 @@ allowed_profile(profile, _, _) { # Simple allowed Profiles allowed_profile(profile, _, allowed) { profile != "Localhost" - profile == allowed[_] + temp = allowed[_] + profile == temp.type } # annotation localhost without wildcard allowed_profile(profile, file, allowed) { profile == "Localhost" - allowed[_] == sprintf("Localhost/%s", [file]) + temp = allowed[_] + temp.type == "Localhost" + file == temp.localHostProfile } # The profiles explicitly in the list get_allowed_profiles[allowed] { - allowed := input.parameters.allowedProfiles[_] + profile := input.parameters.allowedProfiles[_] + profile != "Localhost" + allowed := {"type": profile} } get_allowed_profiles[allowed] { profile := input.parameters.allowedProfiles[_] profile == "Localhost" - file := object.get(input.parameters, "allowedLocalhostFiles", [])[_] - allowed := sprintf("Localhost/%s", [file]) + file := object.get(input.parameters, "allowedLocalhostFiles", [""])[_] + allowed := {"type": "Localhost", "localHostProfile": file} } # Container profile as defined in containers securityContext diff --git a/src/pod-security-policy/seccompv2/src_test.rego b/src/pod-security-policy/seccompv2/src_test.rego index 04d3286f0..372815b36 100644 --- a/src/pod-security-policy/seccompv2/src_test.rego +++ b/src/pod-security-policy/seccompv2/src_test.rego @@ -196,25 +196,25 @@ test_input_seccomp_pod_initcontainer_mixed_not_allowed { test_translation_seccomp_allowed_context_localhost_wildcard_file { inp := {"parameters": input_parameters_localhost_wildcard_both} output := get_allowed_profiles with input as inp - output == {"Localhost", "Localhost/*"} + output == {{"type": "Localhost", "localHostProfile": "*"}} } test_translation_seccomp_allowed_context_localhost_no_file { inp := {"parameters": input_parameters_sc_localhost_no_file} output := get_allowed_profiles with input as inp - output == {"Localhost"} + output == {{"localHostProfile": "", "type": "Localhost"}} } test_translation_seccomp_allowed_context_localhost_with_file { inp := {"parameters": input_parameters_sc_localhost_with_file} output := get_allowed_profiles with input as inp - output == {"Localhost", "Localhost/profile.json"} + output == {{"type": "Localhost", "localHostProfile": "profile.json"}} } test_translation_seccomp_allowed_context_mixed { inp := {"parameters": input_parameters_in_list} output := get_allowed_profiles with input as inp - output == {"Localhost", "Localhost/profile.json", "RuntimeDefault"} + output == {{"type": "Localhost", "localHostProfile": "profile.json"}, {"type": "RuntimeDefault"}} } # Create Review Object diff --git a/website/docs/validation/seccompv2.md b/website/docs/validation/seccompv2.md index 69ddb93ad..a148d9c34 100644 --- a/website/docs/validation/seccompv2.md +++ b/website/docs/validation/seccompv2.md @@ -48,10 +48,7 @@ spec: description: >- An array of allowed profile values for seccomp on Pods/Containers. - Can use the annotation naming scheme: `runtime/default`, `docker/default`, `unconfined` and/or - `localhost/some-profile.json`. The item `localhost/*` will allow any localhost based profile. - - Can also use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` + Can use the securityContext naming scheme: `RuntimeDefault`, `Unconfined` and/or `Localhost`. For securityContext `Localhost`, use the parameter `allowedLocalhostProfiles` to list the allowed profile JSON files. @@ -106,25 +103,22 @@ spec: (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, !variables.allowAllProfiles && !(container.image in variables.exemptImages)) - - name: inputAllowedProfiles - expression: | - !has(variables.params.allowedProfiles) ? [] : variables.params.allowedProfiles - - name: derivedAllowedLocalhostFiles + - name: inputNonLocalHostProfiles expression: | - variables.inputAllowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, "Localhost/" + file) : [] - - name: allowedProfiles + variables.params.allowedProfiles.filter(profile, profile != "Localhost").map(profile, {"type": profile}) + - name: inputLocalHostProfiles expression: | - variables.inputAllowedProfiles + variables.derivedAllowedLocalhostFiles - - name: allowAllLocalhostFiles + variables.params.allowedProfiles.exists(profile, profile == "Localhost") ? variables.params.allowedLocalhostFiles.map(file, {"type": "Localhost", "localHostProfile": string(file)}) : [] + - name: inputAllowedProfiles expression: | - variables.allowedProfiles.exists(profile, profile == "Localhost/*") + variables.inputNonLocalHostProfiles + variables.inputLocalHostProfiles - name: hasPodSeccomp expression: | has(variables.anyObject.spec.securityContext) && has(variables.anyObject.spec.securityContext.seccompProfile) - name: podLocalHostProfile expression: | variables.hasPodSeccomp && has(variables.anyObject.spec.securityContext.seccompProfile.localhostProfile) ? variables.anyObject.spec.securityContext.seccompProfile.localhostProfile : "" - - name: podSecurityContextProfile + - name: podSecurityContextProfileType expression: | has(variables.hasPodSeccomp) && has(variables.anyObject.spec.securityContext.seccompProfile.type) ? variables.anyObject.spec.securityContext.seccompProfile.type : "" @@ -135,7 +129,7 @@ spec: variables.hasPodSeccomp ).map(container, { "container" : container.name, - "profile" : dyn(variables.podSecurityContextProfile), + "profile" : dyn(variables.podSecurityContextProfileType), "file" : variables.podLocalHostProfile, "location" : dyn("pod securityContext"), }) @@ -165,24 +159,23 @@ spec: variables.podSecurityContextProfiles + variables.containerSecurityContextProfiles + variables.containerProfilesMissing - name: badContainerProfilesWithoutFiles expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile != "Localhost" && - !(badContainerProfile.profile in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) + variables.allContainerProfiles.filter(container, + container.profile != "Localhost" && + !variables.inputAllowedProfiles.exists(profile, profile.type == container.profile) + ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.inputAllowedProfiles.map(profile, "{\"type\": \"" + profile.type + "\"" + (has(profile.localHostProfile) ? ", \"localHostProfile\": \"" + profile.localHostProfile + "\"}" : "}")).join(", ")) - name: badContainerProfilesWithFiles expression: | - variables.allContainerProfiles.filter(badContainerProfile, - badContainerProfile.profile == "Localhost" && - !variables.allowAllLocalhostFiles && - !((badContainerProfile.profile + "/" + badContainerProfile.file) in variables.allowedProfiles) - ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.allowedProfiles.join(", ")) + variables.allContainerProfiles.filter(container, + container.profile == "Localhost" && + !variables.inputAllowedProfiles.exists(profile, profile.type == "Localhost" && (has(profile.localHostProfile) && (profile.localHostProfile == container.file || profile.localHostProfile == "*"))) + ).map(badProfile, "Seccomp profile '" + badProfile.profile + "' With file '" + badProfile.file + "' is not allowed for container '" + badProfile.container + "'. Found at: " + badProfile.location + ". Allowed profiles: " + variables.inputAllowedProfiles.map(profile, "{\"type\": \"" + profile.type + "\"" + (has(profile.localHostProfile) ? ", \"localHostProfile\": \"" + profile.localHostProfile + "\"}" : "}")).join(", ")) validations: - expression: 'size(variables.badContainerProfilesWithoutFiles) == 0' messageExpression: | - variables.badContainerProfilesWithoutFiles.join("\n") + variables.badContainerProfilesWithoutFiles.join(", ") - expression: 'size(variables.badContainerProfilesWithFiles) == 0' messageExpression: | - variables.badContainerProfilesWithFiles.join("\n") + variables.badContainerProfilesWithFiles.join(", ") - engine: Rego source: rego: | @@ -230,25 +223,30 @@ spec: # Simple allowed Profiles allowed_profile(profile, _, allowed) { profile != "Localhost" - profile == allowed[_] + temp = allowed[_] + profile == temp.type } # annotation localhost without wildcard allowed_profile(profile, file, allowed) { profile == "Localhost" - allowed[_] == sprintf("Localhost/%s", [file]) + temp = allowed[_] + temp.type == "Localhost" + file == temp.localHostProfile } # The profiles explicitly in the list get_allowed_profiles[allowed] { - allowed := input.parameters.allowedProfiles[_] + profile := input.parameters.allowedProfiles[_] + profile != "Localhost" + allowed := {"type": profile} } get_allowed_profiles[allowed] { profile := input.parameters.allowedProfiles[_] profile == "Localhost" - file := object.get(input.parameters, "allowedLocalhostFiles", [])[_] - allowed := sprintf("Localhost/%s", [file]) + file := object.get(input.parameters, "allowedLocalhostFiles", [""])[_] + allowed := {"type": "Localhost", "localHostProfile": file} } # Container profile as defined in containers securityContext