← К программе курса

Модуль 14 / 19

ServiceAccount, RBAC, securityContext, Pod Security и admission

KUBERNETES

Содержание
01

1. Цели и результат модуля

Вы научитесь проводить запрос через authentication, authorization и admission, выдавать workload минимальные права и применять runtime restrictions без запуска privileged containers.

  • ServiceAccount — identity namespaced workload
  • RBAC разрешает verbs над resources
  • RoleBinding связывает subject и role
  • securityContext ограничивает процесс
  • Pod Security admission отклоняет небезопасный Pod до запуска
02

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
03

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.

04

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.

05

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.

06

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 глобально.

07

7. Методические указания

  • Начинайте с identity и точного verb/resource/namespace
  • Проверяйте can-i под фактическим ServiceAccount
  • Не декодируйте и не печатайте token
  • Не используйте --no-check-certificate для API
  • Отключайте automount, если API не нужен
  • Применяйте restricted context в manifest
  • Разделяйте 401, 403 и admission rejection
  • Не выдавайте wildcard без документированной причины
08

8. Лабораторная работа: ServiceAccount и RBAC

Создайте Role только для чтения Pods и докажите разрешённое и запрещённое действия через impersonation.

terminal
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-m14
09

9. Лабораторная работа: запрос из Pod

Restricted Pod использует projected token и CA для TLS-проверенного запроса к API. Token не выводится в терминал.

terminal
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

10. Лабораторная работа: runtime restrictions

Проверьте UID, NoNewPrivs, seccomp и capabilities, затем докажите read-only root filesystem. Второй Pod не получает token volume.

terminal
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

11. Диагностика: admission rejection и RBAC 403

Privileged Pod отклоняется до сохранения. Затем тот же API-клиент получает HTTP 403 на secrets: это authorization failure, а не TLS или DNS.

terminal
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-labels
terminal
kubectl -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

12. Контроль, критерии освоения и cleanup

  • Порядок authentication, authorization, admission?
  • Чем 401 отличается от 403?
  • ServiceAccount cluster-scoped?
  • Что ограничивает audience token?
  • Когда отключать automount?
  • RoleBinding может связать ClusterRole?
  • Что делает allowPrivilegeEscalation?
  • Зачем drop ALL?
  • Различия enforce/audit/warn?
  • Почему Pod Security не заменяет RBAC?
terminal
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.