Skip to content

Commit 8eb6d56

Browse files
committed
changes to secure dbt docs using microsoft sso
1 parent 8cab847 commit 8eb6d56

File tree

16 files changed

+683
-0
lines changed

16 files changed

+683
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# main.tf
2+
module "sso_auth" {
3+
source = "../modules/cloudfront-microsoft-sso"
4+
5+
env_code = var.env_code
6+
project_code = var.project_code
7+
app_code = var.app_code
8+
aws_region = var.aws_region
9+
use_env_code_flag = var.use_env_code_flag
10+
enable_auth_flag = var.enable_auth_flag
11+
12+
lambda_runtime = var.lambda_runtime
13+
sso_config_arn = aws_secretsmanager_secret.sso_config.arn
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
output "dbt__cloudfront_distribution__domain_name" {
2+
value = module.sso_auth.cloudfront_distribution__domain_name
3+
}
4+
5+
output "dbt__aws_s3_bucket__arn" {
6+
value = module.sso_auth.aws_s3_bucket__arn
7+
}
8+
9+
output "dbt__secret_arn" {
10+
description = "The ARN of the SSO secret"
11+
value = aws_secretsmanager_secret.sso_config.arn
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "4.59.0"
6+
}
7+
}
8+
}
9+
10+
provider "aws" {
11+
region = var.aws_region
12+
profile = "terraform"
13+
14+
default_tags {
15+
tags = {
16+
"environment" = "${lower(var.env_code)}"
17+
"created_by" = "terraform"
18+
}
19+
}
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
resource "aws_secretsmanager_secret" "sso_config" {
2+
name = "dbt-sso-secret"
3+
description = "SSO config (tenant, client_id, client_secret, redirect_uri)"
4+
recovery_window_in_days = 0
5+
}
6+
7+
resource "aws_secretsmanager_secret_version" "sso_config_version" {
8+
secret_id = aws_secretsmanager_secret.sso_config.id
9+
secret_string = jsonencode({
10+
tenant = var.sso_tenant_id
11+
client_id = var.sso_client_id
12+
client_secret = var.sso_client_secret
13+
redirect_uri = var.sso_redirect_uri
14+
})
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
env_code = "dev"
2+
project_code = "entechlog"
3+
app_code = "dbt-docs"
4+
aws_region = "us-east-1"
5+
use_env_code_flag = true
6+
enable_auth_flag = true
7+
8+
sso_tenant_id = ""
9+
sso_client_id = ""
10+
sso_client_secret = ""
11+
sso_redirect_uri = ""
12+
13+
lambda_runtime = "nodejs16.x"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
variable "env_code" {
2+
type = string
3+
description = "Environmental code to identify the target environment"
4+
default = "dev"
5+
}
6+
7+
variable "project_code" {
8+
type = string
9+
description = "Project code used as a prefix for resource names"
10+
default = "entechlog"
11+
}
12+
13+
variable "app_code" {
14+
type = string
15+
description = "Application code used as a prefix for resource names"
16+
default = "dbt-docs"
17+
}
18+
19+
variable "aws_region" {
20+
type = string
21+
description = "Primary region for all AWS resources"
22+
default = "us-east-1"
23+
}
24+
25+
variable "use_env_code_flag" {
26+
type = bool
27+
description = "Toggle on/off the env code in resource names"
28+
default = true
29+
}
30+
31+
variable "enable_auth_flag" {
32+
type = bool
33+
description = "Toggle on/off the SSO authentication"
34+
default = true
35+
}
36+
37+
# -- SSO details: you will store these in Secrets Manager outside the module
38+
variable "sso_tenant_id" {
39+
type = string
40+
description = "Tenant ID for the SSO config"
41+
}
42+
43+
variable "sso_client_id" {
44+
type = string
45+
description = "Client ID for the SSO config"
46+
}
47+
48+
variable "sso_client_secret" {
49+
type = string
50+
description = "Client Secret for the SSO config"
51+
sensitive = true
52+
}
53+
54+
variable "sso_redirect_uri" {
55+
type = string
56+
description = "Redirect URI for the SSO flow"
57+
}
58+
59+
# Optional override for Node.js runtime (the module defaults to nodejs18.x if not specified)
60+
variable "lambda_runtime" {
61+
type = string
62+
description = "Runtime for the Lambda functions"
63+
default = "nodejs18.x"
64+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
resource "aws_cloudfront_origin_access_identity" "app" {
2+
comment = "${var.app_code} CloudFront origin access identity"
3+
}
4+
5+
resource "aws_cloudfront_distribution" "app" {
6+
origin {
7+
domain_name = aws_s3_bucket.app.bucket_regional_domain_name
8+
origin_id = "s3"
9+
10+
s3_origin_config {
11+
origin_access_identity = aws_cloudfront_origin_access_identity.app.cloudfront_access_identity_path
12+
}
13+
}
14+
15+
enabled = true
16+
is_ipv6_enabled = true
17+
comment = "${var.app_code} CloudFront distribution"
18+
default_root_object = "index.html"
19+
20+
# Behavior for SSO callback handling – triggered on SSO provider redirect.
21+
ordered_cache_behavior {
22+
path_pattern = "callback*"
23+
target_origin_id = "s3"
24+
viewer_protocol_policy = "redirect-to-https"
25+
allowed_methods = ["GET", "HEAD", "OPTIONS"]
26+
cached_methods = ["GET", "HEAD"]
27+
forwarded_values {
28+
query_string = true
29+
cookies {
30+
forward = "all"
31+
}
32+
}
33+
dynamic "lambda_function_association" {
34+
for_each = var.enable_auth_flag ? [1] : []
35+
content {
36+
# viewer-request (same as your original)
37+
event_type = "viewer-request"
38+
lambda_arn = aws_lambda_function.sso_callback.qualified_arn
39+
include_body = false
40+
}
41+
}
42+
}
43+
44+
# Default behavior – all other requests go through the SSO authenticator Lambda
45+
default_cache_behavior {
46+
target_origin_id = "s3"
47+
viewer_protocol_policy = "redirect-to-https"
48+
allowed_methods = ["GET", "HEAD", "OPTIONS"]
49+
cached_methods = ["GET", "HEAD"]
50+
forwarded_values {
51+
query_string = false
52+
cookies {
53+
forward = "none"
54+
}
55+
}
56+
dynamic "lambda_function_association" {
57+
for_each = var.enable_auth_flag ? [1] : []
58+
content {
59+
event_type = "viewer-request"
60+
lambda_arn = aws_lambda_function.sso_authenticator.qualified_arn
61+
include_body = false
62+
}
63+
}
64+
min_ttl = 0
65+
default_ttl = 3600
66+
max_ttl = 86400
67+
}
68+
69+
price_class = "PriceClass_100"
70+
71+
restrictions {
72+
geo_restriction {
73+
restriction_type = "none"
74+
}
75+
}
76+
77+
viewer_certificate {
78+
cloudfront_default_certificate = true
79+
}
80+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
data "aws_caller_identity" "current" {}
2+
3+
data "aws_region" "current" {}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
###############################
2+
# Data blocks: IAM policy docs
3+
###############################
4+
5+
# 1) Trust policy for Lambda@Edge
6+
data "aws_iam_policy_document" "lambda_edge_assume_role" {
7+
statement {
8+
effect = "Allow"
9+
actions = ["sts:AssumeRole"]
10+
principals {
11+
type = "Service"
12+
identifiers = [
13+
"lambda.amazonaws.com",
14+
"edgelambda.amazonaws.com"
15+
]
16+
}
17+
}
18+
}
19+
20+
# 2) Secrets access policy if your Lambda needs to read from Secrets Manager
21+
data "aws_iam_policy_document" "lambda_edge_secrets_access" {
22+
statement {
23+
effect = "Allow"
24+
actions = [
25+
# Secrets Manager
26+
"secretsmanager:GetSecretValue",
27+
"secretsmanager:DescribeSecret",
28+
# CloudWatch Logs
29+
"logs:PutLogEvents",
30+
"logs:CreateLogStream",
31+
"logs:CreateLogGroup",
32+
# KMS
33+
"kms:Decrypt"
34+
]
35+
resources = [
36+
# Secrets in the same account/region
37+
"arn:aws:secretsmanager:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*",
38+
39+
# CloudWatch log groups for Lambda
40+
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*",
41+
42+
# KMS keys in the same account/region
43+
"arn:aws:kms:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"
44+
]
45+
}
46+
}
47+
48+
###############################
49+
# IAM Role for Lambda@Edge
50+
###############################
51+
52+
resource "aws_iam_role" "lambda_edge" {
53+
name = "${local.resource_name_prefix}-lambda-edge-role"
54+
assume_role_policy = data.aws_iam_policy_document.lambda_edge_assume_role.json
55+
}
56+
57+
###############################
58+
# Attach Logging Policy
59+
###############################
60+
61+
resource "aws_iam_role_policy_attachment" "lambda_edge_logs" {
62+
role = aws_iam_role.lambda_edge.name
63+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
64+
}
65+
66+
###############################
67+
# Attach Secrets Access Policy
68+
###############################
69+
70+
resource "aws_iam_role_policy" "lambda_edge_secrets_access" {
71+
name = "${local.resource_name_prefix}-edge-secrets-policy"
72+
role = aws_iam_role.lambda_edge.id
73+
policy = data.aws_iam_policy_document.lambda_edge_secrets_access.json
74+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
locals {
2+
# If you want to track code changes in .js/.json:
3+
sso_authenticator_files = fileset(local.sso_authenticator_dir, "*.{js,json}")
4+
sso_authenticator_sha = sha256(join(",", [
5+
for file in local.sso_authenticator_files : filesha256("${local.sso_authenticator_dir}/${file}")
6+
]))
7+
8+
sso_callback_files = fileset(local.sso_callback_dir, "*.{js,json}")
9+
sso_callback_sha = sha256(join(",", [
10+
for file in local.sso_callback_files : filesha256("${local.sso_callback_dir}/${file}")
11+
]))
12+
}
13+
14+
###############################
15+
# Package & deploy SSO Authenticator
16+
###############################
17+
18+
resource "null_resource" "sso_authenticator" {
19+
# No local-exec block here anymore
20+
triggers = {
21+
deployable_dir = local.sso_authenticator_sha
22+
}
23+
}
24+
25+
data "archive_file" "sso_authenticator" {
26+
depends_on = [null_resource.sso_authenticator]
27+
type = "zip"
28+
source_dir = local.sso_authenticator_dir
29+
output_path = "${local.sso_authenticator_dir}/payload.zip"
30+
excludes = ["payload.zip"]
31+
output_file_mode = "0666"
32+
}
33+
34+
resource "aws_lambda_function" "sso_authenticator" {
35+
function_name = "${lower(var.project_code)}-sso-authenticator"
36+
role = aws_iam_role.lambda_edge.arn
37+
filename = data.archive_file.sso_authenticator.output_path
38+
runtime = var.lambda_runtime
39+
handler = "authenticator.handler"
40+
source_code_hash = data.archive_file.sso_authenticator.output_base64sha256
41+
publish = true
42+
}
43+
44+
###############################
45+
# Package & deploy SSO Callback
46+
###############################
47+
48+
resource "null_resource" "sso_callback" {
49+
# No local-exec block here anymore
50+
triggers = {
51+
deployable_dir = local.sso_callback_sha
52+
}
53+
}
54+
55+
data "archive_file" "sso_callback" {
56+
depends_on = [null_resource.sso_callback]
57+
type = "zip"
58+
source_dir = local.sso_callback_dir
59+
output_path = "${local.sso_callback_dir}/payload.zip"
60+
excludes = ["payload.zip"]
61+
output_file_mode = "0666"
62+
}
63+
64+
resource "aws_lambda_function" "sso_callback" {
65+
function_name = "${lower(var.project_code)}-sso-callback"
66+
role = aws_iam_role.lambda_edge.arn
67+
filename = data.archive_file.sso_callback.output_path
68+
runtime = var.lambda_runtime
69+
handler = "callback-handler.handler"
70+
source_code_hash = data.archive_file.sso_callback.output_base64sha256
71+
publish = true
72+
}

0 commit comments

Comments
 (0)