让应用访问自己的报告

AWSBeginner
立即练习

介绍

一个运行中的应用需要读取私有报告。你将通过 Terraform 引用连接 IAM 角色和实例配置文件,授予目标对象的访问权限,并在移除权限前后测试应用的实际请求。

本实验要求你了解 Terraform 引用和 EC2 部署,以及 AWS 角色信任、实例配置文件和范围限定的 S3 权限。全新环境提供运行中的应用、其网络、私有报告存储桶和一个无关的参考应用。你将保持提供的数据和参考环境完整不变。

与认证考试的关联

认证 考试任务 实践内容
Cloud Practitioner (CLF-C02) 任务 2.3 IAM 角色和最小权限对象访问。
Solutions Architect – Associate (SAA-C03) 任务 1.1 通过范围限定的权限实现基于角色的工作负载访问。
CloudOps Engineer – Associate (SOA-C03) 任务 4.1 基础 IAM 角色和策略实现。

实验概览

概念总览:EC2 应用通过实例配置文件和 IAM 角色读取 report.csv,而 private.csv 仍被拒绝。

为运行中的应用赋予角色身份

在这一步中,你将定义 EC2 角色和实例配置文件,将配置文件附加到提供的应用,并观察实际生效的工作负载身份。

上方的 AWS View 显示与 Terminal 查询相同的资源。其 Request path 选择器可请求应用健康状态、工作负载身份或两个私有对象中的任意一个。Terminal 使用预先准备的操作者身份;应用使用自己的实例角色。操作者查询成功并不能证明应用具有权限。

检查已准备好的根目录并记录实例 ID:

cd /home/labex/project/application-reports
cat terraform.tfvars.json
cat application.tf
terraform state list
INSTANCE_ID=$(terraform output -raw instance_id)
echo "$INSTANCE_ID"
REPORT_BUCKET=$(terraform output -raw report_bucket)
echo "$REPORT_BUCKET"

现有状态管理应用实例。网络、AMI、存储桶和参考应用均单独提供。User Data 已将应用指向预先准备的存储桶中的 report.csv,但不包含访问密钥。

在 AWS View 中,选择 application-server、User · 198.51.100.10、Application · 8081 和 Health,然后点击 Send request。预期看到 Connected · 200 和 Report application。选择 Private report 并再次发送请求。应用尚无法读取报告,会返回 Connected · 503 和 Application storage unavailable:HTTP 应用可以访问,但它没有工作负载凭据。

IAM 角色的信任策略控制谁可以承担该角色。实例配置文件将角色连接到 EC2。两者单独都不会授予读取 S3 对象的权限。创建这两个资源:

cat > identity.tf <<'HCL'
resource "aws_iam_role" "reports" {
  name = "tf-report-reader"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
      Action = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_instance_profile" "reports" {
  name = "tf-report-reader"
  role = aws_iam_role.reports.name
}
HCL
sed -i '/vpc_security_group_ids/a\  iam_instance_profile = aws_iam_instance_profile.reports.name' application.tf
terraform fmt
terraform validate
terraform plan

sed 命令在现有安全组属性之后插入实例配置文件属性。该资源引用将运行中的实例连接到配置文件,配置文件再引用角色。审查计划:创建角色和配置文件,更新现有实例的关联,不替换实例或更改 User Data。

应用配置并输入 yes:

terraform apply

检查原生关联和实例配置文件:

aws ec2 \
  describe-iam-instance-profile-associations \
  --filters "Name=instance-id,Values=$INSTANCE_ID" \
  --query 'IamInstanceProfileAssociations[].{Instance:InstanceId,State:State,Profile:IamInstanceProfile.Arn}'
aws iam \
  get-instance-profile \
  --instance-profile-name tf-report-reader \
  --query 'InstanceProfile.{Name:InstanceProfileName,Roles:Roles[].RoleName}'

在 AWS View 中,为 application-server 选择 Workload identity 并发送请求。预期看到 Connected · 200 和包含 assumed-role/tf-report-reader/ 的 ARN。这来自实例内部使用临时角色凭据调用的 STS,而不是 Terminal 操作者。

选择 Private report 并再次发送。预期看到 Connected · 403 和 Access denied。角色身份现在已正常工作,但尚无对象读取权限。这是预期的授权失败。

仅授予目标报告的访问权限

在这一步中,你将为应用角色授予一项对象读取权限,并测试允许的报告和一个无关私有对象。

角色的权限策略决定其会话能够执行哪些操作。使用身份策略,仅允许对确切的 report.csv ARN 执行 s3:GetObject。存储桶名称来自提供的输入;角色来自资源引用。User Data 中不应放入任何静态凭据。

cat > permissions.tf <<'HCL'
resource "aws_iam_role_policy" "report" {
  name = "ReadApplicationReport"
  role = aws_iam_role.reports.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Action = ["s3:GetObject"]
      Resource = ["arn:aws:s3:::${var.report_bucket}/report.csv"]
    }]
  })
}
HCL
terraform fmt
terraform validate
terraform plan

计划新增一个策略。它不得替换应用或创建新存储桶。应用配置并输入 yes:

terraform apply

在 AWS View 中,保持选择 application-server、User 和端口 8081。选择 Private report 并发送请求。预期看到 Connected · 200,报告内容为:

period,total
Q1,320

选择 Unrelated private object 并再次发送请求。预期看到 Connected · 403 和 Access denied。应用可以读取自己的报告,而 private.csv 仍在授权范围之外。此 403 是已连接到的应用返回的实际 S3 授权结果,并非网络连接失败。

返回 Workload identity 并再次发送。其 ARN 必须仍标识工作负载角色。确认收敛:

terraform plan

预期看到 No changes.。 示例结果:应用通过角色身份读取私有报告。各环境中的资源 ID 会有所不同。

撤销权限并测试实际效果

在这一步中,你将仅移除读取策略,并证明拥有有效角色身份的运行中应用不再具有报告访问权限。

移除权限不同于分离实例配置文件或停止实例。保留角色和配置文件,删除策略资源配置,然后审查删除计划:

rm permissions.tf
terraform plan

预期销毁读取策略,不替换实例。应用配置并输入 yes:

terraform apply

独立读取剩余内联策略名称:

aws iam \
  list-role-policies \
  --role-name tf-report-reader \
  --query PolicyNames

预期看到 []。在 AWS View 中,向应用发送 Workload identity 请求。它仍返回 Connected · 200 和相同角色名称。然后发送 Private report 和 Unrelated private object 请求。两者都必须返回 Connected · 403 和 Access denied。尽管应用和角色关联仍存在,被拒绝的读取证明移除权限确实影响了实际工作负载请求。

terraform plan

预期看到 No changes.。

删除受管理资源并保留提供的数据

在这一步中,你将移除受管理的实例、实例配置文件和角色,同时保留外部报告存储桶、其中的对象和参考环境。

策略已移除。审查剩余删除范围:

terraform plan -destroy

预期包含三个受管理资源:应用实例、实例配置文件和 IAM 角色。然后销毁它们,并输入 yes:

terraform destroy

同一个实例必须在原生查询中显示已终止:

aws ec2 \
  describe-instances \
  --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[].Instances[].{ID:InstanceId,State:State.Name}'
terraform state list

预期看到 terminated,且状态中没有受管理资源。使用确切名称查询角色和实例配置文件列表;成功返回的空列表可证明它们已不存在,不能把身份验证失败当作清理完成:

aws iam list-roles --query "Roles[?RoleName=='tf-report-reader'].RoleName"
aws iam list-instance-profiles --query "InstanceProfiles[?InstanceProfileName=='tf-report-reader'].InstanceProfileName"

两者都返回 []。存储桶在此状态之外提供。使用 Terminal 操作者读取两个保留对象并检查字节。这些操作者读取用于检查数据保留;应用的限定访问已另行通过其角色进行测试。

aws s3api get-object --bucket "$REPORT_BUCKET" --key report.csv /tmp/retained-report.csv
cat /tmp/retained-report.csv
aws s3api get-object --bucket "$REPORT_BUCKET" --key private.csv /tmp/retained-private.csv
cat /tmp/retained-private.csv

报告仍为 period,total / Q1,320;无关对象仍为 private,retained。在 AWS View 中,选择 reference-server、User、Application · 8081 和 Health,然后发送请求。预期看到 Connected · 200 和 Reference application。

保留 Terraform 文件以供审阅。之后的普通计划会提出重新创建仍由配置定义的资源;成功销毁不要求删除这些配置。

总结

你通过 Terraform 引用将应用实例连接到角色,区分了角色身份与对象权限,并测试了私有报告读取成功和无关对象访问拒绝。随后,你撤销授权,观察到实际授权失败,并移除了受管理资源,同时保留提供的数据和参考应用。