Scanning Kubernetes infrastructure against CIS Benchmark with Wazuh

| by | Wazuh 4.14.7
Post icon

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:

SectionCIS IDAssessment
Control plane node configuration files1.1.1Ensure that the API server pod specification file permissions are set to 600 or more restrictive.
1.1.2Ensure that the API server pod specification file ownership is set to root:root.
1.1.3Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive
1.1.4Ensure that the controller manager pod specification file ownership is set to root:root.
1.1.5Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive.
1.1.6Ensure that the scheduler pod specification file ownership is set to root:root.
1.1.7Ensure that the etcd pod specification file permissions are set to 600 or more restrictive.
1.1.8Ensure that the etcd pod specification file ownership is set to root:root.
1.1.9Ensure that the Container Network Interface file permissions are set to 600 or more restrictive.
1.1.10Ensure that the Container Network Interface file ownership is set to root:root.
1.1.11Ensure that the etcd data directory permissions are set to 700 or more restrictive.
1.1.12Ensure that the etcd data directory ownership is set to etcd:etcd.
1.1.13Ensure that the default administrative credential file permissions are set to 600.
1.1.14Ensure that the default administrative credential file ownership is set to root:root.
1.1.15Ensure that the scheduler.conf file permissions are set to 600 or more restrictive.
1.1.16Ensure that the scheduler.conf file ownership is set to root:root.
1.1.17Ensure that the controller-manager.conf file permissions are set to 600 or more restrictive.
1.1.18Ensure that the controller-manager.conf file ownership is set to root:root.
1.1.19Ensure that the Kubernetes PKI directory and file ownership is set to root:root.
1.1.20Ensure that the Kubernetes PKI certificate file permissions are set to 644 or more restrictive.
1.1.21Ensure that the Kubernetes PKI key file permissions are set to 600.
API server1.2.2Ensure that the --token-auth-file parameter is not set. 
1.2.4Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate.
1.2.5Ensure that the --kubelet-certificate-authority argument is set as appropriate. 
1.2.6Ensure that the --authorization-mode argument is not set to AlwaysAllow. 
1.2.7Ensure that the –-authorization-mode argument includes Node.
1.2.8Ensure that the --authorization-mode argument includes RBAC.
1.2.10Ensure that the admission control plugin AlwaysAdmit is not set. 
1.2.12Ensure that the admission control plugin ServiceAccount is set. 
1.2.14Ensure that the admission control plugin NodeRestriction is set. 
1.2.15Ensure that the --profiling argument is set to false. 
1.2.16Ensure that the --audit-log-path argument is set. 
1.2.21Ensure that the --service-account-lookup argument is set to true.
1.2.22Ensure that the --service-account-key-file argument is set as appropriate. 
1.2.23Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate. 
1.2.24Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate.
1.2.25Ensure that the --client-ca-file argument is set as appropriate. 
1.2.26Ensure that the --etcd-cafile argument is set as appropriate.
1.2.30Ensure that the --service-account-extend-token-expiration parameter is set to false.
Controller manager and scheduler1.3.2Ensure that the --profiling argument is set to false. 
1.3.3Ensure that the --use-service-account-credentials argument is set to true.
1.3.4Ensure that the --service-account-private-key-file argument is set as appropriate.
1.3.5Ensure that the --root-ca-file argument is set as appropriate.
1.3.6Ensure that the RotateKubeletServerCertificate argument is set to true.
1.3.7Ensure that the --bind-address argument is set to 127.0.0.1.
Scheduler1.4.1Ensure that the --profiling argument is set to false.
1.4.2Ensure that the --bind-address argument is set to 127.0.0.1.
etcd2.1Ensure that the --cert-file and --key-file arguments are set as appropriate.
2.2Ensure that the --client-cert-auth argument is set to true.
2.3Ensure that the --auto-tls argument is not set to true.
2.4Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate.
2.5Ensure that the --peer-client-cert-auth argument is set to true.
2.6Ensure that the --peer-auto-tls argument is not set to true.
Worker node configuration files4.1.1Ensure that the kubelet service file permissions are set to 600 or more restrictive.
4.1.2Ensure that the kubelet service file ownership is set to root:root.
4.1.5Ensure that the --kubeconfig kubelet.conf file permissions are set to 600 or more restrictive.
4.1.6Ensure that the --kubeconfig kubelet.conf file ownership is set to root:root.
4.1.9If the kubelet config.yaml configuration file is being used, validate permissions are set to 600 or more restrictive.
4.1.10If the kubelet config.yaml configuration file is being used, validate file ownership is set to root:root.
Kubelet4.2.1Ensure that the --anonymous-auth argument is set to false.
4.2.2Ensure that the --authorization-mode argument is not set to AlwaysAllow..
4.2.3Ensure that the --client-ca-file argument is set as appropriate.
4.2.6Ensure that the --make-iptables-util-chains argument is set to true.
4.2.10Ensure that the --rotate-certificates argument is not set to false.
Kube-proxy4.3.1Ensure that the kube-proxy metrics service is bound to localhost.
RBAC and service accounts5.1.7Ensure that the group system:masters is not used for the cluster’s default administrative credential.
Pod security standards5.2.2Minimize the admission of privileged containers.
5.2.3Minimize the admission of containers wishing to share the host process ID namespace.
5.2.4Minimize the admission of containers wishing to share the host IPC namespace.
5.2.5Minimize the admission of containers wishing to share the host network namespace.
5.2.6Minimize the admission of containers with allowPrivilegeEscalation.
5.2.7Minimize the admission of root containers.
5.2.8Minimize the admission of containers with the NET_RAW capability.
5.2.11Minimize the admission of HostPath volumes.
Network policies and CNI5.3.2Ensure that all Namespaces have Network Policies defined.
Extensible admission control5.5.1Configure Image Provenance using the ImagePolicyWebhook admission controller.
General policies5.6.4The 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:

  1. Create a new SCA policy file /var/ossec/etc/sca_kubernetes_audit.yml and 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'
  1. Change the ownership and permission of the file so that the root user and the wazuh group have access to it:
# chown root:wazuh /var/ossec/etc/sca_kubernetes_audit.yml
# chmod 640 /var/ossec/etc/sca_kubernetes_audit.yml
  1. Enable the policy by appending the following configuration to the /var/ossec/etc/ossec.conf file 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>
  1. 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.

Kubernetes SCA results.
Figure 1: Kubernetes SCA 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.

References