CIS Kubernetes Benchmark
Secure configuration guidelines for Kubernetes clusters and workloads
v1.10.0 February 2025Overview
▶This CIS Benchmark provides prescriptive guidance for establishing a secure configuration posture for Kubernetes. It covers both self-managed clusters and managed Kubernetes services (EKS, AKS, GKE). The benchmark addresses control plane components, worker nodes, RBAC policies, pod security, networking, and secrets management.
| Section | Area | Recommendations | Focus |
|---|---|---|---|
| 1 | Control Plane Components | ~65 | API Server, Controller Manager, Scheduler, etcd hardening |
| 2 | Worker Node Security | ~35 | Kubelet config, authentication, file permissions |
| 3 | Control Plane Configuration | ~25 | Authentication, authorization, audit logging |
| 4 | Policies | ~60 | RBAC, pod security standards, network policies, secrets |
| 5 | Managed Services | ~35 | EKS/AKS/GKE specifics, image security, supply chain |
Profile Definitions
▶| Profile | Description | Intended Use |
|---|---|---|
| L1 | Level 1 — Base Security | Core security settings that should be applied to all Kubernetes clusters. Minimal impact on cluster functionality. |
| L2 | Level 2 — Defense in Depth | Advanced security controls for environments requiring stronger security posture. May impose operational overhead. |
1 — Control Plane Components
▶Recommendations for securing Kubernetes control plane components including the API Server, Controller Manager, Scheduler, and etcd.
1.1 API Server
▶Disable anonymous requests to the API server. When enabled, requests not rejected by other configured authentication methods are treated as anonymous, with a username of system:anonymous and group system:unauthenticated.
Anonymous access allows unauthenticated users to query the API server. This can expose cluster metadata and, in worst cases, allow modification of cluster state.
ps -ef | grep kube-apiserver | grep -- '--anonymous-auth' # Verify the flag is set to false # Or check the API server manifest: cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep anonymous-auth
Edit the API server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml and set:
--anonymous-auth=false
| Control | Description | IG |
|---|---|---|
| 3.3 | Configure Data Access Control Lists | IG1 |
Do not use static token-based authentication. The token file-based method stores plaintext credentials and lacks rotation, expiration, or revocation capabilities.
Failure to --token-auth-file is Not Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--token-auth-file' # This flag should NOT be present
Remove the --token-auth-file=<filename> argument from the API server configuration. Use OIDC or certificate-based authentication instead.
Enable HTTPS connections between the API server and kubelets. TLS encryption protects data in transit between control plane and worker nodes.
Without --kubelet-https enabled, the Kubernetes container orchestrator may lack critical protections against known attack vectors. Enabling this control mitigates risk and aligns the deployment with industry-accepted security baselines.
ps -ef | grep kube-apiserver | grep -- '--kubelet-https' # Should be true or not set (defaults to true)
Remove the --kubelet-https=false argument if present. The default is true, which is the desired configuration.
Do not allow all requests. The API server --authorization-mode should not include AlwaysAllow. Instead, use combinations of Node, RBAC, and Webhook.
Failure to --authorization-mode Does Not Include AlwaysAllow may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--authorization-mode' # Verify it does NOT contain AlwaysAllow # Expected: --authorization-mode=Node,RBAC
Set --authorization-mode=Node,RBAC in the API server configuration.
Enable audit logging by setting the audit log path. Kubernetes audit logs provide a chronological record of API server calls, including authentication, authorization, and request/response details.
Failure to --audit-log-path is Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--audit-log-path' # Verify it is set to a valid path, e.g., /var/log/apiserver/audit.log
--audit-log-path=/var/log/apiserver/audit.log --audit-log-maxage=30 --audit-log-maxbackup=10 --audit-log-maxsize=100
Do not bind the API server to an insecure address. The insecure port serves HTTP without authentication or authorization and should be disabled entirely.
Failure to --insecure-bind-address is Not Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--insecure-bind-address' # This flag should NOT be present
Remove the --insecure-bind-address argument from the API server configuration.
Disable profiling on the API server. Profiling data can reveal system and program details that could be exploited.
An improperly configured value for --profiling could weaken security controls or allow unintended behavior. Setting this to False ensures the Kubernetes container orchestrator operates within a well-defined security boundary.
ps -ef | grep kube-apiserver | grep -- '--profiling' # Verify --profiling=false
Set --profiling=false in the API server manifest.
Validate service account tokens. The API server should verify that the service account token exists in etcd before accepting it for authentication.
Failure to --service-account-lookup is True may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--service-account-lookup' # Should be true or not set (defaults to true)
Set --service-account-lookup=true in the API server manifest.
Encrypt secrets at rest in etcd. Kubernetes supports encrypting Secret resources at rest using an EncryptionConfiguration with providers like aescbc, aesgcm, kms, or secretbox.
Failure to --encryption-provider-config is Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--encryption-provider-config' # Verify flag points to a valid EncryptionConfiguration file # Check encryption config: cat /etc/kubernetes/encryption-config.yaml
# Create /etc/kubernetes/encryption-config.yaml:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-secret>
- identity: {}
# Add to API server:
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml
Setup TLS for the API server. All API server traffic should be served over TLS. Ensure both the certificate and private key are configured.
Failure to --tls-cert-file and --tls-private-key-file Are Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-apiserver | grep -- '--tls-cert-file' ps -ef | grep kube-apiserver | grep -- '--tls-private-key-file' # Both flags should be set to valid file paths
Set both --tls-cert-file and --tls-private-key-file to valid certificate and key file paths in the API server manifest.
1.2 Controller Manager
▶Disable profiling on the controller manager. Profiling endpoints expose system metrics and should be disabled in production.
An improperly configured value for --profiling could weaken security controls or allow unintended behavior. Setting this to False ensures the Kubernetes container orchestrator operates within a well-defined security boundary.
ps -ef | grep kube-controller-manager | grep -- '--profiling' # Verify --profiling=false
Set --profiling=false in /etc/kubernetes/manifests/kube-controller-manager.yaml.
Use individual service account credentials for each controller. This follows least privilege by giving each controller only the permissions it needs.
Failure to --use-service-account-credentials is True may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-controller-manager | grep -- '--use-service-account-credentials' # Should be set to true
Set --use-service-account-credentials=true in the controller manager manifest.
Configure the private key to sign service account tokens. This ensures service account tokens are signed with a dedicated key pair.
Failure to --service-account-private-key-file is Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-controller-manager | grep -- '--service-account-private-key-file' # Should be set to a valid key file path
Set --service-account-private-key-file=<path/to/key> in the controller manager manifest.
Allow pods to verify the API server's serving certificate. The root CA file is used to validate the API server's TLS certificate.
Failure to --root-ca-file is Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kube-controller-manager | grep -- '--root-ca-file' # Should be set to the cluster CA certificate
Set --root-ca-file=<path/to/ca.pem> in the controller manager manifest.
Bind the controller manager to the loopback interface only. This restricts access to the controller manager's HTTPS endpoint.
An improperly configured value for --bind-address could weaken security controls or allow unintended behavior. Setting this to 127.0.0.1 ensures the Kubernetes container orchestrator operates within a well-defined security boundary.
ps -ef | grep kube-controller-manager | grep -- '--bind-address' # Should be 127.0.0.1
Set --bind-address=127.0.0.1 in the controller manager manifest.
1.3 Scheduler
▶Disable profiling on the scheduler. Profiling data can expose sensitive operational details.
An improperly configured value for --profiling could weaken security controls or allow unintended behavior. Setting this to False ensures the Kubernetes container orchestrator operates within a well-defined security boundary.
ps -ef | grep kube-scheduler | grep -- '--profiling' # Verify --profiling=false
Set --profiling=false in /etc/kubernetes/manifests/kube-scheduler.yaml.
Bind the scheduler to the loopback interface. This prevents external access to the scheduler's HTTPS endpoint.
An improperly configured value for --bind-address could weaken security controls or allow unintended behavior. Setting this to 127.0.0.1 ensures the Kubernetes container orchestrator operates within a well-defined security boundary.
ps -ef | grep kube-scheduler | grep -- '--bind-address' # Should be 127.0.0.1
Set --bind-address=127.0.0.1 in the scheduler manifest.
1.4 etcd
▶Configure TLS encryption for etcd. All client communication to etcd must be encrypted using TLS certificates.
Failure to --cert-file and --key-file Are Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep etcd | grep -- '--cert-file' ps -ef | grep etcd | grep -- '--key-file' # Both should be set to valid file paths
Set --cert-file=<path/to/cert> and --key-file=<path/to/key> in the etcd manifest.
Enable client certificate authentication for etcd. Require all clients (including the API server) to present a valid certificate when connecting.
Failure to --client-cert-auth is True may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep etcd | grep -- '--client-cert-auth' # Should be set to true
Set --client-cert-auth=true in the etcd manifest.
Encrypt etcd peer communication. In multi-node etcd clusters, peer-to-peer traffic should be encrypted with TLS.
Failure to --peer-cert-file and --peer-key-file Are Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep etcd | grep -- '--peer-cert-file' ps -ef | grep etcd | grep -- '--peer-key-file' # Both should be set
Set both --peer-cert-file and --peer-key-file in the etcd manifest to valid certificate and key paths.
Enable peer client certificate authentication. All etcd peers must authenticate using client certificates.
Failure to --peer-client-cert-auth is True may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep etcd | grep -- '--peer-client-cert-auth' # Should be true
Set --peer-client-cert-auth=true in the etcd manifest.
2 — Worker Node Security
▶Recommendations for securing Kubernetes worker nodes, focusing on kubelet configuration, authentication, and file permissions.
2.1 Kubelet Configuration
▶Disable the kubelet read-only port. The read-only port (default 10255) serves unauthenticated health and metrics endpoints.
An improperly configured value for --read-only-port could weaken security controls or allow unintended behavior. Setting this to 0 ensures the Kubernetes container orchestrator operates within a well-defined security boundary.
ps -ef | grep kubelet | grep -- '--read-only-port' # Should be 0 # Or check kubelet config: cat /var/lib/kubelet/config.yaml | grep readOnlyPort
Set readOnlyPort: 0 in the kubelet configuration file or --read-only-port=0 as a command-line argument.
Do not disable streaming connection timeouts. Setting this to 0 means idle connections remain open indefinitely, wasting resources and increasing attack surface.
Failure to --streaming-connection-idle-timeout Is Not Zero may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kubelet | grep -- '--streaming-connection-idle-timeout' # Should not be 0. Default is 4h.
Keep the default value or set streamingConnectionIdleTimeout: 5m in the kubelet config file for tighter timeouts.
Protect tuned kernel parameters. The kubelet should error if kernel parameters differ from its defaults, preventing pods from modifying kernel settings.
Failure to --protect-kernel-defaults is True may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
ps -ef | grep kubelet | grep -- '--protect-kernel-defaults' # Should be true
Set protectKernelDefaults: true in the kubelet configuration.
Limit the rate of events the kubelet generates. High event rates can overwhelm the API server and storage.
Failure to --event-qps is Set Appropriately may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
cat /var/lib/kubelet/config.yaml | grep eventRecordQPS # Recommended: 5 (default) or lower
Set eventRecordQPS: 5 (or lower) in the kubelet configuration file.
2.2 Kubelet Authentication
▶Disable anonymous authentication on the kubelet. Anonymous requests should be rejected so that only authenticated principals can interact with the kubelet API.
Failure to --anonymous-auth is False may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
cat /var/lib/kubelet/config.yaml | grep -A2 'authentication:' # anonymous: # enabled: false
# In kubelet config:
authentication:
anonymous:
enabled: false
Enable kubelet authorization by setting the mode to Webhook. This delegates authorization to the API server, enforcing RBAC policies.
Failure to --authorization-mode Is Not AlwaysAllow may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
cat /var/lib/kubelet/config.yaml | grep -A1 'authorization:' # mode: Webhook
# In kubelet config: authorization: mode: Webhook
Enable certificate-based kubelet authentication. The client CA file validates client certificates presented to the kubelet.
Failure to --client-ca-file is Set may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
cat /var/lib/kubelet/config.yaml | grep 'clientCAFile' # Should be set to the cluster CA certificate
authentication:
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
2.3 Kubelet File Permissions
▶The kubelet configuration file contains security-sensitive parameters. Restrict permissions to prevent unauthorized reads or modifications.
Incorrect permissions on kubelet config File could allow unauthorized reading, writing, or execution of critical files. Proper file permissions are a foundational control that prevents privilege escalation and data tampering.
stat -c %a /var/lib/kubelet/config.yaml # Should be 600 or more restrictive
chmod 600 /var/lib/kubelet/config.yaml chown root:root /var/lib/kubelet/config.yaml
The kubelet systemd service file defines how the kubelet starts. Restrict permissions to prevent tampering.
Incorrect permissions on kubelet Service File could allow unauthorized reading, writing, or execution of critical files. Proper file permissions are a foundational control that prevents privilege escalation and data tampering.
stat -c %a /etc/systemd/system/kubelet.service.d/10-kubeadm.conf # Should be 600 or more restrictive
chmod 600 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf chown root:root /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
Kubernetes PKI certificates and keys must be protected. Incorrect permissions on these files can lead to cluster compromise.
Incorrect permissions on PKI Directory could allow unauthorized reading, writing, or execution of critical files. Proper file permissions are a foundational control that prevents privilege escalation and data tampering.
ls -la /etc/kubernetes/pki/ # All .key files should be 600, .crt files 644 or 600
chmod -R 600 /etc/kubernetes/pki/*.key chmod -R 644 /etc/kubernetes/pki/*.crt chown -R root:root /etc/kubernetes/pki/
3 — Control Plane Configuration
▶Recommendations for Kubernetes control plane configuration including authentication, authorization, and audit logging.
3.1 Authentication & Authorization
▶Use client certificate authentication for user access to the cluster. Certificate-based auth is more secure than token-based methods and supports rotation.
Failure to client Certificate Authentication Is Used may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl config view --raw -o jsonpath='{.users[*].user}'
# Verify client-certificate-data and client-key-data are present
Configure client certificates for all human users and service integrations. Use kubeadm to generate certificates or integrate with an external PKI.
Configure OpenID Connect (OIDC) for user authentication. OIDC integrates with identity providers like Azure AD, Okta, or Dex for centralized identity management.
Misconfiguration of OIDC Authentication can lead to security gaps that may be exploited by attackers. A properly configured Kubernetes container orchestrator reduces exposure to both known vulnerabilities and configuration drift.
ps -ef | grep kube-apiserver | grep -- '--oidc-issuer-url' # Should be set to your identity provider URL
--oidc-issuer-url=https://your-idp.example.com --oidc-client-id=kubernetes --oidc-username-claim=email --oidc-groups-claim=groups
Enable Role-Based Access Control. RBAC is the recommended authorization mechanism for Kubernetes, providing fine-grained access control over cluster resources.
Without RBAC enabled, the Kubernetes container orchestrator may lack critical protections against known attack vectors. Enabling this control mitigates risk and aligns the deployment with industry-accepted security baselines.
ps -ef | grep kube-apiserver | grep -- '--authorization-mode' # Must include RBAC kubectl api-versions | grep rbac
Ensure --authorization-mode includes RBAC, e.g., --authorization-mode=Node,RBAC.
3.2 Logging & Auditing
▶Configure an audit policy that logs access to secrets, configmaps, and other sensitive resources at the Metadata level or above.
Failure to audit Policy Covers Key Resources may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
cat /etc/kubernetes/audit-policy.yaml # Verify rules cover secrets, configmaps, roles, bindings
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps"]
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
- level: Metadata
omitStages:
- RequestReceived
Forward audit logs to a centralized logging system (e.g., Elasticsearch, Splunk, CloudWatch) for analysis, alerting, and retention beyond local storage limits.
Failure to audit Logs Are Forwarded to an External System may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
Verify audit webhook backend is configured or a log shipping agent (Fluentd, Filebeat) is running on control plane nodes.
# Option 1: Audit webhook backend --audit-webhook-config-file=/etc/kubernetes/audit-webhook.yaml # Option 2: Deploy Fluentd/Filebeat DaemonSet # to ship /var/log/apiserver/audit.log
4 — Policies
▶Recommendations for Kubernetes policies covering RBAC, pod security, network policies, and secrets management.
4.1 RBAC & Service Accounts
▶The cluster-admin ClusterRole grants superuser access. Minimize its use to only essential cluster administrators.
Failure to cluster-Admin Role Is Used Only When Necessary may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get clusterrolebindings -o json | jq '.items[] |
select(.roleRef.name == "cluster-admin") |
{name: .metadata.name, subjects: .subjects}'
Review all cluster-admin bindings and replace with scoped roles where possible. Use kubectl delete clusterrolebinding <name> for unnecessary bindings.
Disable automatic mounting of service account tokens in pods that don't need API access. This limits the blast radius if a pod is compromised.
Failure to service Accounts Do Not Auto-Mount Tokens may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get serviceaccounts --all-namespaces -o json | jq '.items[] |
select(.automountServiceAccountToken != false) |
{namespace: .metadata.namespace, name: .metadata.name}'
# Set on the ServiceAccount: apiVersion: v1 kind: ServiceAccount metadata: name: my-sa automountServiceAccountToken: false # Or per-pod: spec: automountServiceAccountToken: false
Do not use the default service account in namespaces. Create dedicated service accounts with only the permissions required by the workload.
If Default ServiceAccount remains used, it presents an unnecessary risk vector that attackers could exploit. Removing or disabling unused components is a fundamental principle of secure system hardening.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.serviceAccountName == "default" or
.spec.serviceAccountName == null) |
{namespace: .metadata.namespace, pod: .metadata.name}'
Create dedicated service accounts and assign them to pods. Restrict the default service account by disabling token automounting.
Review RBAC roles and cluster roles to ensure they follow the principle of least privilege. Avoid wildcard (*) permissions and excessive verbs.
Failure to rBAC Permissions Follow Least Privilege may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Find roles with wildcard access:
kubectl get roles,clusterroles --all-namespaces -o json | jq '.items[] |
select(.rules[]?.resources[]? == "*" or .rules[]?.verbs[]? == "*") |
{kind: .kind, name: .metadata.name}'
Replace wildcard permissions with explicit resource and verb lists. Scope roles to specific namespaces rather than using cluster-wide ClusterRoles.
4.2 Pod Security
▶Configure Pod Security Admission (PSA) to enforce pod security standards. PSA replaced PodSecurityPolicy (PSP) in Kubernetes 1.25+.
Misconfiguration of Pod Security Admission can lead to security gaps that may be exploited by attackers. A properly configured Kubernetes container orchestrator reduces exposure to both known vulnerabilities and configuration drift.
# Check namespace labels:
kubectl get namespaces -o json | jq '.items[] |
{name: .metadata.name,
enforce: .metadata.labels["pod-security.kubernetes.io/enforce"],
warn: .metadata.labels["pod-security.kubernetes.io/warn"]}'
# Apply PSA labels to namespaces: kubectl label ns my-namespace \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/audit=restricted
Do not run containers in privileged mode. Privileged containers have unrestricted access to the host's resources and kernel capabilities.
If Privileged Containers remains used, it presents an unnecessary risk vector that attackers could exploit. Removing or disabling unused components is a fundamental principle of secure system hardening.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.containers[]?.securityContext?.privileged == true) |
{namespace: .metadata.namespace, pod: .metadata.name}'
securityContext: privileged: false allowPrivilegeEscalation: false
Require containers to run as a non-root user. Running as root inside a container increases the risk of container breakout vulnerabilities.
Failure to containers Run as Non-Root may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.securityContext?.runAsNonRoot != true and
(.spec.containers[]?.securityContext?.runAsNonRoot != true)) |
{namespace: .metadata.namespace, pod: .metadata.name}'
securityContext: runAsNonRoot: true runAsUser: 1000
Mount the container's root filesystem as read-only. This prevents malicious processes from writing to the filesystem and reduces attack surface.
Failure to containers Have Read-Only Root Filesystem may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.containers[]?.securityContext?.readOnlyRootFilesystem != true) |
{namespace: .metadata.namespace, pod: .metadata.name}'
securityContext:
readOnlyRootFilesystem: true
# Use emptyDir volumes for writable paths:
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Drop all Linux capabilities and only add back those explicitly needed. Containers run with a default capability set that is broader than necessary.
Failure to containers Drop All Capabilities may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.containers[]? |
(.securityContext?.capabilities?.drop // []) | (. | map(ascii_downcase) | contains(["all"]) | not)) |
{namespace: .metadata.namespace, pod: .metadata.name}'
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE # only if needed
4.3 Network Policies
▶Create default deny NetworkPolicies for ingress and egress in every namespace. By default, Kubernetes allows all pod-to-pod traffic.
The absence of Default Deny Network Policies leaves the Kubernetes container orchestrator without an important security control. Verifying its presence ensures the system meets the minimum security baseline required by the CIS benchmark.
# Check for default deny policies:
kubectl get networkpolicies --all-namespaces -o json | jq '.items[] |
select(.spec.podSelector == {} and
(.spec.policyTypes // [] | contains(["Ingress"]))) |
{namespace: .metadata.namespace, name: .metadata.name}'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: my-namespace
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Use a CNI plugin that enforces NetworkPolicies. Not all CNI plugins (e.g., Flannel) support network policies. Use Calico, Cilium, or Weave Net.
Failure to cNI Plugin Supports Network Policies may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check installed CNI: ls /etc/cni/net.d/ kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'
Install a CNI plugin that supports NetworkPolicies. Recommended: Calico, Cilium, or Weave Net.
4.4 Secrets Management
▶Enable encryption at rest for Kubernetes Secrets stored in etcd. By default, Secrets are stored as base64-encoded plaintext in etcd.
Without encryption, Secrets may transmit or store sensitive information in cleartext, exposing it to interception, eavesdropping, or tampering. Encryption is a critical control for data confidentiality and integrity.
# Verify encryption provider is configured: ps -ef | grep kube-apiserver | grep -- '--encryption-provider-config' # Verify a secret is actually encrypted: ETCDCTL_API=3 etcdctl get /registry/secrets/default/my-secret \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key | hexdump -C | head # Should show encrypted (not plaintext) data
Configure the EncryptionConfiguration as described in recommendation 1.1.9.
Prefer volume mounts over environment variables for secrets. Environment variables are visible in process listings and crash dumps, while volume-mounted secrets can be updated without pod restart.
Failure to secrets Are Not Stored in Environment Variables may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.containers[]?.env[]?.valueFrom?.secretKeyRef != null) |
{namespace: .metadata.namespace, pod: .metadata.name}'
# Mount secrets as volumes instead:
volumes:
- name: db-creds
secret:
secretName: db-credentials
containers:
- volumeMounts:
- name: db-creds
mountPath: /etc/secrets
readOnly: true
Consider using an external secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) with the External Secrets Operator or Secrets Store CSI Driver for enhanced secrets lifecycle management.
Implementing this recommendation reduces the risk of security compromise on the Kubernetes container orchestrator. Unaddressed configuration weaknesses are frequently targeted by attackers during both automated scans and manual penetration testing.
# Check for external secrets operator: kubectl get pods -n external-secrets # Or secrets store CSI driver: kubectl get csidrivers | grep secrets-store
Deploy the External Secrets Operator or the Secrets Store CSI Driver and configure it with your cloud provider's secrets management service.
5 — Managed Services
▶Recommendations specific to managed Kubernetes services (EKS, AKS, GKE) and container image security.
5.1 EKS / AKS / GKE Specifics
▶Restrict the Kubernetes API endpoint to private networks. Public API endpoints expose the cluster to internet-based attacks.
Failure to cluster Endpoint Is Not Publicly Accessible may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# EKS: aws eks describe-cluster --name my-cluster \ --query 'cluster.resourcesVpcConfig.endpointPublicAccess' # AKS: az aks show -g myRG -n myAKS --query 'apiServerAccessProfile' # GKE: gcloud container clusters describe my-cluster \ --format='value(privateClusterConfig.enablePrivateEndpoint)'
Disable public endpoint access and use a private endpoint with VPN or bastion host access.
Deploy worker nodes in private subnets without direct internet access. Use NAT gateways for outbound connectivity.
Failure to cluster Nodes Are in Private Subnets may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
Verify that node subnets do not have public IP auto-assignment and use NAT gateways for internet access.
Configure the managed cluster to use private subnets for node groups with NAT gateway for outbound traffic.
Enable automatic node upgrades to ensure security patches are applied promptly. All major managed services support automatic minor version and patch upgrades.
Without Managed Node Auto-Upgrade enabled, the Kubernetes container orchestrator may lack critical protections against known attack vectors. Enabling this control mitigates risk and aligns the deployment with industry-accepted security baselines.
# EKS: Check managed node group update config aws eks describe-nodegroup --cluster-name my-cluster --nodegroup-name my-ng # AKS: az aks show -g myRG -n myAKS --query 'autoUpgradeProfile' # GKE: gcloud container clusters describe my-cluster \ --format='value(autoupgrade)'
Enable auto-upgrade in the managed node group configuration.
Enable control plane logging for API server, authenticator, controller manager, and scheduler. This provides visibility into cluster operations.
Without Control Plane Logging enabled, the Kubernetes container orchestrator may lack critical protections against known attack vectors. Enabling this control mitigates risk and aligns the deployment with industry-accepted security baselines.
# EKS: aws eks describe-cluster --name my-cluster \ --query 'cluster.logging.clusterLogging' # AKS: az monitor diagnostic-settings list --resource $AKS_ID # GKE: gcloud container clusters describe my-cluster \ --format='value(loggingConfig)'
Enable all control plane log types: api, audit, authenticator, controllerManager, scheduler.
5.2 Image Security
▶Only deploy container images from trusted registries. Use admission controllers to enforce image source policies.
Failure to container Images Are From Trusted Registries may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get pods --all-namespaces -o json | jq '.items[] |
{namespace: .metadata.namespace, pod: .metadata.name,
images: [.spec.containers[].image]}'
# Verify all images are from approved registries
Use OPA/Gatekeeper, Kyverno, or cloud-native admission controllers (ImagePolicyWebhook) to restrict image sources to approved registries.
Scan all container images for known vulnerabilities before deployment. Integrate scanning into CI/CD pipelines and use admission controllers to block vulnerable images.
Failure to container Images Are Scanned for Vulnerabilities may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
Verify that image scanning is integrated into CI/CD pipelines and an admission controller blocks images with critical/high vulnerabilities.
Use Trivy, Grype, Snyk, or your registry's built-in scanner (ECR scanning, ACR scanning, GCR scanning). Add scanning to CI/CD and use admission controllers to enforce.
Use image digests or immutable tags instead of mutable tags like :latest. Mutable tags can change without notice, making deployments non-reproducible.
Failure to image Tags Are Not Mutable may leave the Kubernetes container orchestrator vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
kubectl get pods --all-namespaces -o json | jq '.items[] |
select(.spec.containers[]?.image | test(":latest$") or (test("@sha256:") | not)) |
{namespace: .metadata.namespace, pod: .metadata.name,
images: [.spec.containers[].image]}'
# Use digest references: image: myregistry.io/app@sha256:abc123... # Or use imagePullPolicy: Always is NOT sufficient # Enable tag immutability in your registry