From 5f1ebfe789e5432426e8939b0b040a4e69377827 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Tue, 22 Jul 2025 15:13:03 +0900 Subject: [PATCH 1/7] =?UTF-8?q?waf=20rule=20=EC=A0=95=EC=9D=98=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/deploy/waf/main.tf | 97 +++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/prod-team-account/deploy/waf/main.tf b/prod-team-account/deploy/waf/main.tf index e6ae276..29f0581 100644 --- a/prod-team-account/deploy/waf/main.tf +++ b/prod-team-account/deploy/waf/main.tf @@ -12,6 +12,51 @@ provider "aws" { region = "ap-northeast-2" } +# rule 목록 정의 +locals { + managed_rules = { + # 규칙이름 = { 우선순위, AWS 관리형 규칙 이름 } + "CommonRuleSet" = { priority = 10, name = "AWSManagedRulesCommonRuleSet" } + "KnownBadInputs" = { priority = 20, name = "AWSManagedRulesKnownBadInputsRuleSet" } + "SQLiRuleSet" = { priority = 30, name = "AWSManagedRulesSQLiRuleSet" } + "AmazonIpReputation" = { priority = 40, name = "AWSManagedRulesAmazonIpReputationList" } + } +} + +# IP 기반 요청 제한 규칙 생성 +resource "aws_wafv2_rule_group" "rate_limit_rule" { + name = "${var.project_name}-rate-limit-rule" + scope = "REGIONAL" + capacity = 50 + rule { + name = "RateLimit5Min2000" + priority = 10 + + action { + block {} + } + + statement { + rate_based_statement { + limit = 2000 + aggregate_key_type = "IP" + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "RateLimitMetric" + sampled_requests_enabled = true + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "${var.project_name}-rate-limit-metric" + sampled_requests_enabled = true + } +} + # WAF resource "aws_wafv2_web_acl" "alb_waf" { name = "${var.project_name}-alb-waf" @@ -22,29 +67,51 @@ resource "aws_wafv2_web_acl" "alb_waf" { allow {} } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "waf-alb-metric" - sampled_requests_enabled = true + dynamic "rule" { + for_each = local.managed_rules + content { + name = "AWS-${rule.value.name}" + priority = rule.value.priority + + override_action { + none {} + } + + statement { + managed_rule_group_statement { + vendor_name = "AWS" + name = rule.value.name + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "waf-${rule.value.name}-metric" + sampled_requests_enabled = true + } + } } - rule { - name = "AWS-AWSManagedRulesCommonRuleSet" - priority = 1 + # 생성된 규칙을 사용하여 요청 제한 규칙 추가 + rule{ + name = "RateLimitRule" + priority = 50 + override_action { none {} } + statement { - managed_rule_group_statement { - vendor_name = "AWS" - name = "AWSManagedRulesCommonRuleSet" + rule_group_reference_statement { + arn = aws_wafv2_rule_group.rate_limit_rule.arn } } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "AWSManagedRulesCommonRuleSet" - sampled_requests_enabled = true - } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "waf-alb-metric" + sampled_requests_enabled = true } tags = { From 6a8a2b6d9d2a8442a68876700060dd09f38887a1 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Thu, 24 Jul 2025 11:21:23 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=A0=84=EC=86=A1=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/deploy/waf/main.tf | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/prod-team-account/deploy/waf/main.tf b/prod-team-account/deploy/waf/main.tf index 29f0581..6d15541 100644 --- a/prod-team-account/deploy/waf/main.tf +++ b/prod-team-account/deploy/waf/main.tf @@ -117,4 +117,11 @@ resource "aws_wafv2_web_acl" "alb_waf" { tags = { Name = "${var.project_name}-alb-waf" } -} \ No newline at end of file +} + +# S3로 로그 전송하도록 설정 +resource "aws_wafv2_web_acl_logging_configuration" "waf_logging" { + resource_arn = aws_wafv2_web_acl.alb_waf.arn + log_destination_configs = ["arn:aws:s3:::whs-aws-logs"] +} + From 7d2547ae753649ab876779225fd91301e9ec1357 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 25 Jul 2025 10:25:30 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=A0=84=EC=86=A1=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/deploy/waf/main.tf | 34 ++++++++++++++++------- prod-team-account/deploy/waf/variables.tf | 6 ++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/prod-team-account/deploy/waf/main.tf b/prod-team-account/deploy/waf/main.tf index 6d15541..fc5afcb 100644 --- a/prod-team-account/deploy/waf/main.tf +++ b/prod-team-account/deploy/waf/main.tf @@ -25,15 +25,15 @@ locals { # IP 기반 요청 제한 규칙 생성 resource "aws_wafv2_rule_group" "rate_limit_rule" { - name = "${var.project_name}-rate-limit-rule" - scope = "REGIONAL" - capacity = 50 + name = "${var.project_name}-rate-limit-rule" + scope = "REGIONAL" + capacity = 50 rule { name = "RateLimit5Min2000" priority = 10 action { - block {} + block {} } statement { @@ -93,19 +93,21 @@ resource "aws_wafv2_web_acl" "alb_waf" { } # 생성된 규칙을 사용하여 요청 제한 규칙 추가 - rule{ + rule { name = "RateLimitRule" priority = 50 - override_action { - none {} - } - statement { rule_group_reference_statement { arn = aws_wafv2_rule_group.rate_limit_rule.arn } } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "RateLimitRuleMetric" + sampled_requests_enabled = true + } } visibility_config { @@ -122,6 +124,18 @@ resource "aws_wafv2_web_acl" "alb_waf" { # S3로 로그 전송하도록 설정 resource "aws_wafv2_web_acl_logging_configuration" "waf_logging" { resource_arn = aws_wafv2_web_acl.alb_waf.arn - log_destination_configs = ["arn:aws:s3:::whs-aws-logs"] + log_destination_configs = ["arn:aws:s3:::${var.bucket_name}"] + + logging_filter { + default_behavior = "DROP" + filter { + behavior = "KEEP" + condition { + action_condition { + action = "BLOCK" + } + } + } + } } diff --git a/prod-team-account/deploy/waf/variables.tf b/prod-team-account/deploy/waf/variables.tf index 7c839dd..eb9fcfc 100644 --- a/prod-team-account/deploy/waf/variables.tf +++ b/prod-team-account/deploy/waf/variables.tf @@ -2,4 +2,10 @@ variable "project_name" { description = "The name of the project" type = string default = "cloudfence" +} + +variable "bucket_name" { + description = "The name of the S3 bucket for WAF logs" + type = string + } \ No newline at end of file From 8e101abeacc34a6085ce7e98daf471b6c6c4b652 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 25 Jul 2025 16:13:04 +0900 Subject: [PATCH 4/7] =?UTF-8?q?requirement=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/deploy/waf/main.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/prod-team-account/deploy/waf/main.tf b/prod-team-account/deploy/waf/main.tf index fc5afcb..a8657ee 100644 --- a/prod-team-account/deploy/waf/main.tf +++ b/prod-team-account/deploy/waf/main.tf @@ -130,6 +130,7 @@ resource "aws_wafv2_web_acl_logging_configuration" "waf_logging" { default_behavior = "DROP" filter { behavior = "KEEP" + requirement = "MEET_ANY" condition { action_condition { action = "BLOCK" From 6ca1cb7cd6e11a0ffe8dff4d8f0358ccd183bac5 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 25 Jul 2025 16:14:28 +0900 Subject: [PATCH 5/7] Format Check --- prod-team-account/deploy/waf/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prod-team-account/deploy/waf/main.tf b/prod-team-account/deploy/waf/main.tf index a8657ee..aa07810 100644 --- a/prod-team-account/deploy/waf/main.tf +++ b/prod-team-account/deploy/waf/main.tf @@ -129,7 +129,7 @@ resource "aws_wafv2_web_acl_logging_configuration" "waf_logging" { logging_filter { default_behavior = "DROP" filter { - behavior = "KEEP" + behavior = "KEEP" requirement = "MEET_ANY" condition { action_condition { From 06311df5bd269bec5605648199225d01291f3798 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 25 Jul 2025 16:16:21 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prod-team-account/deploy/waf/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prod-team-account/deploy/waf/main.tf b/prod-team-account/deploy/waf/main.tf index aa07810..f26a121 100644 --- a/prod-team-account/deploy/waf/main.tf +++ b/prod-team-account/deploy/waf/main.tf @@ -130,7 +130,7 @@ resource "aws_wafv2_web_acl_logging_configuration" "waf_logging" { default_behavior = "DROP" filter { behavior = "KEEP" - requirement = "MEET_ANY" + requirement = "MEETS_ANY" condition { action_condition { action = "BLOCK" From a1efa8431f07d088cab61df1b457230ee70314a1 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Fri, 1 Aug 2025 09:37:12 +0900 Subject: [PATCH 7/7] Update CI --- .github/workflows/ci.yml | 47 +++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ce1f12..291fc6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,29 +157,41 @@ jobs: PLAN_TXT=plan.txt PLAN_JSON=plan.json - # Run terraform plan - terraform plan -no-color -out=$PLAN_FILE > /dev/null 2> plan_error.txt || echo "PLAN_FAILED=true" >> $GITHUB_ENV - - # Show plan text output - terraform show -no-color $PLAN_FILE > $PLAN_TXT 2>/dev/null || echo "Plan failed" > $PLAN_TXT - - # Remove ANSI color codes - cat $PLAN_TXT | \ - sed 's/`/\\`/g' | \ - tr -d '\r' | \ - sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" \ - > cleaned_plan.txt + if terraform plan -no-color -out=$PLAN_FILE > /dev/null 2> plan_error.txt; then + echo "PLAN_FAILED=false" >> $GITHUB_ENV + terraform show -no-color $PLAN_FILE > $PLAN_TXT + terraform show -json $PLAN_FILE > $PLAN_JSON || true + else + echo "PLAN_FAILED=true" >> $GITHUB_ENV + echo "Plan failed" > $PLAN_TXT + echo "{}" > $PLAN_JSON + fi + + # 디버깅용 출력 + echo "::group::Raw terraform show output" + cat $PLAN_TXT || echo "(empty)" + echo "::endgroup::" + + sed 's/`/\\`/g' $PLAN_TXT | tr -d '\r' | sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" > cleaned_plan.txt PLAN_CONTENT=$(cat cleaned_plan.txt) + PLAN_ERROR=$(cat plan_error.txt || echo "No error captured") - # Save JSON plan for infracost - terraform show -json $PLAN_FILE > $PLAN_JSON || true + if [ -z "$PLAN_CONTENT" ]; then + PLAN_CONTENT="(no changes or output empty)" + fi + + if [ -z "$PLAN_ERROR" ]; then + PLAN_ERROR="(no errors)" + fi - # Output plan content for PR comment { echo "PLAN_CONTENT<> $GITHUB_OUTPUT working-directory: ${{ matrix.dir }} @@ -203,6 +215,11 @@ jobs: ${{ steps.plan.outputs.PLAN_CONTENT }} ``` + ### Plan Error (if any) + ``` + ${{ steps.plan.outputs.PLAN_ERROR }} + ``` + - name: Setup Infracost uses: infracost/actions/setup@v2