Skip to content

Commit

Permalink
Some updates and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Luca Prete committed Aug 7, 2024
1 parent 33c804b commit 9b45a3f
Show file tree
Hide file tree
Showing 17 changed files with 597 additions and 221 deletions.
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)](./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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@
* limitations under the License.
*/

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

output "ca_ids" {
description = "The CA ids."
value = {
Expand All @@ -27,6 +22,11 @@ output "ca_ids" {
}
}

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

output "cas" {
description = "The CAs."
value = {
Expand Down
Loading

0 comments on commit 9b45a3f

Please sign in to comment.