Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDD-2443 Create cognito and api gateway modules using terraform #1201

Merged
merged 38 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
45710e5
CDD-2443 Create cognito module using terraform
ChristianAMartin Jan 22, 2025
8e0dd34
CDD-2443 Add identity providers and SNS code
ChristianAMartin Jan 24, 2025
98d46a9
CDD-2443 Refactor based on PR comments
ChristianAMartin Jan 24, 2025
7cca0f2
CDD-2443 Add placeholders until identity providers are known
ChristianAMartin Jan 27, 2025
dd38cb5
CDD-2443 Create user mig pre signup and post auth lambdas
ChristianAMartin Jan 28, 2025
393d682
CDD-2443 Create api gateway
ChristianAMartin Jan 29, 2025
49d33a1
CDD-2443 Auto generate secret for cognito user pool
ChristianAMartin Jan 29, 2025
83a2ec2
CDD-2443 Add output for cognito user pool client secret
ChristianAMartin Jan 29, 2025
92d3fe1
CDD-2443 Restrict oauth flow to code and scope to openid. Tighten cog…
ChristianAMartin Jan 30, 2025
466c207
CDD-2443 Disable MFA config and remove SMS config block
ChristianAMartin Jan 30, 2025
45a05f1
CDD-2443 Amended naming conventions and added extra lambda logging
ChristianAMartin Jan 30, 2025
4d55280
CDD-2443 Amended api gateway handler name
ChristianAMartin Jan 30, 2025
35339d2
CDD-2443 Auto confirm user and email for debugging
ChristianAMartin Jan 30, 2025
d3a43de
CDD-2443 Amend pre signup lambda logging
ChristianAMartin Jan 30, 2025
5b7ff7d
CDD-2443 Remove default tags from main.tf
ChristianAMartin Feb 3, 2025
9828355
CDD-2443 Remove default tags from aws main.tf
ChristianAMartin Feb 3, 2025
2c416eb
chore: setup env vars
8lane Feb 3, 2025
4a13c4c
chore: enable auth
8lane Feb 3, 2025
56349d3
chore: update callback and logout urls
8lane Feb 3, 2025
7a33590
chore: debug
8lane Feb 3, 2025
3e7ce74
CDD-2443 Add additional logging for post auth lambda
ChristianAMartin Feb 4, 2025
7bd1bb7
fix: whitelist next-auth cookies in cloudfront
8lane Feb 4, 2025
ea06d41
CDD-2443 Add pre auth lambda
ChristianAMartin Feb 4, 2025
d714f35
CDD-2443 Add description for lambdas
ChristianAMartin Feb 4, 2025
e29233f
CDD-2443 Add description for api gateway lambda
ChristianAMartin Feb 4, 2025
c196f66
chore: update CF auth headers + Cognito callback urls
8lane Feb 4, 2025
96b1c11
chore: is_dev condition for cognito callbacks
8lane Feb 4, 2025
aca1ac0
CDD-2443 Changes to token timeouts and lambda role and policy
ChristianAMartin Feb 4, 2025
07700d9
CDD-2443 Removal of multi IdP and pre post logging lambdas
ChristianAMartin Feb 11, 2025
764940a
fix: remove lambda triggers and tweak cognito config
8lane Feb 4, 2025
2303967
fix: authjs upgrades
8lane Feb 7, 2025
b6f2127
chore: oauth domain passed to fe & fix expiry units
8lane Feb 12, 2025
48ae0f2
chore: additional auth.js cookies
8lane Feb 12, 2025
58d62b9
chore: callback url
8lane Feb 12, 2025
7a82231
chore: allowlist set-cookie and change expiry units
8lane Feb 13, 2025
f652c60
chore: add missing delete secret keys
8lane Feb 14, 2025
9bb3747
CDD-2443 Action PR comments
ChristianAMartin Feb 18, 2025
03694f1
CDD-2443 Pin SNS module to version 6.1.2
ChristianAMartin Feb 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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