Skip to content

Commit

Permalink
feat: add DDoS protection to ALB and hosted zone (#150)
Browse files Browse the repository at this point in the history
Adds the ALB and Route53 hosted zone to AWS Shield Advanced's
DDoS protection.

Also creates DDoS CloudWatch alarms and the SNS topics,
subscriptions and KMS keys required for the us-east-1
hosted zone alarm.
  • Loading branch information
patheard authored Dec 8, 2021
1 parent 6cc72ac commit 865430e
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 4 deletions.
45 changes: 45 additions & 0 deletions aws/alarms/cloudwatch.tf
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,48 @@ resource "aws_cloudwatch_event_rule" "codedeploy_sns" {
Terraform = true
}
}

#
# Shield Advanced DDoS detection: ALB and Route53
#
resource "aws_cloudwatch_metric_alarm" "alb_ddos" {
alarm_name = "ALBDDoS"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "DDoSDetected"
namespace = "AWS/DDoSProtection"
period = "60"
statistic = "Sum"
threshold = "0"
treat_missing_data = "notBreaching"

alarm_description = "DDoS detection for ALB"
alarm_actions = [aws_sns_topic.alert_warning.arn]
ok_actions = [aws_sns_topic.alert_ok.arn]

dimensions = {
ResourceArn = var.lb_arn
}
}

resource "aws_cloudwatch_metric_alarm" "route53_ddos" {
provider = aws.us-east-1

alarm_name = "Route53DDoS"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "DDoSDetected"
namespace = "AWS/DDoSProtection"
period = "60"
statistic = "Sum"
threshold = "0"
treat_missing_data = "notBreaching"

alarm_description = "DDoS detection for Route53"
alarm_actions = [aws_sns_topic.alert_warning_us_east.arn]
ok_actions = [aws_sns_topic.alert_ok_us_east.arn]

dimensions = {
ResourceArn = "arn:aws:route53:::hostedzone/${var.hosted_zone_id}"
}
}
5 changes: 5 additions & 0 deletions aws/alarms/inputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ variable "kms_key_cloudwatch_arn" {
type = string
}

variable "kms_key_cloudwatch_us_east_arn" {
description = "CloudWatch KMS key ARN in us-east-1, used by SNS topics"
type = string
}

variable "lb_arn" {
description = "Load balancer ARN, used by DDoS alarms"
type = string
Expand Down
38 changes: 38 additions & 0 deletions aws/alarms/sns.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ resource "aws_sns_topic" "alert_ok" {
}
}

resource "aws_sns_topic" "alert_warning_us_east" {
provider = aws.us-east-1

name = "alert-warning"
kms_master_key_id = var.kms_key_cloudwatch_us_east_arn
tags = {
(var.billing_tag_key) = var.billing_tag_value
Terraform = true
}
}

resource "aws_sns_topic" "alert_ok_us_east" {
provider = aws.us-east-1

name = "alert-ok"
kms_master_key_id = var.kms_key_cloudwatch_us_east_arn
tags = {
(var.billing_tag_key) = var.billing_tag_value
Terraform = true
}
}

#
# SNS topic subscriptions
#
Expand All @@ -35,6 +57,22 @@ resource "aws_sns_topic_subscription" "topic_ok" {
endpoint = aws_lambda_function.notify_slack_sns.arn
}

resource "aws_sns_topic_subscription" "topic_warning_us_east" {
provider = aws.us-east-1

topic_arn = aws_sns_topic.alert_warning_us_east.arn
protocol = "lambda"
endpoint = aws_lambda_function.notify_slack_sns.arn
}

resource "aws_sns_topic_subscription" "topic_ok_us_east" {
provider = aws.us-east-1

topic_arn = aws_sns_topic.alert_ok_us_east.arn
protocol = "lambda"
endpoint = aws_lambda_function.notify_slack_sns.arn
}

#
# CloudWatch Policy
#
Expand Down
13 changes: 13 additions & 0 deletions aws/kms/kms.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ resource "aws_kms_key" "cloudwatch" {
}
}

resource "aws_kms_key" "cloudwatch_us_east" {
provider = aws.us-east-1

description = "CloudWatch Log Group Key"
enable_key_rotation = true
policy = data.aws_iam_policy_document.kms_cloudwatch.json

tags = {
(var.billing_tag_key) = var.billing_tag_value
Terraform = true
}
}

data "aws_iam_policy_document" "kms_cloudwatch" {
# checkov:skip=CKV_AWS_109: `resources = ["*"]` identifies the KMS key to which the key policy is attached
# checkov:skip=CKV_AWS_111: `resources = ["*"]` identifies the KMS key to which the key policy is attached
Expand Down
5 changes: 5 additions & 0 deletions aws/kms/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ output "kms_key_cloudwatch_arn" {
value = aws_kms_key.cloudwatch.arn
}

output "kms_key_cloudwatch_us_east_arn" {
description = "CloudWatch KMS key ARN in us-east-1"
value = aws_kms_key.cloudwatch_us_east.arn
}

output "kms_key_dynamodb_arn" {
description = "DynamoDB KMS key ARN"
value = aws_kms_key.dynamo_db.arn
Expand Down
19 changes: 19 additions & 0 deletions aws/load_balancer/shield.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
resource "aws_shield_protection" "alb" {
name = "LoadBalancer"
resource_arn = aws_lb.form_viewer.arn

tags = {
(var.billing_tag_key) = var.billing_tag_value
Terraform = true
}
}

resource "aws_shield_protection" "route53_hosted_zone" {
name = "Route53HostedZone"
resource_arn = "arn:aws:route53:::hostedzone/${var.hosted_zone_id}"

tags = {
(var.billing_tag_key) = var.billing_tag_value
Terraform = true
}
}
6 changes: 6 additions & 0 deletions env/common/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ provider "aws" {
region = var.region
allowed_account_ids = [var.account_id]
}

provider "aws" {
alias = "us-east-1"
region = "us-east-1"
allowed_account_ids = [var.account_id]
}
11 changes: 9 additions & 2 deletions env/production/alarms/terragrunt.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependency "hosted_zone" {
config_path = "../hosted_zone"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
hosted_zone_id = ""
}
Expand All @@ -19,15 +20,18 @@ dependency "kms" {
config_path = "../kms"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
kms_key_cloudwatch_arn = ""
kms_key_cloudwatch_arn = ""
kms_key_cloudwatch_us_east_arn = ""
}
}

dependency "load_balancer" {
config_path = "../load_balancer"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
lb_arn = ""
lb_arn_suffix = ""
Expand All @@ -38,6 +42,7 @@ dependency "sqs" {
config_path = "../sqs"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
sqs_deadletter_queue_arn = ""
}
Expand All @@ -47,6 +52,7 @@ dependency "app" {
config_path = "../app"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
ecs_cloudwatch_log_group_name = ""
ecs_cluster_name = ""
Expand All @@ -61,7 +67,8 @@ inputs = {

hosted_zone_id = dependency.hosted_zone.outputs.hosted_zone_id

kms_key_cloudwatch_arn = dependency.kms.outputs.kms_key_cloudwatch_arn
kms_key_cloudwatch_arn = dependency.kms.outputs.kms_key_cloudwatch_arn
kms_key_cloudwatch_us_east_arn = dependency.kms.outputs.kms_key_cloudwatch_us_east_arn

lb_arn = dependency.load_balancer.outputs.lb_arn
lb_arn_suffix = dependency.load_balancer.outputs.lb_arn_suffix
Expand Down
11 changes: 9 additions & 2 deletions env/staging/alarms/terragrunt.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependency "hosted_zone" {
config_path = "../hosted_zone"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
hosted_zone_id = ""
}
Expand All @@ -19,15 +20,18 @@ dependency "kms" {
config_path = "../kms"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
kms_key_cloudwatch_arn = ""
kms_key_cloudwatch_arn = ""
kms_key_cloudwatch_us_east_arn = ""
}
}

dependency "load_balancer" {
config_path = "../load_balancer"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
lb_arn = ""
lb_arn_suffix = ""
Expand All @@ -38,6 +42,7 @@ dependency "sqs" {
config_path = "../sqs"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
sqs_deadletter_queue_arn = ""
}
Expand All @@ -47,6 +52,7 @@ dependency "app" {
config_path = "../app"

mock_outputs_allowed_terraform_commands = ["init", "fmt", "validate", "plan", "show"]
mock_outputs_merge_with_state = true
mock_outputs = {
ecs_cloudwatch_log_group_name = ""
ecs_cluster_name = ""
Expand All @@ -61,7 +67,8 @@ inputs = {

hosted_zone_id = dependency.hosted_zone.outputs.hosted_zone_id

kms_key_cloudwatch_arn = dependency.kms.outputs.kms_key_cloudwatch_arn
kms_key_cloudwatch_arn = dependency.kms.outputs.kms_key_cloudwatch_arn
kms_key_cloudwatch_us_east_arn = dependency.kms.outputs.kms_key_cloudwatch_us_east_arn

lb_arn = dependency.load_balancer.outputs.lb_arn
lb_arn_suffix = dependency.load_balancer.outputs.lb_arn_suffix
Expand Down

0 comments on commit 865430e

Please sign in to comment.