diff --git a/.github/workflows/github-oidc-test.yaml b/.github/workflows/github-oidc-test.yaml deleted file mode 100644 index d4b903c7..00000000 --- a/.github/workflows/github-oidc-test.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Github OIDC test - -on: - workflow_dispatch: - inputs: - environment: - description: Target environment - required: true - type: choice - options: [dev, test, preprod] - -jobs: - listS3: - runs-on: ubuntu-latest - environment: ${{ inputs.environment }} - permissions: - id-token: write - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role - aws-region: eu-west-2 - - - name: List S3 bucket - run: | - aws s3 ls s3://eligibility-signposting-api-${{ inputs.environment }}-tfstate diff --git a/.github/workflows/manual-terraform-plan.yaml b/.github/workflows/manual-terraform-plan.yaml new file mode 100644 index 00000000..8b32921e --- /dev/null +++ b/.github/workflows/manual-terraform-plan.yaml @@ -0,0 +1,71 @@ +name: Github OIDC test + +on: + workflow_dispatch: + inputs: + environment: + description: Target environment + required: true + type: choice + options: [dev, test, preprod] + +jobs: + plan-stacks: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + permissions: + id-token: write + contents: read + + steps: + - name: "Setup Terraform" + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ vars.TF_VERSION }} + + - name: "Set up Python" + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: "Checkout Repository" + uses: actions/checkout@v4 + + - name: "Build lambda artefact" + run: | + make dependencies install-python + make build + - name: "Upload lambda artefact" + uses: actions/upload-artifact@v4 + with: + name: lambda + path: dist/lambda.zip + + - name: "Download Built Lambdas" + uses: actions/download-artifact@v4 + with: + name: lambda + path: ./build + + - name: "Configure AWS Credentials" + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role + aws-region: eu-west-2 + + - name: "Terraform Plan Stacks" + env: + ENVIRONMENT: "dev" + WORKSPACE: "default" + TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }} + TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }} + TF_VAR_API_PRIVATE_KEY_CERT: ${{ secrets.API_PRIVATE_KEY_CERT }} + + run: | + mkdir -p ./build + echo "Running: make terraform env=$ENVIRONMENT workspace=$ENVIRONMENT stack=networking tf-command=plan args=\"-auto-approve\"" + make terraform env=$ENVIRONMENT stack=networking tf-command=plan workspace=$ENVIRONMENT + echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=plan args=\"-auto-approve\"" + make terraform env=$ENVIRONMENT stack=api-layer tf-command=plan workspace=$WORKSPACE + + working-directory: ./infrastructure diff --git a/infrastructure/modules/_shared/default_variables.tf b/infrastructure/modules/_shared/default_variables.tf index 38738be7..1ebf1eb7 100644 --- a/infrastructure/modules/_shared/default_variables.tf +++ b/infrastructure/modules/_shared/default_variables.tf @@ -1,18 +1,31 @@ -# tflint-ignore: terraform_unused_declarations variable "project_name" { default = "eligibility-signposting-api" type = string } -# tflint-ignore: terraform_unused_declarations variable "environment" { description = "The purpose of the account dev/test/ref/prod or the workspace" type = string } -# tflint-ignore: terraform_unused_declarations variable "tags" { description = "A map of tags to assign to resources." type = map(string) default = {} } + +variable "workspace" { + description = "Usually the developer short code or the name of the environment." + type = string +} + +variable "stack_name" { + description = "The name of the stack being deployed" + type = string +} + +variable "region" { + type = string + description = "The aws region." + default = "eu-west-2" +} diff --git a/infrastructure/modules/api_gateway/api_gateway.tf b/infrastructure/modules/api_gateway/api_gateway.tf new file mode 100644 index 00000000..8bcf52e9 --- /dev/null +++ b/infrastructure/modules/api_gateway/api_gateway.tf @@ -0,0 +1,18 @@ +resource "aws_api_gateway_rest_api" "api_gateway" { + name = var.workspace == "default" ? "${var.api_gateway_name}-rest-api" : "${var.workspace}-${var.api_gateway_name}-rest-api" + description = "The API Gateway for ${var.project_name} ${var.environment} environment" + + disable_execute_api_endpoint = var.disable_default_endpoint # We would want to disable this if we are using a custom domain name + + lifecycle { + create_before_destroy = true + } + + tags = { + Stack = var.stack_name + } +} + +resource "aws_api_gateway_account" "api_gateway" { + cloudwatch_role_arn = aws_iam_role.api_gateway.arn +} diff --git a/infrastructure/modules/api_gateway/cloudwatch.tf b/infrastructure/modules/api_gateway/cloudwatch.tf new file mode 100644 index 00000000..b8edfbd6 --- /dev/null +++ b/infrastructure/modules/api_gateway/cloudwatch.tf @@ -0,0 +1,10 @@ +resource "aws_cloudwatch_log_group" "api_gateway" { + name = "/aws/apigateway/${var.workspace}-${var.api_gateway_name}" + retention_in_days = 14 + tags = var.tags + kms_key_id = aws_kms_key.api_gateway.arn + + lifecycle { + prevent_destroy = false + } +} diff --git a/infrastructure/modules/api_gateway/data.tf b/infrastructure/modules/api_gateway/data.tf new file mode 100644 index 00000000..8fc4b38c --- /dev/null +++ b/infrastructure/modules/api_gateway/data.tf @@ -0,0 +1 @@ +data "aws_caller_identity" "current" {} diff --git a/infrastructure/modules/api_gateway/default_variables.tf b/infrastructure/modules/api_gateway/default_variables.tf new file mode 120000 index 00000000..062daf61 --- /dev/null +++ b/infrastructure/modules/api_gateway/default_variables.tf @@ -0,0 +1 @@ +../_shared/default_variables.tf \ No newline at end of file diff --git a/infrastructure/modules/api_gateway/iam.tf b/infrastructure/modules/api_gateway/iam.tf new file mode 100644 index 00000000..20cd13aa --- /dev/null +++ b/infrastructure/modules/api_gateway/iam.tf @@ -0,0 +1,43 @@ +data "aws_iam_policy_document" "assume_role" { + statement { + effect = "Allow" + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["apigateway.amazonaws.com"] + } + } +} + +resource "aws_iam_role" "api_gateway" { + name = "${var.workspace}-${var.api_gateway_name}-role" + assume_role_policy = data.aws_iam_policy_document.assume_role.json +} + +data "aws_iam_policy_document" "api_gateway_logging" { + statement { + sid = "AllowCloudWatchLogging" + effect = "Allow" + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents", + "logs:GetLogEvents", + "logs:FilterLogEvents" + ] + resources = ["*"] + } +} + +resource "aws_iam_policy" "api_gateway_logging" { + name = "${var.workspace}-${var.api_gateway_name}-api-gateway-logging-policy" + description = "Policy to allow API Gateway push logs to Cloudwatch" + policy = data.aws_iam_policy_document.api_gateway_logging.json +} + +resource "aws_iam_role_policy_attachment" "api_gateway_logging" { + role = aws_iam_role.api_gateway.name + policy_arn = aws_iam_policy.api_gateway_logging.arn +} diff --git a/infrastructure/modules/api_gateway/kms.tf b/infrastructure/modules/api_gateway/kms.tf new file mode 100644 index 00000000..4994b9b4 --- /dev/null +++ b/infrastructure/modules/api_gateway/kms.tf @@ -0,0 +1,48 @@ +resource "aws_kms_key" "api_gateway" { + description = "${var.workspace} - KMS Key for ${var.api_gateway_name} API Gateway" + deletion_window_in_days = 14 + enable_key_rotation = true + + tags = { + Stack = var.stack_name + } +} + +resource "aws_kms_alias" "api_gateway" { + name = "alias/${var.workspace}-${var.api_gateway_name}-cloudwatch-logs" + target_key_id = aws_kms_key.api_gateway.key_id +} + +resource "aws_kms_key_policy" "api_gateway" { + key_id = aws_kms_key.api_gateway.id + policy = data.aws_iam_policy_document.api_gateway.json +} + +data "aws_iam_policy_document" "api_gateway" { + statement { + sid = "Enable IAM User Permissions for ${var.api_gateway_name} API Gateway" + effect = "Allow" + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + actions = ["kms:*"] + resources = [aws_kms_key.api_gateway.arn] + } + statement { + sid = "APIGatewayCloudwatchKMSAccess" + effect = "Allow" + principals { + type = "Service" + identifiers = ["logs.${var.region}.amazonaws.com"] + } + actions = [ + "kms:Encrypt*", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ] + resources = [aws_kms_key.api_gateway.arn] + } +} diff --git a/infrastructure/modules/api_gateway/outputs.tf b/infrastructure/modules/api_gateway/outputs.tf new file mode 100644 index 00000000..4937500f --- /dev/null +++ b/infrastructure/modules/api_gateway/outputs.tf @@ -0,0 +1,27 @@ +output "rest_api_id" { + value = aws_api_gateway_rest_api.api_gateway.id +} + +output "root_resource_id" { + value = aws_api_gateway_rest_api.api_gateway.root_resource_id +} + +output "execution_arn" { + value = aws_api_gateway_rest_api.api_gateway.execution_arn +} + +output "cloudwatch_destination_arn" { + value = aws_cloudwatch_log_group.api_gateway.arn +} + +output "api_gateway_account" { + value = aws_api_gateway_account.api_gateway +} + +output "logging_policy_attachment" { + value = aws_iam_role_policy_attachment.api_gateway_logging +} + +output "iam_role_name" { + value = aws_iam_role.api_gateway.name +} diff --git a/infrastructure/modules/api_gateway/providers.tf b/infrastructure/modules/api_gateway/providers.tf new file mode 100644 index 00000000..4b9d1aa1 --- /dev/null +++ b/infrastructure/modules/api_gateway/providers.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.11.1" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.6, != 5.71.0" + } + } +} diff --git a/infrastructure/modules/api_gateway/variables.tf b/infrastructure/modules/api_gateway/variables.tf new file mode 100644 index 00000000..a871aa48 --- /dev/null +++ b/infrastructure/modules/api_gateway/variables.tf @@ -0,0 +1,9 @@ +variable "api_gateway_name" { + type = string + description = "The name of the API Gateway" +} + +variable "disable_default_endpoint" { + type = bool + description = "Indicates whether the default endpoint the API Gateway generates should be disabled. If true, the API will need to be called from a Custom Domain Name" +} diff --git a/infrastructure/modules/lambda/outputs.tf b/infrastructure/modules/lambda/outputs.tf index dfb6abc1..7f27a237 100644 --- a/infrastructure/modules/lambda/outputs.tf +++ b/infrastructure/modules/lambda/outputs.tf @@ -4,3 +4,11 @@ output "aws_lambda_function_id" { output "aws_lambda_function_arn" { value = aws_lambda_function.eligibility_signposting_lambda.arn } + +output "aws_lambda_function_name" { + value = aws_lambda_function.eligibility_signposting_lambda.function_name +} + +output "aws_lambda_invoke_arn" { + value = aws_lambda_function.eligibility_signposting_lambda.invoke_arn +} diff --git a/infrastructure/modules/lambda/variables.tf b/infrastructure/modules/lambda/variables.tf index 23185dd6..4151a6ae 100644 --- a/infrastructure/modules/lambda/variables.tf +++ b/infrastructure/modules/lambda/variables.tf @@ -1,8 +1,3 @@ -variable "workspace" { - description = "Usually the developer short code or the name of the environment." - type = string -} - variable "eligibility_lambda_role_arn" { description = "lambda read role arn for dynamodb" type = string @@ -42,4 +37,3 @@ variable "eligibility_status_table_name" { description = "eligibility datastore table name" type = string } - diff --git a/infrastructure/modules/s3/outputs.tf b/infrastructure/modules/s3/outputs.tf index d6518eb5..e1aa6ba3 100644 --- a/infrastructure/modules/s3/outputs.tf +++ b/infrastructure/modules/s3/outputs.tf @@ -13,3 +13,7 @@ output "storage_bucket_arn" { output "storage_bucket_name" { value = aws_s3_bucket.storage_bucket.bucket } + +output "storage_bucket_id" { + value = aws_s3_bucket.storage_bucket.id +} diff --git a/infrastructure/stacks/api-layer/api_gateway.tf b/infrastructure/stacks/api-layer/api_gateway.tf new file mode 100644 index 00000000..de8f1efc --- /dev/null +++ b/infrastructure/stacks/api-layer/api_gateway.tf @@ -0,0 +1,106 @@ +module "eligibility_signposting_api_gateway" { + source = "../../modules/api_gateway" + api_gateway_name = "eligibility-signposting-api" + disable_default_endpoint = var.environment == "dev" && local.workspace != "default" ? false : true + workspace = local.workspace + stack_name = local.stack_name + environment = var.environment + tags = local.tags +} + +resource "aws_api_gateway_resource" "_status" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + parent_id = module.eligibility_signposting_api_gateway.root_resource_id + path_part = "_status" +} + +resource "aws_api_gateway_resource" "patient_check" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + parent_id = module.eligibility_signposting_api_gateway.root_resource_id + path_part = "patient-check" +} + +resource "aws_api_gateway_resource" "patient" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + parent_id = aws_api_gateway_resource.patient_check.id + path_part = "{id}" +} + +# deployment + +resource "aws_api_gateway_deployment" "eligibility_signposting_api" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + + triggers = { + redeployment = sha1(jsonencode([ + aws_api_gateway_integration.get_patient_check.id, + aws_api_gateway_integration._status.id, + ])) + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_api_gateway_stage" "eligibility-signposting-api" { + deployment_id = aws_api_gateway_deployment.eligibility_signposting_api.id + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + stage_name = "${local.workspace}-eligibility-signposting-api-live" + xray_tracing_enabled = true + + access_log_settings { + destination_arn = module.eligibility_signposting_api_gateway.cloudwatch_destination_arn + format = "{ \"requestId\":\"$context.requestId\", \"ip\": \"$context.identity.sourceIp\", \"caller\":\"$context.identity.caller\", \"user\":\"$context.identity.user\", \"requestTime\":\"$context.requestTime\", \"httpMethod\":\"$context.httpMethod\", \"resourcePath\":\"$context.resourcePath\", \"status\":\"$context.status\", \"protocol\":\"$context.protocol\", \"responseLength\":\"$context.responseLength\", \"accountId\":\"$context.accountId\", \"apiId\":\"$context.apiId\", \"stage\":\"$context.stage\", \"domainName\":\"$context.domainName\", \"error_message\":\"$context.error.message\", \"clientCertSerialNumber\":\"$context.identity.clientCert.serialNumber\", \"clientCertValidityNotBefore\":\"$context.identity.clientCert.validity.notBefore\", \"clientCertValidityNotAfter\":\"$context.identity.clientCert.validity.notAfter\" }" + } + + depends_on = [ + module.eligibility_signposting_api_gateway.api_gateway_account, + module.eligibility_signposting_api_gateway.logging_policy_attachment + ] +} + +resource "aws_api_gateway_method_settings" "check_eligibility" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + stage_name = aws_api_gateway_stage.eligibility-signposting-api.stage_name + method_path = "*/*" + + settings { + metrics_enabled = true + logging_level = "INFO" + } + + depends_on = [ + module.eligibility_signposting_api_gateway.api_gateway_account, + module.eligibility_signposting_api_gateway.logging_policy_attachment + ] +} + +resource "aws_api_gateway_domain_name" "check_eligibility" { + count = var.environment == "dev" && local.workspace != "default" ? 0 : 1 + domain_name = "${local.api_subdomain}.${local.api_domain_name}" + regional_certificate_arn = data.aws_acm_certificate.imported_cert.arn + ownership_verification_certificate_arn = data.aws_acm_certificate.validation_cert.arn + + mutual_tls_authentication { + truststore_uri = "s3://${module.s3_truststore_bucket.storage_bucket_name}/truststore.pem" + truststore_version = aws_s3_object.pem_file.version_id + } + + security_policy = "TLS_1_2" + + endpoint_configuration { + types = ["REGIONAL"] + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_api_gateway_base_path_mapping" "eligibility-signposting-api" { + count = var.environment == "dev" && local.workspace != "default" ? 0 : 1 + api_id = module.eligibility_signposting_api_gateway.rest_api_id + stage_name = aws_api_gateway_stage.eligibility-signposting-api.stage_name + domain_name = "${local.api_subdomain}.${local.api_domain_name}" +} diff --git a/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf b/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf new file mode 100644 index 00000000..2fd4e845 --- /dev/null +++ b/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf @@ -0,0 +1,80 @@ +# Policy document for Permissions boundary +data "aws_iam_policy_document" "assumed_role_permissions_boundary" { + #checkov:skip=CKV2_AWS_40: Ensure AWS IAM policy does not allow full IAM privileges + statement { + sid = "RestrictRegion" + effect = "Allow" + + actions = [ + "acm:*", + "application-autoscaling:*", + "apigateway:*", + "cloudtrail:*", + "cloudwatch:*", + "config:*", + "dynamodb:*", + "ec2:*", + "events:*", + "firehose:*", + "glue:*", + "health:*", + "iam:*", + "kms:*", + "lambda:*", + "logs:*", + "network-firewall:*", + "pipes:*", + "s3:*", + "schemas:*", + "sns:*", + "servicequotas:*", + "ssm:*", + "states:*", + "support:*", + "sqs:*", + "tag:*", + "trustedadvisor:*" + ] + + resources = ["*"] + + condition { + test = "StringEquals" + variable = "aws:RequestedRegion" + values = [var.default_aws_region] + } + } + + statement { + sid = "DenyPrivEsculationViaIamRoles" + effect = "Deny" + actions = ["iam:*"] + resources = ["*"] + condition { + test = "ArnLike" + variable = "iam:PolicyARN" + values = ["arn:aws:iam::*:policy/${upper(var.project_name)}-*"] + } + } + + statement { + sid = "DenyPrivEsculationViaIamProfiles" + effect = "Deny" + actions = ["iam:*"] + resources = ["arn:aws:iam::*:role/${upper(var.project_name)}-*"] + } +} + +# Permissions Boundary policy +resource "aws_iam_policy" "assumed_role_permissions_boundary" { + name = "${local.stack_name}-${upper(var.project_name)}-PermissionsBoundary" + description = "Allows access to AWS services in the regions the client uses only" + policy = data.aws_iam_policy_document.assumed_role_permissions_boundary.json + + tags = merge( + local.tags, + { + Stack = "api-layer" + } + ) +} diff --git a/infrastructure/stacks/api-layer/data.tf b/infrastructure/stacks/api-layer/data.tf index 8fc4b38c..ae018e6b 100644 --- a/infrastructure/stacks/api-layer/data.tf +++ b/infrastructure/stacks/api-layer/data.tf @@ -1 +1,30 @@ data "aws_caller_identity" "current" {} + +data "aws_acm_certificate" "imported_cert" { + domain = "${var.environment}.${local.api_domain_name}" + types = ["IMPORTED"] + provider = aws.eu-west-2 + key_types = ["RSA_4096"] +} + +data "aws_acm_certificate" "validation_cert" { + domain = "${var.environment}.${local.api_domain_name}" + types = ["AMAZON_ISSUED"] + provider = aws.eu-west-2 + key_types = ["RSA_2048"] + most_recent = true +} + +data "aws_kms_alias" "networking_ssm_key" { + name = "alias/dev-Networking-ssm-parameters" +} + +data "aws_ssm_parameter" "mtls_api_client_cert" { + name = "/${var.environment}/mtls/api_client_cert" + with_decryption = true +} + +data "aws_ssm_parameter" "mtls_api_ca_cert" { + name = "/${var.environment}/mtls/api_ca_cert" + with_decryption = true +} diff --git a/infrastructure/stacks/api-layer/healthcheck_status.tf b/infrastructure/stacks/api-layer/healthcheck_status.tf new file mode 100644 index 00000000..12671fca --- /dev/null +++ b/infrastructure/stacks/api-layer/healthcheck_status.tf @@ -0,0 +1,64 @@ +resource "aws_api_gateway_method" "_status" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource._status.id + http_method = "GET" + authorization = "NONE" + + depends_on = [ + aws_api_gateway_resource._status, + ] +} + +resource "aws_api_gateway_integration" "_status" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource._status.id + http_method = aws_api_gateway_method._status.http_method + type = "MOCK" + + request_templates = { + "application/json" = jsonencode({ + statusCode = 200 + }) + } +} + +resource "aws_api_gateway_method_response" "_status" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource._status.id + http_method = aws_api_gateway_method._status.http_method + status_code = "200" + + response_models = { + "application/json" = "Empty" + } +} + +resource "aws_api_gateway_integration_response" "_status" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource._status.id + http_method = aws_api_gateway_method._status.http_method + status_code = aws_api_gateway_method_response._status.status_code + + response_templates = { + "application/json" = jsonencode({ + status = "pass", + version = "", + revision = "", + releaseId = "", + commitId = "", + checks = { + "healthcheckService:status" = [ + { + status = "pass", + timeout = false, + responseCode = 200, + outcome = "

Ok

", + links = { + self = "http://healthcheckService.example.com/_status" + } + } + ] + } + }) + } +} diff --git a/infrastructure/stacks/api-layer/iam_roles.tf b/infrastructure/stacks/api-layer/iam_roles.tf index 94d833b8..e0f618fe 100644 --- a/infrastructure/stacks/api-layer/iam_roles.tf +++ b/infrastructure/stacks/api-layer/iam_roles.tf @@ -1,8 +1,4 @@ -data "aws_iam_policy" "permissions_boundary" { - arn = "arn:aws:iam::${local.current_account_id}:policy/${upper(var.project_name)}-PermissionsBoundary" -} - # Lambda trust policy data "aws_iam_policy_document" "lambda_assume_role" { @@ -30,12 +26,12 @@ data "aws_iam_policy_document" "dps_assume_role" { resource "aws_iam_role" "eligibility_lambda_role" { name = "eligibility_lambda-role${terraform.workspace == "default" ? "" : "-${terraform.workspace}"}" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json - permissions_boundary = data.aws_iam_policy.permissions_boundary.arn + permissions_boundary = aws_iam_policy.assumed_role_permissions_boundary.arn } resource "aws_iam_role" "write_access_role" { name = "external-write-role-${terraform.workspace == "default" ? "" : "-${terraform.workspace}"}" assume_role_policy = data.aws_iam_policy_document.dps_assume_role.json - permissions_boundary = data.aws_iam_policy.permissions_boundary.arn + permissions_boundary = aws_iam_policy.assumed_role_permissions_boundary.arn } diff --git a/infrastructure/stacks/api-layer/lambda.tf b/infrastructure/stacks/api-layer/lambda.tf index a6018a6e..d8e09005 100644 --- a/infrastructure/stacks/api-layer/lambda.tf +++ b/infrastructure/stacks/api-layer/lambda.tf @@ -22,5 +22,5 @@ module "eligibility_signposting_lambda_function" { handler = "eligibility_signposting_api.app.lambda_handler" eligibility_rules_bucket_name = module.s3_rules_bucket.storage_bucket_name eligibility_status_table_name = module.eligibility_status_table.table_name + stack_name = local.stack_name } - diff --git a/infrastructure/stacks/api-layer/locals.tf b/infrastructure/stacks/api-layer/locals.tf index 7d032685..e3094a3a 100644 --- a/infrastructure/stacks/api-layer/locals.tf +++ b/infrastructure/stacks/api-layer/locals.tf @@ -1,3 +1,12 @@ locals { stack_name = "api-layer" + + api_subdomain = var.environment + api_domain_name = "eligibility-signposting-api.nhs.uk" + + # PEM file for certificate + pem_file_content = join("\n", [ + data.aws_ssm_parameter.mtls_api_client_cert.value, + data.aws_ssm_parameter.mtls_api_ca_cert.value + ]) } diff --git a/infrastructure/stacks/api-layer/patient_check.tf b/infrastructure/stacks/api-layer/patient_check.tf new file mode 100644 index 00000000..d0d54d46 --- /dev/null +++ b/infrastructure/stacks/api-layer/patient_check.tf @@ -0,0 +1,34 @@ +resource "aws_api_gateway_method" "get_patient_check" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource.patient.id + http_method = "GET" + authorization = "NONE" + api_key_required = false + + depends_on = [ + aws_api_gateway_resource.patient, + aws_api_gateway_resource.patient_check, + ] +} + +resource "aws_api_gateway_integration" "get_patient_check" { + rest_api_id = module.eligibility_signposting_api_gateway.rest_api_id + resource_id = aws_api_gateway_resource.patient.id + http_method = aws_api_gateway_method.get_patient_check.http_method + integration_http_method = "POST" # Needed for lambda proxy integration + type = "AWS_PROXY" + uri = module.eligibility_signposting_lambda_function.aws_lambda_invoke_arn + + depends_on = [ + aws_api_gateway_method.get_patient_check + ] +} + +resource "aws_lambda_permission" "get_patient_check" { + statement_id = "AllowExecutionFromAPIGateway" + action = "lambda:InvokeFunction" + function_name = module.eligibility_signposting_lambda_function.aws_lambda_function_name + principal = "apigateway.amazonaws.com" + + source_arn = "${module.eligibility_signposting_api_gateway.execution_arn}/*/*" +} diff --git a/infrastructure/stacks/api-layer/provider.tf b/infrastructure/stacks/api-layer/provider.tf index b64be2af..40495eb6 100644 --- a/infrastructure/stacks/api-layer/provider.tf +++ b/infrastructure/stacks/api-layer/provider.tf @@ -1,3 +1,9 @@ provider "aws" { region = "eu-west-2" } + +# Used by ACM +provider "aws" { + alias = "eu-west-2" + region = "eu-west-2" +} diff --git a/infrastructure/stacks/api-layer/truststore_s3_bucket.tf b/infrastructure/stacks/api-layer/truststore_s3_bucket.tf new file mode 100644 index 00000000..c3dc330a --- /dev/null +++ b/infrastructure/stacks/api-layer/truststore_s3_bucket.tf @@ -0,0 +1,37 @@ +module "s3_truststore_bucket" { + source = "../../modules/s3" + bucket_name = "truststore" + environment = var.environment + project_name = var.project_name +} + +resource "aws_s3_bucket_policy" "truststore" { + bucket = module.s3_truststore_bucket.storage_bucket_id + policy = data.aws_iam_policy_document.truststore_api_gateway.json +} + +data "aws_iam_policy_document" "truststore_api_gateway" { + statement { + sid = "Enable S3 access permissions for API Gateway" + effect = "Allow" + + principals { + type = "Service" + identifiers = ["apigateway.amazonaws.com"] + } + + actions = ["s3:GetObject"] + + resources = [ + "${module.s3_truststore_bucket.storage_bucket_arn}/truststore.pem" + ] + } +} + +resource "aws_s3_object" "pem_file" { + bucket = module.s3_truststore_bucket.storage_bucket_name + key = "truststore.pem" + content = local.pem_file_content + + acl = "private" +} diff --git a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf index a7b4e897..c5e80d4f 100644 --- a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf +++ b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf @@ -55,13 +55,43 @@ resource "aws_iam_policy" "api_infrastructure" { # S3 permissions "s3:*", + # KMS permissions + "kms:List*", + "kms:Describe*", + "kms:GetKeyPolicy*", + "kms:GetKeyRotationStatus", + "kms:Decrypt*", + + # Cloudwatch permissions + "logs:Describe*", + "logs:ListTagsForResource", + + #EC2 permissions + "ec2:Describe*", + # IAM permissions (scoped to resources with specific path prefix) "iam:Get*", + "iam:GetPolicy*", + "iam:GetRole*", "iam:List*", "iam:Create*", "iam:Update*", "iam:Delete*", + + # ssm + "ssm:GetParameter", + "ssm:GetParameters", + "ssm:DescribeParameters", + "ssm:ListTagsForResource", + + # acm + "acm:ListCertificates", + "acm:DescribeCertificate", + "acm:GetCertificate", + "acm:ListTagsForCertificate", ], + + Resource = "*" } ] diff --git a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf index f6555f1b..27909c88 100644 --- a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf +++ b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf @@ -45,10 +45,24 @@ data "aws_iam_policy_document" "permissions_boundary" { } } + # Allow access to IAM actions for us-east-1 region only statement { - sid = "DenyPrivEsculationViaIamRoles" - effect = "Deny" - actions = ["iam:*"] + sid = "AllowIamActionsInUsEast1" + effect = "Allow" + actions = ["iam:*"] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "aws:RequestedRegion" + values = ["us-east-1"] + } + } + + statement { + sid = "DenyPrivEsculationViaIamRoles" + effect = "Deny" + actions = ["iam:*"] resources = ["*"] condition { test = "ArnLike" @@ -58,16 +72,16 @@ data "aws_iam_policy_document" "permissions_boundary" { } statement { - sid = "DenyPrivEsculationViaIamProfiles" - effect = "Deny" - actions = ["iam:*"] + sid = "DenyPrivEsculationViaIamProfiles" + effect = "Deny" + actions = ["iam:*"] resources = ["arn:aws:iam::*:role/${upper(var.project_name)}-*"] } } # Permissions Boundary policy resource "aws_iam_policy" "permissions_boundary" { - name = "${upper(var.project_name)}-PermissionsBoundary" + name = "${local.stack_name}-${upper(var.project_name)}-PermissionsBoundary" description = "Allows access to AWS services in the regions the client uses only" policy = data.aws_iam_policy_document.permissions_boundary.json diff --git a/infrastructure/stacks/iams-developer-roles/locals.tf b/infrastructure/stacks/iams-developer-roles/locals.tf new file mode 100644 index 00000000..1696637c --- /dev/null +++ b/infrastructure/stacks/iams-developer-roles/locals.tf @@ -0,0 +1,3 @@ +locals { + stack_name = "iams-developer-roles" +} diff --git a/infrastructure/stacks/networking/README.md b/infrastructure/stacks/networking/README.md index 6e1995c3..4bb7c715 100644 --- a/infrastructure/stacks/networking/README.md +++ b/infrastructure/stacks/networking/README.md @@ -1,78 +1,283 @@ -# Networking stack +# Networking Stack -The networking stack contains the networking resources that are securing the Eligibility Signposting API application resources. +This stack provisions the networking resources that secure the Eligibility Signposting API application. -The stack is documented on [this Confluence page](https://nhsd-confluence.digital.nhs.uk/spaces/Vacc/pages/1054575846/VPC+structure) +For a high-level overview, see the [VPC Structure Confluence Page](https://nhsd-confluence.digital.nhs.uk/spaces/Vacc/pages/1054575846/VPC+structure). + +--- + +## Table of Contents + +- [Networking Stack](#networking-stack) + - [Table of Contents](#table-of-contents) + - [Traffic Flow Explanation](#traffic-flow-explanation) + - [Public HTTPS Request Flow](#public-https-request-flow) + - [Outbound Internet Access](#outbound-internet-access) + - [Internal-Only Traffic](#internal-only-traffic) + - [Security Controls](#security-controls) + - [Network ACLs](#network-acls) + - [Security Groups](#security-groups) + - [Route Tables](#route-tables) + - [Deployment to AWS Development Environment](#deployment-to-aws-development-environment) + - [1. Initialize Terraform and Plan](#1-initialize-terraform-and-plan) + - [2. Apply Terraform Changes](#2-apply-terraform-changes) + - [Release Deployment to AWS (Int, Ref, and Prod)](#release-deployment-to-aws-int-ref-and-prod) + - [ACM Certificates Needed for Each Environment](#acm-certificates-needed-for-each-environment) + - [Domain Validation](#domain-validation) + - [Steps](#steps) + - [mTLS Certificates](#mtls-certificates) + - [1. Generate a CSR](#1-generate-a-csr) + - [2. Send the CSR for Signing](#2-send-the-csr-for-signing) + - [3. Add Root and Intermediate CAs to Truststore](#3-add-root-and-intermediate-cas-to-truststore) + - [4. Validate the CSR](#4-validate-the-csr) + - [5. Upload the CSR and Keys to SSM](#5-upload-the-csr-and-keys-to-ssm) + - [6. Upload the mTLS Secrets to Proxygen](#6-upload-the-mtls-secrets-to-proxygen) + - [Additional Resources](#additional-resources) + +--- ## Traffic Flow Explanation ### Public HTTPS Request Flow -* External client makes HTTPS request → Internet Gateway -* Request routes to Load Balancer or API Gateway in public subnet -* Request forwards to Lambda (or other application) in private subnet -* Lambda processes the request and returns response -* Response returns to client through the same path +1. External client makes HTTPS request → Internet Gateway +2. Request routes to Load Balancer or API Gateway in public subnet +3. Request forwards to Lambda (or other application) in private subnet +4. Lambda processes the request and returns response +5. Response returns to client through the same path ### Outbound Internet Access -* Lambda functions in private subnets can make outbound internet calls via NAT Gateways -* This allows Lambda to call external APIs, download packages, etc. -* No direct inbound access to Lambda from the internet +- Lambda functions in private subnets can make outbound internet calls via NAT Gateways +- No direct inbound access to Lambda from the internet ### Internal-Only Traffic -* Lambda functions access AWS services via VPC Endpoints: - * Gateway Endpoints: S3, DynamoDB - * Interface Endpoints: KMS, CloudWatch, SSM, Secrets Manager, Lambda, STS, SQS -* All traffic between Lambda and AWS services stays within the AWS network -* No internet transit required for AWS service access +- Lambda functions access AWS services via VPC Endpoints: + - **Gateway Endpoints:** S3, DynamoDB + - **Interface Endpoints:** KMS, CloudWatch, SSM, Secrets Manager, Lambda, STS, SQS +- All traffic between Lambda and AWS services stays within the AWS network -### Security Controls +--- -#### Network ACLs +## Security Controls -Public subnets: Allow HTTP(80), HTTPS(443), ephemeral ports -Private subnets: Allow VPC traffic and responses to outbound requests +### Network ACLs -#### Security Groups +- **Public subnets:** Allow HTTP (80), HTTPS (443), ephemeral ports +- **Private subnets:** Allow VPC traffic and responses to outbound requests -Default security group: Deny all -VPC Endpoint security group: Allow HTTPS(443) from within VPC +### Security Groups -#### Route Tables +- **Default security group:** Deny all +- **VPC Endpoint security group:** Allow HTTPS (443) from within VPC -Public subnets: Route to Internet Gateway for external access -Private subnets: Route to NAT Gateways for outbound-only access +### Route Tables -## Deployment to AWS Development Environment +- **Public subnets:** Route to Internet Gateway for external access +- **Private subnets:** Route to NAT Gateways for outbound-only access -This stack should only ever be deployed once per account (e.g. the use of Terraform workspaces is explicitly not recommended beyond specifying `dev` as the environment). +--- -Deployment to the Development environment is done through use of `make` commands +## Deployment to AWS Development Environment + +> **Note:** This stack should only be deployed once per account. Use of Terraform workspaces is not recommended beyond specifying `dev` as the environment. -### Initialize Terraform and Plan +### 1. Initialize Terraform and Plan -Run the following command to initialize Terraform and generate a plan. Replace `` with the target environment: +Run the following command to initialize Terraform and generate a plan. Replace `` with your target environment (e.g., `dev`): ```bash make terraform env=dev stack=networking tf-command=init workspace= +make terraform env=dev stack=networking tf-command=plan workspace= ``` -then +### 2. Apply Terraform Changes + +Deploy the Terraform configuration: ```bash -make terraform env=dev stack=networking tf-command=plan workspace= +make terraform env=dev stack=networking tf-command=apply workspace= ``` -### 1.4 Apply Terraform Changes +For more on Terraform, see the [Terraform Documentation](https://developer.hashicorp.com/terraform/docs). + +--- + +## Release Deployment to AWS (Int, Ref, and Prod) + +Deployment to Int, Ref, and Prod, as well as running automated tests, can be done via GitHub Actions (when implemented). + +--- + +## ACM Certificates Needed for Each Environment + +To verify domain ownership and enable secure communication with the APIM API Proxy, you need: + +1. **A domain validation certificate** +2. **mTLS certificates and secrets** + +> **Manual intervention is required for creation of these assets.** + +--- + +### Domain Validation + +The ACM Certificate Manager is used to request a domain validation certificate (see `infrastructure/stacks/networking/acm_certificates.tf`). +Running Terraform will create this certificate, but DNS validation requires action from the DNS Team. + +#### Steps + +1. In the AWS Console, navigate to **Certificate Manager** for the environment account. +2. In **Domains**, download the CSV of the CNAME details. +3. Email `england.dnsteam@nhs.net` with the subject: + `eligibility-signposting-api.nhs.uk - Requesting a sub domain - ` + Attach the exported CSV and use the following template: + + ```text + Hi DNS Team, + + Please could we add the subdomain '' for our top level domain 'eligibility-signposting-api.nhs.uk'? + + e.g. + + .eligibility-signposting-api.nhs.uk + + We've generated an ACM certificate for DNS validation, which has generated a CNAME name and value which I think would need adding to your records to allow us to validate. I've attached this to the email in a CSV, please let us know if this isn't the approach you expect. + + Thanks, + + Edd + ``` + +4. Check the certificate for DNS validation once you receive confirmation that the subdomain was created. + +- [AWS ACM DNS Validation Guide](https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html) + +--- + +### mTLS Certificates + +See the [APIM documentation](https://nhsd-confluence.digital.nhs.uk/spaces/APM/pages/734397569/How+to+implement+mutual+TLS+mTLS+security+for+your+API+backend) for more details. + +#### 1. Generate a CSR -Deploy the Terraform configuration using the following command: +1. Create a configuration file for the environment, e.g., `dev.conf`: + + ```ini + [req] + default_bits = 4096 + distinguished_name = req_distinguished_name + req_extensions = v3_req + prompt = no + + [req_distinguished_name] + C = GB + ST = West Yorkshire + L = Leeds + O = NHS England + OU = API Management + CN = dev.eligibility-signposting-api.nhs.uk + + [v3_req] + keyUsage = keyEncipherment, dataEncipherment + extendedKeyUsage = serverAuth + ``` + +2. Generate the CSR and private key: + + ```bash + mkdir -p csrs keys + openssl req -new -newkey rsa:4096 -nodes -sha256 \ + -keyout keys/.eligibility-signposting-api.nhs.uk.key \ + -out csrs/.eligibility-signposting-api.nhs.uk.csr \ + -config .conf -extensions v3_req + ``` + + - [OpenSSL CSR Generation Guide](https://www.openssl.org/docs/manmaster/man1/openssl-req.html) + +#### 2. Send the CSR for Signing + +- **Non-production environments:** + Email `itoc.supportdesk@nhs.net` and reference the INT / PTL environment. +- **Production environments:** + Email `dir@nhs.net`. + +Template: + +```text +Hi, + +Please could I get the attached CSR signed. Details as follows: + +Name: +Company/Organisation: Eligibility Data Team, NHS England +Contact Number: +Common Name (CN): dev.eligibility-signposting-api.nhs.uk +Reason for the Certificate: New APIM client +Environment: INT / PTL + +Thanks, + +Edd +``` + +#### 3. Add Root and Intermediate CAs to Truststore + +1. Download the INT CA certificates from [NHS Digital G2 Certificate Technical Implementation](https://digital.nhs.uk/services/spine/updating-nhs-public-key-infrastructure-certificates/g2-certificate-technical-implementation#certificate-downloads). +2. Concatenate the certificates (order matters): + + ```bash + cat intermediate-cert.pem root-cert.pem > truststore.pem + ``` + + This file is used in `infrastructure/stacks/api-layer/truststore_s3_bucket.tf`. + +3. Upload `truststore.pem` to the specified S3 bucket. + +#### 4. Validate the CSR ```bash -make terraform env=dev stack=networking tf-command=apply workspace= +openssl verify -CAfile truststore.pem new-cert.crt +``` + +You should see an `OK` response. + +#### 5. Upload the CSR and Keys to SSM + +Store the following in AWS SSM Parameter Store (see `infrastructure/stacks/networking/ssm.tf`): + +- `/${var.environment}/mtls/api_ca_cert` — the combined CA cert (`truststore.pem`) +- `/${var.environment}/mtls/api_client_cert` — the signed client certificate +- `/${var.environment}/mtls/api_private_key_cert` — the private key + +- [AWS SSM Parameter Store Documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) + +#### 6. Upload the mTLS Secrets to Proxygen + +Requires the [Proxygen CLI](https://github.com/NHSDigital/proxygen-cli). The [specification repository](https://github.com/NHSDigital/eligibility-signposting-api-specification) +includes the CLI, and includes details of how to set up authentication with Proxygen. + +After logging into AWS for the appropriate environment, run: + +```bash +mkdir -p ~/.proxygen +aws ssm get-parameter --name /$AWS_ENV/mtls/api_client_cert --with-decryption | jq ".Parameter.Value" --raw-output > ~/.proxygen/client_cert.pem +aws ssm get-parameter --name /$AWS_ENV/mtls/api_private_key_cert --with-decryption | jq ".Parameter.Value" --raw-output > ~/.proxygen/private_key.pem +proxygen secret put --mtls-cert ~/.proxygen/client_cert.pem --mtls-key ~/.proxygen/private_key.pem $APIM_ENV $secret_name +rm ~/.proxygen/client_cert.pem ~/.proxygen/private_key.pem ``` -## Release Deployment to AWS (Int, Ref and Prod) +--- + +## Additional Resources + +- [Terraform Documentation](https://developer.hashicorp.com/terraform/docs) +- [AWS ACM Documentation](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html) +- [AWS SSM Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) +- [OpenSSL Documentation](https://www.openssl.org/docs/) +- [Proxygen CLI](https://github.com/NHSDigital/proxygen-cli) + +--- -Deployment to Int, Ref and Prod, as well as running the automated tests can be done via GitHub actions, when they are developed. +*For any issues or questions, please contact the Eligibility Data Team.* diff --git a/infrastructure/stacks/networking/acm_certificates.tf b/infrastructure/stacks/networking/acm_certificates.tf new file mode 100644 index 00000000..ba5f9591 --- /dev/null +++ b/infrastructure/stacks/networking/acm_certificates.tf @@ -0,0 +1,35 @@ +resource "aws_acm_certificate" "imported_cert" { + private_key = aws_ssm_parameter.mtls_api_private_key_cert.value + certificate_body = aws_ssm_parameter.mtls_api_client_cert.value + certificate_chain = aws_ssm_parameter.mtls_api_ca_cert.value + + lifecycle { + create_before_destroy = true + } + + tags = { + Region = local.region + Stack = local.stack_name + CertificateType = "Imported" + } +} + +resource "aws_acm_certificate" "domain_validation" { + domain_name = "${var.environment}.eligibility-signposting-api.nhs.uk" + validation_method = "DNS" + + subject_alternative_names = var.environment != "prod" ? [ + "${var.environment}.eligibility-signposting-api.nhs.uk", + "*.${var.environment}.eligibility-signposting-api.nhs.uk" + ] : null + + lifecycle { + create_before_destroy = true + } + + tags = { + Region = local.region + Stack = local.stack_name + CerticateType = "DomainValidation" + } +} diff --git a/infrastructure/stacks/networking/ssm.tf b/infrastructure/stacks/networking/ssm.tf index f5fa3015..666396b9 100644 --- a/infrastructure/stacks/networking/ssm.tf +++ b/infrastructure/stacks/networking/ssm.tf @@ -10,35 +10,47 @@ # } # } # -# resource "aws_ssm_parameter" "mtls_api_ca_cert" { -# name = "/${var.environment}/mtls/api_ca_cert" -# type = "SecureString" -# key_id = aws_kms_key.networking_ssm_key.id -# value = var.API_CA_CERT -# tier = "Advanced" -# tags = { -# Stack = local.stack_name -# } -# } -# -# resource "aws_ssm_parameter" "mtls_api_client_cert" { -# name = "/${var.environment}/mtls/api_client_cert" -# type = "SecureString" -# key_id = aws_kms_key.networking_ssm_key.id -# value = var.API_CLIENT_CERT -# tier = "Advanced" -# tags = { -# Stack = local.stack_name -# } -# } -# -# resource "aws_ssm_parameter" "mtls_api_private_key_cert" { -# name = "/${var.environment}/mtls/api_private_key_cert" -# type = "SecureString" -# key_id = aws_kms_key.networking_ssm_key.id -# value = var.API_PRIVATE_KEY_CERT -# tier = "Advanced" -# tags = { -# Stack = local.stack_name -# } -# } +resource "aws_ssm_parameter" "mtls_api_ca_cert" { + name = "/${var.environment}/mtls/api_ca_cert" + type = "SecureString" + key_id = aws_kms_key.networking_ssm_key.id + value = var.API_CA_CERT + tier = "Advanced" + tags = { + Stack = local.stack_name + } + + lifecycle { + ignore_changes = [value] + } +} + +resource "aws_ssm_parameter" "mtls_api_client_cert" { + name = "/${var.environment}/mtls/api_client_cert" + type = "SecureString" + key_id = aws_kms_key.networking_ssm_key.id + value = var.API_CLIENT_CERT + tier = "Advanced" + tags = { + Stack = local.stack_name + } + + lifecycle { + ignore_changes = [value] + } +} + +resource "aws_ssm_parameter" "mtls_api_private_key_cert" { + name = "/${var.environment}/mtls/api_private_key_cert" + type = "SecureString" + key_id = aws_kms_key.networking_ssm_key.id + value = var.API_PRIVATE_KEY_CERT + tier = "Advanced" + tags = { + Stack = local.stack_name + } + + lifecycle { + ignore_changes = [value] + } +} diff --git a/infrastructure/stacks/networking/variables.tf b/infrastructure/stacks/networking/variables.tf index 66a5ed0a..54b28dce 100644 --- a/infrastructure/stacks/networking/variables.tf +++ b/infrastructure/stacks/networking/variables.tf @@ -1,22 +1,15 @@ -# variable "API_CA_CERT" { -# type = string -# description = "The Certificate Authority (CA) Root for the Client Certificate with Intermediate Certificate" -# sensitive = true -# default = "Test Value" -# -# } -# -# variable "API_CLIENT_CERT" { -# type = string -# description = "The signed Client Certificate" -# sensitive = true -# default = "Test Value" -# } -# -# variable "API_PRIVATE_KEY_CERT" { -# type = string -# description = "The private key for the signed Client Certificate" -# sensitive = true -# default = "Test Value" -# } -# +variable "API_CA_CERT" { + type = string + description = "The Certificate Authority (CA) Root for the Client Certificate with Intermediate Certificate" + sensitive = true +} +variable "API_CLIENT_CERT" { + type = string + description = "The signed Client Certificate" + sensitive = true +} +variable "API_PRIVATE_KEY_CERT" { + type = string + description = "The private key for the signed Client Certificate" + sensitive = true +}