Не авторизован для выполнения sts:AssumeRoleWithWebIdentity- 403

Я пытался запустить модуль external-dns, используя руководство, предоставленное группой k8s-sig. Я следил за каждым шагом руководства и получаю следующую ошибку.

      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"

Я создал политику AWS IAM с помощью Terraform, и она была успешно создана. За исключением роли IAM для учетной записи службы, для которой я использовал , все остальное было создано через Terraform.

Но потом я получил эту статью, в которой говорится, что создание политики AWS IAM с использованием awscli устранит эту ошибку. Поэтому я удалил политику, созданную с помощью Terraform, и воссоздал ее с помощью awscli. Тем не менее, он выдает ту же ошибку.

Ниже мой внешний yaml-файл dns.

      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"

9 ответов

Я тоже боролся с этой ошибкой.

Проблема заключалась в определении доверительных отношений.

В некоторых официальных руководствах по 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}:sub в Condition часть.

Вариант 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, вы увидите, что было добавлено дополнительное условие с постфиксом :aud и ценность sts.amazonaws.com:

Так что это нужно добавить после "${OIDC_PROVIDER}:sub" условие.

Мне удалось получить помощь от 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 предполагает, что вы создали свой кластер eks с помощью terraform и используете манифест rbac из учебника external-dns .

Подобно тому, что описал @Rot-man, у меня был неправильный префикс для ${OIDC_PROVIDER}. Я включил префикс arn, который мне нужно было удалить, поэтому заменил:

      "arn:aws:iam::123456789100:oidc-provider/oidc.eks.eu-region-1.amazonaws.com/id/5942D3B4F3E74660A6688F6D05FE40C5:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa"

с:

      "oidc.eks.eu-region-1.amazonaws.com/id/5942D3B4F3E74660A6688F6D05FE40C5:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa"

в итоге:

      {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::123456789100:oidc-provider/oidc.eks.eu-region-1.amazonaws.com/id/5942D3B4F3E74660A6688F6D05FE40C5"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.eu-region-1.amazonaws.com/id/5942D3B4F3E74660A6688F6D05FE40C5:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa",
                    "oidc.eks.eu-region-1.amazonaws.com/id/5942D3B4F3E74660A6688F6D05FE40C5:aud": "sts.amazonaws.com"   
                }
            }
        }
    ]
}

Значения идентификатора учетной записи, региона и uuid в приведенных выше примерах являются поддельными, поэтому замените их собственными.

Это также может произойти, если у вас есть опечатка в роли, которую вы пытаетесь взять на себя с помощью учетной записи службы, т. е. имя роли в аннотации не соответствует имени роли в AWS IAM.

Например, если в вашей учетной записи службы была аннотация

          eks.amazonaws.com/role-arn: arn:aws:iam::12345678:role/external-dns-service-account-oidc-role

Однако реальная роль в AWS принадлежала ARN.

      eks.amazonaws.com/role-arn: arn:aws:iam::12345678:role/external-dns-service-account

Вы можете столкнуться с этой проблемой

Для меня проблема заключалась в том, что доверительные отношения были (правильно) настроены с использованием одного раздела , тогда как ServiceAccount был аннотирован с другим разделом, например:

      ...
"Principal": {
    "Federated": "arn:aws-us-gov:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}"
},
...
      kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::{{ .Values.aws.account }}:role/{{ .Values.aws.roleName }}

Уведомлениеarn:aws:iamпротивarn:aws-us-gov:iam

Здесь у меня есть несколько возможностей.

Прежде всего, связан ли с вашим кластером поставщик OIDC? IRSA не будет работать без него.

Вы можете проверить это в консоли AWS или через интерфейс командной строки:

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

Первый

Удалить iamserviceaccount, воссоздайте его, удалите определение из вашего Manfiest ExternalDNS (весь первый раздел) и повторно примените его.

      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}

Возможно, возник конфликт, поскольку вы перезаписали то, что создали с помощью eksctl createiamserviceaccount также указав ServiceAccount в вашем ExternalDNS manfiest.

Второй

Обновите свой кластер до v1.19 (если его еще нет):

eksctl upgrade cluster --name {name} покажет, что будет сделано;

eksctl upgrade cluster --name {name} --approve сделаю это

В третьих

Некоторая документация предполагает, что помимо настройки securityContext.fsGroup: 65534, вам также необходимо установить securityContext.runAsUser: 0.

В нашем случае эта проблема возникла при использовании модуля Terraform для создания кластера eks и eksctl для создания iamserviceaccount для контроллера aws-load-balancer. Все работает отлично с первого раза. Но если вы выполняете уничтожение терраформ, вам нужно выполнить некоторую очистку, например удалить сценарий CloudFormation, созданный eksctl. Каким-то образом ситуация пересеклась, и 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

В моем случае я смог подключить роль oidc с политикой разрешений route53, и это устранило ошибку.

https://medium.com/swlh/amazon-eks-setup-external-dns-with-oidc-provider-and-kube2iam-f2487c77b2a1

а затем с учетной записью службы external-dns, используемой вместо роли кластера.

        annotations:
  #   # Substitute your account ID and IAM service role name below.
    eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/external-dns-service-account-oidc-role

Я боролся с аналогичной проблемой после выполнения предложенной настройкиздесь

В журналах развертывания я получил исключение, указанное ниже.

      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. Создайте роль IAM и учетную запись службы для кластера EKS.
       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
        }
    } ]
Другие вопросы по тегам