Имеется два новых проекта в Elastic Kubernetes Service (см. AWS: Elastic Kubernetes Service — автоматизация создания кластера, часть 1 — CloudFormation), каждый проект живёт в отдельном своём namespace.
Кроме того, имеется два пользователя, разработчика, которым надо дать доступ к этим двум пространствам имён, но только на поды в них и только на определённые read-only операции.
Что бы реализовать общий контроль доступа для этих пользователей – нам нужна некая группа, в которую мы сможем их объединить, а затем использовать её для проверки правил доступа.
Для понимания описанной ниже схемы – см. посты Kubernetes: знакомство, часть 4 — аутентификация в AWS EKS, aws-iam-authenticator и AWS IAM и Kubernetes: знакомство, часть 5 — RBAC авторизация и примеры Role и RoleBinding.
Что у нас есть?
У нас есть AWS IAM – его пользователи, группы, роли и политики.
И у нас есть Kubernetes RBAC с его ClusterRole
, RoleBinding
и aws-auth ConfigMap
,
Значит, наша задача делится на две:
- продумать механизм аутентификации и авторизации двух пользователей в AWS IAM, который сможет создать единый объект для идентификации (группу, роль)
- продумать механизм авторизации двух пользователей в Kubernetes RBAC, используя этот объект
Проблема
В чём неочевидная сложность?
В том, как мы свяжем эти две задачи, так как в отличии от IAM Users и IAM Roles – AWS IAM Group не может являться субъектом аутентификации, см. список в AWS JSON Policy Elements: Principal, и мы не сможем использовать ARN такой группы вида arn:aws:iam::111:group/group-name в aws-auth ConfigMap
, что бы дать пользователям этой группы доступ в Kubernetes кластер.
Зато – мы можем использовать IAM Roles, а через IAM Groups – можем подключать общие для пользователей IAM политики с доступами к нужным ролям.
Что мы можем построить, используя это?
- создадим IAM роль с IAM политикой read-only на API-вызовы
eks:*
и ей добавим trust policy с пользователями нашего аккаунта – потом эту роль мы будем использовать в aws-authConfigMap
- создадим IAM Group с IAM политикой, которая разрешает выполнение вызова
sts::AssumeRole
к роли, созданной выше - создадим IAM пользователя, добавим его в эту группу, что бы он через политику группы “наследовал” разрешение на AssumeRole
Немного заглянем вперёд – что будет дальше, в Kubernetes?
Там наш aws-auth ConfigMap
“замапит” нашу IAM нашей роли в AWS на RBAC-роль в кластере, а нашим пользователям мы настроим AWS CLI на выполнение AssumeRole собственно роли arn:aws:iam::534***385:role/iam-bttrm-web-ro-role – тогда в Kubernetes будет приходить общий для них обоих индентификатор, и пользователи смогут выполнять операции, заданные в их общей роли и только в заданных нами namespaces.
Про имена
Итак, у нас есть два пользователя – объединим их в условную группу “web“, которая будет использоваться в именах IAM ролей и групп.
Плюс у нас есть два проекта в Kubetenets с отдельными namespaces – project1 и project2, которые мы тоже объединим в условную группу “web“, но эту группу используем только в именах RBAC-ресурсов в Kuberenetes для большей их наглядности.
Кроме того – к именам ресурсов AWS IAM будем добавлять префикс iam-, а дял RBAC, соответсвенно – rbac-.
Следовательно, у нас будут имена вида:
- iam-bttrm-web-ro-role: тут iam говорит, что это объект из AWS IAM, bttrm – общее имя моего проекта, web – “домен” для наших двух новых веб-проектов
- rbac-bttrm-web-ro-role-binding: rbac – объект связан с Kubernetes RBAC, bttrm – общее имя моего проекта, web – “домен” для наших двух веб-проектов
Содержание
AWS IAM
Что бы представлять – что именно мы делаем, и как оно всё друг с другом будет работать – можно набросать такую вот схему:
Тут:
- пользователю требуется получить доступ к роли, для чего он выполняет AssumeRole-запрос в AWS IAM:
- AWS IAM проводит аутентификацию пользователя
- если аутентификация пройдена – AWS IAM начинаего авторизацию пользователя: проверяет политики доступа, подключенные к нему
- находит политику, разрешающую AssumeRole нужной роли и разрешает пользователю обращение к этой роли
- пользователь обращается к IAM-роли
- AWS IAM проверяет Trust relations этой роли
- убеждается, что пользователь пришёл из того же аккаунта, что наша роль и разрешает доступ
- пользователь обращается в AWS Elastic Kubenetes Service, но уже используя индентификатор (токен) IAM-роли
Эта и следующая схема делалась в cloudcraft.co, но в рабочем процессе подобные просто рисуются от руки в большом блокноте.
Не знаю, почему ни в одном встреченном гайде никто не отобразил все эти связи в виде таких вот простых и наглядных схем – без них делать всё это намного сложнее, особенно по части Kubernetes RBAC.
Пока оставим её тут – по ходу дела можно будет вернутся.
IAM Role
Создаём новую роль – ключевой, по сути, элемент всей конструкции.
Тип ЕС2:
На следующей странице жмём Create policy, в новой вкладке в окне создания политики переключаемся на JSON, вносим политику:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "eks:DescribeCluster", "eks:ListClusters" ], "Resource": "*" } ] }
В нёй мы разрешаем выполнение API-запросов eks:DescribeCluster
и eks:ListClusters
по всем регионам всего нашего аккаунта – эти вызовы используются AWS CLI во время выполнения aws eks update-kubeconfig
, которую будут выполнять наши новые пользователи для настройки своих локальных kubectl
.
Сохраняем её с именем iam-bttrm-eks-ro-policy.
Возвращаемся в предыдущую вкладку с созданием роли, справа кликаем на “Обновить”, и добавляем к роли созданную политику:
Tags пропускаем, сохраняем роль с именем iam-bttrm-web-ro-role:
Запоминаем её Role ARN:
IAM Role Trust relationships
Хотя вот тут>>> в ответах утверждают, что Trust relations в рамках одного аккаунта создавать не требуется – но у меня без него не работало, плюс он упоминается в документации вот тут>>>.
Переключаемся во вкладку Trust relationships, жмём Edit trust relationship, в Principal указываем “AWS”: “arn:aws:iam::534***385:root” – тут root включает в себя всех пользователей (точнее – всех объектов аутентифицикации) нашего аккаунта:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::534***385:root" }, "Action": "sts:AssumeRole" } ] }
Но это ещё не значит, что любой субъект, прошедший аутентификацию в нашем аккаунте сможет использовать (“assu-мить“) эту роль, т.к. у этого субъекта ещё должны быть соответсвующие права на вызов этой конкретно этой роли – и для наших будущих пользователей мы это как раз и реализуем через IAM Group.
IAM Policy
Перед тем, как создавать группу – добавим собственно политику, которая будет разрешать выполнение выполнение API-вызова sts:AssumeRole
к роли, которую мы создали, а потом эту политику подключим к создаваемой группе, через которую она “унаследуется” будущими пользователями группы.
В свою очередь, что бы отобрать у IAM-пользователя доступ к Elastic Kubernetes Service и подам в нём – достаточно будет отключить его от группы.
Создаём политику:
В Resource
указываем ARN политики, которую создали выше:
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::534***385:role/iam-bttrm-web-ro-role" } }
Сохраняем её, например с именем iam-bttrm-allow-assume-web-ro-role-policy:
IAM Group
Переходим в Группы, создаём новую, назовём её iam-bttrm-web-ro-group, подключаем ей созданную раннее политику:
IAM User
Создаём пользователя с Programmatic access, добавляем его к группе iam-bttrm-web-ro-group, к которой подключена политика iam-bttrm-allow-assume-web-ro-role-policy:
Настраиваем локальный AWS CLI профиль на нового пользователя iam-bttrm-web-user-1:
[simterm]
$ aws configure --profile iam-bttrm-web-user-1 AWS Access Key ID [None]: AKI***O4Z AWS Secret Access Key [None]: FoS***kft Default region name [None]: us-east-2 Default output format [None]: json
[/simterm]
Проверяем:
[simterm]
$ aws --profile iam-bttrm-web-user-1 sts get-caller-identity { "UserId": "AID***DUH", "Account": "534***385", "Arn": "arn:aws:iam::534***385:user/iam-bttrm-web-user-1" }
[/simterm]
Пробуем доступ к ЕС2 – должны получить отказ:
[simterm]
$ aws --profile iam-bttrm-web-user-1 ec2 describe-instances An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
[/simterm]
Хорошо – разрешения на API-запрос DescribeInstances
нам никто не давал, и авторизацию мы не прошли (хотя прошли аутентификацию как пользователь iam-bttrm-web-user-1).
Пробуем EKS-операции:
[simterm]
$ aws --profile iam-bttrm-web-user-1 eks list-clusters An error occurred (AccessDeniedException)when calling the ListClusters operation: User: arn:aws:iam::534***385:user/iam-bttrm-web-user-1 is not authorized to perform: eks:ListClusters on resource: arn:aws:eks:us-east-2:534***385:cluster/*
[/simterm]
Отлично – мы снова прошли аутентификацию как iam-bttrm-web-user-1 – но снова не прошли авторизацию, в этот раз на выполнение API-вызова eks:ListClusters
.
А теперь – используем AssumeRole:
- пройдём аутентификацию, как iam-bttrm-web-user-1
- запросим
sts:AssumeRole
– IAM проверит, какие политики к нам подключены и найдёт политику iam-bttrm-allow-assume-web-ro-role-policy - мы временно получим права на выполнение операций, разрешённых для роли iam-bttrm-web-ro-role, среди которых – наш
eks:ListClusters
Пробуем.
Обновляем локальный ~/.aws/config
, и к профилю iam-bttrm-web-user-1 добавим второй профиль – iam-bttrm-web-user-1-eks: (см. быстрый пример работы с AssumeRole в AWS: IAM AssumeRole — описание, примеры):
... [profile iam-bttrm-web-user-1] region = us-east-2 output = json [profile iam-bttrm-web-user-1-eks] role_arn = arn:aws:iam::534***385:role/iam-bttrm-web-ro-role source_profile = iam-bttrm-web-user-1 region = us-east-2
В котором мы используем роль arn:aws:iam::534***385:role/iam-bttrm-web-ro-role, но через source_profile
аутентифицируемся как iam-bttrm-web-user-1. (см. Using an IAM Role in the AWS CLI).
Пробуем – повторяем под обычным профилем:
[simterm]
$ aws --profile iam-bttrm-web-user-1 eks list-clusters --output text An error occurred (AccessDeniedException) [...]
[/simterm]
И под вторым, с AssumeRole:
[simterm]
$ aws --profile iam-bttrm-web-user-1-eks eks list-clusters --output text CLUSTERS bttrm-eks-prod-0 CLUSTERS eksctl-bttrm-eks-production-1 CLUSTERS bttrm-eks-dev-0
[/simterm]
Всё работает, с этим закончили – у нас есть группа, используя которую пользователи получают возможность выполнять eks:ListClusters
.
Используя эту же роль (но свои ACCESS и SECRET ключи для аутентификации) – они настроят свой kubectl
для доступа к подам. А вот доступ к неймспейсам и подам мы ограничим через Kubernetes RBAC, см. .
Kubernetes RBAC
Итак, нам требуется иметь некую группу, через права доступа которой мы сможем выдать доступ к подам в двух различных неймспейсах, при этом эта группа должна быть связана с группой в AWS IAM.
Так как AWS IAM Group ARN мы использовать не можем – то делаем “грязный хак” в виде использования общей роли для разных пользователей группы, по которой мы и будем их идентифицировать для авторизации в Kubernetes RBAC.
Далее нам надо создать:
ClusterRole
– роль, которая будет разрешать операции с подами во всех namespace- две
RoleBinding
в двух namespace – по одной в каждой, которые будут связывать в этом пространстве имён RBAC-группу иClusterRole
, таким образом ограничивая влияние этой ClusterRole для RBAC-группы границами namespace, в которой этаRoleBinding
создана - и обновим aws-auth
ConfigMap
, что бы он связал IAM Role и RBAC-группу
В результате у нас должна получиться следующая схема:
RBAC ClusterRole
Описываем ClusterRole
с именем rbac-bttrm-pods-ro-cluster-role:
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: rbac-bttrm-pods-ro-cluster-role rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"]
Создаём её:
[simterm]
$ kubectl apply -f rbac-bttrm-pods-ro-cluster-role.yml clusterrole.rbac.authorization.k8s.io/rbac-bttrm-pods-ro-cluster-role created
[/simterm]
RBAC RoleBinding
Создаём два RoleBinding
– одинаковых, но в разных namespace – bttrm-web-proj-1-ns и bttrm-web-proj-2-ns:
-- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: rbac-bttrm-web-proj-1-ro-role-binding namespace: bttrm-web-proj-1-ns subjects: - kind: Group name: rbac-bttrm-web-ro-group apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: rbac-bttrm-pods-ro-cluster-role apiGroup: rbac.authorization.k8s.io --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: rbac-bttrm-web-proj-2-ro-role-binding namespace: bttrm-web-proj-2-ns subjects: - kind: Group name: rbac-bttrm-web-ro-group apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: rbac-bttrm-pods-ro-cluster-role apiGroup: rbac.authorization.k8s.io
Создаём сами неймспейсы:
[simterm]
$ kubectl create ns bttrm-web-proj-1-ns namespace/bttrm-web-proj-1-ns created $ kubectl create ns bttrm-web-proj-2-ns namespace/bttrm-web-proj-2-ns created
[/simterm]
Создаём в них поды с NGINX:
[simterm]
$ kubectl -n bttrm-web-proj-1-ns run nginx --image=nginx $ kubectl -n bttrm-web-proj-2-ns run nginx --image=nginx
[/simterm]
И создаём биндинги:
[simterm]
$ kubectl apply -f rbac-bttrm-web-ro-role-binding.yml rolebinding.rbac.authorization.k8s.io/rbac-bttrm-web-proj-1-ro-role-binding created rolebinding.rbac.authorization.k8s.io/rbac-bttrm-web-proj-2-ro-role-binding created
[/simterm]
aws-auth ConfigMap
Теперь нам надо обновить aws-auth ConfigMap
, что бы связать нашу группу в AWS (а по сути – общую роль) с “виртуальной” группой rbac-bttrm-web-ro-group, которую мы “создали” в биндингах.
Редактируем её:
[simterm]
$ kubectl -n kube-system edit cm aws-auth
[/simterm]
Добавляем в mapGroups
:
... - groups: - rbac-bttrm-web-ro-group rolearn: arn:aws:iam::534***385:role/iam-bttrm-web-ro-role username: iam-bttrm-web-ro-role ...
Сохраняем, выходим.
kubecl
config и проверка
Теперь для нашего kubectl
настроим контекст доступа к нашему дев-кластеру, используя AWS CLI профиль iam-bttrm-web-user-1-eks, в котором выполняется AssumeRole arn:aws:iam::534***385:role/iam-bttrm-web-ro-role, при этом для аутентфикации, как помним, используется IAM пользователь iam-bttrm-web-user-1:
[simterm]
$ aws --profile iam-bttrm-web-user-1-eks eks update-kubeconfig --name bttrm-eks-dev-0 Updated context arn:aws:eks:us-east-2:534***385:cluster/bttrm-eks-dev-0 in /home/setevoy/.kube/config
[/simterm]
Пробуем просто get pod
в default namespace:
[simterm]
$ kubectl get pod Error from server (Forbidden): pods is forbidden: User "iam-bttrm-web-ro-role" cannot list resource "pods" in API group "" in the namespace "default"
[/simterm]
И повторяем в bttrm-web-proj-1-ns:
[simterm]
$ kubectl get pod -n bttrm-web-proj-1-ns NAME READY STATUS RESTARTS AGE nginx-7bb7cd8db5-l9jtm 1/1 Running 0 2m24s
[/simterm]
Вот наш NGINX.
Повторяем для неймспейса второго проекта – bttrm-web-proj-2-ns:
[simterm]
$ kubectl get pod -n bttrm-web-proj-2-ns NAME READY STATUS RESTARTS AGE nginx-7bb7cd8db5-9s6zj 1/1 Running 0 2m31s
[/simterm]
Можно попробовать доступы к другим ресурсам, доступа к которым мы не давали, например – к нодам:
[simterm]
$ kubectl get node Error from server (Forbidden): nodes is forbidden: User "iam-bttrm-web-ro-role" cannot list resource "nodes" in API group "" at the cluster scope
[/simterm]
Или использовать auth can-i
:
[simterm]
$ kubectl auth can-i get node Warning: resource 'nodes' is not namespace scoped no $ kubectl auth can-i get pod no $ kubectl auth can-i get pod -n bttrm-web-proj-1-ns yes $ kubectl auth can-i create pod -n bttrm-web-proj-1-ns no
[/simterm]
Готово.
Ссылки по теме
- Let’s do DevOps: Assuming an IAM role from an EC2 instance
- 3 Realistic Approaches to Kubernetes RBAC