CIS Kubernetes - Google Cloud (GKE)
This page lists all 50 policies in the CIS Kubernetes pack for Google Cloud (GKE), as published in cis-kubernetes-gcp version 1.0.1.
Policies by control
4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
- k8s-cluster-admin-role-binding-minimized
- k8s-default-service-accounts-not-used
- k8s-rbac-bind-impersonate-escalate-minimized
- k8s-rbac-secret-access-minimized
- k8s-rbac-system-anonymous-bindings-minimized
- k8s-rbac-system-authenticated-bindings-minimized
- k8s-rbac-system-masters-group-avoided
- k8s-rbac-system-unauthenticated-bindings-minimized
- k8s-rbac-wildcard-use-minimized
- k8s-service-account-token-mounted-minimized
4.2 Pod Security Standards — Enforce Pod Security Standard Baseline profile to prevent privileged containers and host namespace access.
- k8s-pod-security-allow-privilege-escalation-minimized
- k8s-pod-security-host-ipc-minimized
- k8s-pod-security-host-network-minimized
- k8s-pod-security-host-pid-minimized
- k8s-pod-security-privileged-containers-minimized
4.3 Network Security — Use network policies to isolate traffic and restrict network access between pods.
4.5 Binary Authorization — Ensure only trusted container images can be deployed.
4.6 Pod Security — Apply RuntimeDefault seccomp profile to all workloads and ensure default namespace is not used.
5.1 Container Images — Ensure IAM controls and trusted image sources.
- gke-container-registry-iam-minimized
- gke-container-registry-read-only-cluster-access
- k8s-trusted-images-only
5.2 Node Service Accounts — Use dedicated service accounts with Workload Identity for GKE nodes.
5.3 Secrets Encryption — Encrypt Kubernetes secrets using Cloud KMS customer-managed keys.
5.4 Workload Identity — Use GKE Metadata Server and Workload Identity for workload authentication.
5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
- gke-cos-containerd-node-image
- gke-node-auto-repair-enabled
- gke-node-auto-upgrade-enabled
- gke-release-channel-enabled
- gke-shielded-nodes-enabled
- gke-shielded-nodes-integrity-monitoring
- gke-shielded-nodes-secure-boot
5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
- gke-control-plane-authorized-networks-enabled
- gke-google-managed-ssl-certificates
- gke-private-endpoint-enabled
- gke-private-nodes-enabled
- gke-vpc-flow-logs-intranode-visibility
- gke-vpc-native-cluster-required
- gke-worker-node-firewall-configured
5.7 Logging — Enable Cloud Logging, Cloud Monitoring, and auditd logging.
5.8 Authentication and Authorization — Disable client certificate authentication, enable Google Groups for RBAC, and disable legacy ABAC.
5.9 Encryption — Use customer-managed encryption keys (CMEK) for persistent and boot disks.
5.10 Additional Security Controls — Disable Kubernetes Dashboard, avoid alpha clusters, and enable security posture.
Policy details
gke-alpha-clusters-not-production
Severity: medium · Enforcement: advisory
Ensure GKE alpha clusters are not used, as they contain unstable features not suitable for production.
- 5.10 Additional Security Controls — Disable Kubernetes Dashboard, avoid alpha clusters, and enable security posture.
Remediation
Fix: Avoid Alpha Clusters for Production
Do not enable alpha features in production clusters:
import * as gcp from "@pulumi/gcp";
// Good: Standard cluster without alpha features
const cluster = new gcp.container.Cluster("cluster", {
name: "production-cluster",
location: "us-central1",
// Do NOT set enableKubernetesAlpha: true
// ... other configuration
});
// Bad: Alpha cluster (for testing only)
const alphaCluster = new gcp.container.Cluster("alpha-cluster", {
name: "testing-cluster",
location: "us-central1",
enableKubernetesAlpha: true, // DON'T DO THIS in production
});
Alpha cluster limitations:
- Cannot be upgraded (must recreate)
- No SLA or support
- Alpha features may be removed or change without notice
- Not recommended for any production workloads
- Automatically deleted after 30 days
Use alpha clusters only for testing Kubernetes alpha features in non-production environments.
gke-binary-authorization-enabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have Binary Authorization enabled to verify image signatures and attestations.
- 4.5 Binary Authorization — Ensure only trusted container images can be deployed.
Remediation
Fix: Enable Binary Authorization
Configure Binary Authorization on the GKE cluster:
import * as gcp from "@pulumi/gcp";
// Enable Binary Authorization on cluster
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Binary Authorization
binaryAuthorization: {
evaluationMode: "PROJECT_SINGLETON_POLICY_ENFORCE",
},
// ... other configuration
});
// Create Binary Authorization policy
const policy = new gcp.binaryauthorization.Policy("policy", {
defaultAdmissionRule: {
evaluationMode: "REQUIRE_ATTESTATION",
enforcementMode: "ENFORCED_BLOCK_AND_AUDIT_LOG",
requireAttestationsBy: [attestor.name],
},
// Allow images from specific registries
clusterAdmissionRules: [{
cluster: pulumi.interpolate`${cluster.location}.${cluster.name}`,
evaluationMode: "REQUIRE_ATTESTATION",
enforcementMode: "ENFORCED_BLOCK_AND_AUDIT_LOG",
requireAttestationsBy: [attestor.name],
}],
});
// Create attestor for image signing
const attestor = new gcp.binaryauthorization.Attestor("attestor", {
name: "prod-attestor",
attestationAuthorityNote: {
noteReference: note.name,
},
});
Binary Authorization modes:
PROJECT_SINGLETON_POLICY_ENFORCE: Enforce project-wide policy (recommended)DISABLED: No enforcement (not recommended)
Benefits:
- Ensures only signed/verified images run
- Prevents deployment of untrusted images
- Integrates with CI/CD for image attestation
- Compliance with supply chain security requirements
gke-client-certificate-auth-disabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have client certificate authentication disabled in favor of more secure methods.
- 5.8 Authentication and Authorization — Disable client certificate authentication, enable Google Groups for RBAC, and disable legacy ABAC.
Remediation
Fix: Disable Client Certificate Authentication
Disable client certificate issuance for the cluster:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Disable client certificate authentication
masterAuth: {
clientCertificateConfig: {
issueClientCertificate: false,
},
},
// ... other configuration
});
Recommended authentication methods instead:
- Google Cloud IAM with gcloud/kubectl
- Workload Identity for pod-to-GCP authentication
- OIDC providers for user authentication
- Google Groups for RBAC
Client certificates are difficult to rotate and manage securely.
gke-cmek-boot-disks
Severity: medium · Enforcement: advisory
Ensure GKE node pools use customer-managed encryption keys (CMEK) for boot disk encryption.
- 5.9 Encryption — Use customer-managed encryption keys (CMEK) for persistent and boot disks.
Remediation
Fix: Enable CMEK for Boot Disks
Configure node pools to use CMEK for boot disk encryption:
import * as gcp from "@pulumi/gcp";
// Create KMS key
const keyRing = new gcp.kms.KeyRing("keyring", {
name: "gke-keyring",
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("boot-disk-key", {
name: "boot-disk-key",
keyRing: keyRing.id,
purpose: "ENCRYPT_DECRYPT",
});
// Grant GCE service account access to use the key
const cryptoKeyBinding = new gcp.kms.CryptoKeyIAMBinding("boot-disk-key-binding", {
cryptoKeyId: cryptoKey.id,
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
members: [
"serviceAccount:service-PROJECT_NUMBER@compute-system.iam.gserviceaccount.com",
],
});
// Configure node pool with CMEK
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
bootDiskKmsKey: cryptoKey.id,
},
});
Benefits:
- Control over encryption keys
- Key rotation capabilities
- Audit trail for key usage
- Compliance with data residency requirements
Note: By default, GKE uses Google-managed encryption. CMEK provides additional control.
gke-cmek-persistent-disks
Severity: medium · Enforcement: advisory
Ensure Compute Engine persistent disks use customer-managed encryption keys (CMEK).
- 5.9 Encryption — Use customer-managed encryption keys (CMEK) for persistent and boot disks.
Remediation
Fix: Enable CMEK for Persistent Disks
Configure persistent disks to use CMEK:
import * as gcp from "@pulumi/gcp";
// Create KMS key
const keyRing = new gcp.kms.KeyRing("keyring", {
name: "disk-keyring",
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("disk-key", {
name: "disk-key",
keyRing: keyRing.id,
purpose: "ENCRYPT_DECRYPT",
});
// Grant GCE service account access to use the key
const cryptoKeyBinding = new gcp.kms.CryptoKeyIAMBinding("disk-key-binding", {
cryptoKeyId: cryptoKey.id,
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
members: [
"serviceAccount:service-PROJECT_NUMBER@compute-system.iam.gserviceaccount.com",
],
});
// Create disk with CMEK
const disk = new gcp.compute.Disk("disk", {
name: "encrypted-disk",
zone: "us-central1-a",
type: "pd-standard",
size: 100,
diskEncryptionKey: {
kmsKeySelfLink: cryptoKey.id,
},
});
// Or use in Kubernetes StorageClass
const storageClass = new k8s.storage.v1.StorageClass("encrypted-sc", {
metadata: { name: "encrypted-storage" },
provisioner: "kubernetes.io/gce-pd",
parameters: {
type: "pd-standard",
"disk-encryption-kms-key": cryptoKey.id,
},
});
Benefits:
- Control over encryption keys
- Key rotation capabilities
- Audit trail for key usage
- Compliance with data residency requirements
Note:
- By default, GKE uses Google-managed encryption
- CMEK provides additional control
- Applies to dynamically provisioned PersistentVolumes
gke-container-registry-iam-minimized
Severity: high · Enforcement: advisory
Ensure container registry IAM permissions used by GKE are restricted to specific users/service accounts with appropriate roles.
- 5.1 Container Images — Ensure IAM controls and trusted image sources.
Remediation
Fix: Minimize Container Registry IAM Permissions
Grant minimal required permissions for Artifact Registry:
import * as gcp from "@pulumi/gcp";
const repository = new gcp.artifactregistry.Repository("repo", {
repositoryId: "my-repo",
format: "DOCKER",
location: "us-central1",
});
// GKE nodes: Read-only access
const gkeReaderBinding = new gcp.artifactregistry.RepositoryIamMember("gke-reader", {
repository: repository.name,
location: repository.location,
role: "roles/artifactregistry.reader",
member: pulumi.interpolate`serviceAccount:${gkeNodeSa.email}`,
});
// CI/CD: Writer access (limited to CI/CD service account)
const cicdWriterBinding = new gcp.artifactregistry.RepositoryIamMember("cicd-writer", {
repository: repository.name,
location: repository.location,
role: "roles/artifactregistry.writer",
member: pulumi.interpolate`serviceAccount:${cicdSa.email}`,
});
// Developers: Reader access only (use CI/CD to push)
const devReaderBinding = new gcp.artifactregistry.RepositoryIamMember("dev-reader", {
repository: repository.name,
location: repository.location,
role: "roles/artifactregistry.reader",
member: "group:developers@example.com",
});
IAM roles for Artifact Registry:
roles/artifactregistry.reader: Pull images (GKE nodes, developers)roles/artifactregistry.writer: Push images (CI/CD only)roles/artifactregistry.repoAdmin: Manage repository (admins)
Best practices:
- Use service accounts for programmatic access
- Grant write access only to CI/CD pipelines
- Use groups for user management
- Avoid
allUsersandallAuthenticatedUsers
gke-container-registry-read-only-cluster-access
Severity: medium · Enforcement: advisory
Ensure GKE node service accounts only have reader access to Artifact Registry/Container Registry.
- 5.1 Container Images — Ensure IAM controls and trusted image sources.
Remediation
Fix: Grant Read-Only Registry Access to GKE Nodes
Configure node service account with read-only access:
import * as gcp from "@pulumi/gcp";
// Create dedicated service account for GKE nodes
const gkeNodeSa = new gcp.serviceaccount.Account("gke-nodes", {
accountId: "gke-nodes",
displayName: "GKE Node Service Account",
});
// Grant read-only access to Artifact Registry
const artifactRegistryReader = new gcp.projects.IAMMember("ar-reader", {
role: "roles/artifactregistry.reader",
member: pulumi.interpolate`serviceAccount:${gkeNodeSa.email}`,
});
// For Container Registry (legacy), grant storage.objectViewer
const gcsReader = new gcp.projects.IAMMember("gcr-reader", {
role: "roles/storage.objectViewer",
member: pulumi.interpolate`serviceAccount:${gkeNodeSa.email}`,
// Optionally restrict to specific GCS buckets
});
// Use in node pool
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
serviceAccount: gkeNodeSa.email,
oauthScopes: [
"https://www.googleapis.com/auth/cloud-platform",
],
},
});
Why read-only:
- Nodes should pull images, not push
- Image pushes should go through CI/CD pipelines
- Prevents compromised nodes from pushing malicious images
- Follows principle of least privilege
Required roles:
- Artifact Registry:
roles/artifactregistry.reader - Container Registry:
roles/storage.objectViewer(for GCS buckets)
gke-control-plane-authorized-networks-enabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have authorized networks configured to restrict control plane access to specific IP ranges.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Enable Authorized Networks for GKE Control Plane
Configure authorized networks to restrict control plane access:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Configure authorized networks
masterAuthorizedNetworksConfig: {
cidrBlocks: [
{
cidrBlock: "203.0.113.0/24", // Office network
displayName: "office",
},
{
cidrBlock: "198.51.100.0/24", // CI/CD network
displayName: "cicd",
},
],
},
// ... other configuration
});
Note:
- Include all networks that need access (kubectl, CI/CD, monitoring)
- Use narrow CIDR ranges for better security
- Avoid using 0.0.0.0/0 which allows all IPs
gke-cos-containerd-node-image
Severity: medium · Enforcement: advisory
Ensure GKE node pools use Container-Optimized OS (COS) with containerd runtime.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Use Container-Optimized OS with containerd
Configure node pools to use COS with containerd:
import * as gcp from "@pulumi/gcp";
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
imageType: "COS_CONTAINERD", // Recommended
// or "UBUNTU_CONTAINERD" for Ubuntu with containerd
},
});
Recommended image types:
COS_CONTAINERD: Container-Optimized OS with containerd (recommended)UBUNTU_CONTAINERD: Ubuntu with containerd (acceptable alternative)
Avoid:
COS: COS with Docker (deprecated, Docker shim removed in Kubernetes 1.24+)UBUNTU: Ubuntu with Docker (deprecated)WINDOWS_LTSC_CONTAINERD/WINDOWS_SAC_CONTAINERD: Windows nodes (use if required)
Benefits of COS with containerd:
- Optimized for running containers
- Automatic security updates
- Smaller attack surface
- containerd is the standard Kubernetes runtime
gke-google-groups-rbac-enabled
Severity: medium · Enforcement: advisory
Ensure GKE clusters use Google Groups for RBAC to simplify permission management.
- 5.8 Authentication and Authorization — Disable client certificate authentication, enable Google Groups for RBAC, and disable legacy ABAC.
Remediation
Fix: Enable Google Groups for RBAC
Configure Google Groups integration for RBAC management:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Google Groups for RBAC
authenticatorGroupsConfig: {
securityGroup: "gke-security-groups@yourdomain.com",
},
// ... other configuration
});
// Example RoleBinding using Google Group
const roleBinding = new k8s.rbac.v1.ClusterRoleBinding("developers", {
subjects: [{
kind: "Group",
name: "developers@yourdomain.com",
apiGroup: "rbac.authorization.k8s.io",
}],
roleRef: {
kind: "ClusterRole",
name: "edit",
apiGroup: "rbac.authorization.k8s.io",
},
});
Benefits:
- Centralized user management through Google Workspace/Cloud Identity
- Users added/removed from groups automatically gain/lose cluster access
- Easier to audit and manage permissions
- Supports nested groups
gke-google-managed-ssl-certificates
Severity: low · Enforcement: advisory
Ensure Google-managed SSL certificates are used for GKE ingress when applicable.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Use Google-Managed SSL Certificates
Option 1: ManagedCertificate resource (GKE Ingress):
import * as gcp from "@pulumi/gcp";
import * as k8s from "@pulumi/kubernetes";
// Create Google-managed SSL certificate
const cert = new gcp.compute.ManagedSslCertificate("cert", {
managed: {
domains: ["example.com", "www.example.com"],
},
});
// Use in Ingress annotation
const ingress = new k8s.networking.v1.Ingress("ingress", {
metadata: {
annotations: {
"networking.gke.io/managed-certificates": "cert-name",
"kubernetes.io/ingress.class": "gce",
},
},
spec: {
rules: [{
host: "example.com",
http: {
paths: [{
path: "/*",
pathType: "ImplementationSpecific",
backend: {
service: {
name: "service",
port: { number: 80 },
},
},
}],
},
}],
},
});
Option 2: Kubernetes ManagedCertificate CRD:
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: cert-name
spec:
domains:
- example.com
- www.example.com
Benefits:
- Automatic certificate provisioning
- Automatic renewal (no expiration)
- Free (no certificate costs)
- Managed by Google Cloud
gke-legacy-abac-disabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have legacy Attribute-Based Access Control (ABAC) disabled in favor of RBAC.
- 5.8 Authentication and Authorization — Disable client certificate authentication, enable Google Groups for RBAC, and disable legacy ABAC.
Remediation
Fix: Disable Legacy ABAC
Ensure ABAC is disabled (this is the default for new clusters):
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Explicitly disable legacy ABAC (default is false)
enableLegacyAbac: false,
// ... other configuration
});
Why disable ABAC:
- ABAC is deprecated in Kubernetes
- ABAC is less flexible and harder to manage than RBAC
- ABAC permissions are defined in a static file that’s difficult to audit
- RBAC provides fine-grained, dynamic access control
- All GKE clusters should use RBAC (enabled by default)
gke-linux-auditd-logging-enabled
Severity: medium · Enforcement: advisory
Ensure GKE node pools have Linux auditd logging enabled for OS-level security auditing.
- 5.7 Logging — Enable Cloud Logging, Cloud Monitoring, and auditd logging.
Remediation
Fix: Enable Linux auditd Logging
Enable Linux auditd logging on node pools:
import * as gcp from "@pulumi/gcp";
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
linuxNodeConfig: {
sysctls: {
// Configure sysctl parameters if needed
},
},
// Enable Cloud Logging for system logs
loggingVariant: "DEFAULT",
},
});
// Ensure cluster has logging enabled
const cluster = new gcp.container.Cluster("cluster", {
loggingConfig: {
enableComponents: [
"SYSTEM_COMPONENTS",
"WORKLOADS",
],
},
});
Linux auditd logging:
- Captures OS-level audit events
- Logs file access, system calls, authentication
- Integrated with Cloud Logging
- Available on Container-Optimized OS
Logging variants:
DEFAULT: Standard Cloud Logging (recommended)MAX_THROUGHPUT: Higher throughput, may impact performance
What gets logged:
- File system access
- System calls
- Process execution
- Authentication events
- Network connections
Note:
- COS (Container-Optimized OS) has auditd pre-configured
- Logs are automatically sent to Cloud Logging when enabled
- Review audit rules based on security requirements
gke-logging-monitoring-enabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have Cloud Logging and Cloud Monitoring enabled for system and workload observability.
- 5.7 Logging — Enable Cloud Logging, Cloud Monitoring, and auditd logging.
Remediation
Fix: Enable Logging and Monitoring for GKE Cluster
Configure logging and monitoring services:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Cloud Logging (recommended components)
loggingConfig: {
enableComponents: [
"SYSTEM_COMPONENTS", // System logs (kubelet, container runtime, etc.)
"WORKLOADS", // Application container logs
],
},
// Enable Cloud Monitoring (recommended components)
monitoringConfig: {
enableComponents: [
"SYSTEM_COMPONENTS", // System metrics
"WORKLOADS", // Workload metrics (requires GKE 1.24+)
],
managedPrometheus: {
enabled: true, // Enable managed Prometheus collection
},
},
// ... other configuration
});
Components:
SYSTEM_COMPONENTS: Logs/metrics from control plane and system podsWORKLOADS: Logs/metrics from user workload pods- Managed Prometheus: Prometheus-compatible metric collection
gke-metadata-server-enabled
Severity: medium · Enforcement: advisory
Ensure GKE clusters have Workload Identity enabled via GKE Metadata Server.
- 5.4 Workload Identity — Use GKE Metadata Server and Workload Identity for workload authentication.
Remediation
Fix: Enable GKE Metadata Server for Workload Identity
Enable Workload Identity on the cluster:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Workload Identity
workloadIdentityConfig: {
workloadPool: "my-project.svc.id.goog",
},
// ... other configuration
});
// Node pools must also enable GKE Metadata Server
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
workloadMetadataConfig: {
mode: "GKE_METADATA",
},
},
});
Benefits:
- Allows pods to authenticate to GCP APIs using Kubernetes Service Accounts
- Eliminates need for service account keys in pods
- Provides fine-grained IAM permissions per workload
gke-network-policy-enabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have network policy support enabled (via GKE Dataplane V2 or Calico).
- 4.3 Network Security — Use network policies to isolate traffic and restrict network access between pods.
Remediation
Fix: Enable Network Policy Support
Option 1: GKE Dataplane V2 (Recommended):
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Dataplane V2 (includes network policy support)
datapathProvider: "ADVANCED_DATAPATH",
// Requires VPC-native cluster
ipAllocationPolicy: {
clusterSecondaryRangeName: "pods",
servicesSecondaryRangeName: "services",
},
// ... other configuration
});
Option 2: Calico Network Policy:
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Calico network policy
networkPolicy: {
enabled: true,
provider: "CALICO",
},
// Enable network policy addon
addonsConfig: {
networkPolicyConfig: {
disabled: false,
},
},
// ... other configuration
});
GKE Dataplane V2 benefits:
- Built on eBPF for better performance
- Native network policy support
- Better observability and security features
- Recommended for new clusters
gke-node-auto-repair-enabled
Severity: high · Enforcement: advisory
Ensure GKE node pools have auto-repair enabled to automatically fix unhealthy nodes.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Enable Node Auto-Repair
Configure auto-repair in node pool management settings:
import * as gcp from "@pulumi/gcp";
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
management: {
autoRepair: true, // Enable auto-repair
autoUpgrade: true, // Also recommended
},
});
How auto-repair works:
- GKE periodically checks node health
- If a node fails health checks, it’s automatically recreated
- Workloads are gracefully drained before node recreation
- Helps maintain cluster health and availability
Health checks include:
- Kubelet status
- Container runtime status
- Node responsiveness
gke-node-auto-upgrade-enabled
Severity: high · Enforcement: advisory
Ensure GKE node pools have auto-upgrade enabled to automatically update nodes with security patches.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Enable Node Auto-Upgrade
Configure auto-upgrade in node pool management settings:
import * as gcp from "@pulumi/gcp";
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
management: {
autoUpgrade: true, // Enable auto-upgrade
autoRepair: true, // Also recommended
},
});
How auto-upgrade works:
- Nodes automatically upgrade to match control plane version
- Upgrades happen during maintenance windows
- Workloads are gracefully drained before upgrade
- Keeps nodes secure with latest patches
Best practice:
- Use Release Channels for predictable upgrade schedules
- Configure maintenance windows for controlled upgrade timing
- Enable both auto-upgrade and auto-repair
gke-not-using-default-service-account
Severity: high · Enforcement: advisory
Ensure GKE node pools do not use the Compute Engine default service account.
- 5.2 Node Service Accounts — Use dedicated service accounts with Workload Identity for GKE nodes.
Remediation
Fix: Use Dedicated Service Account for GKE Nodes
Create a dedicated service account with minimal permissions:
import * as gcp from "@pulumi/gcp";
// Create dedicated service account for GKE nodes
const gkeNodeSa = new gcp.serviceaccount.Account("gke-nodes", {
accountId: "gke-nodes",
displayName: "GKE Node Service Account",
});
// Grant minimal required permissions
const logWriterBinding = new gcp.projects.IAMMember("gke-log-writer", {
role: "roles/logging.logWriter",
member: pulumi.interpolate`serviceAccount:${gkeNodeSa.email}`,
});
const metricWriterBinding = new gcp.projects.IAMMember("gke-metric-writer", {
role: "roles/monitoring.metricWriter",
member: pulumi.interpolate`serviceAccount:${gkeNodeSa.email}`,
});
const monitoringViewerBinding = new gcp.projects.IAMMember("gke-monitoring-viewer", {
role: "roles/monitoring.viewer",
member: pulumi.interpolate`serviceAccount:${gkeNodeSa.email}`,
});
// Use dedicated service account in node pool
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
serviceAccount: gkeNodeSa.email,
oauthScopes: [
"https://www.googleapis.com/auth/cloud-platform",
],
},
});
The Compute Engine default service account:
- Has Editor role by default (overly permissive)
- Format:
PROJECT_NUMBER-compute@developer.gserviceaccount.com - Should not be used for GKE nodes
gke-private-endpoint-enabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have private endpoint enabled and public access disabled to restrict control plane access.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Enable Private Endpoint for GKE Cluster
Configure private cluster settings to enable private endpoint:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("private-cluster", {
name: "private-gke-cluster",
location: "us-central1",
// Enable private cluster configuration
privateClusterConfig: {
enablePrivateEndpoint: true, // Control plane only accessible via private IP
enablePrivateNodes: true, // Nodes only have private IPs
masterIpv4CidrBlock: "172.16.0.0/28", // CIDR for control plane
},
// Optional: Configure authorized networks for limited public access
masterAuthorizedNetworksConfig: {
cidrBlocks: [
{
cidrBlock: "10.0.0.0/8", // Your corporate network
displayName: "corporate-network",
},
],
},
// ... other configuration
});
Note:
enablePrivateEndpoint: trueensures the control plane is only accessible via private IPenablePrivateNodes: trueensures worker nodes only have private IPs- Use authorized networks only if you need limited public access for CI/CD or management
gke-private-nodes-enabled
Severity: high · Enforcement: advisory
Ensure GKE clusters have private nodes enabled so worker nodes only have internal IPs.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Enable Private Nodes
Configure private nodes for the cluster:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable private cluster with private nodes
privateClusterConfig: {
enablePrivateNodes: true, // Nodes only have private IPs
enablePrivateEndpoint: true, // Also recommended
masterIpv4CidrBlock: "172.16.0.0/28",
},
// Requires VPC-native cluster
ipAllocationPolicy: {
clusterSecondaryRangeName: "pods",
servicesSecondaryRangeName: "services",
},
// ... other configuration
});
Private nodes characteristics:
- Worker nodes have only internal IP addresses
- No direct internet connectivity
- Internet access via Cloud NAT or proxy
- Improved security posture
Requirements:
- VPC-native cluster (IP aliasing)
- Cloud NAT for outbound internet access
- Private Google Access for GCP API access
gke-release-channel-enabled
Severity: medium · Enforcement: advisory
Ensure GKE clusters use Release Channels for automated version management.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Enable Release Channel
Configure a release channel for the cluster:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable release channel
releaseChannel: {
channel: "REGULAR", // or "RAPID" or "STABLE"
},
// ... other configuration
});
Release channel options:
RAPID: Early access to new features (for testing)REGULAR: Balanced updates (recommended for most workloads)STABLE: Conservative updates (for stability-critical workloads)
Benefits:
- Automatic control plane and node upgrades
- Predictable upgrade schedule
- Coordinated rollout of new features
- Reduces manual version management overhead
- Ensures timely security updates
gke-secrets-encryption-kms-enabled
Severity: critical · Enforcement: advisory
Ensure GKE clusters encrypt Kubernetes secrets using Cloud KMS Customer-Managed Encryption Keys.
- 5.3 Secrets Encryption — Encrypt Kubernetes secrets using Cloud KMS customer-managed keys.
Remediation
Fix: Enable Cloud KMS Encryption for GKE Secrets
Configure database encryption with Cloud KMS for GKE cluster:
import * as gcp from "@pulumi/gcp";
// Create a Cloud KMS key ring and key
const keyRing = new gcp.kms.KeyRing("gke-keyring", {
name: "gke-keyring",
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("gke-secrets-key", {
name: "gke-secrets-key",
keyRing: keyRing.id,
purpose: "ENCRYPT_DECRYPT",
});
// Create GKE cluster with database encryption
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
databaseEncryption: {
state: "ENCRYPTED",
keyName: cryptoKey.id,
},
// ... other configuration
});
Important: Application-layer secrets encryption using Cloud KMS must be enabled to protect Kubernetes Secret resources.
gke-security-posture-enabled
Severity: medium · Enforcement: advisory
Ensure GKE clusters have Security Posture enabled for continuous security monitoring and recommendations.
- 5.10 Additional Security Controls — Disable Kubernetes Dashboard, avoid alpha clusters, and enable security posture.
Remediation
Fix: Enable GKE Security Posture
Configure Security Posture and Workload Vulnerability Scanning:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Security Posture
securityPostureConfig: {
mode: "BASIC", // or "ENTERPRISE" for more features
vulnerabilityMode: "VULNERABILITY_BASIC", // or "VULNERABILITY_ENTERPRISE"
},
// ... other configuration
});
Security Posture modes:
BASIC: Free tier with essential security insightsENTERPRISE: Advanced features (requires Security Command Center Premium)DISABLED: No security posture monitoring
Vulnerability scanning modes:
VULNERABILITY_DISABLED: No vulnerability scanningVULNERABILITY_BASIC: Basic workload vulnerability scanningVULNERABILITY_ENTERPRISE: Advanced scanning with more features
Features:
- Continuous security posture assessment
- Workload vulnerability detection
- Configuration best practice recommendations
- Integration with Security Command Center
- Compliance reporting (CIS Kubernetes Benchmark)
Benefits:
- Proactive security monitoring
- Automated vulnerability detection
- Actionable security recommendations
- Compliance insights
gke-shielded-nodes-enabled
Severity: high · Enforcement: advisory
Ensure GKE node pools have Shielded Nodes enabled for enhanced boot-level security.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Enable Shielded Nodes
Configure Shielded GKE Nodes:
import * as gcp from "@pulumi/gcp";
// Enable at cluster level
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable Shielded Nodes feature
enableShieldedNodes: true,
// ... other configuration
});
// Configure at node pool level
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
shieldedInstanceConfig: {
enableSecureBoot: true, // Recommended
enableIntegrityMonitoring: true, // Recommended
},
},
});
Shielded Nodes features:
- Secure Boot: Verifies boot components are signed
- Integrity Monitoring: Detects boot-level modifications
- vTPM: Virtual Trusted Platform Module for key storage
Benefits:
- Protects against rootkits and bootkits
- Ensures nodes boot with verified software
- Detects unauthorized boot-level changes
gke-shielded-nodes-integrity-monitoring
Severity: high · Enforcement: advisory
Ensure GKE node pools have integrity monitoring enabled for Shielded Nodes.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Enable Integrity Monitoring
Configure integrity monitoring for Shielded Nodes:
import * as gcp from "@pulumi/gcp";
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
shieldedInstanceConfig: {
enableIntegrityMonitoring: true, // Enable integrity monitoring
enableSecureBoot: true, // Also recommended
},
},
});
How integrity monitoring works:
- Measures boot components and stores in vTPM
- Compares measurements on each boot
- Reports anomalies to Cloud Monitoring
- Detects unauthorized boot-level changes
Benefits:
- Early detection of compromised nodes
- Continuous verification of node integrity
- Integration with Cloud Monitoring for alerts
gke-shielded-nodes-secure-boot
Severity: high · Enforcement: advisory
Ensure GKE node pools have Secure Boot enabled for Shielded Nodes.
- 5.5 Node Configuration — Ensure nodes use COS with containerd, auto-repair, auto-upgrade, release channels, and Shielded Nodes.
Remediation
Fix: Enable Secure Boot
Configure Secure Boot for Shielded Nodes:
import * as gcp from "@pulumi/gcp";
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
shieldedInstanceConfig: {
enableSecureBoot: true, // Enable Secure Boot
enableIntegrityMonitoring: true, // Also recommended
},
},
});
How Secure Boot works:
- Verifies digital signatures of boot components
- Only allows signed and trusted software to boot
- Prevents execution of malicious bootloaders
- Uses UEFI firmware for verification
Benefits:
- Prevents rootkits and bootkits from loading
- Ensures only trusted software runs at boot
- Provides strongest boot-time security
Note: Secure Boot may be incompatible with some kernel modules or custom configurations.
gke-vpc-flow-logs-intranode-visibility
Severity: medium · Enforcement: advisory
Ensure GKE clusters have intranode visibility enabled for pod-to-pod network observability.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Enable Intranode Visibility
Enable intranode visibility for pod-to-pod traffic monitoring:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Enable intranode visibility
enableIntranodeVisibility: true,
// ... other configuration
});
// Also enable VPC Flow Logs on the subnet
const subnet = new gcp.compute.Subnetwork("subnet", {
name: "gke-subnet",
ipCidrRange: "10.0.0.0/20",
network: network.id,
region: "us-central1",
// Enable VPC Flow Logs
logConfig: {
aggregationInterval: "INTERVAL_5_SEC",
flowSampling: 1.0, // 100% sampling
metadata: "INCLUDE_ALL_METADATA",
},
});
Intranode visibility:
- Exposes pod-to-pod traffic on same node
- Required for VPC Flow Logs to see all traffic
- Enables comprehensive network monitoring
VPC Flow Logs:
- Log network flows for analysis
- Security monitoring and forensics
- Troubleshooting connectivity issues
gke-vpc-native-cluster-required
Severity: medium · Enforcement: advisory
Ensure GKE clusters use VPC-native networking (IP aliasing) instead of routes-based networking.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Enable VPC-Native Networking for GKE Cluster
Configure IP aliasing to create a VPC-native cluster:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
network: "my-vpc",
subnetwork: "my-subnet",
// Enable VPC-native networking
ipAllocationPolicy: {
clusterSecondaryRangeName: "pods", // Secondary range for pods
servicesSecondaryRangeName: "services", // Secondary range for services
},
// ... other configuration
});
Benefits of VPC-native clusters:
- Pods get IP addresses directly from VPC subnet ranges
- Better integration with VPC features (firewall rules, VPC peering, Shared VPC)
- Required for Private GKE clusters
- Better scalability and performance
- Required for GKE Dataplane V2 and network policies
gke-web-ui-disabled
Severity: low · Enforcement: advisory
Ensure GKE clusters have the Kubernetes Dashboard (Web UI) disabled to reduce attack surface.
- 5.10 Additional Security Controls — Disable Kubernetes Dashboard, avoid alpha clusters, and enable security posture.
Remediation
Fix: Disable Kubernetes Web UI
Disable the Kubernetes Dashboard addon:
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-gke-cluster",
location: "us-central1",
// Disable Kubernetes Dashboard
addonsConfig: {
kubernetesDashboard: {
disabled: true,
},
},
// ... other configuration
});
Alternatives to Kubernetes Dashboard:
- Google Cloud Console (GKE Workloads UI)
- kubectl command-line tool
- Kubernetes IDE extensions (Lens, VS Code Kubernetes)
- Cloud-native dashboards (Grafana, etc.)
The Kubernetes Dashboard has had security vulnerabilities and is not recommended for production use.
gke-worker-node-firewall-configured
Severity: high · Enforcement: advisory
Ensure VPC firewall rules are configured to restrict access to GKE worker nodes.
- 5.6 Network Configuration — Configure VPC-native clusters, private endpoints/nodes, authorized networks, and intranode visibility.
Remediation
Fix: Configure Firewall Rules for GKE Worker Nodes
Create restrictive firewall rules for node network tags:
import * as gcp from "@pulumi/gcp";
// Allow only necessary traffic to worker nodes
const allowSshFromBastion = new gcp.compute.Firewall("allow-ssh-from-bastion", {
network: network.id,
direction: "INGRESS",
targetTags: ["gke-node"], // GKE nodes typically have this tag
sourceTags: ["bastion"],
allows: [{
protocol: "tcp",
ports: ["22"],
}],
});
// Allow health checks from GCP load balancers
const allowHealthChecks = new gcp.compute.Firewall("allow-health-checks", {
network: network.id,
direction: "INGRESS",
targetTags: ["gke-node"],
sourceRanges: [
"35.191.0.0/16", // GCP health check ranges
"130.211.0.0/22",
],
allows: [{
protocol: "tcp",
ports: ["10256"], // kube-proxy health check
}],
});
// Deny all other inbound traffic (implicit, but can be explicit)
const denyAll = new gcp.compute.Firewall("deny-all-to-nodes", {
network: network.id,
direction: "INGRESS",
priority: 1000,
targetTags: ["gke-node"],
sourceRanges: ["0.0.0.0/0"],
denies: [{
protocol: "all",
}],
});
Best practices:
- Use private nodes to eliminate public IP exposure
- Restrict SSH access to bastion hosts
- Allow only necessary health check traffic
- Use network tags to target GKE nodes
- Review and minimize NodePort services
gke-workload-identity-enabled
Severity: high · Enforcement: advisory
Ensure GKE node pools have Workload Identity enabled (GKE_METADATA mode) for secure pod-to-GCP authentication.
- 5.2 Node Service Accounts — Use dedicated service accounts with Workload Identity for GKE nodes.
Remediation
Fix: Enable Workload Identity on Node Pools
Configure node pools to use GKE Metadata Server:
import * as gcp from "@pulumi/gcp";
// Cluster must have Workload Identity enabled first
const cluster = new gcp.container.Cluster("cluster", {
workloadIdentityConfig: {
workloadPool: "my-project.svc.id.goog",
},
});
// Enable Workload Identity on node pool
const nodePool = new gcp.container.NodePool("pool", {
cluster: cluster.name,
nodeConfig: {
workloadMetadataConfig: {
mode: "GKE_METADATA", // Required for Workload Identity
},
},
});
// Bind Kubernetes SA to GCP SA
const k8sSa = new k8s.core.v1.ServiceAccount("app-sa", {
metadata: {
name: "app-sa",
namespace: "default",
annotations: {
"iam.gke.io/gcp-service-account": gcpSa.email,
},
},
});
const binding = new gcp.serviceaccount.IAMBinding("workload-identity-binding", {
serviceAccountId: gcpSa.name,
role: "roles/iam.workloadIdentityUser",
members: [
"serviceAccount:my-project.svc.id.goog[default/app-sa]",
],
});
Benefits:
- Pods authenticate as GCP service accounts without keys
- Fine-grained IAM permissions per workload
- Automatic credential rotation
k8s-cluster-admin-role-binding-minimized
Severity: high · Enforcement: advisory
Ensure cluster-admin ClusterRole is bound to a minimal number of users and service accounts.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Minimize cluster-admin Role Bindings
Use more specific roles instead of cluster-admin:
// Instead of binding cluster-admin, create specific roles
const role = new k8s.rbac.v1.Role("specificRole", {
metadata: { namespace: "default" },
rules: [{
apiGroups: [""],
resources: ["pods"],
verbs: ["get", "list", "watch"],
}],
});
const roleBinding = new k8s.rbac.v1.RoleBinding("specificBinding", {
metadata: { namespace: "default" },
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "Role",
name: role.metadata.name,
},
subjects: [{
kind: "User",
name: "specific-user",
}],
});
k8s-default-namespace-not-used
Severity: medium · Enforcement: advisory
Ensure workloads are not deployed to the default namespace.
- 4.6 Pod Security — Apply RuntimeDefault seccomp profile to all workloads and ensure default namespace is not used.
Remediation
Fix: Use Dedicated Namespaces
Create and use dedicated namespaces:
const namespace = new k8s.core.v1.Namespace("app-namespace", {
metadata: {
name: "my-app",
},
});
const deployment = new k8s.apps.v1.Deployment("app", {
metadata: {
namespace: namespace.metadata.name,
},
// ... rest of deployment spec
});
k8s-default-service-accounts-not-used
Severity: medium · Enforcement: advisory
Ensure workloads do not use the default service account.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Use Dedicated Service Accounts
Create and use dedicated service accounts for workloads:
const serviceAccount = new k8s.core.v1.ServiceAccount("appServiceAccount", {
metadata: {
namespace: "default",
name: "my-app-sa",
},
});
const deployment = new k8s.apps.v1.Deployment("app", {
spec: {
template: {
spec: {
serviceAccountName: serviceAccount.metadata.name,
// ... rest of pod spec
},
},
},
});
k8s-pod-security-allow-privilege-escalation-minimized
Severity: high · Enforcement: advisory
Ensure containers do not allow privilege escalation.
- 4.2 Pod Security Standards — Enforce Pod Security Standard Baseline profile to prevent privileged containers and host namespace access.
Remediation
Fix: Disable Privilege Escalation
Set allowPrivilegeEscalation to false in container security context:
const pod = new k8s.core.v1.Pod("pod", {
spec: {
containers: [{
name: "app",
image: "nginx",
securityContext: {
allowPrivilegeEscalation: false,
runAsNonRoot: true,
capabilities: {
drop: ["ALL"],
},
},
}],
},
});
k8s-pod-security-host-ipc-minimized
Severity: high · Enforcement: advisory
Ensure containers do not share the host IPC namespace.
- 4.2 Pod Security Standards — Enforce Pod Security Standard Baseline profile to prevent privileged containers and host namespace access.
Remediation
Fix: Disable Host IPC Sharing
Remove hostIPC from pod spec:
const pod = new k8s.core.v1.Pod("pod", {
spec: {
hostIPC: false, // Explicitly set to false or remove
containers: [{
name: "app",
image: "nginx",
}],
},
});
k8s-pod-security-host-network-minimized
Severity: high · Enforcement: advisory
Ensure containers do not share the host network namespace.
- 4.2 Pod Security Standards — Enforce Pod Security Standard Baseline profile to prevent privileged containers and host namespace access.
Remediation
Fix: Disable Host Network Sharing
Remove hostNetwork from pod spec:
const pod = new k8s.core.v1.Pod("pod", {
spec: {
hostNetwork: false, // Explicitly set to false or remove
containers: [{
name: "app",
image: "nginx",
}],
},
});
k8s-pod-security-host-pid-minimized
Severity: high · Enforcement: advisory
Ensure containers do not share the host process ID namespace.
- 4.2 Pod Security Standards — Enforce Pod Security Standard Baseline profile to prevent privileged containers and host namespace access.
Remediation
Fix: Disable Host PID Sharing
Remove hostPID from pod spec:
const pod = new k8s.core.v1.Pod("pod", {
spec: {
hostPID: false, // Explicitly set to false or remove
containers: [{
name: "app",
image: "nginx",
}],
},
});
k8s-pod-security-privileged-containers-minimized
Severity: critical · Enforcement: advisory
Ensure containers do not run in privileged mode.
- 4.2 Pod Security Standards — Enforce Pod Security Standard Baseline profile to prevent privileged containers and host namespace access.
Remediation
Fix: Disable Privileged Mode
Remove privileged flag from container security context:
const pod = new k8s.core.v1.Pod("pod", {
spec: {
containers: [{
name: "app",
image: "nginx",
securityContext: {
privileged: false, // Explicitly set to false or remove
runAsNonRoot: true,
capabilities: {
drop: ["ALL"],
},
},
}],
},
});
k8s-rbac-bind-impersonate-escalate-minimized
Severity: critical · Enforcement: advisory
Ensure Roles and ClusterRoles do not grant dangerous permissions: bind, impersonate, and escalate, which can lead to privilege escalation.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Remove Dangerous RBAC Permissions
Avoid granting bind, impersonate, and escalate permissions:
// Bad: DON'T grant these dangerous permissions
const dangerousRole = new k8s.rbac.v1.ClusterRole("dangerous", {
rules: [{
apiGroups: ["rbac.authorization.k8s.io"],
resources: ["clusterroles", "roles"],
verbs: ["bind", "escalate"], // DON'T DO THIS
}, {
apiGroups: [""],
resources: ["users", "groups", "serviceaccounts"],
verbs: ["impersonate"], // DON'T DO THIS
}],
});
// Good: Grant only necessary read permissions
const safeRole = new k8s.rbac.v1.ClusterRole("safe", {
rules: [{
apiGroups: ["rbac.authorization.k8s.io"],
resources: ["clusterroles", "roles"],
verbs: ["get", "list", "watch"], // Read-only
}],
});
Why these are dangerous:
bind: Allows binding roles with more permissions than the user hasimpersonate: Allows acting as another user/service accountescalate: Allows creating roles with more permissions than the user has
k8s-rbac-secret-access-minimized
Severity: critical · Enforcement: advisory
Ensure Roles and ClusterRoles do not grant excessive permissions to secrets (create, update, patch, delete, or wildcard verbs).
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Minimize Secret Access Permissions
Limit secret access to read-only (get, list, watch) unless write access is explicitly required:
// Good: Read-only secret access
const readOnlyRole = new k8s.rbac.v1.Role("secretReader", {
metadata: { namespace: "default" },
rules: [{
apiGroups: [""],
resources: ["secrets"],
verbs: ["get", "list", "watch"], // Read-only access
}],
});
// Bad: Avoid granting write access to secrets
const writeRole = new k8s.rbac.v1.Role("secretWriter", {
rules: [{
apiGroups: [""],
resources: ["secrets"],
verbs: ["*"], // DON'T DO THIS
}],
});
Only grant create/update/delete/patch permissions when absolutely necessary and document the justification.
k8s-rbac-system-anonymous-bindings-minimized
Severity: high · Enforcement: advisory
Ensure that RoleBindings and ClusterRoleBindings do not grant permissions to system:anonymous unless absolutely necessary.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Minimize system:anonymous Bindings
Remove bindings to system:anonymous unless required for specific health check endpoints:
// Bad: Granting permissions to anonymous users
const badBinding = new k8s.rbac.v1.ClusterRoleBinding("anonymous-access", {
subjects: [{
kind: "User",
name: "system:anonymous", // DON'T DO THIS
}],
roleRef: {
kind: "ClusterRole",
name: "view",
apiGroup: "rbac.authorization.k8s.io",
},
});
// Good: Require authentication
const goodBinding = new k8s.rbac.v1.ClusterRoleBinding("authenticated-access", {
subjects: [{
kind: "Group",
name: "system:authenticated", // Only authenticated users
}],
roleRef: {
kind: "ClusterRole",
name: "view",
apiGroup: "rbac.authorization.k8s.io",
},
});
// Acceptable: Very limited anonymous access for health checks only
const healthCheckRole = new k8s.rbac.v1.ClusterRole("healthcheck", {
rules: [{
nonResourceURLs: ["/healthz", "/readyz"],
verbs: ["get"],
}],
});
Anonymous access bypasses authentication and should be avoided in production clusters.
k8s-rbac-system-authenticated-bindings-minimized
Severity: medium · Enforcement: advisory
Ensure that RoleBindings and ClusterRoleBindings do not grant excessive permissions to system:authenticated group beyond default bindings.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Minimize system:authenticated Bindings
Use specific groups or service accounts instead of granting permissions to all authenticated users:
// Bad: Granting broad permissions to all authenticated users
const badBinding = new k8s.rbac.v1.ClusterRoleBinding("all-users-admin", {
subjects: [{
kind: "Group",
name: "system:authenticated", // DON'T DO THIS for broad roles
}],
roleRef: {
kind: "ClusterRole",
name: "admin", // Too broad
apiGroup: "rbac.authorization.k8s.io",
},
});
// Good: Use specific groups
const goodBinding = new k8s.rbac.v1.ClusterRoleBinding("dev-team-access", {
subjects: [{
kind: "Group",
name: "developers@example.com", // Specific group
}],
roleRef: {
kind: "ClusterRole",
name: "view",
apiGroup: "rbac.authorization.k8s.io",
},
});
// Good: Use service accounts for workload access
const saBinding = new k8s.rbac.v1.RoleBinding("app-access", {
metadata: { namespace: "production" },
subjects: [{
kind: "ServiceAccount",
name: "app-service-account",
namespace: "production",
}],
roleRef: {
kind: "Role",
name: "app-role",
apiGroup: "rbac.authorization.k8s.io",
},
});
Default allowed bindings:
system:basic-user- Allows basic self-discoverysystem:discovery- Allows API discovery
Binding to system:authenticated grants access to ALL users who can authenticate, including service accounts.
k8s-rbac-system-masters-group-avoided
Severity: critical · Enforcement: advisory
Ensure that RoleBindings and ClusterRoleBindings do not bind to the system:masters group.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Avoid system:masters Group
Remove bindings to the system:masters group and use more granular RBAC permissions:
// Bad: Binding to system:masters
const badBinding = new k8s.rbac.v1.ClusterRoleBinding("admin-binding", {
subjects: [{
kind: "Group",
name: "system:masters", // DON'T DO THIS
}],
roleRef: {
kind: "ClusterRole",
name: "cluster-admin",
apiGroup: "rbac.authorization.k8s.io",
},
});
// Good: Create specific roles with limited permissions
const limitedRole = new k8s.rbac.v1.ClusterRole("limited-admin", {
rules: [{
apiGroups: ["apps"],
resources: ["deployments"],
verbs: ["get", "list", "watch"],
}],
});
const goodBinding = new k8s.rbac.v1.ClusterRoleBinding("limited-binding", {
subjects: [{
kind: "User",
name: "specific-user@example.com", // Bind to specific users
}],
roleRef: {
kind: "ClusterRole",
name: limitedRole.metadata.name,
apiGroup: "rbac.authorization.k8s.io",
},
});
The system:masters group bypasses all authorization checks and should only be used for break-glass scenarios.
k8s-rbac-system-unauthenticated-bindings-minimized
Severity: high · Enforcement: advisory
Ensure that RoleBindings and ClusterRoleBindings do not grant permissions to system:unauthenticated beyond default discovery bindings.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Minimize system:unauthenticated Bindings
Remove non-default bindings to system:unauthenticated group:
// Bad: Granting permissions to unauthenticated users
const badBinding = new k8s.rbac.v1.ClusterRoleBinding("unauthenticated-access", {
subjects: [{
kind: "Group",
name: "system:unauthenticated", // DON'T DO THIS (unless for discovery)
}],
roleRef: {
kind: "ClusterRole",
name: "view",
apiGroup: "rbac.authorization.k8s.io",
},
});
// Good: Require authentication
const goodBinding = new k8s.rbac.v1.ClusterRoleBinding("authenticated-access", {
subjects: [{
kind: "Group",
name: "system:authenticated", // Only authenticated users
}],
roleRef: {
kind: "ClusterRole",
name: "view",
apiGroup: "rbac.authorization.k8s.io",
},
});
Default allowed bindings:
system:public-info-viewer- Allows read-only access to API version infosystem:discovery- Allows read-only access to API discovery endpoints
Any other bindings to system:unauthenticated should be carefully reviewed and justified.
k8s-rbac-wildcard-use-minimized
Severity: high · Enforcement: advisory
Ensure Roles and ClusterRoles do not use wildcards for resources, verbs, or apiGroups.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Remove Wildcards from RBAC Rules
Be specific about resources, verbs, and API groups:
const role = new k8s.rbac.v1.Role("specificRole", {
metadata: { namespace: "default" },
rules: [{
apiGroups: [""], // Specific API group, not "*"
resources: ["pods", "services"], // Specific resources, not "*"
verbs: ["get", "list", "watch"], // Specific verbs, not "*"
}],
});
k8s-seccomp-runtime-default-required
Severity: medium · Enforcement: advisory
Ensure that all pods have seccomp profile set to RuntimeDefault.
- 4.6 Pod Security — Apply RuntimeDefault seccomp profile to all workloads and ensure default namespace is not used.
Remediation
Fix: Apply RuntimeDefault Seccomp Profile
Set the seccomp profile to RuntimeDefault at the pod or container level:
// Good: Pod-level seccomp (applies to all containers)
const pod = new k8s.core.v1.Pod("secure-pod", {
spec: {
securityContext: {
seccompProfile: {
type: "RuntimeDefault",
},
},
containers: [{
name: "app",
image: "nginx",
}],
},
});
// Good: Container-level seccomp
const deployment = new k8s.apps.v1.Deployment("secure-deployment", {
spec: {
template: {
spec: {
containers: [{
name: "app",
image: "nginx",
securityContext: {
seccompProfile: {
type: "RuntimeDefault",
},
},
}],
},
},
},
});
// Bad: No seccomp profile or Unconfined
const insecurePod = new k8s.core.v1.Pod("insecure", {
spec: {
securityContext: {
seccompProfile: {
type: "Unconfined", // DON'T DO THIS
},
},
containers: [{ name: "app", image: "nginx" }],
},
});
Seccomp profile types:
RuntimeDefault: Uses container runtime’s default profile (required by CIS)Localhost: Uses custom profile from node filesystem (not allowed by this policy)Unconfined: Disables seccomp (not allowed)
k8s-service-account-token-mounted-minimized
Severity: medium · Enforcement: advisory
Ensure service account tokens are not automatically mounted in pods unless explicitly required.
- 4.1 RBAC and Authentication — Implement proper RBAC controls to restrict system group bindings and minimize permissions.
Remediation
Fix: Disable Service Account Token Auto-Mounting
Set automountServiceAccountToken: false unless the pod needs Kubernetes API access:
// Good: Disable auto-mounting when not needed
const pod = new k8s.core.v1.Pod("myPod", {
metadata: { name: "my-pod" },
spec: {
automountServiceAccountToken: false, // Explicitly disable
containers: [{
name: "app",
image: "nginx:latest",
}],
},
});
// Also disable at ServiceAccount level
const sa = new k8s.core.v1.ServiceAccount("myServiceAccount", {
metadata: { name: "my-sa" },
automountServiceAccountToken: false, // Disable by default
});
Only enable when the pod explicitly requires Kubernetes API access.
k8s-trusted-images-only
Severity: high · Enforcement: advisory
Ensure that only container images from trusted registries are used, and that specific image tags (not :latest) are specified.
- 5.1 Container Images — Ensure IAM controls and trusted image sources.
Remediation
Fix: Use Trusted Container Images
Use images from trusted registries with specific tags or digests:
// Good: Trusted registry with specific tag
const deployment = new k8s.apps.v1.Deployment("app", {
spec: {
template: {
spec: {
containers: [{
name: "app",
image: "gcr.io/my-project/app:v1.2.3", // Specific version
}],
},
},
},
});
// Best: Use image digest for immutability
const immutableDeployment = new k8s.apps.v1.Deployment("app", {
spec: {
template: {
spec: {
containers: [{
name: "app",
image: "gcr.io/my-project/app@sha256:abc123...", // Image digest
}],
},
},
},
});
// Bad: Untrusted registry or :latest tag
const badDeployment = new k8s.apps.v1.Deployment("app", {
spec: {
template: {
spec: {
containers: [{
name: "app",
image: "random-registry.com/app:latest", // DON'T DO THIS
}],
},
},
},
});
Configure trusted registries in your policy pack:
new PolicyPack("my-pack", {
policies: [
{
...k8sTrustedImagesOnlyPolicy,
configSchema: {
trustedRegistries: [
"123456789012.dkr.ecr.us-east-1.amazonaws.com", // Your ECR
"gcr.io/my-project", // Your GCR
"mycompany.azurecr.io", // Your ACR
],
},
},
],
});
Default trusted registries include:
- Google Container Registry (gcr.io, pkg.dev)
- AWS ECR (.dkr.ecr..amazonaws.com, public.ecr.aws)
- Azure Container Registry (*.azurecr.io)
- Official Kubernetes registries (registry.k8s.io, k8s.gcr.io)
- Docker Hub official images (docker.io/library)
- GitHub Container Registry (ghcr.io)
- Quay.io (quay.io)
- Microsoft Container Registry (mcr.microsoft.com)