Kubernetes v1.35: Деталізоване управління допоміжними групами переходить в стан загальної доступності (GA)

Від імені Kubernetes SIG Node, ми раді оголосити про перехід деталізованого управління допоміжними групами до загальної доступності (GA) у Kubernetes v1.35!

Нове поле Pod supplementalGroupsPolicy було введено як opt-in альфа-функція для Kubernetes v1.31, а потім перейшло до бета у v1.33. Тепер ця функція є загальнодоступною. Ця функція дозволяє реалізувати більш точний контроль над допоміжними групами в контейнерах Linux, що може зміцнити позицію безпеки, особливо при доступі до томів. Крім того, вона також покращує прозорість деталей UID/GID у контейнерах, забезпечуючи покращений нагляд за безпекою.

Якщо ви плануєте оновити свій кластер з v1.32 або більш ранньої версії, будь ласка, зверніть увагу, що деякі зміни поведінки, що порушують сумісність, були введені з бета (v1.33). Для отримання додаткової інформації дивіться розділи змін поведінки, введених у бета та розгляди оновлення у попередньому блозі про перехід до бета.

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

Навіть якщо більшість адміністраторів/користувачів кластерів Kubernetes можуть не бути обізнаними з цим, за звичай Kubernetes обʼєднує інформацію про групи з Podʼа з інформацією, визначеною в /etc/group в образі контейнера.

Ось приклад; маніфест Podʼа, який вказує spec.securityContext.runAsUser: 1000, spec.securityContext.runAsGroup: 3000 та spec.securityContext.supplementalGroups: 4000 як частину контексту безпеки Podʼа.

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

Який результат команди id в контейнері example-container? Вихідні дані повинні бути схожими на це:

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

Звідки в групі допоміжних ідентифікаторів (groups поле) зʼявився ідентифікатор групи 50000, навіть якщо 50000 взагалі не визначено в маніфесті Podʼа? Відповідь — файл /etc/group в образі контейнера.

Перевірка вмісту файлу /etc/group в образі контейнера містить щось подібне до наступного:

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

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

Таким чином, членство в групі, визначене в /etc/group в образі контейнера для первинного користувача контейнера, неявно обʼєднується з інформацією з Podʼа. Будь ласка, зверніть увагу, що це було дизайнерським рішенням, яке поточні реалізації CRI успадкували від Docker, і спільнота насправді ніколи не переосмислювала це до теперішнього часу.

Що з цим не так?

Неявно обʼєднана інформація про групи з /etc/group в образі контейнера становить ризик для безпеки. Ці неявні GID не можуть бути виявлені або перевірені механізмами політики, оскільки немає запису про них у маніфесті Podʼа. Це може призвести до несподіваних проблем з контролем доступу, особливо при доступі до томів (див. kubernetes/kubernetes#112879 для деталей), оскільки дозволи файлів контролюються UID/GIDs в Linux.

Деталізоване управління допоміжними групами в Pod: supplementaryGroupsPolicy

Щоб вирішити цю проблему, .spec.securityContext Pod тепер включає поле supplementalGroupsPolicy.

Це поле дозволяє вам контролювати, як Kubernetes обчислює допоміжні групи для процесів контейнера всередині Podʼа. Доступні політики:

  • Merge: Інформація про членство в групі, визначена в /etc/group для первинного користувача контейнера, буде обʼєднана. Якщо не вказано, ця політика буде застосована (тобто типова поведінка для зворотної сумісності).

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

Я поясню, як працює політика Strict. Наступний маніфест Podʼа вказує supplementalGroupsPolicy: Strict:

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

Результат команди id в контейнері example-container повинен бути схожим на це:

uid=1000 gid=3000 groups=3000,4000

Ви можете побачити, що політика Strict може виключити групу 50000 з groups!

Таким чином, забезпечення supplementalGroupsPolicy: Strict (забезпечене якимось механізмом політики) допомагає запобігти неявним допоміжним групам у Podʼі.

Примітка:

Контейнер з достатніми привілеями може змінити ідентичність процесу. supplementalGroupsPolicy впливає лише на початкову ідентичність процесу.

Читайте далі для отримання додаткової інформації.

Приєднана ідентичність процесу в статусі Podʼа

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

...
status:
  containerStatuses:
  - name: ctr
    user:
      linux:
        gid: 3000
        supplementalGroups:
        - 3000
        - 4000
        uid: 1000
...

Примітка:

Будь ласка, зверніть увагу, що значення в полі status.containerStatuses[].user.linux є спочатку приєднаною ідентичністю процесу до першого процесу контейнера в контейнері. Якщо контейнер має достатні привілеї для виклику системних викликів, повʼязаних з ідентичністю процесу (наприклад, setuid(2), setgid(2) або setgroups(2) тощо), процес контейнера може змінити свою ідентичність. Таким чином, фактична ідентичність процесу буде динамічною.

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

  • встановлення privilege: false та allowPrivilegeEscalation: false у securityContext вашого контейнера, або
  • приведення вашого pod у відповідність до Restricted політики в Pod Security Standard.

Крім того, kubelet не має видимості у втулку NRI або внутрішній роботі середовища виконання контейнера. Адміністратор кластера, що налаштовує вузли або високо привілейовані робочі навантаження з дозволу локального адміністратора, може змінити допоміжні групи для будь-якого пода. Однак це поза сферою контролю Kubernetes і не повинно бути проблемою для вузлів з посиленою безпекою.

Політика Strict вимагає оновлених середовищ виконання контейнерів

Високорівневе середовище виконання контейнера (наприклад, containerd, CRI-O) відіграє ключову роль у обчисленні допоміжних ідентифікаторів груп, які будуть прикріплені до контейнерів. Таким чином, supplementalGroupsPolicy: Strict вимагає CRI-середовища, яке підтримує цю функцію. Стара поведінка (supplementalGroupsPolicy: Merge) може працювати з CRI-середовищем, яке не підтримує цю функцію, тому що ця політика є повністю зворотно сумісною.

Ось кілька CRI-середовищ, які підтримують цю функцію, і версії, які вам потрібно використовувати:

  • containerd: v2.0 або новіше
  • CRI-O: v1.31 або новіше

І ви можете побачити, чи підтримується ця функція в полі .status.features.supplementalGroupsPolicy вузла. Будь ласка, зверніть увагу, що це поле відрізняється від status.declaredFeatures, введеного в KEP-5328: Node Declared Features(formerly Node Capabilities).

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

Оскільки середовища виконання контейнерів універсально підтримують цю функцію, різні політики безпеки можуть почати забезпечувати поведінку Strict як більш безпечну. Найкраща практика — переконатися, що ваші Podʼи готові до цієї вимоги, і всі допоміжні групи прозоро оголошені в специфікації Podʼа, а не в образах.

Як долучитись

Це вдосконалення було ініційовано спільнотою SIG Node. Будь ласка, приєднуйтесь до нас, щоб звʼязатися зі спільнотою та поділитися своїми ідеями та відгуками щодо вищезазначеної функції та не тільки. Ми з нетерпінням чекаємо на ваші відгуки!

Як дізнатися більше?