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/. Внутри три
файла:
| Файл | Что внутри |
|---|---|
token | JWT-токен 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.
Как используется токен
Любой 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 # проверить текущий шаг
Коротко
- ServiceAccount - identity Pod в глазах apiserver, имя вида
system:serviceaccount:<ns>:<sa>. - Без
spec.serviceAccountNamePod получаетdefaultиз своего namespace. - Kubelet монтирует
token,ca.crtиnamespaceв/var/run/secrets/kubernetes.io/serviceaccount/. - С 1.24 токен выдаёт TokenRequest API: он короткоживущий и перевыпускается автоматически.
- Сам по себе SA прав не даёт, права появляются через Role и RoleBinding.
automountServiceAccountToken: falseубирает токен из Pod, которому API не нужен.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс