Узнайте, как настроить поставщика OIDC с помощью EKS и как создать роли IAM для учетных записей служб. Полный исходный код доступен на github .

Contents
Обзор
В последнем выпуске EKS (1.13 и 1.14) плоскость управления AWS Kubernetes поддерживает роли IAM для учетных записей служб. Эта функция позволяет нам связать роль IAM с учетной записью службы Kubernetes. Теперь мы можем предоставить временные учетные данные, а затем предоставить разрешения AWS для контейнеров в любом модуле, который использует эту учетную запись службы. Кроме того, нам больше не нужно предоставлять расширенные разрешения роли IAM рабочего узла, чтобы модули на этом узле могли вызывать API AWS.
Прежде чем эта функция была включена, нам приходилось использовать решения kiam или kube2iam. Подробнее о kube2iam можно прочитать в другом моем рассказе .
В этой истории вы узнаете, как настроить EKS, провайдера OpenID Connect (OIDC), роли IAM и сервисные аккаунты с помощью Terraform. Вы также узнаете о проблемах, с которыми я столкнулся во время установки. Для своей реализации я использую EKS 1.14 (экс.1), Terraform 0.12.12, Terraform AWS provider 2.28.1 .
Преимущества
Функция ролей IAM для учетных записей служб предоставляет следующие преимущества:
- Наименьшие привилегии . Благодаря использованию ролей IAM для сервисных учетных записей вам больше не нужно предоставлять расширенные разрешения роли IAM рабочего узла, чтобы модули на этом узле могли вызывать API AWS. Вы можете ограничить разрешения IAM для учетной записи службы, и только модули, использующие эту учетную запись службы, имеют доступ к этим разрешениям.
- Изоляция учетных данных . Контейнер может извлекать учетные данные только для той роли IAM, которая связана с учетной записью службы, которой он принадлежит. Контейнер никогда не имеет доступа к учетным данным, предназначенным для другого контейнера, принадлежащего другому поду.
- Возможность аудита . Доступ и ведение журнала событий доступны через CloudTrail, чтобы помочь обеспечить ретроспективный аудит.
Технический обзор ролей IAM для учетных записей служб
AWS IAM поддерживает федеративные удостоверения с использованием OIDC. Эта функция позволяет нам аутентифицировать вызовы API AWS с помощью поддерживаемых поставщиков удостоверений и получать действительный веб-токен OIDC JSON (JWT). Вы можете передать этот токен в AssumeRoleWithWebIdentityоперацию AWS STS API и получить учетные данные временной роли IAM. Такие учетные данные можно использовать для связи с такими сервисами, как Amazon S3 и DynamoDB.
Kubernetes уже давно использует учетные записи служб в качестве собственной внутренней системы идентификации. Поды могут аутентифицироваться на сервере API Kubernetes с помощью автоматически монтируемого токена (это был JWT, не относящийся к OIDC), который мог проверить только сервер API Kubernetes. Срок действия этих устаревших токенов сервисной учетной записи не ограничен, и смена ключа подписи — сложный процесс. В Kubernetes версии 1.12 была добавлена поддержка новой ProjectedServiceAccountTokenфункции, представляющей собой веб-токен OIDC JSON, который также содержит идентификатор учетной записи службы и поддерживает настраиваемую аудиторию.
Amazon EKS теперь размещает общедоступную конечную точку обнаружения OIDC для каждого кластера, содержащую ключи подписи для ProjectedServiceAccountTokenвеб-токенов JSON, поэтому внешние системы, такие как IAM, могут проверять и принимать токены OIDC, выпущенные Kubernetes. Источник технической реализации .
Внедрение и настройка
С помощью terraform я создал кластер EKS и провайдера OIDC:
### Backend and provider config for reference terraform { required_version = "~> 0.12.12"backend "remote" { hostname = "app.terraform.io" } }provider "aws" { assume_role { role_arn = var.assume_role_arn session_name = "EKS_deployment_session_${var.tags["Environment"]}" }version = "~> 2.28.1" region = var.region }### EKS cluster config resource "aws_eks_cluster" "cluster" { enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"] name = "${var.tags["ServiceType"]}-${var.tags["Environment"]}" role_arn = aws_iam_role.cluster.arn version = "1.14"vpc_config { subnet_ids = flatten([module.vpc.public_subnets, module.vpc.private_subnets]) security_group_ids = [aws_security_group.cluster.id] endpoint_private_access = "true" endpoint_public_access = "true" } }### OIDC config resource "aws_iam_openid_connect_provider" "cluster" { client_id_list = ["sts.amazonaws.com"] thumbprint_list = [] url = aws_eks_cluster.cluster.identity.0.oidc.0.issuer }
Вы будете удивлены, но этих двух ресурсов достаточно для настройки вашего кластера EKS и провайдера OIDC.
Вот место, где я часами пытался решить проблему с OIDC, поскольку мои учетные записи службы не могли получить действительные учетные данные. Причина, по которой он не работал, заключалась в отсутствующем отпечатке корневого ЦС. Если вы создаете провайдера OIDC для своего кластера EKS с помощью консоли AWS, отпечаток добавляется автоматически. Однако, когда вы создаете его с помощью terraform, список пуст.
[ОБНОВЛЕНО 28.08.20] Существует три способа получения отпечатка и добавления его в ресурс aws_iam_openid_connect_provider. Решение номер три — единственное, полностью поддерживаемое Terraform и достигаемое с помощью поставщика TLS terraform.
- Внешний источник данных Terrafor
### External cli kubergrunt data "external" "thumb" { program = ["kubergrunt", "eks", "oidc-thumbprint", "--issuer-url", aws_eks_cluster.cluster.identity.0.oidc.0.issuer] }### OIDC config resource "aws_iam_openid_connect_provider" "cluster" { client_id_list = ["sts.amazonaws.com"] thumbprint_list = [data.external.thumb.result.thumbprint] url = aws_eks_cluster.cluster.identity.0.oidc.0.issuer }
или без использования бинарника kubegrunt:
Создайте файлы с именем oidc-thumbprint.sh со следующим содержимым.
#!/bin/bashTHUMBPRINT=$(echo | openssl s_client -servername oidc.eks.${1}.amazonaws.com -showcerts -connect oidc.eks.${1}.amazonaws.com:443 2>&- | tac | sed -n '/-----END CERTIFICATE-----/,/-----BEGIN CERTIFICATE-----/p; /-----BEGIN CERTIFICATE-----/q' | tac | openssl x509 -fingerprint -noout | sed 's/://g' | awk -F= '{print tolower($2)}') THUMBPRINT_JSON="{\"thumbprint\": \"${THUMBPRINT}\"}" echo ${THUMBPRINT_JSON}
А затем сослаться на приведенный выше скрипт в ресурсе terraform, который получит отпечаток.
data "aws_region" "current" {}# Fetch OIDC provider thumbprint for root CA data "external" "thumbprint" { program = ["./oidc-thumbprint.sh", data.aws_region.current.name] }resource "aws_iam_openid_connect_provider" "cluster" { client_id_list = ["sts.amazonaws.com"] thumbprint_list = concat([data.external.thumbprint.result.thumbprint], var.oidc_thumbprint_list) url = aws_eks_cluster.cluster.identity.0.oidc.0.issuer }
2. Получите его вручную
На самом деле я получил его вручную, а затем добавил в свой шаблон terraform. Смотрите инструкцию как его получить:
- Прежде чем вы сможете получить отпечаток для OIDC IdP, вам необходимо установить OpenSSL cli.
- Начните с URL-адреса поставщика удостоверений OIDC (например,
https://server.example.com
). Этот URL-адрес доступен, если щелкнуть кластер EKS в консоли AWS, а затем добавить/.well-known/openid-configuration
, чтобы сформировать URL-адрес для документа конфигурации поставщика удостоверений, например следующий: Открыть этот URL-адрес в Интернете браузере, заменив его именем сервера вашего IdP.https://
server.example.com/.well-known/openid-configuration
server.example.com
- В ответе в браузере найдите «jwks_uri». Скопируйте полное доменное имя URL. Не включайте
https://
путь или любой другой путь после домена верхнего уровня. То, что вам нужно, может выглядеть так:
server.example.com/sadasdasd/keys - Используйте инструмент командной строки OpenSSL для выполнения следующей команды, которая использует пример вывода выше
openssl s_client -servername server.example.com/sadasdasd/keys -showcerts -connect server.example.com:443/sadasdasd/keysFor some people the following command will also work:openssl s_client -servername server.example.com -showcerts -connect server.example.com:443
- В командном окне прокручивайте вверх, пока не увидите сертификат. Если вы видите более одного сертификата, найдите последний отображаемый сертификат (в нижней части вывода команды). Это будет сертификат корневого центра сертификации в цепочке центров сертификации. Скопируйте сертификат (включая строки
-----BEGIN CERTIFICATE-----
и-----END CERTIFICATE-----
) и вставьте его в текстовый файл. Затем сохраните файл под именем ca_root.crt . Затем выполните следующее:
openssl x509 -in ca_root.crt -fingerprint -noout # the above line will output something like: # SHA1 Fingerprint=9E:99:A4:8A:99:60:B1:49:26:BB:7F:3B:02:E2:2D:A2:B0:AB:72:80# Remove the colon characters (:) from this string to produce the final thumbprint, like this: echo -n "9E:99:A4:8A:99:60:B1:49:26:BB:7F:3B:02:E2:2D:A2:B0:AB:72:80" | sed 's|:||g'# Final output 9E99A48A9960B14926BB7F3B02E22DA2B0AB7280
Теперь, когда вы получили отпечаток, ваш ресурс OIDC будет выглядеть так:
### OIDC config resource "aws_iam_openid_connect_provider" "cluster" { client_id_list = ["sts.amazonaws.com"] thumbprint_list = ["9E99A48A9960B14926BB7F3B02E22DA2B0AB7280"] url = aws_eks_cluster.cluster.identity.0.oidc.0.issuer }
3. Провайдер TLS [ОБНОВЛЕНО 28.08.20]
С выпуском 2.2.0 -> https://github.com/hashicorp/terraform-provider-tls/releases/tag/v2.2.0 провайдера TLS terraform теперь вы можете очень легко получить отпечаток. Смотри ниже:
data "tls_certificate" "cluster" { url = aws_eks_cluster.cluster.identity.0.oidc.0.issuer }resource "aws_iam_openid_connect_provider" "cluster" { client_id_list = ["sts.amazonaws.com"] thumbprint_list = concat([data.tls_certificate.cluster.certificates.0.sha1_fingerprint], var.oidc_thumbprint_list) url = aws_eks_cluster.cluster.identity.0.oidc.0.issuer }
Завершая этот раздел, я хотел бы еще раз указать, что отпечаток OIDC — это требование IAM, а не функция EKS. При настройке поставщика IAM в консоли автоматически выбирается отпечаток корневого ЦС. Этот отпечаток является хэшем обслуживающего центра сертификации. В Terraform вы должны получить его другим способом, и три варианта подробно описаны выше.
Ограничение доступа к учетным данным профиля инстанса Amazon EC2
Перед созданием фактического примера и обновлением нашей учетной записи службы нам необходимо отключить доступ к учетным данным профиля экземпляра Amazon EC2. По умолчанию контейнеры, работающие на ваших рабочих узлах, не защищены от доступа к учетным данным, которые предоставляются в профиль экземпляра рабочего узла через сервер метаданных экземпляра Amazon EC2.
Чтобы предотвратить доступ контейнеров в модулях к учетным данным, предоставленным в профиль экземпляра рабочего узла, запустите следующие iptables
команды на своих рабочих узлах (как root
) или включите их в сценарий пользовательских данных начальной загрузки вашего экземпляра.
# commands block ALL containers from using the instance profile credentials yum install -y iptables-services iptables --insert FORWARD 1 --in-interface eni+ --destination 169.254.169.254/32 --jump DROP iptables-save | tee /etc/sysconfig/iptables systemctl enable --now iptables
Настройте роль IAM и используйте ее для Amazon VPC CNI.
Подключаемый модуль Amazon VPC CNI для Kubernetes — это сетевой подключаемый модуль для организации сетей модулей в кластерах Amazon EKS. Плагин CNI отвечает за выделение IP-адресов VPC узлам Kubernetes и настройку необходимой сети для модулей на каждом узле. Плагину требуются разрешения IAM, предоставляемые управляемой политикой AWS AmazonEKS_CNI_Policy, для совершения вызовов API AWS от вашего имени. По умолчанию эта политика привязана к роли IAM вашего рабочего узла. Опять же, в этом случае я использую Terraform для создания роли IAM.
- IAM предполагает ролевую политику
Создайте файл oidc_assume_role_policy.json со следующим содержимым:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "${OIDC_ARN}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${OIDC_URL}:sub": "system:serviceaccount:${NAMESPACE}:${SA_NAME}"
}
}
}
]
}
Это общая политика предполагаемых ролей, которую я использую для нескольких ролей IAM.
2. Конфигурация роли IAM
Используя terraform, мы собираемся создать роль IAM с приведенной выше политикой принятия роли, а также управляемой политикой, называемойAmazonEKS_CNI_Policy.
resource "aws_iam_role" "aws_node" { name = "${var.tags["ServiceType"]}-${var.tags["Environment"]}-aws-node"assume_role_policy = templatefile("oidc_assume_role_policy.json", { OIDC_ARN = aws_iam_openid_connect_provider.cluster.arn, OIDC_URL = replace(aws_iam_openid_connect_provider.cluster.url, "https://", ""), NAMESPACE = "kube-system", SA_NAME = "aws-node" })tags = merge( var.tags, { "ServiceAccountName" = "aws-node" "ServiceAccountNameSpace" = "kube-system" } )depends_on = [aws_iam_openid_connect_provider.cluster] }resource "aws_iam_role_policy_attachment" "aws_node" { role = aws_iam_role.aws_node.name policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"depends_on = [aws_iam_role.aws_node] }
В приведенном выше шаблоне вы можете видеть, что я создал новую роль IAM (предположим, что она называется: eks-dev-aws-node ), которая будет прикреплена к служебной учетной записи с именем aws-node, которая существует в пространстве имен kube-system. .
3. Обновите сервисный аккаунт VPC CNI — aws-node
В настоящее время у нас есть сервисный аккаунт, который не использует роли IAM.
apiVersion: v1 kind: ServiceAccount metadata: name: aws-node namespace: kube-system
Чтобы использовать только что созданную роль IAM, мы создаем новый файл (sa_aws_node.yaml) со следующим содержимым:
apiVersion: v1
kind: ServiceAccount
metadata:
name: aws-node
namespace: kube-system
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::0123456789:role/eks-dev-aws-node
Затем вы просто обновляете свою учетную запись службы, запустив:
kubectl apply -f sa_aws_node.yaml
Запустите развертывание aws-node
набора демонов, чтобы применить переменные среды учетных данных. Мутирующий веб-хук не применяет их к уже запущенным модулям.
kubectl rollout restart -n kube-system daemonset.apps/aws-node
Наблюдайте за развертыванием и подождите, пока число DESIRED развертывания не совпадет с UP-TO-DATE
числом.
kubectl get -n kube-system daemonset.apps/aws-node --watch
После смены всех модулей опишите один из модулей и убедитесь, что существуют переменные среды AWS_WEB_IDENTITY_TOKEN_FILE
и .AWS_ROLE_ARN
kubectl exec -n kube-system aws-node-9rgzw env | grep AWS# Expected Output: AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token AWS_VPC_K8S_CNI_LOGLEVEL=DEBUG AWS_ROLE_ARN=arn:aws:iam::0123456789:role/eks-dev-aws-node
Теперь, когда ваш сервисный аккаунт для AWS VPC CNI использует роль IAM. Вы можете удалить AmazonEKS_CNI_Policy
политику из роли IAM рабочего узла.
Вывод
Роли IAM для учетных записей служб — относительно новая функция, и многие надстройки Kubernetes могут ее не поддерживать (информация актуальна на 23.10.2019). Однако такая поддержка будет добавлена относительно быстро и выпущена для общего пользования.
В настоящее время следующие надстройки поддерживают роли IAM для сервисных учетных записей: external-dns, aws-vpc-cni, alb-ingress-controller и cluster-autoscaler.
Судя по моему первоначальному опыту, это не было быстрой задачей, чтобы заставить его работать. Тем не менее, составление этого руководства, надеюсь, поможет кому-то еще сэкономить время.
Статья является переводом marcincuber.medium.com