How is the GKE cluster hardened?¶
This page is for whoever reviews or runs the GKE install from Running hangar on GKE. It lists the
cluster settings deploy/terraform/gke makes and maps them to the CIS GKE Benchmark. It also says what each one does
to a cluster that already exists. It is part of the security model.
The settings¶
The variables live in deploy/terraform/gke/hardening.tf. A new cluster gets the secure value of each.
| Variable | Default | What it does |
|---|---|---|
deletion_protection |
true |
Terraform refuses to destroy the cluster. Set it to false and apply before a deliberate terraform destroy. |
inter_node_encryption |
true |
WireGuard between nodes on Dataplane V2. Git grants to hangar-gateway:8788 no longer cross nodes in plain HTTP (review finding L-10). |
secrets_encryption |
true |
Kubernetes Secrets in etcd are wrapped with a Cloud KMS key that rotates every 90 days. |
secrets_kms_key |
empty | Your own key instead of the hangar-gke/secrets key Terraform creates. It must be in the cluster's region. |
shielded_secure_boot |
false |
Secure Boot on every pool. The example tfvars turns it on for new installs. |
security_posture |
BASIC |
GKE audits workload configuration and shows the findings in the console. |
image_vulnerability_scanning |
true |
Artifact Analysis scans every image pushed to the registry for OS and package CVEs. |
private_nodes |
false |
Nodes get internal addresses only, and Cloud NAT carries their egress. |
master_authorized_networks |
empty | Only these ranges reach the control plane's public endpoint. |
Integrity monitoring and the GKE metadata server are on for every pool whatever the variables say. A test,
tests/test_up_gke.py, holds the defaults and checks each pool.
The CIS control mapping¶
Control numbers follow the CIS GKE Benchmark v1.9.0. Check them against the version your reviewer uses.
| Control | What it asks | Status | How, or why not |
|---|---|---|---|
| 5.1.1 | Image vulnerability scanning | Set | Artifact Analysis on the registry (image_vulnerability_scanning). |
| 5.2.1 | Nodes not on the Compute Engine default service account | Yes, opt-in | dedicated_node_service_account runs every pool as hangar-nodes, which can only write logs and metrics and pull hangar's images. It replaces the pools, so pair it with Secure Boot (KAN-183). |
| 5.2.2 | Workload Identity | Set | hangar's pod uses its own Google service account. |
| 5.3.1 | Secrets encrypted with Cloud KMS | Set | secrets_encryption. |
| 5.4.1 | Legacy metadata APIs off | Set | GKE's default, and GKE_METADATA hides them from pods. |
| 5.4.2 | GKE metadata server | Set | GKE_METADATA on every pool. |
| 5.5.1 | Container-Optimized OS | Partly | The system and gVisor pools run COS. The Kata, heavy and microVM pools need Ubuntu, because they install onto the node. |
| 5.5.2 | Node auto-repair | Set | GKE's default for release-channel pools. |
| 5.5.3 | Node auto-upgrade | Set | GKE's default for release-channel pools. |
| 5.5.4 | Release channels | Set | release_channel, RAPID by default for Agent Sandbox. |
| 5.5.5 | Shielded nodes | Set | enable_shielded_nodes. |
| 5.5.6 | Integrity monitoring | Set | On for every pool. |
| 5.5.7 | Secure Boot | Opt-in | shielded_secure_boot: on for new installs, off for existing ones until a maintenance window. |
| 5.6.1 | Intranode visibility | Not set | GKE refuses it with inter-node encryption, which matters more here. |
| 5.6.2 | VPC-native cluster | Set | Dataplane V2 requires it. |
| 5.6.3 | Control plane authorized networks | Opt-in | master_authorized_networks. |
| 5.6.4 | Private endpoint | Not set | up.sh, the deploy workflow and the power jobs reach the public endpoint. |
| 5.6.5 | Private nodes | Opt-in | private_nodes. |
| 5.6.7 | Google-managed certificates | Set | The Ingress uses a ManagedCertificate. |
| 5.7.1 | Logging and monitoring | Set | GKE's default. |
| 5.8.1 | No client certificate authentication | Set | GKE's default since 1.12. |
| 5.8.3 | Legacy ABAC off | Set | GKE's default. |
| 5.9.2 | CMEK boot disks | Not set | Google-managed keys encrypt disks at rest. Customer keys would replace every pool. |
| 5.10.1 | Kubernetes web UI off | Set | GKE removed it. |
| 5.10.3 | GKE Sandbox for untrusted workloads | Set | Every sandbox runs under gVisor, or Kata on the optional pools. |
| 5.10.4 | Binary Authorization | Not set | The decision is below. |
| 5.10.5 | Security posture | Set | security_posture. |
Google removed GKE's own workload vulnerability scanning: the standard tier on 2025-07-31, the advanced tier on 2026-06-16. Registry scanning covers hangar's images instead.
Binary Authorization¶
hangar doesn't enforce Binary Authorization in 1.0. The control plane builds environment images at run time, one per repository and commit, with Cloud Build. Enforcing would need an attestor in that build path and an attestation on every image. It would also need exemptions for GKE's system images, Agent Sandbox and kata-deploy.
What hangar does instead: the control-plane image pins every base image by digest and installs from the hashed lock file. Images live in hangar's own registry, which only hangar and CI push to. Registry scanning flags CVEs.
A registry allowlist policy is the next step, with no attestations. It lets pods run only images from hangar's registry and Google's.
On an existing cluster¶
Every change applies in place except Secure Boot. Some in-place changes still restart things.
| Change | Terraform | What it disrupts |
|---|---|---|
| Deletion protection | In place | Nothing: it is Terraform state only. |
| Security posture, image scanning | In place | Nothing. |
| Secrets encryption | In place | The zonal control plane restarts; the Kubernetes API is down while GKE re-encrypts every Secret. |
| Inter-node encryption | In place | GKE restarts every node, which can take hours. Suspend sessions first. |
| Secure Boot | Replaces every pool | Each pool is deleted and created again. Suspend sessions; hangar is down until the system pool is back. |
| Private nodes | In place | A rolling update of every pool's nodes. |
| Authorized networks | In place | Callers outside the ranges lose kubectl. GitHub-hosted runners are outside Google's ranges. |
Run terraform init -upgrade first: inter-node encryption needs Google provider 6.36 or later. Then read the plan.
hangar up --gke refuses a plan that replaces anything unless you pass --allow-destroy.