diff --git a/auth.tf b/auth.tf new file mode 100644 index 0000000..862a298 --- /dev/null +++ b/auth.tf @@ -0,0 +1,178 @@ +resource "aws_instance" "auth" { + ami = data.aws_ami.minimal-arm64.id + availability_zone = local.availability_zone + instance_type = "t4g.nano" + subnet_id = aws_subnet.default.id + iam_instance_profile = aws_iam_instance_profile.ecs_instance.name + vpc_security_group_ids = [aws_security_group.auth.id] + associate_public_ip_address = false + user_data_replace_on_change = true + + root_block_device { + volume_type = "gp3" + volume_size = 4 + } + + instance_market_options { + market_type = "spot" + spot_options { + instance_interruption_behavior = "stop" + spot_instance_type = "persistent" + } + } + + user_data = base64encode(<<-INIT + #!/bin/bash + yum install -y amazon-ssm-agent + systemctl enable amazon-ssm-agent + systemctl start amazon-ssm-agent + + amazon-linux-extras disable docker + amazon-linux-extras enable ecs + yum install -y ecs-init tc + echo "ECS_CLUSTER=${aws_ecs_cluster.default.name}" > /etc/ecs/ecs.config + + cat < /etc/systemd/system/mount-ebs.service + [Unit] + Description=Format and Mount Device + DefaultDependencies=no + Before=local-fs.target + Wants=local-fs.target + + [Service] + Type=oneshot + ExecStartPre=/bin/bash -c '(while ! /usr/bin/lsblk -ln -o FSTYPE /dev/sdh 2>/dev/null; do echo "Waiting for block device /dev/sdh..."; sleep 2; done); sleep 2' + ExecStart=/bin/bash -c "if [ \"\$(lsblk -ln -o FSTYPE /dev/sdh)\" != \"ext4\" ]; then /usr/sbin/mkfs.ext4 -L auth /dev/sdh ; fi && /usr/bin/mkdir -p /auth && /usr/bin/mount /dev/sdh /auth ; /usr/bin/mkdir -p /auth/auth" + ExecStop=/usr/bin/umount /auth + RemainAfterExit=yes + + [Install] + WantedBy=multi-user.target + EOF + + systemctl daemon-reload + systemctl enable mount-ebs + systemctl start mount-ebs + systemctl enable ecs + systemctl start --no-block ecs + INIT + ) + + tags = { + Name = "${local.name}-ec2-auth" + } +} + +resource "aws_ecs_task_definition" "auth" { + family = "${local.name}-ecs-task-definition-auth" + + container_definitions = jsonencode([ + { + name = "auth" + image = "749980637880.dkr.ecr.us-east-2.amazonaws.com/infra2-ecr:auth2" + memoryReservation = 200 + + mountPoints = [ + { + sourceVolume = "auth" + containerPath = "/auth" + readOnly = false + } + ] + + dockerLabels = { + "traefik.enable" = "true" + "traefik.http.routers.auth.rule" = "Host(`auth.testing.osucyber.club`)" + "traefik.http.routers.auth.entrypoints" = "websecure" + "traefik.http.routers.auth.tls.certResolver" = "letsencrypt", + "traefik.http.routers.auth.tls.domains.0.main" = "*.testing.osucyber.club", + } + + portMappings = [ + { + containerPort = 3000 + hostPort = 0 + name = "auth" + } + ] + + environment = [ + { name = "DB_URL", value = "/auth/auth.db" }, + ] + + healthCheck = { + retries = 3 + command = [ + "CMD-SHELL", + "true" + ] + timeout = 3 + interval = 10 + } + + logConfiguration = { + logDriver = "awslogs" + options = { + "awslogs-group" = aws_cloudwatch_log_group.default.name + "awslogs-region" = local.region, + "awslogs-stream-prefix" = "auth", + } + } + + stopTimeout = 300 + } + ]) + + volume { + name = "auth" + host_path = "/auth/auth" + } +} + +resource "aws_ebs_volume" "auth" { + availability_zone = local.availability_zone + size = 2 + type = "gp3" + lifecycle { + prevent_destroy = true + } +} + +resource "aws_volume_attachment" "auth" { + device_name = "/dev/sdh" + instance_id = aws_instance.auth.id + volume_id = aws_ebs_volume.auth.id + force_detach = true +} + +resource "aws_ecs_service" "auth" { + depends_on = [aws_iam_role_policy.ecs_cluster_permissions] + name = "${local.name}-ecs-service-auth" + cluster = aws_ecs_cluster.default.id + task_definition = aws_ecs_task_definition.auth.arn + desired_count = 1 + + placement_constraints { + type = "memberOf" + expression = "ec2InstanceId in ['${aws_instance.auth.id}']" + } +} + +resource "aws_security_group" "auth" { + name = "${local.name}-sg-auth" + vpc_id = aws_vpc.default.id + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/ctfd.tf b/ctfd.tf index 9160f65..b166dbb 100644 --- a/ctfd.tf +++ b/ctfd.tf @@ -6,6 +6,7 @@ resource "aws_instance" "ctfd" { iam_instance_profile = aws_iam_instance_profile.ecs_instance.name vpc_security_group_ids = [aws_security_group.ctfd.id] associate_public_ip_address = false + user_data_replace_on_change = true root_block_device { volume_type = "gp3" @@ -127,7 +128,7 @@ resource "aws_ecs_task_definition" "ctfd" { retries = 3 command = [ "CMD-SHELL", - "true" + "python3 -c 'import urllib.request; exit(0 if urllib.request.urlopen(\"http://localhost:8000/healthcheck\", timeout=5).status == 200 else 1)'" ] timeout = 3 interval = 10 diff --git a/ecs.tf b/ecs.tf index 49e2a4b..f81f47a 100644 --- a/ecs.tf +++ b/ecs.tf @@ -32,13 +32,12 @@ data "aws_iam_policy_document" "ecs_cluster_permissions" { effect = "Allow" actions = [ # AWS ECS Agent - "ecs:DeregisterContainerInstance", "ecs:DiscoverPollEndpoint", "ecs:Poll", - "ecs:RegisterContainerInstance", "ecs:StartTelemetrySession", - "ecs:SubmitContainerStateChange", - "ecs:SubmitTaskStateChange", + + # Allow downloading images from private ECR + "ecr:GetAuthorizationToken", "logs:CreateLogStream", "logs:PutLogEvents", @@ -69,16 +68,31 @@ data "aws_iam_policy_document" "ecs_cluster_permissions" { ] } - # statement { - # effect = "Allow" - # actions = [ - # "logs:CreateLogStream", - # "logs:PutLogEvents", - # ] - # resources = [ - # "arn:aws:logs:*:*:*" - # ] - # } + # Allow downloading images from private ECR + statement { + effect = "Allow" + actions = [ + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer", + ] + resources = [ + aws_ecr_repository.default.arn + ] + } + + # AWS ECS Agent + statement { + effect = "Allow" + actions = [ + "ecs:DeregisterContainerInstance", + "ecs:RegisterContainerInstance", + "ecs:SubmitContainerStateChange", + "ecs:SubmitTaskStateChange", + ] + resources = [ + aws_ecs_cluster.default.arn + ] + } } resource "aws_ecs_cluster" "default" { diff --git a/traefik.tf b/traefik.tf index 6960b37..f026290 100644 --- a/traefik.tf +++ b/traefik.tf @@ -15,6 +15,7 @@ resource "aws_instance" "traefik" { vpc_security_group_ids = [aws_security_group.traefik.id] source_dest_check = false associate_public_ip_address = true + user_data_replace_on_change = true root_block_device { volume_type = "gp3"