Skip to content

Commit

Permalink
Add support for importing self-signed certificate to ACM (#5)
Browse files Browse the repository at this point in the history
* Adding ACM support for secret store to self-signed-cert module

* fix(module): Updating version to AWS provider in the example and fixing alias naming to meet conventions

* fix(module): Missed a version

* refactor(module): Switching from secret_store to certificate_backends to support storing secrets in multiple locations

* test(module): Adding terratest for verifying arn. Changing name of secret_store* to certificate_backend

* refactor(module): Update to readme and documentation

* feat(module): Adjusting variable names for terratest

* feat(module): Adding variable for certificate backends to example

* Auto Format

Co-authored-by: cloudpossebot <[email protected]>
  • Loading branch information
r351574nc3 and cloudpossebot authored Sep 14, 2021
1 parent d9b2f71 commit 4831b05
Show file tree
Hide file tree
Showing 29 changed files with 614 additions and 79 deletions.
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,14 @@ Available targets:
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.0 |
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | >= 3.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 2.0 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.0 |
| <a name="provider_tls"></a> [tls](#provider\_tls) | >= 3.0 |

## Modules
Expand All @@ -180,6 +180,7 @@ Available targets:

| Name | Type |
|------|------|
| [aws_acm_certificate.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
| [aws_secretsmanager_secret.certificate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws_secretsmanager_secret.private_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws_secretsmanager_secret_version.certificate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
Expand All @@ -195,9 +196,14 @@ Available targets:
|------|-------------|------|---------|:--------:|
| <a name="input_additional_tag_map"></a> [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.<br>This is for some rare cases where resources want additional configuration of tags<br>and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no |
| <a name="input_allowed_uses"></a> [allowed\_uses](#input\_allowed\_uses) | List of keywords each describing a use that is permitted for the issued certificate.<br>Must be one of of the values outlined in [self\_signed\_cert.allowed\_uses](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/self_signed_cert#allowed_uses). | `list(string)` | n/a | yes |
| <a name="input_asm_recovery_window_in_days"></a> [asm\_recovery\_window\_in\_days](#input\_asm\_recovery\_window\_in\_days) | Number of days that AWS Secrets Manager waits before it can delete the secret. This value can be `0` to force deletion without recovery or range from `7` to `30` days.<br><br>This value is ignored if `var.secrets_store_type` is not `ASM`, or if `var.secrets_store_enabled` is `false`. | `number` | `30` | no |
| <a name="input_asm_recovery_window_in_days"></a> [asm\_recovery\_window\_in\_days](#input\_asm\_recovery\_window\_in\_days) | Number of days that AWS Secrets Manager waits before it can delete the secret. This value can be `0` to force deletion without recovery or range from `7` to `30` days.<br><br>This value is ignored if `var.certificate_backends` is not `ASM`, or if `var.certificate_backend_enabled` is `false`. | `number` | `30` | no |
| <a name="input_attributes"></a> [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,<br>in the order they appear in the list. New attributes are appended to the<br>end of the list. The elements of the list are joined by the `delimiter`<br>and treated as a single ID element. | `list(string)` | `[]` | no |
| <a name="input_basic_constraints"></a> [basic\_constraints](#input\_basic\_constraints) | The [basic constraints](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9) of the issued certificate.<br>Currently, only the `CA` constraint (which identifies whether the subject of the certificate is a CA) can be set.<br><br>Defaults to this certificate not being a CA. | <pre>object({<br> ca = bool<br> })</pre> | <pre>{<br> "ca": false<br>}</pre> | no |
| <a name="input_certificate_backend_kms_key_id"></a> [certificate\_backend\_kms\_key\_id](#input\_certificate\_backend\_kms\_key\_id) | The KMD Key ID (ARN or ID) to use when encrypting either the AWS SSM Parameters or AWS Secrets Manager Secrets relating to the certificate.<br><br>If not specified, the Amazon-managed Key `alias/aws/ssm` will be used if `var.certificate_backends` contains `SSM`,<br>and `alias/aws/secretsmanager` will be used if `var.certificate_backends` is `ASM`. | `string` | `null` | no |
| <a name="input_certificate_backends"></a> [certificate\_backends](#input\_certificate\_backends) | The certificate backend to use when writing secrets related to the self-signed certificate.<br>The value specified can either be `SSM` (AWS Systems Manager Parameter Store), `ASM` (AWS Secrets Manager), <br>and/or `ACM` (AWS Certificate Manager).<br><br>Defaults to `SSM`. | `set(string)` | <pre>[<br> "SSM"<br>]</pre> | no |
| <a name="input_certificate_backends_base64_enabled"></a> [certificate\_backends\_base64\_enabled](#input\_certificate\_backends\_base64\_enabled) | Enable or disable base64 encoding of secrets before writing them to the secrets store. | `bool` | `false` | no |
| <a name="input_certificate_backends_enabled"></a> [certificate\_backends\_enabled](#input\_certificate\_backends\_enabled) | Enable or disable writing to the secrets store. | `bool` | `true` | no |
| <a name="input_certificate_chain"></a> [certificate\_chain](#input\_certificate\_chain) | When using ACM as a certificate backend, some certificates store a certificate chain from a CA. This CA will come from another resource. | `string` | `null` | no |
| <a name="input_context"></a> [context](#input\_context) | Single object for setting entire context at once.<br>See description of individual variables for details.<br>Leave string and numeric variables as `null` to use default value.<br>Individual variable settings (non-null) override settings in context object,<br>except for attributes, tags, and additional\_tag\_map, which are merged. | `any` | <pre>{<br> "additional_tag_map": {},<br> "attributes": [],<br> "delimiter": null,<br> "descriptor_formats": {},<br> "enabled": true,<br> "environment": null,<br> "id_length_limit": null,<br> "label_key_case": null,<br> "label_order": [],<br> "label_value_case": null,<br> "labels_as_tags": [<br> "unset"<br> ],<br> "name": null,<br> "namespace": null,<br> "regex_replace_chars": null,<br> "stage": null,<br> "tags": {},<br> "tenant": null<br>}</pre> | no |
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| <a name="input_descriptor_formats"></a> [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.<br>Map of maps. Keys are names of descriptors. Values are maps of the form<br>`{<br> format = string<br> labels = list(string)<br>}`<br>(Type is `any` so the map values can later be enhanced to provide additional options.)<br>`format` is a Terraform format string to be passed to the `format()` function.<br>`labels` is a list of labels, in order, to pass to `format()` function.<br>Label values will be normalized before being passed to `format()` so they will be<br>identical to how they appear in `id`.<br>Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no |
Expand All @@ -215,12 +221,8 @@ Available targets:
| <a name="input_private_key_ecdsa_curve"></a> [private\_key\_ecdsa\_curve](#input\_private\_key\_ecdsa\_curve) | When `var.cert_key_algorithm` is `ECDSA`, the name of the elliptic curve to use. May be any one of `P224`, `P256`, `P384` or `P521`.<br><br>Ignored if `var.cert_key_algorithm` is not `ECDSA`, or if a preexisting private key is supplied via `var.private_key_contents`.<br><br>Defaults to the `tls` provider default. | `string` | `"P224"` | no |
| <a name="input_private_key_rsa_bits"></a> [private\_key\_rsa\_bits](#input\_private\_key\_rsa\_bits) | When `var.cert_key_algorithm` is `RSA`, the size of the generated RSA key in bits.<br><br>Ignored if `var.cert_key_algorithm` is not `RSA`, or if a preexisting private key is supplied via `var.private_key_contents`.<br><br>Defaults to the `tls` provider default. | `number` | `2048` | no |
| <a name="input_regex_replace_chars"></a> [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.<br>Characters matching the regex will be removed from the ID elements.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| <a name="input_secret_extensions"></a> [secret\_extensions](#input\_secret\_extensions) | The extensions use when writing secrets to the secret store.<br><br>Please refer to `var.secret_path_format` for information on how secret paths are computed. | <pre>object({<br> certificate = string<br> private_key = string<br> })</pre> | <pre>{<br> "certificate": "pem",<br> "private_key": "key"<br>}</pre> | no |
| <a name="input_secret_path_format"></a> [secret\_path\_format](#input\_secret\_path\_format) | The path format to use when writing secrets to the secret store.<br><br>The certificate secret path will be computed as `format(var.secret_path_format, var.name, var.secret_extensions.certificate)`<br>and the private key path as `format(var.secret_path_format, var.name, var.secret_extensions.private_key)`.<br><br>Thus by default, if `var.name`=`example-self-signed-cert`, then the resulting secret paths for the self-signed certificate's<br>PEM file and private key will be `/example-self-signed-cert.pem` and `/example-self-signed-cert.key`, respectively.<br><br>This variable can be overridden in order to create more specific secret store paths. | `string` | `"/%s.%s"` | no |
| <a name="input_secrets_store_base64_enabled"></a> [secrets\_store\_base64\_enabled](#input\_secrets\_store\_base64\_enabled) | Enable or disable base64 encoding of secrets before writing them to the secrets store. | `bool` | `false` | no |
| <a name="input_secrets_store_enabled"></a> [secrets\_store\_enabled](#input\_secrets\_store\_enabled) | Enable or disable writing to the secrets store. | `bool` | `true` | no |
| <a name="input_secrets_store_kms_key_id"></a> [secrets\_store\_kms\_key\_id](#input\_secrets\_store\_kms\_key\_id) | The KMD Key ID (ARN or ID) to use when encrypting either the AWS SSM Parameters or AWS Secrets Manager Secrets relating to the certificate.<br><br>If not specified, the Amazon-managed Key `alias/aws/ssm` will be used if `var.secrets_store_type` is `SSM`,<br>and `alias/aws/secretsmanager` will be used if `var.secrets_store_type` is `ASM`. | `string` | `null` | no |
| <a name="input_secrets_store_type"></a> [secrets\_store\_type](#input\_secrets\_store\_type) | The secret store type to use when writing secrets related to the self-signed certificate.<br>The value specified can either be `SSM` (AWS Systems Manager Parameter Store) or `ASM` (AWS Secrets Manager).<br><br>Defaults to `SSM`. | `string` | `"SSM"` | no |
| <a name="input_secret_extensions"></a> [secret\_extensions](#input\_secret\_extensions) | The extensions use when writing secrets to the certificate backend.<br><br>Please refer to `var.secret_path_format` for information on how secret paths are computed. | <pre>object({<br> certificate = string<br> private_key = string<br> })</pre> | <pre>{<br> "certificate": "pem",<br> "private_key": "key"<br>}</pre> | no |
| <a name="input_secret_path_format"></a> [secret\_path\_format](#input\_secret\_path\_format) | The path format to use when writing secrets to the certificate backend.<br><br>The certificate secret path will be computed as `format(var.secret_path_format, var.name, var.secret_extensions.certificate)`<br>and the private key path as `format(var.secret_path_format, var.name, var.secret_extensions.private_key)`.<br><br>Thus by default, if `var.name`=`example-self-signed-cert`, then the resulting secret paths for the self-signed certificate's<br>PEM file and private key will be `/example-self-signed-cert.pem` and `/example-self-signed-cert.key`, respectively.<br><br>This variable can be overridden in order to create more specific certificate backend paths. | `string` | `"/%s.%s"` | no |
| <a name="input_skid_enabled"></a> [skid\_enabled](#input\_skid\_enabled) | Whether or not the subject key identifier (SKID) should be included in the certificate. | `bool` | `false` | no |
| <a name="input_stage"></a> [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
| <a name="input_subject"></a> [subject](#input\_subject) | The subject configuration for the certificate.<br>This should be a map that is compatible with [tls\_cert\_request.subject](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/cert_request#subject).<br><br>If `common_name` is omitted, it will be set as `module.this.id`. | `any` | `{}` | no |
Expand All @@ -233,6 +235,7 @@ Available targets:

| Name | Description |
|------|-------------|
| <a name="output_certificate_arn"></a> [certificate\_arn](#output\_certificate\_arn) | ARN of certificate stored in ACM that other services may need to refer to. This is useful when the certificate is stored in ACM. |
| <a name="output_certificate_key_path"></a> [certificate\_key\_path](#output\_certificate\_key\_path) | Secrets store path containing the certificate private key file. |
| <a name="output_certificate_pem"></a> [certificate\_pem](#output\_certificate\_pem) | Contents of the certificate PEM. |
| <a name="output_certificate_pem_path"></a> [certificate\_pem\_path](#output\_certificate\_pem\_path) | Secrets store path containing the certificate PEM file. |
Expand Down
6 changes: 6 additions & 0 deletions acm.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "aws_acm_certificate" "default" {
count = local.acm_enabled ? 1 : 0
private_key = local.tls_key
certificate_body = local.tls_certificate
certificate_chain = var.basic_constraints.ca ? var.certificate_chain : null
}
8 changes: 4 additions & 4 deletions asm.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resource "aws_secretsmanager_secret" "certificate" {

name = format(var.secret_path_format, module.this.name, var.secret_extensions.certificate)
recovery_window_in_days = var.asm_recovery_window_in_days
kms_key_id = local.secrets_store_kms_key_id
kms_key_id = local.certificate_backend_kms_key_id

tags = module.this.tags
}
Expand All @@ -12,15 +12,15 @@ resource "aws_secretsmanager_secret_version" "certificate" {
count = local.asm_enabled ? 1 : 0

secret_id = join("", aws_secretsmanager_secret.certificate.*.name)
secret_string = var.secrets_store_base64_enabled ? base64encode(local.tls_certificate) : local.tls_certificate
secret_string = var.certificate_backends_base64_enabled ? base64encode(local.tls_certificate) : local.tls_certificate
}

resource "aws_secretsmanager_secret" "private_key" {
count = local.asm_enabled ? 1 : 0

name = format(var.secret_path_format, module.this.name, var.secret_extensions.private_key)
recovery_window_in_days = var.asm_recovery_window_in_days
kms_key_id = local.secrets_store_kms_key_id
kms_key_id = local.certificate_backend_kms_key_id

tags = module.this.tags
}
Expand All @@ -29,5 +29,5 @@ resource "aws_secretsmanager_secret_version" "private_key" {
count = local.asm_enabled ? 1 : 0

secret_id = join("", aws_secretsmanager_secret.private_key.*.name)
secret_string = var.secrets_store_base64_enabled ? base64encode(local.tls_key) : local.tls_key
secret_string = var.certificate_backends_base64_enabled ? base64encode(local.tls_key) : local.tls_key
}
Loading

0 comments on commit 4831b05

Please sign in to comment.