Files
kubescape/nsa.json
T

1 line
72 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"guid":"","name":"NSA","attributes":{"armoBuiltin":true},"creationTime":"","description":"Implement NSA security advices for K8s ","controls":[{"guid":"","name":"Control plane hardening","attributes":{"armoBuiltin":true},"id":"C-0005","controlID":"C-0005","creationTime":"","description":"Kubernetes control plane API is running with non-secure port enabled which allows attackers to gain unprotected access to the cluster.","remediation":"Set the insecure-port flag of the API server to zero.","rules":[{"guid":"","name":"insecure-port-flag","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\nimport data.cautils as cautils\n\n# Fails if pod has insecure-port flag enabled\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n container := pod.spec.containers[_]\n\tisInsecurePortFlag(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"The API server container: %v has insecure-port flag enabled\", [ container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has insecure-port flag enabled\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isInsecurePortFlag(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"The API server container: %v has insecure-port flag enabled\", [ container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\t\nisInsecurePortFlag(container){\n cautils.list_contains(container.command, \"--insecure-port=1\")\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if the api server has insecure-port enabled","remediation":"Make sure that the insecure-port flag of the api server is set to 0","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Host PID/IPC privileges","attributes":{"armoBuiltin":true},"id":"C-0038","controlID":"C-0038","creationTime":"","description":"Containers should be as isolated as possible from the host machine. The hostPID and hostIPC fields in Kubernetes may excessively expose the host to potentially malicious actions.","remediation":"Remove hostPID and hostIPC privileges unless they are absolutely necessary.","rules":[{"guid":"","name":"host-pid-ipc-privileges","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has hostPID enabled\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tisHostPID(pod.spec)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Pod: %v has hostPID enabled\", [pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if pod has hostIPC enabled\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tisHostIPC(pod.spec)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Pod: %v has hostIPC enabled\", [pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n\n# Fails if workload has hostPID enabled\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tisHostPID(wl.spec.template.spec)\n msga := {\n\t\"alertMessage\": sprintf(\"%v: %v has a pod with hostPID enabled\", [wl.kind, wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if workload has hostIPC enabled\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tisHostIPC(wl.spec.template.spec)\n msga := {\n\t\"alertMessage\": sprintf(\"%v: %v has a pod with hostIPC enabled\", [wl.kind, wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if cronjob has hostPID enabled\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tisHostPID(wl.spec.jobTemplate.spec.template.spec)\n msga := {\n\t\"alertMessage\": sprintf(\"CronJob: %v has a pod with hostPID enabled\", [wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if cronjob has hostIPC enabled\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tisHostIPC(wl.spec.jobTemplate.spec.template.spec)\n msga := {\n\t\"alertMessage\": sprintf(\"CronJob: %v has a pod with hostIPC enabled\", [wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Check that hostPID and hostIPC are set to false. Default is false. Only in pod spec\n\n\nisHostPID(podspec){\n podspec.hostPID == true\n}\n\nisHostIPC(podspec){\n podspec.hostIPC == true\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"Containers should be as isolated as possible from the host machine. The hostPID and hostIPC fields in Kubernetes may excessively expose the host to potentially malicious actions.","remediation":"Make sure that the fields hostIPC and hostPID in the pod spec are not set to true (set to false or not present)","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Immutable container filesystem","attributes":{"armoBuiltin":true},"id":"C-0017","controlID":"C-0017","creationTime":"","description":"Mutable container filesystem can be abused to inject malicious code or data into containers. Use immutable (read-only) filesystem to limit potential attacks.","remediation":"Set the filesystem of the container to read-only when possible (POD securityContext, readOnlyRootFilesystem: true). If containers application needs to write into the filesystem, it is recommended to mount secondary filesystems for specific directories where application require write access.","rules":[{"guid":"","name":"immutable-container-filesystem","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pods has container with mutable filesystem\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isMutableFilesystem(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v has mutable filesystem\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has container with mutable filesystem \ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isMutableFilesystem(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v has mutable filesystem\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if cronjob has container with mutable filesystem \ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tisMutableFilesystem(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v has mutable filesystem\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Default of readOnlyRootFilesystem is false. This field is only in container spec and not pod spec\nisMutableFilesystem(container){\n not container.securityContext.readOnlyRootFilesystem\n }","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container has mutable filesystem","remediation":"Make sure that the securityContext.readOnlyRootFilesystem field in the container/pod spec is set to true","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Non-root containers","attributes":{"armoBuiltin":true},"id":"C-0013","controlID":"C-0013","creationTime":"","description":"Potential attackers may gain access to a container and leverage its existing privileges to conduct an attack. Therefore, it is not recommended to deploy containers with root privileges unless it is absolutely necessary. This contol identifies all the Pods running as root or can escalate to root.","remediation":"If your application does not need root privileges, make sure to define the runAsUser or runAsGroup under the PodSecurityContext and use user ID 1000 or higher. Do not turn on allowPrivlegeEscalation bit and make sure runAsNonRoot is true.","rules":[{"guid":"","name":"non-root-containers","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container configured to run as root\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isRootContainer(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v may run as root\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if pod has container configured to run as root\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isRootPod(pod, container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v may run as root\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n\n\n# Fails if workload has container configured to run as root\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isRootContainer(container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if workload has container configured to run as root\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isRootPod(wl.spec.template, container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if cronjob has a container configured to run as root\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n\t isRootContainer(container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\n# Fails if workload has container configured to run as root\ndeny[msga] {\n \twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n isRootPod(wl.spec.jobTemplate.spec.template, container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v may run as root\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\nisRootPod(pod, container) {\n not container.securityContext.runAsUser\n pod.spec.securityContext.runAsUser == 0\n}\n\nisRootPod(pod, container) {\n not container.securityContext.runAsUser\n\tnot container.securityContext.runAsGroup\n\tnot container.securityContext.runAsNonRoot\n not pod.spec.securityContext.runAsUser\n\tnot pod.spec.securityContext.runAsGroup\n pod.spec.securityContext.runAsNonRoot == false\n}\n\nisRootPod(pod, container) {\n not container.securityContext.runAsGroup\n pod.spec.securityContext.runAsGroup == 0\n}\n\nisRootPod(pod, container) {\n\tnot pod.spec.securityContext.runAsGroup\n\tnot pod.spec.securityContext.runAsUser\n \tcontainer.securityContext.runAsNonRoot == false\n}\n\nisRootContainer(container) {\n container.securityContext.runAsUser == 0\n}\n\nisRootContainer(container) {\n container.securityContext.runAsGroup == 0\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container can run as root","remediation":"Make sure that the user/group in the securityContext of pod/container is set to an id less than 1000, or the runAsNonRoot flag is set to true. Also make sure that the allowPrivilegeEscalation field is set to false","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Privileged container","attributes":{"armoBuiltin":true,"microsoftMitreColumns":["Privilege escalation"]},"id":"C-0057","controlID":"C-0057","creationTime":"","description":"Potential attackers may gain access to privileged containers and inherit access to the host resources. Therefore, it is not recommended to deploy privileged containers unless it is absolutely necessary. This control identifies all the privileged Pods.","remediation":"Remove privileged capabilities by setting the securityContext.privileged to false. If you must deploy a Pod as privileged, add other restriction to it, such as network policy, Seccomp etc and still remove all unnecessary capabilities. Use the exception mechanism to remove unnecessary notifocations.","rules":[{"guid":"","name":"rule-privilege-escalation","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Privilege Escalation::privileged container","mitre":"Privilege Escalation","mitreCode":"TA0004"},"creationTime":"","rule":"package armo_builtins\n# Deny mutating action unless user is in group owning the resource\n\n\n#privileged pods\ndeny[msga] {\n\n\tpod := input[_]\n\tpod.kind == \"Pod\"\n\tcontainers := pod.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following pods are defined as privileged: %v\", [pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 3,\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n }\n}\n\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainers := wl.spec.template.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v is defined as privileged:\", [wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 3,\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n }\n}\n\n#handles cronjob\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainers := wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following cronjobs are defined as privileged: %v\", [wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 3,\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n }\n}\n\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"determines if pods/deployments defined as privileged true","remediation":"avoid defining pods as privilleged","ruleQuery":""}]},{"guid":"","name":"Allowed hostPath","attributes":{"armoBuiltin":true},"id":"C-0006","controlID":"C-0006","creationTime":"","description":"Mounting host directory to the container can be abused to get access to sensitive data and gain persistence on the host machine.","remediation":"Refrain from using host path mount.","rules":[{"guid":"","name":"alert-rw-hostpath","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Persistance::Writable hostPath mount, Lateral Movement::Writable volume mounts on the host"},"creationTime":"","rule":"package armo_builtins\n\n# input: pod\n# apiversion: v1\n# does: returns hostPath volumes\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := pod.spec.containers[_]\n\tvolumeMount := container.volumeMounts[_]\n\tvolumeMount.name == volume.name\n\tisRWMount(volumeMount)\n\n podname := pod.metadata.name\n\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has: %v as hostPath volume\", [podname, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := wl.spec.template.spec.containers[_]\n\tvolumeMount := container.volumeMounts[_]\n\tvolumeMount.name == volume.name\n\tisRWMount(volumeMount)\n\n\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t\n\t}\n}\n\n#handles CronJobs\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n volumes := wl.spec.jobTemplate.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tvolumeMount := container.volumeMounts[_]\n\tvolumeMount.name == volume.name\n\tisRWMount(volumeMount)\n\n \n\tmsga := {\n\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\"packagename\": \"armo_builtins\",\n\t\"alertScore\": 7,\n\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisRWMount(mount) {\n not mount.readOnly\n}\nisRWMount(mount) {\n mount.readOnly == false\n} ","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod"]}],"ruleDependencies":[{"packageName":"cautils"},{"packageName":"kubernetes.api.client"}],"description":"determines if any workload contains a hostPath volume with rw permissions","remediation":"Set the readOnly field of the mount to true","ruleQuery":""}]},{"guid":"","name":"Automatic mapping of service account","attributes":{"armoBuiltin":true},"id":"C-0034","controlID":"C-0034","creationTime":"","description":"Potential attacker may gain access to a POD and steal its service account token. Therefore, it is recommended to disable automatic mapping of the service account tokens in service account configuration and enable it only for PODs that need to use them.","remediation":"Disable automatic mounting of service account tokens to PODs either at the service account level or at the individual POD level, by specifying the automountServiceAccountToken: false. Note that POD level takes precedence.","rules":[{"guid":"","name":"automount-service-account","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n# Fails if user account mount tokens in pod by default.\ndeny [msga]{\n serviceaccounts := [serviceaccount | serviceaccount= input[_]; serviceaccount.kind == \"ServiceAccount\"]\n serviceaccount := serviceaccounts[_]\n isAutoMount(serviceaccount)\n\n msga := {\n\t \"alertMessage\": sprintf(\"the following service account: %v in the following namespace: %v mounts service account tokens in pods by default\", [serviceaccount.metadata.name, serviceaccount.metadata.namespace]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [serviceaccount]\n\t\t}\n\t}\n} \n\nisAutoMount(serviceaccount) {\n\tnot serviceaccount.automountServiceAccountToken == false\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Serviceaccount"]}],"ruleDependencies":[],"description":"fails if service account automountServiceAccountToken enabled","remediation":"Make sure that the automountServiceAccountToken field on the service account spec if set to false","ruleQuery":"armo_builtins"}]},{"guid":"","name":"hostNetwork access","attributes":{"armoBuiltin":true},"id":"C-0041","controlID":"C-0041","creationTime":"","description":"Potential attackers may gain access to a POD and inherit access to the entire host network. For example, in AWS case, they will have access to the entire VPC. This control identifies all the PODs with host network access enabled.","remediation":"Only connect PODs to host network when it is necessary. If not, set the hostNetwork field of the pod spec to false, or completely remove it (false is the default). Whitelist only those PODs that must have access to host network by design.","rules":[{"guid":"","name":"host-network-access","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n# Fails if pod has hostNetwork enabled\ndeny[msga] {\n pods := [ pod | pod = input[_] ; pod.kind == \"Pod\"]\n pod := pods[_]\n\n isHostNetwork(pod.spec)\n\n \tmsga := {\n\t\"alertMessage\": sprintf(\"Pod: %v is connected to the host network\", [pod.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has hostNetwork enabled\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tisHostNetwork(wl.spec.template.spec)\n msga := {\n\t\"alertMessage\": sprintf(\"%v: %v has a pod connected to the host network\", [wl.kind, wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if cronjob has hostNetwork enabled\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tisHostNetwork(wl.spec.jobTemplate.spec.template.spec)\n msga := {\n\t\"alertMessage\": sprintf(\"CronJob: %v has a pod connected to the host network\", [wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisHostNetwork(podspec) {\n podspec.hostNetwork == true\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if pod has hostNetwork enabled","remediation":"Make sure that the hostNetwork field of the pod spec is not set to true (set to false or not present)","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Resource policies","attributes":{"armoBuiltin":true},"id":"C-0009","controlID":"C-0009","creationTime":"","description":"CPU and memory resources should have a limit set for every container to prevent resource exhaustion. This control identifies all the Pods without resource limit definition.","remediation":"Define LimitRange and ResourceQuota policies to limit resource usage for namespaces or nodes.","rules":[{"guid":"","name":"resource-policies","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Check if container has limits\ndeny[msga] {\n \tpods := [pod | pod = input[_]; pod.kind == \"Pod\"]\n pod := pods[_]\n\tcontainer := pod.spec.containers[_]\n\tnot container.resources.limits\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"there are no resource limits defined for container : %v\", [container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n\n# Check if container has limits - for workloads\n# If there is no limits specified in the workload, we check the namespace, since if limits are only specified for namespace\n# and not in workload, it won't be on the yaml\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainer := wl.spec.template.spec.containers[_]\n\tnot container.resources.limits\n\tisNamespaceWithLimits(wl.metadata.namespace)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"there are no resource limits defined for container : %v\", [container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n\t\n}\n\n# Check if container has limits - for cronjobs\n# If there is no limits specified in the cronjob, we check the namespace, since if limits are only specified for namespace\n# and not in cronjob, it won't be on the yaml\ndeny [msga] {\n wl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer := wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tnot container.resources.limits\n\tisNamespaceWithLimits(wl.metadata.namespace)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"there are no resource limits defined for container : %v\", [container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if LimitRange exists but it does not define maximum usage of resources\ndeny[msga] {\n\n limitRanges := [limitRange | limitRange = input[_]; limitRange.kind == \"LimitRange\"]\n limitRange := limitRanges[_]\n\n\tlimits := limitRange.spec.limits[_]\n not limits.max\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following LimitRange: %v does not define a maximum field for resources\", [limitRange.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [limitRange]\n\t\t}\n\t}\n}\n\n# Fails if ResourQuota exists but it does not define maximum usage of resources\ndeny[msga] {\n resourceQuotas := [resourceQuota | resourceQuota = input[_]; resourceQuota.kind == \"ResourceQuota\"]\n resourceQuota := resourceQuotas[_]\n\n not resourceQuota.spec.hard\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following ResourQuota: %v does not define a hard field\", [resourceQuota.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [resourceQuota]\n\t\t}\n\t}\n}\n\n\nlist_contains(list, element) {\n some i\n list[i] == element\n}\n\n\n# Check only LimitRange. For ResourceQuota limits need to be specified. \nisNamespaceWithLimits(namespace) {\n limitRanges := [policy.metadata.namespace | policy = input[_]; policy.kind == \"LimitRange\"]\n not list_contains(limitRanges, namespace)\n}\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod","LimitRange","ResourceQuota"]}],"ruleDependencies":[],"description":"fails if namespace has no resource policies defined","remediation":"Make sure that you definy resource policies (LimitRange or ResourceQuota) which limit the usage of resources for all the namespaces","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Exposed dashboard","attributes":{"armoBuiltin":true,"microsoftMitreColumns":["Initial Access"]},"id":"C-0047","controlID":"C-0047","creationTime":"","description":"Kubernetes dashboard versions before v2.0.1 do not support user authentication. If exposed externally, it will allow unauthenticated remote management of the cluster. This control checks presence of the kubernetes-dashboard deployment and its version number.","remediation":"Update dashboard version to v2.0.1 and above.","rules":[{"guid":"","name":"rule-exposed-dashboard","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Initial Access::Exposed Dashboard"},"creationTime":"","rule":"package armo_builtins\n\n# input: pods\n# apiversion: v1\n# fails if dashboard exists and is exposed\n\ndeny[msga] {\n\tdeployment := input[_]\n startswith(deployment.metadata.name, \"kubernetes-dashboard\")\n container := deployment.spec.template.spec.containers[_]\n version := trim_prefix(container.image, \"kubernetesui/dashboard:v\")\n to_number(replace(version, \".\", \"\")) \u003c 201\n \n\tservice := input[_]\n\tservice.kind == \"Service\"\n\tisNodePortLbService(service)\n count({x | service.spec.selector[x]; deployment.metadata.labels[x]}) == count(service.spec.selector)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"dashboard exists and is exposed %s\", [container.image]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [deployment]\n\t\t}\n }\n}\n\n\n\nisNodePortLbService(service) {\n\tservice.spec.type == \"NodePort\"\n}\n\nisNodePortLbService(service) {\n\tservice.spec.type == \"LoadBalancer\"\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","Service"]}],"ruleDependencies":[],"description":"fails if dashboard exists and is exposed","remediation":"","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Allow privilege escalation","attributes":{"armoBuiltin":true},"id":"C-0016","controlID":"C-0016","creationTime":"","description":"Attackers may gain access to a container and uplift its privilege to enable excessive capabilities.","remediation":"If your application does not need it, make sure the allowPrivilegeEscalation field of the securityContext is set to false.","rules":[{"guid":"","name":"rule-allow-privilege-escalation","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container that allow privilege escalation\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isAllowPrivilegeEscalationContainer(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v allow privilege escalation\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n\n# Fails if workload has a container that allow privilege escalation\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isAllowPrivilegeEscalationContainer(container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v allow privilege escalation\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if cronjob has a container that allow privilege escalation\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tisAllowPrivilegeEscalationContainer(container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"container :%v in %v: %v allow privilege escalation\", [container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\nisAllowPrivilegeEscalationContainer(container) {\n container.securityContext.allowPrivilegeEscalation == true\n}\n\nisAllowPrivilegeEscalationContainer(container) {\n sysAdminCap := \"SYS_ADMIN\"\n contains(container.securityContext.capabilities.add[_], sysAdminCap)\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container allows privilege escalation","remediation":"Make sure that the allowPrivilegeEscalation field in the securityContext of pod/container is set to false","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Applications credentials in configuration files","attributes":{"armoBuiltin":true,"microsoftMitreColumns":["Credential access","Lateral Movement"]},"id":"C-0012","controlID":"C-0012","creationTime":"","description":"Attackers who have access to configuration files can steal the stored secrets and use them. This control checks if ConfigMaps or pod specifications have sensitive information in their configuration.","remediation":"Use Kubernetes secrets or Key Management Systems to store credentials.","rules":[{"guid":"","name":"rule-credentials-in-env-var","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Credential access::Applications credentials in configuration files, Lateral Movement::Applications credentials in configuration files"},"creationTime":"","rule":"package armo_builtins\n# import data.cautils as cautils\n# import data.kubernetes.api.client as client\n\ndeny[msga] {\n\tpod := input[_]\n pod.kind == \"Pod\"\n sensitive_key_names := {\"aws_access_key_id\", \"aws_secret_access_key\", \"azure_batchai_storage_account\", \"azure_batchai_storage_key\",\n \"azure_batch_account\", \"azure_batch_key\", \"passwd\",\"password\", \"username\", \"pwd\", \"cred\", \"token\", \"key\", \"cert\"}\n key_name := sensitive_key_names[_]\n container := pod.spec.containers[_]\n env := container.env[_]\n contains(lower(env.name), key_name)\n\tisNotReference(env)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Pod: %v has sensitive information in environment variables\", [pod.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n }\n}\n\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\n sensitive_key_names := {\"aws_access_key_id\", \"aws_secret_access_key\", \"azure_batchai_storage_account\", \"azure_batchai_storage_key\",\n \"azure_batch_account\", \"azure_batch_key\", \"passwd\",\"password\", \"username\", \"pwd\", \"cred\", \"token\", \"key\", \"cert\"}\n key_name := sensitive_key_names[_]\n container := wl.spec.template.spec.containers[_]\n env := container.env[_]\n contains(lower(env.name), key_name)\n\tisNotReference(env)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has sensitive information in environment variables\", [wl.kind, wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n }\n}\n\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n sensitive_key_names := {\"aws_access_key_id\", \"aws_secret_access_key\", \"azure_batchai_storage_account\", \"azure_batchai_storage_key\",\n \"azure_batch_account\", \"azure_batch_key\", \"passwd\",\"password\", \"username\", \"pwd\", \"cred\", \"token\", \"key\", \"cert\"}\n key_name := sensitive_key_names[_]\n\tcontainer := wl.spec.jobTemplate.spec.template.spec.containers[_]\n env := container.env[_]\n contains(lower(env.name), key_name)\n\tisNotReference(env)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Cronjob: %v has sensitive information in environment variables\", [wl.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n }\n}\n\n\n\nisNotReference(env)\n{\n\tnot env.valueFrom.secretKeyRef\n\tnot env.valueFrom.configMapKeyRef\n}\n\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if Pods have sensitive information in configuration","remediation":"","ruleQuery":"armo_builtins"},{"guid":"","name":"rule-credentials-configmap","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Credential access::Applications credentials in configuration files, Lateral Movement::Applications credentials in configuration files"},"creationTime":"","rule":"package armo_builtins\n# import data.cautils as cautils\n# import data.kubernetes.api.client as client\n\n# fails if config map has keys with suspicious name\ndeny[msga] {\n\tconfigmap := input[_]\n configmap.kind == \"ConfigMap\"\n sensitive_key_names := {\"aws_access_key_id\", \"aws_secret_access_key\", \"azure_batchai_storage_account\", \"azure_batchai_storage_key\",\n \"azure_batch_account\", \"azure_batch_key\", \"pass\", \"username\", \"pwd\", \"cred\", \"token\", \"key\", \"cert\"}\n key_name := sensitive_key_names[_]\n map_secret := configmap.data[map_key]\n contains(lower(map_key), key_name)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"this configmap has sensitive information: %v\", [configmap.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [configmap]\n\t\t}\n }\n}\n\n# fails if config map has values with suspicious content - not base 64\ndeny[msga] {\n sensitive_values := {\"BEGIN\", \"PRIVATE KEY\", \"eyJhbGciO\"}\n value := sensitive_values[_]\n\n\tconfigmap := input[_]\n configmap.kind == \"ConfigMap\"\n map_secret := configmap.data[map_key]\n contains(map_secret, value)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"this configmap has sensitive information: %v\", [configmap.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [configmap]\n\t\t}\n }\n}\n\n# fails if config map has values with suspicious content - base 64\ndeny[msga] {\n sensitive_values := {\"BEGIN\", \"PRIVATE KEY\", \"eyJhbGciO\"}\n value := sensitive_values[_]\n\n\tconfigmap := input[_]\n configmap.kind == \"ConfigMap\"\n map_secret := configmap.data[map_key]\n decoded_secret := base64.decode(map_secret)\n contains(decoded_secret, value)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"this configmap has sensitive information: %v\", [configmap.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [configmap]\n\t\t}\n }\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["ConfigMap"]}],"ruleDependencies":[],"description":"fails if ConfigMaps have sensitive information in configuration","remediation":"","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Cluster-admin binding","attributes":{"armoBuiltin":true,"microsoftMitreColumns":["Privilege escalation"]},"id":"C-0035","controlID":"C-0035","creationTime":"","description":"Attackers who have cluster admin permissions (can perform any action on any resource), can take advantage of their privileges for malicious activities. This control determines which subjects have cluster admin permissions.","remediation":"You should apply least privilege principle. Make sure cluster admin permissions are granted only when it is absolutely necessary. Don't use subjects with such high permissions for daily operations.","rules":[{"guid":"","name":"rule-list-all-cluster-admins","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Privilege Escalation::Cluster-admin binding"},"creationTime":"","rule":"package armo_builtins\nimport data.cautils as cautils\n\n# input: roles\n# apiversion: v1\n# does: returns roles+ related subjects in rolebinding\n\ndeny[msga] {\n\troles := [role | role= input[_]; role.kind == \"Role\"]\n rolebindings := [rolebinding | rolebinding = input[_]; rolebinding.kind == \"RoleBinding\"]\n role:= roles[_]\n rolebinding := rolebindings[_]\n\n rule:= role.rules[_]\n\tcanCreate(rule)\n\tcanCreateResources(rule)\n\n\trolebinding.roleRef.kind == \"Role\"\n\trolebinding.roleRef.name == role.metadata.name\n subjects := rolebinding.subjects[_]\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"The following %v: %v have high privileges, such as cluster-admin\", [subjects.kind, subjects.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [role,rolebinding],\n\t\t\t\"externalObjects\": {\n\t\t\t\t\"subject\" : [subjects]\n\t\t\t}\n\t\t}\n }\n}\n\n# input: ClusterRole\n# apiversion: v1\n# does: returns clusterroles+ related subjects in rolebinding\n\ndeny[msga] {\n roles := [role | role= input[_]; role.kind == \"ClusterRole\"]\n rolebindings := [rolebinding | rolebinding = input[_]; rolebinding.kind == \"RoleBinding\"]\n role:= roles[_]\n rolebinding := rolebindings[_]\n\n rule:= role.rules[_]\n\tcanCreate(rule)\n\tcanCreateResources(rule)\n\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n \n subjects := rolebinding.subjects[_]\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"The following %v: %v have high privileges, such as cluster-admin\", [subjects.kind, subjects.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \"alertObject\": {\n\t\t\t\"k8sApiObjects\": [role,rolebinding],\n\t\t\t\"externalObjects\": {\n\t\t\t\t\"subject\" : [subjects]\n\t\t\t}\n\t\t}\n }\n}\n\n# input: ClusterRole\n# apiversion: v1\n# does:\treturns clusterroles+ related subjects in clusterrolebinding\n\ndeny[msga] {\n roles := [role | role= input[_]; role.kind == \"ClusterRole\"]\n rolebindings := [rolebinding | rolebinding = input[_]; rolebinding.kind == \"ClusterRoleBinding\"]\n role:= roles[_]\n rolebinding := rolebindings[_]\n\n rule:= role.rules[_]\n\tcanCreate(rule)\n\tcanCreateResources(rule)\n\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n subjects := rolebinding.subjects[_]\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"The following %v: %v have high privileges, such as cluster-admin\", [subjects.kind, subjects.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [role,rolebinding],\n\t\t\t\"externalObjects\": {\n\t\t\t\t\"subject\" : [subjects]\n\t\t\t}\n\t\t}\n\t}\n}\n\n\ncanCreate(rule) {\n\tcautils.list_contains(rule.verbs,\"*\")\n}\ncanCreateResources(rule){\n\tcautils.list_contains(rule.resources,\"*\")\n}\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Role","ClusterRole","ClusterRoleBinding","RoleBinding"]}],"ruleDependencies":[{"packageName":"cautils"}],"description":"determines which users have cluster admin permissions","remediation":"","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Exec into container","attributes":{"armoBuiltin":true,"microsoftMitreColumns":["Execution"]},"id":"C-0002","controlID":"C-0002","creationTime":"","description":"Attackers with relevant permissions can run malicious commands in the context of legitimate containers in the cluster using “kubectl exec” command. This control determines which subjects have permissions to use this command.","remediation":"It is recommended to prohibit “kubectl exec” command in production environments. It is also recommended not to use subjects with this permission for daily cluster operations.","rules":[{"guid":"","name":"exec-into-container","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Privilege Escalation::Exec into container"},"creationTime":"","rule":"\npackage armo_builtins\nimport data.cautils as cautils\n\n\n# input: clusterrolebindings + rolebindings\n# apiversion: rbac.authorization.k8s.io/v1 \n# returns subjects that can exec into container\n\ndeny[msga] {\n\t roles := [role | role= input[_]; role.kind == \"Role\"]\n rolebindings := [rolebinding | rolebinding = input[_]; rolebinding.kind == \"RoleBinding\"]\n role:= roles[_]\n rolebinding := rolebindings[_]\n\n rule:= role.rules[_]\n\tcanExecToPodResource(rule)\n\tcanExecToPodVerb(rule)\n\n\trolebinding.roleRef.kind == \"Role\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n subjects := rolebinding.subjects[_]\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following %v: %v, can exec into containers\", [subjects.kind, subjects.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [role, rolebinding],\n\t\t\t\"externalObjects\": {\n\t\t\t\t\"subject\" : [subjects]\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n# input: clusterrolebindings + rolebindings\n# apiversion: rbac.authorization.k8s.io/v1 \n# returns subjects that can exec into container\n\ndeny[msga] {\n roles := [role | role= input[_]; role.kind == \"ClusterRole\"]\n rolebindings := [rolebinding | rolebinding = input[_]; rolebinding.kind == \"RoleBinding\"]\n role:= roles[_]\n rolebinding := rolebindings[_]\n\n rule:= role.rules[_]\n\tcanExecToPodResource(rule)\n\tcanExecToPodVerb(rule)\n\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n subjects := rolebinding.subjects[_]\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following %v: %v, can exec into containers\", [subjects.kind, subjects.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [role, rolebinding],\n\t\t\t\"externalObjects\": {\n\t\t\t\t\"subject\" : [subjects]\n\t\t\t}\n\t\t}\n\t}\n}\n\n# input: clusterrolebindings + rolebindings\n# apiversion: rbac.authorization.k8s.io/v1 \n# returns subjects that can exec into container\n\ndeny[msga] {\n roles := [role | role= input[_]; role.kind == \"ClusterRole\"]\n rolebindings := [rolebinding | rolebinding = input[_]; rolebinding.kind == \"ClusterRoleBinding\"]\n role:= roles[_]\n rolebinding := rolebindings[_]\n\n rule:= role.rules[_]\n\tcanExecToPodResource(rule)\n\tcanExecToPodVerb(rule)\n\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n subjects := rolebinding.subjects[_]\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following %v: %v, can exec into containers\", [subjects.kind, subjects.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n \t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [role, rolebinding],\n\t\t\t\"externalObjects\": {\n\t\t\t\t\"subject\" : [subjects]\n\t\t\t}\n\t\t}\n\t}\n}\n\ncanExecToPodVerb(rule) {\n\tcautils.list_contains(rule.verbs, \"create\")\n}\ncanExecToPodVerb(rule) {\n\tcautils.list_contains(rule.verbs, \"*\")\n}\n\ncanExecToPodResource(rule) {\n\tcautils.list_contains(rule.resources,\"pods/exec\")\n}\ncanExecToPodResource(rule) {\n\tcautils.list_contains(rule.resources,\"pods/*\")\n}\ncanExecToPodResource(rule) {\n\tcautils.list_contains(rule.resources,\"*\")\n}\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Role","ClusterRole","ClusterRoleBinding","RoleBinding"]}],"ruleDependencies":[{"packageName":"cautils"}],"description":"determines which users have permissions to exec into pods","remediation":"","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Dangerous capabilities","attributes":{"armoBuiltin":true},"id":"C-0028","controlID":"C-0028","creationTime":"","description":"Giving dangerous and unnecessary LINUX capabilities to a container can increase the impact of the container compromise. This control identifies all the PODs with dangerous capabilities such as SYS_ADMIN and others.","remediation":"Check and remove all unnecessary capabilities from the POD security context of the containers and use the exception mechanism to remove warnings where these capabilities are necessary.","rules":[{"guid":"","name":"dangerous-capabilities","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isDangerousCapabilities(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v have dangerous capabilities\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainer := wl.spec.template.spec.containers[_]\n isDangerousCapabilities(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in workload: %v have dangerous capabilities\", [container.name, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\ndeny[msga] {\n wl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer := wl.spec.jobTemplate.spec.template.spec.containers[_]\n isDangerousCapabilities(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in cronjob: %v have dangerous capabilities\", [container.name, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisDangerousCapabilities(container){\n dangerousCapabilities := [\"ALL\", \"SYS_ADMIN\", \"NET_ADMIN\", \"SYS_PTRACE\"]\n dangerousCapabilitie := dangerousCapabilities[_]\n contains(container.securityContext.capabilities.add[_], dangerousCapabilitie)\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container has dangrous capabilities","remediation":"Remove all dangerous capabilities which arent necessary for the container.","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Insecure capabilities","attributes":{"armoBuiltin":true},"id":"C-0046","controlID":"C-0046","creationTime":"","description":"Giving insecure or excsessive capabilities to a container can increase the impact of the container compromise. This control identifies all the PODs with dangerous capabilities (see documentation pages for details).","remediation":"Remove all insecure capabilities which arent necessary for the container.","rules":[{"guid":"","name":"insecure-capabilities","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isDangerousCapabilities(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in pod: %v have dangerous capabilities\", [container.name, pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainer := wl.spec.template.spec.containers[_]\n isDangerousCapabilities(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in workload: %v have dangerous capabilities\", [container.name, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\ndeny[msga] {\n wl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer := wl.spec.jobTemplate.spec.template.spec.containers[_]\n isDangerousCapabilities(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"container: %v in cronjob: %v have dangerous capabilities\", [container.name, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisDangerousCapabilities(container){\n insecureCapabilities := [\"CHOWN\", \"DAC_OVERRIDE\", \"FSETID\", \"FOWNER\", \"MKNOD\", \"NET_RAW\", \"SETGID\", \"SETUID\", \"SETFCAP\", \"NET_BIND_SERVICE\",\"SYS_CHROOT\",\"KILL\",\"AUDIT_WRITE\"]\n insecureCapabilitie := insecureCapabilities[_]\n contains(container.securityContext.capabilities.add[_], insecureCapabilitie)\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container has insecure capabilities","remediation":"Remove all insecure capabilities which arent necessary for the container.","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Linux hardening","attributes":{"armoBuiltin":true},"id":"C-0055","controlID":"C-0055","creationTime":"","description":"Containers may be given more privileges than they actually need. This can increase the potential impact of a container compromise.","remediation":"You can use AppArmor, Seccomp, SELinux and Linux Capabilities mechanisms to restrict containers abilities to utilize unwanted privileges.","rules":[{"guid":"","name":"linux-hardening","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod does not define linux security hardening \ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n isUnsafePod(pod)\n container := pod.spec.containers[_]\n isUnsafeContainer(container)\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Pod: %v does not define any linux security hardening\", [pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload does not define linux security hardening \ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n isUnsafeWorkload(wl)\n container := wl.spec.template.spec.containers[_]\n isUnsafeContainer(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Workload: %v does not define any linux security hardening\", [wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# Fails if pod does not define linux security hardening \ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n isUnsafeCronJob(wl)\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n isUnsafeContainer(container)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Cronjob: %v does not define any linux security hardening\", [wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisUnsafePod(pod){\n not pod.spec.securityContext.seccompProfile\n not pod.spec.securityContext.seLinuxOptions\n\tannotations := [pod.metadata.annotations[i] | annotaion = i; startswith(i, \"container.apparmor.security.beta.kubernetes.io\")]\n\tnot count(annotations) \u003e 0\n}\n\nisUnsafeContainer(container){\n not container.securityContext.seccompProfile\n not container.securityContext.seLinuxOptions\n not container.securityContext.capabilities.drop\n}\n\nisUnsafeWorkload(wl) {\n not wl.spec.template.spec.securityContext.seccompProfile\n not wl.spec.template.spec.securityContext.seLinuxOptions\n\tannotations := [wl.spec.template.metadata.annotations[i] | annotaion = i; startswith(i, \"container.apparmor.security.beta.kubernetes.io\")]\n\tnot count(annotations) \u003e 0\n}\n\nisUnsafeCronJob(cronjob) {\n not cronjob.spec.jobTemplate.spec.template.spec.securityContext.seccompProfile\n not cronjob.spec.jobTemplate.spec.template.spec.securityContext.seLinuxOptions\n\tannotations := [cronjob.spec.jobTemplate.spec.template.metadata.annotations[i] | annotaion = i; startswith(i, \"container.apparmor.security.beta.kubernetes.io\")]\n\tnot count(annotations) \u003e 0\n}\n\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container does not defien any linux security hardening","remediation":"Make sure you define at least one linux security hardening property out of Seccomp, SELinux or Capabilities.","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Ingress and Egress blocked","attributes":{"armoBuiltin":true},"id":"C-0030","controlID":"C-0030","creationTime":"","description":"Disable Ingress and Egress traffic on all pods wherever possible. It is recommended to define restrictive network policy on all new PODs, and then enable sources/destinations that this POD must communicate with.","remediation":"Define a network policy that restricts ingress and egress connections.","rules":[{"guid":"","name":"ingress-and-egress-blocked","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# For pods\ndeny[msga] {\n \t\tpods := [pod | pod= input[_]; pod.kind == \"Pod\"]\n\t\tnetworkpolicies := [networkpolicie | networkpolicie= input[_]; networkpolicie.kind == \"NetworkPolicy\"]\n\t\tpod := pods[_]\n\t\tnetworkpoliciesConnectedToPod := [networkpolicie | networkpolicie= networkpolicies[_]; podConnectedToNetworkPolicy(pod, networkpolicie)]\n\t\tcount(networkpoliciesConnectedToPod) \u003e 0\n goodPolicies := [goodpolicie | goodpolicie= networkpoliciesConnectedToPod[_]; isIngerssEgressPolicy(goodpolicie)]\n\t\tcount(goodPolicies) \u003c 1\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"Pod: %v does not have ingress/egress defined\", [pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n\n}\n\n# For pods\ndeny[msga] {\n \t\tpods := [pod | pod= input[_]; pod.kind == \"Pod\"]\n\t\tnetworkpolicies := [networkpolicie | networkpolicie= input[_]; networkpolicie.kind == \"NetworkPolicy\"]\n\t\tpod := pods[_]\n\t\tnetworkpoliciesConnectedToPod := [networkpolicie | networkpolicie= networkpolicies[_]; podConnectedToNetworkPolicy(pod, networkpolicie)]\n\t\tcount(networkpoliciesConnectedToPod) \u003c 1\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"Pod: %v does not have ingress/egress defined\", [pod.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n\n}\n\n# For workloads\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n networkpolicies := [networkpolicie | networkpolicie= input[_]; networkpolicie.kind == \"NetworkPolicy\"]\n\tnetworkpoliciesConnectedToPod := [networkpolicie | networkpolicie= networkpolicies[_]; wlConnectedToNetworkPolicy(wl, networkpolicie)]\n\tcount(networkpoliciesConnectedToPod) \u003e 0\n goodPolicies := [goodpolicie | goodpolicie= networkpoliciesConnectedToPod[_]; isIngerssEgressPolicy(goodpolicie)]\n\tcount(goodPolicies) \u003c 1\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has Pods which don't have ingress/egress defined\", [wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# For workloads\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n networkpolicies := [networkpolicie | networkpolicie= input[_]; networkpolicie.kind == \"NetworkPolicy\"]\n\tnetworkpoliciesConnectedToPod := [networkpolicie | networkpolicie= networkpolicies[_]; wlConnectedToNetworkPolicy(wl, networkpolicie)]\n\tcount(networkpoliciesConnectedToPod) \u003c 1\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has Pods which don't have ingress/egress defined\", [wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# For Cronjobs\ndeny[msga] {\n wl := input[_]\n\twl.kind == \"CronJob\"\n networkpolicies := [networkpolicie | networkpolicie= input[_]; networkpolicie.kind == \"NetworkPolicy\"]\n\tnetworkpoliciesConnectedToPod := [networkpolicie | networkpolicie= networkpolicies[_]; cronjobConnectedToNetworkPolicy(wl, networkpolicie)]\n\tcount(networkpoliciesConnectedToPod) \u003e 0\n goodPolicies := [goodpolicie | goodpolicie= networkpoliciesConnectedToPod[_]; isIngerssEgressPolicy(goodpolicie)]\n\tcount(goodPolicies) \u003c 1\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has Pods which don't have ingress/egress defined\", [wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n# For Cronjobs\ndeny[msga] {\n wl := input[_]\n\twl.kind == \"CronJob\"\n networkpolicies := [networkpolicie | networkpolicie= input[_]; networkpolicie.kind == \"NetworkPolicy\"]\n\tnetworkpoliciesConnectedToPod := [networkpolicie | networkpolicie= networkpolicies[_]; cronjobConnectedToNetworkPolicy(wl, networkpolicie)]\n\tcount(networkpoliciesConnectedToPod) \u003c 1\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has Pods which don't have ingress/egress defined\", [wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\npodConnectedToNetworkPolicy(pod, networkpolicie){\n\tnetworkpolicie.metadata.namespace == pod.metadata.namespace\n count(networkpolicie.spec.podSelector) \u003e 0\n count({x | networkpolicie.spec.podSelector.matchLabels[x] == pod.metadata.labels[x]}) == count(networkpolicie.spec.podSelector.matchLabels)\n}\n\npodConnectedToNetworkPolicy(pod, networkpolicie){\n\tnetworkpolicie.metadata.namespace == pod.metadata.namespace\n count(networkpolicie.spec.podSelector) == 0\n}\n\nwlConnectedToNetworkPolicy(wl, networkpolicie){\n\twl.metadata.namespace == networkpolicie.metadata.namespace\n count(networkpolicie.spec.podSelector) == 0\n}\n\n\nwlConnectedToNetworkPolicy(wl, networkpolicie){\n\twl.metadata.namespace == wl.metadata.namespace\n\tcount(networkpolicie.spec.podSelector) \u003e 0\n count({x | networkpolicie.spec.podSelector.matchLabels[x] == wl.spec.template.metadata.labels[x]}) == count(networkpolicie.spec.podSelector.matchLabels)\n}\n\n\ncronjobConnectedToNetworkPolicy(cj, networkpolicie){\n\tcj.metadata.namespace == networkpolicie.metadata.namespace\n count(networkpolicie.spec.podSelector) == 0\n}\n\ncronjobConnectedToNetworkPolicy(cj, networkpolicie){\n\tcj.metadata.namespace == networkpolicie.metadata.namespace\n\tcount(networkpolicie.spec.podSelector) \u003e 0\n count({x | networkpolicie.spec.podSelector.matchLabels[x] == cj.spec.jobTemplate.spec.template.metadata.labels[x]}) == count(networkpolicie.spec.podSelector.matchLabels)\n}\n\nisIngerssEgressPolicy(networkpolicie) {\n list_contains(networkpolicie.spec.policyTypes, \"Ingress\")\n list_contains(networkpolicie.spec.policyTypes, \"Egress\")\n }\n\nlist_contains(list, element) {\n some i\n list[i] == element\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob","NetworkPolicy"]}],"ruleDependencies":[],"description":"fails if there are no ingress and egress defined for pod","remediation":"Make sure you define ingress and egress policies for all your Pods","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Container hostPort","attributes":{"armoBuiltin":true},"id":"C-0044","controlID":"C-0044","creationTime":"","description":"Configuring hostPort limits you to a particular port, and if any two workloads that specify the same HostPort they cannot be deployed to the same node. Therefore, if the number of replica of such workload is higher than the number of nodes, the deployment will fail.","remediation":"Avoid usage of hostPort unless it is absolutely necessary. Use NodePort / ClusterIP instead.","rules":[{"guid":"","name":"container-hostPort","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container with hostPort\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n container := pod.spec.containers[_]\n\tisHostPort(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v has Host-port\", [ container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has container with hostPort\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isHostPort(container)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if cronjob has container with hostPort\ndeny[msga] {\n \twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n isHostPort(container)\n msga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\nisHostPort(container){\n\tports := container.ports[_]\n ports.hostPort\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"description":"fails if container has hostPort","remediation":"Make sure you do not configure hostPort for the container, if necessary use NodePort / ClusterIP","ruleQuery":"armo_builtins"}]},{"guid":"","name":"Network policies","attributes":{"armoBuiltin":true},"id":"C-0011","controlID":"C-0011","creationTime":"","description":"If no network policy is defined, attackers who gain access to a single container may use it to probe the network. This control lists all namespaces in which no network policies are defined.","remediation":"Define network policies or use similar network protection mechanisms.","rules":[{"guid":"","name":"internal-networking","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Lateral Movement::Container internal networking, Discovery::Network mapping"},"creationTime":"","rule":"package armo_builtins\n\n# input: network policies\n# apiversion: networking.k8s.io/v1\n# fails if no network policies are defined in a certain namespace\n\ndeny[msga] {\n\tnamespaces := [namespace | namespace = input[_]; namespace.kind == \"Namespace\"]\n\tnamespace := namespaces[_]\n\tpolicy_names := [policy.metadata.namespace | policy = input[_]; policy.kind == \"NetworkPolicy\"]\n\tnot list_contains(policy_names, namespace.metadata.name)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"no policy is defined for namespace %v\", [namespace.metadata.name]),\n\t\t\"alertScore\": 9,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [namespace]\n\t\t}\n\t}\n}\n\nlist_contains(list, element) {\n some i\n list[i] == element\n}","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["networkpolicies","namespaces"]}],"ruleDependencies":[],"description":"lists namespaces in which no network policies are defined","remediation":"","ruleQuery":"armo_builtins"}]},{"guid":"","name":"CVE-2021-25741 - Using symlink for arbitrary host file system access.","attributes":{"armoBuiltin":true},"id":"C-0058","controlID":"C-0058","creationTime":"","description":"A user may be able to create a container with subPath or subPathExpr volume mounts to access files \u0026 directories anywhere on the host filesystem. Following Kubernetes versions are affected: v1.22.0 - v1.22.1, v1.21.0 - v1.21.4, v1.20.0 - v1.20.10, version v1.19.14 and lower. This control checks the vulnerable versions and the actual usage of the subPath feature in all Pods in the cluster.","remediation":"To mitigate this vulnerability without upgrading kubelet, you can disable the VolumeSubpath feature gate on kubelet and kube-apiserver, or remove any existing Pods using subPath or subPathExpr feature.","rules":[{"guid":"","name":"Symlink-Exchange-Can-Allow-Host-Filesystem-Access","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\ndeny[msga] {\n\tnodes := input[_]\n\tcurrent_version := nodes.status.nodeInfo.kubeletVersion\n isVulnerableVersion(current_version)\n pod := input[_]\n pod.kind == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n isSubPathContainer(container)\n\n\tmsga := {\n\t\t\t\"alertMessage\": sprintf(\"You may be vulnerable to CVE-2021-25741. You have a Node with version: %v and the following container : %v in pod : %v with subPath/subPathExpr\", [current_version, container.name, pod.metadata.name]),\n\t\t\t\"alertObject\": {\"k8SApiObjects\": [nodes, pod]},\n\t\t}\n}\n\n\ndeny[msga] {\n\tnodes := input[_]\n\tcurrent_version := nodes.status.nodeInfo.kubeletVersion\n isVulnerableVersion(current_version)\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[_]\n isSubPathContainer(container)\n \n\tmsga := {\n\t\"alertMessage\": sprintf(\"You may be vulnerable to CVE-2021-25741. You have a Node with version: %v and the following container : %v in %v : %v with subPath/subPathExpr\", [current_version, container.name, wl.kind, wl.metadata.name]),\n\t\t\t\"alertObject\": {\"k8SApiObjects\": [nodes, wl]},\n\t\t}\n}\n\n\n\ndeny[msga] {\n\tnodes := input[_]\n\tcurrent_version := nodes.status.nodeInfo.kubeletVersion\n isVulnerableVersion(current_version)\n wl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[_]\n isSubPathContainer(container)\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"You may be vulnerable to CVE-2021-25741. You have a Node with version: %v and the following container : %v in %v : %v with subPath/subPathExpr\", [current_version, container.name, wl.kind, wl.metadata.name]),\n\t\t\t\"alertObject\": {\"k8SApiObjects\": [nodes, wl]},\n\t\t}\n}\n\n\n\nisSubPathContainer(container){\n container.volumeMounts[_].subPath\n}\n\nisSubPathContainer(container){\n container.volumeMounts[_].subPathExpr\n}\n\nisVulnerableVersion(version){\n version \u003c= \"v1.19.14\"\n}\n\nisVulnerableVersion(version){\n version \u003e= \"v1.22.0\"\n version \u003c= \"v1.22.1\"\n}\n\n\nisVulnerableVersion(version){\n version \u003e= \"v1.21.0\"\n version \u003c= \"v1.21.4\"\n}\n\n\nisVulnerableVersion(version){\n version \u003e= \"v1.20.0\"\n version \u003c= \"v1.20.9\"\n}\n\nisVulnerableVersion(version){\n\tversion == \"v1.20.10\"\n}\n\n\n","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob","Node"]}],"ruleDependencies":[],"description":"A user may be able to create a container with subPath volume mounts to access files \u0026 directories outside of the volume, including on the host filesystem. This was affected at the following versions: v1.22.0 - v1.22.1, v1.21.0 - v1.21.4, v1.20.0 - v1.20.10, version v1.19.14 and lower. ","remediation":"To mitigate this vulnerability without upgrading kubelet, you can disable the VolumeSubpath feature gate on kubelet and kube-apiserver, and remove any existing Pods making use of the feature.","ruleQuery":"armo_builtins"}]}]}