Day 1: Kubernetes Security -Kubesec

Reference: https://kubesec.io/
  • Kubesec can be installed as a binary package, container image, Admission controller or even as a kubectl plugin.
wget https://github.com/controlplaneio/kubesec/releases/download/v2.11.4/kubesec_linux_amd64.tar.gztar -xvf kubesec_linux_amd64.tar.gz
  • If you pass the below pod.yaml to the kubesec
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
containers:
- name: sec-ctx-demo
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
privileged: true
  • As you can see in the output, Kubesec assigned the score of minus 30 with the reason “Privileged containers can allow almost completely unrestricted host access”.
./kubesec scan pod.yaml 
[
{
"object": "Pod/security-context-demo.default",
"valid": true,
"fileName": "pod.yaml",
"message": "Failed with a score of -30 points",
"score": -30,
"scoring": {
"critical": [
{
"id": "Privileged",
"selector": "containers[] .securityContext .privileged == true",
"
reason": "Privileged containers can allow almost completely unrestricted host access",
"points": -30
}
],
"advise": [
{
"id": "ApparmorAny",
"selector": ".metadata .annotations .\"container.apparmor.security.beta.kubernetes.io/nginx\"",
"reason": "Well defined AppArmor policies may provide greater protection from unknown threats. WARNING: NOT PRODUCTION READY",
"points": 3
},
{
"id": "ServiceAccountName",
"selector": ".spec .serviceAccountName",
"reason": "Service accounts restrict Kubernetes API access and should be configured with least privilege",
"points": 3
},
{
"id": "SeccompAny",
"selector": ".metadata .annotations .\"container.seccomp.security.alpha.kubernetes.io/pod\"",
"reason": "Seccomp profiles set minimum privilege and secure against unknown threats",
"points": 1
},
{
"id": "LimitsCPU",
"selector": "containers[] .resources .limits .cpu",
"reason": "Enforcing CPU limits prevents DOS via resource exhaustion",
"points": 1
},
{
"id": "LimitsMemory",
"selector": "containers[] .resources .limits .memory",
"reason": "Enforcing memory limits prevents DOS via resource exhaustion",
"points": 1
},
{
"id": "RequestsCPU",
"selector": "containers[] .resources .requests .cpu",
"reason": "Enforcing CPU requests aids a fair balancing of resources across the cluster",
"points": 1
},
{
"id": "RequestsMemory",
"selector": "containers[] .resources .requests .memory",
"reason": "Enforcing memory requests aids a fair balancing of resources across the cluster",
"points": 1
},
{
"id": "CapDropAny",
"selector": "containers[] .securityContext .capabilities .drop",
"reason": "Reducing kernel capabilities available to a container limits its attack surface",
"points": 1
},
{
"id": "CapDropAll",
"selector": "containers[] .securityContext .capabilities .drop | index(\"ALL\")",
"reason": "Drop all capabilities and add only those required to reduce syscall attack surface",
"points": 1
},
{
"id": "ReadOnlyRootFilesystem",
"selector": "containers[] .securityContext .readOnlyRootFilesystem == true",
"reason": "An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost",
"points": 1
},
{
"id": "RunAsNonRoot",
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the running image to run as a non-root user to ensure least privilege",
"points": 1
},
{
"id": "RunAsUser",
"selector": "containers[] .securityContext .runAsUser -gt 10000",
"reason": "Run as a high-UID user to avoid conflicts with the host's user table",
"points": 1
}
]
}
}
]
  • Now fix the yaml file by removing the security context block or set privileged: false
apiVersion: v1kind: Podmetadata:name: security-context-demospec:containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]
  • Run the kubesec scan again. As you can see this time score is 0 and the message of passed.
./kubesec scan pod.yaml[{"object": "Pod/security-context-demo.default","valid": true,"fileName": "pod.yaml","message": "Passed with a score of 0 points","score": 0,"scoring": {"advise": [{"id": "ApparmorAny","selector": ".metadata .annotations .\"container.apparmor.security.beta.kubernetes.io/nginx\"","reason": "Well defined AppArmor policies may provide greater protection from unknown threats. WARNING: NOT PRODUCTION READY","points": 3},{"id": "ServiceAccountName","selector": ".spec .serviceAccountName","reason": "Service accounts restrict Kubernetes API access and should be configured with least privilege","points": 3},{"id": "SeccompAny","selector": ".metadata .annotations .\"container.seccomp.security.alpha.kubernetes.io/pod\"","reason": "Seccomp profiles set minimum privilege and secure against unknown threats","points": 1},{"id": "LimitsCPU","selector": "containers[] .resources .limits .cpu","reason": "Enforcing CPU limits prevents DOS via resource exhaustion","points": 1},{"id": "LimitsMemory","selector": "containers[] .resources .limits .memory","reason": "Enforcing memory limits prevents DOS via resource exhaustion","points": 1},{"id": "RequestsCPU","selector": "containers[] .resources .requests .cpu","reason": "Enforcing CPU requests aids a fair balancing of resources across the cluster","points": 1},{"id": "RequestsMemory","selector": "containers[] .resources .requests .memory","reason": "Enforcing memory requests aids a fair balancing of resources across the cluster","points": 1},{"id": "CapDropAny","selector": "containers[] .securityContext .capabilities .drop","reason": "Reducing kernel capabilities available to a container limits its attack surface","points": 1},{"id": "CapDropAll","selector": "containers[] .securityContext .capabilities .drop | index(\"ALL\")","reason": "Drop all capabilities and add only those required to reduce syscall attack surface","points": 1},{"id": "ReadOnlyRootFilesystem","selector": "containers[] .securityContext .readOnlyRootFilesystem == true","reason": "An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost","points": 1},{"id": "RunAsNonRoot","selector": "containers[] .securityContext .runAsNonRoot == true","reason": "Force the running image to run as a non-root user to ensure least privilege","points": 1},{"id": "RunAsUser","selector": "containers[] .securityContext .runAsUser -gt 10000","reason": "Run as a high-UID user to avoid conflicts with the host's user table","points": 1}]}}]
curl -sSX POST --data-binary @"pod.yaml" https://v2.kubesec.io/scan[{"object": "Pod/security-context-demo.default","valid": true,"fileName": "API","message": "Passed with a score of 0 points","score": 0,"scoring": {"advise": [{"id": "ApparmorAny","selector": ".metadata .annotations .\"container.apparmor.security.beta.kubernetes.io/nginx\"","reason": "Well defined AppArmor policies may provide greater protection from unknown threats. WARNING: NOT PRODUCTION READY","points": 3},{"id": "ServiceAccountName","selector": ".spec .serviceAccountName","reason": "Service accounts restrict Kubernetes API access and should be configured with least privilege","points": 3},{"id": "SeccompAny","selector": ".metadata .annotations .\"container.seccomp.security.alpha.kubernetes.io/pod\"","reason": "Seccomp profiles set minimum privilege and secure against unknown threats","points": 1},{"id": "LimitsCPU","selector": "containers[] .resources .limits .cpu","reason": "Enforcing CPU limits prevents DOS via resource exhaustion","points": 1},{"id": "LimitsMemory","selector": "containers[] .resources .limits .memory","reason": "Enforcing memory limits prevents DOS via resource exhaustion","points": 1},{"id": "RequestsCPU","selector": "containers[] .resources .requests .cpu","reason": "Enforcing CPU requests aids a fair balancing of resources across the cluster","points": 1},{"id": "RequestsMemory","selector": "containers[] .resources .requests .memory","reason": "Enforcing memory requests aids a fair balancing of resources across the cluster","points": 1},{"id": "CapDropAny","selector": "containers[] .securityContext .capabilities .drop","reason": "Reducing kernel capabilities available to a container limits its attack surface","points": 1},{"id": "CapDropAll","selector": "containers[] .securityContext .capabilities .drop | index(\"ALL\")","reason": "Drop all capabilities and add only those required to reduce syscall attack surface","points": 1},{"id": "ReadOnlyRootFilesystem","selector": "containers[] .securityContext .readOnlyRootFilesystem == true","reason": "An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost","points": 1},{"id": "RunAsNonRoot","selector": "containers[] .securityContext .runAsNonRoot == true","reason": "Force the running image to run as a non-root user to ensure least privilege","points": 1},{"id": "RunAsUser","selector": "containers[] .securityContext .runAsUser -gt 10000","reason": "Run as a high-UID user to avoid conflicts with the host's user table","points": 1}]}}]
docker run -i kubesec/kubesec:v2 scan /dev/stdin < pod.yaml
kubesec http 8080 &
  • Then you can use curl to post the request
$ curl -sSX POST --data-binary @pod.yaml http://localhost:8080/scan

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Prashant Lakhera

Prashant Lakhera

AWS Community Builder, Ex-Redhat, Author, Blogger, YouTuber, RHCA, RHCDS, RHCE, Docker Certified,4XAWS, CCNA, MCP, Certified Jenkins, Terraform Certified, 1XGCP