AWS-EKS-15--EKS权限管理(下)Pod 是如何调用 AWS 资源的

摘要

原理解析

  • 在Pod中访问AWS资源,如获取S3中的文件,将ingress绑定到ELB,等等,这些操作都需要被授予相应的权限才可以正常访问。

  • 在AWS IAM中的Role可以被授权给基于OIDC身份提供商的外部用户,而在EKS集群中的pod对于AWS IAM来说就属于外部用户,AWS会通过IAM OIDC对这些外部用户的有效性进行校验。

  • 前文已经介绍过如何为EKS创建 IAM OIDC 身份提供商,在EKS(K8s)中,并不是直接对Pod进行权限校验,而是ServcieAccount,在 K8s 中,ServiceAccount 是一种用于身份验证和授权的机制,它为 Pod 提供了一个身份标识。每个 Pod 都与一个 ServiceAccount 相关联,并且可以使用该 ServiceAccount 获取与其关联的身份凭据。

  • 在EKS(K8s)中可以将ServcieAccount与AWS IAM Role进行绑定,同时将ServcieAccount与Pod进行关联,这样Pod也就具备了相应的IAM Role。

  • EKS(K8s)的 ServcieAccount 通过 Idp(EKS OpenID Connect provider)获得 ID_token并将其发送给 IAM Identity providers ,IAM Identity providers负责获取OIDC的key并验证ID_token的有效性。

  • 当ServcieAccount通过IAM OIDC身份校验后,关联的Pod就可以使用相对应的Role获得访问AWS资源的权限。

  • 创建Pod时,如果没有为其指定关联的ServcieAccount,则默认使用Pod所在Namespace下的名称为default的ServcieAccount,创建Namespace时会自动为其创建该缺省的ServcieAccount,默认其没有绑定任何IAM Role,则缺省继承NodeGroup的IAM Role,NodeGroup的IAM Role是在创建EKS集群时自动创建的,其具有对ECR存储库的只读访问、允许Amazon EKS工作节点连接到Amazon EKS群集等权限。

  • Amazon EKS Pod Identity Webhook 会查看与 service account 关联的 Pods,并向 Pod 提供下列环境变量

    • AWS_ROLE_ARN=arn:aws:iam::<ACCOUNT_ID>:role/<IAM_ROLE_NAME>
    • AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
  • AWS_ROLE_ARN就是绑定到ServiceAccount上的IAM Role,AWS_WEB_IDENTITY_TOKEN_FILE就是要发送给IAM Identity providers的ID_token。

  • 默认只有以 root 用户运行的容器才有权限访问 web identity token 文件,当以其它用户运行容器时,需要用 fsGroup 指定一个 groupID。在 Amazon EKS 中,可以将 fsGroup 设置为 65534。这是 Kubernetes 中的推荐做法,因为这个值对应于 nobody 用户和 nogroup 组的 ID。使用 fsGroup: 65534 将确保容器中的非 root 用户具有适当的权限来访问 Web Identity Token 文件。

案例分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.7/docs/install/iam_policy.json

# 使用上一步中下载的策略创建一个 IAM policy
$ aws iam create-policy \
--profile eks-us-west-2 \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam_policy.json

# 在 AWS Load Balancer Controller 的 kube-system 命名空间中创建名为 aws-load-balancer-controller 的 Kubernetes 服务账户,并使用 IAM 角色的名称注释 Kubernetes 服务账户。
$ eksctl create iamserviceaccount \
--cluster=eks-lexing \
--profile eks-us-west-2 \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--role-name AmazonEKSLoadBalancerControllerRole \
--attach-policy-arn=arn:aws:iam::743263909644:policy/AWSLoadBalancerControllerIAMPolicy \
--approve
  • 上面是先通过aws iam create-policy命令创建了一个策略,然后通过eksctl create iamserviceaccount命令为EKS(K8s)创建了一个ServiceAccount,同时创建了一个IAM Role与ServiceAccount绑定,并将该Role与上面创建的策略进行关联。

  • 所以实际上,上面的一个命令干了四件事

    • 1.创建了一个名称为AmazonEKSLoadBalancerControllerRole的IAM角色并关联新建的名称为AWSLoadBalancerControllerIAMPolicy的策略

    • 2.在k8s的kube-systemnamespace下创建了一个名称为aws-load-balancer-controller的ServiceAccount

    1
    2
    3
    $ k get sa -n kube-system aws-load-balancer-controller
    NAME SECRETS AGE
    aws-load-balancer-controller 0 9d
    • 3.在名称为AmazonEKSLoadBalancerControllerRole的IAM角色的信任关系中指定认证方式为OIDC,并且关联名称为aws-load-balancer-controller的ServiceAccount。

    • 4.在名称为aws-load-balancer-controller的ServiceAccount中关联上面的角色AmazonEKSLoadBalancerControllerRole,就是添加注释eks.amazonaws.com/role-arn: arn:aws:iam::743263909644:role/AmazonEKSLoadBalancerControll

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ k edit sa -n kube-system aws-load-balancer-controller
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::743263909644:role/AmazonEKSLoadBalancerControll
    creationTimestamp: "2023-07-04T09:20:41Z"
    labels:
    app.kubernetes.io/managed-by: eksctl
    name: aws-load-balancer-controller
    namespace: kube-system
    resourceVersion: "1707669"
    uid: 857200a6-2e16-4939-bbbc-483dd579acbb
  • 然后在创建Pod时,为Pod指定serviceAccountName: aws-load-balancer-controller

    • 默认情况下,在 Kubernetes 中启动的 Pod 不会以 root 用户身份运行,所以在使用基于 OIDC 的身份认证时,当以非 root 用户运行容器时,可以使用 fsGroup 来指定一个组 ID(group ID),以便容器中的用户具有访问 Web Identity Token 文件的权限。在 Amazon EKS 中,可以将 fsGroup 设置为 65534。这是 Kubernetes 中的推荐做法,因为这个值对应于 nobody 用户和 nogroup 组的 ID。使用 fsGroup: 65534 将确保容器中的非 root 用户具有适当的权限来访问 Web Identity Token 文件,确保容器中的用户具有访问 /var/run/secrets/eks.amazonaws.com/serviceaccount/token 文件的权限。
1
$ kubectl edit deployment -n kube-system aws-load-balancer-controller

使用aws命令创建角色并关联SA

  • 创建Policy
1
2
3
4
5
6
7
$ curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.7/docs/install/iam_policy.json

# 使用上一步中下载的策略创建一个 IAM policy
$ aws iam create-policy \
--profile eks-us-west-2 \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam_policy.json
  • 创建trust-policy.json,用于描述IAM Role的信任关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::743263909644:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/1029FF88CB872B6B7A1CC65D44191A56"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-west-2.amazonaws.com/id/1029FF88CB872B6B7A1CC65D44191A56:aud": "sts.amazonaws.com",
"oidc.eks.us-west-2.amazonaws.com/id/1029FF88CB872B6B7A1CC65D44191A56:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
}
}
}
]
}

上述配置中的 “Condition” 部分是用于在 IAM 角色的信任策略中定义条件。该条件会限制什么样的令牌可以被信任并用于角色的身份验证和授权。
具体来说,“Condition” 对象中的 “StringEquals” 表示字符串相等的条件比较。它包含两个键值对,每个键值对都描述了一个条件。
oidc.eks.us-west-2.amazonaws.com/id/1029FF88CB872B6B7A1CC65D44191A56:aud”: “sts.amazonaws.com”:这个条件表示 OIDC 令牌中的 “aud”(受众)字段必须与 “sts.amazonaws.com” 相等。也就是说,令牌的受众必须是 AWS Security Token Service (STS)。
oidc.eks.us-west-2.amazonaws.com/id/1029FF88CB872B6B7A1CC65D44191A56:sub”: “system:serviceaccount:kube-system:aws-load-balancer-controller”:这个条件表示 OIDC 令牌中的 “sub”(主题)字段必须与 “system:serviceaccount:kube-system:aws-load-balancer-controller” 相等。也就是说,令牌的主题必须是 kube-system 命名空间下的 aws-load-balancer-controller Service Account。
这些条件的目的是确保只有满足这两个条件的 OIDC 令牌才能被信任,并被用于通过 OIDC 进行的身份验证和授权操作。这样可以限制对角色的访问,仅允许特定的 OIDC 令牌来获取访问权限。

  • 创建 IAM Role
1
2
3
4
$ aws iam create-role \
--profile eks-us-west-2 \
--role-name AmazonEKSLoadBalancerControllerRole \
--assume-role-policy-document file://"trust-policy.json"
  • 为 Role 添加 Policy

1
2
3
4
$ aws iam attach-role-policy \
--profile eks-us-west-2 \
--policy-arn arn:aws:iam::743263909644:policy/AWSLoadBalancerControllerIAMPolicy \
--role-name AmazonEKSLoadBalancerControllerRole
  • 创建SA

1
$ kubectl create serviceaccount aws-load-balancer-controller -n kube-system
  • 为SA绑定IAM Role

1
2
3
$ kubectl annotate serviceaccount aws-load-balancer-controller \
-n kube-system \
eks.amazonaws.com/role-arn=arn:aws:iam::743263909644:role/AmazonEKSLoadBalancerControllerRole

动手实践

  • 搞明白了上面的原理,我们只需要按照上面那的步骤做就可以了,接下来就以Pod访问S3为例进行说明。

  • 创建新的策略或使用现有策略,这里我们使用已有的策略arn:aws:iam::aws:policy/AmazonS3FullAccess

  • 使用eksctl create iamserviceaccount创建Service和IAM Role,并将它们进行绑定关联

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ eksctl create iamserviceaccount \
--cluster=eks-lexing \
--namespace=test \
--name=test-s3-sa-new \
--role-name TestS3Role \
--attach-policy-arn=arn:aws:iam::aws:policy/AmazonS3FullAccess \
--approve
2023-07-13 19:59:18 [ℹ] 6 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller,kube-system/aws-node,kube-system/ebs-csi-controller-sa,kube-system/efs-csi-controller-sa,test/test-s3-sa,test/testS3SA) will be excluded
2023-07-13 19:59:18 [ℹ] 1 iamserviceaccount (test/test-s3-sa-new) was included (based on the include/exclude rules)
2023-07-13 19:59:18 [!] serviceaccounts that exist in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
2023-07-13 19:59:18 [ℹ] 1 task: {
2 sequential sub-tasks: {
create IAM role for serviceaccount "test/test-s3-sa-new",
create serviceaccount "test/test-s3-sa-new",
} }2023-07-13 19:59:18 [ℹ] building iamserviceaccount stack "eksctl-eks-lexing-addon-iamserviceaccount-test-test-s3-sa-new"
2023-07-13 19:59:18 [ℹ] deploying stack "eksctl-eks-lexing-addon-iamserviceaccount-test-test-s3-sa-new"
2023-07-13 19:59:19 [ℹ] waiting for CloudFormation stack "eksctl-eks-lexing-addon-iamserviceaccount-test-test-s3-sa-new"
2023-07-13 19:59:50 [ℹ] waiting for CloudFormation stack "eksctl-eks-lexing-addon-iamserviceaccount-test-test-s3-sa-new"
2023-07-13 19:59:51 [ℹ] created serviceaccount "test/test-s3-sa-new"

# 查看sa,确认是否已关联IAM Role
$ k describe sa test-s3-sa-new
Name: test-s3-sa-new
Namespace: test
Labels: app.kubernetes.io/managed-by=eksctl
Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::743263909644:role/TestS3Role
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>


  • 创建一个已经安装过AWS CLI的Pod,这里镜像就使用amazon/aws-cli,注意要指定serviceAccountName: test-s3-sa-new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-test-s3-sa
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-test-s3-sa
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: app-test-s3-sa
spec:
serviceAccountName: test-s3-sa-new
containers:
- image: amazon/aws-cli
imagePullPolicy: Always
name: app-test-s3-sa
ports:
- containerPort: 80
command: ["/bin/sh", "-c"]
args:
- tail -f /dev/null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 部署
$ k apply -f testS3SA.yaml
deployment.apps/deployment-test-s3-sa created
# 进入pod
$ k exec -it deployment-test-s3-sa-5bd985845d-mqjjp -- bash
# 查看当前的AWS用户信息,可以看到这里关联的是一个临时用户,但是从Arn中也能看出来其关联的角色 TestS3Role
bash-4.2# aws sts get-caller-identity
{
"UserId": "AROA22DP3G4GNBLHPZBAM:botocore-session-1689250618",
"Account": "743263909644",
"Arn": "arn:aws:sts::743263909644:assumed-role/TestS3Role/botocore-session-1689250618"
}
# 查看s3存储桶列表,查询成功,说明当前pod已经具备的访问AWS S3资源的权限
bash-4.2# aws s3 ls
2023-07-10 09:34:45 lexing-helm-charts
# 查看环境变量
bash-4.2# echo $AWS_ROLE_ARN
arn:aws:iam::743263909644:role/TestS3Role
bash-4.2# echo $AWS_WEB_IDENTITY_TOKEN_FILE
/var/run/secrets/eks.amazonaws.com/serviceaccount/token