Skip to content

Commit

Permalink
Merge pull request #1201 from UKHSA-Internal/task/add-infra-cognito-d…
Browse files Browse the repository at this point in the history
…b/CDD-2443

CDD-2443 Create cognito and api gateway modules using terraform
  • Loading branch information
ChristianAMartin authored Feb 19, 2025
2 parents 09841c8 + 03694f1 commit ae44589
Show file tree
Hide file tree
Showing 22 changed files with 870 additions and 6 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/deploy-personal-dev-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,12 @@ jobs:
restart_services:
name: Restart services
runs-on: ubuntu-latest
needs: ["push_frontend_docker_image", "push_backend_docker_image", "push_ingestion_docker_image"]
needs:
[
"push_frontend_docker_image",
"push_backend_docker_image",
"push_ingestion_docker_image",
]
steps:
- uses: actions/checkout@v4

Expand Down
4 changes: 3 additions & 1 deletion scripts/_secrets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ function _delete_all_secrets() {
"uhd-${env}-feature-flags-api-keys"
"uhd-${env}-esri-api-key"
"uhd-${env}-esri-maps-service-credentials"
"uhd-${env}-slack-webhook-url")
"uhd-${env}-slack-webhook-url"
"uhd-${env}-cognito-service-credentials"
"uhd-${env}-auth-secret")

for ((i=1; i<=${#secret_ids[@]}; ++i)); do
_delete_secret "${secret_ids[i]}"
Expand Down
13 changes: 13 additions & 0 deletions terraform/20-app/api-gateway.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module "api_gateway" {
source = "../modules/api-gateway"
name = "${local.prefix}-api-gateway"
description = "API Gateway for ${local.prefix}"
api_gateway_stage_name = var.api_gateway_stage_name
lambda_role_arn = module.cognito.cognito_lambda_role_arn
cognito_user_pool_arn = module.cognito.cognito_user_pool_arn
region = local.region
resource_path_part = "{proxy+}"
lambda_invoke_arn = module.api_gateway.lambda_alias_arn
lambda_function_arn = module.api_gateway.api_gateway_lambda_arn
prefix = local.prefix
}
10 changes: 10 additions & 0 deletions terraform/20-app/api-keys.tf
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,18 @@ resource "random_password" "feature_flags_admin_user_password" {
special = true
}

resource "random_password" "auth_secret" {
length = 32
min_numeric = 1
min_lower = 1
min_upper = 1
min_special = 1
special = true
}

locals {
feature_flags_x_auth = random_password.feature_flags_x_auth.result
feature_flags_client_api_key = "*:production.${random_password.feature_flags_client_api_key.result}"
private_api_key = "${random_password.private_api_key_prefix.result}.${random_password.private_api_key_suffix.result}"
auth_secret = random_password.auth_secret.result
}
53 changes: 50 additions & 3 deletions terraform/20-app/cloud-front.front-end.tf
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module "cloudfront_front_end" {
} : {}
}

ordered_cache_behavior = [
ordered_cache_behavior = flatten(concat([
# Behaviour to bypass cloudfront for health check
{
path_pattern = "api/health"
Expand All @@ -81,6 +81,21 @@ module "cloudfront_front_end" {
viewer_protocol_policy = "redirect-to-https"
query_string = false
},
# Behaviour to enable cookie forwarding for auth endpoints
local.is_auth ? [
{
path_pattern = "/api/auth/*"
allowed_methods = ["HEAD", "DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"]
cache_policy_id = local.managed_caching_disabled_policy_id
cached_methods = ["GET", "HEAD"]
compress = true
origin_request_policy_id = aws_cloudfront_origin_request_policy.front_end_auth.id
response_headers_policy_id = aws_cloudfront_response_headers_policy.front_end.id
target_origin_id = "alb"
use_forwarded_values = false
viewer_protocol_policy = "redirect-to-https"
}
] : [],
# Behaviour to bypass CDN for the dynamic alert pages
{
path_pattern = "/"
Expand Down Expand Up @@ -142,7 +157,7 @@ module "cloudfront_front_end" {
use_forwarded_values = false
viewer_protocol_policy = "redirect-to-https"
},
]
]))

custom_error_response = [
{
Expand All @@ -169,7 +184,16 @@ resource "aws_cloudfront_origin_request_policy" "front_end" {
cookies_config {
cookie_behavior = "whitelist"
cookies {
items = ["UKHSAConsentGDPR"]
items = flatten(concat(["UKHSAConsentGDPR", local.is_auth ? [
"__Secure-authjs.callback-url", # Stores the redirect destination after authentication
"__Secure-authjs.csrf-token", # CSRF token required for authentication flows
"__Secure-authjs.session-token", # Main session token
"__Secure-authjs.session-token.0", # Split session token (if size exceeds 4KB)
"__Secure-authjs.session-token.1", # Additional split session token
"__Secure-authjs.session-token.2", # Additional split session token
"__Secure-authjs.session-token.3", # Additional split session token
"__Secure-authjs.session-token.4", # Additional split session token (safety margin)
] : []]))
}
}
headers_config {
Expand All @@ -180,6 +204,29 @@ resource "aws_cloudfront_origin_request_policy" "front_end" {
}
}

resource "aws_cloudfront_origin_request_policy" "front_end_auth" {
name = "${local.prefix}-front-end-auth"

cookies_config {
cookie_behavior = "all"
}

headers_config {
header_behavior = "whitelist"
headers {
items = [
"Accept",
"Content-Type",
"Set-Cookie",
]
}
}

query_strings_config {
query_string_behavior = "all"
}
}

################################################################################
# Cache policies
################################################################################
Expand Down
60 changes: 60 additions & 0 deletions terraform/20-app/cognito.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module "cognito" {
source = "../modules/cognito"
sns_role_arn = aws_iam_role.cognito_sns_role.arn
user_pool_name = "${local.prefix}-user-pool"
client_name = "${local.prefix}-client"
user_pool_domain = "${local.prefix}-domain"
callback_urls = concat(
["https://${terraform.workspace}.dev.ukhsa-dashboard.data.gov.uk/api/auth/callback/cognito"],
local.is_dev ? ["http://localhost:3000/api/auth/callback/cognito", "http://localhost:3001/api/auth/callback/cognito"] : []
)
logout_urls = concat(
["https://${terraform.workspace}.dev.ukhsa-dashboard.data.gov.uk"],
local.is_dev ? ["http://localhost:3000", "http://localhost:3001"] : []
)
region = local.region

ukhsa_oidc_client_id = "ukhsa-oidc-client-id"
ukhsa_oidc_client_secret = "ukhsa-oidc-client-secret"
ukhsa_oidc_issuer_url = "https://example.com/issuer"
ukhsa_oidc_attributes_url = "https://example.com/attributes"

lambda_role_arn = aws_iam_role.cognito_lambda_role.arn
prefix = local.prefix
}

module "app_security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 5.0"

name = "${local.prefix}-security-group"
description = "Security group for the application"
vpc_id = module.vpc.vpc_id

computed_ingress_with_source_security_group_id = [
{
rule = "postgresql-tcp"
source_security_group_id = module.aurora_db_app.security_group_id
}
]
number_of_computed_ingress_with_source_security_group_id = 1

egress_rules = ["all-all"]

tags = {
project_name = local.project
env = terraform.workspace
}
}

output "cognito_user_pool_id" {
description = "Cognito User Pool ID"
value = module.cognito.cognito_user_pool_id
sensitive = true
}

output "cognito_user_pool_client_id" {
description = "Cognito User Pool Client ID"
value = module.cognito.cognito_user_pool_client_id
sensitive = true
}
26 changes: 25 additions & 1 deletion terraform/20-app/ecs.service.front-end.tf
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,15 @@ module "ecs_service_front_end" {
{
name = "AUTH_ENABLED",
value = local.is_auth
}
},
{
name = "AUTH_DOMAIN"
value = module.cognito.cognito_oauth_url
},
{
name = "NEXTAUTH_URL"
value = local.urls.front_end
},
]
secrets = [
{
Expand Down Expand Up @@ -107,6 +115,22 @@ module "ecs_service_front_end" {
{
name = "ESRI_CLIENT_SECRET"
valueFrom = "${aws_secretsmanager_secret.esri_maps_service_credentials.arn}:client_secret::"
},
{
name = "AUTH_SECRET"
valueFrom = "${aws_secretsmanager_secret.auth_secret.arn}:auth_secret::"
},
{
name = "AUTH_CLIENT_URL"
valueFrom = "${aws_secretsmanager_secret.cognito_service_credentials.arn}:client_url::"
},
{
name = "AUTH_CLIENT_ID"
valueFrom = "${aws_secretsmanager_secret.cognito_service_credentials.arn}:client_id::"
},
{
name = "AUTH_CLIENT_SECRET"
valueFrom = "${aws_secretsmanager_secret.cognito_service_credentials.arn}:client_secret::"
}
]
}
Expand Down
1 change: 1 addition & 0 deletions terraform/20-app/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ terraform {
key = "app/state.tfstate"
}
}

1 change: 1 addition & 0 deletions terraform/20-app/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,4 @@ output "lambda" {
ingestion_lambda_arn = module.lambda_ingestion.lambda_function_arn
}
}

36 changes: 36 additions & 0 deletions terraform/20-app/secret-manager.tf
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,42 @@ resource "aws_secretsmanager_secret_version" "google_analytics_credentials" {
})
}

################################################################################
# Cognito
################################################################################

resource "aws_secretsmanager_secret" "cognito_service_credentials" {
name = "${local.prefix}-cognito-service-credentials"
description = "These are the credentials required for AWS Congito service."
kms_key_id = module.kms_secrets_app_engineer.key_id
}

resource "aws_secretsmanager_secret_version" "cognito_service_credentials" {
secret_id = aws_secretsmanager_secret.cognito_service_credentials.id
secret_string = jsonencode({
client_url = module.cognito.cognito_user_pool_issuer_endpoint
client_id = module.cognito.cognito_user_pool_client_id
client_secret = module.cognito.cognito_user_pool_client_secret
})
}

################################################################################
# NextAuth
################################################################################

resource "aws_secretsmanager_secret" "auth_secret" {
name = "${local.prefix}-auth-secret"
description = "Used to encrypt the NextAuth.js JWT"
kms_key_id = module.kms_secrets_app_operator.key_id
}

resource "aws_secretsmanager_secret_version" "auth_secret" {
secret_id = aws_secretsmanager_secret.auth_secret.id
secret_string = jsonencode({
auth_secret = local.auth_secret
})
}

################################################################################
# ESRI maps credentials
################################################################################
Expand Down
69 changes: 69 additions & 0 deletions terraform/20-app/sns.cognito.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module "cognito_sns" {
source = "terraform-aws-modules/sns/aws"
version = "6.1.2"

name = "${local.prefix}-cognito-topic"

subscriptions = [
{
protocol = "email"
endpoint = var.cognito_admin_email
}
]
}

resource "aws_iam_role" "cognito_sns_role" {
name = "${local.prefix}-cognito-sns-role"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "cognito-idp.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_policy" "cognito_sns_policy" {
name = "${local.prefix}-cognito-sns-policy"
description = "Allows Cognito to publish messages to the SNS topic"

policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Sid = "AllowCognitoToPublish",
Effect = "Allow",
Action = ["sns:Publish"],
Resource = module.cognito_sns.topic_arn
}
]
})
}

resource "aws_iam_role_policy_attachment" "cognito_sns_policy_attachment" {
role = aws_iam_role.cognito_sns_role.id
policy_arn = aws_iam_policy.cognito_sns_policy.arn
}

resource "aws_iam_role" "cognito_lambda_role" {
name = "${local.prefix}-lambda-execution-role"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "lambda.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}
12 changes: 12 additions & 0 deletions terraform/20-app/vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ variable "single_nat_gateway" {
}

variable "halo_account_type" {}

variable "api_gateway_stage_name" {
description = "The stage name for API Gateway (e.g. dev or live)"
type = string
default = "dev"
}

variable "cognito_admin_email" {
description = "Admin email address for Cognito SNS notifications"
type = string
default = "[email protected]"
}
Loading

0 comments on commit ae44589

Please sign in to comment.