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

Модуль 2.2: Безпека ServiceAccount

Hands-On Lab Available
K8s Cluster advanced 35 min
Launch Lab ↗

Opens in Killercoda in a new tab

Складність: [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 │
├─────────────────────────────────────────────────────────────┤
│ │
│ За замовчуванням: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 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: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: production
automountServiceAccountToken: false # Вимкнути для всіх Pod, що використовують цей SA
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
serviceAccountName: myapp
automountServiceAccountToken: false # Перевизначити тільки для цього Pod
containers:
- name: app
image: myapp:1.0

Метод 3: Оновлення стандартного ServiceAccount

Розділ «Метод 3: Оновлення стандартного ServiceAccount»
Terminal window
# Оновити стандартний ServiceAccount у просторі імен
kubectl patch serviceaccount default -n production \
-p '{"automountServiceAccountToken": false}'
# Перевірити
kubectl get sa default -n production -o yaml

Створення окремих ServiceAccount

Розділ «Створення окремих ServiceAccount»
# Один ServiceAccount на застосунок
apiVersion: v1
kind: ServiceAccount
metadata:
name: backend-api
namespace: production
automountServiceAccountToken: false
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: frontend-app
namespace: production
automountServiceAccountToken: false
---
# Pod, що використовує окремий SA
apiVersion: v1
kind: Pod
metadata:
name: backend
namespace: production
spec:
serviceAccountName: backend-api
containers:
- name: app
image: backend:1.0

API TokenRequest (Прив’язані токени)

Розділ «API TokenRequest (Прив’язані токени)»

Kubernetes 1.22+ за замовчуванням використовує прив’язані токени — короткоживучі токени, прив’язані до аудиторії, які є більш безпечними ніж довгоживучі секрети.

┌─────────────────────────────────────────────────────────────┐
│ ПРИВ'ЯЗАНІ vs ЗАСТАРІЛІ ТОКЕНИ │
├─────────────────────────────────────────────────────────────┤
│ │
│ Застарілий токен (на основі Secret) │
│ ───────────────────────────────────────────────────────── │
│ • Довгоживучий (ніколи не закінчується) │
│ • Зберігається як Secret │
│ • Не прив'язаний до життєвого циклу Pod │
│ • Працює навіть після видалення Pod │
│ │
│ Прив'язаний токен (TokenRequest API) │
│ ───────────────────────────────────────────────────────── │
│ • Короткоживучий (налаштовуваний термін дії) │
│ • Прив'язаний до конкретного Pod │
│ • Стає недійсним після видалення Pod │
│ • Прив'язаний до аудиторії │
│ • За замовчуванням у K8s 1.22+ │
│ │
│ Прив'язані токени автоматично оновлюються kubelet! │
│ │
└─────────────────────────────────────────────────────────────┘

Створення прив’язаного токена вручну

Розділ «Створення прив’язаного токена вручну»
Terminal window
# Створити короткоживучий токен (1 година)
kubectl create token myapp-sa -n production --duration=1h
# Створити токен з конкретною аудиторією
kubectl create token myapp-sa -n production --audience=api.example.com

Проєктований том для прив’язаного токена

Розділ «Проєктований том для прив’язаного токена»
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
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 мають який доступ │
│ │
└─────────────────────────────────────────────────────────────┘

Пошук Pod зі змонтованим токеном

Розділ «Пошук Pod зі змонтованим токеном»
Terminal window
# Список всіх Pod з їхніми ServiceAccount
kubectl 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 з дозволами»
Terminal window
# Список всіх RoleBinding/ClusterRoleBinding для ServiceAccount
kubectl get rolebindings,clusterrolebindings -A -o json | jq -r '
.items[] |
.subjects[]? |
select(.kind == "ServiceAccount") |
"\(.namespace)/\(.name)"' | sort -u
# Перевірити дозволи конкретного SA
kubectl auth can-i --list --as=system:serviceaccount:default:myapp

Перевірка застарілих токенів у Secret

Розділ «Перевірка застарілих токенів у Secret»
Terminal window
# Знайти секрети типу ServiceAccount token
kubectl get secrets -A -o json | jq -r '
.items[] |
select(.type == "kubernetes.io/service-account-token") |
"\(.metadata.namespace)/\(.metadata.name)"'

Реальні сценарії іспиту

Розділ «Реальні сценарії іспиту»

Сценарій 1: Вимкнення автомонтування токена

Розділ «Сценарій 1: Вимкнення автомонтування токена»
Terminal window
# Вимкнути для стандартного SA у просторі імен production
kubectl patch serviceaccount default -n production \
-p '{"automountServiceAccountToken": false}'
# Створити новий SA з вимкненим автомонтуванням
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: webapp-sa
namespace: production
automountServiceAccountToken: false
EOF

Сценарій 2: Виправлення Pod, що використовує стандартний SA

Розділ «Сценарій 2: Виправлення Pod, що використовує стандартний SA»
Terminal window
# Перевірити, який SA використовує Pod
kubectl get pod myapp -n production -o jsonpath='{.spec.serviceAccountName}'
# Якщо використовується default, створити окремий SA
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
automountServiceAccountToken: false
EOF
# Перестворити Pod з новим SA (не можна змінити SA у працюючому Pod)
kubectl get pod myapp -n production -o yaml > pod.yaml
# Відредагувати pod.yaml: встановити serviceAccountName: myapp-sa
kubectl delete pod myapp -n production
kubectl apply -f pod.yaml

Сценарій 3: Створення SA з мінімальними дозволами

Розділ «Сценарій 3: Створення SA з мінімальними дозволами»
Terminal window
# Створити SA для застосунку, якому потрібно тільки читати ConfigMap
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: config-reader
namespace: production
automountServiceAccountToken: true # Потребує доступу до API
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["app-config"] # Тільки конкретний ConfigMap
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: config-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: config-reader
namespace: production
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
EOF

Глибше про безпеку токенів

Розділ «Глибше про безпеку токенів»

Дослідження токена

Розділ «Дослідження токена»
Terminal window
# Отримати токен з працюючого Pod
kubectl 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 = надмірний доступМінімальні дозволи
Припущення: без токена = безпечноІснують інші вектори атакЗахист у глибину

  1. За яким шляхом токен ServiceAccount монтується за замовчуванням?

    Відповідь `/var/run/secrets/kubernetes.io/serviceaccount/` — містить файли `token`, `ca.crt` та `namespace`.
  2. Як вимкнути автоматичне монтування токена для ServiceAccount?

    Відповідь Встановити `automountServiceAccountToken: false` у специфікації ServiceAccount або у специфікації Pod. Специфікація Pod має пріоритет.
  3. Що таке прив’язані токени і чому вони безпечніші?

    Відповідь Прив'язані токени є короткоживучими, прив'язаними до аудиторії та пов'язаними з конкретним Pod. Вони мають термін дії і стають недійсними після видалення Pod, на відміну від застарілих довгоживучих токенів у Secret.
  4. Чому не всі Pod повинні використовувати стандартний ServiceAccount?

    Відповідь Спільні ServiceAccount ускладнюють аудит та контроль дозволів. Якщо одному застосунку потрібно більше дозволів, усі застосунки, що використовують цей SA, отримують їх. Окремі SA забезпечують принцип найменших привілеїв.

Завдання: Забезпечити безпеку ServiceAccount у просторі імен.

Terminal window
# Підготовка
kubectl create namespace sa-security
kubectl run app1 --image=nginx -n sa-security
kubectl run app2 --image=nginx -n sa-security
# Крок 1: Перевірити поточне використання SA
kubectl 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: Вимкнути автомонтування для стандартного SA
kubectl patch serviceaccount default -n sa-security \
-p '{"automountServiceAccountToken": false}'
# Крок 4: Створити окремий SA (без автомонтування)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: sa-security
automountServiceAccountToken: false
EOF
# Крок 5: Перестворити Pod з новим SA
kubectl delete pod app1 app2 -n sa-security
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app1
namespace: sa-security
spec:
serviceAccountName: app-sa
containers:
- name: app
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: app2
namespace: sa-security
spec:
serviceAccountName: app-sa
containers:
- name: app
image: nginx
EOF
# Крок 6: Переконатися, що токен НЕ змонтовано
kubectl exec app1 -n sa-security -- ls /var/run/secrets/kubernetes.io/serviceaccount/ 2>&1 || echo "Directory not found (expected!)"
# Крок 7: Переконатися, що Pod використовують правильний SA
kubectl 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 для кожного застосунку
  • Монтувати тільки коли потрібно
  • Використовувати прив’язані токени з терміном дії

Основні команди:

Terminal window
# Вимкнути автомонтування
kubectl patch sa default -p '{"automountServiceAccountToken": false}'
# Створити токен вручну
kubectl create token myapp-sa --duration=1h

Поради для іспиту:

  • Знайте налаштування автомонтування і на рівні SA, і на рівні Pod
  • Практикуйте оновлення стандартного SA
  • Розумійте різницю між прив’язаними та застарілими токенами

Модуль 2.3: Безпека API-сервера — Захист API-сервера Kubernetes.