AWS: Kubernetes та Access Management API – нова схема авторизації в EKS

Автор |  04/07/2024
 

Ще одна крута фіча, яку Амазон показав ще на минулому re:Invent в листопаді 2023 – це зміни в тому, як AWS Elastic Kubernetes Service виконує аутентифікацію та авторизацію юзерів. При чому це стосується не тільки саме користувачів кластеру, а і WorkerNodes.

Тобто, не дуже-то нова схема – але в мене ось тільки зараз дійшли руки до апгрейду кластеру з 1.28 на 1.30, заодно буду оновлювати версію модулю terraform-aws-modules/eks зі змінами ESK Access Management API, бо зараз ми на версії 19, а зміни були додані в версії 20 (див. v20.0.0 Release notes),

Про Terraform, мабуть, поговоримо в наступному пості, а сьогодні давайте глянемо як нова система працює, і що вона нам дозволяє. А вже знаючи це – візьмемо Terraform, і взагалі подумаємо про те, як організувати роботу з IAM з урахування змін в EKS Access Management API та EKS Pod Identities.

Загалом по аутентифікації/авторизації в Kubernetes можна ще глянути старі пости:

Як це працює?

Раніше ми мали спеціальний aws-auth ConfigMap, в якому описувались WorkerNodes IAM Roles, всі наші юзери та їхні групи.

Відтепер, ми можемо керувати доступами в EKS напряму через його API використовуючи AWS IAM в ролі аутентифікатора. Тобто, юзер логіниться в AWS, AWS виконує аутентифікацію – перевіряє, що це саме той юзер, за якого він себе видає, а потім, коли юзер підключається до Kubernetes – то виконується його авторизація – перевірка прав доступу до кластеру і в самому кластері.

При цьому ця схема чудово працює з RBAC самого Kubernetes.

І ще одна дуже важлива деталь – що ми нарешті можемо позбутись “дефолтного root-юзера” – прихованого адміністратора кластера, від імені якого він створювався. При чому раніше ми не мали змоги ніде його побачити або змінити, що іноді спричиняло проблеми.

Отже, якщо раніше нам потрібно було самим керувати записами в aws-auth ConfigMap, і не дай боже його зламати (а в мене траплялось через чи то кривий маніфест, чи то не дуже прямі руки) – то тепер ми можемо винести управління доступами в окремий Terraform-код, і управляти доступами набагато простіше і з меншим ризиком.

Зміни в IAM та EKS

Тепер у нас в EKS є дві нові сутності – Access entries та Access policies:

  • Amazon EKS Access Entries – запис в EKS про об’єкт, який пов’язаний з AWS IAM роллю чи юзером
    • описує тип (звичайний юзер, EC2, etc), Kubernetes RBAC Groups, або EKS Access Policy
  • Amazon EKS Access Policy – політика в EKS, яка описує права для EKS Access Entries. І це політики саме EKS – ви не знайдете їх в IAM.

Наразі є 4 EKS Access Policy, які ми можемо використати, і вони аналогічні дефолтним User-facing ClusterRoles в Kubernetes :

  • AmazonEKSClusterAdminPolicy – cluster-admin
  • AmazonEKSAdminPolicy – admin
  • AmazonEKSEditPolicy – edit
  • AmazonEKSViewPolicy – view

Підозрюю, що десь під капотом ці EKS Access Policy просто мапляться на Kubernetes ClusterRoles.

Ці політики ми підключаємо до IAM Role чи IAM user, і під час підключення до Kubernetes-кластеру EKS Autorizer перевіряє які саме права є у цього користувача.

Схематично це можна відобразити так:

Або така схема, з блогу AWS A deep dive into simplified Amazon EKS access management controls:

Замість дефолтних AWS managed IAM Policy ми при створенні EKS Access Policy можемо вказати ім’я Kubernetes RBAC Group – і тоді замість EKS Autorizer буде використано механізм Kubernetes RBAC – далі подивимось, як це працює.

Для Terraform в terraform-provider-aws версії 5.33.0 були додані два нових відповідних типи resource – aws_eks_access_entry та aws_eks_access_policy_association. Але зараз все будемо робити руками.

Налаштування Cluster access management API

Перевіряти як воно працює будемо на існуючому кластері версії 1.28.

Відкриваємо налаштування кластера, вкладка Access, клікаємо Manage access:

Зараз у нас включено ConfigMap (той самий aws-auth) – міняємо на EKS API and ConfigMap – так ми залишимо і старий механізм, і протестуємо новий (в Terraform це також можна зробити):

Звертаємо увагу на попередження “Once you modify a cluster to use EKS access entry API, you cannot change it back to ConfigMap only” – але terraform-aws-modules/eks версії 19.21 ці зміни ігнорує і нормально працює далі, тож можна міняти руками.

Тепер кластер буде виконувати авторизацію юзерів і з aws-auth ConfigMap, і з EKS Access Entry API, з перевагою до Access Entry API.

Після переключення на EKS Access Entry API відразу маємо нові EKS Access Entries:

І як раз тепер ми можемо побачити того самого “прихованого root-юзера” – assumed-role/tf-admin, бо Teraform працює саме від цієї IAM-ролі, і в моєму сетапі це робилось як через цей механізм EKS, від якого тепер можна буде позбутись.

Але з поточного aws-auth ConfigMap взято не все – роль для WorkerNodes є, а от решта записів (юзери з mapUsers та ролі з mapRoles) автоматично не додались. Хоча під час зміни параметра API_AND_CONFIG_MAP через Teraform це наче має відбутись – потім перевіримо.

Підключення нового IAM User до кластеру EKS

Перевірити існуючі EKS Access Entries з AWS CLI можна командою aws eks list-access-entries:

$ aws --profile work eks list-access-entries --cluster-name atlas-eks-test-1-28-cluster
{
    "accessEntries": [
        "arn:aws:iam::492***148:role/test-1-28-default-eks-node-group-20240702094849283200000002",
        "arn:aws:iam::492***148:role/tf-admin"
    ]
}

Давайте додамо нового IAM User з доступом до кластеру.

Створюємо юзера:

В Set permissions не вибираємо нічого, просто клікаємо Next:

Створюємо Access Key – будемо використовувати цього юзера з AWS CLI, аби згенерувати kubectl config:

Додаємо дозвіл на eks:DescribeCluster:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Statement1",
      "Effect": "Allow",
      "Action": ["eks:DescribeCluster"],
      "Resource": ["*"]
    }
  ]
}

Зберігаємо, і створюємо новий AWS CLI профайл:

$ vim -p ~/.aws/config ~/.aws/credentials

Додаємо профайл до ~/.aws/config:

[profile test-eks]
region = us-east-1
output = json

І ключі до ~/.aws/credentials:

[test-eks]
aws_access_key_id = AKI***IMN
aws_secret_access_key = Kdh***7wP

Створюємо новий kubectl context:

$ aws --profile test-eks eks update-kubeconfig --name atlas-eks-test-1-28-cluster --alias test-cluster-test-user
Updated context test-cluster-test-user in /home/setevoy/.kube/config

З юзером закінчили – тепер треба з ним підключитись до кластеру.

EKS: створення Access Entry

Можемо зробити або через AWS Console:

Або з AWS CLI (з робочим профайлом, а не новим, бо в нього ніяких прав нема) і командою aws eks create-access-entry.

В параметрах передаємо ім’я кластера та ARN юзера чи ролі, яких підключаємо до кластеру (але, мабуть, більш коректно буде сказати “для яких створюємо точку входу на кластер“, бо сутність називається Access Entry):

$ aws --profile work eks create-access-entry --cluster-name atlas-eks-test-1-28-cluster --principal-arn arn:aws:iam::492***148:user/test-eks-acess-TO-DEL
{
    "accessEntry": {
        "clusterName": "atlas-eks-test-1-28-cluster",
        "principalArn": "arn:aws:iam::492***148:user/test-eks-acess-TO-DEL",
        "kubernetesGroups": [],
        "accessEntryArn": "arn:aws:eks:us-east-1:492***148:access-entry/atlas-eks-test-1-28-cluster/user/492***148/test-eks-acess-TO-DEL/98c8398d-9494-c9f3-2bfc-86e07086c655",
        ...
        "username": "arn:aws:iam::492***148:user/test-eks-acess-TO-DEL",
        "type": "STANDARD"
    }
}

Ще раз глянемо в AWS Console:

Новий Enrty додано, йдемо далі.

Підключення EKS Access Policy

Зараз доступу до кластеру з новим юзером ми все ще не маємо, бо до нього не підключена політика – поле Access policies пусте.

Перевіряємо з kubectl auth can-i:

$ kubectl auth can-i get pod
no

Додати EKS Access Policy можемо або в AWS Console:

Або знов-таки з AWS CLI і командою aws eks associate-access-policy.

Давайте поки додамо AmazonEKSViewPolicy:

$ aws --profile work eks associate-access-policy --cluster-name atlas-eks-test-1-28-cluster \                                                                    
> --principal-arn arn:aws:iam::492***148:user/test-eks-acess-TO-DEL \
> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
> --access-scope type=cluster
{
    "clusterName": "atlas-eks-test-1-28-cluster",
    "principalArn": "arn:aws:iam::492***148:user/test-eks-acess-TO-DEL",
    "associatedAccessPolicy": {
        "policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy",
        "accessScope": {
            "type": "cluster",
            "namespaces": []
        },
    ...
}

Зверніть увагу на --access-scope type=cluster – зараз ми видали ReadOnly права на весь кластер, але можемо обмежити конкретним неймспейсом(ми) – далі спробуємо.

Глянемо ще раз в AWS Console:

Access Policy додано.

Пробуємо kubectl:

$ kubectl auth can-i get pod
yes

Але не можемо створити под – бо маємо ReadOnly права:

$ kubectl auth can-i create pod
no

Інші корисні команди для AWS CLI:

Видалення default root user

На майбутнє, відключити створення такого юзеру при створенні кластера з aws eks create-cluster можна параметром bootstrapClusterCreatorAdminPermissions=false.

А зараз давайте замінимо його – додамо нашому тестовому юзеру адмін-права і видалимо дефолтного root.

Повторяємо aws eks associate-access-policy, але тепер в --policy-arn вказуємо AmazonEKSClusterAdminPolicy:

$ aws --profile work eks associate-access-policy --cluster-name atlas-eks-test-1-28-cluster \
> --principal-arn arn:aws:iam::492***148:user/test-eks-acess-TO-DEL \
> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
> --access-scope type=cluster

І що з правами тепер?

$ kubectl auth can-i create pod
yes

Тепер маємо двох cluster-admin:

І можемо видалити старого:

$ aws --profile work eks delete-access-entry --cluster-name atlas-eks-test-1-28-cluster --principal-arn arn:aws:iam::492***148:role/tf-admin

Namespaced EKS Access Entry

Замість того, аби видавати права на весь кластер з --access-scope type=cluster – ми можемо зробити юзера адміном тільки у конкретних неймспейсах.

Не відключаємо нашого тестового юзера – бо наразі це наш єдиний адмін. Давайте візьмемо звичайного IAM User, то зробимо його адміном тільки в одному Kubernetes Namespace.

Створюємо новий неймспейс:

$ kk create ns test-ns
namespace/test-ns created

Створюємо нову EKS Access Entry для мого AWS IAM User:

$ aws --profile work eks create-access-entry --cluster-name atlas-eks-test-1-28-cluster --principal-arn arn:aws:iam::492***148:user/arseny

І підключаємо AmazonEKSEditPolicy, але в --access-scope задаємо тип namespace та вказуємо ім’я цього NS:

$ aws --profile work eks associate-access-policy --cluster-name atlas-eks-test-1-28-cluster \
> --principal-arn arn:aws:iam::492***148:user/arseny \
> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy \        
> --access-scope type=namespace,namespaces=test-ns

В access-scope ми можемо задати або clutser, або неймспейс. Не дуже гнучко – але для гнучкості у нас є RBAC.

Генеруємо новий kubectl context з --profile work, де profile work – мій звичайний AWS User, для якого ми створювали EKS Access Entry з AmazonEKSEditPolicy:

$ aws --profile work eks update-kubeconfig --name atlas-eks-test-1-28-cluster --alias test-cluster-arseny-user
Updated context test-cluster-arseny-user in /home/setevoy/.kube/config

Перевіряємо активний kubectl context:

$ kubectl config current-context
test-cluster-arseny-user

І перевіряємо права – спочатку в default Namespace:

$ kubectl --namespace default auth can-i create pod
no

І в тестовому неймспейсі:

$ kubectl --namespace test-ns auth can-i create pod
yes

Nice!

Все працює.

EKS Access Entry та Kubernetes RBAC

Замість того, щоб підключати EKS Access Policy від AWS, яких всього чотири – ми можемо використати звичайний механізм Kubernetes Role-Based Access Control, RBAC.

Це виглядає так:

  1. в EKS створюємо Access Entry
    1. в параметрах Access Entry вказуємо Kubernetes RBAC Group
  2. а далі за звичною схемою – використовуємо RBAC Group та Kubernetes RoleBinding

Тоді ми пройдемо аутентифікацію в AWS, після чого AWS “передасть” нас до Kubernetes, а той вже виконає авторизацію – перевірку наших прав в кластері – на основі нашої RBAC-групи.

Видаляємо створену Access Entry для мого IAM User:

$ aws --profile work eks delete-access-entry --cluster-name atlas-eks-test-1-28-cluster --principal-arn arn:aws:iam::492***148:user/arseny

Створюємо його заново, але тепер додаємо --kubernetes-groups:

$ aws --profile work eks create-access-entry --cluster-name atlas-eks-test-1-28-cluster \                                                              
> --principal-arn arn:aws:iam::492***148:user/arseny \
> --kubernetes-groups test-eks-rbac-group

Глянемо в AWS Console:

Пробуємо перевірити права з kubectl:

$ kubectl --namespace test-ns auth can-i create pod
no

Бо EKS Access Policy ми не додавали, і в RBAC нічого не робили.

Опишемо RoleBinding та зв’яжемо RBAC групу test-eks-rbac-group з дефолтною Kubernetes edit ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: test-eks-rbac-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: edit
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: test-eks-rbac-group

Переключаємо контекст на нашого адміна (я для цього користуюсь kubectx):

$ kx
✔ Switched to context "test-cluster-test-user".

І створюємо RoleBinding в неймспейсі test-ns, аби дати юзеру права Edit тільки в цьому NS:

$ kubectl --namespace test-ns apply -f test-rbac.yml 
rolebinding.rbac.authorization.k8s.io/test-eks-rbac-binding created

Переключаємось на юзера arseny:

$ kx
✔ Switched to context "test-cluster-arseny-user".

І знову перевіряємо права в двох неймспейсах:

$ kubectl --namespace default auth can-i create pod
no

$ kubectl --namespace test-ns auth can-i create pod
yes

RBAC працює.

Ну і в принципі на цьому все.

Механізм працює, основні його сутності в роботі побачили – тепер можна переходити до Terraform.