17

我一直在尝试使用 k8s-sig 组提供的指南运行 external-dns pod。我已遵循指南的每一步,并收到以下错误。

time="2021-02-27T13:27:20Z" level=error msg="records retrieval failed: failed to list hosted zones: WebIdentityErr: failed to retrieve credentials\ncaused by: AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity\n\tstatus code: 403, request id: 87a3ca86-ceb0-47be-8f90-25d0c2de9f48"

我使用 Terraform 创建了 AWS IAM 策略,并成功创建了它。除了我使用过的服务帐户的 IAM 角色外eksctl,其他一切都是通过 Terraform 旋转的。

但后来我找到了这篇文章,它说使用 awscli 创建 AWS IAM 策略可以消除这个错误。所以我删除了使用 Terraform 创建的策略,并使用 awscli 重新创建了它。然而,它抛出了同样的错误错误。

下面是我的外部 dns yaml 文件。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  # If you're using Amazon EKS with IAM Roles for Service Accounts, specify the following annotation.
  # Otherwise, you may safely omit it.
  annotations:
    # Substitute your account ID and IAM service role name below.
    eks.amazonaws.com/role-arn: arn:aws:iam::268xxxxxxx:role/eksctl-ats-Eks1-addon-iamserviceaccoun-Role1-WMLL93xxxx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: k8s.gcr.io/external-dns/external-dns:v0.7.6
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=xyz.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
        - --provider=aws
        - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
        - --registry=txt
        - --txt-owner-id=Z0471542U7WSPZxxxx
      securityContext:
        fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files

我摸不着头脑,因为在网络的任何地方都没有适当的解决方案来解决这个错误。希望在这个论坛中找到解决这个问题的方法。

最终结果必须显示如下内容并填写托管区域中的记录。

time="2020-05-05T02:57:31Z" level=info msg="All records are already up to date"
4

5 回答 5

17

我也为这个错误而苦苦挣扎。

问题在于信任关系的定义。

您可以在一些官方的 aws 教程(像这样)中看到以下设置:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "${OIDC_PROVIDER}:sub": "system:serviceaccount:<my-namespace>:<my-service-account>"
        }
      }
    }
  ]
}

失败的选项 1

我的问题是我在零件my-service-account末尾传递了一个错误的值。${OIDC_PROVIDER}:subCondition

失败的选项 2

在上一次修复之后 - 我仍然面临同样的错误 - 按照这个 aws 教程解决了这个问题,该教程显示了使用 eksctl 和以下命令的输出:

eksctl create iamserviceaccount \
                --name my-serviceaccount \
                --namespace <your-ns> \
                --cluster <your-cluster-name> \
                --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
                --approve

当您查看 AWS Web 控制台的信任关系选项卡中的输出时 - 您可以看到添加了一个附加条件,其后缀为:aud和 的值sts.amazonaws.com

在此处输入图像描述

所以这个需要在"${OIDC_PROVIDER}:sub" 条件后面加上。

于 2021-05-05T11:53:45.447 回答
1

我能够从 Kubernetes Slack 获得帮助(向@Rob Del 大喊),这就是我们想出的。文章中的 k8s rbac 没有任何问题,问题在于 IAM 角色的编写方式。我正在使用 Terraform v0.12.24,但我相信类似于以下 .tf 的内容应该适用于 Terraform v0.14:

data "aws_caller_identity" "current" {}

resource "aws_iam_role" "external_dns_role" {
  name = "external-dns"

  assume_role_policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Federated": format(
            "arn:aws:iam::${data.aws_caller_identity.current.account_id}:%s", 
            replace(
              "${aws_eks_cluster.<YOUR_CLUSTER_NAME>.identity[0].oidc[0].issuer}", 
              "https://", 
              "oidc-provider/"
            )
          )
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            format(
              "%s:sub", 
              trimprefix(
                "${aws_eks_cluster.<YOUR_CLUSTER_NAME>.identity[0].oidc[0].issuer}", 
                "https://"
              )
            ) : "system:serviceaccount:default:external-dns"
          }
        }
      }
    ]
  })
}

上面的 .tf 假设您使用 terraform 创建了 eks 集群,并且您使用了 external-dns 教程中的 rbac 清单。

于 2021-03-12T18:26:03.270 回答
0

遵循此处建议的设置后,我一直在努力解决类似的问题

我最终在部署日志中遇到了以下异常。

time="2021-05-10T06:40:17Z" level=error msg="records retrieval failed: failed to list hosted zones: WebIdentityErr: failed to retrieve credentials\ncaused by: AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity\n\tstatus code: 403, request id: 3fda6c69-2a0a-4bc9-b478-521b5131af9b"
time="2021-05-10T06:41:20Z" level=error msg="records retrieval failed: failed to list hosted zones: WebIdentityErr: failed to retrieve credentials\ncaused by: AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity\n\tstatus code: 403, request id: 7d3e07a2-c514-44fa-8e79-d49314d9adb6"

就我而言,这是一个错误的服务帐户名称映射到创建的新角色的问题。

这是一步一步的方法来完成这项工作而不会遇到太多麻烦。

  1. 创建 IAM 政策
{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "route53:ChangeResourceRecordSets"
          ],
          "Resource": [
            "arn:aws:route53:::hostedzone/*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": [
            "route53:ListHostedZones",
            "route53:ListResourceRecordSets"
          ],
          "Resource": [
            "*"
          ]
        }
      ]
    }
  1. 为您的 EKS 集群创建 IAM 角色和服务账户。
eksctl create iamserviceaccount \
    --name external-dns-sa-eks \
    --namespace default \
    --cluster aecops-grpc-test \
    --attach-policy-arn arn:aws:iam::xxxxxxxx:policy/external-dns-policy-eks  \
    --approve 
    --override-existing-serviceaccounts
  1. 创建了新的托管区域。

aws route53 create-hosted-zone --name " hosted.domain.com. " --caller-reference "grpc-endpoint-external-dns-test-$(date +%s)"

  1. 在创建集群角色和集群角色绑定到先前创建的服务帐户后,部署 ExternalDNS。
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns-sa-eks
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
      # If you're using kiam or kube2iam, specify the following annotation.
      # Otherwise, you may safely omit it.
      annotations:
        iam.amazonaws.com/role: arn:aws:iam::***********:role/eksctl-eks-cluster-name-addon-iamserviceacco-Role1-156KP94SN7D7
    spec:
      serviceAccountName: external-dns-sa-eks
      containers:
      - name: external-dns
        image: k8s.gcr.io/external-dns/external-dns:v0.7.6
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=hosted.domain.com. # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
        - --provider=aws
        - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
        - --registry=txt
        - --txt-owner-id=my-hostedzone-identifier
      securityContext:
        fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files
  1. 使用域名更新 Ingress 资源并重新应用清单。

对于入口对象,ExternalDNS 将根据为入口对象指定的主机创建 DNS 记录。

- host: myapp.hosted.domain.com

  1. 验证创建的新记录。
BASH-3.2$ aws route53 list-resource-record-sets --output json
--hosted-zone-id "/hostedzone/Z065*********" --query "ResourceRecordSets[?Name == 'hosted.domain.com..']|[?Type == 'A']"

[
    {
        "Name": "myapp.hosted.domain.com..",
        "Type": "A",
        "AliasTarget": {
            "HostedZoneId": "ZCT6F*******",
            "DNSName": "****************.elb.ap-southeast-2.amazonaws.com.",
            "EvaluateTargetHealth": true
        }
    } ]
于 2021-05-10T15:21:14.693 回答
0

在我们的例子中,这个问题发生在使用 Terraform 模块创建 eks 集群,并使用 eksctl 为 aws-load-balancer 控制器创建 iamserviceaccount 时。第一次运行一切正常。但是,如果您执行 terraform destroy,则需要进行一些清理,例如删除由 eksctl 创建的 CloudFormation 脚本。不知何故,事情发生了变化,CloudTrail 传递了一个不再有效的资源角色。因此,请检查服务帐户的注释以确保其有效,并在必要时对其进行更新。然后在我的情况下,我删除并重新部署了 aws-load-balancer-controller

%> kubectl describe serviceaccount aws-load-balancer-controller -n kube-system        
Name:                aws-load-balancer-controller
Namespace:           kube-system
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::212222224610:role/eksctl-ch-test-addon-iamserviceaccou-Role1-JQL4R3JM7I1A
Image pull secrets:  <none>
Mountable secrets:   aws-load-balancer-controller-token-b8hw7
Tokens:              aws-load-balancer-controller-token-b8hw7
Events:              <none>
%>

%> kubectl annotate --overwrite serviceaccount aws-load-balancer-controller eks.amazonaws.com/role-arn='arn:aws:iam::212222224610:role/eksctl-ch-test-addon-iamserviceaccou-Role1-17A92GGXZRY6O' -n kube-system
于 2021-08-24T04:40:58.840 回答
0

我这里有几种可能性。

首先,您的集群是否有与之关联的 OIDC 提供程序?没有它,IRSA 将无法工作。

您可以在 AWS 控制台或通过 CLI 使用以下命令进行检查:

aws eks describe-cluster --name {name} --query "cluster.identity.oidc.issuer"

第一的

删除iamserviceaccount,重新创建它,ServiceAccount从您的 ExternalDNS manfiest (整个第一部分)中删除定义并重新应用它。

eksctl delete iamserviceaccount --name {name} --namespace {namespace} --cluster {cluster}
eksctl create iamserviceaccount --name {name} --namespace {namespace} --cluster 
{cluster} --attach-policy-arn {policy-arn} --approve --override-existing-serviceaccounts
kubectl apply -n {namespace} -f {your-externaldns-manifest.yaml}

可能存在一些冲突,因为您通过在 ExternalDNS manfiest 中eksctl createiamserviceaccount还指定了 a 来覆盖您创建的内容。ServiceAccount

第二

将您的集群升级到 v1.19(如果还没有的话):

eksctl upgrade cluster --name {name}将向您展示将要做什么;

eksctl upgrade cluster --name {name} --approve会做的

第三

一些文档建议,除了设置之外securityContext.fsGroup: 65534,您还需要设置securityContext.runAsUser: 0.

于 2021-03-02T20:35:02.857 回答