diff --git a/modules/perforce/examples/complete/dns.tf b/modules/perforce/examples/complete/dns.tf index a3d9a107..02fa1c6a 100644 --- a/modules/perforce/examples/complete/dns.tf +++ b/modules/perforce/examples/complete/dns.tf @@ -1,4 +1,3 @@ - ########################################## # Route53 Hosted Zone for FQDN ########################################## @@ -8,10 +7,10 @@ data "aws_route53_zone" "root" { } ########################################## -# Perforce Helix DNS +# Perforce DNS ########################################## -resource "aws_route53_zone" "helix_private_zone" { - name = "helix.perforce.internal" +resource "aws_route53_zone" "perforce_private_hosted_zone" { + name = "perforce.${data.aws_route53_zone.root.name}" #checkov:skip=CKV2_AWS_38: Hosted zone is private (vpc association) #checkov:skip=CKV2_AWS_39: Query logging disabled by design vpc { @@ -19,54 +18,57 @@ resource "aws_route53_zone" "helix_private_zone" { } } - -resource "aws_route53_record" "helix_swarm" { +# Route all external web service traffic to the NLB +resource "aws_route53_record" "external_perforce_web_services" { zone_id = data.aws_route53_zone.root.id - name = "swarm.helix.${data.aws_route53_zone.root.name}" + name = "*.perforce.${data.aws_route53_zone.root.name}" type = "A" alias { - name = module.perforce_helix_swarm.alb_dns_name - zone_id = module.perforce_helix_swarm.alb_zone_id + name = aws_lb.perforce.dns_name + zone_id = aws_lb.perforce.zone_id evaluate_target_health = true } } -resource "aws_route53_record" "helix_authentication_service" { - zone_id = data.aws_route53_zone.root.zone_id - name = "auth.helix.${data.aws_route53_zone.root.name}" +# Route all internal web service traffic to the ALB +resource "aws_route53_record" "internal_perforce_web_services" { + zone_id = aws_route53_zone.perforce_private_hosted_zone.id + name = "*.${aws_route53_zone.perforce_private_hosted_zone.name}" type = "A" alias { - name = module.perforce_helix_authentication_service.alb_dns_name - zone_id = module.perforce_helix_authentication_service.alb_zone_id + name = aws_lb.perforce_web_services.dns_name + zone_id = aws_lb.perforce_web_services.zone_id evaluate_target_health = true } } -resource "aws_route53_record" "perforce_helix_core" { +# Route all external Helix Core traffic to the NLB +resource "aws_route53_record" "external_helix_core" { zone_id = data.aws_route53_zone.root.zone_id - name = "core.helix.${data.aws_route53_zone.root.name}" + name = "perforce.${data.aws_route53_zone.root.name}" type = "A" - ttl = 300 - #checkov:skip=CKV2_AWS_23:The attached resource is managed by CGD Toolkit - records = [module.perforce_helix_core.helix_core_eip_public_ip] + alias { + name = aws_lb.perforce.dns_name + zone_id = aws_lb.perforce.zone_id + evaluate_target_health = true + } } -resource "aws_route53_record" "perforce_helix_core_pvt" { - zone_id = aws_route53_zone.helix_private_zone.zone_id - name = "core.${aws_route53_zone.helix_private_zone.name}" +# Route all internal Helix Core traffic to the instance +resource "aws_route53_record" "internal_helix_core" { + zone_id = aws_route53_zone.perforce_private_hosted_zone.zone_id + name = aws_route53_zone.perforce_private_hosted_zone.name type = "A" + records = [module.perforce_helix_core.helix_core_private_ip] ttl = 300 - #checkov:skip=CKV2_AWS_23:The attached resource is managed by CGD Toolkit - records = [module.perforce_helix_core.helix_core_eip_private_ip] } ########################################## # Helix Certificate Management ########################################## - -resource "aws_acm_certificate" "helix" { - domain_name = "helix.${var.root_domain_name}" - subject_alternative_names = ["*.helix.${var.root_domain_name}"] +resource "aws_acm_certificate" "perforce" { + domain_name = "perforce.${var.root_domain_name}" + subject_alternative_names = ["*.perforce.${var.root_domain_name}"] validation_method = "DNS" @@ -79,9 +81,9 @@ resource "aws_acm_certificate" "helix" { } } -resource "aws_route53_record" "helix_cert" { +resource "aws_route53_record" "perforce_cert" { for_each = { - for dvo in aws_acm_certificate.helix.domain_validation_options : dvo.domain_name => { + for dvo in aws_acm_certificate.perforce.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type @@ -96,10 +98,10 @@ resource "aws_route53_record" "helix_cert" { zone_id = data.aws_route53_zone.root.id } -resource "aws_acm_certificate_validation" "helix" { +resource "aws_acm_certificate_validation" "perforce" { timeouts { create = "15m" } - certificate_arn = aws_acm_certificate.helix.arn - validation_record_fqdns = [for record in aws_route53_record.helix_cert : record.fqdn] + certificate_arn = aws_acm_certificate.perforce.arn + validation_record_fqdns = [for record in aws_route53_record.perforce_cert : record.fqdn] } diff --git a/modules/perforce/examples/complete/main.tf b/modules/perforce/examples/complete/main.tf index 674c5166..556a0516 100644 --- a/modules/perforce/examples/complete/main.tf +++ b/modules/perforce/examples/complete/main.tf @@ -28,21 +28,27 @@ resource "aws_ecs_cluster_capacity_providers" "providers" { ########################################## module "perforce_helix_core" { - source = "../../helix-core" - vpc_id = aws_vpc.perforce_vpc.id - server_type = "p4d_commit" - instance_subnet_id = aws_subnet.public_subnets[0].id - instance_type = "c6g.large" - instance_architecture = "arm64" - - storage_type = "EBS" - depot_volume_size = 64 - metadata_volume_size = 32 - logs_volume_size = 32 + source = "../../helix-core" + # Networking + vpc_id = aws_vpc.perforce_vpc.id + instance_subnet_id = aws_subnet.private_subnets[0].id + internal = true fully_qualified_domain_name = "core.helix.perforce.${var.root_domain_name}" - helix_authentication_service_url = "https://${aws_route53_record.helix_authentication_service.name}" + + # Compute and Storage + instance_type = "c8g.large" + instance_architecture = "arm64" + storage_type = "EBS" + depot_volume_size = 64 + metadata_volume_size = 32 + logs_volume_size = 32 + + # Configuration + plaintext = true # We will use the Perforce NLB to handle TLS termination + server_type = "p4d_commit" + helix_authentication_service_url = "https://auth.${aws_route53_zone.perforce_private_hosted_zone.name}" } ########################################## @@ -50,39 +56,179 @@ module "perforce_helix_core" { ########################################## module "perforce_helix_authentication_service" { - source = "../../helix-authentication-service" - vpc_id = aws_vpc.perforce_vpc.id - cluster_name = aws_ecs_cluster.perforce_cluster.name - helix_authentication_service_alb_subnets = aws_subnet.public_subnets[*].id - helix_authentication_service_subnets = aws_subnet.private_subnets[*].id - certificate_arn = aws_acm_certificate.helix.arn + source = "../../helix-authentication-service" + + # Networking + vpc_id = aws_vpc.perforce_vpc.id + create_application_load_balancer = false # Shared Perforce web services application load balancer + helix_authentication_service_subnets = aws_subnet.private_subnets[*].id + fully_qualified_domain_name = "auth.perforce.${var.root_domain_name}" + # Compute + cluster_name = aws_ecs_cluster.perforce_cluster.name + + # Configuration enable_web_based_administration = true - fully_qualified_domain_name = "auth.helix.${var.root_domain_name}" - depends_on = [aws_ecs_cluster.perforce_cluster, aws_acm_certificate_validation.helix] + depends_on = [aws_ecs_cluster.perforce_cluster] } ########################################## # Perforce Helix Swarm ########################################## - module "perforce_helix_swarm" { - source = "../../helix-swarm" - vpc_id = aws_vpc.perforce_vpc.id - cluster_name = aws_ecs_cluster.perforce_cluster.name - helix_swarm_alb_subnets = aws_subnet.public_subnets[*].id - helix_swarm_service_subnets = aws_subnet.private_subnets[*].id - certificate_arn = aws_acm_certificate.helix.arn - p4d_port = "ssl:${aws_route53_record.perforce_helix_core_pvt.name}:1666" + source = "../../helix-swarm" + + # Networking + vpc_id = aws_vpc.perforce_vpc.id + create_application_load_balancer = false # Shared Perforce web services application load balancer + helix_swarm_service_subnets = aws_subnet.private_subnets[*].id + fully_qualified_domain_name = "swarm.perforce.${var.root_domain_name}" + + # Compute + cluster_name = aws_ecs_cluster.perforce_cluster.name + + # Configuration + p4d_port = "${aws_route53_record.internal_helix_core.name}:1666" p4d_super_user_arn = module.perforce_helix_core.helix_core_super_user_username_secret_arn p4d_super_user_password_arn = module.perforce_helix_core.helix_core_super_user_password_secret_arn p4d_swarm_user_arn = module.perforce_helix_core.helix_core_super_user_username_secret_arn p4d_swarm_password_arn = module.perforce_helix_core.helix_core_super_user_password_secret_arn + enable_sso = true + + depends_on = [aws_ecs_cluster.perforce_cluster] +} + +########################################## +# Perforce Network Load Balancer +########################################## +resource "aws_lb" "perforce" { + name = "perforce" + load_balancer_type = "network" + subnets = aws_subnet.public_subnets[*].id + security_groups = [aws_security_group.perforce_network_load_balancer.id] + drop_invalid_header_fields = true + enable_cross_zone_load_balancing = true + #checkov:skip=CKV_AWS_91: Access logging not required for example deployment + #checkov:skip=CKV_AWS_150: Load balancer deletion protection disabled for example deployment +} - enable_sso = true +################################################### +# Perforce Web Services Application Load Balancer +################################################### +resource "aws_lb" "perforce_web_services" { + name = "perforce-web-services" + load_balancer_type = "application" + subnets = aws_subnet.private_subnets[*].id + internal = true + security_groups = [aws_security_group.perforce_web_services_alb.id] + drop_invalid_header_fields = true + #checkov:skip=CKV_AWS_91: Access logging not required for example deployment + #checkov:skip=CKV_AWS_150: Load balancer deletion protection disabled for example deployment +} - fully_qualified_domain_name = "swarm.helix.${var.root_domain_name}" +########################################## +# Helix Core Target Group +########################################## +resource "aws_lb_target_group" "helix_core" { + name = "helix-core" + target_type = "instance" + port = 1666 + protocol = "TCP" + vpc_id = aws_vpc.perforce_vpc.id +} - depends_on = [aws_ecs_cluster.perforce_cluster, aws_acm_certificate_validation.helix] +resource "aws_lb_target_group_attachment" "helix_core" { + target_group_arn = aws_lb_target_group.helix_core.arn + target_id = module.perforce_helix_core.helix_core_instance_id + port = 1666 +} + +########################################## +# Web Services Target Group +########################################## +resource "aws_lb_target_group" "perforce_web_services" { + name = "perforce-web-services" + target_type = "alb" + port = 443 + protocol = "TCP" + vpc_id = aws_vpc.perforce_vpc.id +} + +# Default rule redirects to Helix Swarm +resource "aws_lb_listener" "perforce_web_services" { + load_balancer_arn = aws_lb.perforce_web_services.arn + port = 443 + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" + certificate_arn = aws_acm_certificate_validation.perforce.certificate_arn + + default_action { + type = "redirect" + redirect { + host = "swarm.perforce.${var.root_domain_name}" + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } +} + +# Helix Swarm listener rule +resource "aws_lb_listener_rule" "perforce_helix_swarm" { + listener_arn = aws_lb_listener.perforce_web_services.arn + priority = 100 + action { + type = "forward" + target_group_arn = module.perforce_helix_swarm.target_group_arn + } + condition { + host_header { + values = ["swarm.perforce.${var.root_domain_name}"] + } + } +} + +# Helix Authentication Service listener rule +resource "aws_lb_listener_rule" "perforce_helix_authentication_service" { + listener_arn = aws_lb_listener.perforce_web_services.arn + priority = 200 + action { + type = "forward" + target_group_arn = module.perforce_helix_authentication_service.target_group_arn + } + condition { + host_header { + values = ["auth.perforce.${var.root_domain_name}"] + } + } +} + +########################################## +# Helix Core Listener +########################################## +resource "aws_lb_listener" "helix_core" { + load_balancer_arn = aws_lb.perforce.arn + port = 1666 + protocol = "TLS" + ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01" + certificate_arn = aws_acm_certificate_validation.perforce.certificate_arn + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.helix_core.arn + } +} + +########################################## +# Perforce Web Services Listener +########################################## +resource "aws_lb_listener" "perforce_web_services_alb" { + load_balancer_arn = aws_lb.perforce.arn + port = 443 + protocol = "TCP" + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.perforce_web_services.arn + } } diff --git a/modules/perforce/examples/complete/outputs.tf b/modules/perforce/examples/complete/outputs.tf new file mode 100644 index 00000000..c91a51f2 --- /dev/null +++ b/modules/perforce/examples/complete/outputs.tf @@ -0,0 +1,14 @@ +output "helix_core_connection_string" { + value = "ssl:perforce.${var.root_domain_name}:1666" + description = "The connection string for the Helix Core server. Set your P4PORT environment variable to this value." +} + +output "helix_swarm_url" { + value = "swarm.perforce.${var.root_domain_name}" + description = "The URL for the Helix Swarm server." +} + +output "helix_authentication_service_admin_url" { + value = "auth.perforce.${var.root_domain_name}/admin" + description = "The URL for the Helix Authentication Service admin page." +} diff --git a/modules/perforce/examples/complete/security.tf b/modules/perforce/examples/complete/security.tf index a682b045..5a79be38 100644 --- a/modules/perforce/examples/complete/security.tf +++ b/modules/perforce/examples/complete/security.tf @@ -1,33 +1,131 @@ ########################################## -# Internal Access - service to service +# Perforce NLB Security Group ########################################## +resource "aws_security_group" "perforce_network_load_balancer" { + name = "perforce_network_load_balancer" + description = "Perforce Network Load Balancer" + vpc_id = aws_vpc.perforce_vpc.id + #checkov:skip=CKV2_AWS_5:Security group is attached to Perforce NLB +} + +# Egress for Perforce NLB to Helix Core instance +resource "aws_vpc_security_group_egress_rule" "perforce_nlb_outbound_helix_core" { + security_group_id = aws_security_group.perforce_network_load_balancer.id + description = "Perforce NLB outbound to Helix Core" + from_port = 1666 + to_port = 1666 + ip_protocol = "TCP" + referenced_security_group_id = module.perforce_helix_core.security_group_id +} -# Helix Swarm -> Helix Core -resource "aws_vpc_security_group_ingress_rule" "helix_core_inbound_swarm" { +# Ingress from Perforce NLB to Helix Core instance +resource "aws_vpc_security_group_ingress_rule" "perforce_nlb_inbound_helix_core" { security_group_id = module.perforce_helix_core.security_group_id + description = "Perforce NLB inbound to Helix Core" ip_protocol = "TCP" from_port = 1666 to_port = 1666 + referenced_security_group_id = aws_security_group.perforce_network_load_balancer.id +} + +# Egress for Perforce NLB to Perforce Web Services ALB +resource "aws_vpc_security_group_egress_rule" "perforce_nlb_outbound_web_alb" { + security_group_id = aws_security_group.perforce_network_load_balancer.id + description = "Perforce NLB outbound to Web ALB" + from_port = 443 + to_port = 443 + ip_protocol = "TCP" + referenced_security_group_id = aws_security_group.perforce_web_services_alb.id +} + +########################################## +# Perforce Web Services ALB Security Group +########################################## +resource "aws_security_group" "perforce_web_services_alb" { + name = "perforce_web_services_alb" + description = "Perforce Web Services ALB" + vpc_id = aws_vpc.perforce_vpc.id + #checkov:skip=CKV2_AWS_5:Security group is attached to Perforce Web Services ALB +} + +# HTTPS Ingress from Perforce NLB to Perforce Web Services ALB +resource "aws_vpc_security_group_ingress_rule" "perforce_nlb_inbound_web_alb_https" { + security_group_id = aws_security_group.perforce_web_services_alb.id + description = "Perforce NLB inbound HTTPS to Web ALB" + ip_protocol = "TCP" + from_port = 443 + to_port = 443 + referenced_security_group_id = aws_security_group.perforce_network_load_balancer.id +} + +# HTTPS Ingress from Helix Core server (needed for Helix Authentication Service extension) +resource "aws_vpc_security_group_ingress_rule" "perforce_helix_core_inbound_web_alb_https" { + security_group_id = aws_security_group.perforce_web_services_alb.id + description = "Helix Core inbound HTTPS to Web ALB" + ip_protocol = "TCP" + from_port = 443 + to_port = 443 + referenced_security_group_id = module.perforce_helix_core.security_group_id +} + +# Egress for Perfoce Web Services ALB to Helix Swarm service +resource "aws_vpc_security_group_egress_rule" "perforce_alb_outbound_helix_swarm" { + security_group_id = aws_security_group.perforce_web_services_alb.id + description = "Perforce ALB outbound to Helix Swarm" + from_port = 80 + to_port = 80 + ip_protocol = "TCP" referenced_security_group_id = module.perforce_helix_swarm.service_security_group_id - description = "Enables Helix Swarm to access Helix Core." -} - -# Helix Core -> Helix Swarm -resource "aws_vpc_security_group_ingress_rule" "helix_swarm_inbound_core" { - security_group_id = module.perforce_helix_swarm.alb_security_group_id - ip_protocol = "TCP" - from_port = 443 - to_port = 443 - cidr_ipv4 = "${module.perforce_helix_core.helix_core_eip_public_ip}/32" - description = "Enables Helix Core to access Helix Swarm" -} - -# Helix Core -> Helix Authentication Service -resource "aws_vpc_security_group_ingress_rule" "helix_auth_inbound_core" { - security_group_id = module.perforce_helix_authentication_service.alb_security_group_id - ip_protocol = "TCP" - from_port = 443 - to_port = 443 - cidr_ipv4 = "${module.perforce_helix_core.helix_core_eip_public_ip}/32" - description = "Enables Helix Core to access Helix Authentication Service" +} + +# Ingress from Perforce Web Services ALB to Helix Swarm service +resource "aws_vpc_security_group_ingress_rule" "perforce_alb_inbound_helix_swarm" { + security_group_id = module.perforce_helix_swarm.service_security_group_id + description = "Perforce ALB inbound to Helix Swarm" + ip_protocol = "TCP" + from_port = 80 + to_port = 80 + referenced_security_group_id = aws_security_group.perforce_web_services_alb.id + #checkov:skip=CKV_AWS_260:Access restricted to Perforce Web Services ALB +} + +# Egress for Perforce Web Services ALB to Helix Authentication service +resource "aws_vpc_security_group_egress_rule" "perforce_alb_outbound_helix_auth" { + security_group_id = aws_security_group.perforce_web_services_alb.id + description = "Perforce ALB outbound to Helix Auth" + from_port = 3000 + to_port = 3000 + ip_protocol = "TCP" + referenced_security_group_id = module.perforce_helix_authentication_service.service_security_group_id +} + +# Ingress from Perforce Web Services ALB to Helix Authentication service +resource "aws_vpc_security_group_ingress_rule" "perforce_alb_inbound_helix_auth" { + security_group_id = module.perforce_helix_authentication_service.service_security_group_id + description = "Perforce ALB inbound to Helix Auth" + ip_protocol = "TCP" + from_port = 3000 + to_port = 3000 + referenced_security_group_id = aws_security_group.perforce_web_services_alb.id +} + +########################################## +# Helix Swarm to Helix Core +########################################## +resource "aws_vpc_security_group_ingress_rule" "perforce_helix_core_inbound_helix_swarm" { + security_group_id = module.perforce_helix_core.security_group_id + description = "Helix Core inbound to Helix Swarm" + ip_protocol = "TCP" + from_port = 1666 + to_port = 1666 + referenced_security_group_id = module.perforce_helix_swarm.service_security_group_id +} + +resource "aws_vpc_security_group_egress_rule" "perforce_helix_swarm_outbound_helix_core" { + security_group_id = module.perforce_helix_swarm.service_security_group_id + description = "Helix Swarm outbound to Helix Core" + from_port = 1666 + to_port = 1666 + ip_protocol = "TCP" + referenced_security_group_id = module.perforce_helix_core.security_group_id } diff --git a/modules/perforce/examples/complete/versions.tf b/modules/perforce/examples/complete/versions.tf index a49b9edb..2848486f 100644 --- a/modules/perforce/examples/complete/versions.tf +++ b/modules/perforce/examples/complete/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "5.66.0" + version = "5.78.0" } } } diff --git a/modules/perforce/examples/complete/vpc.tf b/modules/perforce/examples/complete/vpc.tf index 9e0645c8..73706742 100644 --- a/modules/perforce/examples/complete/vpc.tf +++ b/modules/perforce/examples/complete/vpc.tf @@ -109,9 +109,9 @@ resource "aws_route_table" "private_rt" { # route to the internet through NAT gateway resource "aws_route" "private_rt_nat_gateway" { - route_table_id = aws_route_table.private_rt.id - destination_cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat_gateway.id + route_table_id = aws_route_table.private_rt.id + destination_cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat_gateway.id } resource "aws_route_table_association" "private_rt_asso" {