diff --git a/infra/Makefile b/base/Makefile similarity index 72% rename from infra/Makefile rename to base/Makefile index 5bbe38f..a239bde 100644 --- a/infra/Makefile +++ b/base/Makefile @@ -3,8 +3,8 @@ .PHONY: prod upgrade prod: - @echo Generating plan upto vpc, for mount targets, and r53, for cloudflare NS entries - terraform plan -target=module.vpc -target=aws_route53_zone.dev_tyk_tech -out=$(@).plan + @echo Generating plan upto cloudflare NS entries + terraform plan -target=aws_route53_zone.dev_tyk_tech -out=$(@).plan terraform apply $(@).plan @echo Generating plan for remaining resouces terraform plan -out=$(@).plan diff --git a/base/README.md b/base/README.md index 5ec7eb6..24ec516 100644 --- a/base/README.md +++ b/base/README.md @@ -3,7 +3,9 @@ - ECR - Github OIDC - efs volumes +- RDS +- VPC and subnets Kept outside the infra module as they have a different lifecycle from the other infra components. Infra can be destroyed if needed. This directory hosts all the components that need persistance. -Check `terraform output`. +Use the Makefile to see the resource targetting that is required to bring this env up from scartch. `terraform output` will show you the outputs that are available for use in other states. diff --git a/base/base-secrets.yaml b/base/base-secrets.yaml new file mode 100644 index 0000000..3cf8143 --- /dev/null +++ b/base/base-secrets.yaml @@ -0,0 +1,16 @@ +cf-apitoken: ENC[AES256_GCM,data:A2YLlySRGyu+ep7z3BzDncW1NNaQZNE6cgTuT5gOY1ABevCzsn4GvQ==,iv:XLfigdcS6frnGgZF8aNkwd44NuzpRoyDJVSmwmICnxI=,tag:qPXXFaJuB1cA0YiD6QNpUA==,type:str] +sops: + kms: + - arn: arn:aws:kms:eu-central-1:754489498669:key/215a7274-5652-4521-8a88-b18e02b8f13e + created_at: "2024-04-22T11:26:03Z" + enc: AQICAHiDjTyDzev9deXqMt8qn7IIVL95PjWZTOOP+RjKHUtt0AGB9x1TONOwrvyTm8MFZsdEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMbN0DMeuAO5lF4MCYAgEQgDv/bkLvMrGB6MKBVBUH7DrYBYFAVjfNHDFp24p+sz1CENoWPEukBJ29LokE7qvxBqxop7d+Ft7oRjfxIg== + aws_profile: "" + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: [] + lastmodified: "2024-04-22T11:26:08Z" + mac: ENC[AES256_GCM,data:9qXuXGLkR9zMAXewMuFmSnPF8Ecga6aJ/9DixPCkeEd/3Ku/N2RbkPTERV5F5SzpFq0n7ObXZnbjTDJKsMGdj7f5t7Vl9Ou+civ/m97IJO4cFaCPGMBiipVRzmJrGXkLTG/VqC/e4+Hpi0DrKJ9B55qwwkbAoxzvEEpmaI9cOY8=,iv:4fKo4a3QdXgy8lbYYVRnT0+bm/F+CwqRoxslnBD5Lpw=,tag:nguzQpfCz2kCUTIpZDNxTQ==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/base/base.tf b/base/base.tf index 3109f1c..48d2554 100644 --- a/base/base.tf +++ b/base/base.tf @@ -27,7 +27,6 @@ locals { "purpose" = "ci", "env" = local.name } - } resource "aws_ecr_repository" "integration" { @@ -59,9 +58,7 @@ resource "aws_ecr_lifecycle_policy" "high_cadence" { depends_on = [aws_ecr_repository.integration] repository = each.key - - policy = local.combined_policy - + policy = local.combined_policy } # Dependency Track filesystem @@ -97,10 +94,3 @@ resource "aws_s3_object" "testreports" { source = "/dev/null" } -# terraform apply -target=null_resource.debug will show the rendered template -# resource "null_resource" "debug" { -# triggers = { -# json = "${data.template_file.tyk_repo_access.rendered}" -# } -# } - diff --git a/base/devenv-euc1-test.tfvars b/base/devenv-euc1-test.tfvars deleted file mode 100644 index e69de29..0000000 diff --git a/base/dns.tf b/base/dns.tf new file mode 100644 index 0000000..86d3669 --- /dev/null +++ b/base/dns.tf @@ -0,0 +1,50 @@ +# DNS and wildcard certificate for *.dev.tyk.technology + +provider "cloudflare" { + api_token = data.sops_file.secrets.data["cf-apitoken"] + # account_id = "35b8134b47c7d01ee8198bb2b82a8dc5" +} + +resource "cloudflare_record" "dev_tyk_tech" { + for_each = toset(aws_route53_zone.dev_tyk_tech.name_servers) + depends_on = [aws_route53_zone.dev_tyk_tech] + + # This is the tyk.technology zone + zone_id = "f3ee9e1c1e0e47f8ab60fae66d39aa8f" + name = "dev" + type = "NS" + value = each.value +} + +resource "aws_route53_zone" "dev_tyk_tech" { + name = "dev.tyk.technology" +} + +# One wildcard cert + +resource "aws_acm_certificate" "dev_tyk_tech" { + domain_name = "*.dev.tyk.technology" + validation_method = "DNS" +} + +resource "aws_route53_record" "dev_tyk_tech" { + for_each = { + for dvo in aws_acm_certificate.dev_tyk_tech.domain_validation_options : dvo.domain_name => { + name = dvo.resource_record_name + record = dvo.resource_record_value + type = dvo.resource_record_type + } + } + + allow_overwrite = true + name = each.value.name + records = [each.value.record] + ttl = 60 + type = each.value.type + zone_id = aws_route53_zone.dev_tyk_tech.zone_id +} + +resource "aws_acm_certificate_validation" "dev_tyk_tech" { + certificate_arn = aws_acm_certificate.dev_tyk_tech.arn + validation_record_fqdns = [for record in aws_route53_record.dev_tyk_tech : record.fqdn] +} diff --git a/base/outputs.tf b/base/outputs.tf index 8effc05..7210e33 100644 --- a/base/outputs.tf +++ b/base/outputs.tf @@ -22,3 +22,30 @@ output "key_name" { value = aws_key_pair.devacc.key_name description = "Key pair for EC2 instances. Private key in secrets.yaml." } + +output "vpc" { + description = "A map of VPC information" + value = { + id = module.vpc.vpc_id + cidr = module.vpc.vpc_cidr_block + public_subnets = module.vpc.public_subnets + private_subnets = module.vpc.private_subnets + } +} + +output "rds" { + description = "Shared PostgreSQL RDS instance" + value = { + address = module.rds.db_instance_address + port = module.rds.db_instance_port + mpasswd_arn = aws_ssm_parameter.rds_master.arn + } +} + +output "dns" { + description = "R53 hosted zone details for dev.tyk.technology" + value = { + zone_id = aws_route53_zone.dev_tyk_tech.zone_id + cert = aws_acm_certificate.dev_tyk_tech.arn + } +} diff --git a/base/rds.tf b/base/rds.tf new file mode 100644 index 0000000..d571408 --- /dev/null +++ b/base/rds.tf @@ -0,0 +1,98 @@ +locals { + pg_port = 5432 +} + +module "rds_sg" { + source = "terraform-aws-modules/security-group/aws" + + name = "deptrack-db" + description = "RDS PostgreSQL" + vpc_id = module.vpc.vpc_id + + # ingress + ingress_with_cidr_blocks = [ + { + from_port = local.pg_port + to_port = local.pg_port + protocol = "tcp" + description = "PostgreSQL access from within VPC" + cidr_blocks = module.vpc.vpc_cidr_block + }, + ] +} + +resource "random_password" "rds_master" { + length = 16 + lower = false +} + +module "rds" { + source = "terraform-aws-modules/rds/aws" + + identifier = "postgres15" + + # All available versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts + engine = "postgres" + engine_version = "15" + family = "postgres15" # DB parameter group + major_engine_version = "15" # DB option group + instance_class = "db.t4g.medium" + allocated_storage = 10 + max_allocated_storage = 50 + + # NOTE: Do NOT use 'user' as the value for 'username' as it throws: + # "Error creating DB Instance: InvalidParameterValue: MasterUsername + # user cannot be used as it is a reserved word used by the engine" + username = "master" + # The password is stored in the state + password = random_password.rds_master.result + port = local.pg_port + + create_db_subnet_group = false + multi_az = true + db_subnet_group_name = module.vpc.database_subnet_group_name + vpc_security_group_ids = [module.rds_sg.security_group_id] + + maintenance_window = "Mon:00:00-Mon:03:00" + backup_window = "03:00-06:00" + enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] + create_cloudwatch_log_group = true + + backup_retention_period = 1 + skip_final_snapshot = true + # TODO: turn on deletion protection when stable + deletion_protection = false + + performance_insights_enabled = true + performance_insights_retention_period = 7 + create_monitoring_role = true + monitoring_interval = 60 + monitoring_role_name = "rds-monitoring" + monitoring_role_use_name_prefix = false + monitoring_role_description = "Role to ship enhanced monitoring to CloudWatch" + + parameters = [ + { + name = "autovacuum" + value = 1 + }, + { + name = "client_encoding" + value = "utf8" + } + ] + + db_option_group_tags = { + "Sensitive" = "low" + } + db_parameter_group_tags = { + "Sensitive" = "low" + } +} + +resource "aws_ssm_parameter" "rds_master" { + name = "/${local.name}/rds/master" + type = "SecureString" + description = "Password for the RDS master user" + value = random_password.rds_master.result +} diff --git a/infra/secrets.tf b/base/secrets.tf similarity index 58% rename from infra/secrets.tf rename to base/secrets.tf index c530565..b252c24 100644 --- a/infra/secrets.tf +++ b/base/secrets.tf @@ -1,5 +1,5 @@ provider "sops" {} data "sops_file" "secrets" { - source_file = "secrets.yaml" + source_file = "base-secrets.yaml" } diff --git a/base/versions.tf b/base/versions.tf index dd00227..161dac5 100644 --- a/base/versions.tf +++ b/base/versions.tf @@ -12,6 +12,14 @@ terraform { source = "hashicorp/aws" version = ">= 4.52.0" } + cloudflare = { + source = "cloudflare/cloudflare" + version = ">= 4.20.0" + } + sops = { + source = "carlpett/sops" + version = ">= 1.0.0" + } } required_version = ">= 1.3" } diff --git a/base/vpc.tf b/base/vpc.tf new file mode 100644 index 0000000..9644b9a --- /dev/null +++ b/base/vpc.tf @@ -0,0 +1,64 @@ +locals { + cidr = "10.91.0.0/16" +} + +data "aws_availability_zones" "available" { + state = "available" +} + +module "private_subnets" { + source = "hashicorp/subnets/cidr" + + base_cidr_block = cidrsubnet(local.cidr, 4, 1) + networks = [ + { name = "privaz1", new_bits = 4 }, + { name = "privaz2", new_bits = 4 }, + { name = "privaz3", new_bits = 4 }, + ] +} + +module "db_subnets" { + source = "hashicorp/subnets/cidr" + + base_cidr_block = cidrsubnet(local.cidr, 4, 8) + networks = [ + { name = "pubaz1", new_bits = 4 }, + { name = "pubaz2", new_bits = 4 }, + { name = "pubaz3", new_bits = 4 }, + ] +} + +module "public_subnets" { + source = "hashicorp/subnets/cidr" + + base_cidr_block = cidrsubnet(local.cidr, 4, 15) + networks = [ + { name = "pubaz1", new_bits = 4 }, + { name = "pubaz2", new_bits = 4 }, + { name = "pubaz3", new_bits = 4 }, + ] +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + + name = "infra" + cidr = local.cidr + + azs = data.aws_availability_zones.available.names + private_subnets = module.private_subnets.networks[*].cidr_block + private_subnet_tags = { Type = "private" } + database_subnets = module.db_subnets.networks[*].cidr_block + database_subnet_tags = { Type = "rds" } + public_subnets = module.public_subnets.networks[*].cidr_block + public_subnet_tags = { Type = "public" } + + create_database_subnet_group = true + enable_nat_gateway = true + single_nat_gateway = true + map_public_ip_on_launch = true + + # Need DNS to address EFS by name + enable_dns_support = true + enable_dns_hostnames = true +} diff --git a/bin/del-cluster.sh b/bin/del-cluster.sh new file mode 100755 index 0000000..32173cb --- /dev/null +++ b/bin/del-cluster.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +for s in $(aws ecs list-services --cluster $1 | jq -r ".serviceArns[]") +do + aws ecs update-service --cluster $1 --service $s --desired-count 0 > ${1}.log + aws ecs delete-service --cluster $1 --service $s >> ${1}.log +done +echo Waiting for services to become inactive for $1 ... +aws ecs wait services-inactive --cluster $1 --services tyk tyk-analytics tyk-pump redis +aws ecs delete-cluster --cluster $1 >> ${1}.log || cat $1.log +rm ${1}.log diff --git a/infra/deptrack.tf b/deptrack/deptrack.tf similarity index 72% rename from infra/deptrack.tf rename to deptrack/deptrack.tf index 58d4090..1eac810 100644 --- a/infra/deptrack.tf +++ b/deptrack/deptrack.tf @@ -1,12 +1,34 @@ # Dependency Track +provider "aws" { + region = "eu-central-1" + default_tags { + tags = { + "managed" = "terraform", + "ou" = "devops", + "purpose" = "security", + } + } +} + +# Persistence layer +data "terraform_remote_state" "base" { + backend = "remote" + + config = { + organization = "Tyk" + workspaces = { + name = var.base + } + } +} + locals { # ports dtrack_port = 8080 dtrack_version = "4.10.0" - dtrack_tags = { - purpose = "security" - } + dtrack_db_name = "deptrack" + dtrack_db_role = "deptrack" } resource "aws_ecs_cluster" "deptrack" { @@ -19,20 +41,18 @@ resource "aws_ecs_cluster" "deptrack" { } } } - tags = local.dtrack_tags } resource "aws_cloudwatch_log_group" "deptrack" { name = "deptrack" retention_in_days = 7 - tags = local.dtrack_tags } resource "aws_security_group" "deptrack" { name = "deptrack" description = "For DependencyTrack on Fargate" - vpc_id = module.vpc.vpc_id + vpc_id = data.terraform_remote_state.base.outputs.vpc.id egress { from_port = 0 @@ -40,20 +60,48 @@ resource "aws_security_group" "deptrack" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } - tags = local.dtrack_tags } resource "aws_vpc_security_group_ingress_rule" "deptrack" { for_each = toset([for p in [2049, local.dtrack_port] : tostring(p)]) security_group_id = aws_security_group.deptrack.id - cidr_ipv4 = module.vpc.vpc_cidr_block + cidr_ipv4 = data.terraform_remote_state.base.outputs.vpc.cidr from_port = each.key to_port = each.key ip_protocol = "tcp" - tags = local.dtrack_tags } +resource "aws_security_group" "http_s" { + name = "http_s" + description = "Allow http(s) inbound traffic from anywhere" + vpc_id = data.terraform_remote_state.base.outputs.vpc.id + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_vpc_security_group_ingress_rule" "http" { + security_group_id = aws_security_group.http_s.id + cidr_ipv4 = "0.0.0.0/0" + from_port = 80 + to_port = 80 + ip_protocol = "tcp" +} + +resource "aws_vpc_security_group_ingress_rule" "https" { + security_group_id = aws_security_group.http_s.id + cidr_ipv4 = "0.0.0.0/0" + from_port = 443 + to_port = 443 + ip_protocol = "tcp" +} + + module "deptrack_api" { source = "terraform-aws-modules/ecs/aws//modules/service" @@ -61,6 +109,8 @@ module "deptrack_api" { cluster_arn = aws_ecs_cluster.deptrack.arn assign_public_ip = true + depends_on = [aws_lb_listener.deptrack] + cpu = 4096 memory = 16384 desired_count = 1 @@ -75,6 +125,15 @@ module "deptrack_api" { { sourceVolume = "dtrack-data", containerPath = "/root" } ] environment = [ + { name = "ALPINE_DATABASE_MODE", value = "external" }, + { name = "ALPINE_DATABASE_URL", value = "jdbc:postgresql://${data.terraform_remote_state.base.outputs.rds.address}:${data.terraform_remote_state.base.outputs.rds.port}/${local.dtrack_db_name}?ssl=true" }, + { name = "ALPINE_DATABASE_DRIVER", value = "org.postgresql.Driver" }, + { name = "ALPINE_DATABASE_USERNAME", value = local.dtrack_db_role }, + { name = "ALPINE_DATABASE_POOL_ENABLED", value = "true" }, + { name = "ALPINE_DATABASE_POOL_MAX_SIZE", value = "20" }, + { name = "ALPINE_DATABASE_POOL_MIN_IDLE", value = "10" }, + { name = "ALPINE_DATABASE_POOL_IDLE_TIMEOUT", value = "300000" }, + { name = "ALPINE_DATABASE_POOL_MAX_LIFETIME", value = "600000" }, { name = "ALPINE_CORS_ENABLED", value = "true" }, { name = "ALPINE_CORS_ALLOW_ORIGIN", value = "*" }, { name = "ALPINE_CORS_ALLOW_METHODS", value = "GET,POST,PUT,DELETE,OPTIONS" }, @@ -90,6 +149,9 @@ module "deptrack_api" { { name = "ALPINE_OIDC_TEAM_SYNCHRONIZATION", value = "false" }, { name = "LOGGING_LEVEL", value = "INFO" } ] + secrets = [ + { name = "ALPINE_DATABASE_PASSWORD", valueFrom = data.terraform_remote_state.base.outputs. } + ] port_mappings = [ { name = "deptrack-api" @@ -115,9 +177,7 @@ module "deptrack_api" { } } } - depends_on = [aws_lb_listener.deptrack] - - subnet_ids = module.vpc.public_subnets + subnet_ids = data.terraform_remote_state.base.outputs.vpc.public_subnets load_balancer = { service = { target_group_arn = aws_lb_target_group.deptrack_api.arn @@ -128,8 +188,6 @@ module "deptrack_api" { create_security_group = false security_group_ids = [aws_security_group.deptrack.id] - - tags = local.dtrack_tags } module "deptrack_fe" { @@ -138,6 +196,11 @@ module "deptrack_fe" { name = "deptrack-fe" cluster_arn = aws_ecs_cluster.deptrack.arn assign_public_ip = true + desired_count = 2 + + create_security_group = false + + depends_on = [module.deptrack_api, aws_lb_listener.deptrack] cpu = 1024 memory = 2048 @@ -172,10 +235,7 @@ module "deptrack_fe" { ] } } - desired_count = 2 - depends_on = [module.deptrack_api, aws_lb_listener.deptrack] - - subnet_ids = module.vpc.public_subnets + subnet_ids = data.terraform_remote_state.base.outputs.vpc.public_subnets load_balancer = { service = { target_group_arn = aws_lb_target_group.deptrack_fe.arn @@ -183,11 +243,7 @@ module "deptrack_fe" { container_port = local.dtrack_port } } - - create_security_group = false - security_group_ids = [aws_security_group.deptrack.id] - - tags = local.dtrack_tags + security_group_ids = [aws_security_group.deptrack.id] } resource "aws_lb" "deptrack" { @@ -195,7 +251,7 @@ resource "aws_lb" "deptrack" { internal = false load_balancer_type = "application" security_groups = [aws_security_group.http_s.id] - subnets = module.vpc.public_subnets + subnets = data.terraform_remote_state.base.outputs.vpc.public_subnets enable_deletion_protection = true @@ -210,7 +266,6 @@ resource "aws_lb" "deptrack" { prefix = "deptrack-lb" enabled = true } - tags = local.dtrack_tags } resource "aws_lb_listener" "deptrack" { @@ -218,7 +273,7 @@ resource "aws_lb_listener" "deptrack" { port = "443" protocol = "HTTPS" ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" - certificate_arn = aws_acm_certificate_validation.dev_tyk_technology.certificate_arn + certificate_arn = data.terraform_remote_state.base.outputs.cert default_action { type = "fixed-response" @@ -228,7 +283,6 @@ resource "aws_lb_listener" "deptrack" { status_code = "404" } } - tags = local.dtrack_tags } resource "aws_lb_listener_rule" "deptrack" { @@ -244,7 +298,6 @@ resource "aws_lb_listener_rule" "deptrack" { values = ["deptrack.dev.tyk.technology"] } } - tags = local.dtrack_tags } resource "aws_lb_listener_rule" "deptrack_api" { @@ -260,7 +313,6 @@ resource "aws_lb_listener_rule" "deptrack_api" { values = ["deptrack-api.dev.tyk.technology"] } } - tags = local.dtrack_tags } resource "aws_lb_target_group" "deptrack_fe" { @@ -268,9 +320,7 @@ resource "aws_lb_target_group" "deptrack_fe" { port = local.dtrack_port protocol = "HTTP" target_type = "ip" - vpc_id = module.vpc.vpc_id - - tags = local.dtrack_tags + vpc_id = data.terraform_remote_state.base.outputs.vpc.id } resource "aws_lb_target_group" "deptrack_api" { @@ -278,20 +328,18 @@ resource "aws_lb_target_group" "deptrack_api" { port = local.dtrack_port protocol = "HTTP" target_type = "ip" - vpc_id = module.vpc.vpc_id + vpc_id = data.terraform_remote_state.base.outputs.vpc.id health_check { enabled = true path = "/api/version" } - - tags = local.dtrack_tags } resource "aws_route53_record" "deptrack" { for_each = toset(["deptrack", "deptrack-api"]) - zone_id = aws_route53_zone.dev_tyk_tech.zone_id + zone_id = data.terraform_remote_state.base.outputs.zone_id name = each.key type = "CNAME" @@ -299,4 +347,3 @@ resource "aws_route53_record" "deptrack" { records = [aws_lb.deptrack.dns_name] } - diff --git a/deptrack/prod.auto.tfvars b/deptrack/prod.auto.tfvars new file mode 100644 index 0000000..9be076e --- /dev/null +++ b/deptrack/prod.auto.tfvars @@ -0,0 +1 @@ +base = "base-prod" diff --git a/deptrack/variables.tf b/deptrack/variables.tf new file mode 100644 index 0000000..9a5f64f --- /dev/null +++ b/deptrack/variables.tf @@ -0,0 +1,4 @@ +variable "base" { + description = "Name of the terraform workspace which holds the base layer" + type = string +} diff --git a/infra/bastion-cloudinit.yaml.tftpl b/infra/bastion-cloudinit.yaml.tftpl index 8d178b8..a618b27 100644 --- a/infra/bastion-cloudinit.yaml.tftpl +++ b/infra/bastion-cloudinit.yaml.tftpl @@ -1,4 +1,5 @@ -# cloud-config for bastion, assumed to be al2023 +#cloud-config +# assumed to be al2023 full # templated for EFS mounts ssh_keys: @@ -31,12 +32,35 @@ yum_repos: name: MongoDB community edition 7.0 packages: - - mongodb-mongosh - - amazon-efs-utils + - mongodb-mongosh + - git + - amazon-efs-utils + - amazon-cloudwatch-agent + - python3-pip + +users: + - default # do not remove + - name: ansible + gecos: Ansible User + shell: /bin/bash + groups: users,admin,wheel + sudo: ALL=(ALL) NOPASSWD:ALL + +ansible: + install_method: pip + package_name: ansible + run_user: ec2-user + galaxy: + actions: + - ["ansible-galaxy", "collection", "install", "community.general"] + setup_controller: + repositories: + - path: /home/ansible/tyk-ci/ + source: git@github.com:TykTechnologies/tyk-ci.git mounts: %{ for m in efs_mounts ~} - - ["${m.dev}:/", "${m.mp}", efs, "defaults,_netdev,nofail", 0, 0 ] + - ["${m.dev}:/", "${m.mp}", efs, "defaults,_netdev,nofail", "0", "0" ] %{ endfor ~} runcmd: diff --git a/infra/cloudflare.tf b/infra/cloudflare.tf deleted file mode 100644 index 2bb604e..0000000 --- a/infra/cloudflare.tf +++ /dev/null @@ -1,14 +0,0 @@ -provider "cloudflare" { - api_token = data.sops_file.secrets.data["cf-apitoken"] - # account_id = "35b8134b47c7d01ee8198bb2b82a8dc5" -} - -resource "cloudflare_record" "dev_tyk_tech" { - for_each = toset(aws_route53_zone.dev_tyk_tech.name_servers) - - # This is the tyk.technology zone - zone_id = "f3ee9e1c1e0e47f8ab60fae66d39aa8f" - name = "dev" - type = "NS" - value = each.value -} diff --git a/infra/iam.tf b/infra/iam.tf index 05af93e..b303433 100644 --- a/infra/iam.tf +++ b/infra/iam.tf @@ -76,3 +76,4 @@ resource "aws_s3_bucket_policy" "deptrack_lb_logs" { } EOF } + diff --git a/infra/infra.tf b/infra/infra.tf index 415d216..9b99ba1 100644 --- a/infra/infra.tf +++ b/infra/infra.tf @@ -1,11 +1,10 @@ provider "aws" { - region = var.region + region = "eu-central-1" default_tags { tags = { "managed" = "terraform", "ou" = "devops", "purpose" = "ci", - "env" = var.name_prefix } } } @@ -22,57 +21,10 @@ data "terraform_remote_state" "base" { } } -data "aws_availability_zones" "available" { - state = "available" -} - -module "private_subnets" { - source = "hashicorp/subnets/cidr" - - base_cidr_block = cidrsubnet(var.cidr, 4, 1) - networks = [ - { name = "privaz1", new_bits = 4 }, - { name = "privaz2", new_bits = 4 }, - { name = "privaz3", new_bits = 4 }, - ] -} - -module "public_subnets" { - source = "hashicorp/subnets/cidr" - - base_cidr_block = cidrsubnet(var.cidr, 4, 15) - networks = [ - { name = "pubaz1", new_bits = 4 }, - { name = "pubaz2", new_bits = 4 }, - { name = "pubaz3", new_bits = 4 }, - ] -} - -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - - name = var.name_prefix - cidr = var.cidr - - azs = data.aws_availability_zones.available.names - private_subnets = module.private_subnets.networks[*].cidr_block - private_subnet_tags = { Type = "private" } - public_subnets = module.public_subnets.networks[*].cidr_block - public_subnet_tags = { Type = "public" } - - enable_nat_gateway = true - single_nat_gateway = true - map_public_ip_on_launch = true - - # Need DNS to address EFS by name - enable_dns_support = true - enable_dns_hostnames = true -} - -resource "aws_security_group" "tasks" { - name = "tasks" +resource "aws_security_group" "instances" { + name = "instances" description = "EFS, ssh and egress" - vpc_id = module.vpc.vpc_id + vpc_id = data.terraform_remote_state.base.outputs.vpc.id egress { from_port = 0 @@ -83,7 +35,7 @@ resource "aws_security_group" "tasks" { } resource "aws_vpc_security_group_ingress_rule" "ssh" { - security_group_id = aws_security_group.tasks.id + security_group_id = aws_security_group.instances.id cidr_ipv4 = "0.0.0.0/0" from_port = 22 to_port = 22 @@ -91,58 +43,29 @@ resource "aws_vpc_security_group_ingress_rule" "ssh" { } resource "aws_vpc_security_group_ingress_rule" "efs_tasks" { - security_group_id = aws_security_group.tasks.id - cidr_ipv4 = module.vpc.vpc_cidr_block + security_group_id = aws_security_group.instances.id + cidr_ipv4 = data.terraform_remote_state.base.outputs.vpc.cidr from_port = 2049 to_port = 2049 ip_protocol = "tcp" } -resource "aws_security_group" "http_s" { - name = "http_s" - description = "Allow http(s) inbound traffic from anywhere" - vpc_id = module.vpc.vpc_id - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } -} - -resource "aws_vpc_security_group_ingress_rule" "http" { - security_group_id = aws_security_group.http_s.id - cidr_ipv4 = "0.0.0.0/0" - from_port = 80 - to_port = 80 - ip_protocol = "tcp" -} - -resource "aws_vpc_security_group_ingress_rule" "https" { - security_group_id = aws_security_group.http_s.id - cidr_ipv4 = "0.0.0.0/0" - from_port = 443 - to_port = 443 - ip_protocol = "tcp" -} - # CD tasks will run in the public subnet resource "aws_efs_mount_target" "shared" { - for_each = toset(module.vpc.public_subnets) + for_each = toset(data.terraform_remote_state.base.outputs.vpc.public_subnets) file_system_id = data.terraform_remote_state.base.outputs.shared_efs subnet_id = each.value - security_groups = [aws_security_group.tasks.id] + security_groups = [aws_security_group.instances.id] } -# The deptrack EFS is only needed in the private subnet +# TODO: remove deptrack when done resource "aws_efs_mount_target" "deptrack" { - for_each = toset(module.vpc.private_subnets) + for_each = toset(data.terraform_remote_state.base.outputs.vpc.public_subnets) file_system_id = data.terraform_remote_state.base.outputs.deptrack_efs subnet_id = each.value - security_groups = [aws_security_group.tasks.id] + security_groups = [aws_security_group.instances.id] } data "cloudinit_config" "bastion" { @@ -153,6 +76,7 @@ data "cloudinit_config" "bastion" { content_type = "text/cloud-config" content = templatefile("bastion-cloudinit.yaml.tftpl", { efs_mounts = [ + # TODO: remove deptrack { dev = data.terraform_remote_state.base.outputs.deptrack_efs, mp = "/deptrack" }, { dev = data.terraform_remote_state.base.outputs.shared_efs, mp = "/shared" }, ] @@ -160,6 +84,11 @@ data "cloudinit_config" "bastion" { } } +# For debugging cloud-init +# output "cloudinit" { +# value = data.cloudinit_config.bastion.rendered +# } + # Bastion module "bastion" { @@ -171,16 +100,23 @@ module "bastion" { instance_type = "t3.nano" key_name = data.terraform_remote_state.base.outputs.key_name monitoring = true - vpc_security_group_ids = [aws_security_group.tasks.id] - subnet_id = element(module.vpc.public_subnets, 0) + vpc_security_group_ids = [aws_security_group.instances.id] + subnet_id = element(data.terraform_remote_state.base.outputs.vpc.public_subnets, 0) associate_public_ip_address = true user_data_base64 = data.cloudinit_config.bastion.rendered - + user_data_replace_on_change = true + # Allow access via SessionManager + create_iam_instance_profile = true + iam_role_description = "IAM role for EC2 instance" + iam_role_policies = { + AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", + CloudWatchAgentServerPolicy = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy" + } # Spot request specific attributes - spot_price = "0.1" - spot_wait_for_fulfillment = true - spot_type = "persistent" - spot_instance_interruption_behavior = "terminate" + # spot_price = "0.1" + # spot_wait_for_fulfillment = true + # spot_type = "persistent" + # spot_instance_interruption_behavior = "terminate" metadata_options = { http_tokens = "required" # IMDSv2 @@ -192,7 +128,7 @@ data "aws_ami" "al2023" { owners = ["amazon"] filter { name = "name" - values = ["al2023-ami-minimal-*"] + values = ["al2023-ami-2023*"] } filter { name = "architecture" @@ -227,49 +163,16 @@ resource "aws_ecs_cluster" "internal" { } } -# One wildcard cert - -resource "aws_acm_certificate" "dev_tyk_technology" { - domain_name = "*.dev.tyk.technology" - validation_method = "DNS" -} - -resource "aws_route53_record" "dev_tyk_technology" { - for_each = { - for dvo in aws_acm_certificate.dev_tyk_technology.domain_validation_options : dvo.domain_name => { - name = dvo.resource_record_name - record = dvo.resource_record_value - type = dvo.resource_record_type - } - } - - allow_overwrite = true - name = each.value.name - records = [each.value.record] - ttl = 60 - type = each.value.type - zone_id = aws_route53_zone.dev_tyk_tech.zone_id -} - -resource "aws_acm_certificate_validation" "dev_tyk_technology" { - certificate_arn = aws_acm_certificate.dev_tyk_technology.arn - validation_record_fqdns = [for record in aws_route53_record.dev_tyk_technology : record.fqdn] -} - # DNS resource "aws_service_discovery_private_dns_namespace" "internal" { name = "dev.internal" description = "Private DNS for resources" - vpc = module.vpc.vpc_id -} - -resource "aws_route53_zone" "dev_tyk_tech" { - name = "dev.tyk.technology" + vpc = data.terraform_remote_state.base.outputs.vpc.id } resource "aws_route53_record" "bastion" { - zone_id = aws_route53_zone.dev_tyk_tech.zone_id + zone_id = data.terraform_remote_state.base.outputs.dns.zone_id name = "bastion" type = "A" diff --git a/infra/outputs.tf b/infra/outputs.tf deleted file mode 100644 index 8ffc013..0000000 --- a/infra/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "vpc_id" { - value = module.vpc.vpc_id - description = "VPC for infra" -} - -output "vpc_cidr" { - value = module.vpc.vpc_cidr_block - description = "CIDR block of infra VPC" -} diff --git a/infra/prod.auto.tfvars b/infra/prod.auto.tfvars index 003b8bd..e512494 100644 --- a/infra/prod.auto.tfvars +++ b/infra/prod.auto.tfvars @@ -1,6 +1,3 @@ base = "base-prod" -name_prefix = "prod" -cidr = "10.91.0.0/16" -region = "eu-central-1" stepca_image = "smallstep/step-ca:0.25.2" -#gromit_image = "tykio/gromit:v1.4.4" +#gromit_image = "tykio/gromit:v1.4.4" diff --git a/infra/secrets.yaml b/infra/secrets.yaml deleted file mode 100644 index 6e7188e..0000000 --- a/infra/secrets.yaml +++ /dev/null @@ -1,18 +0,0 @@ -cf-apitoken: ENC[AES256_GCM,data:yqJE0+WA1V/PNIR8k6mV/R1WAa05MxEAxnZLXE3XslKKF8gDa+XVmA==,iv:xfUGpZmzflh/+d/LTZDB2kKc800/T8pY5iX407mJtZc=,tag:NpvgAiKUlhZA8ltHyiBJ2Q==,type:str] -ca-password: ENC[AES256_GCM,data:DaLzFdvwFZY6FmGMA7UL5w==,iv:TJ7iudFpprSyXxRGNnMgukaJx+/SEv4/ZEwKWQDU8/Y=,tag:P1R/b3KPBBlEbReHToa2vg==,type:str] -ssh-privkey: ENC[AES256_GCM,data:Y/KrykzJaRMY1JPLyGdyY/44hbl/dGEhI7N7k62JY1qORjXaEZ9flElJTIQnUGVetGJOS1TFk7RqzzidrzpoijqZoLBLnM+l02czucPt34YW6HJkyYqyJ34LKEDbn+K7dd21w6vi8prQJVK8NBoJypb5eFK2ZCEbGwJLH31gKTwF2gP1UizDliEyVlBdvNLF84UqcuznwoiPEmcZVt+9L9Z2HibFMoB46F/fFKXt5ei58cH6pRrZg2kQzL3sMlPGt7ue5DlTcrr21lN5XdkdgRASDd5rRhjC0nqZJsKHAw/KgiIc98N8xqqJYFSgJLy/+k7x52gV9tr4axaIorxLpfhVlRGG6U2LcJTPmsOJUMtezEdm0pCXB/sWVzTK+F0taCLkfUC7lacNrgWbrlfgnM90j7vpl6qKMQJGGgqnoCjBQjM/KW3/9A2In+im05+IhCBpNea6LcA3xnFaU3gL/OkrYoojADJLwNfxC4CzwffvsJJgnQH8ZYUHfV962sCz17ED2892N/IWi9av7hk=,iv:IGApkm30soFDLWUDXq7N0FUdaukYX0elHvh25hD7PjM=,tag:7Q5v8stntY8h6+moyItlfA==,type:str] -sops: - kms: - - arn: arn:aws:kms:eu-central-1:754489498669:key/215a7274-5652-4521-8a88-b18e02b8f13e - created_at: "2024-03-13T10:51:57Z" - enc: AQICAHiDjTyDzev9deXqMt8qn7IIVL95PjWZTOOP+RjKHUtt0AE/GE7nGT6W8+wdkGL0kFOCAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMcWnXC4kCv+Y0ERk9AgEQgDvGm/qPx6ZsRkm6mtAMz/veQQf1CkECFKgCY+CBsJri/MBSbQ88V5J3/m/dvKwQ0zmJRLXwTftd5LGxOQ== - aws_profile: "" - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: [] - lastmodified: "2024-03-13T10:51:58Z" - mac: ENC[AES256_GCM,data:LKjtawZ4eig2aXFaGAS0u6FDZNZeykZMpYL9jgpZc/YOWeEu+9pmx0esaU6ZcM1qzJZmXavMH0Tp8S7mEEVByMk8g3qrjzTDAdvyWJvw+3NqnHFnL7nXE4wBqpwTDCnFkC30lty28eeyvaqg+a0Ujhu0ZAItUT5hQSV5s+HzZIg=,iv:NL6pYsMtsAy0e+UlXxGDBOe1cyBwDEQw6jvs1Ohgz6Y=,tag:EBsXTH8w71Fxc34IGcSkNg==,type:str] - pgp: [] - unencrypted_suffix: _unencrypted - version: 3.8.1 diff --git a/infra/storage.tf b/infra/storage.tf index dca49f3..b21b7c3 100644 --- a/infra/storage.tf +++ b/infra/storage.tf @@ -34,9 +34,9 @@ module "storage_sg" { name = "storage" description = "Persistent storage EC2 instances" - vpc_id = module.vpc.vpc_id + vpc_id = data.terraform_remote_state.base.outputs.vpc.id - ingress_cidr_blocks = [module.vpc.vpc_cidr_block] + ingress_cidr_blocks = [data.terraform_remote_state.base.outputs.vpc.cidr] ingress_rules = [ "postgresql-tcp", "mongodb-27017-tcp", @@ -58,9 +58,9 @@ module "storage_components" { monitoring = true vpc_security_group_ids = [ module.storage_sg.security_group_id, - aws_security_group.tasks.id, + aws_security_group.instances.id, ] - subnet_id = element(module.vpc.private_subnets, 1) + subnet_id = element(data.terraform_remote_state.base.outputs.vpc.private_subnets, 1) spot_price = "0.1" spot_wait_for_fulfillment = true @@ -75,7 +75,7 @@ module "storage_components" { resource "aws_route53_zone" "storage_internal" { name = "storage.internal" vpc { - vpc_id = module.vpc.vpc_id + vpc_id = data.terraform_remote_state.base.outputs.vpc.id } } diff --git a/infra/variables.tf b/infra/variables.tf index 1218dc3..2e75564 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -3,20 +3,6 @@ variable "base" { type = string } -variable "name_prefix" { - description = "Prefixed to resource names where possible" - type = string -} - -variable "cidr" { - description = "CIDR for VPC" - type = string -} - -variable "region" { - type = string -} - variable "stepca_image" { description = "Full repo URL with tag of the step-ca image to use" } diff --git a/infra/versions.tf b/infra/versions.tf index ae08f1e..f65fbbb 100644 --- a/infra/versions.tf +++ b/infra/versions.tf @@ -11,17 +11,6 @@ terraform { source = "hashicorp/aws" version = ">= 5.31.0" } - cloudflare = { - source = "cloudflare/cloudflare" - version = ">= 4.20.0" - } - sops = { - source = "carlpett/sops" - version = ">= 1.0.0" - } - template = { - source = "hashicorp/template" - } } - required_version = ">= 1.3" + required_version = ">= 1.7" }