From 3d205a564e9aa9bfa3d9bdacf938edc6f6a2e15c Mon Sep 17 00:00:00 2001 From: cloudneutral Date: Wed, 10 Dec 2025 15:28:38 +0800 Subject: [PATCH] Enforce least-privilege bootstrap IAM user and role --- .../aws-cloud/bootstrap/identity/main.tf | 115 ++++++++++++------ 1 file changed, 79 insertions(+), 36 deletions(-) diff --git a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap/identity/main.tf b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap/identity/main.tf index 760bf285..b5ad0915 100644 --- a/iac-template/terraform-hcl-standard/aws-cloud/bootstrap/identity/main.tf +++ b/iac-template/terraform-hcl-standard/aws-cloud/bootstrap/identity/main.tf @@ -27,13 +27,87 @@ resource "aws_iam_role" "terraform_deploy_role" { ) } -# 可选:当前阶段保持你原来的 Admin full access -# (未来你可以把它缩到最小权限) -resource "aws_iam_role_policy_attachment" "attach_admin" { +resource "aws_iam_role_policy" "terraform_deploy_role_policy" { count = var.create_role ? 1 : 0 - role = aws_iam_role.terraform_deploy_role[0].name - policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" + name = "${local.role_name}-bootstrap-minimal" + role = aws_iam_role.terraform_deploy_role[0].id + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + # Bootstrap S3 backend (state bucket) + { + Effect = "Allow", + Action = [ + "s3:CreateBucket", + "s3:GetBucketLocation", + "s3:ListBucket", + "s3:PutBucketVersioning", + "s3:PutBucketPolicy", + "s3:PutBucketTagging", + "s3:PutEncryptionConfiguration", + "s3:PutBucketPublicAccessBlock", + ], + Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}" + }, + { + Effect = "Allow", + Action = [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:PutObjectTagging" + ], + Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}/*" + }, + + # DynamoDB state lock table + { + Effect = "Allow", + Action = [ + "dynamodb:CreateTable", + "dynamodb:DescribeTable", + "dynamodb:UpdateTable", + "dynamodb:TagResource", + "dynamodb:UntagResource" + ], + Resource = "arn:aws:dynamodb:${local.config_region}:${local.account.account_id}:table/${local.bootstrap.state.dynamodb_table_name}" + }, + + # IAM roles needed for bootstrap lifecycle + { + Effect = "Allow", + Action = [ + "iam:GetRole", + "iam:CreateRole", + "iam:DeleteRole", + "iam:UpdateAssumeRolePolicy", + "iam:TagRole", + "iam:UntagRole" + ], + Resource = [ + "arn:aws:iam::${local.account.account_id}:role/${local.role_name}", + "arn:aws:iam::${local.account.account_id}:role/bootstrap-*", + "arn:aws:iam::${local.account.account_id}:role/terraform-*" + ] + }, + { + Effect = "Allow", + Action = [ + "iam:PutRolePolicy", + "iam:DeleteRolePolicy", + "iam:AttachRolePolicy", + "iam:DetachRolePolicy" + ], + Resource = [ + "arn:aws:iam::${local.account.account_id}:role/${local.role_name}", + "arn:aws:iam::${local.account.account_id}:role/bootstrap-*", + "arn:aws:iam::${local.account.account_id}:role/terraform-*" + ] + } + ] + }) } # @@ -64,37 +138,6 @@ resource "aws_iam_user_policy" "terraform_user_policy" { "sts:AssumeRole" ], Resource = var.create_role ? aws_iam_role.terraform_deploy_role[0].arn : var.existing_role_arn - }, - - # S3: Terraform state bucket - { - Effect = "Allow", - Action = [ - "s3:ListBucket" - ], - Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}" - }, - { - Effect = "Allow", - Action = [ - "s3:GetObject", - "s3:PutObject", - "s3:DeleteObject" - ], - Resource = "arn:aws:s3:::${local.bootstrap.state.bucket_name}/*" - }, - - # DynamoDB: state lock table - { - Effect = "Allow", - Action = [ - "dynamodb:GetItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:UpdateItem", - "dynamodb:DescribeTable" - ], - Resource = "arn:aws:dynamodb:${local.config_region}:${local.account.account_id}:table/${local.bootstrap.state.dynamodb_table_name}" } ] })