Skip to content

Commit

Permalink
Our objective for this task is to align the titles and logic with the…
Browse files Browse the repository at this point in the history
… commercial version.

Resolves: SLK-67408
  • Loading branch information
ManojShastha committed Jul 17, 2023
1 parent 9af96f6 commit 6126b63
Show file tree
Hide file tree
Showing 26 changed files with 262 additions and 202 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "The default namespace should not be used"
# title: "Workloads in the default namespace"
# description: "ensure that default namespace should not be used"
# scope: package
# schemas:
Expand All @@ -21,12 +21,20 @@ import data.lib.kubernetes

default defaultNamespaceInUse = false

allowedKinds := ["pod", "replicaset", "replicationcontroller", "deployment", "statefulset", "daemonset", "cronjob", "job"]

defaultNamespaceInUse {
kubernetes.namespace == "default"
lower(kubernetes.kind) == allowedKinds[_]
}

deny[res] {
defaultNamespaceInUse
msg := sprintf("%s '%s' should not be set with 'default' namespace", [kubernetes.kind, kubernetes.name])
msg := kubernetes.format(
sprintf(
"%s %s in %s namespace should set metadata.namespace to a non-default namespace",
[lower(kubernetes.kind), kubernetes.name, kubernetes.namespace]
)
)
res := result.new(msg, input.metadata.namespace)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Unused capabilities should be dropped (drop any)"
# title: "Default capabilities: some containers do not drop any"
# description: "Security best practices require containers to run with minimal required capabilities."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "hostAliases is set"
# title: "Manages /etc/hosts"
# description: "Managing /etc/hosts aliases can prevent the container engine from modifying the file after a pod’s containers have already been started."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Do not allow update/create of a malicious pod"
# title: "Manage Kubernetes workloads and pods"
# description: "Check whether role permits update/create of a malicious pod"
# scope: package
# schemas:
Expand All @@ -20,9 +20,9 @@ package builtin.kubernetes.KSV048
import data.lib.kubernetes
import data.lib.utils

workloads := ["deployments", "daemonsets", "statefulsets", "replicationcontrollers", "replicasets", "jobs", "cronjobs"]
workloads := ["pods", "deployments", "jobs", "cronjobs", "statefulsets", "daemonsets", "replicasets", "replicationcontrollers"]

changeVerbs := ["update", "create", "*"]
changeVerbs := ["create", "update", "patch", "delete", "deletecollection", "impersonate", "*"]

readKinds := ["Role", "ClusterRole"]

Expand All @@ -35,6 +35,6 @@ update_malicious_pod[input.rules[ru]] {

deny[res] {
badRule := update_malicious_pod[_]
msg := "Role permits create/update of a malicious pod"
msg := kubernetes.format(sprintf("%s '%s' should not have access to resources %s for verbs %s", [kubernetes.kind, kubernetes.name, workloads, changeVerbs]))
res := result.new(msg, badRule)
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,75 @@ test_update_malicious_pod_cronjobs {

count(r) > 0
}

test_update_malicious_pod_deletecollection {
r := deny with input as {
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "Role",
"metadata": {
"namespace": "default",
"name": "pod-reader",
},
"rules": [{
"apiGroups": ["*"],
"resources": ["cronjobs"],
"verbs": ["deletecollection"],
}],
}

count(r) > 0
}

test_update_malicious_pod_delete {
r := deny with input as {
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "Role",
"metadata": {
"namespace": "default",
"name": "pod-reader",
},
"rules": [{
"apiGroups": ["*"],
"resources": ["job"],
"verbs": ["delete"],
}],
}

count(r) == 0
}

test_update_malicious_pod_patch {
r := deny with input as {
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "Role",
"metadata": {
"namespace": "default",
"name": "pod-reader",
},
"rules": [{
"apiGroups": ["*"],
"resources": ["job"],
"verbs": ["patch"],
}],
}

count(r) == 0
}

test_update_malicious_pod_impersonate {
r := deny with input as {
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "Role",
"metadata": {
"namespace": "default",
"name": "pod-reader",
},
"rules": [{
"apiGroups": ["*"],
"resources": ["job"],
"verbs": ["impersonate"],
}],
}

count(r) == 0
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Default capabilities not dropped"
# title: "Default capabilities: some containers do not drop all"
# description: "The container should drop all default capabilities and add only those that are needed for its execution."
# scope: package
# schemas:
Expand Down
2 changes: 1 addition & 1 deletion rules/kubernetes/policies/general/delete_pod_logs.rego
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Do not allow deletion of pod logs"
# title: "Delete pod logs"
# description: "Used to cover attacker’s tracks, but most clusters ship logs quickly off-cluster."
# scope: package
# schemas:
Expand Down
25 changes: 11 additions & 14 deletions rules/kubernetes/policies/general/get_shell_on_pod.rego
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Do not allow getting shell on pods"
# title: "Exec into Pods"
# description: "Check whether role permits getting shell on pods"
# scope: package
# schemas:
Expand All @@ -20,24 +20,21 @@ package builtin.kubernetes.KSV053
import data.lib.kubernetes
import data.lib.utils

workloads := ["pods/exec"]

changeVerbs := ["create", "update", "patch", "delete", "deletecollection", "impersonate", "*"]

readKinds := ["Role", "ClusterRole"]

get_shell_on_pod[ruleA] {
execPodsRestricted[input.rules[ru]] {
some ru, r, v
input.kind == readKinds[_]
some i, j
ruleA := input.rules[i]
ruleB := input.rules[j]
i < j
ruleA.apiGroups[_] == "*"
ruleA.resources[_] == "pods/exec"
ruleA.verbs[_] == "create"
ruleB.apiGroups[_] == "*"
ruleB.resources[_] == "pods"
ruleB.verbs[_] == "get"
input.rules[ru].resources[r] == workloads[_]
input.rules[ru].verbs[v] == changeVerbs[_]
}

deny[res] {
badRule := get_shell_on_pod[_]
msg := "Role permits getting shell on pods"
badRule := execPodsRestricted[_]
msg := kubernetes.format(sprintf("%s '%s' should not have access to resource '%s' for verbs %s", [kubernetes.kind, kubernetes.name, workloads, changeVerbs]))
res := result.new(msg, badRule)
}
27 changes: 1 addition & 26 deletions rules/kubernetes/policies/general/get_shell_on_pod_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ test_getting_shell_on_pods {
"resources": ["pods/exec"],
"verbs": ["create"],
},
{
"apiGroups": ["*"],
"resources": ["pods"],
"verbs": ["get"],
},
],
}

Expand All @@ -39,11 +34,6 @@ test_getting_shell_on_pods_no_pod_exec {
"resources": ["pods/exec1"],
"verbs": ["create"],
},
{
"apiGroups": ["*"],
"resources": ["pods"],
"verbs": ["get"],
},
],
}

Expand All @@ -64,11 +54,6 @@ test_getting_shell_on_pods_no_verb_create {
"resources": ["pods/exec"],
"verbs": ["create1"],
},
{
"apiGroups": ["*"],
"resources": ["pods"],
"verbs": ["get"],
},
],
}

Expand All @@ -89,11 +74,6 @@ test_getting_shell_on_pods_no_resource_pod {
"resources": ["pods/exec"],
"verbs": ["create1"],
},
{
"apiGroups": ["*"],
"resources": ["pods1"],
"verbs": ["get"],
},
],
}

Expand All @@ -114,13 +94,8 @@ test_getting_shell_on_pods_no_verb_get {
"resources": ["pods/exec"],
"verbs": ["create1"],
},
{
"apiGroups": ["*"],
"resources": ["pods"],
"verbs": ["get1"],
},
],
}

count(r) == 0
}
}
2 changes: 1 addition & 1 deletion rules/kubernetes/policies/general/manage_configmaps.rego
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Do not allow management of configmaps"
# title: "Manage configmaps"
# description: "Some workloads leverage configmaps to store sensitive data or configuration parameters that affect runtime behavior that can be modified by an attacker or combined with another issue to potentially lead to compromise."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Do not allow management of networking resources"
# title: "Manage Kubernetes networking"
# description: "The ability to control which pods get service traffic directed to them allows for interception attacks. Controlling network policy allows for bypassing lateral movement restrictions."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Do not allow management of RBAC resources"
# title: "Manage Kubernetes RBAC resources"
# description: "An effective level of access equivalent to cluster-admin should not be provided."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Runs with low group ID"
# title: "Runs with GID <= 10000"
# description: "Force the container to run with group ID > 10000 to avoid conflicts with the host’s user table."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Runs with low user ID"
# title: "Runs with UID <= 10000"
# description: "Force the container to run with user ID > 10000 to avoid conflicts with the host’s user table."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Image tag ':latest' used"
# title: "Image tag \":latest\" used"
# description: "It is best to avoid using the ':latest' image tag when deploying containers in production. Doing so makes it hard to track which version of the image is running, and hard to roll back the version."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "HostProcess container defined"
# title: "Access to host process"
# description: "Windows pods offer the ability to run HostProcess containers which enable privileged access to the Windows node."
# scope: package
# schemas:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# METADATA
# title: "Seccomp profile unconfined"
# title: "Seccomp policies disabled"
# description: "Seccomp profile must not be explicitly set to 'Unconfined'."
# scope: package
# schemas:
Expand All @@ -20,20 +20,50 @@ package builtin.kubernetes.KSV104
import data.lib.kubernetes
import data.lib.utils

failSeccomp[profile] {
spec := input.spec
profile := spec.securityContext.seccompProfile
profile.type == "Unconfined"
# 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
}

failSeccomp[profile] {
container := kubernetes.containers[_]
profile := container.securityContext.seccompProfile
profile.type == "Unconfined"
# 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]
}

# getContainersWithDisallowedSeccompProfileType returns all containers which have a seccomp
# profile set and is profile set to "Unconfined"
getContainersWithDisallowedSeccompProfileType[name] {
container := kubernetes.containers[_]
type := container.securityContext.seccompProfile.type
type == "Unconfined"
name = container.name
}

# 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
}

deny[res] {
cause := failSeccomp[_]
msg := "You should not set Seccomp profile to 'Unconfined'."
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]
)
)
res := result.new(msg, cause)
}
Loading

0 comments on commit 6126b63

Please sign in to comment.