From 8ab7c4e430b000fb4f321254576bc26c8c72a591 Mon Sep 17 00:00:00 2001 From: Laiba Bareera Date: Thu, 14 May 2026 19:58:28 +0500 Subject: [PATCH 1/3] add benchmark for gke-1.9 --- cfg/config.yaml | 7 + cfg/gke-1.9.0/config.yaml | 9 + cfg/gke-1.9.0/controlplane.yaml | 6 + cfg/gke-1.9.0/managedservices.yaml | 751 +++++++++++++++++++++++++++++ cfg/gke-1.9.0/master.yaml | 6 + cfg/gke-1.9.0/node.yaml | 65 +++ cfg/gke-1.9.0/policies.yaml | 511 ++++++++++++++++++++ cmd/util.go | 6 +- cmd/util_test.go | 11 +- 9 files changed, 1368 insertions(+), 4 deletions(-) create mode 100644 cfg/gke-1.9.0/config.yaml create mode 100644 cfg/gke-1.9.0/controlplane.yaml create mode 100644 cfg/gke-1.9.0/managedservices.yaml create mode 100644 cfg/gke-1.9.0/master.yaml create mode 100644 cfg/gke-1.9.0/node.yaml create mode 100644 cfg/gke-1.9.0/policies.yaml diff --git a/cfg/config.yaml b/cfg/config.yaml index 74f0f359e..7d3a231c5 100644 --- a/cfg/config.yaml +++ b/cfg/config.yaml @@ -308,6 +308,7 @@ version_mapping: "gke-1.2.0": "gke-1.2.0" "gke-1.6.0": "gke-1.6.0" "gke-1.8.0": "gke-1.8.0" + "gke-1.9.0": "gke-1.9.0" "ocp-3.10": "rh-0.7" "ocp-3.11": "rh-0.7" "ocp-4.0": "rh-1.0" @@ -435,6 +436,12 @@ target_mapping: - "controlplane" - "policies" - "managedservices" + "gke-1.9.0": + - "master" + - "node" + - "controlplane" + - "policies" + - "managedservices" "eks-1.0.1": - "master" - "node" diff --git a/cfg/gke-1.9.0/config.yaml b/cfg/gke-1.9.0/config.yaml new file mode 100644 index 000000000..d9ab6f274 --- /dev/null +++ b/cfg/gke-1.9.0/config.yaml @@ -0,0 +1,9 @@ +--- +## Version-specific settings that override the values in cfg/config.yaml + +node: + proxy: + defaultkubeconfig: "/var/lib/kubelet/kubeconfig" + + kubelet: + defaultconf: "/etc/kubernetes/kubelet-config.yaml" diff --git a/cfg/gke-1.9.0/controlplane.yaml b/cfg/gke-1.9.0/controlplane.yaml new file mode 100644 index 000000000..9c06acc32 --- /dev/null +++ b/cfg/gke-1.9.0/controlplane.yaml @@ -0,0 +1,6 @@ +--- +controls: +version: "gke-1.9.0" +id: 2 +text: "Control Plane Configuration" +type: "controlplane" diff --git a/cfg/gke-1.9.0/managedservices.yaml b/cfg/gke-1.9.0/managedservices.yaml new file mode 100644 index 000000000..eb6b2a2ab --- /dev/null +++ b/cfg/gke-1.9.0/managedservices.yaml @@ -0,0 +1,751 @@ +--- +controls: +version: "gke-1.9.0" +id: 5 +text: "Managed Services" +type: "managedservices" +groups: + - id: 5.1 + text: "Image Registry and Image Scanning" + checks: + - id: 5.1.1 + text: "Ensure Image Vulnerability Scanning is enabled (Automated)" + audit: "/bin/sh -c 'if gcloud services list --enabled | grep -q containerscanning.googleapis.com; then echo enabled; else echo disabled; fi'" + tests: + test_items: + - flag: enabled + remediation: | + For Images Hosted in GCR: + Using Google Cloud Console + Go to GCR by visiting: https://console.cloud.google.com/gcr + Select Settings and, under the Vulnerability Scanning heading, click the TURN ON button. + Using Command Line + gcloud services enable containeranalysis.googleapis.com + For Images Hosted in AR: + Using Google Cloud Console + Go to GCR by visiting: https://console.cloud.google.com/artifacts + Select Settings and, under the Vulnerability Scanning heading, click the ENABLE button. + Using Command Line + gcloud services enable containerscanning.googleapis.com + scored: true + + - id: 5.1.2 + text: "Minimize user access to Container Image repositories (Manual)" + audit: | + gcloud projects get-iam-policy \ + --flatten="bindings[].members" \ + --format='table(bindings.members,bindings.role)' \ + --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner" + type: "manual" + remediation: | + For Images Hosted in AR: + Using Command Line: + + gcloud artifacts repositories set-iam-policy \ + --location + + To learn how to configure policy files see: https://cloud.google.com/artifact-registry/docs/access-control#grant + + For Images Hosted in GCR: + Using Command Line: + To change roles at the GCR bucket level: + Firstly, run the following if read permissions are required: + + gsutil iam ch ::objectViewer gs://artifacts..appspot.com + + Then remove the excessively privileged role (Storage Admin / Storage Object + Admin / Storage Object Creator) using: + + gsutil iam ch -d :: gs://artifacts..appspot.com + + where: + can be one of the following: + user, if the is a Google account. + serviceAccount, if specifies a Service account. + can be one of the following: + a Google account (for example, someone@example.com). + a Cloud IAM service account. + + To modify roles defined at the project level and subsequently inherited within the GCR + bucket, or the Service Account User role, extract the IAM policy file, modify it + accordingly and apply it using: + + gcloud projects set-iam-policy + scored: false + + - id: 5.1.3 + text: "Minimize cluster access to read-only for Container Image repositories (Manual)" + audit: | + gcloud projects get-iam-policy \ + --flatten="bindings[].members" \ + --format='table(bindings.members,bindings.role)' \ + --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner" + type: "manual" + remediation: | + For Images Hosted in AR: + Using Command Line: + Add artifactregistry.reader role + + gcloud artifacts repositories add-iam-policy-binding \ + --location= \ + --member='serviceAccount:' \ + --role='roles/artifactregistry.reader' + + Remove any roles other than artifactregistry.reader + + gcloud artifacts repositories remove-iam-policy-binding \ + --location \ + --member='serviceAccount:' \ + --role='' + + For Images Hosted in GCR: + For an account explicitly granted to the bucket: + Firstly add read access to the Kubernetes Service Account: + + gsutil iam ch ::objectViewer gs://artifacts..appspot.com + + where: + can be one of the following: + user, if the is a Google account. + serviceAccount, if specifies a Service account. + can be one of the following: + a Google account (for example, someone@example.com). + a Cloud IAM service account. + + Then remove the excessively privileged role (Storage Admin / Storage Object + Admin / Storage Object Creator) using: + + gsutil iam ch -d :: gs://artifacts..appspot.com + + For an account that inherits access to the GCR Bucket through Project level + permissions, modify the Projects IAM policy file accordingly, then upload it using: + + gcloud projects set-iam-policy + scored: false + + - id: 5.1.4 + text: "Ensure only trusted container images are used (Manual)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq .binaryAuthorization + type: "manual" + remediation: | + Using Command Line: + Update the cluster to enable Binary Authorization: + + gcloud container cluster update --location --project --binauthz-evaluation-mode= + scored: false + + - id: 5.2 + text: "Identity and Access Management (IAM)" + checks: + - id: 5.2.1 + text: "Ensure GKE clusters are not running using the Compute Engine default service account (Automated)" + audit: | + /bin/bash -c " + # Check for required variables + if [[ -z \$PROJECT_ID ]] || [[ -z \$CLUSTER_NAME ]] || [[ -z \$LOCATION ]]; then + echo 'MISSING_REQUIRED_VARIABLES' + exit 1 + fi + + # Get NODE_POOL if not already set + if [[ -z \$NODE_POOL ]]; then + NODE_POOL=\$(gcloud container clusters describe \$CLUSTER_NAME --location \$LOCATION --project \$PROJECT_ID --format='value(nodePools[0].name)' 2>/dev/null) + [[ -z \$NODE_POOL ]] && { echo 'FAILED_NODE_POOL'; exit 1; } + fi + + # Get service account from node pool + SA=\$(gcloud container node-pools describe \$NODE_POOL --cluster \$CLUSTER_NAME --location \$LOCATION --project \$PROJECT_ID --format='value(config.serviceAccount)' 2>/dev/null) + [[ -z \$SA ]] && { echo 'FAILED_SERVICE_ACCOUNT'; exit 1; } + + # Check if service account is 'default' + [[ \"\$SA\" == \"default\" ]] && echo 'DEFAULT_SA_IN_USE' || echo 'DEFAULT_SA_NOT_IN_USE' + " + tests: + test_items: + - flag: "DEFAULT_SA_NOT_IN_USE" + set: true + compare: + op: eq + value: "DEFAULT_SA_NOT_IN_USE" + remediation: | + Using Command Line: + To create a minimally privileged service account: + + gcloud iam service-accounts create \ + --display-name "GKE Node Service Account" + export NODE_SA_EMAIL=gcloud iam service-accounts list \ + --format='value(email)' --filter='displayName:GKE Node Service Account' + + Grant the following roles to the service account: + + export PROJECT_ID=gcloud config get-value project + gcloud projects add-iam-policy-binding --member \ + serviceAccount: --role roles/monitoring.metricWriter + gcloud projects add-iam-policy-binding --member \ + serviceAccount: --role roles/monitoring.viewer + gcloud projects add-iam-policy-binding --member \ + serviceAccount: --role roles/logging.logWriter + + To create a new Node pool using the Service account, run the following command: + + gcloud container node-pools create \ + --service-account=@.iam.gserviceaccount.com \ + --cluster= --zone + + Note: The workloads will need to be migrated to the new Node pool, and the old node + pools that use the default service account should be deleted to complete the + remediation. + scored: true + + - id: 5.2.2 + text: "Prefer using dedicated GCP Service Accounts and Workload Identity (Manual)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq .workloadIdentityConfig + type: "manual" + remediation: | + Using Command Line: + + gcloud container clusters update --location \ + --workload-pool .svc.id.goog + + Note that existing Node pools are unaffected. New Node pools default to --workload- + metadata-from-node=GKE_METADATA_SERVER. + + Then, modify existing Node pools to enable GKE_METADATA_SERVER: + + gcloud container node-pools update --cluster \ + --location --workload-metadata=GKE_METADATA + + Workloads may need to be modified in order for them to use Workload Identity as + described within: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity. + Also consider the effects on the availability of hosted workloads as Node pools + are updated. It may be more appropriate to create new Node Pools. + scored: false + + - id: 5.3 + text: "Cloud Key Management Service (Cloud KMS)" + checks: + - id: 5.3.1 + text: "Ensure Kubernetes Secrets are encrypted using keys managed in Cloud KMS (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.databaseEncryption' + type: "manual" + remediation: | + To create a key: + Create a key ring: + + gcloud kms keyrings create --location --project \ + + + Create a key: + + gcloud kms keys create --location --keyring \ + --purpose encryption --project + + Grant the Kubernetes Engine Service Agent service account the Cloud KMS + CryptoKey Encrypter/Decrypter role: + + gcloud kms keys add-iam-policy-binding --location \ + --keyring --member serviceAccount: \ + --role roles/cloudkms.cryptoKeyEncrypterDecrypter --project + + To create a new cluster with Application-layer Secrets Encryption: + + gcloud container clusters create --cluster-version=latest \ + --zone \ + --database-encryption-key projects//locations//keyRings//cryptoKeys/ \ + --project + + To enable on an existing cluster: + + gcloud container clusters update --zone \ + --database-encryption-key projects//locations//keyRings//cryptoKeys/ \ + --project + scored: false + + - id: 5.4 + text: "Node Metadata" + checks: + - id: 5.4.1 + text: "Ensure the GKE Metadata Server is Enabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --format json | jq '.nodePools[].config.workloadMetadataConfig' + type: "manual" + remediation: | + Using Command Line: + + gcloud container clusters update --identity-namespace=.svc.id.goog + + Note that existing Node pools are unaffected. New Node pools default to --workload- + metadata-from-node=GKE_METADATA_SERVER. + + To modify an existing Node pool to enable GKE Metadata Server: + + gcloud container node-pools update --cluster= \ + --workload-metadata-from-node=GKE_METADATA_SERVER + + Workloads may need modification in order for them to use Workload Identity as + described within: https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity. + scored: false + + - id: 5.5 + text: "Node Configuration and Maintenance" + checks: + - id: 5.5.1 + text: "Ensure Container-Optimized OS (cos_containerd) is used for GKE node images (Automated)" + audit: | + gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.config.imageType' + type: "manual" + remediation: | + Using Command Line: + To set the node image to cos for an existing cluster's Node pool: + + gcloud container clusters upgrade --image-type cos_containerd \ + --location --node-pool + scored: false + + - id: 5.5.2 + text: "Ensure Node Auto-Repair is enabled for GKE nodes (Automated)" + audit: | + gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.management' + type: "manual" + remediation: | + Using Command Line: + To enable node auto-repair for an existing cluster's Node pool: + + gcloud container node-pools update --cluster \ + --location --enable-autorepair + scored: false + + - id: 5.5.3 + text: "Ensure Node Auto-Upgrade is enabled for GKE nodes (Automated)" + audit: | + gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.management' + type: "manual" + remediation: | + Using Command Line: + To enable node auto-upgrade for an existing cluster's Node pool, run the following + command: + + gcloud container node-pools update --cluster \ + --location --enable-autoupgrade + scored: false + + - id: 5.5.4 + text: "When creating New Clusters - Automate GKE version management using Release Channels (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq .releaseChannel.channel + type: "manual" + remediation: | + Using Command Line: + Create a new cluster by running the following command: + + gcloud container clusters create --location --release-channel + + where is stable or regular, according to requirements. + scored: false + + - id: 5.5.5 + text: "Ensure Shielded GKE Nodes are Enabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.shieldedNodes' + type: "manual" + remediation: | + Using Command Line: + To migrate an existing cluster, the flag --enable-shielded-nodes needs to be + specified in the cluster update command: + + gcloud container clusters update --location --enable-shielded-nodes + scored: false + + - id: 5.5.6 + text: "Ensure Integrity Monitoring for Shielded GKE Nodes is Enabled (Automated)" + audit: | + gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq .config.shieldedInstanceConfig + type: "manual" + remediation: | + Using Command Line: + To create a Node pool within the cluster with Integrity Monitoring enabled, run the + following command: + + gcloud container node-pools create --cluster --location --shielded-integrity-monitoring + + Workloads from existing non-conforming Node pools will need to be migrated to the + newly created Node pool, then delete non-conforming Node pools to complete the + remediation + scored: false + + - id: 5.5.7 + text: "Ensure Secure Boot for Shielded GKE Nodes is Enabled (Automated)" + audit: | + gcloud container node-pools describe $POOL_NAME --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq .config.shieldedInstanceConfig + type: "manual" + remediation: | + Using Command Line: + To create a Node pool within the cluster with Secure Boot enabled, run the following + command: + + gcloud container node-pools create --cluster --location --shielded-secure-boot + + + Workloads will need to be migrated from existing non-conforming Node pools to the + newly created Node pool, then delete the non-conforming pools. + scored: false + + - id: 5.6 + text: "Cluster Networking" + checks: + - id: 5.6.1 + text: "Enable VPC Flow Logs and Intranode Visibility (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.networkConfig.enableIntraNodeVisibility' + type: "manual" + remediation: | + Using Command Line: + 1. Find the subnetwork name associated with the cluster. + + gcloud container clusters describe \ + --location --format json | jq '.subnetwork' + + 2. Update the subnetwork to enable VPC Flow Logs. + gcloud compute networks subnets update --enable-flow-logs + scored: false + + - id: 5.6.2 + text: "Ensure use of VPC-native clusters (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.ipAllocationPolicy.useIpAliases' + type: "manual" + remediation: | + Using Command Line: + To enable Alias IP on a new cluster, run the following command: + + gcloud container clusters create --location \ + --enable-ip-alias + scored: false + + - id: 5.6.3 + text: "Ensure Control Plane Authorized Networks is Enabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.masterAuthorizedNetworksConfig' + type: "manual" + remediation: | + Using Command Line: + To enable Control Plane Authorized Networks for an existing cluster, run the following + command: + + gcloud container clusters update --location \ + --enable-master-authorized-networks + + Along with this, you can list authorized networks using the --master-authorized-networks + flag which contains a list of up to 20 external networks that are allowed to + connect to your cluster's control plane through HTTPS. You provide these networks as + a comma-separated list of addresses in CIDR notation (such as 90.90.100.0/24). + scored: false + + - id: 5.6.4 + text: "Ensure clusters are created with Private Endpoint Enabled and Public Access Disabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.privateClusterConfig.enablePrivateEndpoint' + type: "manual" + remediation: | + Using Command Line: + Create a cluster with a Private Endpoint enabled and Public Access disabled by including + the --enable-private-endpoint flag within the cluster create command: + + gcloud container clusters create --enable-private-endpoint + + Setting this flag also requires the setting of --enable-private-nodes, --enable-ip-alias + and --master-ipv4-cidr=. + scored: false + + - id: 5.6.5 + text: "Ensure clusters are created with Private Nodes (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.privateClusterConfig.enablePrivateNodes' + type: "manual" + remediation: | + Using Command Line: + To create a cluster with Private Nodes enabled, include the --enable-private-nodes + flag within the cluster create command: + + gcloud container clusters create --enable-private-nodes + + Setting this flag also requires the setting of --enable-ip-alias and + --master-ipv4-cidr=. + scored: false + + - id: 5.6.6 + text: "Consider firewalling GKE worker nodes (Manual)" + audit: | + gcloud compute instances describe $CLUSTER_NAME --zone $LOCATION --project $PROJECT_ID --format json | \ + jq '{tags: .tags.items[], serviceaccount:.serviceAccounts[].email, network: .networkInterfaces[].network}' + type: "manual" + remediation: | + Using Command Line: + Use the following command to generate firewall rules, setting the variables as + appropriate: + + gcloud compute firewall-rules create \ + --network --priority --direction \ + --action --target-tags \ + --target-service-accounts \ + --source-ranges --source-tags \ + --source-service-accounts \ + --destination-ranges --rules + scored: false + + - id: 5.6.7 + text: "Ensure use of Google-managed SSL Certificates (Automated)" + audit: | + svc_json="$(kubectl get svc -A -o json 2>/dev/null || echo '{"items":[],"__err":"SVC_FORBIDDEN"}')" + ing_json="$(kubectl get ingress -A -o json 2>/dev/null || echo '{"items":[],"__err":"INGRESS_FORBIDDEN"}')" + mc_json="$(kubectl get managedcertificates -A -o json 2>/dev/null || echo '{"items":[],"__err":"MC_FORBIDDEN"}')" + + printf '%s\n%s\n%s\n' "$svc_json" "$ing_json" "$mc_json" \ + | jq -rs ' + (.[0] // {}) as $svcsRaw | + (.[1] // {}) as $ingsRaw | + (.[2] // {}) as $mcsRaw | + + # If any list failed, surface an error and DO NOT print the success string + if ($svcsRaw.__err or $ingsRaw.__err or $mcsRaw.__err) then + "ERROR_KUBECTL_LIST:" + + ([ + ($svcsRaw.__err // empty), + ($ingsRaw.__err // empty), + ($mcsRaw.__err // empty) + ] | join(",")) + else + ($svcsRaw.items // []) as $svcs | + ($ingsRaw.items // []) as $ings | + ($mcsRaw.items // []) as $mcs | + + def trim: gsub("^\\s+|\\s+$";""); + def hasmc($ns;$name): any($mcs[]?; .metadata.namespace==$ns and .metadata.name==$name); + + ([ + # Public Services (not eligible for managed certs) + $svcs[]? | select(.spec.type=="LoadBalancer") + | "FOUND_PUBLIC_LB_SERVICE:\(.metadata.namespace // "default"):\(.metadata.name)" + ] + [ + # Ingresses missing managed-certs annotation + $ings[]? as $i + | ($i.metadata.annotations."networking.gke.io/managed-certificates" // "") as $ann + | select($ann=="") + | "FOUND_INGRESS_WITHOUT_MANAGED_CERT:\($i.metadata.namespace // "default"):\($i.metadata.name)" + ] + [ + # Ingresses referencing non-existent ManagedCertificate(s) + $ings[]? as $i + | ($i.metadata.annotations."networking.gke.io/managed-certificates" // "") as $ann + | select($ann!="") + | ($i.metadata.namespace // "default") as $ns + | ($ann | split(",") | map(trim) | map(select(length>0)) | .[]) as $mc + | select(hasmc($ns;$mc) | not) + | "FOUND_MISSING_MANAGED_CERT_RESOURCE:\($ns):\($i.metadata.name):cert=\($mc)" + ]) as $f + | if ($f|length)>0 + then $f[] + else "ALL_INGRESSES_USE_MANAGED_CERTS_AND_NO_PUBLIC_LB_SERVICES" + end + end + ' + tests: + test_items: + - flag: "ALL_INGRESSES_USE_MANAGED_CERTS_AND_NO_PUBLIC_LB_SERVICES" + set: true + compare: + op: eq + value: "ALL_INGRESSES_USE_MANAGED_CERTS_AND_NO_PUBLIC_LB_SERVICES" + remediation: | + If services of type:LoadBalancer are discovered, consider replacing the Service with + an Ingress. + + To configure the Ingress and use Google-managed SSL certificates, follow the + instructions as listed at: https://cloud.google.com/kubernetes-engine/docs/how- + to/managed-certs. + scored: true + + - id: 5.7 + text: "Logging" + checks: + - id: 5.7.1 + text: "Ensure Logging and Cloud Monitoring is Enabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | \ + jq '{loggingService: .loggingService, monitoringService: .monitoringService}' + type: "manual" + remediation: | + To enable Logging for an existing cluster, run the following command: + gcloud container clusters update --location \ + --logging= + + See https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#--logging + for a list of available components for logging. + + To enable Cloud Monitoring for an existing cluster, run the following command: + gcloud container clusters update --location \ + --monitoring= + + See https://cloud.google.com/sdk/gcloud/reference/container/clusters/update#-- + monitoring for a list of available components for Cloud Monitoring. + scored: false + + - id: 5.7.2 + text: "Enable Linux auditd logging (Manual)" + audit: | + kubectl get daemonsets -A -o json | jq '.items[] | select (.spec.template.spec.containers[].image | contains ("gcr.io/stackdriver-agents/stackdriver-logging-agent"))'| jq '{name: .metadata.name, annotations: .metadata.annotations."kubernetes.io/description", namespace: .metadata.namespace, status: .status}' + type: "manual" + remediation: | + Using Command Line: + Download the example manifests: + curl https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-node-tools/master/os-audit/cos-auditd-logging.yaml > cos-auditd-logging.yaml + + Edit the example manifests if needed. Then, deploy them: + kubectl apply -f cos-auditd-logging.yaml + + Verify that the logging Pods have started. If a different Namespace was defined in the + manifests, replace cos-auditd with the name of the namespace being used: + kubectl get pods --namespace=cos-auditd + scored: false + + - id: 5.8 + text: "Authentication and Authorization" + checks: + - id: 5.8.1 + text: "Ensure authentication using Client Certificates is Disabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.masterAuth.clientKey' + type: "manual" + remediation: | + Using Command Line: + Create a new cluster without a Client Certificate: + gcloud container clusters create [CLUSTER_NAME] \ + --no-issue-client-certificate + scored: false + + - id: 5.8.2 + text: "Manage Kubernetes RBAC users with Google Groups for GKE (Manual)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '{Enabled: .authenticatorGroupsConfig.enabled, "Security Group": .authenticatorGroupsConfig.securityGroup}' + type: "manual" + remediation: | + Using Command Line: + Follow the G Suite Groups instructions at: https://cloud.google.com/kubernetes- + engine/docs/how-to/role-based-access-control#google-groups-for-gke. + + Command line statement to create a new cluster: + gcloud container clusters create CLUSTER_NAME \ + --location=CONTROL_PLANE_LOCATION \ + --security-group="gke-security-groups@DOMAIN" + + Command line statement to update an existing cluster: + gcloud container clusters update CLUSTER_NAME \ + --location=CONTROL_PLANE_LOCATION \ + --security-group="gke-security-groups@DOMAIN" + + Finally create Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings that + reference the G Suite Groups. + scored: false + + - id: 5.8.3 + text: "Ensure Legacy Authorization (ABAC) is Disabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.legacyAbac' + type: "manual" + remediation: | + Using Command Line: + To disable Legacy Authorization for an existing cluster, run the following command: + gcloud container clusters update --location --no-enable-legacy-authorization + scored: false + + - id: 5.9 + text: "Storage" + checks: + - id: 5.9.1 + text: "Enable Customer-Managed Encryption Keys (CMEK) for GKE Persistent Disks (PD) (Manual)" + audit: | + gcloud compute disks describe $PV_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.diskEncryptionKey.kmsKeyName' + type: "manual" + remediation: | + Using Command Line: + Follow the instructions detailed at: https://cloud.google.com/kubernetes-engine/docs/how-to/using-cmek. + scored: false + + - id: 5.9.2 + text: "Enable Customer-Managed Encryption Keys (CMEK) for Boot Disks (Automated)" + audit: | + gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format="table(name, config.diskType, config.bootDiskKmsKey)" + type: "manual" + remediation: | + Using Command Line: + Create a new node pool using customer-managed encryption keys for the node boot + disk, of either pd-standard or pd-ssd: + gcloud container node-pools create --disk-type \ + --boot-disk-kms-key projects//locations//keyRings//cryptoKeys/ + + Create a cluster using customer-managed encryption keys for the node boot disk, of + either pd-standard or pd-ssd: + gcloud container clusters create --disk-type \ + --boot-disk-kms-key projects//locations//keyRings//cryptoKeys/ + scored: false + + - id: 5.10 + text: "Other Cluster Configurations" + checks: + - id: 5.10.1 + text: "Ensure Kubernetes Web UI is Disabled (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.addonsConfig.kubernetesDashboard + type: "manual" + remediation: | + Using Command Line: + To disable the Kubernetes Dashboard on an existing cluster, run the following + command: + gcloud container clusters update --location --update-addons=KubernetesDashboard=DISABLED + scored: false + + - id: 5.10.2 + text: "Ensure that Alpha clusters are not used for production workloads (Automated)" + audit: | + gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.enableKubernetesAlpha' + type: "manual" + remediation: | + Using Command Line: + Upon creating a new cluster + gcloud container clusters create --location + + Do not use the --enable-kubernetes-alpha argument. + scored: false + + - id: 5.10.3 + text: "Consider GKE Sandbox for running untrusted workloads (Automated)" + audit: | + gcloud container node-pools describe $NODE_POOL --cluster $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.config.sandboxConfig' + type: "manual" + remediation: | + Using Command Line: + To enable GKE Sandbox on an existing cluster, a new Node pool must be created, + which can be done using: + gcloud container node-pools create --location --cluster --image-type=cos_containerd --sandbox="type=gvisor + scored: false + + - id: 5.10.4 + text: "Enable Security Posture (Automated)" + audit: "gcloud container clusters describe $CLUSTER_NAME --location $LOCATION --project $PROJECT_ID --format json | jq '.securityPostureConfig'" + type: "manual" + remediation: | + Using Command Line: + To enable Security Posture on an existing cluster, run the following command: + gcloud container clusters update CLUSTER_NAME \ + --location=CONTROL_PLANE_LOCATION \ + --security-posture=standard + scored: false diff --git a/cfg/gke-1.9.0/master.yaml b/cfg/gke-1.9.0/master.yaml new file mode 100644 index 000000000..f83172f5d --- /dev/null +++ b/cfg/gke-1.9.0/master.yaml @@ -0,0 +1,6 @@ +--- +controls: +version: "gke-1.9.0" +id: 1 +text: "Control Plane Components" +type: "master" diff --git a/cfg/gke-1.9.0/node.yaml b/cfg/gke-1.9.0/node.yaml new file mode 100644 index 000000000..d62809b9a --- /dev/null +++ b/cfg/gke-1.9.0/node.yaml @@ -0,0 +1,65 @@ +--- +controls: +version: "gke-1.9.0" +id: 3 +text: "Worker Nodes" +type: "node" +groups: + - id: 3.1 + text: "Worker Node Configuration Files" + checks: + - id: 3.1.1 + text: "Ensure that the kubeconfig file permissions are set to 644 or more restrictive (Automated)" + audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c permissions=%a $proxykubeconfig; fi'' ' + tests: + test_items: + - flag: "permissions" + compare: + op: bitmask + value: "644" + remediation: | + Run the below command (based on the file location on your system) on each worker node. + For example, + + chmod 644 $proxykubeconfig + scored: true + + - id: 3.1.2 + text: "Ensure that the kubelet kubeconfig file ownership is set to root:root (Automated)" + audit: '/bin/sh -c ''if test -e $proxykubeconfig; then stat -c %U:%G $proxykubeconfig; fi'' ' + tests: + test_items: + - flag: root:root + remediation: | + Run the below command (based on the file location on your system) on each worker node. + For example: + + chown root:root $proxykubeconfig + scored: true + + - id: 3.1.3 + text: "Ensure that the kubelet configuration file has permissions set to 644 (Automated)" + audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c permissions=%a $kubeletconf; fi'' ' + tests: + test_items: + - flag: "permissions" + compare: + op: bitmask + value: "644" + remediation: | + Run the following command (using the kubelet config file location) + + chmod 644 $kubeletconf + scored: true + + - id: 3.1.4 + text: "Ensure that the kubelet configuration file ownership is set to root:root (Automated)" + audit: '/bin/sh -c ''if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'' ' + tests: + test_items: + - flag: root:root + remediation: | + Run the following command (using the config file location identied in the Audit step) + + chown root:root $kubeletconf + scored: true diff --git a/cfg/gke-1.9.0/policies.yaml b/cfg/gke-1.9.0/policies.yaml new file mode 100644 index 000000000..f96318efd --- /dev/null +++ b/cfg/gke-1.9.0/policies.yaml @@ -0,0 +1,511 @@ +--- +controls: +version: "gke-1.9.0" +id: 4 +text: "Kubernetes Policies" +type: "policies" +groups: + - id: 4.1 + text: "RBAC and Service Accounts" + checks: + - id: 4.1.1 + text: "Ensure that the cluster-admin role is only used where required (Automated)" + audit: | + kubectl get clusterrolebindings -o json | jq -r ' + [ + .items[] + | select(.roleRef.name == "cluster-admin") + | .subjects[]? + | select(.kind != "Group" or .name != "system:masters") + ] + | if length == 0 + then "NO_CLUSTER_ADMIN_BINDINGS" + else "FOUND_CLUSTER_ADMIN_BINDING" + end + ' + tests: + test_items: + - flag: "NO_CLUSTER_ADMIN_BINDINGS" + set: true + compare: + op: eq + value: "NO_CLUSTER_ADMIN_BINDINGS" + remediation: | + Identify all ClusterRoleBindings to the "cluster-admin" role and review their subjects: + + kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name | grep cluster-admin + + If non-system principals (users, groups, or service accounts) do not strictly require cluster-admin, + rebind them to a least-privileged (Cluster)Role and then remove the excessive binding: + + kubectl delete clusterrolebinding + + Notes: + - Do not modify bindings with the "system:" prefix that are required for core components. + - Prefer assigning narrowly scoped Roles/ClusterRoles that grant only the permissions needed. + scored: true + + - id: 4.1.2 + text: "Minimize access to secrets (Automated)" + audit: | + count=$(kubectl get roles --all-namespaces -o json | jq ' + .items[] + | select(.rules[]? + | (.resources[]? == "secrets") + and ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "watch")) + )' | wc -l) + + if [ "$count" -gt 0 ]; then + echo "SECRETS_ACCESS_FOUND" + fi + tests: + test_items: + - flag: "SECRETS_ACCESS_FOUND" + set: false + remediation: | + Where possible, remove get, list and watch access to Secret objects in the cluster. + scored: true + + - id: 4.1.3 + text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" + audit: | + wildcards=$(kubectl get roles --all-namespaces -o json | jq ' + .items[] | select( + .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") + )' | wc -l) + + wildcards_clusterroles=$(kubectl get clusterroles -o json | jq ' + .items[] | select( + .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*") + )' | wc -l) + + total=$((wildcards + wildcards_clusterroles)) + + if [ "$total" -gt 0 ]; then + echo "wildcards_present" + fi + tests: + test_items: + - flag: wildcards_present + set: false + remediation: | + Where possible replace any use of wildcards in clusterroles and roles with specific + objects or actions. + scored: true + + - id: 4.1.4 + text: "Ensure that default service accounts are not actively used (Automated)" + audit: | + echo "šŸ”¹ Default Service Accounts with automountServiceAccountToken enabled:" + default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq ' + [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length') + if [ "$default_sa_count" -gt 0 ]; then + echo "default_sa_not_auto_mounted" + fi + + echo "\nšŸ”¹ Pods using default ServiceAccount:" + pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq ' + [.items[] | select(.spec.serviceAccountName == "default")] | length') + if [ "$pods_using_default_sa" -gt 0 ]; then + echo "default_sa_used_in_pods" + fi + tests: + test_items: + - flag: default_sa_not_auto_mounted + set: false + - flag: default_sa_used_in_pods + set: false + remediation: | + Create explicit service accounts wherever a Kubernetes workload requires specific + access to the Kubernetes API server. + + Modify the configuration of each default service account to include this value + + automountServiceAccountToken: false + scored: true + + - id: 4.1.5 + text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" + audit: | + echo "šŸ”¹ Pods with automountServiceAccountToken enabled:" + pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq ' + [.items[] | select(.spec.automountServiceAccountToken != false)] | length') + + if [ "$pods_with_token_mount" -gt 0 ]; then + echo "automountServiceAccountToken" + fi + tests: + test_items: + - flag: automountServiceAccountToken + set: false + remediation: | + Modify the definition of pods and service accounts which do not need to mount service + account tokens to disable it. + scored: true + + - id: 4.1.6 + text: "Avoid use of system:masters group (Automated)" + audit: | + found=0 + for csr in $(kubectl get csr -o name 2>/dev/null | sed 's|^.*/||'); do + req=$(kubectl get csr "$csr" -o jsonpath='{.spec.request}' 2>/dev/null) + [ -z "$req" ] && continue + if echo "$req" | base64 -d 2>/dev/null | openssl req -noout -text 2>/dev/null | grep -q 'O = system:masters'; then + conds=$(kubectl get csr "$csr" -o json | jq -r '[.status.conditions[]?.type] | join(",")') + echo "FOUND_SYSTEM_MASTERS_CSR:${csr}:${conds:-NONE}" + found=1 + fi + done + if [ "$found" -eq 0 ]; then + echo "NO_SYSTEM_MASTERS_CREDENTIALS_FOUND" + fi + tests: + test_items: + - flag: "NO_SYSTEM_MASTERS_CREDENTIALS_FOUND" + set: true + compare: + op: eq + value: "NO_SYSTEM_MASTERS_CREDENTIALS_FOUND" + remediation: | + Remove the system:masters group from all users in the cluster. + scored: true + + - id: 4.1.7 + text: "Limit use of the Bind, Impersonate and Escalate permissions in the Kubernetes cluster (Manual)" + type: "manual" + audit: | + kubectl get clusterrole,role -A -o json | jq -r ' + def risky_verbs: ["bind","impersonate","escalate"]; + + .items[] + | . as $r + | [ $r.rules[]? + | select( + (.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate")) + ) + | { + resources: ((.resources // ["*"]) | join(",")), + verbs: ((.verbs // []) | map(select(IN("bind","impersonate","escalate"))) | join(",")) + } + ] as $matches + | select($matches | length > 0) + | [ + $r.kind, + $r.metadata.name, + ($r.metadata.namespace // "cluster-wide"), + ($matches | map(.resources) | unique | join(",")), + ($matches | map(.verbs) | unique | join(",")) + ] | @tsv + ' | awk -F'\t' 'BEGIN{ + printf "%-15s %-40s %-20s %-40s %-30s\n","KIND","NAME","NAMESPACE","RESOURCES","VERBS" + print "--------------- ---------------------------------------- -------------------- ---------------------------------------- ------------------------------" + }{ + printf "%-15s %-40s %-20s %-40s %-30s\n",$1,$2,$3,$4,$5 + }' + remediation: | + Where possible, remove the impersonate, bind and escalate rights from subjects. + scored: false + + - id: 4.1.8 + text: "Avoid bindings to system:anonymous (Automated)" + audit: | + # Flags any ClusterRoleBinding/RoleBinding that targets the user "system:anonymous". + # Prints "NO_ANONYMOUS_BINDINGS" when none are found. + ( + kubectl get clusterrolebindings -o json | jq -r ' + .items[] + | select((.subjects | length) > 0) + | select(any(.subjects[]?; + .kind=="User" and .name=="system:anonymous" + )) + | "FOUND_ANONYMOUS:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" + '; + kubectl get rolebindings -A -o json | jq -r ' + .items[] + | select((.subjects | length) > 0) + | select(any(.subjects[]?; + .kind=="User" and .name=="system:anonymous" + )) + | "FOUND_ANONYMOUS:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" + ' + ) | (grep -q '^FOUND_ANONYMOUS:' && cat || echo 'NO_ANONYMOUS_BINDINGS') + tests: + test_items: + - flag: "NO_ANONYMOUS_BINDINGS" + set: true + compare: + op: eq + value: "NO_ANONYMOUS_BINDINGS" + remediation: | + Identify all clusterrolebindings and rolebindings to the user system:anonymous. + Check if they are used and review the permissions associated with the binding using the + commands in the Audit section above or refer to GKE documentation + (https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac#detect-prevent-default). + + Strongly consider replacing unsafe bindings with an authenticated, user-defined group. + Where possible, bind to non-default, user-defined groups with least-privilege roles. + + If there are any unsafe bindings to the user system:anonymous, proceed to delete them + after consideration for cluster operations with only necessary, safer bindings. + + kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] + kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] + scored: true + + - id: 4.1.9 + text: "Avoid non-default bindings to system:unauthenticated (Automated)" + audit: | + # Flags any non-default binding to the group "system:unauthenticated". + # Prints "NO_NON_DEFAULT_UNAUTH_BINDINGS" when none are found. + ( + kubectl get clusterrolebindings -o json | jq -r ' + .items[] + | select(.metadata.name != "system:public-info-viewer") + | select((.subjects | length) > 0) + | select(any(.subjects[]?; + .kind=="Group" and .name=="system:unauthenticated" + )) + | "FOUND_UNAUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" + '; + kubectl get rolebindings -A -o json | jq -r ' + .items[] + | select((.subjects | length) > 0) + | select(any(.subjects[]?; + .kind=="Group" and .name=="system:unauthenticated" + )) + | "FOUND_UNAUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" + ' + ) | (grep -q "^FOUND_UNAUTH:" && cat || echo "NO_NON_DEFAULT_UNAUTH_BINDINGS") + tests: + test_items: + - flag: "NO_NON_DEFAULT_UNAUTH_BINDINGS" + set: true + compare: + op: eq + value: "NO_NON_DEFAULT_UNAUTH_BINDINGS" + remediation: | + Identify all non-default clusterrolebindings and rolebindings to the group + system:unauthenticated. Check if they are used and review the permissions + associated with the binding using the commands in the Audit section above or refer to + GKE documentation (https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac#detect-prevent-default). + + Strongly consider replacing non-default, unsafe bindings with an authenticated, user- + defined group. Where possible, bind to non-default, user-defined groups with least- + privilege roles. + + If there are any non-default, unsafe bindings to the group system:unauthenticated, + proceed to delete them after consideration for cluster operations with only necessary, + safer bindings. + + kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] + kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] + scored: true + + - id: 4.1.10 + text: "Avoid non-default bindings to system:authenticated (Automated)" + audit: | + # Flags any non-default binding to the group "system:authenticated". + # Allowed defaults (CRB): system:basic-user, system:discovery + # Prints "NO_NON_DEFAULT_AUTH_BINDINGS" when none are found. + ( + kubectl get clusterrolebindings -o json | jq -r ' + .items[] + | select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery")) + | select((.subjects | length) > 0) + | select(any(.subjects[]?; + .kind=="Group" and .name=="system:authenticated" + )) + | "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" + '; + kubectl get rolebindings -A -o json | jq -r ' + .items[] + | select((.subjects | length) > 0) + | select(any(.subjects[]?; + .kind=="Group" and .name=="system:authenticated" + )) + | "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)" + ' + ) | (grep -q "^FOUND_AUTH:" && cat || echo "NO_NON_DEFAULT_AUTH_BINDINGS") + tests: + test_items: + - flag: "NO_NON_DEFAULT_AUTH_BINDINGS" + set: true + compare: + op: eq + value: "NO_NON_DEFAULT_AUTH_BINDINGS" + remediation: | + Identify all non-default clusterrolebindings and rolebindings to the group + system:authenticated. Check if they are used and review the permissions associated + with the binding using the commands in the Audit section above or refer to GKE + documentation. + + Strongly consider replacing non-default, unsafe bindings with an authenticated, user- + defined group. Where possible, bind to non-default, user-defined groups with least- + privilege roles. + + If there are any non-default, unsafe bindings to the group system:authenticated, + proceed to delete them after consideration for cluster operations with only necessary, + safer bindings. + + kubectl delete clusterrolebinding [CLUSTER_ROLE_BINDING_NAME] + kubectl delete rolebinding [ROLE_BINDING_NAME] --namespace [ROLE_BINDING_NAMESPACE] + scored: true + + - id: 4.2 + text: "Pod Security Standards" + checks: + - id: 4.2.1 + text: "Ensure that the cluster enforces Pod Security Standard Baseline profile or stricter for all namespaces. (Manual)" + audit: | + diff \ + <(kubectl get namespace -l pod-security.kubernetes.io/enforce=baseline -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}') \ + <(kubectl get namespace -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}') + type: "manual" + remediation: | + Ensure that Pod Security Admission is in place for every namespace which contains + user workloads. + Run the following command to enforce the Baseline profile in a namespace: + + kubectl label namespace pod-security.kubernetes.io/enforce=baseline + scored: false + + - id: 4.3 + text: "Network Policies and CNI" + checks: + - id: 4.3.1 + text: "Ensure that the CNI in use supports Network Policies (Manual)" + type: "manual" + remediation: | + To use a CNI plugin with Network Policy, enable Network Policy in GKE, and the CNI plugin + will be updated. See Recommendation 5.6.7. + scored: false + + - id: 4.3.2 + text: "Ensure that all Namespaces have Network Policies defined (Automated)" + audit: | + (kubectl get ns -o json; kubectl get networkpolicy -A -o json) \ + | jq -rs ' + (.[0].items | map(.metadata.name) + | map(select(.!="kube-system" and .!="kube-public" and .!="kube-node-lease"))) as $ns + | + ( (.[1].items // []) + | sort_by(.metadata.namespace) + | group_by(.metadata.namespace) + | map({key: .[0].metadata.namespace, value: length}) + | from_entries + ) as $np + | + [ $ns[] | select( ($np[.] // 0) == 0 ) ] as $missing + | + if ($missing|length)>0 + then ($missing[] | "FOUND_NAMESPACE_WITHOUT_NETWORKPOLICY:"+.) + else "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" + end + ' + tests: + test_items: + - flag: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" + set: true + compare: + op: eq + value: "ALL_NAMESPACES_HAVE_NETWORK_POLICIES" + remediation: | + Follow the documentation and create NetworkPolicy objects as needed. + See: https://cloud.google.com/kubernetes-engine/docs/how-to/network-policy#creating_a_network_policy + for more information. + scored: true + + - id: 4.4 + text: "Secrets Management" + checks: + - id: 4.4.1 + text: "Prefer using secrets as files over secrets as environment variables (Automated)" + audit: | + output=$(kubectl get all --all-namespaces -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}') + if [ -z "$output" ]; then echo "NO_ENV_SECRET_REFERENCES"; else echo "ENV_SECRET_REFERENCES_FOUND"; fi + tests: + test_items: + - flag: "NO_ENV_SECRET_REFERENCES" + set: true + compare: + op: eq + value: "NO_ENV_SECRET_REFERENCES" + remediation: | + If possible, rewrite application code to read secrets from mounted secret files, rather than + from environment variables. + scored: true + + - id: 4.4.2 + text: "Consider external secret storage (Manual)" + type: "manual" + remediation: | + Refer to the secrets management options offered by your cloud provider or a third-party + secrets management solution. + scored: false + + - id: 4.5 + text: "Extensible Admission Control" + checks: + - id: 4.5.1 + text: "Configure Image Provenance using ImagePolicyWebhook admission controller (Manual)" + type: "manual" + remediation: | + Follow the Kubernetes documentation and setup image provenance. + Also see recommendation 5.1.4. + scored: false + + - id: 4.6 + text: "General Policies" + checks: + - id: 4.6.1 + text: "Create administrative boundaries between resources using namespaces (Manual)" + type: "manual" + audit: | + kubectl get namespaces + remediation: | + Follow the documentation and create namespaces for objects in your deployment as you need + them. + scored: false + + - id: 4.6.2 + text: "Ensure that the seccomp profile is set to RuntimeDefault in your pod definitions (Automated)" + type: "manual" + audit: | + kubectl get pods --all-namespaces -o json |\ + jq -r '.items[] | select(.metadata.annotations."seccomp.security.alpha.kubernetes.io/pod" == "runtime/default" or .spec.securityContext.seccompProfile.type == "RuntimeDefault") | {namespace: .metadata.namespace, name: .metadata.name, seccompProfile: .spec.securityContext.seccompProfile.type}' + remediation: | + Use security context to enable the RuntimeDefault seccomp profile in your pod + definitions. An example is as below: + + { + "namespace": "kube-system", + "name": "metrics-server-v0.7.0-dbcc8ddf6-gz7d4", + "seccompProfile": "RuntimeDefault" + } + scored: false + + - id: 4.6.3 + text: "Apply Security Context to Your Pods and Containers (Manual)" + type: "manual" + remediation: | + Follow the Kubernetes documentation and apply security contexts to your pods. For a + suggested list of security contexts, you may refer to the CIS Google Container- + Optimized OS Benchmark. + scored: false + + - id: 4.6.4 + text: "The default namespace should not be used (Automated)" + audit: | + output=$(kubectl get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true) + if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi + tests: + test_items: + - flag: "DEFAULT_NAMESPACE_UNUSED" + set: true + compare: + op: eq + value: "DEFAULT_NAMESPACE_UNUSED" + remediation: | + Ensure that namespaces are created to allow for appropriate segregation of Kubernetes + resources and that all new resources are created in a specific namespace. + scored: true diff --git a/cmd/util.go b/cmd/util.go index 092b17ff8..a7316b680 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -573,10 +573,12 @@ func gkeBenchmark(version string) string { switch version { case "1.15", "1.16", "1.17", "1.18", "1.19": return "gke-1.0" - case "1.28", "1.29", "1.30": + case "1.28", "1.29": return "gke-1.6.0" - case "1.31", "1.32", "1.33", "1.34": + case "1.30": return "gke-1.8.0" + case "1.31", "1.32", "1.33", "1.34": + return "gke-1.9.0" default: return "gke-1.2.0" } diff --git a/cmd/util_test.go b/cmd/util_test.go index 9f87865d8..d3a4825bf 100644 --- a/cmd/util_test.go +++ b/cmd/util_test.go @@ -714,12 +714,19 @@ func Test_getPlatformBenchmarkVersion(t *testing.T) { want: "gke-1.6.0", }, { - name: "gke 1.31", + name: "gke 1.30", args: args{ - platform: Platform{Name: "gke", Version: "1.31"}, + platform: Platform{Name: "gke", Version: "1.30"}, }, want: "gke-1.8.0", }, + { + name: "gke 1.33", + args: args{ + platform: Platform{Name: "gke", Version: "1.33"}, + }, + want: "gke-1.9.0", + }, { name: "aliyun", args: args{ From ac66117cd10cd94395b8c3ebdfdd2595e54b56d1 Mon Sep 17 00:00:00 2001 From: Laiba Bareera Date: Fri, 15 May 2026 13:08:10 +0500 Subject: [PATCH 2/3] add gke-1.9 as default version --- cmd/util.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/util.go b/cmd/util.go index 4de16fd43..68c67fc28 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -573,6 +573,8 @@ func gkeBenchmark(version string) string { switch version { case "1.15", "1.16", "1.17", "1.18", "1.19": return "gke-1.0" + case "1.20", "1.21", "1.22", "1.23", "1.24", "1.25", "1.26", "1.27": + return "gke-1.2.0" case "1.28", "1.29": return "gke-1.6.0" case "1.30": @@ -580,7 +582,7 @@ func gkeBenchmark(version string) string { case "1.31", "1.32", "1.33", "1.34": return "gke-1.9.0" default: - return "gke-1.2.0" + return "gke-1.9.0" } } From 368dbe9c5cb2dbad1373fa16738751a92a802c80 Mon Sep 17 00:00:00 2001 From: Laiba Bareera Date: Fri, 15 May 2026 14:09:43 +0500 Subject: [PATCH 3/3] fix the checks --- cfg/gke-1.9.0/policies.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cfg/gke-1.9.0/policies.yaml b/cfg/gke-1.9.0/policies.yaml index f96318efd..a9410f19a 100644 --- a/cfg/gke-1.9.0/policies.yaml +++ b/cfg/gke-1.9.0/policies.yaml @@ -9,7 +9,7 @@ groups: text: "RBAC and Service Accounts" checks: - id: 4.1.1 - text: "Ensure that the cluster-admin role is only used where required (Automated)" + text: "Ensure that the cluster-admin role is only used where required (Manual)" audit: | kubectl get clusterrolebindings -o json | jq -r ' [ @@ -46,7 +46,7 @@ groups: scored: true - id: 4.1.2 - text: "Minimize access to secrets (Automated)" + text: "Minimize access to secrets (Manual)" audit: | count=$(kubectl get roles --all-namespaces -o json | jq ' .items[] @@ -67,7 +67,7 @@ groups: scored: true - id: 4.1.3 - text: "Minimize wildcard use in Roles and ClusterRoles (Automated)" + text: "Minimize wildcard use in Roles and ClusterRoles (Manual)" audit: | wildcards=$(kubectl get roles --all-namespaces -o json | jq ' .items[] | select( @@ -125,7 +125,7 @@ groups: scored: true - id: 4.1.5 - text: "Ensure that Service Account Tokens are only mounted where necessary (Automated)" + text: "Ensure that Service Account Tokens are only mounted where necessary (Manual)" audit: | echo "šŸ”¹ Pods with automountServiceAccountToken enabled:" pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq ' @@ -485,7 +485,7 @@ groups: scored: false - id: 4.6.3 - text: "Apply Security Context to Your Pods and Containers (Manual)" + text: "Apply Security Context to Pods and Containers (Manual)" type: "manual" remediation: | Follow the Kubernetes documentation and apply security contexts to your pods. For a @@ -494,7 +494,7 @@ groups: scored: false - id: 4.6.4 - text: "The default namespace should not be used (Automated)" + text: "The default namespace should not be used (Manual)" audit: | output=$(kubectl get all -n default --no-headers 2>/dev/null | grep -v '^service\s\+kubernetes\s' || true) if [ -z "$output" ]; then echo "DEFAULT_NAMESPACE_UNUSED"; else echo "DEFAULT_NAMESPACE_IN_USE"; fi @@ -508,4 +508,4 @@ groups: remediation: | Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace. - scored: true + scored: false