Перейти до вмісту

Модуль 4.5: Сервісні акаунти (ServiceAccounts)

Hands-On Lab Available
K8s Cluster intermediate 45 min
Launch Lab ↗

Opens in Killercoda in a new tab

Складність: [СЕРЕДНЯ] — важлива тема для доступу до API та ідентифікації робочих навантажень

Час на проходження: 40–55 хвилин

Передумови: Модуль 4.4: Контексти безпеки (SecurityContexts), базові маніфести Pod’ів та словник RBAC


Результати навчання

Розділ «Результати навчання»

Після завершення цього модуля ви зможете:

  • Впровадити власний сервісний акаунт та призначити робочим навантаженням використовувати цю ідентичність замість стандартного акаунту простору імен.
  • Порівняти патерни стандартного сервісного акаунту, власного акаунту та акаунту без токена, вирішуючи, чи має Под отримувати облікові дані Kubernetes API.
  • Налагодити збої викликів API всередині кластера, простежуючи ідентичність Под’а, проєкцію токена, правила Ролі та суб’єкти RoleBinding.
  • Спроєктувати конфігурацію сервісного акаунту за принципом найменших привілеїв, що надає застосунку лише ті дозволи в межах простору імен, які йому потрібні.
  • Діагностувати ризики витоку токенів та застарілих токенів, перевіряючи налаштування автомонтування, проєктовані томи та токени, запитані вручну.

Чому цей модуль важливий

Розділ «Чому цей модуль важливий»

Гіпотетичний сценарій: команда випускає невеликий вебдашборд, який лише відображає статичні сторінки стану, тож ніхто не очікує, що він спілкуватиметься з Kubernetes API. Згодом образ отримує екстрене виправлення після виявлення вади віддаленого виконання коду, і перше питання в каналі реагування на інциденти — не те, чи був скомпрометований контейнер. Краще питання — чи мав цей загалом нудний Под доступний для читання токен сервісного акаунту, змонтований у /var/run/secrets/kubernetes.io/serviceaccount/, адже цей єдиний файл може перетворити компрометацію застосунку на доступ до площини управління.

Сервісні акаунти — це рівень ідентифікації для процесів усередині Под’ів, тоді як RBAC вирішує, що ці ідентичності можуть робити після успішної автентифікації. Завдання CKAD часто перевіряють видиму частину, як-от створення сервісного акаунту або додавання serviceAccountName до специфікації Под’а, але виробничі збої зазвичай ховаються в невидимому ланцюжку між допуском, проєкцією токена та авторизацією. Вам потрібно бачити весь шлях: Под посилається на ідентичність, kubelet монтує токен, клієнт надсилає цей токен до API-сервера, а API-сервер перевіряє суб’єкти RoleBinding чи ClusterRoleBinding, перш ніж повернути дані.

Цей модуль будує цей ланцюжок по одній ланці за раз. Ви почнете з огляду автоматично створеного сервісного акаунту default, потім створите спеціалізовані ідентичності робочих навантажень, а тоді вирішите, коли монтування токена слід вимкнути взагалі. Останні розділи пов’язують цю механіку з налагодженням RBAC, адже найпоширеніша помилка з сервісними акаунтами — ставитися до автентифікації так, ніби вона є авторизацією. Kubernetes 1.35 зберігає сервісні акаунти звичними на поверхні, але його сучасна модель токенів дуже відрізняється від довгоживучих токенів на основі Secret, які використовували старіші кластери.

Сервісні акаунти як ідентичність робочого навантаження

Розділ «Сервісні акаунти як ідентичність робочого навантаження»

Kubernetes відокремлює людей-користувачів від ідентичностей робочих навантажень. Користувачі-люди зазвичай надходять із зовнішнього постачальника ідентичності, як-от OpenID Connect, інтеграція з хмарним IAM чи робочий процес із клієнтськими сертифікатами, і Kubernetes не зберігає для них нативних об’єктів User. Робочі навантаження ж потребують ідентичності, яку можна виразити як об’єкт Kubernetes, прив’язати через RBAC та впровадити в Под’и під час допуску. Сервісний акаунт — це і є такий нативний об’єкт: він представляє ідентичність процесу для коду, що виконується всередині кластера.

Ця відмінність має значення, бо Под — не людина, навіть якщо маніфест створила людина. Якщо контролер Деплойменту створює десять реплік, кожна репліка потребує узгодженої ідентичності, яка переживає перезапуски й переплановування. Об’єкт сервісного акаунту дає контролеру ім’я, яке можна вписати в кожен шаблон Под’а, тоді як самі облікові дані постачаються пізніше механізмом проєкції токенів. Уявіть сервісний акаунт як запис у реєстрі перепусток, а проєктований токен — як тимчасову перепустку, надруковану на одну зміну.

Кожен простір імен отримує сервісний акаунт із назвою default під час свого створення. Якщо ви створюєте Под, не вказуючи spec.serviceAccountName, контролер допуску сервісних акаунтів змінює Под і призначає йому стандартний акаунт простору імен. Стандартна ідентичність зазвичай не має корисних дозволів RBAC, окрім базового виявлення API, але вона все одно є автентифікованою ідентичністю й все одно може отримати змонтований токен, якщо автомонтування не вимкнено. Саме тому «немає явного сервісного акаунту» — це не те саме, що «немає ідентичності».

Terminal window
# Перегляд стандартного ServiceAccount (Kubernetes 1.35 — лише NAME і AGE)
kubectl get serviceaccount
# NAME AGE
# default 10d
# describe не показує застарілого токена Secret — Под'и все одно отримують проєктовані токени
kubectl describe sa default
# Name: default
# Namespace: default
# Tokens: <none>
# Або огляньте об'єкт безпосередньо
kubectl get sa default -o yaml

Інженери, які вивчали Kubernetes до версії 1.24, часто очікують стовпець SECRETS у виводі kubectl get serviceaccount. Цей стовпець було вилучено в Kubernetes 1.24+, тож сучасні кластери — зокрема 1.35 — виводять лише NAME та AGE. kubectl describe sa default може показувати Tokens: <none>, навіть коли допущені Под’и все одно отримують прив’язані, проєктовані токени в /var/run/secrets/kubernetes.io/serviceaccount/. Відсутність застарілого токена Secret на об’єкті сервісного акаунту не означає, що робоче навантаження не має облікових даних API; це означає, що Kubernetes більше не створює автоматично довгоживучі Secret’и типу kubernetes.io/service-account-token для кожного сервісного акаунту.

Коли Под існує, ви можете підтвердити, яку ідентичність він отримав, прочитавши специфікацію Под’а або скориставшись kubectl describe. Це часто найшвидша перша перевірка під час екзаменаційного питання з налагодження, бо вона підказує, чи налагоджуєте ви заплановану ідентичність, чи стандартний акаунт простору імен. Маніфест може виглядати правильно в Git, тоді як насправді працює старіший ReplicaSet, застарілий Под або створений вручну налагоджувальний Под із чимось іншим.

Terminal window
# Перевірте ServiceAccount Под'а
kubectl get pod my-pod -o jsonpath='{.spec.serviceAccountName}'
# default
# Або в describe
kubectl describe pod my-pod | grep "Service Account"

Зробіть паузу й передбачте: якщо маніфест Под’а залишає serviceAccountName порожнім, що ви маєте очікувати від цієї команди після того, як допуск прийняв Под? Корисна відповідь — не лише «default». Глибша відповідь у тому, що значення додано на боці сервера, тож рішення про ідентичність відбулося, навіть попри те, що це поле було відсутнє у файлі, який ви подали.

Послідовність допуску достатньо сувора, щоб зупинити поганий Под ще до планування. Спершу контролер допуску сервісних акаунтів перевіряє, чи Под називає сервісний акаунт. Якщо поле порожнє, він вписує default; якщо поле задане, він зберігає цю назву. Далі він пересвідчується, що згаданий сервісний акаунт існує в тому самому просторі імен, що й Под, адже сервісні акаунти — це об’єкти простору імен, і Под не може напряму використовувати сервісний акаунт з іншого простору імен.

Після того як ідентичність існує, допуск оцінює правила монтування токена як з Под’а, так і з сервісного акаунту. Якщо монтування дозволене, специфікація допущеного Под’а містить проєктований том, який kubelet наповнить токеном, файлом простору імен та сертифікатом CA. Саме тому помилки сервісних акаунтів можуть проявлятися як помилки допуску, несподіванки на етапі планування або збої API на рівні застосунку — залежно від того, де розривається ланцюжок. Ім’я ідентичності, змонтовані облікові дані та правила авторизації — це окремі перевірки.

Аналогія з перепусткою працівника корисна, якщо тримати її точною. Сервісний акаунт схожий на запис про працівника, який каже «це робоче навантаження — читач інвентарю», а не на майстер-ключ до всієї будівлі. Проєктований токен — це перепустка, надрукована на поточну зміну, а RBAC — система контролю доступу на кожних дверях. Стандартна перепустка може провести вас до вестибюля, але вона не повинна відчиняти серверну, якщо хтось навмисно не прив’язав до неї дозволи.

Створення та призначення спеціалізованих ідентичностей

Розділ «Створення та призначення спеціалізованих ідентичностей»

Найменші привілеї починаються з називання тієї ідентичності, яку має використовувати робоче навантаження. Якщо кожен Под працює як default, вивід аудиту не може відрізнити вебфронтенд від завдання міграції, а пізніша прив’язка RoleBinding до default розширює доступ для кожного робочого навантаження, яке забуло відмовитися. Спеціалізований сервісний акаунт робить намір видимим у маніфестах, суб’єктах RBAC, журналах аудиту та нотатках реагування на інциденти. Назва має описувати роль застосунку, а не розробника, який її створив.

Імперативна команда доречна для швидких екзаменаційних завдань, тимчасових лабораторних середовищ та зосередженого налагодження. Вона негайно створює об’єкт і дозволяє перейти до маніфесту Под’а чи Деплойменту, який його споживає. У довгоживучих середовищах ту саму ідентичність усе одно слід представити декларативно в системі контролю версій, адже RBAC та ідентичність робочого навантаження є конфігурацією, важливою для безпеки, а не одноразовим станом кластера.

Terminal window
# Створення ServiceAccount
kubectl create serviceaccount my-app-sa
# У конкретному просторі імен
kubectl create sa my-app-sa -n my-namespace

Коротка назва ресурсу sa — це псевдонім ресурсу Kubernetes, а не псевдонім оболонки, тож вона залишається виконуваною в неінтерактивних оболонках. Для ясності в навчальному матеріалі все одно варто знати обидві форми, бо екзамен CKAD та офіційні приклади часто використовують скорочені назви ресурсів. Важливе правило — двійковий файл залишається kubectl; не покладайтеся на функцію оболонки чи локальний псевдонім, щоб скопійовані приклади працювали.

Декларативна ідентичність навмисно невелика. Об’єкт сервісного акаунту сам по собі не містить правил дозволів, і його не слід трактувати як місце для приховування облікових даних. Його головне завдання — надати суб’єкт у межах простору імен, на який можуть посилатися Под’и і який може прив’язувати RBAC. Тому блок metadata — найважливіша частина: назвіть його чітко, помістіть у той самий простір імен, що й робоче навантаження, і тримайте мітки власності узгодженими з рештою застосунку.

apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default

Призначення Под’а до ідентичності виконується через spec.serviceAccountName. Це поле розташоване на рівні специфікації Под’а, а не всередині контейнера, бо всі контейнери в Под’і поділяють одну й ту саму ідентичність Kubernetes. Sidecar’и, init-контейнери та основний контейнер застосунку — усі бачать той самий проєктований том сервісного акаунту, якщо окремі монтування томів чи налаштування автомонтування не блокують доступ. Ця спільна ідентичність — причина уникати поміщення непов’язаних процесів в один Под.

apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-app-sa # Use this ServiceAccount
containers:
- name: app
image: curlimages/curl
command: ["sleep", "3600"]

Для Деплойментів, DaemonSet’ів, StatefulSet’ів, Job’ів та CronJob’ів сервісний акаунт належить до шаблону Под’а. Розміщення serviceAccountName на самому об’єкті контролера не спрацює, бо контролер — не той процес, якому потрібна ідентичність під час виконання. Контролер штампує Под’и зі spec.template, тож ідентичність має бути всередині spec.template.spec. Коли ви змінюєте це поле в Деплойменті, очікуйте новий ReplicaSet та розгортання, бо шаблон Под’а змінився.

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
serviceAccountName: my-app-sa # Pod template uses this SA
containers:
- name: app
image: curlimages/curl
command: ["sleep", "3600"]

Перш ніж це запускати, який вивід ви очікуєте від kubectl get pod my-pod -o jsonpath='{.spec.serviceAccountName}' після того, як наведений вище Под допущено? Якщо ви очікуєте my-app-sa, ви правильно простежуєте шлях допуску. Якщо команда виводить default, перевірте, чи застосували ви той маніфест, який думаєте, що застосували, чи був Под перестворений після редагування шаблону Деплойменту і чи назва сервісного акаунту існує в тому самому просторі імен.

Тут є тонке операційне правило: зміна сервісного акаунту на наявному «голому» Под’і — не той спосіб, яким виправляють робоче навантаження, що вже працює. Специфікації Под’ів здебільшого незмінні після створення, а контролери перестворюють Под’и із шаблонів. На практиці ви оновлюєте шаблон Деплойменту, Job’а чи StatefulSet’а, даєте контролеру створити нові Под’и, а тоді перевіряєте нові Под’и, а не старі. Це дозволяє уникнути поширеної екзаменаційної пастки, коли маніфест виправлено, але спостережуваний Под усе ще зі старого шаблону.

Назви сервісних акаунтів також стають частиною слідів аудиту. Автентифікація Kubernetes ідентифікує сервісний акаунт як ім’я користувача на кшталт system:serviceaccount:namespace:name, і перевірки авторизації використовують цей суб’єкт. Коли журнали аудиту показують, що system:serviceaccount:payments:invoice-reader отримав перелік ConfigMap’ів, ім’я дає вам відправну гіпотезу. Коли суб’єкт — лише system:serviceaccount:payments:default, вам спершу доведеться з’ясувати, яке саме робоче навантаження насправді стояло за викликом.

Проєкція токенів та виклики API зсередини Под’а

Розділ «Проєкція токенів та виклики API зсередини Под’а»

Називання ідентичності — лише перша половина автентифікації. Процес має пред’явити доказ цієї ідентичності, коли викликає API-сервер, і Kubernetes постачає цей доказ через проєктований том. За замовчуванням kubelet монтує файли в /var/run/secrets/kubernetes.io/serviceaccount/ усередині кожного контейнера. Клієнтські бібліотеки знають цей шлях, тому клієнти всередині кластера зазвичай можуть виявити API-сервер та автентифікуватися без написаного вручну файлу kubeconfig.

Змонтований каталог зазвичай містить три корисні файли. Токен — це JSON Web Token, який використовується в HTTP-заголовку Authorization: Bearer. Сертифікат CA дозволяє клієнтові перевірити, що https://kubernetes.default.svc справді є API-сервером кластера. Файл простору імен повідомляє застосунку, в якому просторі імен він працює, що допомагає клієнтським бібліотекам та невеликим скриптам уникати жорстко закодованих значень простору імен.

Terminal window
# Перегляд змонтованих файлів токена
kubectl exec my-pod -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# ca.crt
# namespace
# token
# Перегляд токена
kubectl exec my-pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
ФайлПризначення
tokenJWT-токен для автентифікації в API
ca.crtСертифікат CA для перевірки API-сервера
namespaceПростір імен Под’а

Таблиця виглядає простою, але кожен файл відповідає на інше питання. Токен відповідає «хто викликає?». Сертифікат CA відповідає «чи спілкуюсь я зі справжнім API-сервером?». Файл простору імен відповідає «де я працюю?». Втрата будь-якої з цих частин змінює характер збою. Відсутній токен спричиняє помилки автентифікації, відсутній CA може спричинити помилки перевірки TLS, а відсутній простір імен часто змушує клієнтів звертатися до неправильного шляху або вимагати явного аргументу простору імен.

Токен сервісного акаунту — це JWT, тож він має заголовок, корисне навантаження (payload) та підпис. Корисне навантаження містить твердження про сервісний акаунт, простір імен, Под, аудиторію та термін дії. Підпис дозволяє API-серверу виявити підробку, а отже редагування тексту токена всередині контейнера не підвищує привілеїв. Якщо зловмисник змінює твердження з однієї назви сервісного акаунту на іншу, перевірка підпису провалюється, і автентифікацію відхиляють ще до того, як буде оцінено RBAC.

Сучасні токени Kubernetes є прив’язаними та короткоживучими. Токен, який Под отримує через стандартний проєктований том, прив’язаний до Под’а й має термін дії, а kubelet оновлює його, перш ніж він стане недійсним. Добре написані клієнти Kubernetes перечитують токен або дозволяють офіційній клієнтській бібліотеці керувати ротацією. Скрипт, який читає токен один раз на старті процесу, а потім працює багато годин, може згодом збоїти із заплутаними помилками автентифікації, якщо ніколи не перечитує файл.

Коли робочому навантаженню справді потрібно викликати API-сервер без клієнтської бібліотеки, необхідні HTTP-складники видимі. Скрипт читає токен, використовує сертифікат CA для перевірки TLS, читає свій простір імен і надсилає токен як облікові дані типу bearer. Це не бажаний патерн для складних застосунків, але це чудова налагоджувальна модель, бо вона точно показує, від чого залежить автентифікація всередині кластера.

Terminal window
# Усередині Под'а, запит до API
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
# Список Под'ів у поточному просторі імен
curl -s --cacert $CACERT \
-H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods

Якщо цей запит повертає помилку автентифікації, спершу огляньте монтування токена. Якщо він повертає відповідь «заборонено» (forbidden), автентифікація, ймовірно, успішна, а RBAC відхилив дієслово чи ресурс. Ця відмінність є центральною для налагодження сервісних акаунтів: «не можу автентифікуватися» і «не авторизовано» — це різні збої. Перший вказує на проєкцію токена, налаштування автомонтування, аудиторію запиту або налаштування TLS; другий вказує на об’єкти Role, ClusterRole, RoleBinding та ClusterRoleBinding.

Для ручного тестування з робочої станції kubectl create token спілкується з TokenRequest API та просить API-сервер видати короткоживучий токен для сервісного акаунту. Команда не відтворює стару поведінку автоматичного Secret. Вона корисна під час тестування зовнішньої інтеграції або відтворення проблеми авторизації за допомогою curl, але не повинна перетворюватися на приховану систему розповсюдження облікових даних. Якщо зовнішній системі потрібен довготривалий доступ до кластера, спроєктуйте цю інтеграцію навмисно, а не копіюйте токени в довільні файли.

Terminal window
# Створення короткоживучого токена
kubectl create token my-app-sa
# Створення токена з терміном дії
kubectl create token my-app-sa --duration=1h

Повний шлях ідентичності найлегше запам’ятати як послідовність, а не як розрізнені можливості. Створіть або виберіть сервісний акаунт, призначте його шаблону Под’а, дайте проєкції токена розмістити облікові дані в контейнері, а тоді дайте робочому навантаженню пред’явити ці облікові дані API-серверу. API-сервер автентифікує токен, а потім запитує в авторизації, чи може суб’єкт виконати запитану дію.

flowchart TD
classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px;
A["1. Create ServiceAccount\n<code>kubectl create serviceaccount my-app-sa</code>"] --> B["2. Assign to Pod\n<code>spec:</code>\n<code> serviceAccountName: my-app-sa</code>"]
B --> C["3. Token Mounted Automatically\n<code>/var/run/secrets/kubernetes.io/serviceaccount/</code>\n<code>├── token ← JWT token</code>\n<code>├── ca.crt ← API CA cert</code>\n<code>└── namespace ← Pod namespace</code>"]
C --> D["4. Pod Uses Token for API Access\n<code>curl -H \"Authorization: Bearer $(cat /var/run/...)\"</code>\n<code>https://kubernetes/api/v1/...</code>"]

Зверніть увагу, що діаграма не включає створення дозволів. Ця відсутність навмисна. Токен доводить ідентичність, але не надає права отримувати перелік Под’ів, читати Secret’и чи виправляти Деплойменти. Дозволи — це окремі правила авторизації, і збереження цього розділення чітким заощадить час, коли ви зіткнетеся з відповіддю 403 Forbidden під час екзамену CKAD.

Вимкнення автомонтування та вибір терміну дії токенів

Розділ «Вимкнення автомонтування та вибір терміну дії токенів»

Безпечне значення за замовчуванням для багатьох застосунків — взагалі жодних облікових даних Kubernetes API. Статичний вебсервер, воркер, який спілкується лише з базою даних, або застосунок, що отримує конфігурацію через змінні середовища, можуть не потребувати викликів до API-сервера. Якщо таке робоче навантаження все одно отримує токен, то кожна вразливість застосунку має другий наслідок: читач чи зловмисник, який дістанеться файлової системи контейнера, також зможе спробувати скористатися обліковими даними кластера.

Kubernetes дає вам два місця, де можна вимкнути автоматичне монтування токена. Поле на рівні Под’а — найсильніше локальне твердження, бо воно керує саме цим Под’ом. Поле на рівні сервісного акаунту встановлює поведінку за замовчуванням для Под’ів, які використовують цей сервісний акаунт. Коли присутні обидва, специфікація Под’а є прямим рішенням на рівні робочого навантаження, тож її найлегше помітити під час перегляду маніфесту. Використовуйте її, коли робоче навантаження має бути видимо ізольоване від облікових даних API.

apiVersion: v1
kind: Pod
metadata:
name: no-api-access
spec:
automountServiceAccountToken: false # Don't mount token
containers:
- name: app
image: curlimages/curl
command: ["sleep", "3600"]

Налаштування на рівні сервісного акаунту корисне, коли ви хочете іменовану ідентичність, але водночас хочете, щоб монтування токена було опційним. Це звучить незвично, доки ви не врахуєте контролери, sidecar’и вебхуків чи простори імен з обмеженими політиками, де мітки ідентичності та угоди про власність важливі навіть для робочих навантажень, які не повинні викликати API. Обмежений сервісний акаунт може виражати «це робоче навантаження належить цьому застосунку», водночас відмовляючись від стандартного монтування облікових даних.

apiVersion: v1
kind: ServiceAccount
metadata:
name: restricted-sa
automountServiceAccountToken: false # Default for pods using this SA

Обирайте налаштування на рівні Под’а, переглядаючи окреме робоче навантаження, та налаштування на рівні сервісного акаунту, визначаючи ідентичність застосунку, що використовується багатьма Под’ами. Якщо Деплоймент має змішані контейнери і лише одному sidecar’у потрібен API, не припускайте, що спільна ідентичність Под’а є нешкідливою. Оскільки всі контейнери в Под’і поділяють той самий том токена сервісного акаунту, якщо ви не спроєктуєте власні монтування, непов’язаний контейнер може стати частиною межі довіри.

Сучасні прив’язані токени також дозволяють запитувати конкретні аудиторії та терміни дії. Аудиторія обмежує те, хто має приймати токен. Токен, призначений для API-сервера Kubernetes, не повинен автоматично прийматися зовнішньою системою секретів, а токен, призначений для зовнішньої інтеграції, не повинен автоматично прийматися деінде. Прив’язка до аудиторії перетворює токен із загальної перепустки на перепустку, надруковану для конкретного столу.

# Request token with specific audience
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-app-sa
containers:
- name: app
image: my-app
volumeMounts:
- name: token
mountPath: /var/run/secrets/tokens
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600 # 1 hour
audience: my-audience

Цей проєктований том відрізняється від стандартного шляху монтування, тож застосунок має бути налаштований на читання токена з власного розташування. Це корисно, коли sidecar’у чи допоміжному процесу потрібен токен для конкретної аудиторії, тоді як основний застосунок не повинен використовувати стандартний шлях клієнтської бібліотеки. Чим конкретніші монтування, аудиторія та термін дії, тим менший радіус ураження, якщо саме цей файл буде розкрито.

Історично, до того як Kubernetes 1.24 припинив автоматично створювати Secret’и токенів сервісних акаунтів, кластери часто містили довгоживучі Secret’и токенів для кожного сервісного акаунту. Ті токени були зручними, але ризикованими, бо скопійований Secret міг залишатися дійсним, доки хтось його не видалить чи не зротує ключі підпису. У Kubernetes 1.35 ви не повинні відтворювати цей застарілий патерн Secret для нових робочих навантажень.

kubernetes.io/service-account-token
# Legacy credential model (deprecated — do not use in 1.35)
# Pre-1.24 clusters auto-created Secrets of type:
# Manually creating that Secret type revives long-lived bearer credentials.

Для налагодження з робочої станції kubectl create token — це сучасний підхід. Він викликає TokenRequest API та повертає короткоживучий токен типу bearer. Це не заміна проєктуванню зовнішньої ідентичності, але це правильний інструмент, коли вам потрібно відтворити проблему авторизації за допомогою curl поза Под’ом.

Terminal window
# Current debugging approach (TokenRequest API — short-lived token)
kubectl create token my-app-sa
# Optional expiration for manual tests
kubectl create token my-app-sa --duration=1h

Поганою звичкою є використання kubectl create token як заміни старих постійних Secret’ів токенів — а не сама команда. Це інструмент налагодження та інтеграції, а не привід вставляти токени типу bearer у репозиторії конфігурацій. Якщо людина просить токен сервісного акаунту, який ніколи не завершується, ваш перегляд проєкту має зробити паузу й запитати, яка система зберігатиме, ротуватиме, обмежуватиме та аудитуватиме ці облікові дані.

Який підхід ви б обрали тут і чому: пакетний Job’у потрібно отримати перелік ConfigMap’ів у власному просторі імен протягом двох хвилин на старті, тоді як вебфронтенд у тому самому просторі імен ніколи не викликає API? Сильна відповідь створює власний сервісний акаунт для Job’а, прив’язує лише потрібний дозвіл на читання та вимикає автомонтування токена для фронтенду. Спільне використання default для них обох швидко розв’язує YAML і створює ширшу ідентичність, ніж потребує будь-яке з робочих навантажень.

Термін дії токена не замінює RBAC. Короткоживучий токен із широкими дозволами все одно може завдати шкоди протягом свого дійсного вікна, а вузько обмежений токен із непотрібною аудиторією все одно може прийматися більшою кількістю систем, ніж передбачалося. Ставтеся до терміну дії, аудиторії, прив’язки до об’єкта, налаштувань автомонтування та RBAC як до незалежних рівнів. Кожен рівень має відповідати на одне питання: як довго ці облікові дані дійсні, хто має їх приймати, який об’єкт ними володіє, де вони змонтовані та що вони можуть робити?

Налагодження RBAC: ідентичність — це не авторизація

Розділ «Налагодження RBAC: ідентичність — це не авторизація»

Найважливіше речення в налагодженні сервісних акаунтів таке: автентифікація повідомляє Kubernetes, хто викликає, а авторизація вирішує, що той, хто викликає, може робити. Под може мати правильний сервісний акаунт, дійсний проєктований токен та справний мережевий шлях до API-сервера, але все одно отримати 403 Forbidden, бо жоден RoleBinding не надає запитаного дієслова для запитаного ресурсу. Коли робоче навантаження каже «API зламано», уважно прочитайте помилку, перш ніж змінювати налаштування ідентичності.

Суб’єкти RBAC посилаються на сервісні акаунти за іменем та простором імен. RoleBinding у просторі імен застосунку може прив’язати Роль до system:serviceaccount:that-namespace:that-name через структурований запис суб’єкта. ClusterRoleBinding може надавати дозволи на рівні кластера чи між просторами імен, що іноді потрібно для контролерів, але є занадто широким для звичайних Под’ів застосунків. Для проєктування застосунків рівня CKAD надавайте перевагу Ролі та RoleBinding у просторі імен, якщо вимога справді не перетинає меж просторів імен.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: default
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader-to-my-app
namespace: default
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

Цей маніфест надає лише три дієслова на лише основний ресурс pods у просторі імен default. Він не надає доступу до Secret’ів, Деплойментів, Нод чи Под’ів в іншому просторі імен. Він також не надає дозволу оновлювати Под’и, тобто робоче навантаження, яке згодом спробує виправити мітки, усе одно зазнає невдачі. Це хороша поведінка найменших привілеїв, а не ознака того, що прив’язка зламана.

Ви можете протестувати авторизацію поза Под’ом за допомогою перевірок у стилі імперсонації. kubectl auth can-i може запитати, чи може названий суб’єкт сервісного акаунту виконати дієслово над ресурсом у просторі імен. Це швидше, ніж багаторазово розгортати контейнер та читати журнали застосунку, і це робить суб’єкт явним. Коли відповідь — «ні», огляньте суб’єкти RoleBinding та правила Ролі, перш ніж торкатися проєкції токена.

Terminal window
kubectl auth can-i list pods \
--as=system:serviceaccount:default:my-app-sa \
-n default
kubectl auth can-i get secrets \
--as=system:serviceaccount:default:my-app-sa \
-n default

Налагодження всередині кластера дотримується того самого розділення. Спершу перевірте ідентичність за специфікацією Под’а: чи відповідає spec.serviceAccountName запланованому сервісному акаунту? Далі перевірте файли токена: чи має контейнер очікуваний проєктований том та шлях до токена? Тоді перевірте авторизацію: чи може суб’єкт виконати дієслово над ресурсом? Порядок має значення, бо зміна RBAC не може виправити відсутній токен, а зміна монтувань токена не може виправити відсутній RoleBinding.

SelfSubjectAccessReview — ще одна корисна концепція, бо вона дозволяє автентифікованому клієнтові запитати в API-сервера, чи може він виконати дію. Фреймворки застосунків можуть використовувати цей патерн, коли їм потрібно вмикати чи вимикати функції залежно від дозволів. Для того, хто навчається, ключова ідея в тому, що перевірка виконується як поточний користувач чи сервісний акаунт. Це не чарівний обхід; вона просто просить рівень авторизації пояснити рішення «так чи ні» для поточної ідентичності того, хто викликає.

apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
resourceAttributes:
namespace: default
verb: list
resource: pods

Гіпотетичний сценарій: контролер успішно запускається після того, як ви призначаєте my-app-sa, але його журнали показують forbidden: User "system:serviceaccount:default:my-app-sa" cannot list resource "pods" in API group "" in the namespace "default". Призначення сервісного акаунту спрацювало, бо API-сервер називає цей точний суб’єкт у помилці. Виправлення — не ще один токен чи ще один перезапуск Под’а; виправлення — це правило RBAC та прив’язка, які надають конкретне дієслово та ресурс.

Трапляється й зворотна проблема. Робоче навантаження може мати RoleBinding, але все одно зазнавати невдачі з помилкою автентифікації, бо автомонтування вимкнено або токен із власною аудиторією пред’являється неправильному отримувачу. У цьому разі додавання ширшого RBAC лише розширює радіус ураження, не виправляючи облікові дані. Хороше налагодження тримає рівні розділеними: ім’я ідентичності, наявність токена, дійсність токена, аудиторія та авторизація RBAC.

Сервісні акаунти також взаємодіють із межами просторів імен. RoleBinding живе в одному просторі імен і надає дозволи в межах цього простору імен, але його суб’єкт може посилатися на сервісний акаунт з іншого простору імен. Це посилання на суб’єкт між просторами імен є законним, але воно має бути рідкісним у проєктуванні застосунків, бо ускладнює міркування про власність. Для завдань CKAD тримайте сервісний акаунт, Роль, RoleBinding та робоче навантаження в тому самому просторі імен, якщо в завданні явно не сказано інакше.

Аудит наявного стану простору імен

Розділ «Аудит наявного стану простору імен»

Проєктувати новий сервісний акаунт чистіше, ніж аудитувати старий простір імен, бо новий проєкт можна почати з найменших привілеїв. Наявні простори імен мають історію: тимчасові Job’и, покинуті RoleBinding’и, міграції контролерів та Под’и, які тихо повернулися до default. Тому корисний аудит починається з відокремлення інвентаризації від оцінки. Спершу перелічіть, які ідентичності існують і які робочі навантаження на них посилаються. Лише коли ця інвентаризація стане ясною, варто вирішувати, яким ідентичностям потрібен RBAC, яким не потрібен токен, а які слід видалити після того, як власники робочих навантажень підтвердять, що вони не використовуються.

Перше питання інвентаризації просте: які сервісні акаунти існують у просторі імен і чи відповідають їхні назви реальним ролям робочих навантажень? Назви на кшталт web, worker, backup-reader чи metrics-scraper дають рецензентам корисні підказки. Назви на кшталт test, new-sa чи admin-sa — це сигнали ризику, бо вони не пояснюють робоче навантаження чи межу дозволів. Ви не доводите незахищеність лише з назви, але неясні назви підвищують імовірність того, що широкі дозволи виживуть, бо ніхто не хоче зламати невідому залежність.

Друге питання інвентаризації — які Под’и насправді використовують кожну ідентичність. Сервісний акаунт без поточних Под’ів усе одно може бути потрібен CronJob’у, але невикористовувана ідентичність із потужними прив’язками заслуговує на ближчий погляд. Под, що використовує default, не є автоматично вразливим, проте це привід запитати, чи застосунок навмисно обрав стандартний акаунт простору імен, чи просто успадкував його через упущення. Ця відмінність важлива під час очищення, бо успадковані стандартні значення часто приховують випадкові шляхи доступу.

Далі йде стан автомонтування. Для кожного робочого навантаження запитайте, чи є в застосунку причина викликати Kubernetes API. Якщо відповідь — ні, правильним висновком аудиту є не «сервісний акаунт повинен мати менше дозволів», а «Под не повинен отримувати токен». Видалення монтування сильніше, ніж надання порожньої Ролі, бо воно прибирає облікові дані типу bearer з файлової системи. RBAC усе одно має значення, але відсутній токен — це чистіший засіб контролю для робочих навантажень без API.

Коли робоче навантаження справді потребує доступу до API, аудитуйте найменший повний шлях, а не лише Роль. Шаблон Под’а повинен називати спеціалізований сервісний акаунт, сервісний акаунт повинен бути в очікуваному просторі імен, токен повинен монтуватися лише там, де потрібно, а RoleBinding повинен прив’язувати цей суб’єкт до вузької Ролі. Невідповідність у будь-якій точці послаблює картину. Наприклад, вузька Роль, прив’язана до default, усе одно ризикована, бо суб’єкт є спільним, тоді як добре названий сервісний акаунт із широким ClusterRoleBinding може бути занадто потужним.

Аудит застарілих токенів усе ще актуальний на оновлених кластерах. Kubernetes 1.35 не створює автоматично Secret’и токенів старого стилю для кожного сервісного акаунту, але створені вручну Secret’и токенів сервісних акаунтів усе ще можуть існувати в деяких середовищах задля сумісності. Під час аудиту шукайте Secret’и типу kubernetes.io/service-account-token та запитуйте, чому кожен із них існує. Задокументована інтеграція для початкового завантаження — це одне; непояснений довгоживучий Secret токена поруч із простором імен застосунку — це інше.

Проєктовані власні токени заслуговують на власну перевірку, бо вони часто вказують на нестандартну інтеграцію. Под, який проєктує токен у /var/run/secrets/tokens із власною аудиторією, ймовірно, спілкується з конкретним отримувачем, як-от зовнішньою системою ідентичності чи секретів. Питання аудиту — чи відповідають аудиторія, термін дії та шлях монтування цій інтеграції. Токен із нечіткою аудиторією або монтуванням, спільним для кожного контейнера в Под’і, може бути занадто дозвільним, навіть коли правила RBAC вузькі.

Висновки аудиту слід писати як перевірювані твердження. «Стандартний сервісний акаунт — це погано» — занадто розпливчасто, щоб діяти. «Деплоймент frontend не має залежності від Kubernetes API і все одно отримує стандартне монтування токена» — придатне для дії, бо виправлення — це зміна шаблону Под’а. «RoleBinding read-all надає list на Под’и суб’єкту system:serviceaccount:default:default» — придатне для дії, бо виправлення — це названий сервісний акаунт та вужчий суб’єкт. Хороші висновки вказують на точний рівень, що зазнав збою.

Є також позитивний результат аудиту, який варто записати: деякі робочі навантаження повинні зберегти свій доступ через сервісний акаунт. Контролери, оператори, збирачі метрик та Job’и, які перевіряють ресурси Kubernetes, потребують облікових даних API для виконання своїх завдань. Мета аудиту — не прибрати кожен токен; вона в тому, щоб зробити кожен токен пояснюваним. Сервісний акаунт прийнятний, коли його робоче навантаження, монтування токена, аудиторія токена та дозволи RBAC утворюють узгоджений ланцюжок, який інший інженер може перевірити пізніше.

Для практики CKAD перетворіть аудит на повторюваний чеклист. Визначте ідентичність Под’а, перевірте, чи існує шлях до токена, вирішіть, чи потребує робоче навантаження API, огляньте суб’єкти RoleBinding та запустіть перевірку авторизації для точного дієслова та ресурсу. Ця послідовність швидша, ніж читання кожного файлу YAML згори донизу, бо вона дотримується того самого шляху, яким іде запит до API. Вона також запобігає поширеній помилці виправлення того рівня, який найлегше редагувати, замість того рівня, який насправді зазнав збою.

Очищення потребує такої самої обережності, як і створення. Видалення невикористовуваного сервісного акаунту перед видаленням чи оновленням контролера, який на нього посилається, може спричинити відхилення нових Под’ів під час допуску, бо контролер допуску сервісних акаунтів перевіряє, чи існує названа ідентичність. Безпечніша послідовність очищення — спершу оновити шаблон робочого навантаження, поспостерігати, як нові Под’и стають справними, підтвердити, що жоден поточний шаблон контролера не посилається на стару ідентичність, і лише тоді прибрати застарілий сервісний акаунт та прив’язки.

Під час перегляду звертайте увагу на мітки власності та історію Git навколо об’єктів сервісних акаунтів. Ідентичність, створену Helm-чартом, оператором чи платформним доповненням, може узгоджувати інший контролер, тож ручні правки можуть бути скасовані чи перезаписані. Це не означає, що доступ прийнятний; це означає, що виправлення належить до значень вищого чарту, конфігурації оператора чи політики платформи. Сервісні акаунти — це об’єкти безпеки, але вони також є частиною системи доставки застосунку.

Нарешті, пишіть рекомендації аудиту так, щоб зберігати намір розробника. Якщо власник застосунку каже, що робоче навантаження викликає API, щоб виявити сусідні Под’и, запитайте, який ресурс, дієслово, простір імен та клієнтський шлях задіяні. Така розмова часто перетворює розпливчасте прохання про «доступ до API» на Роль із трьома дієсловами або на рішення взагалі прибрати залежність від API. Найцінніший перегляд сервісного акаунту — той, що перетворює припущений дозвіл на заявлену, перевірювану вимогу.

Тримайте остаточну нотатку аудиту поблизу маніфесту робочого навантаження, бо рішення щодо сервісних акаунтів швидко застарівають, коли вони живуть лише в коментарі до тікета. Майбутній супровідник повинен мати змогу прочитати шаблон Под’а, сервісний акаунт та RoleBinding разом і зрозуміти, чому токен існує або чому він навмисно відсутній.

Патерни та антипатерни

Розділ «Патерни та антипатерни»

Проєктування сервісного акаунту здебільшого про зменшення неоднозначності. Хороший патерн робить очевидним, яке робоче навантаження викликає API, який простір імен володіє ідентичністю, які шляхи токенів присутні та які правила RBAC пояснюють успішні запити. Поганий патерн приховує ці відповіді за стандартними значеннями, спільними ідентичностями чи широкими ClusterRoleBinding’ами. Таблиця нижче зосереджується на рішеннях, що змінюють операційну поведінку, а не на стильових уподобаннях.

Патерн чи антипатернКоли він з’являєтьсяЧому він працює чи дає збійКраще рішення
Патерн: один сервісний акаунт на роль застосункуУ робочого навантаження є конкретне завдання API, як-от читання ConfigMap’ів чи перелік Под’івЖурнали аудиту, RoleBinding’и та реагування на інциденти — усі вказують на ту саму іменовану ідентичністьНазвіть сервісний акаунт за роллю робочого навантаження та прив’яжіть лише потрібні дієслова
Патерн: вимкнути автомонтування для Под’ів без APIСтатичні вебсервери, прості воркери та застосунки, що отримують конфігурацію без викликів APIВидалення токена прибирає одні облікові дані площини управління з файлової системи контейнераЗадайте automountServiceAccountToken: false на Под’і чи сервісному акаунті
Патерн: Роль у межах простору імен плюс RoleBindingЗастосунку потрібні ресурси лише у власному просторі іменМежа дозволів слідує за межею власності застосункуНадавайте перевагу RoleBinding над ClusterRoleBinding, якщо вимога не перетинає простори імен
Антипатерн: прив’язка дозволів до defaultКоманда хоче швидко змусити збійний Под працюватиКожне робоче навантаження, яке забуло сервісний акаунт, може успадкувати новий доступСтворіть іменований сервісний акаунт і прив’яжіть цей суб’єкт натомість
Антипатерн: використання kubectl create token як фабрики секретівЗовнішній скрипт просить токен типу bearer, а плану ротації немаєКороткоживучі токени стають скопійованими обліковими даними з неясним зберіганням та поведінкою оновленняВикористовуйте TokenRequest навмисно або інтегруйтеся з постачальником ідентичності, спроєктованим для цієї системи
Антипатерн: трактування 403 Forbidden як проблеми токенаПод має токен, але йому бракує запитаного правила RBACПерестворення Под’ів чи токенів не змінює авторизаціюСкористайтеся kubectl auth can-i та огляньте суб’єкти RoleBinding і правила Ролі
Антипатерн: спільне використання ідентичності Под’а непов’язаними контейнерамиSidecar’у потрібен доступ до API, а основному застосунку — ніУсі контейнери часто можуть дістатися того самого шляху змонтованого токенаРозділіть робочі навантаження або змонтуйте конкретний проєктований токен лише там, де він потрібен

Міркування щодо масштабування — це придатність до перегляду. Малі кластери можуть пережити кілька заплутаних виборів сервісних акаунтів, бо та сама людина може знати кожне робоче навантаження. Більші кластери потребують назв, просторів імен, міток та прив’язок, щоб пояснювати себе без приватної пам’яті. Тому патерн сервісного акаунту з найменшими привілеями стосується не лише обмеження зловмисників; він також робить майбутнє налагодження дешевшим.

Рамка ухвалення рішень

Розділ «Рамка ухвалення рішень»

Починайте з поведінки застосунку, а не з об’єкта Kubernetes. Якщо процес не викликає Kubernetes API, оберіть жодного змонтованого токена й задокументуйте це рішення у специфікації Под’а чи сервісного акаунту. Якщо процес викликає API лише у власному просторі імен, створіть власний сервісний акаунт та RoleBinding простору імен. Якщо процес спостерігає за ресурсами рівня кластера чи кількома просторами імен, пригальмуйте й трактуйте проєкт як доступ рівня контролера, а не як звичайний доступ застосунку.

ПитанняОберіть цеКомпроміс, який треба прийняти
Чи робоче навантаження ніколи не викликає Kubernetes API?Вимкніть автомонтування токенаДеякі клієнтські бібліотеки всередині кластера зазнають невдачі, доки їх не переналаштувати, що є очікуваним сигналом
Чи потрібні йому лише читання в межах простору імен?Власний сервісний акаунт плюс Роль і RoleBindingБільше YAML, ніж використання default, але вивід аудиту стає змістовним
Чи потрібно йому оновлювати ресурси?Додайте лише потрібні дієслова update чи patchПомилкове дієслово може стати доступом на запис до більшої кількості об’єктів, ніж потрібно застосунку
Чи потрібен зовнішній системі токен?Використовуйте TokenRequest із явною аудиторією та терміном діїВи маєте обробляти оновлення та безпечне зберігання поза кластером
Чи потрібна йому видимість на рівні кластера?Розгляньте проєкт у стилі контролера з обережними правилами ClusterRoleClusterRoleBinding збільшує радіус ураження й має переглядатися ретельніше

Під час налагодження читайте збої через ту саму рамку у зворотному порядку. Якщо Под не може знайти файли токена, перевірте налаштування автомонтування та проєкцію тома. Якщо токен відхилено, перевірте аудиторію, термін дії та чи спрямований запит на запланованого отримувача. Якщо API-сервер називає сервісний акаунт у помилці «заборонено», перевірте RBAC. Якщо дозвіл несподівано спрацьовує, шукайте ширші ClusterRoleBinding’и чи прив’язки до стандартного акаунту простору імен.

Для швидкості на екзамені CKAD запам’ятайте компактний шлях: об’єкт сервісного акаунту, посилання шаблону Под’а, монтування токена, прив’язка RBAC, тест авторизації. Цей порядок запобігає випадковим змінам. Він також збігається з тим, як Kubernetes обробляє запит — від допуску маніфесту до авторизації виклику API, тобто кожна перевірка або підтверджує поточний рівень, або вказує на наступний.

  • Kubernetes 1.24 припинив автоматично створювати довгоживучі Secret’и токенів сервісних акаунтів та вилучив стовпець SECRETS із kubectl get serviceaccount, тож Tokens: <none> у describe не означає, що Под’и не отримують проєктованих токенів.
  • Ім’я користувача сервісного акаунту форматується як system:serviceaccount:<namespace>:<name>, і саме цей точний рядок суб’єкта ви часто бачите в помилках авторизації та подіях аудиту.
  • Прив’язані токени сервісних акаунтів стали стандартною моделлю проєкції ще до Kubernetes 1.35, тож сучасні кластери очікують ротацію та завершення токена, а не статичні файли токенів, які живуть вічно.
  • TokenRequest API підтримує аудиторії, що дозволяє одному сервісному акаунту запитати токен, призначений для Kubernetes API, та окремий токен, призначений для іншого довіреного отримувача.
ПомилкаЧому вона трапляєтьсяЯк її виправити
Надання дозволів сервісному акаунту defaultВін уже призначений Под’ам, тож перше збійне робоче навантаження швидко починає працюватиСтворіть іменований сервісний акаунт для робочого навантаження й прив’яжіть дозволи лише до цього суб’єкта
Забування serviceAccountName всередині шаблону ДеплойментуІнженери розміщують поле біля метаданих Деплойменту замість специфікації Под’аПомістіть serviceAccountName під spec.template.spec та перевірте нові Под’и після розгортання
Трактування 403 Forbidden як помилки відсутнього токенаЗастосунок каже, що виклик API провалився, але точний статус ігноруєтьсяВідокремте автентифікацію від авторизації й протестуйте суб’єкт за допомогою kubectl auth can-i
Залишення токенів змонтованими для застосунків без APIСтандартну поведінку автомонтування легко не помітити в простих Под’ахЗадайте automountServiceAccountToken: false на Под’ах чи на обмежених сервісних акаунтах
Копіювання короткоживучих токенів у зовнішню конфігураціюkubectl create token здається зручним генератором облікових данихСпроєктуйте явну аудиторію, термін дії, оновлення та зберігання або скористайтеся належною зовнішньою інтеграцією ідентичності
Прив’язка Ролі в неправильному просторі іменRoleBinding’и надають дозволи у власному просторі імен, а не там, де живе сервісний акаунтТримайте робоче навантаження, сервісний акаунт, Роль та RoleBinding разом, якщо доступ між просторами імен не є навмисним
Спільне використання однієї ідентичності Под’а непов’язаними контейнерамиSidecar’и роблять зручним пакування процесів разомРозділіть межі довіри або проєктуйте конкретний токен лише в той контейнер, який його потребує
Питання 1: Ваш Деплоймент має `serviceAccountName: report-reader` у верхньорівневій специфікації Деплойменту, але нові Под'и все одно показують сервісний акаунт `default`. Що слід оглянути та змінити?

Поле має бути під spec.template.spec, бо Деплоймент створює Под’и із шаблону Под’а. Верхньорівневе поле на Деплойменті не призначає ідентичність під час виконання. Перемістіть поле в шаблон, застосуйте маніфест та перевірте щойно створені Под’и, а не старіший Под із попереднього ReplicaSet.

Питання 2: Под може прочитати `/var/run/secrets/kubernetes.io/serviceaccount/token`, але його виклик API повертає `403 Forbidden` із зазначенням `system:serviceaccount:default:my-app-sa`. Який рівень найімовірніше неправильний?

Автентифікація працює, бо API-сервер ідентифікував точний суб’єкт сервісного акаунту. Імовірна проблема — авторизація: Role, ClusterRole, RoleBinding чи ClusterRoleBinding не надає запитаного дієслова для запитаного ресурсу. Скористайтеся kubectl auth can-i --as=system:serviceaccount:default:my-app-sa з тим самим дієсловом та простором імен, щоб підтвердити рішення RBAC.

Питання 3: Статичний Под NGINX ніколи не викликає Kubernetes API, але перегляд безпеки виявляє змонтований токен сервісного акаунту. Яку зміну проєкту вам слід зробити?

Вимкніть автомонтування токена для цього робочого навантаження, бажано безпосередньо в шаблоні Под’а через automountServiceAccountToken: false, щоб рішення було видимим під час перегляду. Якщо кілька Под’ів поділяють обмежену ідентичність, задання цього поля на сервісному акаунті також може встановити безпечніше значення за замовчуванням. Мета — прибрати непотрібні облікові дані площини управління з файлової системи контейнера.

Питання 4: Job'у потрібно отримати перелік ConfigMap'ів у своєму просторі імен під час старту, а потім він завершується. Чи слід прив'язувати цей дозвіл до сервісного акаунту `default` простору імен?

Ні. Створіть іменований сервісний акаунт для Job’а та прив’яжіть Роль, що надає лише потрібні дієслова читання ConfigMap’ів у цьому просторі імен. Прив’язка default робить кожен Под, який випадково використовує стандартний акаунт простору імен, потенційним носієм того самого дозволу, що послаблює і найменші привілеї, і ясність аудиту.

Питання 5: Ваша зовнішня інтеграція просить токен сервісного акаунту, який зберігатиметься у файлі конфігурації місяцями. Чому `kubectl create token` сам по собі не є повним проєктом?

Команда створює короткоживучий токен через TokenRequest API, тож зберігання та оновлення все одно потребують навмисного плану. Копіювання виводу в довгоживучий файл відтворює операційний ризик старих статичних облікових даних без надання вам ротації чи перегляду аудиторії. Кращий проєкт визначає аудиторію, термін дії, безпечне зберігання, оновлення та вузькі дозволи RBAC, які потрібні інтеграції.

Питання 6: Под використовує власний проєктований токен із `audience: vault`, а потім скрипт надсилає цей токен до `https://kubernetes.default.svc`. Якого збою слід очікувати і чому?

API-сервер може відхилити токен, бо аудиторія не призначена для цього отримувача. Прив’язка до аудиторії обмежує те, де токен має прийматися, тож токен, викарбуваний для зовнішньої системи, не є автоматично дійсними обліковими даними Kubernetes API. Використовуйте стандартний API-токен для викликів Kubernetes API або запитуйте токен із правильною аудиторією для запланованого отримувача.

Питання 7: RoleBinding існує в просторі імен `tools`, але Под застосунку та сервісний акаунт перебувають у просторі імен `default`, і застосунок усе одно не може отримати перелік Под'ів у `default`. У чому помилка простору імен?

RoleBinding надає дозволи в тому просторі імен, де RoleBinding існує. Якщо прив’язка в tools, вона не надає дозволів на Под’и в межах простору імен default, навіть якщо її суб’єкт посилається на сервісний акаунт із default. Помістіть Роль та RoleBinding у той простір імен, до ресурсів якого застосунок потребує доступу, або скористайтеся ретельно переглянутим ClusterRoleBinding лише тоді, коли доступ на рівні кластера справді потрібен.

Ця вправа припускає сумісний з Kubernetes 1.35 кластер та простір імен, у якому ви можете створювати Под’и, сервісні акаунти, Ролі та RoleBinding’и. Використовуйте одноразовий простір імен, якщо практикуєтеся поза розміщеною лабораторією. Мета — впровадити власну ідентичність, порівняти її зі стандартною поведінкою, налагодити збій дозволу, а потім прибрати непотрібний витік токена з робочого навантаження, якому не потрібен доступ до API.

  1. Створіть сервісний акаунт із назвою dojo-reader та призначте простий Под використовувати його. Перевірте, що специфікація допущеного Под’а показує dojo-reader, а не default.
  2. Огляньте монтування токена всередині Под’а та визначте файли token, ca.crt і namespace. Поясніть, який файл доводить ідентичність, який файл захищає TLS, а який уникає жорстко закодованих значень простору імен.
  3. Спробуйте отримати перелік Под’ів зсередини контейнера, використовуючи змонтований токен. Поспостерігайте, чи є збій автентифікацією, чи авторизацією, а тоді надайте лише get, list та watch на Под’и в просторі імен.
  4. Скористайтеся kubectl auth can-i як system:serviceaccount:<namespace>:dojo-reader, щоб підтвердити, що перелік Под’ів дозволено, а читання Secret’ів заборонено.
  5. Створіть другий Под із назвою dojo-no-api з automountServiceAccountToken: false. Перевірте, що стандартного каталогу сервісного акаунту немає або він не містить файлів токена.
Підказка до розв'язання для завдань 1 та 2

Спершу створіть сервісний акаунт, а потім помістіть serviceAccountName: dojo-reader під spec Под’а. Після запуску Под’а скористайтеся kubectl get pod <pod-name> -o jsonpath='{.spec.serviceAccountName}', щоб перевірити допущену ідентичність. Скористайтеся kubectl exec <pod-name> -- ls /var/run/secrets/kubernetes.io/serviceaccount/, щоб оглянути проєктовані файли, та зіставте кожен файл з автентифікацією, перевіркою TLS чи виявленням простору імен.

Підказка до розв'язання для завдань 3 та 4

Якщо запит до API всередині кластера досягає API-сервера й отримує 403 Forbidden, залиште призначення сервісного акаунту та монтування токена незмінними. Створіть Роль лише з дієсловами читання Под’ів та прив’яжіть її до dojo-reader у тому самому просторі імен. Тоді запустіть kubectl auth can-i list pods --as=system:serviceaccount:<namespace>:dojo-reader -n <namespace> та порівняйте з kubectl auth can-i get secrets --as=system:serviceaccount:<namespace>:dojo-reader -n <namespace>.

Підказка до розв'язання для завдання 5

Додайте automountServiceAccountToken: false до специфікації Под’а для dojo-no-api. Після запуску Под’а огляньте стандартний шлях сервісного акаунту. Умова успіху — не те, що Под має інший токен; вона в тому, що робоче навантаження більше не отримує стандартних облікових даних API, бо йому не потрібно викликати Kubernetes API.

Критерії успіху:

  • Впровадити власний сервісний акаунт та призначити його як маніфесту Под’а, так і шаблону Под’а в стилі контролера.
  • Порівняти стандартне монтування токена з Под’ом без токена та пояснити, коли кожен патерн доречний.
  • Налагодити збій API всередині кластера, відокремивши проєкцію токена від авторизації RBAC.
  • Спроєктувати Роль та RoleBinding за принципом найменших привілеїв, що надають лише читання Под’ів у межах простору імен.
  • Діагностувати ризик витоку токена, довівши, що Под без API не має автоматичного монтування токена сервісного акаунту.

Перевірка засвоєного

Розділ «Перевірка засвоєного»

Інженери, які вивчали Kubernetes до версії 1.24, часто очікують стовпець SECRETS у kubectl get serviceaccount. Цей стовпець було вилучено в Kubernetes 1.24+, тож сучасні кластери — зокрема 1.35 — виводять лише NAME та AGE.

Перш ніж рухатися далі, поясніть, чому kubectl describe sa default може показувати Tokens: <none>, тоді як Под, що використовує цей сервісний акаунт, усе одно має токен типу bearer у /var/run/secrets/kubernetes.io/serviceaccount/token. Ґрунтовна відповідь відокремлює об’єкт сервісного акаунту (немає застарілого автоматично створеного Secret токена) від допуску Под’а та проєкції токена kubelet’ом (прив’язаний, короткоживучий JWT, змонтований під час виконання).

Модуль 4.6: Визначення власних ресурсів (CRDs) — розширте Kubernetes API власними ресурсами та валідацією схеми.