Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(checks): respect PodSecurityContext for containers #315

Merged
merged 1 commit into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions checks/kubernetes/general/runs_with_GID_le_10000_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,26 @@ test_low_gid_denied if {
count(r) == 1
r[_].msg == "Container 'hello' of Pod 'hello-gid' should set 'securityContext.runAsGroup' > 10000"
}

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

count(r) == 1
r[_].msg == "Container 'hello' of Pod 'hello-gid' should set 'securityContext.runAsGroup' > 10000"
}
23 changes: 23 additions & 0 deletions checks/kubernetes/general/runs_with_UID_le_10000_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,26 @@ test_zero_uid_denied if {
count(r) == 1
r[_].msg == "Container 'hello' of Pod 'hello-uid' should set 'securityContext.runAsUser' > 10000"
}

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

count(r) == 1
r[_].msg == "Container 'hello' of Pod 'hello-uid' should set 'securityContext.runAsUser' > 10000"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test_pod_context_custom_profile_denied if {
},
}

count(r) == 1
count(r) == 2
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now securityContext inherits podSecurityContext, so both the rules for the pod and the container are triggered here. We can improve this rule in a separate PR for result deduplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've opened the PR

}

test_both_undefined_type_denied if {
Expand Down
17 changes: 14 additions & 3 deletions lib/kubernetes/kubernetes.rego
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package lib.kubernetes

import rego.v1

import data.lib.k8s_sec_context

default is_gatekeeper := false

is_gatekeeper if {
Expand Down Expand Up @@ -39,8 +41,6 @@ default namespace := "default"

namespace := object.metadata.namespace

#annotations = object.metadata.annotations

kind := object.kind

is_pod if {
Expand Down Expand Up @@ -94,7 +94,18 @@ split_image(image) := [image_name, tag] if {

pod_containers(pod) := all_containers if {
keys = {"containers", "initContainers"}
all_containers = [c | keys[k]; c = pod.spec[k][_]]
all_containers = [c |
keys[k]
some container in pod.spec[k]
c := json.patch(
container,
[{
"op": "add",
"path": "securityContext",
"value": k8s_sec_context.resolve_container_sec_context(pod, container),
}],
)
]
}

containers contains container if {
Expand Down
35 changes: 26 additions & 9 deletions lib/kubernetes/kubernetes_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,35 @@ test_containers if {
test_containers := containers with input as {
"apiVersion": "v1",
"kind": "Pod",
"spec": {"containers": [{
"command": [
"sh",
"-c",
"echo 'Hello !' && sleep 1h",
],
"image": "busybox",
"name": "hello-containers",
}]},
"spec": {
"securityContext": {
"runAsUser": 1000,
"runAsGroup": 1001,
"fsGroup": 2000,
"supplementalGroups": [4000],
},
"containers": [{
"command": [
"sh",
"-c",
"echo 'Hello !' && sleep 1h",
],
"image": "busybox",
"name": "hello-containers",
"securityContext": {
"runAsGroup": 3000,
"allowPrivilegeEscalation": false,
},
}],
},
}

test_containers[_].name == "hello-containers"
test_containers[_].securityContext == {
"runAsUser": 1000,
"runAsGroup": 3000,
"allowPrivilegeEscalation": false,
}
}

test_isapiserver_has_valid_container if {
Expand Down
33 changes: 33 additions & 0 deletions lib/kubernetes/security_context.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# METADATA
# custom:
# library: true
# input:
# selector:
# - type: kubernetes
# - type: rbac
package lib.k8s_sec_context
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to create a separate package for some functions, because the kubernetes package defines an object rule that overrides OPA functions with namespace object. I did not change the name of the rule as it is library and used in checks.


import rego.v1

# Some fields are present in both SecurityContext and PodSecurityContext.
# When both are set, the values in SecurityContext take precedence.
# See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#securitycontext-v1-core
resolve_container_sec_context(pod, container) := object.union(
_inherited_sec_ctx(pod),
object.get(container, "securityContext", {}),
)

_inherited_sec_ctx(pod) := {k: v |
ctx := object.get(pod, ["spec", "securityContext"], {})
some k, v in ctx
k in _inherited_sec_ctx_fields
}

_inherited_sec_ctx_fields := {
"runAsGroup",
"runAsNonRoot",
"runAsUser",
"seLinuxOptions",
"seccompProfile",
"windowsOptions",
}
Loading