From f23e9a07164aabbf9a203257d60f232814742b40 Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Tue, 29 Jul 2025 11:23:24 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Inspector=20=EC=8A=A4=EC=BA=94=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=9C=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organizations/backend.tf | 9 ++++ .../organizations/main.tf | 26 +++++++++ .../eventbridge/backend.tf | 9 ++++ .../runtime-verification/eventbridge/main.tf | 51 ++++++++++++++++++ .../runtime-verification/iam/backend.tf | 9 ++++ .../runtime-verification/iam/main.tf | 33 ++++++++++++ .../runtime-verification/iam/outputs.tf | 4 ++ .../runtime-verification/iam/variables.tf | 5 ++ .../runtime-verification/inspector/backend.tf | 9 ++++ .../runtime-verification/inspector/main.tf | 33 ++++++++++++ .../runtime-verification/lambda/backend.tf | 9 ++++ .../lambda/lambda_function.py | 44 +++++++++++++++ .../lambda/lambda_function_payload.zip | Bin 0 -> 1098 bytes .../runtime-verification/lambda/main.tf | 39 ++++++++++++++ .../runtime-verification/lambda/outputs.tf | 9 ++++ .../runtime-verification/lambda/variables.tf | 5 ++ 16 files changed, 294 insertions(+) create mode 100644 management-team-account/inspector-delegation/organizations/backend.tf create mode 100644 management-team-account/inspector-delegation/organizations/main.tf create mode 100644 operation-team-account/runtime-verification/eventbridge/backend.tf create mode 100644 operation-team-account/runtime-verification/eventbridge/main.tf create mode 100644 operation-team-account/runtime-verification/iam/backend.tf create mode 100644 operation-team-account/runtime-verification/iam/main.tf create mode 100644 operation-team-account/runtime-verification/iam/outputs.tf create mode 100644 operation-team-account/runtime-verification/iam/variables.tf create mode 100644 operation-team-account/runtime-verification/inspector/backend.tf create mode 100644 operation-team-account/runtime-verification/inspector/main.tf create mode 100644 operation-team-account/runtime-verification/lambda/backend.tf create mode 100644 operation-team-account/runtime-verification/lambda/lambda_function.py create mode 100644 operation-team-account/runtime-verification/lambda/lambda_function_payload.zip create mode 100644 operation-team-account/runtime-verification/lambda/main.tf create mode 100644 operation-team-account/runtime-verification/lambda/outputs.tf create mode 100644 operation-team-account/runtime-verification/lambda/variables.tf diff --git a/management-team-account/inspector-delegation/organizations/backend.tf b/management-team-account/inspector-delegation/organizations/backend.tf new file mode 100644 index 00000000..62f53b08 --- /dev/null +++ b/management-team-account/inspector-delegation/organizations/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-management-state" + key = "inspector-delegation/organizations.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-management-lock" + } +} \ No newline at end of file diff --git a/management-team-account/inspector-delegation/organizations/main.tf b/management-team-account/inspector-delegation/organizations/main.tf new file mode 100644 index 00000000..3f02a968 --- /dev/null +++ b/management-team-account/inspector-delegation/organizations/main.tf @@ -0,0 +1,26 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +provider "aws" { + alias = "operation" + region = "ap-northeast-2" +} + +data "aws_caller_identity" "operation" { + provider = aws.operation +} + +resource "aws_inspector2_delegated_admin_account" "this" { + account_id = data.aws_caller_identity.operation.account_id +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/eventbridge/backend.tf b/operation-team-account/runtime-verification/eventbridge/backend.tf new file mode 100644 index 00000000..596aa632 --- /dev/null +++ b/operation-team-account/runtime-verification/eventbridge/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "runtime-verification/eventbridge.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/eventbridge/main.tf b/operation-team-account/runtime-verification/eventbridge/main.tf new file mode 100644 index 00000000..3589cbf6 --- /dev/null +++ b/operation-team-account/runtime-verification/eventbridge/main.tf @@ -0,0 +1,51 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +data "terraform_remote_state" "lambda" { + backend = "s3" + config = { + bucket = "cloudfence-operation-state" + key = "runtime-verification/lambda.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + } +} + +resource "aws_cloudwatch_event_rule" "inspector_event_rule" { + name = "inspector-event-rule" + description = "Event rule for AWS Inspector findings" + + event_pattern = jsonencode({ + source = ["aws.inspector2"], + detail-type = ["Inspector2 Finding"], + detail = { + finding = { + severity = ["HIGH", "CRITICAL"], + } + } + }) +} + +resource "aws_cloudwatch_event_target" "inspector_event_target" { + rule = aws_cloudwatch_event_rule.inspector_event_rule.name + arn = data.terraform_remote_state.lambda.outputs.lambda_function_arn +} + +resource "aws_lambda_permission" "inspector_event_permission" { + statement_id = "AllowExecutionFromEventBridge" + action = "lambda:InvokeFunction" + function_name = data.terraform_remote_state.lambda.outputs.lambda_function_name + principal = "events.amazonaws.com" + source_arn = aws_cloudwatch_event_rule.inspector_event_rule.arn +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/iam/backend.tf b/operation-team-account/runtime-verification/iam/backend.tf new file mode 100644 index 00000000..8b820ee8 --- /dev/null +++ b/operation-team-account/runtime-verification/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "runtime-verification/iam.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/iam/main.tf b/operation-team-account/runtime-verification/iam/main.tf new file mode 100644 index 00000000..d6431474 --- /dev/null +++ b/operation-team-account/runtime-verification/iam/main.tf @@ -0,0 +1,33 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +resource "aws_iam_role" "lambda_exec_role" { + name = "${var.project_name}-inspector-lambda-exec-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "lambda.amazonaws.com" + } + }] + }) +} + +resource "aws_iam_role_policy_attachment" "lambda_logs" { + role = aws_iam_role.lambda_exec_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/iam/outputs.tf b/operation-team-account/runtime-verification/iam/outputs.tf new file mode 100644 index 00000000..a662f1bb --- /dev/null +++ b/operation-team-account/runtime-verification/iam/outputs.tf @@ -0,0 +1,4 @@ +output "lambda_exec_role_arn" { + description = "The ARN of the IAM role for the Lambda function" + value = aws_iam_role.lambda_exec_role.arn +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/iam/variables.tf b/operation-team-account/runtime-verification/iam/variables.tf new file mode 100644 index 00000000..7c839dd8 --- /dev/null +++ b/operation-team-account/runtime-verification/iam/variables.tf @@ -0,0 +1,5 @@ +variable "project_name" { + description = "The name of the project" + type = string + default = "cloudfence" +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/inspector/backend.tf b/operation-team-account/runtime-verification/inspector/backend.tf new file mode 100644 index 00000000..cb20378c --- /dev/null +++ b/operation-team-account/runtime-verification/inspector/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "runtime-verification/inspector.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/inspector/main.tf b/operation-team-account/runtime-verification/inspector/main.tf new file mode 100644 index 00000000..cb6f1ffa --- /dev/null +++ b/operation-team-account/runtime-verification/inspector/main.tf @@ -0,0 +1,33 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +provider "aws" { + alias = "prod" + region = "ap-northeast-2" +} + +data "aws_caller_identity" "prod" { + provider = aws.prod +} + +data "aws_caller_identity" "current" {} + +resource "aws_inspector2_enabler" "this" { + account_ids = [data.aws_caller_identity.current.account_id] + resource_types = ["EC2"] +} + +resource "aws_inspector2_delegated_admin_account" "prod_account" { + account_id = data.aws_caller_identity.prod.account_id +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/lambda/backend.tf b/operation-team-account/runtime-verification/lambda/backend.tf new file mode 100644 index 00000000..59c89a02 --- /dev/null +++ b/operation-team-account/runtime-verification/lambda/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "runtime-verification/lambda.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + encrypt = true + } +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/lambda/lambda_function.py b/operation-team-account/runtime-verification/lambda/lambda_function.py new file mode 100644 index 00000000..add59de3 --- /dev/null +++ b/operation-team-account/runtime-verification/lambda/lambda_function.py @@ -0,0 +1,44 @@ +import json +import os +import urllib.request + +SLACK_WEBHOOK_URL = os.environ.get('SLACK_WEBHOOK_URL', '') + +def lambda_handler(event, context): + print(json.dumps(event)) # 디버깅을 위해 전체 이벤트를 로그로 남깁니다. + + # Inspector 이벤트에서 필요한 정보 추출 + finding = event.get('detail', {}) + title = finding.get('title', 'N/A') + severity = finding.get('severity', 'N/A') + + resources = finding.get('resources', [{}]) + resource_id = resources[0].get('id', 'N/A') + + region = event.get('region', 'N/A') + finding_arn = finding.get('findingArn', '') + console_url = f"https://{region}.console.aws.amazon.com/inspector/v2/findings/details?finding-arn={finding_arn}" if finding_arn else "#" + + # 슬랙 메시지 구성 + slack_message = { + "text": f"*New High-Severity Inspector Finding*", + "blocks": [ + { "type": "header", "text": { "type": "plain_text", "text": f"{severity}: {title}" } }, + { "type": "section", "text": { "type": "mrkdwn", "text": f"*Affected Resource:*\n```{resource_id}```" } }, + { "type": "actions", "elements": [{ "type": "button", "text": { "type": "plain_text", "text": "View Finding Details" }, "url": console_url, "style": "primary" }] } + ] + } + + if not SLACK_WEBHOOK_URL: + print("Slack Webhook URL is not set.") + return {'statusCode': 500} + + req = urllib.request.Request(SLACK_WEBHOOK_URL, data=json.dumps(slack_message).encode('utf-8'), headers={'Content-Type': 'application/json'}) + + try: + with urllib.request.urlopen(req) as response: + print(f"Message posted to Slack, status: {response.status}") + except urllib.error.HTTPError as e: + print(f"Error posting to Slack: {e.code} {e.reason}") + + return {'statusCode': 200} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/lambda/lambda_function_payload.zip b/operation-team-account/runtime-verification/lambda/lambda_function_payload.zip new file mode 100644 index 0000000000000000000000000000000000000000..76a732264de7ffb9589ee5d1618d1a98b501b749 GIT binary patch literal 1098 zcmWIWW@Zs#U|`^2m=yIl>NMN#@(Iig3_9!#3_=Vt3^|FpNhyi(X{C9|C7JnodIgoC zA)E}%pY=O3z5sD)1vdjD%U4DQ2Cx~mq4%@zxCzwl-=8#5zQkzLITo&`2Skqh7JANN zxt%K~Q+&_*?A_e8w?oUmzlmvGpc}67(Od4^ls4~7#ihDWmgdb;yRiSnr2p;bvP$i9 z!})hzeKC7xewy9cjd40l)Z(=cYM+_cI&01^qpk?!2_Kg5dxdN5{%*GYotfO-oqPX2 zIC%HibL;OX*cLHAIPSwGSK<(US6kw``nuPlT8djdU8>JcFxxeyUGRjK4D)rnOwM%= zSAKgX+7zxMc|FuwgX8z!h{!3=CyGw$b-VfV!=u0DKTj*3Jbw7}W^2@TMEw0?sB&aQ<^Tjx|hwYNFn zo?KLEb|$_@@BA@|Z#uuZ5ATzGnt!}Ce)h)+ANi)m2yD9Awm`Fc!x5FQTvO7ln2t=z z=+^34#{WHg_PV2*JW4a=bez?{xt0`8Uf>eH%|Z9KPyB+*b(7>OxIeIQUrg+}8E?NK zPG^sqdpqa8h{aoVn@<1X)%s~P`%_}@r=m}6{mNlVO>A1H9?V^LZR4uzS)IJ|*i;o3 zsB0~-{Uz$RU$x5AOfvNCkFd2pYqGYzezo}0hBh6uNtRwKog=Cnm0h?xHb3%FnP6W% z_wN_wlm8^1E`R)I`}&hd@2~&-aDJJIV6Lob)dG>XGXm9Gug3IETp8^CZ}sv<_hnY8rjkFI({`2@x=7}Wl-2jS z8U=cHy>Lp2sPqc@zf6XUGimpF|4WnIA9BWfY}j=(Z!vdE%JM7zwOuM%hi+dt^ zj^6{@J6D@rt=+2XRPg#q^VhFig6b;#1G2ig&2Mbne^uM0MkvVF{Moc8O&PW^Cw0#R zl>Xke_uInPKaZ^P&Q*Cg?dGlqk;d6!{HBZ!49ud_n(I$Ayt@MdKL>0kuH2q1k2SYj|R001Qi@sa=l literal 0 HcmV?d00001 diff --git a/operation-team-account/runtime-verification/lambda/main.tf b/operation-team-account/runtime-verification/lambda/main.tf new file mode 100644 index 00000000..dc6fefc9 --- /dev/null +++ b/operation-team-account/runtime-verification/lambda/main.tf @@ -0,0 +1,39 @@ + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + +} + +provider "aws" { + region = "ap-northeast-2" +} + +data "terraform_remote_state" "iam" { + backend = "s3" + config = { + bucket = "cloudfence-operation-state" + key = "runtime-verification/iam.tfstate" + region = "ap-northeast-2" + dynamodb_table = "s3-operation-lock" + } +} + +resource "aws_lambda_function" "inspector_slack_notification" { + function_name = "inspector_slack_notification" + role = data.terraform_remote_state.iam.outputs.lambda_exec_role_arn + handler = "index.lambda_handler" + runtime = "python3.9" + + environment { + variables = { + SLACK_WEBHOOK_URL = var.slack_webhook_url + } + } + filename = "lambda_function_payload.zip" + source_code_hash = filebase64sha256("lambda_function_payload.zip") +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/lambda/outputs.tf b/operation-team-account/runtime-verification/lambda/outputs.tf new file mode 100644 index 00000000..b6b761dd --- /dev/null +++ b/operation-team-account/runtime-verification/lambda/outputs.tf @@ -0,0 +1,9 @@ +output "lambda_function_arn" { + description = "The ARN of the Inspector Slack notification Lambda function" + value = aws_lambda_function.inspector_slack_notification.arn +} + +output "lambda_function_name" { + description = "The name of the Inspector Slack notification Lambda function" + value = aws_lambda_function.inspector_slack_notification.function_name +} \ No newline at end of file diff --git a/operation-team-account/runtime-verification/lambda/variables.tf b/operation-team-account/runtime-verification/lambda/variables.tf new file mode 100644 index 00000000..5ecd0a22 --- /dev/null +++ b/operation-team-account/runtime-verification/lambda/variables.tf @@ -0,0 +1,5 @@ +variable "slack_webhook_url" { + description = "The incoming webhook URL for Slack notifications" + type = string + sensitive = true +} \ No newline at end of file From bb33fe74872cb076af52fa03b133bcae9f2a725b Mon Sep 17 00:00:00 2001 From: Choi Yunho Date: Tue, 29 Jul 2025 13:20:07 +0900 Subject: [PATCH 2/2] =?UTF-8?q?tfsec=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- operation-team-account/runtime-verification/lambda/main.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operation-team-account/runtime-verification/lambda/main.tf b/operation-team-account/runtime-verification/lambda/main.tf index dc6fefc9..371c3bec 100644 --- a/operation-team-account/runtime-verification/lambda/main.tf +++ b/operation-team-account/runtime-verification/lambda/main.tf @@ -1,4 +1,3 @@ - terraform { required_providers { aws = { @@ -23,6 +22,8 @@ data "terraform_remote_state" "iam" { } } +# 이 Lambda 함수는 단순 알림 기능만 수행하므로 X-Ray 옵션을 제외 +#tfsec:ignore:aws-lambda-enable-tracing resource "aws_lambda_function" "inspector_slack_notification" { function_name = "inspector_slack_notification" role = data.terraform_remote_state.iam.outputs.lambda_exec_role_arn