Налаштування контексту безпеки для Podʼа або контейнера

Контекст безпеки визначає параметри привілеїв та контролю доступу для Podʼа або контейнера. Налаштування контексту безпеки включають, але не обмежуються:

  • Дискреційний контроль доступу: Дозвіл на доступ до обʼєкта, такого як файл, базується на ідентифікаторі користувача (UID) та ідентифікаторі групи (GID).

  • Security Enhanced Linux (SELinux): Обʼєкти призначаються мітки безпеки.

  • Виконання з привілеями або без них.

  • Linux Capabilities: Надає процесу деякі привілеї, але не всі привілеї користувача root.

  • AppArmor: Використовуйте профілі програм для обмеження можливостей окремих програм.

  • Seccomp: Фільтрує системні виклики процесу.

  • allowPrivilegeEscalation: Контролює, чи може процес отримувати більше привілеїв, ніж його батьківський процес. Ця логічна величина безпосередньо контролює, чи встановлюється прапорець no_new_privs для процесу контейнера. allowPrivilegeEscalation завжди true, коли контейнер:

    • запущений з привілеями, або
    • має CAP_SYS_ADMIN
  • readOnlyRootFilesystem: Підключає кореневу файлову систему контейнера тільки для читання.

Вищевказані пункти не є повним набором налаштувань контексту безпеки,докладну інформацію див. у SecurityContext для повного переліку.

Перш ніж ви розпочнете

Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:

Для перевірки версії введіть kubectl version.

Встановлення контексту безпеки для Pod

Щоб вказати параметри безпеки для Podʼа, включіть поле securityContext в специфікацію Pod. Поле securityContext є обʼєктом PodSecurityContext. Параметри безпеки, які ви вказуєте для Pod, застосовуються до всіх контейнерів в Podʼі. Ось файл конфігурації для Podʼа з securityContext та томом emptyDir:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    supplementalGroups: [4000]
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

У файлі конфігурації поле runAsUser вказує, що для будь-яких контейнерів в Podʼі, всі процеси виконуються з ідентифікатором користувача 1000. Поле runAsGroup вказує основний ідентифікатор групи 3000 для усіх процесів у контейнерах Pod. Якщо це поле відсутнє, основний ідентифікатор групи контейнерів буде root(0). Будь-які створені файли також належатимуть користувачу 1000 та групі 3000 при вказанні runAsGroup. Оскільки вказано поле fsGroup, всі процеси контейнера також належать до додаткової групи ідентифікатора 2000. Власником тому /data/demo та всіх створених файлів у цьому томі буде груповий ідентифікатор 2000. Додатково, коли вказане поле supplementalGroups, всі процеси контейнера також є частиною вказаних груп. Якщо це поле пропущене, це означає, що воно порожнє.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context.yaml

Перевірте, що контейнер Pod запущений:

kubectl get pod security-context-demo

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo -- sh

У вашій оболонці перелічіть запущені процеси:

ps

Виведений результат показує, що процеси виконуються від імені користувача 1000, що є значенням runAsUser:

PID   USER     TIME  COMMAND
    1 1000      0:00 sleep 1h
    6 1000      0:00 sh
...

У вашій оболонці перейдіть до /data, та виведіть список тек:

cd /data
ls -l

Виведений результат показує, що тека /data/demo має ідентифікатор групи 2000, що є значенням fsGroup.

drwxrwsrwx 2 root 2000 4096 Jun  6 20:08 demo

У вашій оболонці перейдіть до /data/demo, та створіть файл:

cd demo
echo hello > testfile

Виведіть список файлів у теці /data/demo:

ls -l

Виведений результат показує, що testfile має ідентифікатор групи 2000, що є значенням fsGroup.

-rw-r--r-- 1 1000 2000 6 Jun  6 20:08 testfile

Виконайте наступну команду:

id

Результат буде схожий на цей:

uid=1000 gid=3000 groups=2000,3000,4000

З результату видно, що gid дорівнює 3000, що є таким самим, як поле runAsGroup. Якби поле runAsGroup було пропущено, gid залишився б 0 (root), і процес зміг би взаємодіяти з файлами, які належать групі root(0) та групам, які мають необхідні права групи для групи root (0). Ви також можете побачити, що groups містить ідентифікатори груп, які вказані в fsGroup і supplementalGroups, поряд з gid.

Вийдіть з оболонки:

exit

Неявні членства груп, визначені в /etc/group в контейнерному образі

Стандартно Kubernetes обʼєднує інформацію про групи з Podʼа з інформацією, визначеною в /etc/group в контейнерному образі.

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

Цей контекст безпеки Podʼа містить runAsUser, runAsGroup та supplementalGroups. Проте ви можете побачити, що фактичні додаткові групи, що прикріплені до процесу контейнера, включатимуть ідентифікатори груп, які походять з /etc/group всередині контейнерного образу.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-5.yaml

Перевірте, чи контейнер Pod запущений:

kubectl get pod security-context-demo

Отримайте оболонку для запущеного контейнера:

kubectl exec -it security-context-demo -- sh

Перевірте ідентичність процесу:

$ id

Вивід буде схожий на:

uid=1000 gid=3000 groups=3000,4000,50000

Ви можете побачити, що groups включає ідентифікатор групи 50000. Це тому, що користувач (uid=1000), який визначений в образі, належить до групи (gid=50000), яка визначена в /etc/group всередині контейнерного образу.

Перевірте /etc/group в контейнерному образі:

$ cat /etc/group

Ви побачите, що uid 1000 належить до групи 50000.

...
user-defined-in-image:x:1000:
group-defined-in-image:x:50000:user-defined-in-image

Вийдіть з оболонки:

exit

Налаштування SupplementalGroups для Podʼа

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [alpha] (стандартно увімкнено: false)

Цю функцію можна увімкнути, встановивши функціональну можливість SupplementalGroupsPolicy для kubelet та kube-apiserver, а також налаштувавши поле .spec.securityContext.supplementalGroupsPolicy для Podʼа.

Поле supplementalGroupsPolicy визначає політику для розрахунку додаткових груп для процесів контейнера в Podʼі. Для цього поля є два дійсних значення:

  • Merge: Членство в групах, визначене в /etc/group для основного користувача контейнера, буде обʼєднано. Це є стандартною політикою, якщо не зазначено інше.

  • Strict: Тільки ідентифікатори груп у полях fsGroup, supplementalGroups або runAsGroup прикріплюються як додаткові групи для процесів контейнера. Це означає, що жодне членство в групах з /etc/group для основного користувача контейнера не буде обʼєднано.

Коли функція увімкнена, вона також надає ідентичність процесу, прикріплену до першого процесу контейнера в полі .status.containerStatuses[].user.linux. Це буде корисно для виявлення, чи прикріплені неявні ідентифікатори груп.

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
    supplementalGroupsPolicy: Strict
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

Цей маніфест Podʼа визначає supplementalGroupsPolicy=Strict. Ви можете побачити, що жодне членство в групах, визначене в /etc/group, не обʼєднується в додаткові групи для процесів контейнера.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-6.yaml

Перевірте, що контейнер Podʼа працює:

kubectl get pod security-context-demo

Перевірте ідентичність процесу:

kubectl exec -it security-context-demo -- id

Вивід буде подібним до цього:

uid=1000 gid=3000 groups=3000,4000

Перегляньте статус Podʼа:

kubectl get pod security-context-demo -o yaml

Ви можете побачити, що поле status.containerStatuses[].user.linux надає ідентичність процесу, прикріплену до першого процесу контейнера.

...
status:
  containerStatuses:
  - name: sec-ctx-demo
    user:
      linux:
        gid: 3000
        supplementalGroups:
        - 3000
        - 4000
        uid: 1000
...

Реалізації

Відомо, що наступні середовища виконання контейнерів підтримують контроль додаткових груп з тонкою настройкою.

На рівні CRI:

Ви можете перевірити, чи підтримується функція в статусі вузла.

apiVersion: v1
kind: Node
...
status:
  features:
    supplementalGroupsPolicy: true

Налаштування політики зміни дозволів та прав власності тому для Pod

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.23 [stable]

Типово Kubernetes рекурсивно змінює права власності та дозволи для вмісту кожного тому так, щоб вони відповідали значенню fsGroup, вказаному в securityContext Podʼа при підключенні цього тому. Для великих томів перевірка та зміна власності та дозволів може займати багато часу, сповільнюючи запуск Podʼів. Ви можете використовувати поле fsGroupChangePolicy в securityContext для контролю способу, яким Kubernetes перевіряє та керує власністю та дозволами для тому.

fsGroupChangePolicy — fsGroupChangePolicy визначає поведінку зміни власності та дозволів тому перед тим, як він буде використаний в Pod. Це поле застосовується лише до типів томів, які підтримують контроль власності та дозволів за допомогою fsGroup. Це поле має два можливі значення:

  • OnRootMismatch: Змінювати дозволи та права власності тільки у випадку, якщо дозволи та права кореневої теки не відповідають очікуваним дозволам тому. Це може допомогти скоротити час зміни власності та дозволів тому.
  • Always: Завжди змінювати дозволи та права власності тому під час підключення.

Наприклад:

securityContext:
  runAsUser: 1000
  runAsGroup: 3000
  fsGroup: 2000
  fsGroupChangePolicy: "OnRootMismatch"

Делегування зміни прав власності та дозволів тому до драйвера CSI

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [stable]

Якщо ви розгортаєте драйвер Container Storage Interface (CSI), який підтримує VOLUME_MOUNT_GROUP NodeServiceCapability, процес встановлення права власності та дозволів файлу на основі fsGroup, вказаного в securityContext, буде виконуватися драйвером CSI, а не Kubernetes. У цьому випадку, оскільки Kubernetes не виконує жодної зміни права власності та дозволів, fsGroupChangePolicy не набуває чинності, і згідно з вказаним CSI, очікується, що драйвер монтує том з наданим fsGroup, що призводить до отримання тому, який є доступними для читаання/запису для fsGroup.

Встановлення контексту безпеки для контейнера

Для вказання параметрів безпеки для контейнера, включіть поле securityContext в маніфест контейнера. Поле securityContext є обʼєктом SecurityContext. Параметри безпеки, які ви вказуєте для контейнера, застосовуються тільки до окремого контейнера, і вони перевизначають налаштування, зроблені на рівні Podʼа, коли є перетин. Налаштування контейнера не впливають на томи Podʼів.

Ось файл конфігурації для Podʼа з одним контейнером. Як Pod, так і контейнер мають поле securityContext:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: sec-ctx-demo-2
    image: gcr.io/google-samples/hello-app:2.0
    securityContext:
      runAsUser: 2000
      allowPrivilegeEscalation: false

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-2.yaml

Перевірте, що контейнер Pod запущений:

kubectl get pod security-context-demo-2

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo-2 -- sh

У вашій оболонці перегляньте запущені процеси:

ps aux

Виведений результат показує, що процеси виконуються від імені користувача 2000. Це значення runAsUser, вказане для контейнера. Воно перевизначає значення 1000, вказане для Pod.

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
2000         1  0.0  0.0   4336   764 ?        Ss   20:36   0:00 /bin/sh -c node server.js
2000         8  0.1  0.5 772124 22604 ?        Sl   20:36   0:00 node server.js
...

Вийдіть з оболонки:

exit

Встановлення можливостей для контейнера

За допомогою можливостей Linux, ви можете надати певні привілеї процесу, не надаючи всі привілеї користувачеві з правами root. Щоб додати або видалити можливості Linux для контейнера, включіть поле capabilities в розділ securityContext маніфесту контейнера.

Спочатку подивімося, що відбувається, коли ви не включаєте поле capabilities. Ось файл конфігурації, який не додає або не видаляє жодних можливостей контейнера:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-3
spec:
  containers:
  - name: sec-ctx-3
    image: gcr.io/google-samples/hello-app:2.0

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-3.yaml

Перевірте, що контейнер Pod запущений:

kubectl get pod security-context-demo-3

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo-3 -- sh

У вашій оболонці перегляньте запущені процеси:

ps aux

Виведений результат показує ідентифікатори процесів (PID) для контейнера:

USER  PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root    1  0.0  0.0   4336   796 ?     Ss   18:17   0:00 /bin/sh -c node server.js
root    5  0.1  0.5 772124 22700 ?     Sl   18:17   0:00 node server.js

У вашій оболонці перегляньте статус для процесу 1:

cd /proc/1
cat status

Виведений результат показує bitmap можливостей для процесу:

...
CapPrm:	00000000a80425fb
CapEff:	00000000a80425fb
...

Запамʼятайте bitmap можливостей, а потім вийдіть з оболонки:

exit

Далі, запустіть контейнер, який є такий самий, як попередній контейнер, за винятком того, що він має додаткові можливості.

Ось файл конфігурації для Pod, який запускає один контейнер. Конфігурація додає можливості CAP_NET_ADMIN та CAP_SYS_TIME:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-4
spec:
  containers:
  - name: sec-ctx-4
    image: gcr.io/google-samples/hello-app:2.0
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-4.yaml

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo-4 -- sh

У вашій оболонці перегляньте можливості для процесу 1:

cd /proc/1
cat status

Виведений результат показує бітову карту можливостей для процесу:

...
CapPrm:	00000000aa0435fb
CapEff:	00000000aa0435fb
...

Порівняйте можливості двох контейнерів:

00000000a80425fb
00000000aa0435fb

У bitmap можливостей першого контейнера біти 12 і 25 не встановлені. У другому контейнері, біти 12 і 25 встановлені. Біт 12 — це CAP_NET_ADMIN, а біт 25 — це CAP_SYS_TIME. Дивіться capability.h для визначень констант можливостей.

Встановлення профілю Seccomp для контейнера

Щоб встановити профіль Seccomp для контейнера, включіть поле seccompProfile в розділ securityContext вашого маніфесту Pod або контейнера. Поле seccompProfile є обʼєктом SeccompProfile, який складається з type та localhostProfile. Допустимі варіанти для type включають RuntimeDefault, Unconfined та Localhost. localhostProfile повинен бути встановлений лише якщо type: Localhost. Він вказує шлях до попередньо налаштованого профілю на вузлі, повʼязаного з розташуванням налаштованого профілю Seccomp kubelet (налаштованого за допомогою прапорця --root-dir).

Ось приклад, який встановлює профіль Seccomp до стандартного профілю контейнера вузла:

...
securityContext:
  seccompProfile:
    type: RuntimeDefault

Ось приклад, який встановлює профіль Seccomp до попередньо налаштованого файлу за шляхом <kubelet-root-dir>/seccomp/my-profiles/profile-allow.json:

...
securityContext:
  seccompProfile:
    type: Localhost
    localhostProfile: my-profiles/profile-allow.json

Налаштування профілю AppArmor для контейнера

Щоб налаштувати профіль AppArmor для контейнера, включіть поле appArmorProfile в секцію securityContext вашого контейнера. Поле appArmorProfile є обʼєктом AppArmorProfile, що складається з type та localhostProfile. Дійсні опції для type включають RuntimeDefault (стандартно), Unconfined і Localhost. localhostProfile слід встановлювати тільки якщо type є Localhost. Це вказує на назву попередньо налаштованого профілю на вузлі. Профіль повинен бути завантажений на всіх вузлах, які підходять для Podʼа, оскільки ви не знаєте, де буде розгорнуто Pod. Підходи до налаштування власних профілів обговорюються в Налаштування вузлів з профілями.

Примітка: Якщо containers[*].securityContext.appArmorProfile.type явно встановлено на RuntimeDefault, то Pod не буде допущено, якщо AppArmor не включено на вузлі. Однак, якщо containers[*].securityContext.appArmorProfile.type не зазначено, то стандартне значення (що також є RuntimeDefault) буде застосовано тільки якщо вузол має увімкнений AppArmor. Якщо вузол має вимкнений AppArmor, Pod буде допущено, але контейнер не буде обмежено профілем RuntimeDefault.

Ось приклад, який встановлює профіль AppArmor на стандартний профіль контейнерного середовища вузла:

...
containers:
- name: container-1
  securityContext:
    appArmorProfile:
      type: RuntimeDefault

Ось приклад, який встановлює профіль AppArmor на попередньо налаштований профіль з назвою k8s-apparmor-example-deny-write:

...
containers:
- name: container-1
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: k8s-apparmor-example-deny-write

Для отримання додаткової інформації дивіться Обмеження доступу контейнера до ресурсів з AppArmor.

Призначення міток SELinux контейнеру

Щоб призначити мітки SELinux контейнеру, включіть поле seLinuxOptions в розділ securityContext вашого маніфесту Podʼа або контейнера. Поле seLinuxOptions є обʼєктом SELinuxOptions. Ось приклад, який застосовує рівень SELinux:

...
securityContext:
  seLinuxOptions:
    level: "s0:c123,c456"

Ефективне переозначення обʼєктів SELinux в томах

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.28 [beta] (стандартно увімкнено: true)

Стандартно, контейнерне середовище рекурсивно призначає мітку SELinux для всіх файлів на всіх томах Pod. Щоб прискорити цей процес, Kubernetes може миттєво змінити мітку SELinux тому за допомогою параметра монтування -o context=<мітка>.

Щоб скористатися цим прискоренням, мають бути виконані всі ці умови:

  • Функціональні можливості ReadWriteOncePod та SELinuxMountReadWriteOncePod повинні бути увімкнені.
  • Pod повинен використовувати PersistentVolumeClaim з відповідними accessModes та функціональною можливстю:
    • Або том має accessModes: ["ReadWriteOncePod"], і властивість включення SELinuxMountReadWriteOncePod увімкнена.
    • Або том може використовувати будь-які інші режими доступу та обидва SELinuxMountReadWriteOncePod та SELinuxMount повинні бути увімкнені.
  • Pod (або всі його контейнери, які використовують PersistentVolumeClaim) повинні мати встановлені параметри seLinuxOptions.
  • Відповідний PersistentVolume повинен бути або:
    • Том, який використовує старі типи томів iscsi, rbd або fc.
    • Або том, який використовує драйвер CSI CSI. Драйвер CSI повинен оголосити, що він підтримує монтування з -o context, встановивши spec.seLinuxMount: true у його екземплярі CSIDriver.

Для будь-яких інших типів томів переозначення SELinux відбувається іншим шляхом: контейнерне середовище рекурсивно змінює мітку SELinux для всіх inodes (файлів і тек) у томі. Чим більше файлів і тек у томі, тим довше відбувається це переозначення.

Управління доступом до файлової системи /proc

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.12 [alpha] (стандартно увімкнено: false)

Для середовищ виконання, які слідують специфікації виконання OCI, контейнери типово запускаються у режимі, де є кілька шляхів, які промасковані та доступні тільки для читання. Результатом цього є те, що в межах простору імен монтування контейнера присутні ці шляхи, і вони можуть працювати подібно до того, якби контейнер був ізольованим хостом, але процес контейнера не може записувати до них. Список промаскованих і доступних тільки для читання шляхів такий:

  • Промасковані шляхи:

    • /proc/asound
    • /proc/acpi
    • /proc/kcore
    • /proc/keys
    • /proc/latency_stats
    • /proc/timer_list
    • /proc/timer_stats
    • /proc/sched_debug
    • /proc/scsi
    • /sys/firmware
    • /sys/devices/virtual/powercap
  • Шляхи доступні тільки для читання:

    • /proc/bus
    • /proc/fs
    • /proc/irq
    • /proc/sys
    • /proc/sysrq-trigger

Для деяких Podʼів вам може знадобитися обійти стандартний шлях маскування. Найбільш поширений контекст, коли це потрібно, — це спроба запуску контейнерів у межах контейнера Kubernetes (у межах Podʼа).

Поле procMount в securityContext дозволяє користувачеві запитати, щоб /proc контейнера був Unmasked, або міг бути підмонтований для читання-запису контейнерним процесом. Це також стосується /sys/firmware, який не знаходиться в /proc.

...
securityContext:
  procMount: Unmasked

Обговорення

Контекст безпеки для Pod застосовується до Контейнерів Pod і також до Томів Pod при необхідності. Зокрема, fsGroup та seLinuxOptions застосовуються до Томів наступним чином:

  • fsGroup: Томи, які підтримують управління власністю, модифікуються так, щоб бути власністю та доступними для запису за GID, вказаним у fsGroup. Докладніше див. Документ із проєктування управління власністю томів.

  • seLinuxOptions: Томи, які підтримують мітку SELinux, переозначаються так, щоб бути доступними за міткою, вказаною у seLinuxOptions. Зазвичай вам потрібно лише встановити розділ level. Це встановлює мітку Multi-Category Security (MCS), яку отримують всі Контейнери у Pod, а також Томи.

Очищення

Видаліть Pod:

kubectl delete pod security-context-demo
kubectl delete pod security-context-demo-2
kubectl delete pod security-context-demo-3
kubectl delete pod security-context-demo-4

Що далі

Елементи на цій сторінці відносяться до сторонніх продуктів чи проєктів, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Ознайомтесь з настановами на вебсайті CNCF для отримання докладної інформації.

Ознайомтесь з посібником з контенту перед тим, як пропонувати додавання посилання на стороні компоненти.

Змінено August 27, 2024 at 9:57 PM PST: Removing the reviewers section from the front matter (81a711722d)