From 96d08263a8eadf056dcede4a788b5b24af76c0ad Mon Sep 17 00:00:00 2001 From: Cris Daniluk Date: Wed, 30 Jun 2021 20:04:44 -0400 Subject: [PATCH] allow alb support for situations where waf is required --- alb.tf | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++ elb.tf | 24 ++++++---- groups.tf | 25 +++++++--- main.tf | 22 +++++++-- outputs.tf | 23 +++++++-- variables.tf | 119 ++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 307 insertions(+), 37 deletions(-) create mode 100644 alb.tf diff --git a/alb.tf b/alb.tf new file mode 100644 index 0000000..2ecedb7 --- /dev/null +++ b/alb.tf @@ -0,0 +1,131 @@ +resource "aws_security_group" "alb_https" { + count = var.create_alb ? 1 : 0 + + name_prefix = var.name + description = "Bitbucket Inbound LB (HTTPS)" + vpc_id = var.vpc_id + + tags = merge( + var.tags, + var.alb_additional_sg_tags, + { "Name" : var.name } + ) + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_security_group_rule" "alb_https_egress" { + count = var.create_alb ? 1 : 0 + + description = "Allow traffic from the LB to the instances" + from_port = 7990 + protocol = "tcp" + security_group_id = aws_security_group.alb_https[0].id + source_security_group_id = aws_security_group.this.id + to_port = 7990 + type = "egress" +} + + +resource "aws_security_group_rule" "alb_https_ingress" { + count = var.create_alb && length(var.alb_allowed_https_cidr_blocks) > 0 ? 1 : 0 + + cidr_blocks = var.alb_allowed_https_cidr_blocks #tfsec:ignore:AWS006 + description = "Allow HTTPS traffic from the allowed ranges" + from_port = var.alb_https_port + protocol = "tcp" + security_group_id = aws_security_group.alb_https[0].id + to_port = var.alb_https_port + type = "ingress" +} + +resource "aws_lb" "https" { + count = var.create_alb ? 1 : 0 + + name_prefix = substr(var.name, 0, 6) + internal = var.alb_https_internal + security_groups = [aws_security_group.alb_https[0].id] + subnets = var.alb_https_subnets + tags = var.tags + + dynamic "access_logs" { + for_each = var.access_logs_enabled ? ["this"] : [] + + content { + bucket = var.access_logs_bucket + prefix = var.access_logs_prefix + enabled = var.access_logs_enabled + } + } +} + + +resource "aws_lb_listener" "https" { + count = var.create_alb ? 1 : 0 + + certificate_arn = var.alb_certificate + load_balancer_arn = aws_lb.https[0].arn + port = var.alb_https_port + protocol = "HTTPS" + ssl_policy = var.alb_ssl_policy + tags = var.tags + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.https[0].arn + } +} + +resource "aws_lb_target_group" "https" { + count = var.create_alb ? 1 : 0 + + name_prefix = substr(var.name, 0, 6) + port = 7990 + protocol = "HTTP" + tags = var.tags + vpc_id = var.vpc_id + + health_check { + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 3 + path = "/status" + interval = 30 + } +} + +resource "aws_lb" "ssh" { + count = var.create_alb ? 1 : 0 + + name_prefix = substr(var.name, 0, 6) + internal = var.alb_ssh_internal + load_balancer_type = "network" + subnets = var.alb_ssh_subnets + tags = var.tags +} + +resource "aws_lb_listener" "ssh" { + count = var.create_alb ? 1 : 0 + + load_balancer_arn = aws_lb.ssh[0].arn + port = var.alb_ssh_port + protocol = "TCP" + tags = var.tags + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.ssh[0].arn + } +} + +resource "aws_lb_target_group" "ssh" { + count = var.create_alb ? 1 : 0 + + name_prefix = substr(var.name, 0, 6) + port = 7999 + protocol = "TCP" + tags = var.tags + vpc_id = var.vpc_id +} diff --git a/elb.tf b/elb.tf index ad80b0c..c7c2b1a 100644 --- a/elb.tf +++ b/elb.tf @@ -1,4 +1,6 @@ resource "aws_security_group" "elb" { + count = var.create_alb ? 0 : 1 + name_prefix = var.name description = "Bitbucket Inbound ELB" vpc_id = var.vpc_id @@ -6,7 +8,7 @@ resource "aws_security_group" "elb" { tags = merge( var.tags, var.elb_additional_sg_tags, - { "Name" : "${var.name}" } + { "Name" : var.name } ) lifecycle { @@ -15,53 +17,59 @@ resource "aws_security_group" "elb" { } resource "aws_security_group_rule" "elb_egress" { + count = var.create_alb ? 0 : 1 + description = "Allow traffic from the ELB to the instances" from_port = 7990 protocol = "tcp" - security_group_id = aws_security_group.elb.id + security_group_id = aws_security_group.elb[0].id source_security_group_id = aws_security_group.this.id to_port = 7990 type = "egress" } resource "aws_security_group_rule" "elb_egress_ssh" { + count = var.create_alb ? 0 : 1 + description = "Allow SSH traffic from the ELB to the instances" from_port = 7999 protocol = "tcp" - security_group_id = aws_security_group.elb.id + security_group_id = aws_security_group.elb[0].id source_security_group_id = aws_security_group.this.id to_port = 7999 type = "egress" } resource "aws_security_group_rule" "elb_ingress" { - count = length(var.elb_allowed_cidr_blocks) > 0 ? 1 : 0 + count = !var.create_alb && length(var.elb_allowed_cidr_blocks) > 0 ? 1 : 0 cidr_blocks = var.elb_allowed_cidr_blocks #tfsec:ignore:AWS006 description = "Allow HTTPS traffic from the allowed ranges" from_port = var.elb_port protocol = "tcp" - security_group_id = aws_security_group.elb.id + security_group_id = aws_security_group.elb[0].id to_port = var.elb_port type = "ingress" } resource "aws_security_group_rule" "elb_ingress_ssh" { - count = length(var.elb_allowed_cidr_blocks) > 0 ? 1 : 0 + count = !var.create_alb && length(var.elb_allowed_cidr_blocks) > 0 ? 1 : 0 cidr_blocks = var.elb_allowed_cidr_blocks #tfsec:ignore:AWS006 description = "Allow SSH traffic from the allowed ranges" from_port = var.elb_ssh_port protocol = "tcp" - security_group_id = aws_security_group.elb.id + security_group_id = aws_security_group.elb[0].id to_port = var.elb_ssh_port type = "ingress" } resource "aws_elb" "this" { + count = var.create_alb ? 0 : 1 + name_prefix = substr(var.name, 0, 6) internal = var.elb_internal - security_groups = [aws_security_group.elb.id] + security_groups = [aws_security_group.elb[0].id] subnets = var.elb_subnets tags = var.tags diff --git a/groups.tf b/groups.tf index f6c001f..0ae637d 100644 --- a/groups.tf +++ b/groups.tf @@ -3,11 +3,8 @@ resource "aws_security_group" "this" { description = "Attached to all Bitbucket instances" vpc_id = var.vpc_id - tags = merge( - var.tags, - map( - "Name", "${var.name}" - ) + tags = merge(var.tags, + { "Name" : var.name } ) lifecycle { @@ -31,17 +28,31 @@ resource "aws_security_group_rule" "allow_inbound_http_from_lb" { from_port = 7990 protocol = "tcp" security_group_id = aws_security_group.this.id - source_security_group_id = aws_security_group.elb.id + source_security_group_id = try(aws_security_group.alb_https[0].id, aws_security_group.elb[0].id) to_port = 7990 type = "ingress" } resource "aws_security_group_rule" "allow_inbound_http_from_lb_ssh" { + count = var.create_alb ? 0 : 1 + description = "Allow SSH traffic from the load balancer" from_port = 7999 protocol = "tcp" security_group_id = aws_security_group.this.id - source_security_group_id = aws_security_group.elb.id + source_security_group_id = aws_security_group.elb[0].id to_port = 7999 type = "ingress" } + +resource "aws_security_group_rule" "allow_inbound_from_lb_ssh" { + count = var.create_alb ? 1 : 0 + + cidr_blocks = var.alb_allowed_ssh_cidr_blocks + description = "Allow SSH traffic - NLBs do not support SGs" + from_port = 7999 + protocol = "tcp" + security_group_id = aws_security_group.this.id + to_port = 7999 + type = "ingress" +} diff --git a/main.tf b/main.tf index cde1070..9ed3344 100644 --- a/main.tf +++ b/main.tf @@ -45,7 +45,7 @@ locals { db_url = "jdbc:postgresql://${module.bitbucketdb.instance_connection_info.endpoint}/postgres" db_username = module.bitbucketdb.instance_connection_info.username db_password = replace(data.aws_secretsmanager_secret_version.dbpassword.secret_string, "$", "\\$") - elb_port = var.elb_port + elb_port = var.create_alb ? var.alb_https_port : var.elb_port license_key = var.license_key mount_point = "/opt/atlassian/data" region = local.region @@ -76,7 +76,8 @@ resource "aws_autoscaling_group" "this" { health_check_type = "EC2" force_delete = false launch_configuration = aws_launch_configuration.this.name - load_balancers = [aws_elb.this.id] + load_balancers = !var.create_alb ? [aws_elb.this[0].id] : null + target_group_arns = var.create_alb ? [aws_lb_target_group.https[0].id, aws_lb_target_group.ssh[0].id] : null max_size = var.asg_max_size min_size = var.asg_min_size wait_for_capacity_timeout = "15m" @@ -165,8 +166,21 @@ resource "aws_route53_record" "this" { zone_id = var.zone_id alias { - name = aws_elb.this.dns_name - zone_id = aws_elb.this.zone_id + name = try(aws_lb.https[0].dns_name, aws_elb.this[0].dns_name) + zone_id = try(aws_lb.https[0].zone_id, aws_elb.this[0].zone_id) + evaluate_target_health = true + } +} + +resource "aws_route53_record" "ssh" { + count = var.zone_id != null && var.create_alb && var.dns_ssh_prefix != null ? 1 : 0 + name = var.dns_ssh_prefix + type = "A" + zone_id = var.zone_id + + alias { + name = try(aws_lb.ssh[0].dns_name, "null") + zone_id = try(aws_lb.ssh[0].zone_id, "null") evaluate_target_health = true } } diff --git a/outputs.tf b/outputs.tf index 2568eb7..e9a757a 100644 --- a/outputs.tf +++ b/outputs.tf @@ -44,18 +44,33 @@ output "iam_role_arn" { } output "lb_arn" { - description = "ARN of the ELB for Bitbucket access" - value = aws_elb.this.arn + description = "ARN of the ELB for Bitbucket access (HTTPS when ALB is used)" + value = try(aws_lb.https[0].arn, aws_elb.this[0].arn) } output "lb_dns_name" { description = "DNS Name of the ELB for Bitbucket access" - value = aws_elb.this.dns_name + value = try(aws_lb.https[0].dns_name, aws_elb.this[0].dns_name) } output "lb_zone_id" { description = "Route53 Zone ID of the ELB for Bitbucket access" - value = aws_elb.this.zone_id + value = try(aws_lb.https[0].zone_id, aws_elb.this[0].zone_id) +} + +output "ssh_lb_arn" { + description = "ARN of the LB for Bitbucket SSH access (only valid when ALB is used)" + value = try(aws_lb.ssh[0].arn, null) +} + +output "ssh_lb_dns_name" { + description = "DNS Name of the LB for Bitbucket access (only valid when ALB is used)" + value = try(aws_lb.ssh[0].dns_name, null) +} + +output "ssh_lb_zone_id" { + description = "Route53 Zone ID of the LB for Bitbucket SSH access" + value = try(aws_lb.ssh[0].zone_id, null) } output "url" { diff --git a/variables.tf b/variables.tf index b14ad63..a45bb1a 100644 --- a/variables.tf +++ b/variables.tf @@ -34,18 +34,6 @@ variable "data_volume_size" { type = number } -variable "dns_prefix" { - default = null - description = "the hostname that will be used for bitbucket. This will be combined with the domain in `zone_id` or the value of `domain_name` to form the base url." - type = string -} - -variable "domain_name" { - default = null - description = "domain name, which is only used if `zone_id` is not specified to compute the base url" - type = string -} - variable "license_key" { default = "" description = "Bitbucket license key (optional, must be a single line)" @@ -261,6 +249,90 @@ variable "availability_zone" { type = string } +variable "vpc_id" { + description = "VPC to create associated resources in" + type = string +} + +######################################## +# ALB Vars (used when `create_alb == true`) +######################################## + +variable "create_alb" { + default = false + description = "Create an ALB. This will by requirement create an NLB for SSH access on a separate address." + type = bool +} + +variable "alb_additional_sg_tags" { + default = {} + description = "Additional tags to apply to the LB security group. Useful if you use an external process to manage ingress rules." + type = map(string) +} + +variable "alb_allowed_https_cidr_blocks" { + default = ["0.0.0.0/0"] + description = "List of allowed CIDR blocks. If `[]` is specified, no inbound ingress rules will be created" + type = list(string) +} + +variable "alb_allowed_ssh_cidr_blocks" { + default = ["0.0.0.0/0"] + description = "List of allowed CIDR blocks for SSH access. If `[]` is specified, no inbound ingress rules will be created" + type = list(string) +} + +variable "alb_certificate" { + description = "ARN of certificate to associate with LB" + type = string +} + +variable "alb_https_internal" { + default = true + description = "Create as an internal or internet-facing LB" + type = bool +} + +variable "alb_ssh_internal" { + default = true + description = "Create as an internal or internet-facing LB for SSH" + type = bool +} + +variable "alb_https_port" { + default = 443 + description = "Port that the Load Balancer for Bitbucket should listen for HTTPS on (Default is 443.)" + type = number +} + +variable "alb_ssh_port" { + default = 22 + description = "Port that the Load Balancer for Bitbucket should listen for SSH on (Default is 22.)" + type = number +} + +variable "alb_ssl_policy" { + default = "ELBSecurityPolicy-TLS-1-2-2017-01" + description = "SSL policy for ALB" + type = string +} + +variable "alb_https_subnets" { + description = "Subnets to associate HTTPS LB to" + type = list(string) +} + +variable "alb_ssh_subnets" { + default = null + description = "Subnets to associate SSH LB to" + type = list(string) +} + + +######################################## +# ELB Vars (used when `create_alb == false`) +######################################## + variable "elb_additional_sg_tags" { default = {} description = "Additional tags to apply to the ELB security group. Useful if you use an external process to manage ingress rules." @@ -274,6 +346,7 @@ variable "elb_allowed_cidr_blocks" { } variable "elb_certificate" { + default = null description = "ARN of certificate to associate with ELB" type = string } @@ -297,12 +370,30 @@ variable "elb_ssh_port" { } variable "elb_subnets" { + default = null description = "Subnets to associate ELB to" type = list(string) } -variable "vpc_id" { - description = "VPC to create associated resources in" +######################################## +# DNS Vars +######################################## + +variable "domain_name" { + default = null + description = "domain name, which is only used if `zone_id` is not specified to compute the base url" + type = string +} + +variable "dns_prefix" { + default = null + description = "Hostname that will be used for bitbucket. This will be combined with the domain in `zone_id` or the value of `domain_name` to form the base url." + type = string +} + +variable "dns_ssh_prefix" { + default = null + description = "Hostname that will be used for bitbucket SSH access. This is only used when `create_alb == true`" type = string }