Продолжаем погружение в AWS Elastic Kubernetes Service, EKS.
Предыдущие части:
- Kubernetes: знакомство, часть 1 — архитектура и основные компоненты, обзор
- Kubernetes: знакомство, часть 2 — создание кластера с AWS cloud-provider и AWS LoadBalancer
- Kubernetes: знакомство, часть 3 — обзор AWS EKS и ручное создание кластера
Продолжение:
В предыдущем посте серии — Kubernetes: знакомство, часть 3 — обзор AWS EKS и ручное создание кластера — мы запустили свой EKS кластер — всё отлично.
kubectl работает, ресурсы создаются.
Но когда после выполнения aws eks update-kubeconfig для настройки kubectl на своём маке наш бекенд-девелопер попытался подключиться:
[simterm]
root@ip-10-0-42-255:~# kubectl get nodes error: You must be logged in to the server (Unauthorized)
[/simterm]
Казалось бы — оба IAM пользователя с админ правами в AWS Console — что могло пойти не так?
Потому — начинаем искать причину, которая снова заставляет немного углубиться в процесс аутентификации и авторизации.
В топике ниже рассмотрим:
- аутентификация vs авторизация
- модули и процесс аутентификации пользователя в EKS —
aws-iam-authenticator, AWS AIM
А в следующей части — авторизацию и RBAC в Kubernetes.
Содержание
Authentication vs Authorization
Для начала — поговорим о разнице между понятиями аутентификация и авторизация пользователя.
Authentication
Аутентификация — это процесс, при котором клиент должен доказать серверу, что что он именно тот, за кого себя выдаёт.
Например, после создания EKS мы поднимаем рабочие ноды, которые затем обращаются к API-серверу Kubernetes Control Plane, что бы подключиться к кластеру.
При этом — API-сервер должен иметь возможность проверить — что за клиент к нему обратился, и имеет ли он вообще право к нам обращаться.
Для этого у Kubernetes имеются authentication modules или authenticators: когде API-сервер получается запрос от клиента, будь то новая Worker Node, kubelet или просто API-запрос, отправленный curl-ом — он обращается к одному из настроенных у Kubernetes аутентификаторов и запускает процесс аутентификации клиента, отправившего запрос.
Все доступные модули можно увидеть в документации, но нас сейчас интересует один, использующийся в AWS Elatic Kubernetes Service — aws-iam-authenticator для проверки валидности пользователя, используя сервис AWS IAM, который мы будем рассматривать ниже.
Authorization
После того, как пользователь прошёл аутентификацию, Kubernetes должен проверить сам запрос — есть ли у данного клиента право на выполнение запрошенных действий, например — выполнение вызова kubectl get pods.
Если вспомнить IAM-политики, то в них мы явно описываем права на конкретные сервисы и действия, например:
Тут в Action указаны действия, разрешённые пользователю, к которому эта политика подключена, и при вызове им операции «s3:DeleteObject» — пользователь получит отказ на выполнение действия, т.е. проверяется контроль доступа к API-вызовам (а вся работа с AWS, как и с Kubernetes, выполняется через API-вызовы к ядру AWS).
Аналогично аутентификации — у Kubernetes есть authorisation modules или authorisers, например — Attribute-based access control (ABAC) и Role-based access control (RBAC).
Модулю авторизации передаётся пользователь, прошедший аутентификацию, после чего он проходит авторизацию, на основании которой API-сервер принимает решение о выполнении действия — или отказе.
Смотрите документацию тут — Controlling Access to the Kubernetes API.
Далее рассмотрим весь процесс аутентификации и авторизации в AWS EKS, а потом перейдём к авторизации.
AWS EKS Authentication и Authorization
Всю процедуру отлично демонстрирует следующая схема:
AWS EKS IAM Authentication
Для аутентификации EKS использует токены — см. Webhook Token Authentication: клиент передаёт API-серверу специально сформированный токен аутентификации, в котором среди прочих данных передаётся идентификатор пользователя.
В случае с EKS — этим идентификатором могут являться ARN (Amazon Resource Name) либо IAM-пользователя, либо IAM-роли.
Kubrnetes-аутентификатор в свою очередь передаёт извлечённый идентификатор самому AWS IAM для проверки — есть ли такой, и имеет ли право говорить с нами, а в роли аутентификатора в Kubernetes использует AWS IAM Authenticator.
Т.е. процесс выглядит следующим образом:
- клиент выполняет запрос к API-серверу, передавая токен аутентификации, в котором включён ID пользователя
- API-сервер передаёт токен другому сервису Kubernetes Control Plane —
aws-iam-authenticator aws-iam-authenticatorобращается к AWS IAM, передавая этого пользователя для проверки — имеется ли такой IAM-идентификатор (ARN) и имеет ли он право доступа к запрашиваемому EKS-кластеру- у себя AWS проводит собственную аутентификацию, используя секретный ключ, связанный с предоставленным ACCESS_ID,
- и проводит собственную авторизацию на соответствие политикам, подключенным к этом пользователю — пользователь без прав на API-вызовы к
eks::*должен быть отвергнут
aws-iam-authenticatorобращается к Kubernetes, проверяя у него (черезaws-authConfigMap, будет в AWS EKS aws-auth ConfigMap) — имеет ли этот IAM-индентификатор право на доступ к кластеруaws-iam-authenticatorвозвращает ответ валиден или нет клиент API-серверу- API-сервер отвечает клиенту ответом с данными, либо сообщает You must be logged in to the server (Unauthorized) и отвергает запрос
Примечание: ранее использовался встроенный в сам Kubernetes механизм AWS cloud-provider, был в Kubernetes: знакомство, часть 2 — создание кластера с AWS cloud-provider и AWS LoadBalancer.
При этом сам aws-iam-authenticator может использоваться как на сервере — так и на клиенте, только на сервере он запускается как aws-iam-authenticator server, а на клиенте — aws-iam-authenticator token -i:
Но в моём случае клиент настраивался через aws eks update-kubeconfig и использует AWS CLI вместо вызова aws-iam-authenticator (см. AWS CLI vs aws-iam-authenticator).
Аутентификация kubectl
Используем kubectl на локальной машине в роли клиента, и посмотрим всю цепочку сами.
Итак, kubectl обращается к локальному файлу ~/.kube/config, из которого получает URL API-сервера EKS-кластера, к которому ему следует обращаться:
... server: https://715***834.sk1.us-east-2.eks.amazonaws.com ...
Далее, нас интересует часть в exec, а именно — command и args:
...
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
args:
- --region
- us-east-2
- eks
- get-token
- --cluster-name
- mobilebackend-dev-eks-0-cluster
command: aws
...
Которая собственно и описывает команду для получения токена для аутентификации в EKS, в котором используется AWS CLI — aws eks get-token.
Очень хочется покопаться в аутентификации самого AWS API, например быстро нагугленные решения есть в Developer Guide для AWS S3, если будет время — можно будет попробовать сделать всю работу просто используя curl, т.к. под капотом AWS CLI точно так же обращается к AWS по API, как наш kubectl — к API-серверу Kubernets.
Вызываем команду из command, передавая аргументы из args:
[simterm]
$ aws --profile arseniy --region us-east-2 eks get-token --cluster-name mobilebackend-dev-eks-0-cluster
{"kind": "ExecCredential", "apiVersion": "client.authentication.k8s.io/v1alpha1", "spec": {}, "status": {"expirationTimestamp": "2019-08-31T10:27:24Z", "token": "k8s-aws-v1.aHR...zEy"}}
[/simterm]
«token»: «k8s-aws-v1.aHR…zEy» — токен получен.
Содержимое можно просмотреть например в https://www.base64decode.org (вставляем часть токена после первого «_«).
Оно будет следующим:
Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKI***D4Q%2F20190831%2Fus-east-1%2Fsts%2Faws4_request&X-Amz-Date=20190831T092243Z&X-Amz-Expires=0&X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&X-Amz-Signature=ff2***f7f
В поле Amz-Credential=AKI***D4Q как раз передаётся ACCESS_KEY нашего пользователя из профиля AWS CLI arseniy (про профили поговорим совсем скоро в в AWS профили):
[simterm]
$ cat ~/.aws/credentials | grep -B1 -A2 AKI***D4Q [arseniy] aws_access_key_id = AKI***D4Q aws_secret_access_key = q0I***jvj
[/simterm]
Итак:
- передаём ACCESS_KEY
- используя ACCESS_KEY — получаем ARN пользователя
- используя ARN,
aws-iam-authenticatorна EKS Control Plane проверяет — разрешён ли пользователю доступ к кластеру (см. AWS EKS aws-auth ConfigMap)
Проверим наш ключ:
[simterm]
$ aws --profile arseniy iam list-access-keys --user-name arseniy
{
"AccessKeyMetadata": [
{
"UserName": "arseniy",
"AccessKeyId": "AKI***D4Q",
...
[/simterm]
AWS-аккаунт, в котором используется ключ:
[simterm]
$ aws --profile arseniy sts get-access-key-info --access-key-id AKI***D4Q
{
"Account": "534***385"
}
[/simterm]
И ARN пользователя в этом аккаунте:
[simterm]
$ aws --profile arseniy iam get-user --user-name arseniy
{
"User": {
"Path": "/",
"UserName": "arseniy",
"UserId": "AID***JU6",
"Arn": "arn:aws:iam::534***385:user/arseniy",
...
[/simterm]
Всё хорошо.
AWS CLI vs aws-iam-authenticator
Аналогично — вместо AWS CLI мы можем вызвать aws-iam-authenticator для получения токена, что бы совсем соответствовать картинке выше.
На Arch Linux устанавливаем его из AUR:
[simterm]
$ yaourt -S aws-iam-authenticator-bin
[/simterm]
И получаем токен:
[simterm]
$ aws-iam-authenticator token -i mobilebackend-dev-eks-0-cluster
{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1alpha1","spec":{},"status":{"expirationTimestamp":"2019-08-31T10:38:41Z","token":"k8s-aws-v1.aHR***ODU"}}
[/simterm]
Можно вручную изменить конфиг kubectl, и вызвать aws-iam-authenticator, т.е. вместо:
...
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
args:
- --region
- us-east-2
- eks
- get-token
- --cluster-name
- mobilebackend-dev-eks-0-cluster
command: aws
...
Зададим:
...
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
args:
- token
- -i
- mobilebackend-dev-eks-0-cluster
command: aws-iam-authenticator
env:
- name: AWS_PROFILE
value: arseniy
...
Проверяем доступ:
[simterm]
$ kubectl auth can-i get pods yes
[/simterm]
Т.е. тут мы видим, что kubectl просто получает токен, используя один из доступных ему методов, заданных через ~/.kube/conf — либо используя AWS CLI (вызывая /usr/bin/aws), либо с помощью /usr/bin/aws-iam-authenticator.
AWS профили
Давайте рассмотрим ещё одну часть файла ~/.kube/config, а именно — env:
...
command: aws
env:
- name: AWS_PROFILE
value: arseniy
Тут ещё в переменную AWS_PROFILE задаётся и имя профиля AWS CLI, для которого требуется получить токен, см. AWS: именованные профили доступа.
Итак:
kubectlсчитывает~/.kube/config- находит API Server URL
- находит команду для получения токена (
commandиargs) - проверяет профиль пользователя, для которого требуется получить токен
- обращается к AWS, получает токен
- обращается к API-серверу, передавая токен аутентификации
Проверяем.
Получаем токен:
[simterm]
$ export AWS_PROFILE=arseniy
$ aws-iam-authenticator token -i mobilebackend-dev-eks-0-cluster
{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1alpha1","spec":{},"status":{"expirationTimestamp":"2019-08-31T11:00:15Z","token":"k8s-aws-v1.aHR***Y2E"}}
[/simterm]
Проверяем его — получаем ARN пользователя:
[simterm]
$ aws-iam-authenticator token -verify -i mobilebackend-dev-eks-0-cluster -t $token&{ARN:arn:aws:iam::534***385:user/arseniy CanonicalARN:arn:aws:iam::534***385:user/arseniy AccountID:534***385 UserID:AID***JU6 SessionName:}
[/simterm]
Для удобства — сохраним его в переменную $token:
[simterm]
$ token="k8s-aws-v1.aHR***Y2E"
[/simterm]
И простым curl — пробуем подключиться к API-серверу:
[simterm]
$ curl -X GET https://715***834.sk1.us-east-2.eks.amazonaws.com/api --insecure --header "Authorization: Bearer $token"
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "ip-172-16-49-148.us-east-2.compute.internal:443"
}
]
}
[/simterm]
Точно так же сам kubectl выполняет API-запросы к Kubernetes.
Кстати — все эти запросы можно посмотреть в CloudWatch Logs, если вы включили их при создании или в настройках кластера, например логи аутентификации — в логах, внезапно, authentificator-***:
heptio-authenticator-aws vs aws-iam-authenticator
Сначала удивился, почему в логих виден heptio-authenticator-aws, если в документации AWS говорится про aws-iam-authenticator? См. Managing Cluster Authentication.
Но всё оказалось просто: до версии 4.0 aws-iam-authenticator назывался heptio-authenticator-aws.
См. v0.3.0 и v0.4.0-alpha.1.
Хорошо — тут мы разобрались. А что происходит дальше?
AWS EKS aws-auth ConfigMap
Собственно, тут и начинается «магия» аутентификации — теперь aws-iam-authenticator должен:
- проверить у самого AWS IAM — есть ли запрошенный пользователь в системе, и имеет ли он права, т.е. выполнить его аутентифицакию
- передать его API-серверу Kubernetes, после чего тот выполнит авторизацию — проверит, имеет ли этот пользоваль право доступа к кластеру, и тут как раз используется
aws-authConfigMap
Если вернуться к созданию кластера из поста Kubernetes: знакомство, часть 2 — создание кластера с AWS cloud-provider и AWS LoadBalancer — мы создавали там ConfigMap который выглядел следующим образом:
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::534***385:role/mobilebackend-dev-eks-0-wn-stack-NodeInstanceRole-15NNFZK6WW4IG
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
Где в строке arn:aws:iam::534***385:role/mobilebackend-dev-eks-0-wn-stack-NodeInstanceRole-15NNFZK6WW4IG мы передаём ARN роли, которой разрешён доступ к кластеру. Группы обсудим в Авторизации, в следующей части.
Добавление пользователя к кластеру
И вернёмся к вопросу — почему другой IAM-пользователь получает сообщение «You must be logged in to the server (Unauthorized)«?
Собственно, ответ явно виден выше — потому что его нет в нашем ConfigMap.
Проверяем документацию aws-iam-authenticator:
... # each mapUsers entry maps an IAM role to a static username and set of groups mapUsers: ...
Обновляем содержимое нашего ConfigMap — добавляем ARN пользоваля, его логин, и задаём ему группу system:masters:
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::534***385:role/mobilebackend-dev-eks-0-wn-stack-NodeInstanceRole-15NNFZK6WW4IG
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
mapUsers: |
- userarn: arn:aws:iam::534***385:user/yaroslav
username: yaroslav
groups:
- system:masters
Применяем:
[simterm]
$ kubectl apply -f aws-auth-cm.yaml configmap/aws-auth configured
[/simterm]
Проверяем его на кластере — находим сам ConfigMap:
[simterm]
$ kubectl -n kube-system get cm NAME DATA AGE aws-auth 2 2d ...
[/simterm]
И проверяем его содержимое:
[simterm]
$ kubectl -n kube-system describe cm aws-auth
Name: aws-auth
Namespace: kube-system
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","data":{"mapRoles":"- rolearn: arn:aws:iam::534***385:role/mobilebackend-dev-eks-0-wn-stack-NodeInstanceRole-15NNFZK...
Data
====
mapRoles:
----
- rolearn: arn:aws:iam::534***385:role/mobilebackend-dev-eks-0-wn-stack-NodeInstanceRole-15NNFZK6WW4IG
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
mapUsers:
----
- userarn: arn:aws:iam::534***385:user/yaroslav
username: yaroslav
groups:
- system:masters
Events: <none>
[/simterm]
Готово — теперь arn:aws:iam::534***385:user/yaroslav может выполнять любые операции на сервере.
«root» aka Cluster creator
Последний нюанс во всей этой схеме, это факт того, что вы никак не можете увидеть создателя кластера, из-за чего, собственно, и возникло непонимание: пользователь arn:aws:iam::534***385:user/arseniy имел полный доступ к системе, а arn:aws:iam::534***385:user/yaroslav — нет.
Причём найти «создателя» пока удалось только в CloudTrail по API-вызову к AWS — CreateCluster :
Возникает она потому что:
The IAM identity that created the EKS cluster is automatically “hardwired” in the AWS IAM Authenticator. This means that this IAM identity is recognised and authenticated (and mapped to a user in the system:masters group) by the AWS IAM Authenticator without being listed in the aws-auth ConfigMap.
И при этом никакой возможности увидеть её где-то «внутри» самого EKS вроде как нет (но я написал в саппорт — может подскажут).
UPD 02.09.2019
Пришёл ответ от саппорта — всё-таки увидеть нельзя:
At this time, the IAM entity that creates the cluster becomes the first cluster administrator. This entity is passed to
the master nodes and is not visible from the `aws-auth` ConfigMap. This is similar to the root user for your AWS account
in that it has the system:masters permission.
Ссылки по теме
- Kubernetes Client Authentication on Amazon EKS
- How auth works in EKS with IAM Users
- Managing Users or IAM Roles for your Cluster
- How do I provide access to other users and roles after cluster creation in Amazon EKS?









