Kubesec.io admission controller for Kubernetes Pods, Deployments, DaemonSets and StatefulSets.
For the kubectl scan plugin see kubectl-kubesec
Generate webhook configuration files with a new TLS certificate and CA Bundle:
make certs
Deploy the admission controller and webhooks in the kubesec namespace (requires Kubernetes 1.20 or newer):
make deploy
Enable Kubesec validation by adding this label:
kubectl label namespaces default kubesec-validation=enabled
Try to apply a privileged Deployment:
kubectl apply -f ./test/deployment.yaml
Error from server (InternalError): error when creating "./test/deployment.yaml":
Internal error occurred: admission webhook "deployment.admission.kubesec.io" denied the request:
deployment-test score is -30, deployment minimum accepted score is 0
Scan Result:
{
"error": "",
"score": -30,
"scoring": {
"critical": [
{
"selector": "containers[] .securityContext .privileged == true",
"reason": "Privileged containers can allow almost completely unrestricted host access",
"weight": 0
}
],
"advise": [
{
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the running image to run as a non-root user to ensure least privilege"
},
{
"selector": "containers[] .securityContext .capabilities .drop",
"reason": "Reducing kernel capabilities available to a container limits its attack surface",
"href": "https://kubernetes.io/docs/tasks/configure-pod-container/security-context/"
},
{
"selector": "containers[] .securityContext .readOnlyRootFilesystem == true",
"reason": "An immutable root filesystem can prevent malicious binaries being added to
PATH and increase attack cost"
},
{
"selector": "containers[] .securityContext .runAsUser \u003e 10000",
"reason": "Run as a high-UID user to avoid conflicts with the host's user table"
},
{
"selector": "containers[] .securityContext .capabilities .drop | index(\"ALL\")",
"reason": "Drop all capabilities and add only those required to reduce syscall attack surface"
}
]
}
}
Try to apply a privileged DaemonSet:
kubectl apply -f ./test/daemonset.yaml
Error from server (InternalError): error when creating "./test/daemonset.yaml":
Internal error occurred: admission webhook "daemonset.admission.kubesec.io" denied the request:
daemonset-test score is -30, daemonset minimum accepted score is 0
Scan Result:
{
"error": "",
"score": -30,
"scoring": {
"critical": [
{
"selector": "containers[] .securityContext .privileged == true",
"reason": "Privileged containers can allow almost completely unrestricted host access",
"weight": 0
}
],
"advise": [
{
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the running image to run as a non-root user to ensure least privilege"
},
{
"selector": "containers[] .securityContext .capabilities .drop",
"reason": "Reducing kernel capabilities available to a container limits its attack surface",
"href": "https://kubernetes.io/docs/tasks/configure-pod-container/security-context/"
},
{
"selector": "containers[] .securityContext .readOnlyRootFilesystem == true",
"reason": "An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost"
},
{
"selector": "containers[] .securityContext .runAsUser \u003e 10000",
"reason": "Run as a high-UID user to avoid conflicts with the host's user table"
},
{
"selector": "containers[] .securityContext .capabilities .drop | index(\"ALL\")",
"reason": "Drop all capabilities and add only those required to reduce syscall attack surface"
}
]
}
}
Try to apply a privileged StatefulSet:
kubectl apply -f ./test/statefulset.yaml
Error from server (InternalError): error when creating "./test/statefulset.yaml":
Internal error occurred: admission webhook "statefulset.admission.kubesec.io" denied the request:
statefulset-test score is -30, statefulset minimum accepted score is 0
Scan Result:
{
"error": "",
"score": -30,
"scoring": {
"critical": [
{
"selector": "containers[] .securityContext .privileged == true",
"reason": "Privileged containers can allow almost completely unrestricted host access",
"weight": 0
}
],
"advise": [
{
"selector": ".spec .volumeClaimTemplates[] .spec .accessModes | index(\"ReadWriteOnce\")",
"reason": ""
},
{
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the running image to run as a non-root user to ensure least privilege"
},
{
"selector": "containers[] .securityContext .capabilities .drop",
"reason": "Reducing kernel capabilities available to a container limits its attack surface",
"href": "https://kubernetes.io/docs/tasks/configure-pod-container/security-context/"
},
{
"selector": "containers[] .securityContext .readOnlyRootFilesystem == true",
"reason": "An immutable root filesystem can prevent malicious binaries being added to
PATH and increase attack cost"
},
{
"selector": "containers[] .securityContext .runAsUser \u003e 10000",
"reason": "Run as a high-UID user to avoid conflicts with the host's user table"
}
]
}
}
You can set the minimum Kubesec.io score in ./deploy/webhook/yaml
:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubesec-webhook
labels:
app: kubesec-webhook
spec:
replicas: 1
template:
metadata:
labels:
app: kubesec-webhook
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8081"
spec:
containers:
- name: kubesec-webhook
image: controlplaneio/kubesec:0.1-dev
imagePullPolicy: Always
command:
- ./kubesec
args:
- -tls-cert-file=/etc/webhook/certs/cert.pem
- -tls-key-file=/etc/webhook/certs/key.pem
- -min-score=0
ports:
- containerPort: 8080
- containerPort: 8081
volumeMounts:
- name: webhook-certs
mountPath: /etc/webhook/certs
readOnly: true
volumes:
- name: webhook-certs
secret:
secretName: kubesec-webhook-certs
The admission controller exposes Prometheus RED metrics for each webhook a Grafana dashboard is available here.
Kudos to Xabier for the awesome kubewebhook library.