Kubernetes is widely used for orchestrating containerized workloads. Securing a Kubernetes cluster requires configuring control-plane components, worker nodes, and workloads according to security best practices. As Kubernetes clusters evolve through upgrades and operational changes, maintaining secure configurations becomes increasingly challenging.
The CIS Kubernetes Benchmark provides security recommendations for hardening Kubernetes clusters, including control-plane components, worker nodes, role-based access control (RBAC), and pod security. The Wazuh Security Configuration Assessment (SCA) module assesses endpoint configurations against security baselines defined in SCA policies and reports configuration issues.
In this blog post, we create a custom Wazuh SCA policy that implements CIS Kubernetes Benchmark v2.0.1. We deploy the policy on a kubeadm-provisioned Kubernetes control-plane node and use Wazuh to assess the node’s configuration against the CIS benchmark.
Infrastructure
We use the following infrastructure to demonstrate this capability:
- A pre-built, ready-to-use Wazuh OVA 4.14.7, which includes the Wazuh central components (Wazuh server, Wazuh indexer, and Wazuh dashboard). Follow this guide to download and set up the Wazuh virtual machine.
- An Ubuntu 24.04 endpoint with:
- The Wazuh agent 4.14.7 installed and enrolled in the Wazuh server.
- A Kubernetes control-plane provisioned with kubeadm.
Automating compliance checks with Wazuh SCA
The Wazuh SCA module assesses endpoint configurations against security requirements defined in SCA policies and reports each check as Passed, Failed, or Not applicable. In this blog post, we create a custom SCA policy that implements 77 requirements from the CIS Kubernetes Benchmark v2.0.1. The policy evaluates Kubernetes components and resources across the following CIS benchmark sections:
| Section | CIS ID | Assessment |
| Control plane node configuration files | 1.1.1 | Ensure that the API server pod specification file permissions are set to 600 or more restrictive. |
| 1.1.2 | Ensure that the API server pod specification file ownership is set to root:root. | |
| 1.1.3 | Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive | |
| 1.1.4 | Ensure that the controller manager pod specification file ownership is set to root:root. | |
| 1.1.5 | Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive. | |
| 1.1.6 | Ensure that the scheduler pod specification file ownership is set to root:root. | |
| 1.1.7 | Ensure that the etcd pod specification file permissions are set to 600 or more restrictive. | |
| 1.1.8 | Ensure that the etcd pod specification file ownership is set to root:root. | |
| 1.1.9 | Ensure that the Container Network Interface file permissions are set to 600 or more restrictive. | |
| 1.1.10 | Ensure that the Container Network Interface file ownership is set to root:root. | |
| 1.1.11 | Ensure that the etcd data directory permissions are set to 700 or more restrictive. | |
| 1.1.12 | Ensure that the etcd data directory ownership is set to etcd:etcd. | |
| 1.1.13 | Ensure that the default administrative credential file permissions are set to 600. | |
| 1.1.14 | Ensure that the default administrative credential file ownership is set to root:root. | |
| 1.1.15 | Ensure that the scheduler.conf file permissions are set to 600 or more restrictive. | |
| 1.1.16 | Ensure that the scheduler.conf file ownership is set to root:root. | |
| 1.1.17 | Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive. | |
| 1.1.18 | Ensure that the controller-manager.conf file ownership is set to root:root. | |
| 1.1.19 | Ensure that the Kubernetes PKI directory and file ownership is set to root:root. | |
| 1.1.20 | Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive. | |
| 1.1.21 | Ensure that the Kubernetes PKI key file permissions are set to 600. | |
| API server | 1.2.2 | Ensure that the --token-auth-file parameter is not set. |
| 1.2.4 | Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate. | |
| 1.2.5 | Ensure that the --kubelet-certificate-authority argument is set as appropriate. | |
| 1.2.6 | Ensure that the --authorization-mode argument is not set to AlwaysAllow. | |
| 1.2.7 | Ensure that the –-authorization-mode argument includes Node. | |
| 1.2.8 | Ensure that the --authorization-mode argument includes RBAC. | |
| 1.2.10 | Ensure that the admission control plugin AlwaysAdmit is not set. | |
| 1.2.12 | Ensure that the admission control plugin ServiceAccount is set. | |
| 1.2.14 | Ensure that the admission control plugin NodeRestriction is set. | |
| 1.2.15 | Ensure that the --profiling argument is set to false. | |
| 1.2.16 | Ensure that the --audit-log-path argument is set. | |
| 1.2.21 | Ensure that the --service-account-lookup argument is set to true. | |
| 1.2.22 | Ensure that the --service-account-key-file argument is set as appropriate. | |
| 1.2.23 | Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate. | |
| 1.2.24 | Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate. | |
| 1.2.25 | Ensure that the --client-ca-file argument is set as appropriate. | |
| 1.2.26 | Ensure that the --etcd-cafile argument is set as appropriate. | |
| 1.2.30 | Ensure that the --service-account-extend-token-expiration parameter is set to false. | |
| Controller manager and scheduler | 1.3.2 | Ensure that the --profiling argument is set to false. |
| 1.3.3 | Ensure that the --use-service-account-credentials argument is set to true. | |
| 1.3.4 | Ensure that the --service-account-private-key-file argument is set as appropriate. | |
| 1.3.5 | Ensure that the --root-ca-file argument is set as appropriate. | |
| 1.3.6 | Ensure that the RotateKubeletServerCertificate argument is set to true. | |
| 1.3.7 | Ensure that the --bind-address argument is set to 127.0.0.1. | |
| Scheduler | 1.4.1 | Ensure that the --profiling argument is set to false. |
| 1.4.2 | Ensure that the --bind-address argument is set to 127.0.0.1. | |
| etcd | 2.1 | Ensure that the --cert-file and --key-file arguments are set as appropriate. |
| 2.2 | Ensure that the --client-cert-auth argument is set to true. | |
| 2.3 | Ensure that the --auto-tls argument is not set to true. | |
| 2.4 | Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate. | |
| 2.5 | Ensure that the --peer-client-cert-auth argument is set to true. | |
| 2.6 | Ensure that the --peer-auto-tls argument is not set to true. | |
| Worker node configuration files | 4.1.1 | Ensure that the kubelet service file permissions are set to 600 or more restrictive. |
| 4.1.2 | Ensure that the kubelet service file ownership is set to root:root. | |
| 4.1.5 | Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive. | |
| 4.1.6 | Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root. | |
| 4.1.9 | If the kubelet config.yaml configuration file is being used, validate permissions are set to 600 or more restrictive. | |
| 4.1.10 | If the kubelet config.yaml configuration file is being used, validate file ownership is set to root:root. | |
| Kubelet | 4.2.1 | Ensure that the --anonymous-auth argument is set to false. |
| 4.2.2 | Ensure that the --authorization-mode argument is not set to AlwaysAllow.. | |
| 4.2.3 | Ensure that the --client-ca-file argument is set as appropriate. | |
| 4.2.6 | Ensure that the --make-iptables-util-chains argument is set to true. | |
| 4.2.10 | Ensure that the --rotate-certificates argument is not set to false. | |
| Kube-proxy | 4.3.1 | Ensure that the kube-proxy metrics service is bound to localhost. |
| RBAC and service accounts | 5.1.7 | Ensure that the group system:masters is not used for the cluster’s default administrative credential. |
| Pod security standards | 5.2.2 | Minimize the admission of privileged containers. |
| 5.2.3 | Minimize the admission of containers wishing to share the host process ID namespace. | |
| 5.2.4 | Minimize the admission of containers wishing to share the host IPC namespace. | |
| 5.2.5 | Minimize the admission of containers wishing to share the host network namespace. | |
| 5.2.6 | Minimize the admission of containers with allowPrivilegeEscalation. | |
| 5.2.7 | Minimize the admission of root containers. | |
| 5.2.8 | Minimize the admission of containers with the NET_RAW capability. | |
| 5.2.11 | Minimize the admission of HostPath volumes. | |
| Network policies and CNI | 5.3.2 | Ensure that all Namespaces have Network Policies defined. |
| Extensible admission control | 5.5.1 | Configure Image Provenance using the ImagePolicyWebhook admission controller. |
| General policies | 5.6.4 | The default namespace should not be used. |
Configuring Wazuh to scan endpoints against the CIS Kubernetes Benchmark
Wazuh SCA policy defines the security configuration checks to run on monitored endpoints. The SCA policy is a YAML file that consists of a policy metadata block, a checks list, and optional requirements and variables blocks. The requirements block defines whether the policy is evaluated, while the variables block stores reusable values referenced by the checks. Each check defines a logical condition (all, any, or none) and one or more rules. On Linux, Wazuh supports four rule types:
- f: for file content matched with a regular expression,
- d: for directory existence,
- p: for process names, and
- c: for command output matched against a pattern.
The following example shows the policy requirements block. It verifies the presence of the Kubernetes API server static pod manifest, which identifies a standard kubeadm control-plane node. If the manifest is absent, Wazuh marks all checks as Not applicable.
requirements:
title: "Verify the Kubernetes API server static pod manifest exists"
description: >
The Kubernetes API server static pod manifest (/etc/kubernetes/manifests/kube-apiserver.yaml)
must exist for this policy to apply. If it is absent, all checks report as Not applicable.
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml'
Configure the monitored endpoints
Perform the following steps on each monitored endpoint to configure the Wazuh SCA module to scan them against the CIS Kubernetes Benchmarks:
- Create a new SCA policy file
/var/ossec/etc/sca_kubernetes_audit.ymland add the following content:
policy:
id: "k8s_cis_hardening"
file: "sca_kubernetes_audit.yml"
name: "CIS Kubernetes Benchmark v2.0.1"
description: >
Checks a kubeadm Kubernetes control-plane node against a subset of the CIS
Kubernetes Benchmark v2.0.1 (Kubernetes v1.34-v1.35), covering control
plane configuration file permissions/ownership, API server, controller
manager, scheduler and etcd process arguments, and kubelet configuration.
Community policy — not officially supported by Wazuh.
references:
- https://www.cisecurity.org/benchmark/kubernetes
- https://documentation.wazuh.com/current/user-manual/capabilities/sec-config-assessment/creating-custom-policies.html
regex_type: pcre2
requirements:
title: "Verify this host is a kubeadm Kubernetes control-plane node"
description: >
The API server static pod manifest (/etc/kubernetes/manifests/kube-apiserver.yaml)
must be present for this policy to apply. On worker-only nodes or hosts not
running Kubernetes, all checks report as Not applicable.
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml'
variables:
$manifests: /etc/kubernetes/manifests
$pki: /etc/kubernetes/pki
checks:
# ---------------------------------------------------------------------------
# Section 1.1 — Control Plane Node Configuration Files
# ---------------------------------------------------------------------------
- id: 92001
title: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive"
description: "Ensure that the API server pod specification file has permissions of 600 or more restrictive."
rationale: >
The API server pod specification file controls various parameters that set the
behavior of the API server. You should restrict its file permissions to
maintain the integrity of the file. The file should be writable by only the
administrators on the system.
remediation: "chmod 600 /etc/kubernetes/manifests/kube-apiserver.yaml"
compliance:
- cis_kubernetes: ["1.1.1"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/manifests/kube-apiserver.yaml -> n:^(\d+) compare <= 600'
- id: 92002
title: "Ensure that the API server pod specification file ownership is set to root:root"
description: "Ensure that the API server pod specification file ownership is set to root:root."
rationale: >
The API server pod specification file controls various parameters that set the
behavior of the API server. You should set its file ownership to maintain the
integrity of the file. The file should be owned by root:root.
remediation: "chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml"
compliance:
- cis_kubernetes: ["1.1.2"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/manifests/kube-apiserver.yaml -> r:^root:root$'
- id: 92003
title: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive"
description: "Ensure that the controller manager pod specification file has permissions of 600 or more restrictive."
rationale: >
The controller manager pod specification file controls various parameters that
set the behavior of the Controller Manager on the control plane node. You
should restrict its file permissions to maintain the integrity of the file.
remediation: "chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml"
compliance:
- cis_kubernetes: ["1.1.3"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml -> n:^(\d+) compare <= 600'
- id: 92004
title: "Ensure that the controller manager pod specification file ownership is set to root:root"
description: "Ensure that the controller manager pod specification file ownership is set to root:root."
rationale: >
The controller manager pod specification file controls various parameters
that set the behavior of the Controller Manager. It should be owned by
root:root to maintain its integrity.
remediation: "chown root:root /etc/kubernetes/manifests/kube-controller-manager.yaml"
compliance:
- cis_kubernetes: ["1.1.4"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/manifests/kube-controller-manager.yaml -> r:^root:root$'
- id: 92005
title: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive"
description: "Ensure that the scheduler pod specification file has permissions of 600 or more restrictive."
rationale: >
The scheduler pod specification file controls various parameters that set
the behavior of the Scheduler on the control plane node. Restrict its file
permissions to maintain the integrity of the file.
remediation: "chmod 600 /etc/kubernetes/manifests/kube-scheduler.yaml"
compliance:
- cis_kubernetes: ["1.1.5"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/manifests/kube-scheduler.yaml -> n:^(\d+) compare <= 600'
- id: 92006
title: "Ensure that the scheduler pod specification file ownership is set to root:root"
description: "Ensure that the scheduler pod specification file ownership is set to root:root."
rationale: >
The scheduler pod specification file controls various parameters that set
the behavior of the Scheduler. It should be owned by root:root to maintain
its integrity.
remediation: "chown root:root /etc/kubernetes/manifests/kube-scheduler.yaml"
compliance:
- cis_kubernetes: ["1.1.6"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/manifests/kube-scheduler.yaml -> r:^root:root$'
- id: 92007
title: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive"
description: "Ensure that the etcd pod specification file has permissions of 600 or more restrictive."
rationale: >
The etcd pod specification file controls various parameters that set the
behavior of etcd. Restrict its file permissions to maintain the integrity
of the file.
remediation: "chmod 600 /etc/kubernetes/manifests/etcd.yaml"
compliance:
- cis_kubernetes: ["1.1.7"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/manifests/etcd.yaml -> n:^(\d+) compare <= 600'
- id: 92008
title: "Ensure that the etcd pod specification file ownership is set to root:root"
description: "Ensure that the etcd pod specification file ownership is set to root:root."
rationale: >
The etcd pod specification file controls various parameters that set the
behavior of etcd. It should be owned by root:root to maintain its
integrity.
remediation: "chown root:root /etc/kubernetes/manifests/etcd.yaml"
compliance:
- cis_kubernetes: ["1.1.8"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/manifests/etcd.yaml -> r:^root:root$'
- id: 92009
title: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive"
description: "Ensure that the Container Network Interface files have permissions of 600 or more restrictive."
rationale: >
Container Network Interface provides various networking options for
overlay networking. Restrict CNI configuration file permissions to
maintain the integrity of those files.
remediation: "chmod 600 /etc/cni/net.d/10-calico.conflist"
compliance:
- cis_kubernetes: ["1.1.9"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/cni/net.d/10-calico.conflist -> n:^(\d+) compare <= 600'
- id: 92010
title: "Ensure that the Container Network Interface file ownership is set to root:root"
description: "Ensure that the Container Network Interface file ownership is set to root:root."
rationale: >
CNI configuration files hold networking parameters for the cluster and
should be owned by root:root to maintain their integrity.
remediation: "chown root:root /etc/cni/net.d/10-calico.conflist"
compliance:
- cis_kubernetes: ["1.1.10"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/cni/net.d/10-calico.conflist -> r:^root:root$'
- id: 92011
title: "Ensure that the etcd data directory permissions are set to 700 or more restrictive"
description: "Ensure that the etcd data directory has permissions of 700 or more restrictive."
rationale: >
etcd is a highly-available key-value store used by Kubernetes for
persistent storage of all its REST API objects. Restrict its data
directory permissions to maintain the integrity of the data.
remediation: "chmod 700 /var/lib/etcd"
compliance:
- cis_kubernetes: ["1.1.11"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /var/lib/etcd -> n:^(\d+) compare <= 700'
- id: 92012
title: "Ensure that the etcd data directory ownership is set to etcd:etcd"
description: "Ensure that the etcd data directory ownership is set to etcd:etcd."
rationale: >
etcd is a highly-available key-value store used by Kubernetes for
persistent storage. It should be owned by etcd:etcd to maintain the
integrity of the data.
remediation: "chown etcd:etcd /var/lib/etcd"
compliance:
- cis_kubernetes: ["1.1.12"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /var/lib/etcd -> r:^etcd:etcd$'
- id: 92013
title: "Ensure that the default administrative credential file permissions are set to 600"
description: "Ensure that the default administrative credential file (admin.conf) has permissions of 600."
rationale: >
The default administrative credential file provides administrative level
access. It should be restricted to only the administrators on the system.
remediation: "chmod 600 /etc/kubernetes/admin.conf"
compliance:
- cis_kubernetes: ["1.1.13"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/admin.conf -> n:^(\d+) compare <= 600'
- id: 92014
title: "Ensure that the default administrative credential file ownership is set to root:root"
description: "Ensure that the default administrative credential file (admin.conf) ownership is set to root:root."
rationale: >
The default administrative credential file provides administrative level
access and should be owned by root:root to maintain its integrity.
remediation: "chown root:root /etc/kubernetes/admin.conf"
compliance:
- cis_kubernetes: ["1.1.14"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/admin.conf -> r:^root:root$'
- id: 92015
title: "Ensure that the scheduler.conf file permissions are set to 600 or more restrictive"
description: "Ensure that the scheduler.conf file has permissions of 600 or more restrictive."
rationale: >
scheduler.conf holds credentials the scheduler uses to authenticate to
the API server and should be restricted to maintain its integrity.
remediation: "chmod 600 /etc/kubernetes/scheduler.conf"
compliance:
- cis_kubernetes: ["1.1.15"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/scheduler.conf -> n:^(\d+) compare <= 600'
- id: 92016
title: "Ensure that the scheduler.conf file ownership is set to root:root"
description: "Ensure that the scheduler.conf file ownership is set to root:root."
rationale: >
scheduler.conf holds credentials the scheduler uses to authenticate to
the API server and should be owned by root:root to maintain its
integrity.
remediation: "chown root:root /etc/kubernetes/scheduler.conf"
compliance:
- cis_kubernetes: ["1.1.16"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/scheduler.conf -> r:^root:root$'
- id: 92017
title: "Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive"
description: "Ensure that the controller-manager.conf file has permissions of 600 or more restrictive."
rationale: >
controller-manager.conf holds credentials the controller manager uses to
authenticate to the API server and should be restricted to maintain its
integrity.
remediation: "chmod 600 /etc/kubernetes/controller-manager.conf"
compliance:
- cis_kubernetes: ["1.1.17"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/controller-manager.conf -> n:^(\d+) compare <= 600'
- id: 92018
title: "Ensure that the controller-manager.conf file ownership is set to root:root"
description: "Ensure that the controller-manager.conf file ownership is set to root:root."
rationale: >
controller-manager.conf holds credentials the controller manager uses to
authenticate to the API server and should be owned by root:root to
maintain its integrity.
remediation: "chown root:root /etc/kubernetes/controller-manager.conf"
compliance:
- cis_kubernetes: ["1.1.18"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/controller-manager.conf -> r:^root:root$'
- id: 92019
title: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root"
description: "Ensure that the Kubernetes PKI directory and file ownership is set to root:root."
rationale: >
Kubernetes makes use of a number of certificates as part of its
operation. The ownership of the directory containing the PKI information
and all files in that directory should be root:root to maintain their
integrity.
remediation: "chown -R root:root /etc/kubernetes/pki/"
compliance:
- cis_kubernetes: ["1.1.19"]
condition: all
rules:
- 'c:timeout 10 sh -c "find /etc/kubernetes/pki -not -user root | grep -q . && echo bad || echo ok" -> r:^ok$'
- 'c:timeout 10 sh -c "find /etc/kubernetes/pki -not -group root | grep -q . && echo bad || echo ok" -> r:^ok$'
- id: 92020
title: "Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive"
description: "Ensure that Kubernetes PKI certificate files have permissions of 644 or more restrictive."
rationale: >
Kubernetes makes use of a number of certificate files as part of the
operation of its components. Their permissions should be 644 or more
restrictive to protect their integrity and confidentiality.
remediation: "chmod -R 644 /etc/kubernetes/pki/*.crt"
compliance:
- cis_kubernetes: ["1.1.20"]
condition: all
rules:
- 'c:timeout 10 sh -c "find /etc/kubernetes/pki -name *.crt | xargs stat -c %a | sort -rn | head -1" -> n:^(\d+) compare <= 644'
- id: 92021
title: "Ensure that the Kubernetes PKI key file permissions are set to 600"
description: "Ensure that Kubernetes PKI key files have permissions of 600."
rationale: >
Kubernetes makes use of a number of key files as part of the operation of
its components. Their permissions should be set to 600 to protect their
integrity and confidentiality.
remediation: "chmod -R 600 /etc/kubernetes/pki/*.key"
compliance:
- cis_kubernetes: ["1.1.21"]
condition: all
rules:
- 'c:timeout 10 sh -c "find /etc/kubernetes/pki -name *.key | xargs stat -c %a | sort -rn | head -1" -> n:^(\d+) compare <= 600'
# ---------------------------------------------------------------------------
# Section 1.2 — API Server
# ---------------------------------------------------------------------------
- id: 92022
title: "Ensure that the --token-auth-file parameter is not set"
description: "Do not use token based authentication."
rationale: >
Static token-based authentication tokens are stored in clear-text on the
apiserver and cannot be revoked or rotated without restarting the
apiserver. Do not use this authentication method.
remediation: >
Follow the documentation and configure alternate mechanisms for
authentication, then remove --token-auth-file=<filename> from
/etc/kubernetes/manifests/kube-apiserver.yaml.
compliance:
- cis_kubernetes: ["1.2.2"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--token-auth-file'
- id: 92023
title: "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate"
description: "Enable certificate based kubelet authentication."
rationale: >
The apiserver, by default, does not authenticate itself to the kubelet's
HTTPS endpoints. Client certificate authentication should be used to
authenticate the apiserver to the kubelet.
remediation: >
Set --kubelet-client-certificate and --kubelet-client-key in
/etc/kubernetes/manifests/kube-apiserver.yaml.
compliance:
- cis_kubernetes: ["1.2.4"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--kubelet-client-certificate='
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--kubelet-client-key='
- id: 92024
title: "Ensure that the --kubelet-certificate-authority argument is set as appropriate"
description: "Verify kubelet's certificate before establishing connection."
rationale: >
By default the apiserver does not verify the kubelet's serving
certificate, which makes the connection subject to man-in-the-middle
attacks over untrusted networks.
remediation: >
Set --kubelet-certificate-authority=<ca-string> in
/etc/kubernetes/manifests/kube-apiserver.yaml.
compliance:
- cis_kubernetes: ["1.2.5"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--kubelet-certificate-authority='
- id: 92025
title: "Ensure that the --authorization-mode argument is not set to AlwaysAllow"
description: "Do not always authorize all requests."
rationale: "The API Server can be configured to allow all requests; this should not be used on any production cluster."
remediation: "Set --authorization-mode to values other than AlwaysAllow."
compliance:
- cis_kubernetes: ["1.2.6"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--authorization-mode=.*AlwaysAllow'
- id: 92026
title: "Ensure that the --authorization-mode argument includes Node"
description: "Restrict kubelet nodes to reading only objects associated with them."
rationale: >
Node authorization mode only allows kubelets to read Secret, ConfigMap,
PersistentVolume, and PersistentVolumeClaim objects associated with
their own node.
remediation: "Set --authorization-mode to a value that includes Node."
compliance:
- cis_kubernetes: ["1.2.7"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--authorization-mode=.*Node'
- id: 92027
title: "Ensure that the --authorization-mode argument includes RBAC"
description: "Turn on Role Based Access Control."
rationale: >
RBAC allows fine-grained control over the operations different entities
can perform on cluster resources.
remediation: "Set --authorization-mode to a value that includes RBAC."
compliance:
- cis_kubernetes: ["1.2.8"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--authorization-mode=.*RBAC'
- id: 92028
title: "Ensure that the admission control plugin AlwaysAdmit is not set"
description: "Do not allow all requests through the AlwaysAdmit admission controller."
rationale: "Setting admission control plugin AlwaysAdmit allows all requests and does not filter any requests."
remediation: "Remove AlwaysAdmit from --enable-admission-plugins, or set --enable-admission-plugins without AlwaysAdmit."
compliance:
- cis_kubernetes: ["1.2.10"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--enable-admission-plugins=.*AlwaysAdmit'
- id: 92029
title: "Ensure that the admission control plugin ServiceAccount is set"
description: "Automate service accounts management."
rationale: >
When you create a pod without specifying a service account, it is
automatically assigned the default service account. The ServiceAccount
admission controller should not be disabled.
remediation: "Ensure --disable-admission-plugins does not include ServiceAccount."
compliance:
- cis_kubernetes: ["1.2.12"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--disable-admission-plugins=.*ServiceAccount'
- id: 92030
title: "Ensure that the admission control plugin NodeRestriction is set"
description: "Limit the Node and Pod objects a kubelet can modify."
rationale: >
NodeRestriction limits the Node and Pod objects a kubelet can modify to
those associated with its own node.
remediation: "Set --enable-admission-plugins to include NodeRestriction."
compliance:
- cis_kubernetes: ["1.2.14"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--enable-admission-plugins=.*NodeRestriction'
- id: 92031
title: "Ensure that the --profiling argument is set to false (kube-apiserver)"
description: "Disable profiling, if not needed."
rationale: >
Profiling generates a significant amount of program data that could
potentially be exploited to uncover system and program details.
remediation: "Set --profiling=false in /etc/kubernetes/manifests/kube-apiserver.yaml."
compliance:
- cis_kubernetes: ["1.2.15"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--profiling=false'
- id: 92032
title: "Ensure that the --audit-log-path argument is set"
description: "Enable auditing on the Kubernetes API Server and set the desired audit log path."
rationale: >
Auditing provides a security-relevant chronological record of the
sequence of activities that have affected the system.
remediation: >
Set --audit-log-path=/var/log/apiserver/audit.log in
/etc/kubernetes/manifests/kube-apiserver.yaml.
compliance:
- cis_kubernetes: ["1.2.16"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--audit-log-path='
- id: 92033
title: "Ensure that the --service-account-lookup argument is set to true"
description: "Validate service account before validating token."
rationale: >
Without --service-account-lookup, the apiserver only verifies that the
authentication token is valid, without checking that the referenced
service account still exists.
remediation: "Set --service-account-lookup=true, or omit the flag so the default (true) applies."
compliance:
- cis_kubernetes: ["1.2.21"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--service-account-lookup=false'
- id: 92034
title: "Ensure that the --service-account-key-file argument is set as appropriate"
description: "Explicitly set a service account public key file for service accounts on the apiserver."
rationale: >
By default, if --service-account-key-file is not specified, the
apiserver uses the private key from the TLS serving certificate,
preventing key rotation independent of the TLS certificate.
remediation: "Set --service-account-key-file to the public key file for service account tokens."
compliance:
- cis_kubernetes: ["1.2.22"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--service-account-key-file='
- id: 92035
title: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate"
description: "etcd should be configured to make use of TLS encryption for its client connections."
rationale: >
etcd stores all cluster state as its source of truth and should be
properly secured, with client connections making use of TLS encryption.
remediation: "Set --etcd-certfile and --etcd-keyfile as appropriate."
compliance:
- cis_kubernetes: ["1.2.23"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--etcd-certfile='
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--etcd-keyfile='
- id: 92036
title: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate"
description: "Setup TLS connection on the API server."
rationale: >
API server communication contains sensitive parameters that should
remain encrypted in transit.
remediation: "Set --tls-cert-file and --tls-private-key-file as appropriate."
compliance:
- cis_kubernetes: ["1.2.24"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--tls-cert-file='
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--tls-private-key-file='
- id: 92037
title: "Ensure that the --client-ca-file argument is set as appropriate"
description: "Setup TLS connection on the API server."
rationale: "Client certificate authentication is used to authenticate clients to the API server."
remediation: "Set --client-ca-file as appropriate."
compliance:
- cis_kubernetes: ["1.2.25"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--client-ca-file='
- id: 92038
title: "Ensure that the --etcd-cafile argument is set as appropriate"
description: "etcd should be configured to make use of TLS encryption for its client connections."
rationale: "The API server communicates with etcd and should authenticate the etcd CA."
remediation: "Set --etcd-cafile as appropriate."
compliance:
- cis_kubernetes: ["1.2.26"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--etcd-cafile='
- id: 92039
title: "Ensure that the --service-account-extend-token-expiration parameter is set to false"
description: "By default Kubernetes extends service account token lifetimes to one year."
rationale: >
Extended token lifetimes mean a lost or stolen credential could be valid
for an extended period of time.
remediation: "Set --service-account-extend-token-expiration=false."
compliance:
- cis_kubernetes: ["1.2.30"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--service-account-extend-token-expiration=false'
# ---------------------------------------------------------------------------
# Section 1.3 — Controller Manager
# ---------------------------------------------------------------------------
- id: 92040
title: "Ensure that the --profiling argument is set to false (kube-controller-manager)"
description: "Disable profiling, if not needed."
rationale: "Profiling can expose system and program details that increase attack surface."
remediation: "Set --profiling=false in /etc/kubernetes/manifests/kube-controller-manager.yaml."
compliance:
- cis_kubernetes: ["1.3.2"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-controller-manager.yaml -> r:--profiling=false'
- id: 92041
title: "Ensure that the --use-service-account-credentials argument is set to true"
description: "Use individual service account credentials for each controller."
rationale: >
Controller manager creates a service account per controller when this is
enabled, following the principle of least privilege.
remediation: "Set --use-service-account-credentials=true."
compliance:
- cis_kubernetes: ["1.3.3"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-controller-manager.yaml -> r:--use-service-account-credentials=true'
- id: 92042
title: "Ensure that the --service-account-private-key-file argument is set as appropriate"
description: "Explicitly set a service account private key file for service accounts on the controller manager."
rationale: "The controller manager signs service account tokens and should use a dedicated private key."
remediation: "Set --service-account-private-key-file as appropriate."
compliance:
- cis_kubernetes: ["1.3.4"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-controller-manager.yaml -> r:--service-account-private-key-file='
- id: 92043
title: "Ensure that the --root-ca-file argument is set as appropriate"
description: "Allow pods to verify the API server's serving certificate before establishing connections."
rationale: >
Root CA certificates injected into pods enable pods to verify the API
server's identity.
remediation: "Set --root-ca-file as appropriate."
compliance:
- cis_kubernetes: ["1.3.5"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-controller-manager.yaml -> r:--root-ca-file='
- id: 92044
title: "Ensure that the RotateKubeletServerCertificate argument is set to true"
description: "Enable kubelet server certificate rotation on controller-manager."
rationale: >
RotateKubeletServerCertificate causes the kubelet to rotate its serving
certificate as its existing credentials expire, avoiding downtime due to
expired certificates.
remediation: "Set --feature-gates=RotateKubeletServerCertificate=true, or leave the feature gate unset so the default (true) applies."
compliance:
- cis_kubernetes: ["1.3.6"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/kube-controller-manager.yaml -> r:RotateKubeletServerCertificate=false'
- id: 92045
title: "Ensure that the --bind-address argument is set to 127.0.0.1 (kube-controller-manager)"
description: "Do not bind the controller manager service to non-loopback insecure addresses."
rationale: >
Binding to a non-loopback address exposes an unauthenticated HTTP
endpoint, allowing unauthorized access to controller-manager metrics and
other information.
remediation: "Set --bind-address=127.0.0.1."
compliance:
- cis_kubernetes: ["1.3.7"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-controller-manager.yaml -> r:--bind-address=127\.0\.0\.1'
# ---------------------------------------------------------------------------
# Section 1.4 — Scheduler
# ---------------------------------------------------------------------------
- id: 92046
title: "Ensure that the --profiling argument is set to false (kube-scheduler)"
description: "Disable profiling, if not needed."
rationale: "Profiling can expose system and program details that increase attack surface."
remediation: "Set --profiling=false in /etc/kubernetes/manifests/kube-scheduler.yaml."
compliance:
- cis_kubernetes: ["1.4.1"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-scheduler.yaml -> r:--profiling=false'
- id: 92047
title: "Ensure that the --bind-address argument is set to 127.0.0.1 (kube-scheduler)"
description: "Do not bind the scheduler service to non-loopback insecure addresses."
rationale: >
Binding to a non-loopback address exposes an unauthenticated HTTP
endpoint, allowing unauthorized access to scheduler information.
remediation: "Set --bind-address=127.0.0.1."
compliance:
- cis_kubernetes: ["1.4.2"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-scheduler.yaml -> r:--bind-address=127\.0\.0\.1'
# ---------------------------------------------------------------------------
# Section 2 — etcd
# ---------------------------------------------------------------------------
- id: 92048
title: "Ensure that the --cert-file and --key-file arguments are set as appropriate"
description: "Configure TLS encryption for the etcd service."
rationale: "etcd should be configured to make use of TLS encryption for its client connections."
remediation: "Set --cert-file and --key-file as appropriate."
compliance:
- cis_kubernetes: ["2.1"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--cert-file='
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--key-file='
- id: 92049
title: "Ensure that the --client-cert-auth argument is set to true"
description: "Enable client authentication on etcd service."
rationale: >
etcd should be configured for peer and client authentication to protect
the confidentiality and integrity of cluster state.
remediation: "Set --client-cert-auth=true."
compliance:
- cis_kubernetes: ["2.2"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--client-cert-auth=true'
- id: 92050
title: "Ensure that the --auto-tls argument is not set to true"
description: "Do not use self-signed certificates for TLS."
rationale: >
Self-signed certificates generated with --auto-tls bypass the CA-signed
certificate chain and are not appropriate for client connections in
production.
remediation: "Do not set --auto-tls=true; configure --cert-file and --key-file explicitly."
compliance:
- cis_kubernetes: ["2.3"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--auto-tls=true'
- id: 92051
title: "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate"
description: "etcd should be configured to make use of TLS encryption for peer connections."
rationale: "etcd's peer connections carry cluster state and should be encrypted and authenticated."
remediation: "Set --peer-cert-file and --peer-key-file as appropriate."
compliance:
- cis_kubernetes: ["2.4"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--peer-cert-file='
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--peer-key-file='
- id: 92052
title: "Ensure that the --peer-client-cert-auth argument is set to true"
description: "Enable peer client authentication on etcd service."
rationale: >
etcd should be configured for peer authentication to protect the
confidentiality and integrity of cluster state exchanged between etcd
members.
remediation: "Set --peer-client-cert-auth=true."
compliance:
- cis_kubernetes: ["2.5"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--peer-client-cert-auth=true'
- id: 92053
title: "Ensure that the --peer-auto-tls argument is not set to true"
description: "Do not use automatically generated self-signed certificates for TLS connections between peers."
rationale: >
Self-signed certificates generated with --peer-auto-tls bypass the
CA-signed certificate chain and are not appropriate for peer connections
in production.
remediation: "Do not set --peer-auto-tls=true; configure --peer-cert-file and --peer-key-file explicitly."
compliance:
- cis_kubernetes: ["2.6"]
condition: none
rules:
- 'f:/etc/kubernetes/manifests/etcd.yaml -> r:--peer-auto-tls=true'
# ---------------------------------------------------------------------------
# Section 4.1 — Kubelet configuration files
# ---------------------------------------------------------------------------
- id: 92054
title: "Ensure that the kubelet service file permissions are set to 600 or more restrictive"
description: "Ensure that the kubelet service file has permissions of 600 or more restrictive."
rationale: >
The kubelet service file controls various parameters that set the
behavior of the kubelet. Restrict its file permissions to maintain the
integrity of the file.
remediation: "chmod 600 /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf"
compliance:
- cis_kubernetes: ["4.1.1"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf -> n:^(\d+) compare <= 600'
- id: 92055
title: "Ensure that the kubelet service file ownership is set to root:root"
description: "Ensure that the kubelet service file ownership is set to root:root."
rationale: >
The kubelet service file controls various parameters that set the
behavior of the kubelet and should be owned by root:root to maintain its
integrity.
remediation: "chown root:root /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf"
compliance:
- cis_kubernetes: ["4.1.2"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf -> r:^root:root$'
- id: 92056
title: "Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive"
description: "Ensure that the kubelet.conf file has permissions of 600 or more restrictive."
rationale: >
kubelet.conf holds credentials the kubelet uses to authenticate to the
API server and should be restricted to maintain its integrity.
remediation: "chmod 600 /etc/kubernetes/kubelet.conf"
compliance:
- cis_kubernetes: ["4.1.5"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /etc/kubernetes/kubelet.conf -> n:^(\d+) compare <= 600'
- id: 92057
title: "Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root"
description: "Ensure that the kubelet.conf file ownership is set to root:root."
rationale: >
kubelet.conf holds credentials the kubelet uses to authenticate to the
API server and should be owned by root:root to maintain its integrity.
remediation: "chown root:root /etc/kubernetes/kubelet.conf"
compliance:
- cis_kubernetes: ["4.1.6"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /etc/kubernetes/kubelet.conf -> r:^root:root$'
- id: 92058
title: "If the kubelet config.yaml configuration file is being used validate permissions set to 600 or more restrictive"
description: "Ensure that the kubelet config.yaml file has permissions of 600 or more restrictive."
rationale: >
The kubelet config.yaml file controls various parameters for the
kubelet. Restrict its file permissions to maintain the integrity of the
file.
remediation: "chmod 600 /var/lib/kubelet/config.yaml"
compliance:
- cis_kubernetes: ["4.1.9"]
condition: all
rules:
- 'c:timeout 5 stat -c %a /var/lib/kubelet/config.yaml -> n:^(\d+) compare <= 600'
- id: 92059
title: "If the kubelet config.yaml configuration file is being used validate file ownership is set to root:root"
description: "Ensure that the kubelet config.yaml file ownership is set to root:root."
rationale: >
The kubelet config.yaml file controls various parameters for the
kubelet and should be owned by root:root to maintain its integrity.
remediation: "chown root:root /var/lib/kubelet/config.yaml"
compliance:
- cis_kubernetes: ["4.1.10"]
condition: all
rules:
- 'c:timeout 5 stat -c %U:%G /var/lib/kubelet/config.yaml -> r:^root:root$'
# ---------------------------------------------------------------------------
# Section 4.2 — Kubelet
# ---------------------------------------------------------------------------
- id: 92060
title: "Ensure that the --anonymous-auth argument is set to false (kubelet)"
description: "Disable anonymous requests to the kubelet server."
rationale: >
Anonymous requests, if unauthenticated, are treated as anonymous
requests and provide the potential to bypass any configured
authentication.
remediation: "Set authentication.anonymous.enabled to false in /var/lib/kubelet/config.yaml."
compliance:
- cis_kubernetes: ["4.2.1"]
condition: all
rules:
- 'c:timeout 5 sh -c "grep -A2 anonymous: /var/lib/kubelet/config.yaml" -> r:enabled:\s*false'
- id: 92061
title: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (kubelet)"
description: "Do not allow all requests. Enable explicit authorization."
rationale: >
Kubelets, by default, allow all authenticated requests without further
authorization checks unless AlwaysAllow is disabled.
remediation: "Set authorization.mode to Webhook in /var/lib/kubelet/config.yaml."
compliance:
- cis_kubernetes: ["4.2.2"]
condition: none
rules:
- 'c:timeout 5 sh -c "grep -A1 ^authorization: /var/lib/kubelet/config.yaml" -> r:mode:\s*AlwaysAllow'
- id: 92062
title: "Ensure that the --client-ca-file argument is set as appropriate (kubelet)"
description: "Enable Kubelet authentication using certificates."
rationale: "Client certificate authentication should be used to authenticate clients to the kubelet's HTTPS endpoint."
remediation: "Set authentication.x509.clientCAFile in /var/lib/kubelet/config.yaml."
compliance:
- cis_kubernetes: ["4.2.3"]
condition: all
rules:
- 'f:/var/lib/kubelet/config.yaml -> r:clientCAFile:\s*\S+'
- id: 92063
title: "Ensure that the --make-iptables-util-chains argument is set to true"
description: "Allow Kubelet to manage iptables."
rationale: >
Kubelets can automatically manage the required changes to iptables
based on the networking configuration of the pods, keeping the ruleset
in sync with pod networking.
remediation: "Do not set makeIPTablesUtilChains to false in /var/lib/kubelet/config.yaml."
compliance:
- cis_kubernetes: ["4.2.6"]
condition: none
rules:
- 'f:/var/lib/kubelet/config.yaml -> r:makeIPTablesUtilChains:\s*false'
- id: 92064
title: "Ensure that the --rotate-certificates argument is not set to false"
description: "Enable kubelet client certificate rotation."
rationale: >
The kubelet can automatically request rotated certificates as its
existing credentials near expiration.
remediation: "Do not set rotateCertificates to false in /var/lib/kubelet/config.yaml."
compliance:
- cis_kubernetes: ["4.2.10"]
condition: none
rules:
- 'f:/var/lib/kubelet/config.yaml -> r:rotateCertificates:\s*false'
- id: 92065
title: "Minimize the admission of privileged containers"
description: "Do not generally permit containers to be run with the securityContext.privileged flag set to true."
rationale: >
Privileged containers have access to all Linux Kernel capabilities and
devices. A container running with full privileges can do almost
everything that the host can do. There should be at least one admission
control policy defined which does not permit privileged containers.
remediation: >
Add policies to each namespace in the cluster which has user workloads
to restrict the admission of privileged containers.
compliance:
- cis_kubernetes: ["5.2.2"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.containers[*].securityContext.privileged} -> r:true'
- id: 92066
title: "Minimize the admission of containers with allowPrivilegeEscalation"
description: "Do not generally permit containers to be run with the allowPrivilegeEscalation flag set to true."
rationale: >
A container running with the allowPrivilegeEscalation flag set to true
may have processes that can gain more privileges than their parent.
There should be at least one admission control policy defined which does
not permit containers to allow privilege escalation.
remediation: >
Add policies to each namespace in the cluster which has user workloads
to restrict the admission of containers with
securityContext.allowPrivilegeEscalation: true.
compliance:
- cis_kubernetes: ["5.2.6"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.containers[*].securityContext.allowPrivilegeEscalation} -> r:true'
- id: 92067
title: "Minimize the admission of root containers"
description: "Do not generally permit containers to be run as the root user."
rationale: >
Containers which run as the root user, whilst constrained by Container
Runtime security features, still have an escalated likelihood of
container breakout. Ideally, all containers should run as a defined
non-UID 0 user.
remediation: >
Create a policy for each namespace in the cluster ensuring that either
MustRunAsNonRoot or MustRunAs with a UID range not including 0 is set.
compliance:
- cis_kubernetes: ["5.2.7"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.containers[*].securityContext.runAsUser} -> r:\b0\b'
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.containers[*].securityContext.runAsNonRoot} -> r:false'
- id: 92068
title: "Minimize the admission of HostPath volumes"
description: "Do not generally admit containers which make use of hostPath volumes."
rationale: >
A container which mounts a hostPath volume as part of its specification
will have access to the filesystem of the underlying cluster node. The
use of hostPath volumes may allow containers access to privileged areas
of the node filesystem.
remediation: >
Add policies to each namespace in the cluster which has user workloads
to restrict the admission of containers which use hostPath volumes.
compliance:
- cis_kubernetes: ["5.2.11"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.volumes[*].hostPath.path} -> r:/\w'
- id: 92069
title: "Ensure that the kube-proxy metrics service is bound to localhost"
description: "Do not bind the kube-proxy metrics port to non-loopback addresses."
rationale: >
The kube-proxy metrics API exposes endpoints (/metrics and /configz)
which disclose information about the configuration and operation of
kube-proxy. These endpoints should not be exposed to untrusted networks
as they do not support encryption or authentication to restrict access
to the data they provide.
remediation: >
Modify or remove any values which bind the metrics service to a
non-localhost address in the kube-proxy ConfigMap.
compliance:
- cis_kubernetes: ["4.3.1"]
condition: none
rules:
- 'c:timeout 20 sh -c "kubectl --kubeconfig=/etc/kubernetes/admin.conf get cm kube-proxy -n kube-system -o jsonpath=\"{.data.config\.conf}\" 2>/dev/null | grep -oP \"metricsBindAddress:\s*\K.*\"" -> r:"(?!127\.0\.0\.1)[^"]+"'
- id: 92070
title: "Ensure that the group system:masters is not used for the cluster's default administrative credential"
description: >
The special group system:masters should not be used to grant permissions
to any user or service account, except where strictly necessary (e.g.
bootstrapping access prior to RBAC being fully available).
rationale: >
The system:masters group has unrestricted access to the Kubernetes API
hard-coded into the API server source code. An authenticated user who is
a member of this group cannot have their access reduced, even if all
bindings and cluster role bindings which mention it are removed.
remediation: >
Remove the system:masters group from the credential distributed for
day-to-day cluster administration.
compliance:
- cis_kubernetes: ["5.1.7"]
condition: none
rules:
- 'c:timeout 5 sh -c "grep -oP \"client-certificate-data:\s*\K\S+\" /etc/kubernetes/admin.conf | base64 -d | openssl x509 -noout -subject" -> r:system:masters'
- id: 92071
title: "Minimize the admission of containers wishing to share the host process ID namespace"
description: "Do not generally permit containers to be run with the hostPID flag set to true."
rationale: >
A container running in the host's PID namespace can inspect processes
running outside the container. If the container also has access to
ptrace capabilities this can be used to escalate privileges outside of
the container. There should be at least one admission control policy
defined which does not permit containers to share the host PID
namespace.
remediation: >
Configure the Admission Controller to restrict the admission of hostPID
containers.
compliance:
- cis_kubernetes: ["5.2.3"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.hostPID} -> r:true'
- id: 92072
title: "Minimize the admission of containers wishing to share the host IPC namespace"
description: "Do not generally permit containers to be run with the hostIPC flag set to true."
rationale: >
A container running in the host's IPC namespace can use IPC to interact
with processes outside the container. There should be at least one
admission control policy defined which does not permit containers to
share the host IPC namespace.
remediation: >
Add policies to each namespace in the cluster which has user workloads
to restrict the admission of hostIPC containers.
compliance:
- cis_kubernetes: ["5.2.4"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.hostIPC} -> r:true'
- id: 92073
title: "Minimize the admission of containers wishing to share the host network namespace"
description: "Do not generally permit containers to be run with the hostNetwork flag set to true."
rationale: >
A container running in the host's network namespace could access the
local loopback device, and could access network traffic to and from
other pods. There should be at least one admission control policy
defined which does not permit containers to share the host network
namespace.
remediation: >
Add policies to each namespace in the cluster which has user workloads
to restrict the admission of hostNetwork containers.
compliance:
- cis_kubernetes: ["5.2.5"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.hostNetwork} -> r:true'
- id: 92074
title: "Minimize the admission of containers with the NET_RAW capability"
description: "Do not generally permit containers with the potentially dangerous NET_RAW capability."
rationale: >
Containers run with a default set of capabilities as assigned by the
Container Runtime. By default this can include potentially dangerous
capabilities. Ideally, all containers should drop this capability.
There should be at least one admission control policy defined which
does not permit containers with the NET_RAW capability.
remediation: >
Add policies to each namespace in the cluster which has user workloads
to restrict the admission of containers with the NET_RAW capability.
compliance:
- cis_kubernetes: ["5.2.8"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A -o jsonpath={.items[*].spec.containers[*].securityContext.capabilities.add} -> r:NET_RAW'
- id: 92075
title: "Ensure that all Namespaces have Network Policies defined"
description: "Use network policies to isolate traffic in your cluster network."
rationale: >
Running different applications on the same Kubernetes cluster creates a
risk of one compromised application attacking a neighboring
application. A network policy is a specification of how selections of
pods are allowed to communicate with each other and other network
endpoints. If there are no network policies in a namespace all traffic
will be allowed into and out of the pods in that namespace.
remediation: "Follow the documentation and create NetworkPolicy objects as you need them."
compliance:
- cis_kubernetes: ["5.3.2"]
condition: none
rules:
- 'c:timeout 20 sh -c "kubectl --kubeconfig=/etc/kubernetes/admin.conf get ns -o jsonpath={.items[*].metadata.name} | tr \" \" \"\n\" | sort -u > /tmp/sca_5_3_2_ns && kubectl --kubeconfig=/etc/kubernetes/admin.conf get networkpolicy -A -o jsonpath={.items[*].metadata.namespace} | tr \" \" \"\n\" | sort -u > /tmp/sca_5_3_2_np && grep -vxFf /tmp/sca_5_3_2_np /tmp/sca_5_3_2_ns | grep -q . && echo has-ns-without-policy || echo all-covered" -> r:has-ns-without-policy'
- id: 92076
title: "Configure Image Provenance using ImagePolicyWebhook admission controller"
description: "Configure Image Provenance for your deployment."
rationale: >
Kubernetes supports plugging in provenance rules to accept or reject
the images in your deployments. You could configure such rules to
ensure that only approved images are deployed in the cluster.
remediation: "Follow the Kubernetes documentation and setup image provenance."
compliance:
- cis_kubernetes: ["5.5.1"]
condition: all
rules:
- 'f:/etc/kubernetes/manifests/kube-apiserver.yaml -> r:--enable-admission-plugins=(\S*,)?ImagePolicyWebhook'
- id: 92077
title: "The default namespace should not be used"
description: >
Kubernetes provides a default namespace, where objects are placed if no
namespace is specified for them. Placing objects in this namespace
makes application of RBAC and other controls more difficult.
rationale: >
Resources in a Kubernetes cluster should be segregated by namespace, to
allow for security controls to be applied at that level and to make it
easier to manage resources.
remediation: >
Ensure that namespaces are created to allow for appropriate segregation
of Kubernetes resources and that all new resources are created in a
specific namespace.
compliance:
- cis_kubernetes: ["5.6.4"]
condition: none
rules:
- 'c:timeout 20 kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -n default -o jsonpath={.items[*].metadata.name} -> r:\S'
- Change the ownership and permission of the file so that the
rootuser and thewazuhgroup have access to it:
# chown root:wazuh /var/ossec/etc/sca_kubernetes_audit.yml # chmod 640 /var/ossec/etc/sca_kubernetes_audit.yml
- Enable the policy by appending the following configuration to the
/var/ossec/etc/ossec.conffile on the monitored endpoint:
<ossec_config>
<sca>
<enabled>yes</enabled>
<scan_on_start>yes</scan_on_start>
<interval>24h</interval>
<skip_nfs>yes</skip_nfs>
<policies>
<policy>/var/ossec/etc/sca_kubernetes_audit.yml</policy>
</policies>
</sca>
</ossec_config>
- Restart the Wazuh agent to apply the changes:
# systemctl restart wazuh-agent
Visualizing the scan results
Navigate to the Endpoint security > Configuration assessment page on the Wazuh dashboard. Select the monitored Kubernetes endpoint, and then select CIS Kubernetes Benchmark v2.0.1 to view the scan results.

Conclusion
This blog post demonstrates how to build a custom Wazuh SCA policy that assesses a Kubernetes control-plane node against the CIS Kubernetes Benchmark v2.0.1. The custom policy evaluates control-plane configuration files, component arguments, kubelet settings, and Kubernetes resources queried through the Kubernetes API.
Running the policy against a kubeadm v1.35.7 cluster identifies 14 failed checks that require further review and remediation. By periodically assessing Kubernetes configurations with Wazuh SCA, security teams can identify configuration changes and insecure settings and maintain visibility into their alignment with the CIS Kubernetes Benchmark.
Wazuh is a free and open source security platform that provides capabilities for threat detection, incident response, vulnerability management, compliance monitoring, and endpoint security. If you have questions about this blog post or Wazuh, join our community. Our team actively engages there and is ready to help.