Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a new certification authority service (CAS) module #2481

Merged
merged 5 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Currently available modules:
- **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid), [GKE cluster](./modules/gke-cluster-standard), [GKE hub](./modules/gke-hub), [GKE nodepool](./modules/gke-nodepool), [GCVE private cloud](./modules/gcve-private-cloud)
- **data** - <!-- [AlloyDB instance](./modules/alloydb-instance), --> [Analytics Hub](./modules/analytics-hub), [BigQuery dataset](./modules/bigquery-dataset), [Bigtable instance](./modules/bigtable-instance), [Dataplex](./modules/dataplex), [Dataplex DataScan](./modules/dataplex-datascan), [Cloud SQL instance](./modules/cloudsql-instance), [Spanner instance](./modules/spanner-instance), [Firestore](./modules/firestore), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag), [Data Catalog Tag](./modules/data-catalog-tag), [Data Catalog Tag Template](./modules/data-catalog-tag-template), [Datafusion](./modules/datafusion), [Dataproc](./modules/dataproc), [GCS](./modules/gcs), [Pub/Sub](./modules/pubsub), [Dataform Repository](./modules/dataform-repository/)
- **development** - [API Gateway](./modules/api-gateway), [Apigee](./modules/apigee), [Artifact Registry](./modules/artifact-registry), [Container Registry](./modules/container-registry), [Cloud Source Repository](./modules/source-repository), [Workstation cluster](./modules/workstation-cluster)
- **security** - [Binauthz](./modules/binauthz/), [KMS](./modules/kms), [SecretManager](./modules/secret-manager), [VPC Service Control](./modules/vpc-sc), [Certificate Manager](./modules/certificate-manager/)
- **security** - [Binauthz](./modules/binauthz/), [Certificate Authority Service (CAS)](./modules/certificate-authority-service), [KMS](./modules/kms), [SecretManager](./modules/secret-manager), [VPC Service Control](./modules/vpc-sc), [Certificate Manager](./modules/certificate-manager/)
- **serverless** - [Cloud Function v1](./modules/cloud-function-v1), [Cloud Function v2](./modules/cloud-function-v2), [Cloud Run](./modules/cloud-run), [Cloud Run v2](./modules/cloud-run-v2)

For more information and usage examples see each module's README file.
Expand Down
1 change: 1 addition & 0 deletions modules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ These modules are used in the examples included in this repository. If you are u
## Security

- [Binauthz](./binauthz/)
- [Certificate Authority Service (CAS)](./certificate-authority-service)
- [KMS](./kms)
- [SecretManager](./secret-manager)
- [VPC Service Control](./vpc-sc)
Expand Down
125 changes: 125 additions & 0 deletions modules/certificate-authority-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Certificate Authority Service (CAS)

The module allows you to create one or more CAs and an optional CA pool.

<!-- BEGIN TOC -->
- [Examples](#examples)
- [Basic CA infrastructure](#basic-ca-infrastructure)
- [Create custom CAs](#create-custom-cas)
- [Reference an existing CA pool](#reference-an-existing-ca-pool)
- [IAM](#iam)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->

## Examples

### Basic CA infrastructure

This is enough to create a test CA pool and a self-signed root CA.

```hcl
module "cas" {
source = "./fabric/modules/certificate-authority-service"
project_id = var.project_id
location = "europe-west1"
ca_pool_config = {
name = "test-cas"
}
}
# tftest modules=1 resources=2 inventory=basic.yaml
```

### Create custom CAs

You can create multiple, custom CAs.

```hcl
module "cas" {
source = "./fabric/modules/certificate-authority-service"
project_id = var.project_id
location = "europe-west1"
ca_pool_config = {
name = "test-cas"
}
ca_configs = {
root_ca_1 = {
key_spec_algorithm = "RSA_PKCS1_4096_SHA256"
key_usage = {
client_auth = true
server_auth = true
}
}
root_ca_2 = {
subject = {
common_name = "test2.example.com"
organization = "Example"
}
}
}
}
# tftest modules=1 resources=3 inventory=custom_cas.yaml
```

### Reference an existing CA pool

```hcl
module "cas" {
source = "./fabric/modules/certificate-authority-service"
project_id = var.project_id
location = "europe-west1"
ca_pool_config = {
ca_pool_id = var.ca_pool_id
}
}
# tftest modules=1 resources=1 inventory=existing_ca.yaml
```

### IAM

You can assign authoritative and addittive IAM roles to identities on the CA pool, using the usual fabric interface (`iam`, `iam_bindings`, `iam_binding_addittive`, `iam_by_principals`).

```hcl
module "cas" {
source = "./fabric/modules/certificate-authority-service"
project_id = var.project_id
location = "europe-west1"
ca_pool_config = {
name = "test-cas"
}
iam = {
"roles/privateca.certificateManager" = [
var.service_account.iam_email
]
}
iam_bindings_additive = {
cert-manager = {
member = "group:${var.group_email}"
role = "roles/privateca.certificateManager"
}
}
}
# tftest modules=1 resources=4 inventory=iam.yaml
```
<!-- BEGIN TFDOC -->
## Variables

| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [ca_pool_config](variables.tf#L116) | The CA pool config. If you pass ca_pool_id, an existing pool is used. | <code title="object&#40;&#123;&#10; ca_pool_id &#61; optional&#40;string, null&#41;&#10; name &#61; optional&#40;string, null&#41;&#10; tier &#61; optional&#40;string, &#34;DEVOPS&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [location](variables.tf#L140) | The location of the CAs. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L145) | Project id. | <code>string</code> | ✓ | |
| [ca_configs](variables.tf#L17) | The CA configurations. | <code title="map&#40;object&#40;&#123;&#10; deletion_protection &#61; optional&#40;string, true&#41;&#10; type &#61; optional&#40;string, &#34;SELF_SIGNED&#34;&#41;&#10; is_ca &#61; optional&#40;bool, true&#41;&#10; lifetime &#61; optional&#40;string, null&#41;&#10; pem_ca_certificate &#61; optional&#40;string, null&#41;&#10; ignore_active_certificates_on_deletion &#61; optional&#40;bool, false&#41;&#10; skip_grace_period &#61; optional&#40;bool, true&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; gcs_bucket &#61; optional&#40;string, null&#41;&#10; key_spec &#61; optional&#40;object&#40;&#123;&#10; algorithm &#61; optional&#40;string, &#34;RSA_PKCS1_2048_SHA256&#34;&#41;&#10; kms_key_id &#61; optional&#40;string, null&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; key_usage &#61; optional&#40;object&#40;&#123;&#10; cert_sign &#61; optional&#40;bool, true&#41;&#10; client_auth &#61; optional&#40;bool, false&#41;&#10; code_signing &#61; optional&#40;bool, false&#41;&#10; content_commitment &#61; optional&#40;bool, false&#41;&#10; crl_sign &#61; optional&#40;bool, true&#41;&#10; data_encipherment &#61; optional&#40;bool, false&#41;&#10; decipher_only &#61; optional&#40;bool, false&#41;&#10; digital_signature &#61; optional&#40;bool, false&#41;&#10; email_protection &#61; optional&#40;bool, false&#41;&#10; encipher_only &#61; optional&#40;bool, false&#41;&#10; key_agreement &#61; optional&#40;bool, false&#41;&#10; key_encipherment &#61; optional&#40;bool, true&#41;&#10; ocsp_signing &#61; optional&#40;bool, false&#41;&#10; server_auth &#61; optional&#40;bool, true&#41;&#10; time_stamping &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; subject &#61; optional&#40;object&#40;&#123;&#10; common_name &#61; string&#10; organization &#61; string&#10; country_code &#61; optional&#40;string&#41;&#10; locality &#61; optional&#40;string&#41;&#10; organizational_unit &#61; optional&#40;string&#41;&#10; postal_code &#61; optional&#40;string&#41;&#10; province &#61; optional&#40;string&#41;&#10; street_address &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#10; common_name &#61; &#34;test.example.com&#34;&#10; organization &#61; &#34;Test Example&#34;&#10; &#125;&#41;&#10; subject_alt_name &#61; optional&#40;object&#40;&#123;&#10; dns_names &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; email_addresses &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; ip_addresses &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; uris &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; subordinate_config &#61; optional&#40;object&#40;&#123;&#10; root_ca_id &#61; optional&#40;string&#41;&#10; pem_issuer_certificates &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; test-ca &#61; &#123;&#125;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |

## Outputs

| name | description | sensitive |
|---|---|:---:|
| [ca_ids](outputs.tf#L17) | The CA ids. | |
| [ca_pool_id](outputs.tf#L25) | The CA pool id. | |
| [cas](outputs.tf#L30) | The CAs. | |
<!-- END TFDOC -->
71 changes: 71 additions & 0 deletions modules/certificate-authority-service/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

# tfdoc:file:description IAM bindings.

locals {
_iam_principal_roles = distinct(flatten(values(var.iam_by_principals)))
_iam_principals = {
for r in local._iam_principal_roles : r => [
for k, v in var.iam_by_principals :
k if try(index(v, r), null) != null
]
}
iam = {
for role in distinct(concat(keys(var.iam), keys(local._iam_principals))) :
role => concat(
try(var.iam[role], []),
try(local._iam_principals[role], [])
)
}
}

resource "google_privateca_ca_pool_iam_binding" "authoritative" {
for_each = local.iam
ca_pool = local.ca_pool_id
role = each.key
members = each.value
}

resource "google_privateca_ca_pool_iam_binding" "bindings" {
for_each = var.iam_bindings
ca_pool = local.ca_pool_id
role = each.value.role
members = each.value.members
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}

resource "google_privateca_ca_pool_iam_member" "bindings" {
for_each = var.iam_bindings_additive
ca_pool = local.ca_pool_id
role = each.value.role
member = each.value.member
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}
104 changes: 104 additions & 0 deletions modules/certificate-authority-service/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

locals {
ca_pool_id = coalesce(
var.ca_pool_config.ca_pool_id == null,
try(google_privateca_ca_pool.ca_pool[0].name, null)
)
}
resource "google_privateca_ca_pool" "ca_pool" {
count = var.ca_pool_config.ca_pool_id == null ? 1 : 0
name = var.ca_pool_config.name
project = var.project_id
location = "europe-west8"
tier = "DEVOPS"
}

resource "google_privateca_certificate_authority" "cas" {
for_each = var.ca_configs
pool = local.ca_pool_id
certificate_authority_id = each.key
project = var.project_id
location = var.location
type = each.value.type
deletion_protection = each.value.deletion_protection
lifetime = each.value.lifetime
pem_ca_certificate = each.value.pem_ca_certificate
ignore_active_certificates_on_deletion = each.value.ignore_active_certificates_on_deletion
skip_grace_period = each.value.skip_grace_period
gcs_bucket = each.value.gcs_bucket
labels = each.value.labels

config {
subject_config {
subject {
common_name = each.value.subject.common_name
country_code = each.value.subject.country_code
organizational_unit = each.value.subject.organizational_unit
locality = each.value.subject.locality
organization = each.value.subject.organization
province = each.value.subject.province
street_address = each.value.subject.street_address
postal_code = each.value.subject.postal_code
}
subject_alt_name {
dns_names = each.value.subject_alt_name.dns_names
email_addresses = each.value.subject_alt_name.email_addresses
ip_addresses = each.value.subject_alt_name.ip_addresses
uris = each.value.subject_alt_name.uris
}
}
x509_config {
ca_options {
is_ca = each.value.is_ca
}
key_usage {
base_key_usage {
cert_sign = each.value.key_usage.cert_sign
content_commitment = each.value.key_usage.content_commitment
crl_sign = each.value.key_usage.crl_sign
data_encipherment = each.value.key_usage.data_encipherment
decipher_only = each.value.key_usage.decipher_only
digital_signature = each.value.key_usage.digital_signature
encipher_only = each.value.key_usage.encipher_only
key_agreement = each.value.key_usage.key_agreement
key_encipherment = each.value.key_usage.key_encipherment
}
extended_key_usage {
client_auth = each.value.key_usage.client_auth
code_signing = each.value.key_usage.code_signing
email_protection = each.value.key_usage.email_protection
ocsp_signing = each.value.key_usage.ocsp_signing
server_auth = each.value.key_usage.server_auth
time_stamping = each.value.key_usage.time_stamping
}
}
}
}

key_spec {
algorithm = each.value.key_spec.algorithm
cloud_kms_key_version = each.value.key_spec.kms_key_id
}

subordinate_config {
certificate_authority = each.value.subordinate_config.root_ca_id
pem_issuer_chain {
pem_certificates = each.value.subordinate_config.pem_issuer_certificates
}
}
}
36 changes: 36 additions & 0 deletions modules/certificate-authority-service/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

output "ca_ids" {
description = "The CA ids."
value = {
for k, v in google_privateca_certificate_authority.cas
: k => v.id
}
}

output "ca_pool_id" {
description = "The CA pool id."
value = local.ca_pool_id
}

output "cas" {
description = "The CAs."
value = {
for k, v in google_privateca_certificate_authority.cas
: k => v
}
}
Loading