Skip to content

Commit 644c0bc

Browse files
authored
Merge pull request #149 from zilliztech/add-gcp-byoc-i-gcs-cmek
Add GCP BYOC-I GCS CMEK support
2 parents 83caa37 + f1f9401 commit 644c0bc

9 files changed

Lines changed: 145 additions & 9 deletions

File tree

examples/gcp-project-byoc-I/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@ The example grants the storage service account to the fixed BYOC-I Kubernetes se
4747

4848
The booter VM always uses a dedicated booter service account. The Zilliz BYOC organization service account is not granted permission to impersonate the maintenance service account. The in-cluster `infra/infra-agent-sa` Kubernetes service account uses GKE Workload Identity to access the maintenance service account instead.
4949

50+
### GCS Bucket CMEK
51+
52+
The GCS bucket uses Google-managed encryption by default. To use a customer-managed Cloud KMS key for new bucket objects, enable CMEK:
53+
54+
```hcl
55+
enable_gcs_kms = true
56+
```
57+
58+
When `gcs_kms_key_name` is empty, Terraform creates a Cloud KMS key ring and crypto key in the BYOC-I region and uses that key for the bucket. The generated KMS resource names are derived from the bucket name.
59+
60+
To use an existing Cloud KMS key instead, pass the full key resource name:
61+
62+
```hcl
63+
gcs_kms_key_name = "projects/<gcp-project-id>/locations/<region>/keyRings/<key-ring>/cryptoKeys/<key>"
64+
```
65+
66+
Terraform-created KMS keys are automatically granted to the bucket project's Cloud Storage service agent. When using an existing key, `grant_gcs_kms_key_iam = true` default grants the same `roles/cloudkms.cryptoKeyEncrypterDecrypter` permission on that key. The Terraform runner must be allowed to manage IAM on the KMS key. If the permission is already granted outside Terraform for an existing key, set:
67+
68+
```hcl
69+
grant_gcs_kms_key_iam = false
70+
```
71+
72+
The KMS key location must be compatible with the bucket location. Changing the bucket default KMS key affects new objects written after the change; existing objects are not automatically re-encrypted.
73+
5074
The booter image is not required in `terraform.tfvars`. Production defaults to `gcr.io/zilliz-byoc-prod/gcp-byoc-i-booter:latest`; UAT defaults to `gcr.io/zilliz-byoc-uat/gcp-byoc-i-booter:latest`. For development testing only, override `booter_image` locally.
5175

5276
For booter troubleshooting, set `booter_print_serial_logs_on_apply = true` to print the booter VM serial console logs during `terraform apply`. This requires `gcloud` to be installed and authenticated on the Terraform runner.

examples/gcp-project-byoc-I/main.tf

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ module "vpc" {
2222
module "gcs" {
2323
source = "../../modules/gcp_byoc_i/gcs"
2424

25-
bucket_name = local.bucket_name
26-
gcp_region = local.gcp_region
27-
force_destroy = var.bucket_force_destroy
28-
labels = local.common_labels
25+
bucket_name = local.bucket_name
26+
gcp_project_id = var.gcp_project_id
27+
gcp_region = local.gcp_region
28+
force_destroy = var.bucket_force_destroy
29+
labels = local.common_labels
30+
enable_gcs_kms = var.enable_gcs_kms
31+
gcs_kms_key_name = var.gcs_kms_key_name
32+
grant_gcs_kms_key_iam = var.grant_gcs_kms_key_iam
2933

3034
depends_on = [google_project_service.required]
3135
}

examples/gcp-project-byoc-I/outputs.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ output "gcs_bucket_id" {
1414
value = module.gcs.bucket_id
1515
}
1616

17+
output "gcs_kms_key_name" {
18+
value = module.gcs.kms_key_name
19+
}
20+
1721
output "management_sa" {
1822
value = module.iam.management_sa_email
1923
}

examples/gcp-project-byoc-I/services.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
locals {
2-
required_project_services = toset([
2+
required_project_services = toset(concat([
33
"cloudresourcemanager.googleapis.com",
44
"artifactregistry.googleapis.com",
55
"compute.googleapis.com",
66
"container.googleapis.com",
77
"dns.googleapis.com",
88
"iam.googleapis.com",
99
"storage.googleapis.com",
10-
])
10+
], var.enable_gcs_kms ? ["cloudkms.googleapis.com"] : []))
1111
}
1212

1313
resource "google_project_service" "required" {

examples/gcp-project-byoc-I/terraform.sample.tfvars

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
project_id = "proj-xxxxxxxx"
2-
dataplane_id = "zilliz-byoc-gcp-us-west1-xxxxxxxx"
3-
gcp_project_id = "customer-gcp-project"
1+
project_id = "proj-xxxxxxxx"
2+
dataplane_id = "zilliz-byoc-gcp-us-west1-xxxxxxxx"
3+
gcp_project_id = "customer-gcp-project"
44

55
# Optional overrides.
66
# vpc_cidr = "10.0.0.0/16"
@@ -26,6 +26,12 @@ gcp_project_id = "customer-gcp-project"
2626
# customer_gke_cluster_name = "zilliz-byoc-gke"
2727
# customer_bucket_name = "zilliz-byoc-gcp-bucket"
2828
# bucket_force_destroy = true
29+
# Enable GCS bucket default encryption with a customer-managed Cloud KMS key. When gcs_kms_key_name is empty, Terraform creates a key ring and crypto key.
30+
# enable_gcs_kms = true
31+
# Optional existing key. Leave empty to let Terraform create one.
32+
# gcs_kms_key_name = "projects/customer-gcp-project/locations/us-west1/keyRings/example-key-ring/cryptoKeys/example-key"
33+
# For existing keys only, set to false if the Cloud Storage service agent has already been granted KMS encrypter/decrypter permission.
34+
# grant_gcs_kms_key_iam = true
2935
# enable_resource_manager_tags = true
3036
# Leave tag IDs empty to let Terraform create a per-dataplane tag.
3137
# vendor_tag_key_id = "tagKeys/1234567890"

examples/gcp-project-byoc-I/variables.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,24 @@ variable "bucket_force_destroy" {
235235
default = false
236236
}
237237

238+
variable "enable_gcs_kms" {
239+
description = "Enable Cloud KMS customer-managed encryption key for the GCS bucket."
240+
type = bool
241+
default = false
242+
}
243+
244+
variable "gcs_kms_key_name" {
245+
description = "Existing Cloud KMS key resource name used as the default GCS bucket encryption key. Leave empty to let Terraform create one when enable_gcs_kms is true."
246+
type = string
247+
default = ""
248+
}
249+
250+
variable "grant_gcs_kms_key_iam" {
251+
description = "Whether Terraform should grant the Cloud Storage service agent roles/cloudkms.cryptoKeyEncrypterDecrypter on an existing gcs_kms_key_name. Terraform-created keys are always granted."
252+
type = bool
253+
default = true
254+
}
255+
238256
variable "labels" {
239257
description = "Labels applied to supported GCP resources."
240258
type = map(string)

modules/gcp_byoc_i/gcs/main.tf

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
1+
locals {
2+
create_gcs_kms_key = var.enable_gcs_kms && var.gcs_kms_key_name == ""
3+
grant_gcs_kms_key_iam = var.enable_gcs_kms && (local.create_gcs_kms_key || var.grant_gcs_kms_key_iam)
4+
auto_gcs_kms_name_prefix = trimsuffix(substr(replace(replace(lower(var.bucket_name), ".", "-"), "_", "-"), 0, 50), "-")
5+
gcs_kms_key_ring_name = "${local.auto_gcs_kms_name_prefix}-kr"
6+
gcs_kms_crypto_key_name = "${local.auto_gcs_kms_name_prefix}-key"
7+
effective_gcs_kms_key_name = var.enable_gcs_kms ? (var.gcs_kms_key_name != "" ? var.gcs_kms_key_name : google_kms_crypto_key.gcs[0].id) : ""
8+
}
9+
10+
data "google_project" "this" {
11+
count = local.grant_gcs_kms_key_iam ? 1 : 0
12+
13+
project_id = var.gcp_project_id
14+
}
15+
16+
resource "google_kms_key_ring" "gcs" {
17+
count = local.create_gcs_kms_key ? 1 : 0
18+
19+
project = var.gcp_project_id
20+
name = local.gcs_kms_key_ring_name
21+
location = var.gcp_region
22+
}
23+
24+
resource "google_kms_crypto_key" "gcs" {
25+
count = local.create_gcs_kms_key ? 1 : 0
26+
27+
name = local.gcs_kms_crypto_key_name
28+
key_ring = google_kms_key_ring.gcs[0].id
29+
}
30+
31+
resource "google_kms_crypto_key_iam_member" "gcs_cmek" {
32+
count = local.grant_gcs_kms_key_iam ? 1 : 0
33+
34+
crypto_key_id = local.effective_gcs_kms_key_name
35+
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
36+
member = "serviceAccount:service-${data.google_project.this[0].number}@gs-project-accounts.iam.gserviceaccount.com"
37+
38+
depends_on = [google_kms_crypto_key.gcs]
39+
}
40+
141
resource "google_storage_bucket" "this" {
242
name = var.bucket_name
343
location = var.gcp_region
@@ -12,4 +52,16 @@ resource "google_storage_bucket" "this" {
1252
},
1353
var.labels,
1454
)
55+
56+
dynamic "encryption" {
57+
for_each = var.enable_gcs_kms ? [1] : []
58+
59+
content {
60+
default_kms_key_name = local.effective_gcs_kms_key_name
61+
}
62+
}
63+
64+
depends_on = [
65+
google_kms_crypto_key_iam_member.gcs_cmek,
66+
]
1567
}

modules/gcp_byoc_i/gcs/outputs.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ output "bucket_id" {
55
output "bucket_url" {
66
value = google_storage_bucket.this.url
77
}
8+
9+
output "kms_key_name" {
10+
value = local.effective_gcs_kms_key_name
11+
}

modules/gcp_byoc_i/gcs/variables.tf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ variable "gcp_region" {
88
type = string
99
}
1010

11+
variable "gcp_project_id" {
12+
description = "Customer GCP project ID."
13+
type = string
14+
default = ""
15+
}
16+
1117
variable "force_destroy" {
1218
description = "Whether to force destroy non-empty buckets."
1319
type = bool
@@ -19,3 +25,21 @@ variable "labels" {
1925
type = map(string)
2026
default = {}
2127
}
28+
29+
variable "enable_gcs_kms" {
30+
description = "Enable Cloud KMS customer-managed encryption key for the GCS bucket."
31+
type = bool
32+
default = false
33+
}
34+
35+
variable "gcs_kms_key_name" {
36+
description = "Existing Cloud KMS key resource name used as the default GCS bucket encryption key. Leave empty to let Terraform create one when enable_gcs_kms is true."
37+
type = string
38+
default = ""
39+
}
40+
41+
variable "grant_gcs_kms_key_iam" {
42+
description = "Whether Terraform should grant the Cloud Storage service agent roles/cloudkms.cryptoKeyEncrypterDecrypter on an existing gcs_kms_key_name. Terraform-created keys are always granted."
43+
type = bool
44+
default = true
45+
}

0 commit comments

Comments
 (0)