From f9d273de9bcc5048ec44b96d3031874246526056 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Fri, 25 Nov 2016 13:53:15 +0100 Subject: [PATCH] Add public routiug In order to make an env publicily reachable we introduce the handling of an additional zone which is part of the user input during setup. Additionally we make the use of an acm certificate to provide proper termination for the traffic layer. --- cmd/terraformer/terraformer.go | 39 +++++--- infrastructure/terraform/template/network.tf | 58 ++++++------ infrastructure/terraform/template/platform.tf | 92 +++++++++++-------- infrastructure/terraform/template/routing.tf | 29 ++++++ .../terraform/template/scheduling.tf | 46 ++++++---- infrastructure/terraform/template/storage.tf | 4 +- .../terraform/template/variables.tf | 10 +- 7 files changed, 176 insertions(+), 102 deletions(-) diff --git a/cmd/terraformer/terraformer.go b/cmd/terraformer/terraformer.go index a62a5b5..4093d7b 100644 --- a/cmd/terraformer/terraformer.go +++ b/cmd/terraformer/terraformer.go @@ -46,7 +46,7 @@ const ( defaultTmpPath = "/tmp" fmtBucket = "bucket=%s" - fmtBucketState = "%s-tapglue-snaas-state" + fmtBucketState = "%s-snaas-state" fmtKey = "key=%s/%s.tfstate" fmtNamespace = "%s-%s" fmtPlan = "%s/%s.plan" @@ -63,6 +63,7 @@ const ( tfCmdRemote = "remote" tplTFVars = ` +domain = "{{.Domain}}" key = { access = "{{.KeyAccess}}" } @@ -74,6 +75,13 @@ pg_password = "{{.PGPassword}}" varRegion = "region" ) +// vars bundles together all generated or given input that is custom to the env. +type vars struct { + KeyAccess string + Domain string + PGPassword string +} + func main() { var ( env = flag.String("env", "", "Environment used for isolation.") @@ -188,12 +196,25 @@ func main() { log.Fatalf("state dir creation failed: %s", err) } + fmt.Println("\nWhat is the domain the env should be reachable at?") + fmt.Print("|> ") + domain := "" + fmt.Scanf("%s", &domain) + + if domain == "" { + log.Fatal("Can't work without a domain.") + } + pubKey, err := generateKeyPair(filepath.Join(statePath, defaultKeyPath)) if err != nil { log.Fatal(err) } - if err = generateVarFile(varFile, pubKey, generate.RandomString(32)); err != nil { + if err = generateVarFile(varFile, vars{ + Domain: domain, + KeyAccess: strings.Trim(string(pubKey), "\n"), + PGPassword: generate.RandomStringSafe(32), + }); err != nil { log.Fatalf("var file create failed: %s", err) } @@ -298,7 +319,7 @@ func generateKeyPair(privateKeyPath string) ([]byte, error) { return nil, err } - privateFile, err := os.Create(privateKeyPath) + privateFile, err := os.OpenFile(privateKeyPath, os.O_CREATE|os.O_WRONLY, 0400) if err != nil { return nil, err } @@ -320,7 +341,7 @@ func generateKeyPair(privateKeyPath string) ([]byte, error) { return ssh.MarshalAuthorizedKey(pub), nil } -func generateVarFile(path string, accessKeyPub []byte, pgPassword string) error { +func generateVarFile(path string, vs vars) error { f, err := os.Create(path) if err != nil { return err @@ -331,15 +352,7 @@ func generateVarFile(path string, accessKeyPub []byte, pgPassword string) error return err } - tmpl.Execute(f, struct { - KeyAccess string - PGPassword string - }{ - KeyAccess: strings.Trim(string(accessKeyPub), "\n"), - PGPassword: pgPassword, - }) - - return nil + return tmpl.Execute(f, vs) } func prepareCmd(dir string, environ []string, command string, args ...string) *exec.Cmd { diff --git a/infrastructure/terraform/template/network.tf b/infrastructure/terraform/template/network.tf index 0d6bb13..e530ba3 100644 --- a/infrastructure/terraform/template/network.tf +++ b/infrastructure/terraform/template/network.tf @@ -3,9 +3,9 @@ provider "aws" { } resource "aws_vpc" "env" { - cidr_block = "10.0.0.0/16" - enable_dns_support = true - enable_dns_hostnames = true + cidr_block = "10.0.0.0/16" + enable_dns_support = true + enable_dns_hostnames = true tags { Name = "${var.env}" @@ -22,10 +22,12 @@ resource "aws_internet_gateway" "env" { resource "aws_nat_gateway" "env" { allocation_id = "${aws_eip.nat.id}" - depends_on = [ + + depends_on = [ "aws_internet_gateway.env", ] - subnet_id = "${aws_subnet.perimeter-a.id}" + + subnet_id = "${aws_subnet.perimeter-a.id}" } resource "aws_eip" "nat" { @@ -68,19 +70,19 @@ resource "aws_route_table" "perimeter" { } resource "aws_route_table_association" "perimeter-a" { - route_table_id = "${aws_route_table.perimeter.id}" - subnet_id = "${aws_subnet.perimeter-a.id}" + route_table_id = "${aws_route_table.perimeter.id}" + subnet_id = "${aws_subnet.perimeter-a.id}" } resource "aws_route_table_association" "perimeter-b" { - route_table_id = "${aws_route_table.perimeter.id}" - subnet_id = "${aws_subnet.perimeter-b.id}" + route_table_id = "${aws_route_table.perimeter.id}" + subnet_id = "${aws_subnet.perimeter-b.id}" } resource "aws_subnet" "platform-a" { - availability_zone = "${var.region}a" - cidr_block = "10.0.2.0/23" - vpc_id = "${aws_vpc.env.id}" + availability_zone = "${var.region}a" + cidr_block = "10.0.2.0/23" + vpc_id = "${aws_vpc.env.id}" tags { Name = "platform-a" @@ -88,9 +90,9 @@ resource "aws_subnet" "platform-a" { } resource "aws_subnet" "platform-b" { - availability_zone = "${var.region}b" - cidr_block = "10.0.10.0/23" - vpc_id = "${aws_vpc.env.id}" + availability_zone = "${var.region}b" + cidr_block = "10.0.10.0/23" + vpc_id = "${aws_vpc.env.id}" tags { Name = "platform-b" @@ -101,7 +103,7 @@ resource "aws_route_table" "platform" { vpc_id = "${aws_vpc.env.id}" route { - cidr_block = "0.0.0.0/0" + cidr_block = "0.0.0.0/0" nat_gateway_id = "${aws_nat_gateway.env.id}" } @@ -111,23 +113,25 @@ resource "aws_route_table" "platform" { } resource "aws_route_table_association" "platform-a" { - route_table_id = "${aws_route_table.platform.id}" - subnet_id = "${aws_subnet.platform-a.id}" + route_table_id = "${aws_route_table.platform.id}" + subnet_id = "${aws_subnet.platform-a.id}" } resource "aws_route_table_association" "platform-b" { - route_table_id = "${aws_route_table.platform.id}" - subnet_id = "${aws_subnet.platform-b.id}" + route_table_id = "${aws_route_table.platform.id}" + subnet_id = "${aws_subnet.platform-b.id}" } resource "aws_instance" "bastion" { - ami = "${var.ami_minimal["${var.region}"]}" - instance_type = "t2.medium" - key_name = "${aws_key_pair.access.key_name}" - vpc_security_group_ids = [ + ami = "${var.ami_minimal["${var.region}"]}" + instance_type = "t2.medium" + key_name = "${aws_key_pair.access.key_name}" + + vpc_security_group_ids = [ "${aws_security_group.perimeter.id}", ] - subnet_id = "${aws_subnet.perimeter-a.id}" + + subnet_id = "${aws_subnet.perimeter-a.id}" tags { Name = "bastion" @@ -135,6 +139,6 @@ resource "aws_instance" "bastion" { } resource "aws_eip" "bastion" { - instance = "${aws_instance.bastion.id}" - vpc = true + instance = "${aws_instance.bastion.id}" + vpc = true } diff --git a/infrastructure/terraform/template/platform.tf b/infrastructure/terraform/template/platform.tf index f3ba244..7c5fc03 100644 --- a/infrastructure/terraform/template/platform.tf +++ b/infrastructure/terraform/template/platform.tf @@ -1,40 +1,47 @@ +data "aws_acm_certificate" "perimeter" { + domain = "${var.domain}" + + statuses = [ + "ISSUED", + ] +} + resource "aws_instance" "monitoring" { - ami = "${var.ami_minimal["${var.region}"]}" - instance_type = "t2.medium" - key_name = "${aws_key_pair.access.key_name}" + ami = "${var.ami_minimal["${var.region}"]}" + instance_type = "t2.medium" + key_name = "${aws_key_pair.access.key_name}" + vpc_security_group_ids = [ "${aws_security_group.platform.id}", ] - subnet_id = "${aws_subnet.platform-a.id}" + + subnet_id = "${aws_subnet.platform-a.id}" tags { Name = "monitoring" } } -resource "aws_iam_server_certificate" "monitoring" { - name = "monitoring" - certificate_body = "${file("${path.module}/../../certs/self/self.crt")}" - private_key = "${file("${path.module}/../../certs/self/self.key")}" -} - resource "aws_elb" "monitoring" { connection_draining = true connection_draining_timeout = 10 cross_zone_load_balancing = true idle_timeout = 30 name = "monitoring" - security_groups = [ + + security_groups = [ "${aws_security_group.perimeter.id}", ] - subnets = [ + + subnets = [ "${aws_subnet.perimeter-a.id}", "${aws_subnet.perimeter-b.id}", ] - access_logs = { - bucket = "${aws_s3_bucket.logs-elb.id}" - interval = 5 + access_logs = { + bucket = "${aws_s3_bucket.logs-elb.id}" + bucket_prefix = "monitoring" + interval = 5 } instances = [ @@ -42,11 +49,11 @@ resource "aws_elb" "monitoring" { ] listener { - instance_port = 3000 - instance_protocol = "http" - lb_port = 443 - lb_protocol = "https" - ssl_certificate_id = "${aws_iam_server_certificate.monitoring.arn}" + instance_port = 3000 + instance_protocol = "http" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "${data.aws_acm_certificate.perimeter.arn}" } tags { @@ -59,18 +66,22 @@ resource "aws_autoscaling_group" "service" { health_check_grace_period = 60 health_check_type = "EC2" launch_configuration = "${aws_launch_configuration.service.name}" - load_balancers = [ + + load_balancers = [ "${aws_elb.gateway-http.name}", ] - max_size = 30 - min_size = 1 - name = "service" - termination_policies = [ + + max_size = 30 + min_size = 1 + name = "service" + + termination_policies = [ "OldestInstance", "OldestLaunchConfiguration", "ClosestToNextInstanceHour", ] - vpc_zone_identifier = [ + + vpc_zone_identifier = [ "${aws_subnet.platform-a.id}", "${aws_subnet.platform-b.id}", ] @@ -89,9 +100,10 @@ resource "aws_launch_configuration" "service" { key_name = "${aws_key_pair.access.key_name}" iam_instance_profile = "${aws_iam_instance_profile.ecs-agent-profile.name}" image_id = "${var.ami_ecs_agent["${var.region}"]}" - instance_type = "m4.large" + instance_type = "m4.large" name_prefix = "ecs-service-" - security_groups = [ + + security_groups = [ "${aws_security_group.platform.id}", ] @@ -99,7 +111,7 @@ resource "aws_launch_configuration" "service" { create_before_destroy = true } - user_data = <> /etc/ecs/ecs.config @@ -142,17 +154,20 @@ resource "aws_elb" "gateway-http" { cross_zone_load_balancing = true idle_timeout = 30 name = "gateway-http" - security_groups = [ + + security_groups = [ "${aws_security_group.perimeter.id}", ] - subnets = [ + + subnets = [ "${aws_subnet.perimeter-a.id}", "${aws_subnet.perimeter-b.id}", ] - access_logs = { - bucket = "${aws_s3_bucket.logs-elb.id}" - interval = 5 + access_logs = { + bucket = "${aws_s3_bucket.logs-elb.id}" + bucket_prefix = "gateway-http" + interval = 5 } health_check { @@ -164,10 +179,11 @@ resource "aws_elb" "gateway-http" { } listener { - instance_port = 8083 - instance_protocol = "tcp" - lb_port = 443 - lb_protocol = "tcp" + instance_port = 8083 + instance_protocol = "http" + lb_port = 443 + lb_protocol = "https" + ssl_certificate_id = "${data.aws_acm_certificate.perimeter.arn}" } tags { diff --git a/infrastructure/terraform/template/routing.tf b/infrastructure/terraform/template/routing.tf index 16ced26..6536ed3 100644 --- a/infrastructure/terraform/template/routing.tf +++ b/infrastructure/terraform/template/routing.tf @@ -9,6 +9,7 @@ resource "aws_route53_record" "ratelimiter-cache" { ttl = "5" type = "CNAME" zone_id = "${aws_route53_zone.env.id}" + records = [ "${aws_elasticache_cluster.ratelimiter.cache_nodes.0.address}", ] @@ -19,7 +20,35 @@ resource "aws_route53_record" "service-master" { ttl = "5" type = "CNAME" zone_id = "${aws_route53_zone.env.id}" + records = [ "${aws_db_instance.service-master.address}", ] } + +resource "aws_route53_zone" "perimeter" { + comment = "Zone for public termination of the env." + name = "${replace(var.domain, "*.", "")}" +} + +resource "aws_route53_record" "gateway-http" { + name = "${var.env}-${var.region}" + ttl = "60" + type = "CNAME" + zone_id = "${aws_route53_zone.perimeter.id}" + + records = [ + "${aws_elb.gateway-http.dns_name}", + ] +} + +resource "aws_route53_record" "monitoring" { + name = "monitoring-${var.env}-${var.region}" + ttl = "60" + type = "CNAME" + zone_id = "${aws_route53_zone.perimeter.id}" + + records = [ + "${aws_elb.monitoring.dns_name}", + ] +} diff --git a/infrastructure/terraform/template/scheduling.tf b/infrastructure/terraform/template/scheduling.tf index 2c33474..571482e 100644 --- a/infrastructure/terraform/template/scheduling.tf +++ b/infrastructure/terraform/template/scheduling.tf @@ -1,26 +1,29 @@ resource "aws_ecs_service" "gateway-http" { - cluster = "${aws_ecs_cluster.service.id}" - depends_on = [ + cluster = "${aws_ecs_cluster.service.id}" + + depends_on = [ "aws_iam_instance_profile.ecs-agent-profile", "aws_db_instance.service-master", "aws_elasticache_cluster.ratelimiter", ] - deployment_maximum_percent = 200 - deployment_minimum_healthy_percent = 50 - desired_count = 3 - iam_role = "${aws_iam_role.ecs-scheduler.arn}" - name = "gateway-http" - task_definition = "${aws_ecs_task_definition.gateway-http.arn}" + + deployment_maximum_percent = 200 + deployment_minimum_healthy_percent = 50 + desired_count = 3 + iam_role = "${aws_iam_role.ecs-scheduler.arn}" + name = "gateway-http" + task_definition = "${aws_ecs_task_definition.gateway-http.arn}" load_balancer { - container_name = "gateway-http" - container_port = 8083 - elb_name = "${aws_elb.gateway-http.id}" + container_name = "gateway-http" + container_port = 8083 + elb_name = "${aws_elb.gateway-http.id}" } } resource "aws_ecs_task_definition" "gateway-http" { - family = "gateway-http" + family = "gateway-http" + container_definitions = <