From 18ef9abe0d8ec94df59bb056d6b5096ca2c6737b Mon Sep 17 00:00:00 2001 From: pwh9882 Date: Mon, 31 Aug 2026 03:19:20 +0000 Subject: [PATCH 1/3] Add durable Terraform remote state backend --- IaC/backend.tf | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 IaC/backend.tf diff --git a/IaC/backend.tf b/IaC/backend.tf new file mode 100644 index 0000000000..bb05895e1e --- /dev/null +++ b/IaC/backend.tf @@ -0,0 +1,5 @@ +terraform { + required_version = "~> 1.16.0" + + backend "s3" {} +} From b52e512550b8a3b1c9caca4221e5b9ea919a9fa6 Mon Sep 17 00:00:00 2001 From: pwh9882 Date: Mon, 31 Aug 2026 11:35:56 +0000 Subject: [PATCH 2/3] Set application domain to sql.ddps.cloud --- IaC/var.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IaC/var.tf b/IaC/var.tf index 25498dd9e7..e86bb1e678 100644 --- a/IaC/var.tf +++ b/IaC/var.tf @@ -89,7 +89,7 @@ variable "ondemand_server_instance_class" { variable "domain_name" { description = "Domain name for the application" type = string - default = "playzone.ddps.cloud" + default = "sql.ddps.cloud" } variable "on_demand_base_capacity" { From f7e0ca17c2993d9fbb2ea2d84132c38bc1a1b18d Mon Sep 17 00:00:00 2001 From: pwh9882 Date: Mon, 31 Aug 2026 12:12:27 +0000 Subject: [PATCH 3/3] Derive deployment resource names from prefix --- IaC/ami/ami-builder-userdata.sh | 10 ++++----- IaC/ami/ami.tf | 8 ++++--- IaC/ami/var.tf | 4 +++- IaC/ec2/ec2.tf | 11 +++++++--- IaC/ec2/userdata.sh | 24 ++++++++++---------- IaC/ec2/var.tf | 7 +++++- IaC/lambda/lambda.tf | 5 ++--- IaC/locals.tf | 9 ++++++++ IaC/logging.tf | 39 +++++++++++++++++++++++++++++++++ IaC/main.tf | 21 +++++++++++++++--- IaC/var.tf | 27 ++++++----------------- 11 files changed, 114 insertions(+), 51 deletions(-) create mode 100644 IaC/locals.tf create mode 100644 IaC/logging.tf diff --git a/IaC/ami/ami-builder-userdata.sh b/IaC/ami/ami-builder-userdata.sh index eda1fe4002..2354367566 100644 --- a/IaC/ami/ami-builder-userdata.sh +++ b/IaC/ami/ami-builder-userdata.sh @@ -58,9 +58,9 @@ EOF docker compose build aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com -docker tag platform-sql-judge:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-sql-judge:latest -docker tag platform-ctfd:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-ctfd:latest -docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-sql-judge:latest -docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-ctfd:latest +docker tag platform-sql-judge:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${SQL_JUDGE_ECR_REPOSITORY_NAME}:latest +docker tag platform-ctfd:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${CTFD_ECR_REPOSITORY_NAME}:latest +docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${SQL_JUDGE_ECR_REPOSITORY_NAME}:latest +docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${CTFD_ECR_REPOSITORY_NAME}:latest -docker rmi -f $(docker images -aq) \ No newline at end of file +docker rmi -f $(docker images -aq) diff --git a/IaC/ami/ami.tf b/IaC/ami/ami.tf index bf7b494465..90bc93e5d7 100644 --- a/IaC/ami/ami.tf +++ b/IaC/ami/ami.tf @@ -1,5 +1,5 @@ resource "aws_ecr_repository" "platform-sql-judge-ecr-repository" { - name = "platform-sql-judge" + name = var.sql_judge_ecr_repository_name image_tag_mutability = "MUTABLE" force_delete = true # Allow deletion even with images @@ -9,7 +9,7 @@ resource "aws_ecr_repository" "platform-sql-judge-ecr-repository" { } resource "aws_ecr_repository" "platform-sql-judge-ecr-ctfd" { - name = "platform-ctfd" + name = var.ctfd_ecr_repository_name image_tag_mutability = "MUTABLE" force_delete = true # Allow deletion even with images @@ -128,6 +128,8 @@ resource "aws_instance" "ami_builder_arm" { GOOGLE_CLIENT_SECRET = var.google_client_secret REGION = var.region AWS_ACCOUNT_ID = var.aws_account_id + CTFD_ECR_REPOSITORY_NAME = var.ctfd_ecr_repository_name + SQL_JUDGE_ECR_REPOSITORY_NAME = var.sql_judge_ecr_repository_name })) tags = { @@ -171,4 +173,4 @@ resource "null_resource" "terminate_builder_arm" { provisioner "local-exec" { command = "aws ec2 terminate-instances --instance-ids ${aws_instance.ami_builder_arm[0].id} --region ${var.region} --profile ${var.aws_profile}" } -} \ No newline at end of file +} diff --git a/IaC/ami/var.tf b/IaC/ami/var.tf index 10368f3380..7b69455e10 100644 --- a/IaC/ami/var.tf +++ b/IaC/ami/var.tf @@ -10,4 +10,6 @@ variable "ctfd_secret_key" { type = string } variable "google_client_id" { type = string } variable "google_client_secret" { type = string } variable "aws_account_id" { type = string } -variable "elasticache_serverless_endpoint" { type = string } \ No newline at end of file +variable "ctfd_ecr_repository_name" { type = string } +variable "sql_judge_ecr_repository_name" { type = string } +variable "elasticache_serverless_endpoint" { type = string } diff --git a/IaC/ec2/ec2.tf b/IaC/ec2/ec2.tf index 4f25da45d6..2ad07b158a 100644 --- a/IaC/ec2/ec2.tf +++ b/IaC/ec2/ec2.tf @@ -211,8 +211,13 @@ resource "aws_launch_template" "arm_launch_template" { } user_data = base64encode(templatefile("${path.module}/userdata.sh", { - REGION = var.region - AWS_ACCOUNT_ID = var.aws_account_id + REGION = var.region + AWS_ACCOUNT_ID = var.aws_account_id + CTFD_ECR_REPOSITORY_NAME = var.ctfd_ecr_repository_name + SQL_JUDGE_ECR_REPOSITORY_NAME = var.sql_judge_ecr_repository_name + APPLICATION_LOG_GROUP_NAME = var.application_log_group_name + BEHAVIOR_LOG_GROUP_NAME = var.behavior_log_group_name + BEHAVIOR_LOG_STREAM_NAME = var.behavior_log_stream_name })) tag_specifications { @@ -315,4 +320,4 @@ resource "aws_autoscaling_policy" "request_count_tracking" { # 어떤게 좋을지 알아봐야 할듯함. target_value = 300.0 # 인스턴스당 300 요청 유지 } -} \ No newline at end of file +} diff --git a/IaC/ec2/userdata.sh b/IaC/ec2/userdata.sh index ed0c24f61d..cfafe28eaf 100644 --- a/IaC/ec2/userdata.sh +++ b/IaC/ec2/userdata.sh @@ -14,38 +14,38 @@ cat << 'CWCONFIG' | tee /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-a "collect_list": [ { "file_path": "/home/ubuntu/sql-playzone/platform/.data/CTFd/logs/logins.log", - "log_group_name": "/aws/ec2/sql-playzone", + "log_group_name": "${APPLICATION_LOG_GROUP_NAME}", "log_stream_name": "logins", "timezone": "Local" }, { "file_path": "/home/ubuntu/sql-playzone/platform/.data/CTFd/logs/registrations.log", - "log_group_name": "/aws/ec2/sql-playzone", + "log_group_name": "${APPLICATION_LOG_GROUP_NAME}", "log_stream_name": "registrations", "timezone": "Local" }, { "file_path": "/home/ubuntu/sql-playzone/platform/.data/CTFd/logs/submissions.log", - "log_group_name": "/aws/ec2/sql-playzone", + "log_group_name": "${APPLICATION_LOG_GROUP_NAME}", "log_stream_name": "submissions", "timezone": "Local" }, { "file_path": "/home/ubuntu/sql-playzone/platform/.data/CTFd/logs/error.log", - "log_group_name": "/aws/ec2/sql-playzone", + "log_group_name": "${APPLICATION_LOG_GROUP_NAME}", "log_stream_name": "error", "timezone": "Local" }, { "file_path": "/home/ubuntu/sql-playzone/platform/.data/CTFd/logs/sql-judge.log", - "log_group_name": "/aws/ec2/sql-playzone", + "log_group_name": "${APPLICATION_LOG_GROUP_NAME}", "log_stream_name": "sql-judge", "timezone": "Local" }, { "file_path": "/home/ubuntu/sql-playzone/platform/.data/CTFd/logs/sql_challenge_behavior.log", - "log_group_name": "/aws/ec2/sql-playzone-behavior", - "log_stream_name": "sql-challenge", + "log_group_name": "${BEHAVIOR_LOG_GROUP_NAME}", + "log_stream_name": "${BEHAVIOR_LOG_STREAM_NAME}", "timezone": "Local" } ] @@ -70,12 +70,12 @@ git pull origin main aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com # Pull images from ECR -docker pull ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-sql-judge:latest -docker pull ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-ctfd:latest +docker pull ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${SQL_JUDGE_ECR_REPOSITORY_NAME}:latest +docker pull ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${CTFD_ECR_REPOSITORY_NAME}:latest # Tag the pulled images with local names that docker-compose expects -docker tag ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-sql-judge:latest platform-sql-judge:latest -docker tag ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/platform-ctfd:latest platform-ctfd:latest +docker tag ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${SQL_JUDGE_ECR_REPOSITORY_NAME}:latest platform-sql-judge:latest +docker tag ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${CTFD_ECR_REPOSITORY_NAME}:latest platform-ctfd:latest # Run docker-compose -docker compose up -d \ No newline at end of file +docker compose up -d diff --git a/IaC/ec2/var.tf b/IaC/ec2/var.tf index aa4b6f6236..8d4ba01759 100644 --- a/IaC/ec2/var.tf +++ b/IaC/ec2/var.tf @@ -13,4 +13,9 @@ variable "asg_desired_capacity" { type = number } variable "key_name" { type = string } variable "ondemand_instance_type" { type = string } variable "region" { type = string } -variable "aws_account_id" { type = string } \ No newline at end of file +variable "aws_account_id" { type = string } +variable "ctfd_ecr_repository_name" { type = string } +variable "sql_judge_ecr_repository_name" { type = string } +variable "application_log_group_name" { type = string } +variable "behavior_log_group_name" { type = string } +variable "behavior_log_stream_name" { type = string } diff --git a/IaC/lambda/lambda.tf b/IaC/lambda/lambda.tf index c68a46e147..f6a8f15b10 100644 --- a/IaC/lambda/lambda.tf +++ b/IaC/lambda/lambda.tf @@ -39,8 +39,7 @@ resource "aws_iam_role_policy" "lambda_policy" { { Effect = "Allow" Action = [ - "s3:PutObject", - "s3:PutObjectAcl" + "s3:PutObject" ] Resource = "arn:aws:s3:::${var.bucket_name}/*" } @@ -99,4 +98,4 @@ resource "aws_lambda_permission" "allow_eventbridge" { function_name = aws_lambda_function.log_processor.function_name principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.daily_trigger.arn -} \ No newline at end of file +} diff --git a/IaC/locals.tf b/IaC/locals.tf new file mode 100644 index 0000000000..9b002bc496 --- /dev/null +++ b/IaC/locals.tf @@ -0,0 +1,9 @@ +locals { + ctfd_ecr_repository_name = "${var.prefix}-ctfd" + sql_judge_ecr_repository_name = "${var.prefix}-sql-judge" + + application_log_group_name = "/aws/ec2/${var.prefix}" + behavior_log_group_name = "/aws/ec2/${var.prefix}-behavior" + behavior_log_stream_name = "${var.prefix}-sql-challenge" + log_bucket_name = "${var.prefix}-logs-${var.aws_account_id}-${var.region}" +} diff --git a/IaC/logging.tf b/IaC/logging.tf new file mode 100644 index 0000000000..e122f19385 --- /dev/null +++ b/IaC/logging.tf @@ -0,0 +1,39 @@ +resource "aws_cloudwatch_log_group" "application" { + name = local.application_log_group_name +} + +resource "aws_cloudwatch_log_group" "behavior" { + name = local.behavior_log_group_name + retention_in_days = 3 +} + +resource "aws_s3_bucket" "log_archive" { + bucket = local.log_bucket_name +} + +resource "aws_s3_bucket_ownership_controls" "log_archive" { + bucket = aws_s3_bucket.log_archive.id + + rule { + object_ownership = "BucketOwnerEnforced" + } +} + +resource "aws_s3_bucket_public_access_block" "log_archive" { + bucket = aws_s3_bucket.log_archive.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "log_archive" { + bucket = aws_s3_bucket.log_archive.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} diff --git a/IaC/main.tf b/IaC/main.tf index 7c0f02ef92..409af17b47 100644 --- a/IaC/main.tf +++ b/IaC/main.tf @@ -1,6 +1,14 @@ provider "aws" { region = var.region profile = var.aws_profile + + default_tags { + tags = { + Project = "sql-playzone" + Deployment = var.prefix + ManagedBy = "terraform" + } + } } # VPC Module @@ -59,6 +67,8 @@ module "ami" { google_client_id = var.google_client_id google_client_secret = var.google_client_secret aws_account_id = var.aws_account_id + ctfd_ecr_repository_name = local.ctfd_ecr_repository_name + sql_judge_ecr_repository_name = local.sql_judge_ecr_repository_name elasticache_serverless_endpoint = module.elasticache.elasticache_serverless_endpoint[0].address depends_on = [module.vpc, module.rds, module.elasticache] @@ -84,6 +94,11 @@ module "ec2" { ondemand_instance_type = var.ondemand_server_instance_class region = var.region aws_account_id = var.aws_account_id + ctfd_ecr_repository_name = local.ctfd_ecr_repository_name + sql_judge_ecr_repository_name = local.sql_judge_ecr_repository_name + application_log_group_name = aws_cloudwatch_log_group.application.name + behavior_log_group_name = aws_cloudwatch_log_group.behavior.name + behavior_log_stream_name = local.behavior_log_stream_name depends_on = [module.vpc, module.rds] # We do not append ami module but you have to create ami before apply this module # depends_on = [module.vpc, module.rds, module.ami] # When you want to use only `terraform apply` without designating targets, use this. @@ -96,9 +111,9 @@ module "lambda" { prefix = var.prefix region = var.region aws_account_id = var.aws_account_id - bucket_name = var.log_bucket_name - log_group_name = var.behavior_log_group_name - log_stream_name = var.behavior_log_stream_name + bucket_name = aws_s3_bucket.log_archive.bucket + log_group_name = aws_cloudwatch_log_group.behavior.name + log_stream_name = local.behavior_log_stream_name depends_on = [module.vpc] } diff --git a/IaC/var.tf b/IaC/var.tf index e86bb1e678..6d85b00bb8 100644 --- a/IaC/var.tf +++ b/IaC/var.tf @@ -1,7 +1,12 @@ variable "prefix" { - description = "The prefix to use for all resources" + description = "Deployment identifier used to name all deployment-scoped resources" type = string - default = "playzone" + default = "sql-2026-s2" + + validation { + condition = length(var.prefix) <= 28 && can(regex("^[a-z0-9][a-z0-9-]*[a-z0-9]$", var.prefix)) + error_message = "prefix must be at most 28 characters and contain only lowercase letters, numbers, and internal hyphens." + } } variable "region" { @@ -121,21 +126,3 @@ variable "asg_desired_capacity" { type = number default = 1 } - -variable "log_bucket_name" { - description = "S3 Bucket name for storing logs" - type = string - default = "sql-playzone-log" -} - -variable "behavior_log_group_name" { - description = "CloudWatch Log Group name for behavior logs" - type = string - default = "/aws/ec2/sql-playzone-behavior" -} - -variable "behavior_log_stream_name" { - description = "CloudWatch Log Stream name for behavior logs" - type = string - default = "sql-challenge" -}