Skip to content

Commit 0f63eab

Browse files
authored
feat: add support for policy bundles and metrics SA (#1529)
1 parent 5d3d54e commit 0f63eab

File tree

11 files changed

+198
-18
lines changed

11 files changed

+198
-18
lines changed

Diff for: examples/simple_zonal_with_acm/README.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Simple Zonal Cluster
22

3-
This example illustrates how to create a simple cluster and install [Anthos Config Management](https://cloud.google.com/anthos-config-management/docs/).
3+
This example illustrates how to create a simple cluster and install [Anthos Config Management](https://cloud.google.com/anthos-config-management/docs/)'s [Config Sync](https://cloud.google.com/anthos-config-management/docs/config-sync-overview) and [Policy Controller](https://cloud.google.com/anthos-config-management/docs/concepts/policy-controller) with the [Policy Essentials v2022 policy bundle](https://cloud.google.com/anthos-config-management/docs/how-to/using-policy-essentials-v2022).
44

55
It incorporates the standard cluster module and the [ACM install module](../../modules/acm).
66

@@ -27,13 +27,19 @@ After applying the Terraform configuration, you can run the following commands t
2727
kubectl describe ns shipping-dev
2828
```
2929
30+
4. You can also use `kubectl` to view any policy violations on the cluster:
31+
32+
```
33+
kubectl get constraint -l policycontroller.gke.io/bundleName=policy-essentials-v2022 -o json | jq -cC '.items[]| [.metadata.name,.status.totalViolations]'
34+
```
35+
3036
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
3137
## Inputs
3238
3339
| Name | Description | Type | Default | Required |
3440
|------|-------------|------|---------|:--------:|
3541
| cluster\_name\_suffix | A suffix to append to the default cluster name | `string` | `""` | no |
36-
| project\_id | The project ID to host the cluster in | `any` | n/a | yes |
42+
| project\_id | The project ID to host the cluster in | `string` | n/a | yes |
3743
| region | The region to host the cluster in | `string` | `"us-central1"` | no |
3844
| zone | The zone to host the cluster in | `string` | `"us-central1-a"` | no |
3945

Diff for: examples/simple_zonal_with_acm/acm.tf

+4
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ module "acm" {
2525
policy_dir = "foo-corp"
2626

2727
secret_type = "ssh"
28+
29+
policy_bundles = ["https://github.com/GoogleCloudPlatform/acm-policy-controller-library/bundles/policy-essentials-v2022#e4094aacb91a35b0219f6f4cf6a31580e85b3c28"]
30+
31+
create_metrics_gcp_sa = true
2832
}

Diff for: examples/simple_zonal_with_acm/variables.tf

+3
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616

1717
variable "project_id" {
1818
description = "The project ID to host the cluster in"
19+
type = string
1920
}
2021

2122
variable "cluster_name_suffix" {
2223
description = "A suffix to append to the default cluster name"
24+
type = string
2325
default = ""
2426
}
2527

2628
variable "region" {
2729
description = "The region to host the cluster in"
30+
type = string
2831
default = "us-central1"
2932
}
3033

Diff for: examples/simple_zonal_with_acm/versions.tf

+6-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ terraform {
2121
version = "~> 4.0"
2222
}
2323
kubernetes = {
24-
source = "hashicorp/kubernetes"
24+
source = "hashicorp/kubernetes"
25+
version = "~> 2.10"
26+
}
27+
random = {
28+
source = "hashicorp/random"
29+
version = ">= 2.1"
2530
}
2631
}
2732
required_version = ">= 0.13"

Diff for: modules/acm/README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ data "google_client_config" "default" {}
6767
| cluster\_membership\_id | The cluster membership ID. If unset, one will be autogenerated. | `string` | `""` | no |
6868
| cluster\_name | GCP cluster Name used to reach cluster and which becomes the cluster name in the Config Sync kubernetes custom resource. | `string` | n/a | yes |
6969
| configmanagement\_version | Version of ACM. | `string` | `""` | no |
70+
| create\_metrics\_gcp\_sa | Create a Google service account for ACM metrics writing | `bool` | `false` | no |
7071
| create\_ssh\_key | Controls whether a key will be generated for Git authentication | `bool` | `true` | no |
72+
| enable\_config\_sync | Whether to enable the ACM Config Sync on the cluster | `bool` | `true` | no |
7173
| enable\_fleet\_feature | Whether to enable the ACM feature on the fleet. | `bool` | `true` | no |
7274
| enable\_fleet\_registration | Whether to create a new membership. | `bool` | `true` | no |
7375
| enable\_log\_denies | Whether to enable logging of all denies and dryrun failures for ACM Policy Controller. | `bool` | `false` | no |
@@ -77,19 +79,22 @@ data "google_client_config" "default" {}
7779
| https\_proxy | URL for the HTTPS proxy to be used when communicating with the Git repo. | `string` | `null` | no |
7880
| install\_template\_library | Whether to install the default Policy Controller template library | `bool` | `true` | no |
7981
| location | GCP location used to reach cluster. | `string` | n/a | yes |
82+
| metrics\_gcp\_sa\_name | The name of the Google service account for ACM metrics writing | `string` | `"acm-metrics-writer"` | no |
83+
| policy\_bundles | A list of Policy Controller policy bundles git urls (example: https://github.com/GoogleCloudPlatform/acm-policy-controller-library.git/bundles/policy-essentials-v2022) to install on the cluster. | `list(string)` | `[]` | no |
8084
| policy\_dir | Subfolder containing configs in ACM Git repo. If un-set, uses Config Management default. | `string` | `""` | no |
8185
| project\_id | GCP project\_id used to reach cluster. | `string` | n/a | yes |
8286
| secret\_type | git authentication secret type, is passed through to ConfigManagement spec.git.secretType. Overriden to value 'ssh' if `create_ssh_key` is true | `string` | `"ssh"` | no |
8387
| source\_format | Configures a non-hierarchical repo if set to 'unstructured'. Uses [ACM defaults](https://cloud.google.com/anthos-config-management/docs/how-to/installing#configuring-config-management-operator) when unset. | `string` | `""` | no |
8488
| ssh\_auth\_key | Key for Git authentication. Overrides 'create\_ssh\_key' variable. Can be set using 'file(path/to/file)'-function. | `string` | `null` | no |
8589
| sync\_branch | ACM repo Git branch. If un-set, uses Config Management default. | `string` | `""` | no |
86-
| sync\_repo | ACM Git repo address | `string` | n/a | yes |
90+
| sync\_repo | ACM Git repo address | `string` | `""` | no |
8791
| sync\_revision | ACM repo Git revision. If un-set, uses Config Management default. | `string` | `""` | no |
8892

8993
## Outputs
9094

9195
| Name | Description |
9296
|------|-------------|
97+
| acm\_metrics\_writer\_sa | The ACM metrics writer Service Account |
9398
| configmanagement\_version | Version of ACM installed. |
9499
| git\_creds\_public | Public key of SSH keypair to allow the Anthos Config Management Operator to authenticate to your Git repository. |
95100
| wait | An output to use when you want to depend on cmd finishing |

Diff for: modules/acm/creds.tf

+90-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17+
locals {
18+
# GCP service account ids must be <= 30 chars matching regex ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$
19+
service_account_name = trimsuffix(substr(var.metrics_gcp_sa_name, 0, 30), "-")
20+
}
21+
1722
resource "tls_private_key" "k8sop_creds" {
1823
count = var.create_ssh_key ? 1 : 0
1924
algorithm = "RSA"
@@ -22,10 +27,92 @@ resource "tls_private_key" "k8sop_creds" {
2227

2328
# Wait for the ACM operator to create the namespace
2429
resource "time_sleep" "wait_acm" {
25-
count = (var.create_ssh_key == true || var.ssh_auth_key != null) ? 1 : 0
30+
count = (var.create_ssh_key == true || var.ssh_auth_key != null || var.enable_policy_controller || var.enable_config_sync) ? 1 : 0
31+
depends_on = [google_gke_hub_feature_membership.main]
32+
33+
create_duration = "300s"
34+
}
35+
36+
resource "google_service_account_iam_binding" "config-management-monitoring-iam" {
37+
count = var.enable_config_sync && var.create_metrics_gcp_sa ? 1 : 0
38+
service_account_id = google_service_account.acm_metrics_writer_sa[0].name
39+
role = "roles/iam.workloadIdentityUser"
40+
41+
members = ["serviceAccount:${var.project_id}.svc.id.goog[config-management-monitoring/default]"]
42+
2643
depends_on = [google_gke_hub_feature_membership.main]
44+
}
45+
46+
resource "google_service_account_iam_binding" "gatekeeper-system-iam" {
47+
count = var.enable_policy_controller && var.create_metrics_gcp_sa ? 1 : 0
48+
service_account_id = google_service_account.acm_metrics_writer_sa[0].name
49+
role = "roles/iam.workloadIdentityUser"
50+
51+
members = ["serviceAccount:${var.project_id}.svc.id.goog[gatekeeper-system/gatekeeper-admin]"]
52+
53+
depends_on = [google_gke_hub_feature_membership.main]
54+
}
55+
56+
module "annotate-sa-config-management-monitoring" {
57+
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
58+
version = "~> 3.1"
59+
60+
count = var.enable_config_sync && var.create_metrics_gcp_sa ? 1 : 0
61+
skip_download = true
62+
cluster_name = var.cluster_name
63+
cluster_location = var.location
64+
project_id = var.project_id
65+
66+
kubectl_create_command = "kubectl annotate --overwrite sa -n config-management-monitoring default iam.gke.io/gcp-service-account=${google_service_account.acm_metrics_writer_sa[0].email}"
67+
kubectl_destroy_command = "kubectl annotate sa -n config-management-monitoring default iam.gke.io/gcp-service-account-"
68+
69+
module_depends_on = time_sleep.wait_acm
70+
}
71+
72+
module "annotate-sa-gatekeeper-system" {
73+
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
74+
version = "~> 3.1"
75+
76+
count = var.enable_policy_controller && var.create_metrics_gcp_sa ? 1 : 0
77+
skip_download = true
78+
cluster_name = var.cluster_name
79+
cluster_location = var.location
80+
project_id = var.project_id
81+
82+
kubectl_create_command = "kubectl annotate --overwrite sa -n gatekeeper-system gatekeeper-admin iam.gke.io/gcp-service-account=${google_service_account.acm_metrics_writer_sa[0].email}"
83+
kubectl_destroy_command = "kubectl annotate sa -n gatekeeper-system gatekeeper-admin iam.gke.io/gcp-service-account-"
84+
85+
module_depends_on = time_sleep.wait_acm
86+
}
87+
88+
module "annotate-sa-gatekeeper-system-restart" {
89+
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
90+
version = "~> 3.1"
91+
92+
count = var.enable_policy_controller && var.create_metrics_gcp_sa ? 1 : 0
93+
skip_download = true
94+
cluster_name = var.cluster_name
95+
cluster_location = var.location
96+
project_id = var.project_id
97+
98+
kubectl_create_command = "kubectl rollout restart deployment gatekeeper-controller-manager -n gatekeeper-system"
99+
kubectl_destroy_command = ""
100+
101+
module_depends_on = module.annotate-sa-gatekeeper-system
102+
}
103+
104+
resource "google_service_account" "acm_metrics_writer_sa" {
105+
count = var.create_metrics_gcp_sa ? 1 : 0
106+
107+
display_name = "ACM Metrics Writer SA"
108+
account_id = local.service_account_name
109+
project = var.project_id
110+
}
27111

28-
create_duration = "60s"
112+
resource "google_project_iam_member" "acm_metrics_writer_sa_role" {
113+
project = var.project_id
114+
role = "roles/monitoring.metricWriter"
115+
member = "serviceAccount:${google_service_account.acm_metrics_writer_sa[0].email}"
29116
}
30117

31118
resource "kubernetes_secret_v1" "creds" {
@@ -38,6 +125,6 @@ resource "kubernetes_secret_v1" "creds" {
38125
}
39126

40127
data = {
41-
"${local.k8sop_creds_secret_key}" = local.private_key
128+
(local.k8sop_creds_secret_key) = local.private_key
42129
}
43130
}

Diff for: modules/acm/feature.tf

+13-9
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,20 @@ resource "google_gke_hub_feature_membership" "main" {
3838
configmanagement {
3939
version = var.configmanagement_version
4040

41-
config_sync {
42-
source_format = var.source_format != "" ? var.source_format : null
41+
dynamic "config_sync" {
42+
for_each = var.enable_config_sync ? [{ enabled = true }] : []
4343

44-
git {
45-
sync_repo = var.sync_repo
46-
policy_dir = var.policy_dir != "" ? var.policy_dir : null
47-
sync_branch = var.sync_branch != "" ? var.sync_branch : null
48-
sync_rev = var.sync_revision != "" ? var.sync_revision : null
49-
secret_type = var.secret_type
50-
https_proxy = var.https_proxy
44+
content {
45+
source_format = var.source_format != "" ? var.source_format : null
46+
47+
git {
48+
sync_repo = var.sync_repo
49+
policy_dir = var.policy_dir != "" ? var.policy_dir : null
50+
sync_branch = var.sync_branch != "" ? var.sync_branch : null
51+
sync_rev = var.sync_revision != "" ? var.sync_revision : null
52+
secret_type = var.secret_type
53+
https_proxy = var.https_proxy
54+
}
5155
}
5256
}
5357

Diff for: modules/acm/outputs.tf

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
output "git_creds_public" {
1818
description = "Public key of SSH keypair to allow the Anthos Config Management Operator to authenticate to your Git repository."
19-
value = var.create_ssh_key ? coalesce(tls_private_key.k8sop_creds.*.public_key_openssh...) : null
19+
value = var.create_ssh_key ? coalesce(tls_private_key.k8sop_creds[*].public_key_openssh...) : null
2020
}
2121

2222
output "configmanagement_version" {
@@ -31,3 +31,8 @@ output "wait" {
3131
google_gke_hub_feature_membership.main
3232
]
3333
}
34+
35+
output "acm_metrics_writer_sa" {
36+
description = "The ACM metrics writer Service Account"
37+
value = google_service_account.acm_metrics_writer_sa[0].email
38+
}

Diff for: modules/acm/policy_bundles.tf

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
module "policy_bundles" {
18+
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
19+
version = "~> 3.1"
20+
21+
for_each = toset(var.policy_bundles)
22+
project_id = var.project_id
23+
cluster_name = var.cluster_name
24+
cluster_location = var.location
25+
kubectl_create_command = "kubectl apply -k ${each.key}"
26+
kubectl_destroy_command = "kubectl delete -k ${each.key}"
27+
28+
module_depends_on = [time_sleep.wait_acm]
29+
}

Diff for: modules/acm/variables.tf

+25
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ variable "configmanagement_version" {
5757
variable "sync_repo" {
5858
description = "ACM Git repo address"
5959
type = string
60+
default = ""
6061
}
6162

6263
variable "sync_branch" {
@@ -108,6 +109,12 @@ variable "ssh_auth_key" {
108109
default = null
109110
}
110111

112+
variable "enable_config_sync" {
113+
description = "Whether to enable the ACM Config Sync on the cluster"
114+
type = bool
115+
default = true
116+
}
117+
111118
# Policy Controller config
112119
variable "enable_policy_controller" {
113120
description = "Whether to enable the ACM Policy Controller on the cluster"
@@ -139,3 +146,21 @@ variable "enable_referential_rules" {
139146
type = bool
140147
default = true
141148
}
149+
150+
variable "policy_bundles" {
151+
description = "A list of Policy Controller policy bundles git urls (example: https://github.com/GoogleCloudPlatform/acm-policy-controller-library.git/bundles/policy-essentials-v2022) to install on the cluster."
152+
type = list(string)
153+
default = []
154+
}
155+
156+
variable "create_metrics_gcp_sa" {
157+
description = "Create a Google service account for ACM metrics writing"
158+
type = bool
159+
default = false
160+
}
161+
162+
variable "metrics_gcp_sa_name" {
163+
description = "The name of the Google service account for ACM metrics writing"
164+
type = string
165+
default = "acm-metrics-writer"
166+
}

Diff for: test/integration/simple_zonal/controls/acm.rb

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019 Google LLC
1+
# Copyright 2019-2023 Google LLC
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -48,5 +48,12 @@
4848
expect(namespace).not_to be nil
4949
end
5050
end
51+
52+
describe "gatekeeper-system namespace" do
53+
let(:namespace) { client.get_namespace("gatekeeper-system") }
54+
it "should exist" do
55+
expect(namespace).not_to be nil
56+
end
57+
end
5158
end
5259
end

0 commit comments

Comments
 (0)