Модуль 2.2: Безпека ServiceAccount
Складність:
[MEDIUM]- Критично для безпеки навантаженьЧас на виконання: 40-45 хвилин
Передумови: Модуль 2.1 (Глибоке занурення в RBAC), знання ServiceAccount з CKA
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Налаштувати ServiceAccounts з вимкненим automountServiceAccountToken та обмеженими дозволами
- Аудитувати використання стандартних ServiceAccount у просторах імен для виявлення надмірно відкритих облікових даних
- Реалізувати прив’язані токени service account з терміном дії та обмеженнями аудиторії
- Діагностувати збої автентифікації Pod, спричинені неправильною конфігурацією ServiceAccount
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Кожен Pod працює від імені ServiceAccount. За замовчуванням це ServiceAccount ‘default’ з автоматично змонтованими обліковими даними. Якщо Pod скомпрометовано, зловмисник отримує ці облікові дані — потенційно з доступом до API Kubernetes.
CKS перевіряє вашу здатність зміцнювати ServiceAccount та мінімізувати ризики.
Проблема ServiceAccount
Розділ «Проблема ServiceAccount»┌─────────────────────────────────────────────────────────────┐│ РИЗИК СТАНДАРТНОГО SERVICEACCOUNT │├─────────────────────────────────────────────────────────────┤│ ││ За замовчуванням: ││ ││ ┌─────────────────────────────────────────────────────┐ ││ │ Pod │ ││ │ │ ││ │ Токен змонтовано за шляхом: │ ││ │ /var/run/secrets/kubernetes.io/serviceaccount/ │ ││ │ │ ││ │ Містить: │ ││ │ ├── token (JWT для автентифікації API) │ ││ │ ├── ca.crt (сертифікат CA кластера) │ ││ │ └── namespace (простір імен Pod) │ ││ │ │ ││ └─────────────────────────────────────────────────────┘ ││ ││ Сценарій атаки: ││ 1. Зловмисник компрометує застосунок ││ 2. Читає токен з файлової системи ││ 3. Використовує токен для виклику API Kubernetes ││ 4. Залежно від RBAC, може отримати доступ до секретів, ││ Pod тощо ││ │└─────────────────────────────────────────────────────────────┘Вимкнення автоматичного монтування токена
Розділ «Вимкнення автоматичного монтування токена»Метод 1: На рівні ServiceAccount
Розділ «Метод 1: На рівні ServiceAccount»apiVersion: v1kind: ServiceAccountmetadata: name: myapp namespace: productionautomountServiceAccountToken: false # Вимкнути для всіх Pod, що використовують цей SAМетод 2: На рівні Pod
Розділ «Метод 2: На рівні Pod»apiVersion: v1kind: Podmetadata: name: myappspec: serviceAccountName: myapp automountServiceAccountToken: false # Перевизначити тільки для цього Pod containers: - name: app image: myapp:1.0Метод 3: Оновлення стандартного ServiceAccount
Розділ «Метод 3: Оновлення стандартного ServiceAccount»# Оновити стандартний ServiceAccount у просторі іменkubectl patch serviceaccount default -n production \ -p '{"automountServiceAccountToken": false}'
# Перевіритиkubectl get sa default -n production -o yamlСтворення окремих ServiceAccount
Розділ «Створення окремих ServiceAccount»# Один ServiceAccount на застосунокapiVersion: v1kind: ServiceAccountmetadata: name: backend-api namespace: productionautomountServiceAccountToken: false---apiVersion: v1kind: ServiceAccountmetadata: name: frontend-app namespace: productionautomountServiceAccountToken: false---# Pod, що використовує окремий SAapiVersion: v1kind: Podmetadata: name: backend namespace: productionspec: serviceAccountName: backend-api containers: - name: app image: backend:1.0API TokenRequest (Прив’язані токени)
Розділ «API TokenRequest (Прив’язані токени)»Kubernetes 1.22+ за замовчуванням використовує прив’язані токени — короткоживучі токени, прив’язані до аудиторії, які є більш безпечними ніж довгоживучі секрети.
┌─────────────────────────────────────────────────────────────┐│ ПРИВ'ЯЗАНІ vs ЗАСТАРІЛІ ТОКЕНИ │├─────────────────────────────────────────────────────────────┤│ ││ Застарілий токен (на основі Secret) ││ ───────────────────────────────────────────────────────── ││ • Довгоживучий (ніколи не закінчується) ││ • Зберігається як Secret ││ • Не прив'язаний до життєвого циклу Pod ││ • Працює навіть після видалення Pod ││ ││ Прив'язаний токен (TokenRequest API) ││ ───────────────────────────────────────────────────────── ││ • Короткоживучий (налаштовуваний термін дії) ││ • Прив'язаний до конкретного Pod ││ • Стає недійсним після видалення Pod ││ • Прив'язаний до аудиторії ││ • За замовчуванням у K8s 1.22+ ││ ││ Прив'язані токени автоматично оновлюються kubelet! ││ │└─────────────────────────────────────────────────────────────┘Створення прив’язаного токена вручну
Розділ «Створення прив’язаного токена вручну»# Створити короткоживучий токен (1 година)kubectl create token myapp-sa -n production --duration=1h
# Створити токен з конкретною аудиторієюkubectl create token myapp-sa -n production --audience=api.example.comПроєктований том для прив’язаного токена
Розділ «Проєктований том для прив’язаного токена»apiVersion: v1kind: Podmetadata: name: myappspec: serviceAccountName: myapp-sa automountServiceAccountToken: false # Вимкнути стандартне монтування containers: - name: app image: myapp:1.0 volumeMounts: - name: token mountPath: /var/run/secrets/tokens readOnly: true volumes: - name: token projected: sources: - serviceAccountToken: path: token expirationSeconds: 3600 # 1 година audience: api.example.com # Конкретна аудиторіяНайкращі практики ServiceAccount
Розділ «Найкращі практики ServiceAccount»┌─────────────────────────────────────────────────────────────┐│ КОНТРОЛЬНИЙ СПИСОК БЕЗПЕКИ SERVICEACCOUNT │├─────────────────────────────────────────────────────────────┤│ ││ □ Вимкнути автомонтування для стандартного ServiceAccount ││ kubectl patch sa default -p ││ '{"automountServiceAccountToken": false}' ││ ││ □ Створити окремий ServiceAccount для кожного застосунку ││ Один SA на навантаження, без спільного використання ││ ││ □ Монтувати токен тільки коли потрібно ││ Більшість застосунків не потребують доступу до API K8s ││ ││ □ Використовувати прив'язані токени з терміном дії ││ Короткоживучі, прив'язані до аудиторії ││ ││ □ Мінімальні дозволи RBAC ││ Тільки те, що застосунку дійсно потрібно ││ ││ □ Аудит використання ServiceAccount ││ Які SA мають який доступ ││ │└─────────────────────────────────────────────────────────────┘Аудит ServiceAccount
Розділ «Аудит ServiceAccount»Пошук Pod зі змонтованим токеном
Розділ «Пошук Pod зі змонтованим токеном»# Список всіх Pod з їхніми ServiceAccountkubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name} -> {.spec.serviceAccountName}{"\n"}{end}'
# Перевірити, чи увімкнено автомонтуванняkubectl get pods -A -o json | jq -r ' .items[] | select(.spec.automountServiceAccountToken != false) | "\(.metadata.namespace)/\(.metadata.name): automount enabled"'Пошук ServiceAccount з дозволами
Розділ «Пошук ServiceAccount з дозволами»# Список всіх RoleBinding/ClusterRoleBinding для ServiceAccountkubectl get rolebindings,clusterrolebindings -A -o json | jq -r ' .items[] | .subjects[]? | select(.kind == "ServiceAccount") | "\(.namespace)/\(.name)"' | sort -u
# Перевірити дозволи конкретного SAkubectl auth can-i --list --as=system:serviceaccount:default:myappПеревірка застарілих токенів у Secret
Розділ «Перевірка застарілих токенів у Secret»# Знайти секрети типу ServiceAccount tokenkubectl get secrets -A -o json | jq -r ' .items[] | select(.type == "kubernetes.io/service-account-token") | "\(.metadata.namespace)/\(.metadata.name)"'Реальні сценарії іспиту
Розділ «Реальні сценарії іспиту»Сценарій 1: Вимкнення автомонтування токена
Розділ «Сценарій 1: Вимкнення автомонтування токена»# Вимкнути для стандартного SA у просторі імен productionkubectl patch serviceaccount default -n production \ -p '{"automountServiceAccountToken": false}'
# Створити новий SA з вимкненим автомонтуваннямcat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata: name: webapp-sa namespace: productionautomountServiceAccountToken: falseEOFСценарій 2: Виправлення Pod, що використовує стандартний SA
Розділ «Сценарій 2: Виправлення Pod, що використовує стандартний SA»# Перевірити, який SA використовує Podkubectl get pod myapp -n production -o jsonpath='{.spec.serviceAccountName}'
# Якщо використовується default, створити окремий SAcat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata: name: myapp-sa namespace: productionautomountServiceAccountToken: falseEOF
# Перестворити Pod з новим SA (не можна змінити SA у працюючому Pod)kubectl get pod myapp -n production -o yaml > pod.yaml# Відредагувати pod.yaml: встановити serviceAccountName: myapp-sakubectl delete pod myapp -n productionkubectl apply -f pod.yamlСценарій 3: Створення SA з мінімальними дозволами
Розділ «Сценарій 3: Створення SA з мінімальними дозволами»# Створити SA для застосунку, якому потрібно тільки читати ConfigMapcat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata: name: config-reader namespace: productionautomountServiceAccountToken: true # Потребує доступу до API---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: configmap-reader namespace: productionrules:- apiGroups: [""] resources: ["configmaps"] resourceNames: ["app-config"] # Тільки конкретний ConfigMap verbs: ["get"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: config-reader-binding namespace: productionsubjects:- kind: ServiceAccount name: config-reader namespace: productionroleRef: kind: Role name: configmap-reader apiGroup: rbac.authorization.k8s.ioEOFГлибше про безпеку токенів
Розділ «Глибше про безпеку токенів»Дослідження токена
Розділ «Дослідження токена»# Отримати токен з працюючого Podkubectl exec myapp -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
# Декодувати JWT (без верифікації)TOKEN=$(kubectl exec myapp -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .Вміст токена
Розділ «Вміст токена»{ "aud": ["https://kubernetes.default.svc"], "exp": 1704067200, // Час закінчення "iat": 1703980800, // Час видачі "iss": "https://kubernetes.default.svc", "kubernetes.io": { "namespace": "production", "pod": { "name": "myapp-abc123", "uid": "..." }, "serviceaccount": { "name": "myapp-sa", "uid": "..." } }, "sub": "system:serviceaccount:production:myapp-sa"}Чи знали ви?
Розділ «Чи знали ви?»-
TokenRequest API було представлено в Kubernetes 1.12 і стало стандартним у 1.22. Він значно безпечніший за старі токени на основі секретів.
-
Прив’язані токени оновлюються автоматично kubelet до закінчення терміну дії. Застосункам не потрібно обробляти оновлення — файл оновлюється на місці.
-
Стандартний ServiceAccount існує автоматично в кожному просторі імен. Його зміна впливає на всі Pod, які не вказують ServiceAccount.
-
Деякі контролери потребують доступу до API — оператори, вебхуки допуску та застосунки, що працюють з Kubernetes. Вони легітимно потребують змонтованих токенів з відповідними дозволами RBAC.
-
PodCertificateRequests (бета-версія у K8s 1.35) забезпечують нативну ідентифікацію навантажень з автоматичною ротацією сертифікатів. Kubelet генерує ключі та запитує сертифікати X.509 через об’єкти
PodCertificateRequest, що дозволяє використовувати чистий mTLS без bearer-токенів. Це майбутнє автентифікації між Pod у Kubernetes.
Поширені помилки
Розділ «Поширені помилки»| Помилка | Чому це шкодить | Рішення |
|---|---|---|
| Використання стандартного SA для всього | Спільні дозволи, складний аудит | Створюйте окремі SA |
| Не вимикати автомонтування | Непотрібний доступ до API | Вимкнути за замовчуванням |
| Довгоживучі токени у Secret | Ніколи не закінчуються, можуть бути викрадені | Використовуйте прив’язані токени |
| Надмірний RBAC для SA | Скомпрометований Pod = надмірний доступ | Мінімальні дозволи |
| Припущення: без токена = безпечно | Існують інші вектори атак | Захист у глибину |
Тест
Розділ «Тест»-
За яким шляхом токен ServiceAccount монтується за замовчуванням?
Відповідь
`/var/run/secrets/kubernetes.io/serviceaccount/` — містить файли `token`, `ca.crt` та `namespace`. -
Як вимкнути автоматичне монтування токена для ServiceAccount?
Відповідь
Встановити `automountServiceAccountToken: false` у специфікації ServiceAccount або у специфікації Pod. Специфікація Pod має пріоритет. -
Що таке прив’язані токени і чому вони безпечніші?
Відповідь
Прив'язані токени є короткоживучими, прив'язаними до аудиторії та пов'язаними з конкретним Pod. Вони мають термін дії і стають недійсними після видалення Pod, на відміну від застарілих довгоживучих токенів у Secret. -
Чому не всі Pod повинні використовувати стандартний ServiceAccount?
Відповідь
Спільні ServiceAccount ускладнюють аудит та контроль дозволів. Якщо одному застосунку потрібно більше дозволів, усі застосунки, що використовують цей SA, отримують їх. Окремі SA забезпечують принцип найменших привілеїв.
Практична вправа
Розділ «Практична вправа»Завдання: Забезпечити безпеку ServiceAccount у просторі імен.
# Підготовкаkubectl create namespace sa-securitykubectl run app1 --image=nginx -n sa-securitykubectl run app2 --image=nginx -n sa-security
# Крок 1: Перевірити поточне використання SAkubectl get pods -n sa-security -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.serviceAccountName}{"\n"}{end}'
# Крок 2: Переконатися, що токен змонтованоkubectl exec app1 -n sa-security -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# Крок 3: Вимкнути автомонтування для стандартного SAkubectl patch serviceaccount default -n sa-security \ -p '{"automountServiceAccountToken": false}'
# Крок 4: Створити окремий SA (без автомонтування)cat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata: name: app-sa namespace: sa-securityautomountServiceAccountToken: falseEOF
# Крок 5: Перестворити Pod з новим SAkubectl delete pod app1 app2 -n sa-security
cat <<EOF | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: app1 namespace: sa-securityspec: serviceAccountName: app-sa containers: - name: app image: nginx---apiVersion: v1kind: Podmetadata: name: app2 namespace: sa-securityspec: serviceAccountName: app-sa containers: - name: app image: nginxEOF
# Крок 6: Переконатися, що токен НЕ змонтованоkubectl exec app1 -n sa-security -- ls /var/run/secrets/kubernetes.io/serviceaccount/ 2>&1 || echo "Directory not found (expected!)"
# Крок 7: Переконатися, що Pod використовують правильний SAkubectl get pods -n sa-security -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.serviceAccountName}{"\n"}{end}'
# Очищенняkubectl delete namespace sa-securityКритерії успіху: Pod використовують окремий SA без змонтованого токена.
Підсумок
Розділ «Підсумок»Стандартна поведінка (небезпечна):
- Токен автоматично монтується до всіх Pod
- Часто використовується стандартний SA
- Довгоживучі токени
Безпечна конфігурація:
- Вимкнути автомонтування для стандартного SA
- Створити окремий SA для кожного застосунку
- Монтувати тільки коли потрібно
- Використовувати прив’язані токени з терміном дії
Основні команди:
# Вимкнути автомонтуванняkubectl patch sa default -p '{"automountServiceAccountToken": false}'
# Створити токен вручнуkubectl create token myapp-sa --duration=1hПоради для іспиту:
- Знайте налаштування автомонтування і на рівні SA, і на рівні Pod
- Практикуйте оновлення стандартного SA
- Розумійте різницю між прив’язаними та застарілими токенами
Наступний модуль
Розділ «Наступний модуль»Модуль 2.3: Безпека API-сервера — Захист API-сервера Kubernetes.