Skip to content

Commit bff7df7

Browse files
authored
Create a new "secret" abstraction. (#150)
Signed-off-by: Matt Moore <[email protected]>
1 parent 1030aba commit bff7df7

File tree

6 files changed

+200
-2
lines changed

6 files changed

+200
-2
lines changed

.github/workflows/documentation.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
- prober
2626
- cron
2727
- configmap
28+
- secret
2829

2930
steps:
3031
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

modules/configmap/main.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ resource "google_monitoring_alert_policy" "anomalous-secret-access" {
3939
}
4040
}
4141

42-
display_name = "Abnormal Secret Access: ${var.name}"
42+
display_name = "Abnormal ConfigMap Access: ${var.name}"
4343
combiner = "OR"
4444

4545
conditions {
46-
display_name = "Abnormal Secret Access: ${var.name}"
46+
display_name = "Abnormal ConfigMap Access: ${var.name}"
4747

4848
condition_matched_log {
4949
filter = <<EOT

modules/secret/README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# `secret`
2+
3+
This module encapsulates the creation of a Google Secret Manager secret to hold
4+
sensitive data in a manner that can be used as an environment variable or
5+
volume with Cloud Run. Unlike `configmap` this data is considered sensitive and
6+
so it is NOT loaded directly by this logic, but by an authorized party. Notably,
7+
the built-in alert policy WILL fire when the authorized party loads new values
8+
into the secret, this is by design.
9+
10+
```hcl
11+
module "my-secret" {
12+
source = "chainguard-dev/common/infra//modules/secret"
13+
14+
project_id = var.project_id
15+
name = "my-secret"
16+
17+
# What the service accessing this configuration will run as.
18+
service-account = google_service_account.foo.email
19+
20+
# What group of identities are authorized to add new secret versions.
21+
authorized-adder = "group:[email protected]"
22+
23+
# Optionally: channels to notify if this secret is manipulated.
24+
notification-channels = [ ... ]
25+
}
26+
27+
module "foo-service" {
28+
source = "chainguard-dev/common/infra//modules/regional-go-service"
29+
project_id = var.project_id
30+
name = "foo"
31+
regions = module.networking.regional-networks
32+
33+
service_account = google_service_account.foo.email
34+
containers = {
35+
"foo" = {
36+
source = {
37+
working_dir = path.module
38+
importpath = "./cmd/foo"
39+
}
40+
ports = [{ container_port = 8080 }]
41+
volume_mounts = [{
42+
name = "foo"
43+
mount_path = "/var/run/foo/"
44+
}]
45+
}
46+
}
47+
volumes = [{
48+
name = "foo"
49+
secret = {
50+
secret = module.my-secret.secret_id
51+
items = [{
52+
version = "latest"
53+
path = "my-filename"
54+
}]
55+
}
56+
}]
57+
}
58+
```
59+
60+
<!-- BEGIN_TF_DOCS -->
61+
## Requirements
62+
63+
No requirements.
64+
65+
## Providers
66+
67+
| Name | Version |
68+
|------|---------|
69+
| <a name="provider_google"></a> [google](#provider\_google) | n/a |
70+
71+
## Modules
72+
73+
No modules.
74+
75+
## Resources
76+
77+
| Name | Type |
78+
|------|------|
79+
| [google_monitoring_alert_policy.anomalous-secret-access](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/monitoring_alert_policy) | resource |
80+
| [google_secret_manager_secret.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/secret_manager_secret) | resource |
81+
| [google_secret_manager_secret_iam_binding.authorize-service-access](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/secret_manager_secret_iam_binding) | resource |
82+
| [google_secret_manager_secret_iam_binding.authorize-version-adder](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/secret_manager_secret_iam_binding) | resource |
83+
| [google_client_openid_userinfo.me](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_openid_userinfo) | data source |
84+
| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source |
85+
86+
## Inputs
87+
88+
| Name | Description | Type | Default | Required |
89+
|------|-------------|------|---------|:--------:|
90+
| <a name="input_authorized-adder"></a> [authorized-adder](#input\_authorized-adder) | A member-style representation of the identity authorized to add new secret values (e.g. group:oncall@my-corp.dev). | `string` | n/a | yes |
91+
| <a name="input_name"></a> [name](#input\_name) | The name to give the secret. | `string` | n/a | yes |
92+
| <a name="input_notification-channels"></a> [notification-channels](#input\_notification-channels) | The channels to notify if the configuration data is improperly accessed. | `list(string)` | `[]` | no |
93+
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | n/a | `string` | n/a | yes |
94+
| <a name="input_service-account"></a> [service-account](#input\_service-account) | The email of the service account that will access the secret. | `string` | n/a | yes |
95+
96+
## Outputs
97+
98+
| Name | Description |
99+
|------|-------------|
100+
| <a name="output_secret_id"></a> [secret\_id](#output\_secret\_id) | The ID of the secret. |
101+
<!-- END_TF_DOCS -->

modules/secret/main.tf

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Create the GCP secret to hold the configuration data.
2+
resource "google_secret_manager_secret" "this" {
3+
secret_id = var.name
4+
replication {
5+
auto {}
6+
}
7+
lifecycle {
8+
prevent_destroy = true
9+
}
10+
}
11+
12+
// Only the service account as which the service runs should have access to the secret.
13+
resource "google_secret_manager_secret_iam_binding" "authorize-service-access" {
14+
secret_id = google_secret_manager_secret.this.id
15+
role = "roles/secretmanager.secretAccessor"
16+
members = ["serviceAccount:${var.service-account}"]
17+
}
18+
19+
// Authorize the specified identity to add new secret values.
20+
resource "google_secret_manager_secret_iam_binding" "authorize-version-adder" {
21+
project = var.project_id
22+
secret_id = google_secret_manager_secret.this.secret_id
23+
role = "roles/secretmanager.secretVersionAdder"
24+
members = [var.authorized-adder]
25+
}
26+
27+
// Get a project number for this project ID.
28+
data "google_project" "project" { project_id = var.project_id }
29+
30+
// What identity is deploying this?
31+
data "google_client_openid_userinfo" "me" {}
32+
33+
// Create an alert policy to notify if the secret is accessed by an unauthorized entity.
34+
resource "google_monitoring_alert_policy" "anomalous-secret-access" {
35+
# In the absence of data, incident will auto-close after an hour
36+
alert_strategy {
37+
auto_close = "3600s"
38+
39+
notification_rate_limit {
40+
period = "3600s" // re-alert hourly if condition still valid.
41+
}
42+
}
43+
44+
display_name = "Abnormal Secret Access: ${var.name}"
45+
combiner = "OR"
46+
47+
conditions {
48+
display_name = "Abnormal Secret Access: ${var.name}"
49+
50+
condition_matched_log {
51+
filter = <<EOT
52+
protoPayload.serviceName="secretmanager.googleapis.com"
53+
protoPayload.request.name: ("projects/${var.project_id}/secrets/${var.name}/" OR "projects/${data.google_project.project.number}/secrets/${var.name}/")
54+
55+
-- Ignore the identity that is intended to access this.
56+
-(
57+
protoPayload.authenticationInfo.principalEmail="${var.service-account}"
58+
protoPayload.methodName="google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion"
59+
)
60+
EOT
61+
}
62+
}
63+
64+
notification_channels = var.notification-channels
65+
66+
enabled = "true"
67+
project = var.project_id
68+
}

modules/secret/outputs.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
output "secret_id" {
2+
description = "The ID of the secret."
3+
value = google_secret_manager_secret.this.id
4+
}

modules/secret/variables.tf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
variable "project_id" {
2+
type = string
3+
}
4+
5+
variable "name" {
6+
description = "The name to give the secret."
7+
type = string
8+
}
9+
10+
variable "authorized-adder" {
11+
description = "A member-style representation of the identity authorized to add new secret values (e.g. group:[email protected])."
12+
type = string
13+
}
14+
15+
variable "service-account" {
16+
description = "The email of the service account that will access the secret."
17+
type = string
18+
}
19+
20+
variable "notification-channels" {
21+
description = "The channels to notify if the configuration data is improperly accessed."
22+
type = list(string)
23+
default = []
24+
}

0 commit comments

Comments
 (0)