From ac49fad8e280695e2a4812561326c8ad5505d947 Mon Sep 17 00:00:00 2001 From: Brett Kochendorfer Date: Tue, 26 May 2026 13:37:46 -0500 Subject: [PATCH] feat(gke): Enable Shielded Instances We are currently getting the default top level configuration to enable shielding in the cluster but it is not enabled at the node pool level. Enabling at the node pool recreates the nodepool so instead I want to allow configuring per nodepool, rolling out a new nodepool, and then draining the old node pool. This also allows us to test with things like GPU or only in non-production for now. --- google_gke/README.md | 1 + google_gke/cluster.tf | 10 +++++++++- google_gke/locals.tf | 2 ++ google_gke/variables.tf | 9 +++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/google_gke/README.md b/google_gke/README.md index 06304ac9..1eb2e75c 100644 --- a/google_gke/README.md +++ b/google_gke/README.md @@ -415,6 +415,7 @@ module "gke" { | [node\_pools\_guest\_accelerator](#input\_node\_pools\_guest\_accelerator) | Map containing node pools guest accelerator. Each node pool's name is the key. See locals.tf for defaults. | `map(map(string))` |
{
"tf-default-node-pool": {}
}
| no | | [node\_pools\_labels](#input\_node\_pools\_labels) | Map containing node pools non-default labels (as a map of strings). Each key is used as node pool's name prefix. See locals.tf for defaults. | `map(map(string))` |
{
"tf-default-node-pool": {}
}
| no | | [node\_pools\_oauth\_scopes](#input\_node\_pools\_oauth\_scopes) | Map containing node pools non-default OAuth scopes (as an list). Each node pool's name is the key. See locals.tf for defaults. | `map(list(string))` |
{
"tf-default-node-pool": []
}
| no | +| [node\_pools\_shielded\_instance\_config](#input\_node\_pools\_shielded\_instance\_config) | Per-node-pool shielded instance config. Keyed by node pool name. Pools not present in this map fall back to GKE provider defaults (integrity monitoring on, secure boot off). Changing shielded\_instance\_config on an existing pool forces recreation, so opt in per pool. |
map(object({
enable_secure_boot = optional(bool, true)
enable_integrity_monitoring = optional(bool, true)
}))
| `{}` | no | | [node\_pools\_spot\_enabled](#input\_node\_pools\_spot\_enabled) | Map containing node pools spot enabled. Each node pool's name is the key. See locals.tf for defaults. | `map(bool)` |
{
"tf-default-node-pool": false
}
| no | | [node\_pools\_sysctls](#input\_node\_pools\_sysctls) | Map containing node pools non-default linux node config sysctls (as a map of maps). Each node pool's name is the key. | `map(map(any))` |
{
"tf-default-node-pool": {}
}
| no | | [node\_pools\_tags](#input\_node\_pools\_tags) | Map containing node pools non-default tags (as an list). Each node pool's name is the key. See locals.tf for defaults. | `map(list(string))` |
{
"tf-default-node-pool": []
}
| no | diff --git a/google_gke/cluster.tf b/google_gke/cluster.tf index accb638b..8089c9bc 100644 --- a/google_gke/cluster.tf +++ b/google_gke/cluster.tf @@ -4,7 +4,6 @@ # * cluster telemetry (some kinda new monitoring / logging / metrics aggregation & dashboard for gke clusters; in beta) # * enable_binary_authorization (all container images validated by Google Binary Authorization; needs further impact investigation) # * enable_l4_ilb_subsetting (needs further impact investigation) -# * shielded_instance_config.enable_secure_boot & shielded_instance_config.enable_integrity_monitoring (needs further impact investigation) # * database_encryption to be added with CloudKMS key (postponed for adding CloudKMS keys structure to Terraform or secrets management) # @@ -311,6 +310,15 @@ resource "google_container_node_pool" "pools" { spot = local.node_pools_spot_enabled[each.key] + dynamic "shielded_instance_config" { + for_each = local.node_pools_shielded_instance_config[each.key] != null ? [local.node_pools_shielded_instance_config[each.key]] : [] + + content { + enable_secure_boot = shielded_instance_config.value.enable_secure_boot + enable_integrity_monitoring = shielded_instance_config.value.enable_integrity_monitoring + } + } + machine_type = each.value.machine_type oauth_scopes = local.node_pools_oauth_scopes[each.key] service_account = google_service_account.cluster_service_account.email diff --git a/google_gke/locals.tf b/google_gke/locals.tf index a87ea67d..38bf139a 100644 --- a/google_gke/locals.tf +++ b/google_gke/locals.tf @@ -55,6 +55,8 @@ locals { node_pools_taints = { for node_pool in var.node_pools : node_pool.name => lookup(var.node_pools_taints, node_pool.name, []) } node_pools_spot_enabled = { for node_pool in var.node_pools : node_pool.name => lookup(var.node_pools_spot_enabled, node_pool.name, false) } + node_pools_shielded_instance_config = { for node_pool in var.node_pools : node_pool.name => lookup(var.node_pools_shielded_instance_config, node_pool.name, null) } + # Google Group for RBAC cluster_authenticator_security_group = var.google_group_name == null ? [] : [{ security_group = var.google_group_name diff --git a/google_gke/variables.tf b/google_gke/variables.tf index 034bc7fc..8c6d50e1 100644 --- a/google_gke/variables.tf +++ b/google_gke/variables.tf @@ -249,6 +249,15 @@ variable "node_pools_sysctls" { } } +variable "node_pools_shielded_instance_config" { + description = "Per-node-pool shielded instance config. Keyed by node pool name. Pools not present in this map fall back to GKE provider defaults (integrity monitoring on, secure boot off). Changing shielded_instance_config on an existing pool forces recreation, so opt in per pool." + type = map(object({ + enable_secure_boot = optional(bool, true) + enable_integrity_monitoring = optional(bool, true) + })) + default = {} +} + variable "node_pools_tags" { description = "Map containing node pools non-default tags (as an list). Each node pool's name is the key. See locals.tf for defaults." type = map(list(string))