Skip to content

Commit

Permalink
Merge pull request #71 from nikpivkin/checks-ksv104
Browse files Browse the repository at this point in the history
fix(checks): check the Seccomp of the controllers
  • Loading branch information
simar7 authored Feb 7, 2024
2 parents 38c3895 + 8adeef4 commit c34404c
Show file tree
Hide file tree
Showing 2 changed files with 251 additions and 35 deletions.
74 changes: 41 additions & 33 deletions checks/kubernetes/pss/baseline/11_seccomp_profile_unconfined.rego
Original file line number Diff line number Diff line change
Expand Up @@ -27,49 +27,57 @@
package builtin.kubernetes.KSV104

import data.lib.kubernetes
import data.lib.utils

# getSeccompContainers returns all containers which have a seccomp
# profile set and is profile not set to "unconfined"
getSeccompContainers[container] {
some i
keys := [key | key := sprintf("%s/%s", [
"container.seccomp.security.alpha.kubernetes.io",
kubernetes.containers[_].name,
])]
seccomp := object.filter(kubernetes.annotations[_], keys)
val := seccomp[i]
val != "unconfined"
[a, c] := split(i, "/")
container = c
pod_seccomp_profile_path := ["securityContext", "seccompProfile", "type"]

controller_seccomp_profile_path := ["spec", "securityContext", "seccompProfile", "type"]

seccomp_annotation_key_prefix := "container.seccomp.security.alpha.kubernetes.io"

container_seccomp_annotation_key(container_name) := sprintf("%s/%s", [seccomp_annotation_key_prefix, container_name])

container_seccomp_from_annotations(container) := profile {
annotation_key := container_seccomp_annotation_key(container.name)
profile := kubernetes.annotations[_][annotation_key]
} else := ""

# containers_with_unconfined_seccomp_profile_type returns all containers which have a seccomp
# profile set and is profile set to "Unconfined"
containers_with_unconfined_seccomp_profile_type[name] {
seccomp := container_seccomp[_]
lower(seccomp.type) == "unconfined"
name := seccomp.container.name
}

# getNoSeccompContainers returns all containers which do not have
# a seccomp profile specified or profile set to "unconfined"
getNoSeccompContainers[container] {
container := kubernetes.containers[_].name
not getSeccompContainers[container]
# containers_with_unconfined_seccomp_profile_type returns all containers that do not have
# a seccomp profile type specified, since the default is unconfined
# https://kubernetes.io/docs/tutorials/security/seccomp/#enable-the-use-of-runtimedefault-as-the-default-seccomp-profile-for-all-workloads
containers_with_unconfined_seccomp_profile_type[name] {
seccomp := container_seccomp[_]
seccomp.type == ""
name := seccomp.container.name
}

# getContainersWithDisallowedSeccompProfileType returns all containers which have a seccomp
# profile set and is profile set to "Unconfined"
getContainersWithDisallowedSeccompProfileType[name] {
container_seccomp[{"container": container, "type": type}] {
kubernetes.is_pod
container := kubernetes.containers[_]
type := container.securityContext.seccompProfile.type
type == "Unconfined"
name = container.name
profile := container_seccomp_from_annotations(container)
type := object.get(container, pod_seccomp_profile_path, profile)
}

# getContainersWithDisallowedSeccompProfileType returns all containers which do not have
# a seccomp profile type specified
getContainersWithDisallowedSeccompProfileType[name] {
container := kubernetes.containers[_]
not container.securityContext.seccompProfile.type
name = container.name
container_seccomp[{"container": container, "type": type}] {
not kubernetes.is_pod
pod := kubernetes.pods[_]
container := kubernetes.pod_containers(pod)[_]
profile := container_seccomp_from_annotations(container)

# the profile type specified in the template takes precedence over the annotation
tplSeccompProfile := object.get(pod, controller_seccomp_profile_path, profile)
type := object.get(container, pod_seccomp_profile_path, tplSeccompProfile)
}

deny[res] {
cause := getContainersWithDisallowedSeccompProfileType[_]
msg := kubernetes.format(sprintf("container %s of %s %s in %s namespace should specify a seccomp profile", [getNoSeccompContainers[_], lower(kubernetes.kind), kubernetes.name, kubernetes.namespace]))
cause := containers_with_unconfined_seccomp_profile_type[_]
msg := kubernetes.format(sprintf("container %q of %s %q in %q namespace should specify a seccomp profile", [cause, lower(kubernetes.kind), kubernetes.name, kubernetes.namespace]))
res := result.new(msg, cause)
}
212 changes: 210 additions & 2 deletions checks/kubernetes/pss/baseline/11_seccomp_profile_unconfined_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,30 @@ test_container_seccomp_profile_unconfined_denied {
"-c",
"echo 'Hello' && sleep 1h",
],
"securityContext": {"seccompProfile": {"type": "RuntimeDefault"}},
"securityContext": {"seccompProfile": {"type": "Unconfined"}},
}]},
}

count(r) == 0
count(r) == 1
}

test_container_empty_seccomp_profile_unconfined_denied {
r := deny with input as {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {"name": "hello-sysctls"},
"spec": {"containers": [{
"name": "hello",
"image": "busybox",
"command": [
"sh",
"-c",
"echo 'Hello' && sleep 1h",
],
}]},
}

count(r) == 1
}

test_container_seccomp_profile_unconfined_allowed {
Expand All @@ -41,3 +60,192 @@ test_container_seccomp_profile_unconfined_allowed {

count(r) == 0
}

test_deployment_seccomp_profile_unconfined_allowed {
r := deny with input as {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "mydeployment",
"namespace": "mynamespace",
},
"spec": {
"selector": {"matchLabels": {"app": "myapp"}},
"template": {
"metadata": {"labels": {"app": "myapp"}},
"spec": {
"containers": [{
"name": "container",
"image": "node:8-alpine",
}],
"securityContext": {"seccompProfile": {"type": "RuntimeDefault"}},
},
},
},
}
count(r) == 0
}

test_deployment_seccomp_profile_unconfined_denied {
r := deny with input as {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "mydeployment",
"namespace": "mynamespace",
},
"spec": {
"selector": {"matchLabels": {"app": "myapp"}},
"template": {
"metadata": {"labels": {"app": "myapp"}},
"spec": {
"containers": [{
"name": "container",
"image": "node:8-alpine",
}],
"securityContext": {"seccompProfile": {"type": "Unconfined"}},
},
},
},
}
count(r) == 1
}

test_deployment_override_seccomp_profile_unconfined_allowed {
r := deny with input as {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "mydeployment",
"namespace": "mynamespace",
},
"spec": {
"selector": {"matchLabels": {"app": "myapp"}},
"template": {
"metadata": {"labels": {"app": "myapp"}},
"spec": {
"containers": [{
"name": "container",
"image": "node:8-alpine",
"securityContext": {"seccompProfile": {"type": "RuntimeDefault"}},
}],
"securityContext": {"seccompProfile": {"type": "Unconfined"}},
},
},
},
}
count(r) == 0
}

test_deployment_override_seccomp_profile_unconfined_deny {
r := deny with input as {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "mydeployment",
"namespace": "mynamespace",
},
"spec": {
"selector": {"matchLabels": {"app": "myapp"}},
"template": {
"metadata": {"labels": {"app": "myapp"}},
"spec": {
"containers": [
{
"name": "container",
"image": "node:8-alpine",
},
{
"name": "container2",
"image": "node:8-alpine",
"securityContext": {"seccompProfile": {"type": "Unconfined"}},
},
],
"securityContext": {"seccompProfile": {"type": "RuntimeDefault"}},
},
},
},
}
count(r) == 1
contains(r[_].msg, "container2")
}

test_cronjob_seccomp_profile_unconfined_denied {
r := deny with input as {
"apiVersion": "batch/v1",
"kind": "CronJob",
"metadata": {
"name": "mydeployment",
"namespace": "mynamespace",
},
"spec": {
"schedule": "* * * * *",
"jobTemplate": {"spec": {"template": {"spec": {"containers": [{
"name": "test-container",
"image": "node:8-alpine",
"securityContext": {"seccompProfile": {"type": "Unconfined"}},
}]}}}},
},
}
count(r) == 1
}

test_pod_annotations_seccomp_profile_unconfined_denied {
r := deny with input as {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "mydeployment",
"annotations": {"container.seccomp.security.alpha.kubernetes.io/test-container": "unconfined"},
},
"spec": {"containers": [{
"name": "test-container",
"image": "node:8-alpine",
}]},
}
count(r) == 1
}

test_pod_annotations_seccomp_profile_unconfined_allowed {
r := deny with input as {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "mydeployment",
"annotations": {"container.seccomp.security.alpha.kubernetes.io/test-container": "runtime/default"},
},
"spec": {"containers": [{
"name": "test-container",
"image": "node:8-alpine",
}]},
}
count(r) == 0
}

test_deployment_annotations_seccomp_profile_unconfined_allowed {
r := deny with input as {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "mydeployment",
"namespace": "default",
},
"spec": {
"selector": {"matchLabels": {"app": "myapp"}},
"template": {
"metadata": {
"labels": {"app": "myapp"},
"annotations": {"container.seccomp.security.alpha.kubernetes.io/test-container": "unconfined"},
},
"spec": {
"containers": [{
"name": "test-container",
"image": "node:8-alpine",
}],
"securityContext": {"seccompProfile": {"type": "RuntimeDefault"}},
},
},
},
}
count(r) == 0
}

0 comments on commit c34404c

Please sign in to comment.