diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e6020ef..83e1ceb 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,24 +2,24 @@ name: Terraform Apply on: push: - branches: [main] + branches: [main] # main 브랜치에 push될 때 실행 permissions: - contents: read - id-token: write + contents: read # 코드 리포지토리 읽기 권한 + id-token: write # OIDC 인증을 위한 ID 토큰 발급 권한 jobs: detect-changes: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.set.outputs.matrix }} + matrix: ${{ steps.set.outputs.matrix }} # 다음 job에 전달할 matrix 출력 steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v3 # 현재 리포지토리 코드 체크아웃 - name: Filter Paths id: filter - uses: dorny/paths-filter@v3 + uses: dorny/paths-filter@v3 # 어떤 디렉토리에 변경이 있는지 필터링 with: filters: | operation: @@ -40,6 +40,7 @@ jobs: - name: Build Matrix from Filter (with subdirs) id: set env: + # 필터링된 결과를 환경변수로 받아옴 FILTER_OUTPUTS_operation: ${{ steps.filter.outputs.operation }} FILTER_OUTPUTS_identity: ${{ steps.filter.outputs.identity }} FILTER_OUTPUTS_prod: ${{ steps.filter.outputs.prod }} @@ -48,6 +49,7 @@ jobs: FILTER_OUTPUTS_stage: ${{ steps.filter.outputs.stage }} FILTER_OUTPUTS_management: ${{ steps.filter.outputs.management }} run: | + # 계정 별 IAM Role Key 매핑 declare -A ROLE_MAP=( ["operation"]="ROLE_ARN_OPERATION" ["identity"]="ROLE_ARN_IDENTITY" @@ -60,6 +62,7 @@ jobs: MATRIX_ITEMS=() + # 변경된 경로에 따라 matrix 구성 for KEY in "${!ROLE_MAP[@]}"; do VAR_NAME="FILTER_OUTPUTS_${KEY}" VALUE="${!VAR_NAME}" @@ -85,6 +88,7 @@ jobs: fi done + # 최종 matrix JSON 출력 if [ ${#MATRIX_ITEMS[@]} -eq 0 ]; then echo "matrix=[]" >> $GITHUB_OUTPUT else @@ -93,14 +97,14 @@ jobs: fi terraform-apply: - needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} + needs: detect-changes # detect-changes job 이후 실행 + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경사항이 있을 경우에만 실행 runs-on: ubuntu-latest strategy: - matrix: + matrix: # matrix 기반 반복 실행 include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} - fail-fast: false + fail-fast: false # 하나 실패해도 나머지 job은 계속 진행 steps: - name: Checkout repository @@ -110,17 +114,17 @@ jobs: uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 - role-to-assume: ${{ secrets[matrix.role_key] }} + role-to-assume: ${{ secrets[matrix.role_key] }} # OIDC 기반으로 계정별 IAM Role Assume - name: Setup Terraform uses: hashicorp/setup-terraform@v1 with: - terraform_version: 1.4.0 + terraform_version: 1.4.0 # Terraform 버전 명시 - name: Terraform Init - run: terraform init - working-directory: ${{ matrix.dir }} + run: terraform init # Terraform 초기화: 백엔드 설정 및 provider 다운로드 + working-directory: ${{ matrix.dir }} # matrix로 전달된 디렉토리에서 실행 - name: Terraform Apply - run: terraform apply -auto-approve - working-directory: ${{ matrix.dir }} + run: terraform apply -auto-approve # 사용자 승인 없이 자동 적용 + working-directory: ${{ matrix.dir }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a931aa5..4c1e1d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,8 @@ name: Organization CI on: pull_request: - branches: [main] - paths: + branches: [main] # main 브랜치로 PR이 열릴 때 실행 + paths: # 아래 디렉토리 중 하나라도 변경되었을 경우에만 실행 - "operation-team-account/**" - "identity-team-account/**" - "prod-team-account/**" @@ -14,11 +14,11 @@ on: permissions: contents: read - pull-requests: write - id-token: write + pull-requests: write #PR comment 작성 + id-token: write # OIDC 인증용 jobs: - detect-changes: + detect-changes: # 다음 Job에서 사용할 matrix 값 runs-on: ubuntu-latest outputs: matrix: ${{ steps.set.outputs.matrix }} @@ -26,19 +26,21 @@ jobs: - name: Checkout Code uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 0 # 전체 커밋 내역 필요 (diff 비교용) - - name: Fetch origin/main + - name: Fetch origin/main # main 브랜치 가져옴 (diff 비교 대상) run: git fetch origin main - name: Detect Changed Directories & Build Matrix id: set run: | - FILES=$(git diff --name-only origin/main...${{ github.sha }}) + # 변경된 파일 목록을 가져옴 + FILES=$(git diff --name-only origin/main...${{ github.sha }}) echo "Changed files:" echo "$FILES" - declare -A ROLE_MAP=( + # 디렉토리명 → IAM Role 키 매핑 + declare -A ROLE_MAP=( ["operation-team-account"]="ROLE_ARN_OPERATION" ["identity-team-account"]="ROLE_ARN_IDENTITY" ["prod-team-account"]="ROLE_ARN_PROD" @@ -50,20 +52,19 @@ jobs: TMP_FILE=$(mktemp) + # 변경된 디렉토리들 중 .tf 파일이 있는 디렉토리만 필터링 for FILE in $FILES; do DIR=$(dirname "$FILE") TOP_DIR=$(echo $DIR | cut -d/ -f1) ROLE_KEY="${ROLE_MAP[$TOP_DIR]}" if [ -n "$ROLE_KEY" ]; then - # 루트 디렉터리 if [ "$DIR" == "$TOP_DIR" ]; then TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE fi else - # 하위 디렉터리 TF_COUNT=$(find "$DIR" -maxdepth 1 -name '*.tf' | wc -l) if [ "$TF_COUNT" -gt 0 ]; then echo "$DIR|$ROLE_KEY" >> $TMP_FILE @@ -72,11 +73,9 @@ jobs: fi done - # 중복 제거 + # 중복 제거 후 JSON 형식의 실행 matrix 생성 UNIQUE_LINES=$(sort $TMP_FILE | uniq) - - MATRIX_JSON="[" - FIRST=1 + MATRIX_JSON="["; FIRST=1 while IFS= read -r LINE; do DIR=$(echo $LINE | cut -d"|" -f1) @@ -100,13 +99,13 @@ jobs: terraform-ci: needs: detect-changes - if: ${{ needs.detect-changes.outputs.matrix != '[]' }} + if: ${{ needs.detect-changes.outputs.matrix != '[]' }} # 변경된 디렉토리가 있을 때만 실행 runs-on: ubuntu-latest strategy: matrix: - include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} - fail-fast: false + include: ${{ fromJson(needs.detect-changes.outputs.matrix) }} # 변경된 디렉토리별 반복 실행 + fail-fast: false # 하나 실패해도 나머지 job 실행 env: INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }} @@ -116,72 +115,189 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Configure AWS Credentials + - name: Configure AWS Credentials # GitHub OIDC로 AWS 로그인 uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ap-northeast-2 role-to-assume: ${{ secrets[matrix.role_key] }} + - name: Install Terraform + run: | + # Terraform CLI 설치 + curl -LO https://releases.hashicorp.com/terraform/1.4.0/terraform_1.4.0_linux_amd64.zip + unzip terraform_1.4.0_linux_amd64.zip + sudo mv terraform /usr/local/bin/ + - name: Install tfsec run: | + # 보안 스캐너 설치 curl -sSL https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash - - name: Run tfsec (fail on HIGH+) - run: tfsec --minimum-severity HIGH --no-color ${{ matrix.dir }} + - name: Run tfsec (fail on HIGH+) # HIGH 이상이면 실패 처리 + working-directory: ${{ matrix.dir }} + run: tfsec --minimum-severity HIGH --no-color . - - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 - with: - terraform_version: 1.4.0 + - name: Run tfsec (all severities) and save JSON # tfsec 다시 실행 후 전체 결과 JSON으로 저장 + run: | + tfsec --format json --out tfsec_results.json . + working-directory: ${{ matrix.dir }} + continue-on-error: true - - name: Terraform Init - run: terraform init + - name: Notify Slack for LOW/MEDIUM tfsec findings # LOW, Medium으로 감지된 내용을 slack 전송하고 알림 + if: always() + working-directory: ${{ matrix.dir }} + run: | + # 개수로 감지 + LOW_MEDIUM_COUNT=$(jq '[.results[] | select(.severity=="LOW" or .severity=="MEDIUM")] | length' tfsec_results.json) + + if [ "$LOW_MEDIUM_COUNT" -gt 0 ]; then + echo "Sending Slack alert for LOW/MEDIUM findings..." + + MESSAGE=$(jq -r ' + [.results[] + | select(.severity=="LOW" or .severity=="MEDIUM") + | "- *\(.severity)* [\(.rule_id)]: \(.description)" + ] | join("\n") + ' tfsec_results.json) + + curl -X POST -H 'Content-type: application/json' \ + --data "$(jq -n --arg text "* tfsec LOW/MEDIUM Findings in `${{ matrix.dir }}`*\n\n$MESSAGE" '{ text: $text }')" \ + ${{ secrets.SLACK_WEBHOOK_URL }} + else + echo "No LOW or MEDIUM findings to notify." + fi + + - name: Terraform Init # provider, backend 초기화 + run: terraform init -input=false working-directory: ${{ matrix.dir }} - - name: Terraform Format Check + - name: Terraform Format Check # 포맷 체크 (코드 스타일) run: terraform fmt -check -recursive working-directory: ${{ matrix.dir }} - - name: Terraform Validate + - name: Terraform Validate # 문법 검사 run: terraform validate working-directory: ${{ matrix.dir }} - - name: Terraform Plan + - name: Terraform Plan # 실제 배포 전 변경사항 확인 id: plan run: | - terraform plan -no-color -out=tfplan.binary - terraform show -no-color tfplan.binary > plan.txt - terraform show -json tfplan.binary > plan.json - echo 'PLAN<> $GITHUB_OUTPUT - cat plan.txt >> $GITHUB_OUTPUT - echo 'EOF' >> $GITHUB_OUTPUT + START_TIME=$(date -u +"%Y-%m-%d %H:%M:%S UTC") + echo "START_TIME=$START_TIME" >> $GITHUB_ENV + + PLAN_FILE=tfplan.binary + PLAN_TXT=plan.txt + PLAN_JSON=plan.json + + 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 + + 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") + + { + echo "PLAN_CONTENT<> $GITHUB_OUTPUT working-directory: ${{ matrix.dir }} - - name: Comment Terraform Plan on PR + - name: Comment Terraform Plan on PR # Plan 결과를 PR에 댓글로 작성 if: github.event.pull_request.number != '' uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.pull_request.number }} body: | - ### Terraform Plan Output for `${{ matrix.dir }}` + ## [Terraform Plan Summary] + | 항목 | 값 | + |-----------------|-----| + | **Status** | `${{ steps.plan.outcome }}` | + | **Directory** | `${{ matrix.dir }}` | + | **Executed At** | `${{ env.START_TIME }}` | + + --- + + ### Plan Output ```hcl - ${{ steps.plan.outputs.PLAN }} + ${{ steps.plan.outputs.PLAN_CONTENT }} ``` - - name: Setup Infracost - uses: infracost/actions/setup@v2 + ### Plan Error (if any) + ``` + ${{ steps.plan.outputs.PLAN_ERROR }} + ``` - - name: Infracost Breakdown + - name: Setup Infracost # 비용 분석 도구 설정 + uses: infracost/actions/setup@v3 # v2 에서 v3로 버전 변경(최신 기능 및 버스 픽스) + with: + api-key: ${{ secrets.INFRACOST_API_KEY }} + + - name: Generate Infracost Baseline # main 브랜치로 체크아웃 후 기존 비용을 저장한 파일(스냅샷) 생성 + working-directory: ${{ matrix.dir }} run: | + git fetch origin ${{ github.base_ref }} + git checkout origin/${{ github.base_ref }} -- . + terraform init -input=false infracost breakdown \ - --path=plan.json \ + --path=. \ --format=json \ - --out-file=infracost.json + --out-file infracost-baseline.json + + - name: Generate Infracost Diff # PR 브랜치로 돌아와, 비용 차이를 계산하여 저장 working-directory: ${{ matrix.dir }} + run: | + git checkout ${{ github.head_ref }} - - name: Infracost Comment on Pull Request - uses: infracost/actions/comment@v1 - with: - path: ${{ matrix.dir }}/infracost.json - behavior: update + infracost diff \ + --path=. \ + --compare-to infracost-baseline.json \ + --format=json \ + --out-file infracost-diff.json + + - name: Post Infracost Comment to PR + if: github.event_name == 'pull_request' + working-directory: ${{ matrix.dir }} + run: | + # diff JSON에 projects 배열이 비어있는지 확인 + if jq '.projects | length' infracost-diff.json | grep -q '^0$'; then + # 변경사항 없음 메시지 (로그용) + echo "✅ No infrastructure cost changes detected." + echo "Generating and posting full cost breakdown for current infrastructure." + + terraform init -input=false + # 전체 비용 스냅샷 생성 + infracost breakdown \ + --path=. \ + --format=json \ + --out-file infracost-full.json + + # 전체 비용 분석 댓글 + infracost comment github \ + --path=infracost-full.json \ + --repo=${{ github.repository }} \ + --pull-request=${{ github.event.pull_request.number }} \ + --github-token=${{ secrets.GITHUB_TOKEN }} \ + --behavior=update + else + # 변경사항이 있을 때는 diff 댓글 + infracost comment github \ + --path=infracost-diff.json \ + --repo=${{ github.repository }} \ + --pull-request=${{ github.event.pull_request.number }} \ + --github-token=${{ secrets.GITHUB_TOKEN }} \ + --behavior=update + fi diff --git a/dev-team-account/OIDC/iam/backend.tf b/dev-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..d0d6476 --- /dev/null +++ b/dev-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-dev-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-dev-lock" + } +} \ No newline at end of file diff --git a/dev-team-account/OIDC/iam/main.tf b/dev-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..4910e67 --- /dev/null +++ b/dev-team-account/OIDC/iam/main.tf @@ -0,0 +1,44 @@ +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 +# +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "application-deployment-role1" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*", + "repo:WHS-DevSecOps-infra/Application-Development:*"] + + thumbprint_list = [ + "d89e3bd43d5d909b47a18977aa9d5ce36cee184c" + ] + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "dev-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "rds:*", + "s3:*", + "ec2:*", + "kms:*", + "dynamodb:*", + "iam:*" + ], + "Resource" : "*" + } + ] + }) +} diff --git a/dev-team-account/state/S3/main.tf b/dev-team-account/state/S3/main.tf index 18e9d4d..4c83a34 100644 --- a/dev-team-account/state/S3/main.tf +++ b/dev-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-dev-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-dev-state" @@ -51,55 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } -data "aws_caller_identity" "current" {} - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-dev-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -107,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } } diff --git a/identity-team-account/OIDC/iam/backend.tf b/identity-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..7f4b07a --- /dev/null +++ b/identity-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-identity-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-identity-lock" + } +} \ No newline at end of file diff --git a/identity-team-account/OIDC/iam/main.tf b/identity-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..73b8185 --- /dev/null +++ b/identity-team-account/OIDC/iam/main.tf @@ -0,0 +1,97 @@ +# identity-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "Organization-role" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*", + "repo:WHS-DevSecOps-infra/Monitoring:*", + "repo:WHS-DevSecOps-infra/Application-Development:*" + ] + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [ + + ] + thumbprint_list = [ + "d89e3bd43d5d909b47a18977aa9d5ce36cee184c" + ] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "org-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "SSOAccess", + "Effect" : "Allow", + "Action" : [ + "sso:*", + "sso:CreateAccountAssignment" + ], + "Resource" : "*" + }, + { + "Sid" : "S3Access", + "Effect" : "Allow", + "Action" : [ + "s3:*" + ], + "Resource" : [ + "*" + ] + }, + { + "Sid" : "KMSAccess", + "Effect" : "Allow", + "Action" : [ + "kms:*" + ], + "Resource" : "*" + }, + { + "Sid" : "DynamoDBAccess", + "Effect" : "Allow", + "Action" : [ + "dynamodb:*" + ], + "Resource" : "*" + }, + { + "Sid" : "Statement1", + "Effect" : "Allow", + "Action" : [ + "organizations:*" + ], + "Resource" : "*" + }, + { + "Sid" : "Statement2", + "Effect" : "Allow", + "Action" : [ + "identitystore:*" + ], + "Resource" : "*" + }, + { + "Sid" : "Statement3", + "Effect" : "Allow", + "Action" : [ + "iam:*" + ], + "Resource" : [ + "*" + ] + } + ] + }) +} diff --git a/identity-team-account/state/S3/main.tf b/identity-team-account/state/S3/main.tf index bca05d8..990db35 100644 --- a/identity-team-account/state/S3/main.tf +++ b/identity-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-identity-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-identity-state" @@ -51,55 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } -data "aws_caller_identity" "current" {} - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-identity-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -107,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } } diff --git a/management-team-account/OIDC/iam/backend.tf b/management-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..511e47a --- /dev/null +++ b/management-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-management-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-management-lock" + } +} \ No newline at end of file diff --git a/management-team-account/OIDC/iam/main.tf b/management-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..03cbf02 --- /dev/null +++ b/management-team-account/OIDC/iam/main.tf @@ -0,0 +1,42 @@ +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "management-role" + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Monitoring:*", + "repo:WHS-DevSecOps-infra/Application-Development:*"] + + + thumbprint_list = ["d89e3bd43d5d909b47a18977aa9d5ce36cee184c"] + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "management" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "s3:*", + "dynamoDB:*", + "kms:*", + "iam:*", + "cloudtrail:*" + ], + "Resource" : "*" + } + ] + }) +} diff --git a/management-team-account/state/S3/main.tf b/management-team-account/state/S3/main.tf index b2e4986..e41e553 100644 --- a/management-team-account/state/S3/main.tf +++ b/management-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-management-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-management-state" @@ -51,57 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } - -data "aws_caller_identity" "current" {} - - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-management-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -109,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } } diff --git a/modules/S3_kms/main.tf b/modules/S3_kms/main.tf new file mode 100644 index 0000000..2f9ddf5 --- /dev/null +++ b/modules/S3_kms/main.tf @@ -0,0 +1,42 @@ +data "aws_caller_identity" "current" {} + +resource "aws_kms_key" "this" { + description = var.description + enable_key_rotation = true + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Sid = "AllowRootAccountFullAccess", + Effect = "Allow", + Principal = { + AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + }, + Action = "kms:*", + Resource = "*" + }, + { + Sid = "AllowS3ServicePrincipal" + Effect = "Allow" + Principal = { + Service = "s3.amazonaws.com" + } + Action = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ] + Resource = "*" + Condition = { + StringEquals = { + "aws:SourceArn" = var.s3_bucket_arn, + "aws:SourceAccount" = data.aws_caller_identity.current.account_id + } + } + } + ] + }) +} \ No newline at end of file diff --git a/modules/S3_kms/outputs.tf b/modules/S3_kms/outputs.tf new file mode 100644 index 0000000..1411c3c --- /dev/null +++ b/modules/S3_kms/outputs.tf @@ -0,0 +1,4 @@ +output "kms_key_arn" { + description = "The ARN of the KMS key used for S3 encryption" + value = aws_kms_key.this.arn +} \ No newline at end of file diff --git a/modules/S3_kms/variables.tf b/modules/S3_kms/variables.tf new file mode 100644 index 0000000..b7db9cd --- /dev/null +++ b/modules/S3_kms/variables.tf @@ -0,0 +1,9 @@ +variable "description" { + type = string + default = "KMS key for S3 encryption" +} + +variable "s3_bucket_arn" { + type = string + description = "ARN of the S3 bucket that will use this KMS key" +} \ No newline at end of file diff --git a/modules/iam_OIDC/main.tf b/modules/iam_OIDC/main.tf new file mode 100644 index 0000000..1e5482f --- /dev/null +++ b/modules/iam_OIDC/main.tf @@ -0,0 +1,52 @@ + +provider "aws" { + region = "ap-northeast-2" + +} +resource "aws_iam_openid_connect_provider" "github" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ + "sts.amazonaws.com" # OIDC에서 사용할 클라이언트 ID + ] + + thumbprint_list = var.thumbprint_list +} + +resource "aws_iam_role" "oidc_role" { + name = var.role_name + description = "cicd" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = concat( + [ + { + Effect = "Allow", + Principal = { + Federated = aws_iam_openid_connect_provider.github.arn + }, + Action = "sts:AssumeRoleWithWebIdentity", + Condition = { + StringEquals = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + }, + StringLike = { + "token.actions.githubusercontent.com:sub" = var.sub_condition + } + } + } + ], + var.add_root_trust ? [ + { + Effect = "Allow", + Principal = { + AWS = "arn:aws:iam::${var.account_id}:root" + }, + Action = "sts:AssumeRole" + } + ] : [] + ) + + }) +} diff --git a/modules/iam_OIDC/outputs.tf b/modules/iam_OIDC/outputs.tf new file mode 100644 index 0000000..56e363e --- /dev/null +++ b/modules/iam_OIDC/outputs.tf @@ -0,0 +1,4 @@ +output "oidc_role_name" { + description = "OIDC로 생성된 IAM Role 이름" + value = aws_iam_role.oidc_role.name +} \ No newline at end of file diff --git a/modules/iam_OIDC/variables.tf b/modules/iam_OIDC/variables.tf new file mode 100644 index 0000000..07246fd --- /dev/null +++ b/modules/iam_OIDC/variables.tf @@ -0,0 +1,39 @@ +variable "role_name" { + type = string + description = "OIDC 역할 이름" +} + +variable "sub_condition" { + type = list(string) + description = "OIDC Subject (sub) 조건" +} + +variable "policy_arns" { + type = list(string) + description = "Attach할 IAM 정책 목록" +} + +resource "aws_iam_role_policy_attachment" "attach_policy" { + for_each = toset(var.policy_arns) + role = aws_iam_role.oidc_role.name + policy_arn = each.value +} + +variable "thumbprint_list" { + description = "OIDC provider thumbprint list" + type = list(string) + default = ["6938fd4d98bab03faadb97b34396831e3780aea1"] +} + +variable "add_root_trust" { + description = "Whether to add root account trust" + type = bool + default = false +} + +variable "account_id" { + description = "AWS account ID for root trust" + type = string + default = "" +} + diff --git a/operation-team-account/OIDC/iam/backend.tf b/operation-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..2b14d75 --- /dev/null +++ b/operation-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-operation-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-operation-lock" + } +} \ No newline at end of file diff --git a/operation-team-account/OIDC/iam/main.tf b/operation-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..c8d9493 --- /dev/null +++ b/operation-team-account/OIDC/iam/main.tf @@ -0,0 +1,116 @@ +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + + + + +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "operation-cicd" + account_id = "502676416967" + add_root_trust = true + + + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Monitoring:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*", + "repo:yunhoch0i/Application-Deployment:*"] + thumbprint_list = ["d89e3bd43d5d909b47a18977aa9d5ce36cee184c"] + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [] + + + +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "operation-cicd" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "s3:ListBucket", + "s3:GetObject", + "s3:PutObject", + "s3:*", + "sts:AssumeRole" + ], + "Resource" : ["*"] + }, + { + "Effect" : "Allow", + "Action" : [ + "cloudwatch:*", + "cloudtrail:*" + ], + "Resource" : "*" + }, + { + "Sid" : "KMSAccess", + "Effect" : "Allow", + "Action" : [ + "kms:*" + ], + "Resource" : "*" + }, + { + "Sid" : "DynamoDBAccess", + "Effect" : "Allow", + "Action" : [ + "dynamodb:*" + ], + "Resource" : "*" + }, + { + "Sid" : "TerraformBackendOperationState", + "Effect" : "Allow", + "Action" : [ + "s3:GetObject", + "s3:PutObject", + "s3:ListBucket" + ], + "Resource" : [ + "arn:aws:s3:::cloudfence-operation-state", + "arn:aws:s3:::cloudfence-operation-state/*" + ] + }, + { + "Sid" : "TerraformDynamoDBLock", + "Effect" : "Allow", + "Action" : [ + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem" + ], + "Resource" : "arn:aws:dynamodb:*:*:table/s3-operation-lock" + }, + { + "Sid" : "KMSAccessForState", + "Effect" : "Allow", + "Action" : [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + "Resource" : "arn:aws:kms:ap-northeast-2:502676416967:key/9901c9d1-8b00-47a9-bd7a-53cfc1f70d25" + }, + { + "Sid" : "ECRAndIAMManagement", + "Effect" : "Allow", + "Action" : [ + "ecr:*", + "iam:CreateServiceLinkedRole" + ], + "Resource" : "*" + } + ] + } + ) +} diff --git a/operation-team-account/state/S3/main.tf b/operation-team-account/state/S3/main.tf index 9061094..d843767 100644 --- a/operation-team-account/state/S3/main.tf +++ b/operation-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-operation-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-operation-state" @@ -51,55 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } -data "aws_caller_identity" "current" {} - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-operation-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -107,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } } diff --git a/prod-team-account/OIDC/iam/backend.tf b/prod-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..ff7f314 --- /dev/null +++ b/prod-team-account/OIDC/iam/backend.tf @@ -0,0 +1,11 @@ + +terraform { + backend "s3" { + bucket = "cloudfence-prod-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-prod-lock" + profile = "whs-sso-prod" + } +} \ No newline at end of file diff --git a/prod-team-account/OIDC/iam/main.tf b/prod-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..478ecc5 --- /dev/null +++ b/prod-team-account/OIDC/iam/main.tf @@ -0,0 +1,136 @@ +# prod-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "Application-Deployment-role2" + add_root_trust = false + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*", + "repo:WHS-DevSecOps-infra/Application-Development:*", + "repo:WHS-DevSecOps-infra/Monitoring:*"] + + thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"] + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [] +} + +data "aws_caller_identity" "current" {} + + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "prod-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "rds:*", + "s3:*", + "ec2:*", + "dynamodb:*", + "kms:*" + ], + "Resource" : "*" + }, + { + "Sid" : "TerraformBackendProdState", + "Effect" : "Allow", + "Action" : [ + "s3:GetObject", + "s3:PutObject", + "s3:ListBucket" + ], + "Resource" : [ + "arn:aws:s3:::cloudfence-prod-state", + "arn:aws:s3:::cloudfence-prod-state/*" + ] + }, + { + "Sid" : "TerraformBackendOperationState", + "Effect" : "Allow", + "Action" : [ + "s3:GetObject", + "s3:ListBucket" + ], + "Resource" : [ + "arn:aws:s3:::cloudfence-operation-state", + "arn:aws:s3:::cloudfence-operation-state/*" + ] + }, + { + "Sid" : "TerraformDynamoDBLock", + "Effect" : "Allow", + "Action" : [ + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem" + ], + "Resource" : "arn:aws:dynamodb:*:*:table/s3-operation-lock" + }, + { + "Sid" : "KMSDecryptForStateFiles", + "Effect" : "Allow", + "Action" : [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + "Resource" : "arn:aws:kms:ap-northeast-2:243359234795:key/c2c5da76-b55b-4bcc-a240-10cc6d6e9940" + }, + { + "Sid" : "AllProdResourceManagement", + "Effect" : "Allow", + "Action" : [ + "ec2:*", + "ecs:*", + "iam:*", + "elasticloadbalancing:*", + "codedeploy:*", + "autoscaling:*", + "cloudwatch:*", + "wafv2:*" + ], + "Resource" : "*" + }, + { + "Sid" : "AllowACMCertificateManagement", + "Effect" : "Allow", + "Action" : [ + "acm:RequestCertificate", + "acm:DescribeCertificate", + "acm:DeleteCertificate", + "acm:ListTagsForCertificate", + "acm:AddTagsToCertificate", + "acm:RemoveTagsFromCertificate" + ], + "Resource" : "*" + }, + { + "Sid" : "AllowRoute53DNSValidation", + "Effect" : "Allow", + "Action" : [ + "route53:GetChange", + "route53:ChangeResourceRecordSets", + "route53:ListHostedZonesByName", + "route53:GetHostedZone", + "route53:ListResourceRecordSets" + ], + "Resource" : "arn:aws:route53:::hostedzone/*" + }, + { + "Sid" : "AllowRoute53GetChange", + "Effect" : "Allow", + "Action" : "route53:GetChange", + "Resource" : "arn:aws:route53:::change/*" + } + ] + }) +} \ No newline at end of file diff --git a/prod-team-account/state/S3/main.tf b/prod-team-account/state/S3/main.tf index ee89ecf..ff6b675 100644 --- a/prod-team-account/state/S3/main.tf +++ b/prod-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-prod-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-prod-state" @@ -51,55 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } -data "aws_caller_identity" "current" {} - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-prod-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -107,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } } diff --git a/security-team-account/OIDC/iam/backend.tf b/security-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..320a88e --- /dev/null +++ b/security-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-security-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-security-lock" + } +} \ No newline at end of file diff --git a/security-team-account/OIDC/iam/main.tf b/security-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..a830cc1 --- /dev/null +++ b/security-team-account/OIDC/iam/main.tf @@ -0,0 +1,42 @@ +# security-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "security-role" + + add_root_trust = false + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/CI-CD_Examples:*"] + + thumbprint_list = ["2b18947a6a9fc7764fd8b5fb18a863b0c6dac24f"] + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [] +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "security" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "rds:*", + "s3:*", + "ec2:*", + "dynamodb:*", + "kms:*", + "iam:*" + ], + "Resource" : "*" + } + ] + }) +} \ No newline at end of file diff --git a/security-team-account/state/S3/main.tf b/security-team-account/state/S3/main.tf index 83cf94d..e39985c 100644 --- a/security-team-account/state/S3/main.tf +++ b/security-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-security-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-security-state" @@ -51,55 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } -data "aws_caller_identity" "current" {} - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-security-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -107,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } } diff --git a/stage-team-account/OIDC/iam/backend.tf b/stage-team-account/OIDC/iam/backend.tf new file mode 100644 index 0000000..4fd9913 --- /dev/null +++ b/stage-team-account/OIDC/iam/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "cloudfence-stage-state" + key = "OIDC/iam.tfstate" + region = "ap-northeast-2" + encrypt = true + dynamodb_table = "s3-stage-lock" + } +} diff --git a/stage-team-account/OIDC/iam/main.tf b/stage-team-account/OIDC/iam/main.tf new file mode 100644 index 0000000..d9c0edd --- /dev/null +++ b/stage-team-account/OIDC/iam/main.tf @@ -0,0 +1,45 @@ +# stage-team-account의 main.tf +# modules/github_oidc를 불러와 해당account별 OIDC역할을 자동으로 생성하는 구조 + +module "github_oidc" { + source = "../../../modules/iam_OIDC" + + role_name = "Application-deployment-role3" + + thumbprint_list = ["d89e3bd43d5d909b47a18977aa9d5ce36cee184c"] + # GitHub Actions에서 이 role을 사용할 수 있도록 허용하는 sub조건 + sub_condition = ["repo:WHS-DevSecOps-infra/Organization:*", + "repo:WHS-DevSecOps-infra/Application-Deployment:*"] + + + # 이 role에 연결할 정책들(IAM 정책 ARN) + policy_arns = [] + +} + + + +#tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_role_policy" "custom_inline_policy" { + name = "stage-role" + role = module.github_oidc.oidc_role_name # 모듈에서 출력된 role이름 참조 + + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Sid" : "VisualEditor0", + "Effect" : "Allow", + "Action" : [ + "s3:*", + "ec2:*", + "rds:*", + "dynamodb:*", + "kms:*", + "iam:*" + ], + "Resource" : "*" + } + ] + }) +} \ No newline at end of file diff --git a/stage-team-account/state/S3/main.tf b/stage-team-account/state/S3/main.tf index 9181e6f..8551691 100644 --- a/stage-team-account/state/S3/main.tf +++ b/stage-team-account/state/S3/main.tf @@ -10,6 +10,13 @@ provider "aws" { region = "ap-northeast-2" } +# KMS 모듈 호출 +module "s3_kms" { + source = "../../../modules/S3_kms" + description = "KMS key for S3 encryption" + s3_bucket_arn = "arn:aws:s3:::cloudfence-stage-state" +} + # S3 버킷 생성 resource "aws_s3_bucket" "state_org" { bucket = "cloudfence-stage-state" @@ -51,55 +58,6 @@ resource "aws_s3_bucket_public_access_block" "state_org_block" { restrict_public_buckets = true } -data "aws_caller_identity" "current" {} - -# S3 암호화를 위한 KMS 키 -resource "aws_kms_key" "s3_key" { - description = "KMS key for S3 encryption" - enable_key_rotation = true - - # KMS 키 정책 추가 - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - - # 현재 계정에게 모든 KMS 작업 권한 부여 - { - Sid = "AllowRootAccountFullAccess", - Effect = "Allow", - Principal = { - AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" - }, - Action = "kms:*", - Resource = "*" - }, - - # S3 서비스에게 암복호화 권한 부여 - { - Sid = "AllowS3ServicePrincipal" - Effect = "Allow" - Principal = { - Service = "s3.amazonaws.com" - } - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ] - Resource = "*" - Condition = { - StringEquals = { - "aws:SourceArn" = "arn:aws:s3:::cloudfence-stage-state", - "aws:SourceAccount" = data.aws_caller_identity.current.account_id - } - } - } - ] - }) -} - # S3 버킷 서버 측 암호화 resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { bucket = aws_s3_bucket.state_org.id @@ -107,7 +65,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" - kms_master_key_id = aws_kms_key.s3_key.arn + kms_master_key_id = module.s3_kms.kms_key_arn } } }