diff --git a/.github/actions/trivy/action.yaml b/.github/actions/trivy/action.yaml index be940ce..010386e 100644 --- a/.github/actions/trivy/action.yaml +++ b/.github/actions/trivy/action.yaml @@ -8,8 +8,13 @@ runs: components_exit_code=0 modules_exit_code=0 - ./scripts/terraform/trivy.sh ./infrastructure/terraform/components || components_exit_code=$? - ./scripts/terraform/trivy.sh ./infrastructure/terraform/modules || modules_exit_code=$? + if [ -d ./infrastructure/terraform/components ]; then + ./scripts/terraform/trivy.sh ./infrastructure/terraform/components || components_exit_code=$? + fi + + if [ -d ./infrastructure/terraform/modules ]; then + ./scripts/terraform/trivy.sh ./infrastructure/terraform/modules || modules_exit_code=$? + fi if [ $components_exit_code -ne 0 ] || [ $modules_exit_code -ne 0 ]; then echo "Trivy misconfigurations detected." diff --git a/infrastructure/modules/ssl/README.md b/infrastructure/modules/ssl/README.md new file mode 100644 index 0000000..d7ecda3 --- /dev/null +++ b/infrastructure/modules/ssl/README.md @@ -0,0 +1,40 @@ + + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.10.1 | +| [tls](#requirement\_tls) | 4.1.0 | +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | +| [component](#input\_component) | The name of the tfscaffold component | `string` | n/a | yes | +| [default\_tags](#input\_default\_tags) | A map of default tags to apply to all taggable resources within the component | `map(string)` | `{}` | no | +| [environment](#input\_environment) | The name of the tfscaffold environment | `string` | n/a | yes | +| [name](#input\_name) | A unique name to distinguish this module invocation from others within the same CSI scope | `string` | n/a | yes | +| [project](#input\_project) | The name of the tfscaffold project | `string` | n/a | yes | +| [region](#input\_region) | The AWS Region | `string` | n/a | yes | +| [subject\_common\_name](#input\_subject\_common\_name) | Common name for certificate subject | `string` | n/a | yes | +| [subject\_country](#input\_subject\_country) | Country for certificate subject | `string` | `"GB"` | no | +| [subject\_locality](#input\_subject\_locality) | Locality for certificate subject | `string` | `"Leeds"` | no | +| [subject\_organization](#input\_subject\_organization) | Organization for certificate subject | `string` | `"NHS England"` | no | +| [subject\_organizational\_unit](#input\_subject\_organizational\_unit) | Organizational unit for certificate subject | `string` | `"NHS Notify"` | no | +| [subject\_province](#input\_subject\_province) | Province for certificate subject | `string` | `"West Yorkshire"` | no | +## Modules + +No modules. +## Outputs + +| Name | Description | +|------|-------------| +| [cacert\_pem](#output\_cacert\_pem) | Truststore | +| [server\_crt](#output\_server\_crt) | Server Certificate | +| [server\_key](#output\_server\_key) | Server Key | + + + diff --git a/infrastructure/modules/ssl/locals.tf b/infrastructure/modules/ssl/locals.tf new file mode 100644 index 0000000..398b1a4 --- /dev/null +++ b/infrastructure/modules/ssl/locals.tf @@ -0,0 +1,24 @@ +locals { + module = "ssl" + + # Compound Scope Identifier + csi = replace( + format( + "%s-%s-%s-%s", + var.project, + var.environment, + var.component, + var.name + ), + "_", + "", + ) + + default_tags = merge( + var.default_tags, + { + Module = local.module + Name = local.csi + }, + ) +} diff --git a/infrastructure/modules/ssl/outputs.tf b/infrastructure/modules/ssl/outputs.tf new file mode 100644 index 0000000..4241a55 --- /dev/null +++ b/infrastructure/modules/ssl/outputs.tf @@ -0,0 +1,17 @@ +output "server_crt" { + description = "Server Certificate" + value = tls_locally_signed_cert.integration_testing_client_cert.cert_pem + sensitive = true +} + +output "server_key" { + description = "Server Key" + value = tls_private_key.integration_testing_client_key.private_key_pem + sensitive = true +} + +output "cacert_pem" { + description = "Truststore" + value = tls_self_signed_cert.ca_cert.cert_pem + sensitive = true +} diff --git a/infrastructure/modules/ssl/providers.tf b/infrastructure/modules/ssl/providers.tf new file mode 100644 index 0000000..ad6ac72 --- /dev/null +++ b/infrastructure/modules/ssl/providers.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + } + tls = { + source = "hashicorp/tls" + version = "4.1.0" + } + } +} diff --git a/infrastructure/modules/ssl/ssm_parameter_ca_crt.tf b/infrastructure/modules/ssl/ssm_parameter_ca_crt.tf new file mode 100644 index 0000000..c824b9d --- /dev/null +++ b/infrastructure/modules/ssl/ssm_parameter_ca_crt.tf @@ -0,0 +1,9 @@ +resource "aws_ssm_parameter" "ca_crt" { + name = format("/%s/%s/${local.module}/ca-crt", var.project, var.environment) + type = "SecureString" + value = tls_self_signed_cert.ca_cert.cert_pem + + lifecycle { + create_before_destroy = true + } +} diff --git a/infrastructure/modules/ssl/ssm_parameter_ca_key.tf b/infrastructure/modules/ssl/ssm_parameter_ca_key.tf new file mode 100644 index 0000000..7c25260 --- /dev/null +++ b/infrastructure/modules/ssl/ssm_parameter_ca_key.tf @@ -0,0 +1,9 @@ +resource "aws_ssm_parameter" "ca_key" { + name = format("/%s/%s/${local.module}/ca-key", var.project, var.environment) + type = "SecureString" + value = tls_private_key.ca_key.private_key_pem + + lifecycle { + create_before_destroy = true + } +} diff --git a/infrastructure/modules/ssl/ssm_parameter_server_crt.tf b/infrastructure/modules/ssl/ssm_parameter_server_crt.tf new file mode 100644 index 0000000..d3cff53 --- /dev/null +++ b/infrastructure/modules/ssl/ssm_parameter_server_crt.tf @@ -0,0 +1,9 @@ +resource "aws_ssm_parameter" "server_crt" { + name = format("/%s/%s/${local.module}/server-crt", var.project, var.environment) + type = "SecureString" + value = tls_locally_signed_cert.integration_testing_client_cert.cert_pem + + lifecycle { + create_before_destroy = true + } +} diff --git a/infrastructure/modules/ssl/ssm_parameter_server_key.tf b/infrastructure/modules/ssl/ssm_parameter_server_key.tf new file mode 100644 index 0000000..c9cabcb --- /dev/null +++ b/infrastructure/modules/ssl/ssm_parameter_server_key.tf @@ -0,0 +1,9 @@ +resource "aws_ssm_parameter" "server_key" { + name = format("/%s/%s/${local.module}/server-key", var.project, var.environment) + type = "SecureString" + value = tls_private_key.integration_testing_client_key.private_key_pem + + lifecycle { + create_before_destroy = true + } +} diff --git a/infrastructure/modules/ssl/tls_cert_request_server_csr.tf b/infrastructure/modules/ssl/tls_cert_request_server_csr.tf new file mode 100644 index 0000000..7f2f88f --- /dev/null +++ b/infrastructure/modules/ssl/tls_cert_request_server_csr.tf @@ -0,0 +1,19 @@ +resource "tls_cert_request" "server_csr" { + + private_key_pem = tls_private_key.integration_testing_client_key.private_key_pem + + dns_names = [var.subject_common_name] + + subject { + country = var.subject_country + province = var.subject_province + locality = var.subject_locality + common_name = var.subject_common_name + organization = var.subject_organization + organizational_unit = var.subject_organizational_unit + } + + depends_on = [ + tls_private_key.integration_testing_client_key, + ] +} diff --git a/infrastructure/modules/ssl/tls_locally_signed_cert_server_cert.tf b/infrastructure/modules/ssl/tls_locally_signed_cert_server_cert.tf new file mode 100644 index 0000000..25c1b65 --- /dev/null +++ b/infrastructure/modules/ssl/tls_locally_signed_cert_server_cert.tf @@ -0,0 +1,21 @@ +resource "tls_locally_signed_cert" "integration_testing_client_cert" { + cert_request_pem = tls_cert_request.server_csr.cert_request_pem + ca_private_key_pem = tls_private_key.ca_key.private_key_pem + ca_cert_pem = tls_self_signed_cert.ca_cert.cert_pem + + validity_period_hours = 8760 + + allowed_uses = [ + "digital_signature", + "key_encipherment", + "server_auth", + "client_auth", + ] + + depends_on = [ + tls_private_key.ca_key, + tls_self_signed_cert.ca_cert, + tls_private_key.integration_testing_client_key, + tls_cert_request.server_csr + ] +} diff --git a/infrastructure/modules/ssl/tls_private_key_ca_key.tf b/infrastructure/modules/ssl/tls_private_key_ca_key.tf new file mode 100644 index 0000000..e34a634 --- /dev/null +++ b/infrastructure/modules/ssl/tls_private_key_ca_key.tf @@ -0,0 +1,4 @@ +resource "tls_private_key" "ca_key" { + algorithm = "RSA" + rsa_bits = 4096 +} diff --git a/infrastructure/modules/ssl/tls_private_key_server_key.tf b/infrastructure/modules/ssl/tls_private_key_server_key.tf new file mode 100644 index 0000000..3dd8848 --- /dev/null +++ b/infrastructure/modules/ssl/tls_private_key_server_key.tf @@ -0,0 +1,4 @@ +resource "tls_private_key" "integration_testing_client_key" { + algorithm = "RSA" + rsa_bits = 4096 +} diff --git a/infrastructure/modules/ssl/tls_self_signed_cert_ca_cert.tf b/infrastructure/modules/ssl/tls_self_signed_cert_ca_cert.tf new file mode 100644 index 0000000..7c43574 --- /dev/null +++ b/infrastructure/modules/ssl/tls_self_signed_cert_ca_cert.tf @@ -0,0 +1,22 @@ +resource "tls_self_signed_cert" "ca_cert" { + private_key_pem = tls_private_key.ca_key.private_key_pem + + is_ca_certificate = true + + subject { + country = var.subject_country + province = var.subject_province + locality = var.subject_locality + common_name = var.subject_common_name + organization = var.subject_organization + organizational_unit = var.subject_organizational_unit + } + + validity_period_hours = 17520 + + allowed_uses = [ + "digital_signature", + "cert_signing", + "crl_signing", + ] +} diff --git a/infrastructure/modules/ssl/variables.tf b/infrastructure/modules/ssl/variables.tf new file mode 100644 index 0000000..28b9b15 --- /dev/null +++ b/infrastructure/modules/ssl/variables.tf @@ -0,0 +1,82 @@ +## +# Basic Required Variables for tfscaffold Modules +## + +variable "project" { + type = string + description = "The name of the tfscaffold project" +} + +variable "environment" { + type = string + description = "The name of the tfscaffold environment" +} + +variable "component" { + type = string + description = "The name of the tfscaffold component" +} + +variable "aws_account_id" { + type = string + description = "The AWS Account ID (numeric)" +} + +variable "region" { + type = string + description = "The AWS Region" +} + +## +# tfscaffold variables specific to this module +## + +variable "default_tags" { + type = map(string) + description = "A map of default tags to apply to all taggable resources within the component" + default = {} +} + +## +# Variables specific to this module +## + +variable "name" { + type = string + description = "A unique name to distinguish this module invocation from others within the same CSI scope" +} + +variable "subject_country" { + type = string + description = "Country for certificate subject" + default = "GB" +} + +variable "subject_province" { + type = string + description = "Province for certificate subject" + default = "West Yorkshire" +} + +variable "subject_locality" { + type = string + description = "Locality for certificate subject" + default = "Leeds" +} + +variable "subject_common_name" { + type = string + description = "Common name for certificate subject" +} + +variable "subject_organization" { + type = string + description = "Organization for certificate subject" + default = "NHS England" +} + +variable "subject_organizational_unit" { + type = string + description = "Organizational unit for certificate subject" + default = "NHS Notify" +} diff --git a/infrastructure/modules/ssl/versions.tf b/infrastructure/modules/ssl/versions.tf new file mode 100644 index 0000000..c43599a --- /dev/null +++ b/infrastructure/modules/ssl/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.10.1" +}