← Kubernetes: безопасность
7 мин · средне · Урок 2 из 3

ServiceAccount и токен в Pod

Под какой identity код внутри Pod ходит в API кластера, откуда берётся токен и почему automount стоит выключать.

Что такое ServiceAccount

ServiceAccount (SA) - это identity, под которой код внутри Pod обращается к kube-apiserver. У каждого SA своё имя в формате system:serviceaccount:<namespace>:<sa-name>. Имя видит apiserver, когда проверяет RBAC: что этому subject разрешено в Role и RoleBinding.

ServiceAccount живёт внутри namespace, как ConfigMap или Secret. Если не указать SA в spec Pod - kubelet поставит default из того же namespace. Это создаёт скрытую дыру: дефолтный SA технически существует везде, и если дать ему лишние права через ClusterRoleBinding, их получат все Pod в кластере.

Как создать SA

Одна команда:

kubectl create serviceaccount app-sa

Под капотом это создаёт объект:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: demo

Само по себе SA никаких прав не даёт. Это просто identity. Чтобы получить права, нужны Role и RoleBinding (или ClusterRole + ClusterRoleBinding для cluster-wide).

Как SA попадает в Pod

В spec Pod:

spec:
  serviceAccountName: app-sa
  containers:
    - ...

Если не указать - kubelet подставит default. Поле immutable: у существующего Pod его не поменять, нужно пересоздать.

Откуда внутри Pod берётся токен

Kubelet при запуске Pod монтирует в контейнер projected volume по адресу /var/run/secrets/kubernetes.io/serviceaccount/. Внутри три файла:

ФайлЧто внутри
tokenJWT-токен SA (короткоживущий, обновляется автоматически)
ca.crtсертификат kube-apiserver для TLS-проверки
namespaceимя namespace, где живёт Pod

Раньше (до 1.24) каждый SA автоматически получал отдельный Secret с вечным токеном, и kubelet просто монтировал его. Сейчас kubelet запрашивает токен через TokenRequest API, токен короткоживущий (час по умолчанию). Обновление kubelet берёт на себя: он перевыпускает токен, когда прошло около 80% срока жизни, либо когда токену стукнуло сутки - что случится раньше. Старые Secret-токены ещё принимаются, но новые автоматически не создаются, а неиспользуемые легаси-токены кластер с 1.29 подчищает сам.

Посмотреть токен изнутри Pod можно так:

kubectl exec probe -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token

Вернётся длинная строка из трёх частей, разделённых точками. Это JWT: header.payload.signature. Payload читается через base64:

kubectl exec probe -- sh -c \
  'cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d. -f2 | base64 -d 2>/dev/null'

Внутри будут sub, aud, exp и блок kubernetes.io со ссылкой на SA: serviceaccount.name=app-sa, pod.name=probe.

Как используется токен

Pod токен из volume Bearer-токен kube-apiserver проверяет подпись проверка RBAC Ответ 200 или 403
Токен говорит, кто пришёл; что ему можно, решает уже RBAC.

Любой http-клиент в Pod может подняться до API:

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

curl --cacert $CA -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default/api/v1/namespaces/$NS/pods

apiserver проверяет подпись токена, видит subject SA, проверяет RBAC. Если у SA нет права list pods в namespace, ответ 403.

Как отключить mount

Иногда токен в Pod не нужен: приложение не ходит в API. Тогда отключают automount, чтобы compromise Pod не дал лёгкий доступ к кластеру:

spec:
  automountServiceAccountToken: false
  serviceAccountName: app-sa

Или на уровне самого SA, чтобы выключить для всех Pod с этим SA:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
automountServiceAccountToken: false

Поле в spec Pod имеет приоритет над полем в SA. По умолчанию automount включён - это историческое наследие и потенциальная уязвимость, при аудитах часто требуют выключать там, где не нужно.

Как посмотреть своими глазами

kubectl create serviceaccount app-sa

kubectl run probe --image=busybox:1.36 \
  --overrides='{"spec":{"serviceAccountName":"app-sa"}}' \
  -- sleep 3600

kubectl exec probe -- \
  ls -la /var/run/secrets/kubernetes.io/serviceaccount/

kubectl exec probe -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

Pod видит сам себя глазами API через токен в смонтированном volume. Эта идея лежит в основе всех operator и in-cluster клиентов.

Домашка

Теорию закрепляет задание на своём кластере: 2 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-serviceaccount-token     # развернуть стартовое состояние
./run.sh verify k8s-serviceaccount-token    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.