Продолжаем погружение в 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-auth
ConfigMap, будет в 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-auth
ConfigMap
Если вернуться к созданию кластера из поста 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?