4. IAM与权限模型设计

在EKS上运行容器工作负载时,权限模型与EC2时代的做法有本质区别。EC2实例通常绑定一个实例配置文件,实例上所有进程共享同一套权限;而Kubernetes集群中,一个节点上可能同时运行几十个来自不同团队的Pod,如果它们共享节点IAM角色,任何一个Pod被攻破都可能拿到节点上所有AWS权限。因此,EKS的权限设计必须把“节点权限”和“Pod权限”彻底分离,并围绕IRSA(IAM Roles for Service Accounts)建立以服务账户为边界的授权体系。

一、三层权限边界:节点、Pod与Kubernetes RBAC

面向容器工作负载的IAM模型可以拆成三层。第一层是节点IAM角色,它由EKS托管节点组或自管节点的启动配置绑定,负责kubelet向EKS控制面注册、节点拉取基础镜像、上报日志等基础设施动作。这一层权限应当尽量收窄,只保留AmazonEKSWorkerNodePolicy、AmazonEC2ContainerRegistryReadOnly和AmazonEKS_CNI_Policy等必要托管策略,绝不能把业务所需的S3、DynamoDB权限挂在节点角色上。

第二层是Pod级IAM角色,通过IRSA把Kubernetes ServiceAccount与IAM Role关联。Pod内的AWS SDK会从注入的Web Identity Token文件读取OIDC令牌,再调用STS的AssumeRoleWithWebIdentity换取临时凭证。这一层是业务权限的主要载体,每个微服务对应一个独立角色。

第三层是Kubernetes RBAC,它控制Pod能访问哪些Kubernetes API资源,与IAM是正交的两套体系。IRSA解决“Pod能调用哪些AWS API”,RBAC解决“Pod能读写哪些K8s对象”,两者不能互相替代。

EKS 容器工作负载三层权限模型 第一层 · 节点 IAM 角色(Node Instance Profile) EKS Worker Node kubelet / CNI / 基础镜像拉取 AmazonEKSWorkerNodePolicy AmazonEC2ContainerRegistryReadOnly AmazonEKS_CNI_Policy ✗ 禁止挂载业务权限 S3 / DynamoDB / SQS 等不得出现在节点角色 第二层 · Pod 级 IAM 角色(IRSA) ServiceAccount order-svc-sa annotation: role-arn Pod AWS SDK / CLI 读取 WebIdentity Token STS AssumeRole WithWebIdentity IAM Role order-svc-role 临时凭证 15min~12h OIDC Provider 校验 iss / sub / aud 条件键:sub = system:serviceaccount:ns:sa 第三层 · Kubernetes RBAC(与 IAM 正交) Role / ClusterRole verbs: get,list,watch RoleBinding 绑定 SA 与 Role 控制 K8s API 访问,不涉及 AWS API IAM 管 AWS,RBAC 管 K8s,二者独立授权

二、IRSA的信任策略与权限策略

IRSA的核心是IAM Role的信任策略。它必须限定OIDC提供方、受众和主体,其中sub条件键是防止跨命名空间冒用的关键。以下是一个订单服务的角色信任策略示例,命名空间为prod,服务账户为order-svc-sa:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.cn-north-1.amazonaws.com.cn/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "oidc.eks.cn-north-1.amazonaws.com.cn/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com",
        "oidc.eks.cn-north-1.amazonaws.com.cn/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:prod:order-svc-sa"
      }
    }
  }]
}

权限策略则遵循最小权限原则。以订单服务为例,它需要从ECR拉取镜像(由节点角色完成,Pod本身不需要)、写入CloudWatch Logs、读写一张DynamoDB订单表、向SQS发送事件。策略中应把资源ARN写全,并利用条件键进一步收窄:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "WriteLogs",
      "Effect": "Allow",
      "Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
      "Resource": "arn:aws:logs:cn-north-1:111122223333:log-group:/aws/eks/prod/order-svc:*"
    },
    {
      "Sid": "OrderTableRW",
      "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:Query"],
      "Resource": [
        "arn:aws:dynamodb:cn-north-1:111122223333:table/orders",
        "arn:aws:dynamodb:cn-north-1:111122223333:table/orders/index/*"
      ],
      "Condition": {
        "ForAllValues:StringEquals": {"dynamodb:LeadingKeys": ["${aws:PrincipalTag/tenant}"]}
      }
    },
    {
      "Sid": "SendEvents",
      "Effect": "Allow",
      "Action": "sqs:SendMessage",
      "Resource": "arn:aws:sqs:cn-north-1:111122223333:order-events"
    }
  ]
}

注意ECR拉取权限的归属:Pod运行时并不需要ecr:GetAuthorizationToken或ecr:BatchGetImage,这些动作由节点上的kubelet以节点角色执行。如果使用Fargate,则由Fargate执行角色承担。把ECR拉取权限错误地授予Pod角色,是常见的权限膨胀来源。

三、服务账户与Pod的绑定方式

创建IRSA的完整链路包含四步:为集群创建OIDC提供方、创建IAM角色并写入信任策略、创建带eks.amazonaws.com/role-arn注解的ServiceAccount、在Deployment中引用该ServiceAccount。以下命令展示关键操作:

# 1. 关联 OIDC 提供方
eksctl utils associate-iam-oidc-provider \
  --cluster prod-eks --region cn-north-1 --approve

# 2. 创建 ServiceAccount 并自动生成信任策略
eksctl create iamserviceaccount \
  --cluster prod-eks --region cn-north-1 \
  --namespace prod --name order-svc-sa \
  --attach-policy-arn arn:aws:iam::111122223333:policy/OrderSvcPolicy \
  --approve --override-existing-serviceaccounts

# 3. 验证注解
kubectl get sa order-svc-sa -n prod -o jsonpath='{.metadata.annotations}'

在Deployment中通过serviceAccountName: order-svc-sa引用后,EKS的Pod Identity Webhook会自动注入两个关键内容:环境变量AWS_ROLE_ARN、AWS_WEB_IDENTITY_TOKEN_FILE,以及挂载到/var/run/secrets/eks.amazonaws.com/serviceaccount/token的令牌卷。SDK会优先使用这套凭证链,无需在代码中硬编码任何密钥。

验证IRSA是否生效的最快方式:进入Pod执行aws sts get-caller-identity,返回的Arn应为arn:aws:sts::111122223333:assumed-role/order-svc-role/...。如果返回的是节点角色Arn,说明注解未生效或Webhook未注入。

四、最小权限的落地检查清单

权限设计完成后,建议用以下清单做一次审计,避免常见疏漏:

检查项合格标准常见问题
节点角色权限仅含EKS Worker、ECR ReadOnly、CNI三项误挂S3全读写
信任策略sub条件精确到namespace:sa使用通配符*导致跨命名空间冒用
资源ARN粒度表、队列、日志组写全名使用*覆盖整个账号
ECR拉取归属由节点/Fargate执行角色承担错误授予Pod角色
凭证有效期默认1小时,按需调整设置12小时且无轮换
权限边界对高风险角色设置Permissions Boundary无边界,角色可自我提权

对于多租户场景,还可以在权限策略中使用aws:PrincipalTag条件键,把租户标识写入角色的标签,再让DynamoDB的LeadingKeys条件与之匹配,从而在同一个IAM角色下实现数据行级隔离。这种“角色共享、数据隔离”的模式比“每租户一个角色”更易维护,但要求标签管理严格,否则会形成越权风险。

不要使用iam:PassRole配合宽泛资源来允许Pod自行切换角色,这会让被攻破的Pod横向获取其他服务权限。若确需角色链,应把iam:PassedToService限定为eks.amazonaws.com,并只允许传递明确列出的角色ARN。

最后,权限模型不是一次性配置。建议把IAM角色、策略、ServiceAccount注解全部纳入IaC(如Terraform或eksctl配置文件),在CI中执行iam-policy-validation与access-analyzer检查,任何权限变更都经过代码评审。这样既能保证最小权限持续有效,也能在出现安全事件时快速定位是哪一个服务账户、哪一条策略产生了越权路径。