Модуль 14 / 19
ServiceAccount, RBAC, securityContext, Pod Security и admission
Содержание
1. Цели и результат модуля
Вы научитесь проводить запрос через authentication, authorization и admission, выдавать workload минимальные права и применять runtime restrictions без запуска privileged containers.
- ServiceAccount — identity namespaced workload
- RBAC разрешает verbs над resources
- RoleBinding связывает subject и role
- securityContext ограничивает процесс
- Pod Security admission отклоняет небезопасный Pod до запуска
2. Теория: путь API-запроса
API Server сначала устанавливает identity через authentication, затем authorizer решает, разрешено ли действие, после чего admission может изменить или отклонить объект. Успешный RBAC не обходит admission, а Pod Security не выдаёт доступ к API.
- 401 — credentials не приняты
- 403 — identity известна, но действие запрещено
- 422/Forbidden admission — объект нарушает policy
- Running Pod — результат уже после admission, scheduling и kubelet
3. ServiceAccount и projected token
ServiceAccount — namespaced identity для процессов Pod. Современный Pod получает short-lived projected token с audience и сроком, CA bundle и namespace metadata. Старые постоянные Secret-токены не следует создавать без специальной причины.
Если приложение не обращается к API, задавайте automountServiceAccountToken: false. Сам факт token mount не даёт прав: их определяет authorization.
4. RBAC и least privilege
Role действует в namespace; ClusterRole описывает cluster-scoped или переиспользуемый набор правил. RoleBinding выдаёт Role или ClusterRole в одном namespace, ClusterRoleBinding — во всём кластере. Правило задаёт apiGroups, resources, verbs и иногда resourceNames.
Не используйте wildcard и cluster-admin ради устранения 403. Сначала определите точный запрос, проверьте kubectl auth can-i и выдайте минимальные verbs/resources.
5. securityContext и Linux controls
runAsNonRoot/runAsUser задают identity процесса; allowPrivilegeEscalation:false блокирует получение дополнительных privileges; capabilities.drop удаляет Linux capabilities; readOnlyRootFilesystem защищает root layer; seccomp RuntimeDefault фильтрует syscalls.
Эти настройки дополняют image hygiene, filesystem permissions, namespaces, cgroups и LSM. Они не заменяют NetworkPolicy и RBAC.
6. Pod Security admission
Pod Security Standards имеют уровни Privileged, Baseline и Restricted. Namespace labels выбирают режим enforce, audit и warn и при необходимости версию. enforce отклоняет объект; audit записывает нарушение; warn сообщает клиенту.
Restricted требует, среди прочего, non-root intent, запрет privilege escalation, drop ALL capabilities и seccomp profile. Исключения проектируют узко для системных workloads, а не отключают policy глобально.
7. Методические указания
- Начинайте с identity и точного verb/resource/namespace
- Проверяйте can-i под фактическим ServiceAccount
- Не декодируйте и не печатайте token
- Не используйте --no-check-certificate для API
- Отключайте automount, если API не нужен
- Применяйте restricted context в manifest
- Разделяйте 401, 403 и admission rejection
- Не выдавайте wildcard без документированной причины
8. Лабораторная работа: ServiceAccount и RBAC
Создайте Role только для чтения Pods и докажите разрешённое и запрещённое действия через impersonation.
mkdir -p /tmp/k8s-m14
kubectl create namespace k8s-m14
kubectl label namespace k8s-m14 \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
cat > /tmp/k8s-m14/rbac.yaml <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata: {name: pod-reader, namespace: k8s-m14}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: {name: pod-reader, namespace: k8s-m14}
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: {name: pod-reader, namespace: k8s-m14}
subjects:
- {kind: ServiceAccount, name: pod-reader, namespace: k8s-m14}
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: pod-reader}
EOF
kubectl apply --dry-run=server -f /tmp/k8s-m14/rbac.yaml
kubectl apply -f /tmp/k8s-m14/rbac.yaml
kubectl auth can-i list pods --as=system:serviceaccount:k8s-m14:pod-reader -n k8s-m14
kubectl auth can-i get secrets --as=system:serviceaccount:k8s-m14:pod-reader -n k8s-m14
kubectl auth can-i --list --as=system:serviceaccount:k8s-m14:pod-reader -n k8s-m149. Лабораторная работа: запрос из Pod
Restricted Pod использует projected token и CA для TLS-проверенного запроса к API. Token не выводится в терминал.
cat > /tmp/k8s-m14/client.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata: {name: api-client, namespace: k8s-m14}
spec:
serviceAccountName: pod-reader
securityContext:
runAsNonRoot: true
runAsUser: 10001
seccompProfile: {type: RuntimeDefault}
containers:
- name: client
image: curlimages/curl:8.16.0
command: ["sh", "-c", "sleep 3600"]
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: {drop: ["ALL"]}
EOF
kubectl apply --dry-run=server -f /tmp/k8s-m14/client.yaml
kubectl apply -f /tmp/k8s-m14/client.yaml
kubectl -n k8s-m14 wait --for=condition=Ready pod/api-client --timeout=180s
kubectl -n k8s-m14 exec api-client -- sh -c '
token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -fsS --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
-H "Authorization: Bearer $token" \
https://kubernetes.default.svc/api/v1/namespaces/k8s-m14/pods
' | head -c 600
kubectl -n k8s-m14 get pod api-client -o jsonpath='tokenTTL={.spec.volumes[0].projected.sources[0].serviceAccountToken.expirationSeconds}{"\n"}'10. Лабораторная работа: runtime restrictions
Проверьте UID, NoNewPrivs, seccomp и capabilities, затем докажите read-only root filesystem. Второй Pod не получает token volume.
kubectl -n k8s-m14 exec api-client -- id
kubectl -n k8s-m14 exec api-client -- sh -c 'grep -E "^(NoNewPrivs|Seccomp|CapEff):" /proc/1/status'
kubectl -n k8s-m14 exec api-client -- touch /write-test 2>&1 || true
cat > /tmp/k8s-m14/no-token.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata: {name: no-token, namespace: k8s-m14}
spec:
serviceAccountName: pod-reader
automountServiceAccountToken: false
securityContext: {runAsNonRoot: true, runAsUser: 10001, seccompProfile: {type: RuntimeDefault}}
containers:
- name: app
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
securityContext: {allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities: {drop: ["ALL"]}}
EOF
kubectl apply -f /tmp/k8s-m14/no-token.yaml
kubectl -n k8s-m14 wait --for=condition=Ready pod/no-token --timeout=120s
kubectl -n k8s-m14 get pod no-token -o jsonpath='{.spec.volumes}{"\n"}'11. Диагностика: admission rejection и RBAC 403
Privileged Pod отклоняется до сохранения. Затем тот же API-клиент получает HTTP 403 на secrets: это authorization failure, а не TLS или DNS.
cat > /tmp/k8s-m14/privileged.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata: {name: privileged-test, namespace: k8s-m14}
spec:
containers:
- name: app
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
securityContext: {privileged: true}
EOF
kubectl apply --dry-run=server -f /tmp/k8s-m14/privileged.yaml || true
kubectl apply -f /tmp/k8s-m14/privileged.yaml || true
kubectl -n k8s-m14 get pod privileged-test 2>&1 || true
kubectl get namespace k8s-m14 --show-labelskubectl -n k8s-m14 exec api-client -- sh -c '
token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -sS -w "\nhttp=%{http_code}\n" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
-H "Authorization: Bearer $token" \
https://kubernetes.default.svc/api/v1/namespaces/k8s-m14/secrets
'
kubectl auth can-i get secrets --as=system:serviceaccount:k8s-m14:pod-reader -n k8s-m14
kubectl get role,rolebinding -n k8s-m14 -o yamlПорядок: endpoint/TLS → authenticated identity → verb/apiGroup/resource/subresource/namespace → binding → admission policy → runtime security.
12. Контроль, критерии освоения и cleanup
- Порядок authentication, authorization, admission?
- Чем 401 отличается от 403?
- ServiceAccount cluster-scoped?
- Что ограничивает audience token?
- Когда отключать automount?
- RoleBinding может связать ClusterRole?
- Что делает allowPrivilegeEscalation?
- Зачем drop ALL?
- Различия enforce/audit/warn?
- Почему Pod Security не заменяет RBAC?
kubectl delete namespace k8s-m14 --wait=true
rm -rf /tmp/k8s-m14
kubectl get namespace k8s-m14 2>&1 || trueМодуль освоен, если workload читает Pods, получает 403 на Secrets, проходит Restricted policy, а privileged manifest отклоняется admission.