Модуль 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 та видайте мінімальні права.
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.
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.