Це багатосторінковий друкований вигляд цього розділу. Натисність щоб друкувати.

Повернутися до звичайного перегляду сторінки.

Управління доступом до API Kubernetes

1 - Автентифікація

Ця сторінка надає огляд автентифікації.

Користувачі в Kubernetes

У всіх кластерах Kubernetes є дві категорії користувачів: службові облікові записи, які керуються Kubernetes, і звичайні користувачі.

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

  • адміністратор розповсюджує приватні ключі
  • сховище користувачів, таке як Keystone або Google Accounts
  • файл зі списком імен користувачів та паролів

У цьому відношенні Kubernetes не має обʼєктів, які представляють звичайні облікові записи користувачів. Звичайні користувачі не можуть бути додані до кластера через API виклик.

Хоча звичайного користувача не можна додати через API виклик, будь-який користувач, який предʼявляє дійсний сертифікат, підписаний центром сертифікації (CA) кластера, вважається автентифікованим. У цій конфігурації Kubernetes визначає імʼя користувача з поля загального імені (CN) у сертифікаті (наприклад, "/CN=bob"). Після цього підсистема контролю доступу на основі ролей (RBAC) визначає, чи авторизований користувач для виконання певної операції з ресурсом. Детальніше про це можна прочитати у темі про звичайних користувачів у запиті сертифікатів.

На відміну від цього, службові облікові записи є користувачами, якими керує Kubernetes API. Вони привʼязані до певних просторів імен і створюються автоматично API сервером або вручну через API виклики. Службові облікові записи привʼязані до набору облікових даних, збережених як Secrets, які монтуються в Podʼи, що дозволяє процесам всередині кластера взаємодіяти з Kubernetes API.

API запити привʼязані або до звичайного користувача, або до службово облікового запису, або обробляються як анонімні запити. Це означає, що кожен процес всередині або поза кластером, від користувача, який вводить kubectl на робочій станції, до kubelets на вузлах, до членів панелі управління, повинен автентифікуватися при виконанні запитів до API сервера, або бути обробленим як анонімний користувач.

Стратегії автентифікації

Kubernetes використовує клієнтські сертифікати, токени на предʼявника (bearer tokens) або проксі для автентифікації (authenticating proxy) для автентифікації API запитів через втулки автентифікації. Під час виконання HTTP запитів до API сервера втулки намагаються асоціювати наступні атрибути із запитом:

  • Імʼя користувача: рядок, який ідентифікує кінцевого користувача. Загальноприйняті значення можуть бути kube-admin або jane@example.com.
  • UID: рядок, який ідентифікує кінцевого користувача та намагається бути більш стабільним і унікальним, ніж імʼя користувача.
  • Групи: набір рядків, кожен з яких вказує на членство користувача в певній логічній групі користувачів. Загальноприйняті значення можуть бути system:masters або devops-team.
  • Додаткові поля: елемент map рядків для отримання переліку рядків, що містить додаткову інформацію, яку авторизатори можуть вважати корисною.

Усі значення є непрозорими для системи автентифікації та мають значення лише при інтерпретації авторизатором.

Ви можете одночасно ввімкнути кілька методів автентифікації. Зазвичай вам слід використовувати принаймні два методи:

  • токени службових облікових записів
  • принаймні один інший метод для автентифікації користувачів.

Коли увімкнено кілька модулів автентифікаторів, перший модуль, який успішно автентифікує запит, перериває подальшу оцінку. API сервер не гарантує порядок виконання автентифікаторів.

Група system:authenticated включена до списку груп для всіх автентифікованих користувачів.

Інтеграції з іншими протоколами автентифікації (LDAP, SAML, Kerberos, альтернативні схеми x509 тощо) можуть бути здійснені за допомогою проксі автентифікації або вебхука автентифікації.

X509 клієнтські сертифікати

Автентифікація за допомогою клієнтських сертифікатів увімкнена шляхом передачі опції --client-ca-file=SOMEFILE до API сервера. Вказаний файл повинен містити один або більше центрів сертифікації для використання у валідації клієнтських сертифікатів, представлених API серверу. Якщо представлено клієнтський сертифікат і він підтверджений, загальне імʼя субʼєкта використовується як імʼя користувача для запиту. Починаючи з Kubernetes 1.4, клієнтські сертифікати також можуть вказувати на членство користувача в групах, використовуючи поля організації сертифіката. Щоб включити кілька членств у групах для користувача, включіть кілька полів організації в сертифікат.

Наприклад, використовуючи командний рядок openssl для генерації запиту на підпис сертифіката:

openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

Це створить CSR для імені користувача "jbeda", який належить до двох груп, "app1" і "app2".

Дивіться Керування сертифікатами для отримання інформації про те, як створити клієнтський сертифікат.

Статичний файл токенів

API сервер читає токени на предʼявника (bearer tokens) з файлу при використанні опції --token-auth-file=SOMEFILE у командному рядку. Наразі токени діють безстроково, і список токенів не можна змінити без перезавантаження API сервера.

Файл токенів є csv-файлом з мінімум 3 стовпцями: токен, імʼя користувача, uid користувача, а також необовʼязкові імена груп.

Використання токена на предʼявника у запиті

При використанні автентифікації за допомогою токенів з HTTP клієнта, API сервер очікує заголовок Authorization зі значенням Bearer <token>. Маркер має бути послідовністю символів, яку можна помістити в значення HTTP заголовка, використовуючи лише можливості кодування та цитування HTTP. Наприклад, якщо токен — 31ada4fd-adec-460c-809a-9e56ceb75269, тоді він буде виглядати у заголовку HTTP, як показано нижче.

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

Bootstrap токени

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

Для спрощення початкового налаштування нових кластерів, Kubernetes включає тип токена на предʼявника (bearer token), який керується динамічно, так званий Bootstrap Token. Ці токени зберігаються як Secrets у просторі імен kube-system, де ними можна динамічно керувати та створювати. Менеджер контролерів містить контролер TokenCleaner, який видаляє bootstrap токени в міру їх завершення.

Токени мають форму [a-z0-9]{6}.[a-z0-9]{16}. Перший компонент є ID токена, а другий компонент є Secret токена. Ви вказуєте токен у HTTP заголовку наступним чином:

Authorization: Bearer 781292.db7bc3a58fc5f07e

Ви повинні увімкнути Bootstrap Token Authenticator з прапорцем --enable-bootstrap-token-auth на API сервері. Ви повинні увімкнути контролер TokenCleaner за допомогою прапорця --controllers у Controller Manager. Це робиться за допомогою чогось типу --controllers=*,tokencleaner. kubeadm зробить це за вас, якщо ви використовуєте його для початкового налаштування кластера.

Автентифікатор автентифікує як system:bootstrap:<Token ID>. Він включений у групу system:bootstrappers. Імена користувачів та групи навмисно обмежені, щоб перешкоджати користувачам використовувати ці токени після початкового налаштування. Імена користувачів та групи можна використовувати (і використовуються kubeadm) для створення відповідних політик авторизації для підтримки початкового налаштування кластера.

Детальнішу інформацію про автентифікатор Bootstrap Token та контролери, а також про керування цими токенами за допомогою kubeadm, дивіться у Bootstrap Tokens.

Токени службових облікових записів

Службовий обліковий запис є автоматично увімкненим автентифікатором, який використовує підписані токени на предʼявника (bearer tokens) для перевірки запитів. Втулок приймає два необовʼязкові прапорці:

  • --service-account-key-file Файл, що містить PEM-кодовані x509 RSA або ECDSA приватні або публічні ключі, що використовуються для перевірки токенів службових облікових записів. Вказаний файл може містити кілька ключів, і прапорець може бути вказаний кілька разів з різними файлами. Якщо не вказано, використовується --tls-private-key-file.
  • --service-account-lookup Якщо увімкнено, токени, які видаляються з API, будуть відкликані.

Службові облікові записи зазвичай створюються автоматично API сервером та асоціюються з Podʼами, які працюють у кластері через ServiceAccount Контролер допуску. Токени на предʼявника (bearer tokens) монтуються в Podʼи у відомих місцях, що дозволяє процесам всередині кластера взаємодіяти з API сервером. Облікові записи можуть бути явно асоційовані з Podʼами, використовуючи поле serviceAccountName у PodSpec.

apiVersion: apps/v1 # ця apiVersion актуальна станом з Kubernetes 1.9
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
    # ...
    spec:
      serviceAccountName: bob-the-bot
      containers:
      - name: nginx
        image: nginx:1.14.2

Токени на предʼявника службових облікових записів (bearer tokens) є цілком дійсними для використання за межами кластера і можуть бути використані для створення ідентичностей для тривалих завдань, які бажають взаємодіяти з API Kubernetes. Щоб вручну створити службовий обліковий запис, використовуйте команду kubectl create serviceaccount (NAME). Це створює службовий обліковий запис у поточному просторі імен.

kubectl create serviceaccount jenkins
serviceaccount/jenkins created

Створіть асоційований токен:

kubectl create token jenkins
eyJhbGciOiJSUzI1NiIsImtp...

Створений токен є підписаним JSON Web Token (JWT).

Підписаний JWT може бути використаний як токен на предʼявника (bearer token) для автентифікації як вказаний службовий обліковий запис. Дивіться вище для інформації про те, як токен включається у запит. Зазвичай ці токени монтуються в Podʼи для доступу до API сервера всередині кластера, але можуть бути використані й ззовні кластера.

Службові облікові записи автентифікуються з імʼям користувача system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT), і належать групам system:serviceaccounts та system:serviceaccounts:(NAMESPACE).

Токени OpenID Connect

OpenID Connect — це варіант OAuth2, підтримуваний деякими провайдерами OAuth2, зокрема Microsoft Entra ID, Salesforce та Google. Головне розширення протоколу OAuth2 полягає в додатковому полі, яке повертається разом із токеном доступу, називається ID Token. Цей токен є JSON Web Token (JWT) з добре відомими полями, такими як електронна пошта користувача, підписаними сервером.

Для ідентифікації користувача автентифікатор використовує id_token (а не access_token) з відповіді токена OAuth2 як токен носія. Дивіться вище для того, як токен включається у запит.

sequenceDiagram participant user as Користувач participant idp as Провайдер ідентичності participant kube as kubectl participant api as Сервер API user ->> idp: 1. Вхід до Провайдера ідентичності activate idp idp -->> user: 2. Надання access_token,
id_token та refresh_token deactivate idp activate user user ->> kube: 3. Виклик kubectl
з --token, що є id_token
АБО додавання токенів до .kube/config deactivate user activate kube kube ->> api: 4. Authorization: Bearer... deactivate kube activate api api ->> api: 5. Чи є підпис JWT дійсним? api ->> api: 6. Чи не минув термін дії JWT? (iat+exp) api ->> api: 7. Чи авторизований користувач? api -->> kube: 8. Авторизовано: Виконання
дії та повернення результату deactivate api activate kube kube --x user: 9. Повернення результату deactivate kube
  1. Увійдіть до свого провайдера ідентичності.

  2. Ваш провайдер ідентичності надасть вам access_token, id_token та refresh_token.

  3. Використовуючи kubectl, використовуйте свій id_token із прапорцем --token або додайте його безпосередньо до вашого kubeconfig.

  4. kubectl надсилає ваш id_token у заголовку Authorization до сервера API.

  5. Сервер API перевіряє, чи є підпис JWT дійсним.

  6. Перевіряє, чи не минув термін дії id_token.

    Виконує перевірку вимог та/або користувача, якщо з AuthenticationConfiguration налаштовані вирази CEL.

  7. Переконується, що користувач авторизований.

  8. Після авторизації сервер API повертає відповідь kubectl.

  9. kubectl надає зворотній звʼязок користувачу.

Оскільки всі дані, необхідні для верифікації вашої особи, містяться в id_token, Kubernetes не потрібно "дзвонити додому" до провайдера ідентичності. У моделі, де кожен запит є stateless, це забезпечує дуже масштабоване рішення для автентифікації. Це має кілька викликів:

  1. Kubernetes не має "вебінтерфейсу" для ініціювання процесу автентифікації. Немає оглядача або інтерфейсу для збору облікових даних, тому вам потрібно спочатку автентифікуватися у свого провайдера ідентичності.
  2. id_token не можна відкликати, він схожий на сертифікат, тому він повинен бути короткостроковим (лише кілька хвилин), що може бути дуже незручним, оскільки потрібно отримувати новий токен кожні кілька хвилин.
  3. Для автентифікації в панелі управління Kubernetes, ви повинні використовувати команду kubectl proxy або зворотний проксі, який вставляє id_token.

Налаштування сервера API

Використання прапорців

Щоб увімкнути втулок, налаштуйте наступні прапорці на сервері API:

ПараметрОписПрикладОбовʼязковий
--oidc-issuer-urlURL провайдера, який дозволяє серверу API знаходити публічні ключі підпису. Приймаються лише URL-адреси, що використовують схему https://. Це зазвичай URL виявлення провайдера, змінений на порожній шляхЯкщо URL виявлення OIDC провайдера https://accounts.provider.example/.well-known/openid-configuration, значення повинно бути https://accounts.provider.exampleТак
--oidc-client-idІдентифікатор клієнта, для якого мають бути видані всі токени.kubernetesТак
--oidc-username-claimJWT вимога для використання як імені користувача. Стандартно sub, яке очікується що має бути унікальним ідентифікатором кінцевого користувача. Адміністратори можуть вибрати інші вимоги, такі як email або name, залежно від свого провайдера. Однак, вимоги, відмінні від email, будуть мати префікс URL провайдера, щоб уникнути зіткнень назв з іншими втулками.subНі
--oidc-username-prefixПрефікс, доданий до вимог імені користувача, щоб уникнути зіткнень з наявними іменами (наприклад, користувачами system:). Наприклад, значення oidc: створить імена користувачів, такі як oidc:jane.doe. Якщо цей прапорець не вказано, і значення --oidc-username-claim відрізняється від email, стандартний префікс ( Issuer URL )#, де ( Issuer URL ) — це значення --oidc-issuer-url. Значення - можна використовувати для відключення всіх префіксів.oidc:Ні
--oidc-groups-claimJWT вимога для використання як групи користувача. Якщо вимога присутня, вона повинна бути масивом рядків.groupsНі
--oidc-groups-prefixПрефікс, доданий до вимог груп, щоб уникнути зіткнень з наявними назвами (наприклад, групами system:). Наприклад, значення oidc: створить назви груп, такі як oidc:engineering та oidc:infra.oidc:Ні
--oidc-required-claimПара ключ=значення, яка описує обовʼязкову вимогу в ID Token. Якщо встановлено, вимога перевіряється на наявність в ID Token з відповідним значенням. Повторіть цей прапорець, щоб вказати кілька вимог.claim=valueНі
--oidc-ca-fileШлях до сертифіката для ЦС, який підписав вебсертифікат вашого провайдера ідентичності. Стандартно використовується кореневий ЦС хосту./etc/kubernetes/ssl/kc-ca.pemНі
--oidc-signing-algsПрийняті алгоритми підпису. Стандартно "RS256".RS512Ні
Налаштування автентифікації з файлу
СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [beta]

JWT Автентифікатор є автентифікатором для автентифікації користувачів Kubernetes за допомогою токенів, що відповідають стандарту JWT. Автентифікатор спробує розібрати необроблений ID токен, перевірити, чи він підписаний налаштованим видавцем. Публічний ключ для перевірки підпису перевіряється на публічній точці доступу видавця за допомогою OIDC discovery.

Мінімальний допустимий JWT повинен містити наступні твердження:

{
  "iss": "https://example.com",   // має збігатися з issuer.url
  "aud": ["my-app"],              // принаймні один з елементів в issuer.audiences повинен збігатися з твердженням "aud" в наданих JWT.
  "exp": 1234567890,              // закінчення терміну дії токена у вигляді часу Unix (кількість секунд, що минули з 1 січня 1970 року UTC)
  "<username-claim>": "user"      // це твердження для імені користувача, налаштоване в claimMappings.username.claim або claimMappings.username.expression
}

Підхід з використанням конфігураційного файлу дозволяє налаштовувати декілька JWT автентифікаторів, кожен з унікальними issuer.url та issuer.discoveryURL. Конфігураційний файл навіть дозволяє використовувати CEL вирази для зіставлення тверджень на атрибути користувача, а також для перевірки тверджень та інформації про користувача. API сервер також автоматично перезавантажує автентифікатори при зміні конфігураційного файлу. Ви можете використовувати метрику apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds для моніторингу часу останнього перезавантаження конфігурації сервером API.

Необхідно вказати шлях до конфігураційного файлу автентифікації за допомогою прапорця --authentication-config на сервері API. Якщо ви хочете використовувати командні прапорці замість конфігураційного файлу, вони продовжать працювати як раніше. Щоб отримати нові можливості, такі як налаштування декількох автентифікаторів, встановлення декількох аудиторій для одного видавця, перейдіть на використання конфігураційного файлу.

Для Kubernetes версії v1.31, формат файлу структурованої конфігурації автентифікації є на рівні бета-версії, і механізм використання цієї конфігурації також є на рівні бета-версії. За умови, що ви не вимкнули спеціально функційну можливість StructuredAuthenticationConfiguration для вашого кластера, ви можете увімкнути структуровану автентифікацію, вказавши аргумент командного рядка --authentication-config для kube-apiserver. Приклад файлу конфігурації структурованої автентифікації наведено нижче.

---
#
# УВАГА: це приклад конфігурації.
#        Не використовуйте це для вашого кластера!
#
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
# список автентифікаторів для автентифікації користувачів Kubernetes за допомогою токенів, що відповідають стандарту JWT.
# максимальна кількість дозволених автентифікаторів – 64.
jwt:
- issuer:
    # URL має бути унікальним для всіх автентифікаторів.
    # URL не повинен конфліктувати з видавцем, налаштованим у --service-account-issuer.
    url: https://example.com # Те ж саме, що і --oidc-issuer-url.
    # discoveryURL, якщо вказано, замінює URL, що використовується для отримання інформації про виявлення,
    # замість використання "{url}/.well-known/openid-configuration".
    # Точно вказане значення використовується, тому "/.well-known/openid-configuration"
    # має бути включено у discoveryURL, якщо це потрібно.
    #
    # Поле "issuer" у отриманій інформації про виявлення має збігатися з полем "issuer.url"
    # в AuthenticationConfiguration і буде використовуватися для перевірки твердження "iss" у наданих JWT.
    # Це для сценаріїв, коли точки доступу well-known та jwks розміщені в іншому
    # місці, ніж видавець (наприклад, локально в кластері).
    # discoveryURL має відрізнятися від URL, якщо вказано, і має бути унікальним для всіх автентифікаторів.
    discoveryURL: https://discovery.example.com/.well-known/openid-configuration
    # PEM-кодовані сертифікати CA, які використовуються для перевірки підключення при отриманні
    # інформації про виявлення. Якщо не вказано, буде використовуватися системний перевіряючий.
    # Те саме значення, що і вміст файлу, на який посилається прапорець --oidc-ca-file.
    certificateAuthority: <PEM-кодовані сертифікати CA>    
    # audiences – це набір прийнятних аудиторій, для яких повинен бути виданий JWT.
    # Принаймні один з елементів повинен збігатися з твердженням "aud" у наданих JWT.
    audiences:
    - my-app # Те ж саме, що і --oidc-client-id.
    - my-other-app
    # це повинно бути встановлено на "MatchAny", коли вказано кілька аудиторій.
    audienceMatchPolicy: MatchAny
  # правила, що застосовуються для перевірки тверджень токена для автентифікації користувачів.
  claimValidationRules:
    # Те ж саме, що і --oidc-required-claim key=value.
  - claim: hd
    requiredValue: example.com
    # Замість claim та requiredValue, ви можете використовувати expression для перевірки твердження.
    # expression – це вираз CEL, який оцінюється до булевого значення.
    # всі вирази повинні бути true для успішної перевірки.
  - expression: 'claims.hd == "example.com"'
    # Повідомлення налаштовує повідомлення про помилку, яке відображається в логах сервера API, коли перевірка не вдається.
    message: твердження hd повинно бути встановлено у example.com
  - expression: 'claims.exp - claims.nbf <= 86400'
    message: загальний час життя токена не повинен перевищувати 24 години
  claimMappings:
    # username представляє опцію для атрибута імені користувача.
    # Це єдиний обовʼязковий атрибут.
    username:
      # Те ж саме, що і --oidc-username-claim. Взаємовиключно з username.expression.
      claim: "sub"
      # Те ж саме, що і --oidc-username-prefix. Взаємовиключно з username.expression.
      # якщо username.claim встановлено, username.prefix обовʼязково має бути встановлено.
      # Встановіть значення "" явно, якщо префікс не потрібен.
      prefix: ""
      # Взаємовиключно з username.claim і username.prefix.
      # expression – це вираз CEL, який оцінюється як рядок.
      #
      # 1.  Якщо у виразі username.expression використовується 'claims.email', то 'claims.email_verified' має бути використано у
      # username.expression або extra[*].valueExpression або claimValidationRules[*].expression.
      # Приклад виразу правила валідації заявки, який автоматично збігається з валідацією
      # застосовується, коли username.claim має значення 'email' - 'claims.?email_verified.orValue(true)'.
      # 2.  Якщо імʼя користувача, що оцінюється на основі виразу username.expression, є порожнім рядком, запит на автентифікацію
      # запит не буде виконано.
      expression: 'claims.username + ":external-user"'
    # groups представляє опцію для атрибута групи.
    groups:
      # Те ж саме, що і --oidc-groups-claim. Взаємовиключно з groups.expression.
      claim: "sub"
      # Те ж саме, що і --oidc-groups-prefix. Взаємовиключно з groups.expression.
      # якщо groups.claim встановлено, groups.prefix обовʼязково має бути встановлено.
      # Встановіть значення "" явно, якщо префікс не потрібен.
      prefix: ""
      # Взаємовиключно з groups.claim і groups.prefix.
      # expression – це вираз CEL, який оцінюється як рядок або список рядків.
      expression: 'claims.roles.split(",")'
    # uid представляє опцію для атрибута унікального ідентифікатора.
    uid:
      # Взаємовиключно з uid.expression.
      claim: "sub"
      # Взаємовиключно з uid.claim.
      # expression – це вираз CEL, який оцінюється як рядок.
      expression: 'claims.uid'
    # екстра атрибути для додавання до обʼєктв UserInfo. Ключі мають бути у вигляді шляху з префіксом домену та бути унікальними.
    extra:
    - key: "example.com/tenant"
      # valueExpression – це вираз CEL, який оцінюється як рядок або список рядків.
      valueExpression: 'claims.tenant'
    # правила валідації, що застосовуються до фінального обʼєкта користувача.
    userValidationRules:
      #  expression – це вираз CEL, який оцінюється до булевого значення.
      # всі вирази повинні бути true для успішної перевірки.
    - expression: "!user.username.startsWith('system:')"
      # message налаштовує повідомлення про помилку, яке відображається в логах сервера API, коли перевірка не вдається.
      message: 'неможна використовувате це імʼя користувача, зарезервовано префіксом system:'
    - expression: "user.groups.all(group, !group.startsWith('system:'))"
      message: 'неможна використовувате цю назву групи, зарезервовано префіксом system:'
  • Вираз правил валідації твердження (claim)

    jwt.claimValidationRules[i].expression представляє вираз, який буде оцінений CEL. Вирази CEL мають доступ до вмісту корисного навантаження токена, організованого у змінну CEL claims. claims - це карта імен тверджень (як рядків) до значень тверджень (будь-якого типу).

  • Вираз правила валідації користувача

    jwt.userValidationRules[i].expression представляє вираз, який буде оцінений CEL. Вирази CEL мають доступ до вмісту userInfo, організованого у змінну CEL user. Зверніться до UserInfo API документації для отримання схеми user.

  • Вираз зіставлення твердження

    jwt.claimMappings.username.expression, jwt.claimMappings.groups.expression, jwt.claimMappings.uid.expression jwt.claimMappings.extra[i].valueExpression представляє вираз, який буде оцінений CEL. Вирази CEL мають доступ до вмісту корисного навантаження токена, організованого у змінну CEL claims. claims — зіствлення імен тверджень (як рядків) до значень тверджень (будь-якого типу).

    Щоб дізнатися більше, дивіться Документацію по CEL.

    Ось приклади AuthenticationConfiguration з різними корисними навантаженнями токена.

    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
    - issuer:
        url: https://example.com
        audiences:
        - my-app
      claimMappings:
        username:
          expression: 'claims.username + ":external-user"'
        groups:
          expression: 'claims.roles.split(",")'
        uid:
          expression: 'claims.sub'
        extra:
        - key: 'example.com/tenant'
          valueExpression: 'claims.tenant'
      userValidationRules:
      - expression: "!user.username.startsWith('system:')" # вибарз буде оцінений як true, тоож валідація пройде успішно.
        message: 'username cannot used reserved system: prefix'
    
    TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA
    

    де корисне навантаження токена:

      {
        "aud": "kubernetes",
        "exp": 1703232949,
        "iat": 1701107233,
        "iss": "https://example.com",
        "jti": "7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873",
        "nbf": 1701107233,
        "roles": "user,admin",
        "sub": "auth",
        "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
        "username": "foo"
      }
    

    Токен із зазначеною вище AuthenticationConfiguration створить наступний об’єкт UserInfo і успішно автентифікує користувача.

    {
        "username": "foo:external-user",
        "uid": "auth",
        "groups": [
            "user",
            "admin"
        ],
        "extra": {
            "example.com/tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a"
        }
    }
    

    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
    - issuer:
        url: https://example.com
        audiences:
        - my-app
      claimValidationRules:
      - expression: 'claims.hd == "example.com"' # маркер нижче не має цього твердження, тому перевірка не вдасться.
        message: the hd claim must be set to example.com
      claimMappings:
        username:
          expression: 'claims.username + ":external-user"'
        groups:
          expression: 'claims.roles.split(",")'
        uid:
          expression: 'claims.sub'
        extra:
        - key: 'example.com/tenant'
          valueExpression: 'claims.tenant'
      userValidationRules:
      - expression: "!user.username.startsWith('system:')" # tвибарз буде оцінений як true, тоож валідація пройде успішно.
        message: 'username cannot used reserved system: prefix'
    
    TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA
    

    де корисне навантаження токена:

      {
        "aud": "kubernetes",
        "exp": 1703232949,
        "iat": 1701107233,
        "iss": "https://example.com",
        "jti": "7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873",
        "nbf": 1701107233,
        "roles": "user,admin",
        "sub": "auth",
        "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
        "username": "foo"
      }
    

    Токен із зазначеною вище AuthenticationConfiguration не зможе автентифікуватись, оскільки твердження hd не має значення example.com. Сервер API поверне помилку 401 Unauthorized.

    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
    - issuer:
        url: https://example.com
        audiences:
        - my-app
      claimValidationRules:
      - expression: 'claims.hd == "example.com"'
        message: the hd claim must be set to example.com
      claimMappings:
        username:
          expression: '"system:" + claims.username' # це призведе до додавання префіксу "system:" до імені користувача і не пройде перевірку.
        groups:
          expression: 'claims.roles.split(",")'
        uid:
          expression: 'claims.sub'
        extra:
        - key: 'example.com/tenant'
          valueExpression: 'claims.tenant'
      userValidationRules:
      - expression: "!user.username.startsWith('system:')" # імʼя користувача буде system:foo, а вираз матиме значення false, тому перевірка не вдасться.
        message: 'username cannot used reserved system: prefix'
    
    TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJoZCI6ImV4YW1wbGUuY29tIiwiaWF0IjoxNzAxMTEzMTAxLCJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwianRpIjoiYjViMDY1MjM3MmNkMjBlMzQ1YjZmZGZmY2RjMjE4MWY0YWZkNmYyNTlhYWI0YjdlMzU4ODEyMzdkMjkyMjBiYyIsIm5iZiI6MTcwMTExMzEwMSwicm9sZXMiOiJ1c2VyLGFkbWluIiwic3ViIjoiYXV0aCIsInRlbmFudCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0YSIsInVzZXJuYW1lIjoiZm9vIn0.FgPJBYLobo9jnbHreooBlvpgEcSPWnKfX6dc0IvdlRB-F0dCcgy91oCJeK_aBk-8zH5AKUXoFTlInfLCkPivMOJqMECA1YTrMUwt_IVqwb116AqihfByUYIIqzMjvUbthtbpIeHQm2fF0HbrUqa_Q0uaYwgy8mD807h7sBcUMjNd215ff_nFIHss-9zegH8GI1d9fiBf-g6zjkR1j987EP748khpQh9IxPjMJbSgG_uH5x80YFuqgEWwq-aYJPQxXX6FatP96a2EAn7wfPpGlPRt0HcBOvq5pCnudgCgfVgiOJiLr_7robQu4T1bis0W75VPEvwWtgFcLnvcQx0JWg
    

    де корисне навантаження токена:

      {
        "aud": "kubernetes",
        "exp": 1703232949,
        "hd": "example.com",
        "iat": 1701113101,
        "iss": "https://example.com",
        "jti": "b5b0652372cd20e345b6fdffcdc2181f4afd6f259aab4b7e35881237d29220bc",
        "nbf": 1701113101,
        "roles": "user,admin",
        "sub": "auth",
        "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
        "username": "foo"
      }
    

    Токен із наведеною вище AuthenticationConfiguration створить такий обʼєкт UserInfo:

    {
        "username": "system:foo",
        "uid": "auth",
        "groups": [
            "user",
            "admin"
        ],
        "extra": {
            "example.com/tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a"
        }
    }
    

    який не пройде перевірку користувача, оскільки ім’я користувача починається з system:. Сервер API поверне помилку 401 Unauthorized.

Обмеження
  1. Розподілені твердження не працюють через вирази CEL.
  2. Конфігурація селектора вихідного трафіку не підтримується для викликів до issuer.url та issuer.discoveryURL.

Kubernetes не надає провайдера ідентифікації OpenID Connect. Ви можете використовувати наявного публічного провайдера ідентифікації OpenID Connect (наприклад, Google або інші). Або ж ви можете запустити власного провайдера ідентифікації, такого як dex, Keycloak, CloudFoundry UAA, або Tremolo Security's OpenUnison.

Для того, щоб провайдер ідентифікації працював з Kubernetes, він повинен:

  1. Підтримувати OpenID Connect discovery

    Публічний ключ для перевірки підпису отримується з публічної точки доступу видавця за допомогою OIDC discovery. Якщо ви використовуєте файл конфігурації автентифікації, провайдер ідентифікації не обовʼязково має публічно відкривати точку доступу discovery. Ви можете розмістити точку доступу discovery в іншому місці, ніж видавець (наприклад, локально в кластері) і вказати issuer.discoveryURL у файлі конфігурації.

  2. Працювати через TLS з не застарілими шифрами

  3. Мати сертифікат, підписаний ЦС (навіть якщо ЦС не комерційний або самопідписаний)

Примітка щодо вимоги №3 вище, що потреби в сертифікаті, підписаного ЦС. Якщо ви розгортаєте власного провайдера ідентифікації (на відміну від одного з хмарних провайдерів, таких як Google або Microsoft), ви ПОВИІННІ мати сертифікат вебсервера провайдера ідентифікації, підписаний сертифікатом з прапорцем CA, встановленим на TRUE, навіть якщо він самопідписаний. Це повʼязано з тим, що реалізація клієнта TLS в GoLang дуже сувора до стандартів перевірки сертифікатів. Якщо у вас немає під рукою ЦС, ви можете використовувати скрипт gencert від команди Dex для створення простого ЦС і пари підписаного сертифіката та ключа. Або ви можете використовувати цей подібний скрипт, який генерує сертифікати SHA256 з довшим терміном дії та більшим розміром ключа.

Інструкції з налаштування для конкретних систем:

Використання kubectl

Варіант 1 — Автентифікатор OIDC

Перший варіант — використання автентифікатора kubectl oidc, який встановлює id_token як токен на предʼявника для всіх запитів і оновлює токен після закінчення його терміну дії. Після того, як ви увійшли до свого провайдера, використовуйте kubectl, щоб додати ваші id_token, refresh_token, client_id та client_secret для налаштування втулка.

Провайдери, які не повертають id_token як частину відповіді на оновлення токена, не підтримуються цим втулком і повинні використовувати "Варіант 2" нижче.

kubectl config set-credentials USER_NAME \
   --auth-provider=oidc \
   --auth-provider-arg=idp-issuer-url=( issuer url ) \
   --auth-provider-arg=client-id=( your client id ) \
   --auth-provider-arg=client-secret=( your client secret ) \
   --auth-provider-arg=refresh-token=( your refresh token ) \
   --auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
   --auth-provider-arg=id-token=( your id_token )

Як приклад, запустіть наведену нижче команду після автентифікації у постачальника ідентифікаційної інформації:

kubectl config set-credentials mmosley  \
        --auth-provider=oidc  \
        --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP  \
        --auth-provider-arg=client-id=kubernetes  \
        --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5  \
        --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
        --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
        --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

Що створить наведену нижче конфігурацію:

users:
- name: mmosley
  user:
    auth-provider:
      config:
        client-id: kubernetes
        client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
        id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
        idp-certificate-authority: /root/ca.pem
        idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
        refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
      name: oidc

Після закінчення терміну дії вашого id_token kubectl спробує оновити ваш id_token за допомогою ваших refresh_token і client_secret, зберігаючи нові значення для refresh_token і id_token у вашому .kube/config.

Варіант 2 — Використання опції --token

Команда kubectl дозволяє передати токен за допомогою параметра --token. Скопіюйте та вставте id_token у цей параметр:

kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Автентифікація за допомогою вебхука

Автентифікація за допомогою вебхука — це механізм перевірки маркерів носіїв.

  • --authentication-token-webhook-config-file — файл конфігурації, який описує, як отримати доступ до віддаленого сервісу вебхука.
  • --authentication-token-webhook-cache-ttl — як довго кешувати рішення щодо автентифікації. Стандартно дві хвилини.
  • --authentication-token-webhook-version визначає, чи використовувати authentication.k8s.io/v1beta1 або authentication.k8s.io/v1 обʼєкти TokenReview для надсилання/отримання інформації від вебхука. Стандартно v1beta1.

Файл конфігурації використовує формат файлу kubeconfig. У файлі clusters посилається на віддалений сервіс, а users посилається на вебхук API-сервера. Приклад:

# Версія API Kubernetes
apiVersion: v1
# Тип обʼєкта API
kind: Config
# clusters посилається на віддалений сервіс.
clusters:
  - name: name-of-remote-authn-service
    cluster:
      certificate-authority: /path/to/ca.pem         # ЦС для перевірки віддаленого сервісу.
      server: https://authn.example.com/authenticate # URL віддаленого сервісу для запиту. 'https' рекомендовано для промислового застосування.

# users посилається на конфігурацію вебхука API-сервера.
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # сертифікат для використання втулком вебхука
      client-key: /path/to/key.pem          # ключ, що відповідає сертифікату

# файли kubeconfig потребують контексту. Надати один для API-сервера.
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authn-service
    user: name-of-api-server
  name: webhook

Коли клієнт намагається автентифікуватись на API-сервері за допомогою токена на предʼявника, як розглядалось вище, вебхук автентифікації надсилає POST-запит з JSON-серіалізованим обʼєктом TokenReview, що містить токен для віддаленого сервісу.

Зверніть увагу, що обʼєкти API вебхука підпадають під ті ж правила сумісності версій, що й інші обʼєкти API Kubernetes. Виконавці повинні перевірити поле apiVersion запиту, щоб забезпечити правильну десеріалізацію, і повинні відповідати обʼєктом TokenReview тієї ж версії, що й запит.

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "spec": {
    # Непрозорий токен на прежʼявника носія, надісланий на API-сервер
    "token": "014fbff9a07c...",

    # Необовʼязковий список ідентифікаторів аудиторії для сервера, якому був представлений токен.
    # Автентифікатори токенів, що враховують аудиторію (наприклад, OIDC автентифікатори токенів)
    # повинні перевірити, що токен був призначений для принаймні однієї з аудиторій у цьому списку,
    # і повернути перетин цього списку та дійсних аудиторій для токена в статусі відповіді.
    # Це гарантує, що токен дійсний для автентифікації на сервері, якому він був представлений.
    # Якщо аудиторії не надані, токен повинен бути перевірений для автентифікації на API-сервері Kubernetes.
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    # Непрозорий токен на предʼявника, надісланий на API-сервер
    "token": "014fbff9a07c...",

    # Необовʼязковий список ідентифікаторів аудиторії для сервера, якому був представлений токен.
    # Автентифікатори токенів, що враховують аудиторію (наприклад, OIDC автентифікатори токенів)
    # повинні перевірити, що токен був призначений для принаймні однієї з аудиторій у цьому списку,
    # і повернути перетин цього списку та дійсних аудиторій для токена в статусі відповіді.
    # Це гарантує, що токен дійсний для автентифікації на сервері, якому він був представлений.
    # Якщо аудиторії не надані, токен повинен бути перевірений для автентифікації на API-сервері Kubernetes.
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

Віддалений сервіс повинен заповнити поле status запиту, щоб вказати на успішність входу. Поле spec тіла відповіді ігнорується та може бути опущене. Віддалений сервіс повинен повернути відповідь, використовуючи ту ж версію API TokenReview, яку він отримав. Успішна перевірка маркера носія буде виглядати так:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      # Обовʼязково
      "username": "janedoe@example.com",
      # Необовʼязково
      "uid": "42",
      # Необовʼязкові членства у групах
      "groups": ["developers", "qa"],
      # Необовʼязкова додаткова інформація, надана автентифікатором.
      # Це не повинно містити конфіденційних даних, оскільки це може бути записано в логах
      # або обʼєктах API та доступно для вебхуків допуску.
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    },
    # Необовʼязковий список, який можуть повернути автентифікатори токенів, що враховують аудиторію,
    # містить аудиторії зі списку `spec.audiences`, для яких токен був дійсним.
    # Якщо це опущено, токен вважається дійсним для автентифікації на API-сервері Kubernetes.
    "audiences": ["https://myserver.example.com"]
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      # Обовʼязково
      "username": "janedoe@example.com",
      # Необовʼязково
      "uid": "42",
      # Необовʼязкові членства у групах
      "groups": ["developers", "qa"],
      # Необовʼязкова додаткова інформація, надана автентифікатором.
      # Це не повинно містити конфіденційних даних, оскільки це може бути записано в логах
      # або обʼєктах API та доступно для вебхуків допуску.
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    },
    # Необовʼязковий список, який можуть повернути автентифікатори токенів, що враховують аудиторію,
    # містить аудиторії зі списку `spec.audiences`, для яких токен був дійсним.
    # Якщо це опущено, токен вважається дійсним для автентифікації на API-сервері Kubernetes.
    "audiences": ["https://myserver.example.com"]
  }
}

Невдала спроба запиту виглядатиме так:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false,
    # Необовʼязково включати деталі, чому автентифікація не вдалася.
    # Якщо помилка не вказана, API поверне загальне повідомлення Unauthorized.
    # Поле error ігнорується, коли authenticated=true.
    "error": "Credentials are expired"
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false,
    # Необовʼязково включати деталі, чому автентифікація не вдалася.
    # Якщо помилка не вказана, API поверне загальне повідомлення Unauthorized.
    # Поле error ігнорується, коли authenticated=true.
    "error": "Credentials are expired"
  }
}

Проксі автентифікації

API-сервер може бути налаштований для ідентифікації користувачів на основі значень заголовків запиту, таких як X-Remote-User. Цей механізм призначений для використання в комбінації з проксі-сервером автентифікації, який встановлює значення заголовка запиту.

  • --requestheader-username-headers Обовʼязково, нечутливий до регістру. Імена заголовків для перевірки, у порядку, для ідентифікації користувача. Перший заголовок, який містить значення, використовується як імʼя користувача.
  • --requestheader-group-headers Версія 1.6+. Необовʼязково, нечутливий до регістру. Пропонується використовувати "X-Remote-Group". Імена заголовків для перевірки, у порядку, для визначення груп користувача. Усі значення в усіх зазначених заголовках використовуються як імена груп.
  • --requestheader-extra-headers-prefix Версія 1.6+. Необовʼязково, нечутливий до регістру. Пропонується використовувати "X-Remote-Extra-". Префікси заголовків для перевірки додаткової інформації про користувача (зазвичай використовується налаштованим втулком авторизації). У всіх заголовків, які починаються з будь-якого з зазначених префіксів, префікси вилучаються. Решта імені заголовка перетворюється на нижній регістр і декодується у відповідності з RFC 3986, і стає додатковим ключем, а значення заголовка — додатковим значенням.

Наприклад, з цією конфігурацією:

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

цей запит:

GET / HTTP/1.1
X-Remote-User: fido
X-Remote-Group: dogs
X-Remote-Group: dachshunds
X-Remote-Extra-Acme.com%2Fproject: some-project
X-Remote-Extra-Scopes: openid
X-Remote-Extra-Scopes: profile

призведе до такої інформації про користувача:

name: fido
groups:
- dogs
- dachshunds
extra:
  acme.com/project:
  - some-project
  scopes:
  - openid
  - profile

Для запобігання підробці заголовків, проксі-сервер автентифікації повинен представити дійсний клієнтський сертифікат на API-сервер для перевірки за допомогою вказаного CA перед тим, як заголовки запиту будуть перевірені. ПОПЕРЕДЖЕННЯ: не використовуйте CA, який використовується в іншому контексті, якщо ви не розумієте ризики та механізми захисту використання CA.

  • --requestheader-client-ca-file Обовʼязково. Файл з сертифікатами у форматі PEM. Дійсний клієнтський сертифікат повинен бути представлений і перевірений за допомогою сертифікатів у вказаному файлі перед перевіркою заголовків запиту для імен користувачів.
  • --requestheader-allowed-names Необовʼязково. Список значень Common Name (CN). Якщо встановлено, дійсний клієнтський сертифікат з CN з вказаного списку повинен бути представлений перед перевіркою заголовків запиту для імен користувачів. Якщо порожній, дозволений будь-який CN.

Анонімні запити

Коли увімкнено, запити, які не відхиляються іншими налаштованими методами автентифікації, розглядаються як анонімні запити та отримують імʼя користувача system:anonymous і групу system:unauthenticated.

Наприклад, на сервері з налаштованою автентифікацією за допомогою токенів та увімкненим анонімним доступом, запит із недійсним токеном автентифікації отримає помилку 401 Unauthorized. Запит без токена автентифікації буде розглядатися як анонімний запит.

У версіях 1.5.1-1.5.x анонімний доступ типово вимкнено і може бути увімкнено шляхом додавання опції --anonymous-auth=true до API-сервера.

У версіях 1.6+ анонімний доступ типово увімкнено, якщо використовується режим авторизації, відмінний від AlwaysAllow, і може бути вимкнено шляхом додавання опції --anonymous-auth=false до API-сервера. Починаючи з версії 1.6, авторизатори ABAC і RBAC вимагають явної авторизації користувача system:anonymous або групи system:unauthenticated, тому застарілі правила політики, які надають доступ користувачеві * або групі *, не включають анонімних користувачів.

Налаштування анонімної автентифікації

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

AuthenticationConfiguration можна використовувати для налаштування анонімного автентифікатора. Щоб увімкнути налаштування анонімної автентифікації через конфігураційний файл, потрібно увімкнути функціональну можливість AnonymousAuthConfigurableEndpoints. Коли ця функціональна можливість увімкнена, ви не можете встановити прапорець --anonymous-auth.

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

Ось приклад конфігураційного файлу автентифікації:

---
#
# УВАГА: це приклад конфігурації.
#        Не використовуйте його для вашого власного кластера!
#
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
anonymous:
  enabled: true
  conditions:
  - path: /livez
  - path: /readyz
  - path: /healthz

У наведеній конфігурації лише точки доступу /livez, /readyz і /healthz доступні для анонімних запитів. Будь-які інші точки доступу будуть недоступні, навіть якщо це дозволено конфігурацією RBAC.

Імперсонізація користувачів

Користувач може діяти від імені іншого користувача через заголовки імперсонізації. Це дозволяє вручну перевизначити інформацію про користувача, який виконує запит. Наприклад, адміністратор може використовувати цю функцію для налагодження політики авторизації, тимчасово імперсонуючи іншого користувача та перевіряючи, чи запит відхилено.

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

  • Користувач робить API-запит зі своїми обліковими даними і заголовками імперсонізації.
  • API-сервер автентифікує користувача.
  • API-сервер переконується, що автентифіковані користувачі мають права імперсонізації.
  • Інформація про користувача замінюється значеннями імперсонізації.
  • Запит оцінюється, авторизація діє на основі імперсонованої інформації про користувача.

Для здійснення запиту на імперсонізацію можна використовувати такі заголовки HTTP:

  • Impersonate-User: Імʼя користувача, від імені якого потрібно діяти.
  • Impersonate-Group: Імʼя групи, від імені якої потрібно діяти. Може надаватися кілька разів для встановлення кількох груп. Опціонально. Потрібен Impersonate-User.
  • Impersonate-Extra-( extra name ): Динамічний заголовок для звʼязування додаткових полів з користувачем. Опціонально. Потрібен Impersonate-User. Для збереження послідовності ( extra name ) повинно бути малими літерами, а будь-які символи, які не є допустимими в HTTP-заголовках, МАЮТЬ бути у форматі utf8 та процентно-кодовані.
  • Impersonate-Uid: Унікальний ідентифікатор, який представляє імперсонованого користувача. Опціонально. Потрібен Impersonate-User. Kubernetes не накладає жодних вимог щодо формату цього рядка.

Приклад заголовків імперсонізації при імперсонуванні користувача з групами:

Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins

Приклад заголовків імперсонізації при імперсонуванні користувача з UID та додатковими полями:

Impersonate-User: jane.doe@example.com
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development
Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b

Для використання kubectl встановіть прапорець --as для налаштування заголовка Impersonate-User, встановіть прапорець --as-group для налаштування заголовка Impersonate-Group.

kubectl drain mynode
Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

Встановіть прапорці --as і --as-group:

kubectl drain mynode --as=superman --as-group=system:masters
node/mynode cordoned
node/mynode drained

Для імперсонізації користувача, групи, ідентифікатора користувача (UID) або додаткових полів, користувач, який виконує імперсонізацію, повинен мати можливість виконувати дію "impersonate" з типом атрибута, який імперсонується ("user", "group", "uid" і т.д.). Для кластерів, що використовують втулок авторизації RBAC, наступна роль ClusterRole охоплює правила, необхідні для налаштування заголовків імперсонізації користувачів і груп:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: impersonator
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]

Для імперсонізації, додаткові поля та імперсоновані UID належать до групи apiGroup "authentication.k8s.io". Додаткові поля оцінюються як субресурси ресурсу "userextras". Щоб дозволити користувачеві використовувати заголовки імперсонізації для додаткового поля "scopes" та для UID, користувачеві слід надати таку роль:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: scopes-and-uid-impersonator
rules:
# Може встановлювати заголовок "Impersonate-Extra-scopes" та заголовок "Impersonate-Uid".
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes", "uids"]
  verbs: ["impersonate"]

Значення заголовків імперсонізації також можна обмежити, обмеживши набір resourceNames, які ресурс може приймати.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: limited-impersonator
rules:
# Може імперсонувати користувача "jane.doe@example.com"
- apiGroups: [""]
  resources: ["users"]
  verbs: ["impersonate"]
  resourceNames: ["jane.doe@example.com"]

# Може імперсонувати групи "developers" та "admins"
- apiGroups: [""]
  resources: ["groups"]
  verbs: ["impersonate"]
  resourceNames: ["developers","admins"]

# Може імперсонувати додаткове поле "scopes" зі значеннями "view" і "development"
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]
  resourceNames: ["view", "development"]

# Може імперсонувати UID "06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"
- apiGroups: ["authentication.k8s.io"]
  resources: ["uids"]
  verbs: ["impersonate"]
  resourceNames: ["06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"]

Втулки облікових даних client-go

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

k8s.io/client-go та інструменти, які використовують його, такі як kubectl та kubelet, можуть виконувати зовнішню команду для отримання облікових даних користувача.

Ця функція призначена для клієнтських інтеграцій з протоколами автентифікації, які не підтримуються на рівні k8s.io/client-go (LDAP, Kerberos, OAuth2, SAML та ін.). Вутулок реалізує логіку, специфічну для протоколу, а потім повертає непрозорі облікові дані для використання. Майже всі випадки використання втулків автентифікації потребують наявності компоненту на стороні сервера з підтримкою автентифікації токенів webhook, щоб інтерпретувати формат облікових даних, який генерується клієнтським втулком.

Приклад використання

У гіпотетичному сценарії використання організація запускає зовнішню службу, яка обмінює облікові дані LDAP на підписані токени, специфічні для користувача. Служба також може відповідати на запити автентифікатора токенів webhook, щоб перевірити токени. Користувачам буде потрібно встановити втулок автентифікації на своїй робочій станції.

Для автентифікації в API:

  • Користувач видає команду kubectl.
  • Вутулок облікових даних запитує користувача облікові дані LDAP, обмінює облікові дані зовнішньою службою на токен.
  • Вутулок облікових даних повертає токен client-go, який використовує його як токен власника на сервері API.
  • Сервер API використовує автентифікатор токенів webhook, щоб надіслати TokenReview зовнішній службі.
  • Зовнішня служба перевіряє підпис на токені та повертає імʼя користувача та групи.

Налаштування

Втулки облікових даних налаштовуються через файли конфігурації kubectl як частина полів користувача.

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # Команда для виконання. Обовʼязково.
      command: "example-client-go-exec-plugin"

      # Версія API, яку слід використовувати при декодуванні ресурсу ExecCredentials. Обовʼязково.
      #
      # Версія API, що повертається втулком, ПОВИННА відповідати версії, вказаній тут.
      #
      # Щоб інтегруватися з інструментами, які підтримують кілька версій (наприклад, client.authentication.k8s.io/v1beta1),
      # встановіть змінну середовища, передайте аргумент інструменту, що вказує, яку версію очікує втулка виконання,
      # або прочитайте версію з обʼєкта ExecCredential у змінній середовища KUBERNETES_EXEC_INFO.
      apiVersion: "client.authentication.k8s.io/v1"

      # Змінні середовища, що встановлюються під час виконання втулку. Необовʼязково.
      env:
      - name: "FOO"
        value: "bar"

      # Аргументи, які передаються під час виконання втулку. Необовʼязково.
      args:
      - "arg1"
      - "arg2"

      # Текст, який показуєтсья користувачу, коли виконуваний файл не знайдено. Необовʼязково.
      installHint: |
        example-client-go-exec-plugin потрібно для автентифікації
        в поточному кластері.  Його можна встановити:

        На macOS: brew install example-client-go-exec-plugin

        На Ubuntu: apt-get install example-client-go-exec-plugin

        На Fedora: dnf install example-client-go-exec-plugin

        ...        

      # Чи надавати інформацію про кластер, яка може містити
      # дуже великі дані сертифікату CA, цьому втулка виконання як частину KUBERNETES_EXEC_INFO
      # змінної середовища.
      provideClusterInfo: true

      # Угода між втулком виконання та стандартним введенням/виведенням. Якщо
      # угода не може бути виконана, цей втулок виконання не буде запущено, та буде
      # повернено помилку. Допустимі значення: "Never" (цей втулок виконання ніколи не використовує стандартний ввід),
      # "IfAvailable" (цей втулок виконання хоче використовувати стандартний ввід, якщо він доступний),
      # або "Always" (цей втулок виконання вимагає стандартний ввід для роботи). Обовʼязково.
      interactiveMode: Never
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
    extensions:
    - name: client.authentication.k8s.io/exec # зарезервоване імʼя розширення для кожної конфігурації виконання кластера
      extension:
        arbitrary: config
        this: може бути надано через змінну середовища KUBERNETES_EXEC_INFO при встановленні provideClusterInfo
        you: ["можете", "покласти", "будь-що", "тут"]
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # Команда для виконання. Обовʼязково.
      command: "example-client-go-exec-plugin"

      # Версія API, яку слід використовувати при декодуванні ресурсу ExecCredentials. Обовʼязково.
      #
      # Версія API, повернута втулком, ПОВИННА відповідати версії, вказаній тут.
      #
      # Щоб інтегруватися з інструментами, які підтримують кілька версій (наприклад, client.authentication.k8s.io/v1),
      # встановіть змінну середовища, перед ```yaml
      apiVersion: "client.authentication.k8s.io/v1beta1"

      # Змінні середовища, що встановлюються під час виконання втулку. Необовʼязково.
      env:
      - name: "FOO"
        value: "bar"

      # Аргументи, які передаються під час виконання втулку. Необовʼязково.
      args:
      - "arg1"
      - "arg2"

      # Текст, який показується користувачу, коли виконуваний файл не знайдено. Необовʼязково.
      installHint: |
        example-client-go-exec-plugin потрібно для автентифікації
        в поточному кластері.  Його можна встановити:

        На macOS: brew install example-client-go-exec-plugin

        На Ubuntu: apt-get install example-client-go-exec-plugin

        На Fedora: dnf install example-client-go-exec-plugin

        ...        

      # Чи надавати інформацію про кластер, яка може містити
      # дуже великі дані сертифікату CA, цьому втулку виконання як частину KUBERNETES_EXEC_INFO
      # змінної середовища.
      provideClusterInfo: true

      # Угода між втулком виконання та стандартним введенням/виведенням. Якщо
      # угода не може бути виконана, цей втулок виконання не буде запущено, а буде
      # повернено помилку. Допустимі значення: "Never" (цей втулок виконання ніколи не використовує стандартний ввід),
      # "IfAvailable" (цей втулок виконання хоче використовувати стандартний ввід, якщо він доступний),
      # або "Always" (цей втулок виконання вимагає стандартний ввід для роботи). Необовʼязково.
      # За замовчуванням - "IfAvailable".
      interactiveMode: Never
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
    extensions:
    - name: client.authentication.k8s.io/exec # зарезервоване імʼя розширення для кожної конфігурації виконання кластера
      extension:
        arbitrary: config
        this: може бути надано через змінну середовища KUBERNETES_EXEC_INFO при встановленні provideClusterInfo
        you: ["можете", "покласти", "будь-що", "тут"]
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

Відносні шляхи до команд інтерпретуються відносно теки файлу конфігурації. Якщо KUBECONFIG встановлено на /home/jane/kubeconfig, а команда виконання — ./bin/example-client-go-exec-plugin, то виконується бінарний файл /home/jane/bin/example-client-go-exec-plugin.

- name: my-user
  user:
    exec:
      # Шлях відносно теки kubeconfig
      command: "./bin/example-client-go-exec-plugin"
      apiVersion: "client.authentication.k8s.io/v1"
      interactiveMode: Never

Формати вводу та виводу

Виконана команда виводить обʼєкт ExecCredential у stdout. k8s.io/client-go автентифікується в Kubernetes API, використовуючи отримані облікові дані в status. Виконана команда отримує обʼєкт ExecCredential на вхід через змінну середовища KUBERNETES_EXEC_INFO. Цей вхід містить корисну інформацію, таку як очікувана версія API поверненого обʼєкта ExecCredential та чи може втулок використовувати stdin для взаємодії з користувачем.

Під час запуску з інтерактивної сесії (тобто термінал), stdin може бути наданий прямо втулку. Втулки повинні використовувати поле spec.interactive вхідного обʼєкта ExecCredential зі змінної середовища KUBERNETES_EXEC_INFO для визначення, чи був наданий stdin. Вимоги втулка до stdin (тобто чи stdin є необовʼязковим, строго обовʼязковим або ніколи не використовується для успішного запуску втулка) вказується за допомогою поля user.exec.interactiveMode у kubeconfig (див. таблицю нижче для дійсних значень). Поле user.exec.interactiveMode є необовʼязковим у client.authentication.k8s.io/v1beta1 і обовʼязковим у client.authentication.k8s.io/v1.

Значення interactiveMode
Значення interactiveModeЗначення
NeverЦей втулок виконання ніколи не потребує використання стандартного вводу, і тому втулок виконання буде запущений незалежно від того, чи доступний стандартний ввід для введення користувача.
IfAvailableЦей втулок виконання хоче використовувати стандартний ввід, якщо він доступний, але може працювати, якщо стандартний ввід недоступний. Тому втулок виконання буде запущений незалежно від наявності введення з стандартного вводу. Якщо стандартний ввід доступний для введення користувача, він буде наданий цьому втулку виконання.
AlwaysЦей втулок виконання потребує стандартний ввід для роботи, і тому втулок виконання буде запущений лише тоді, коли стандартний ввід доступний для введення користувача. Якщо стандартний ввід недоступний для введення користувача, втулок виконання не буде запущений, і виконавець втулку поверне помилку.

Для використання облікових даних токена власника, втулок повертає токен у статусі ExecCredential

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

Альтративно, можна повернути PEM-кодований сертифікат клієнта та ключ для використання TLS-автентифікації клієнта. Якщо втулок повертає різний сертифікат та ключ при наступному виклику, k8s.io/client-go закриє існуючі зʼєднання з сервером, щоб змусити новий TLS-обмін.

Якщо вказано, що clientKeyData та clientCertificateData повинні бути присутніми.

clientCertificateData може містити додаткові проміжні сертифікати для відправки на сервер.

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

Додатково відповідь може включати термін дії облікового запису, форматований як RFC 3339 мітка часу.

Наявність або відсутність терміну дії має такий вплив:

  • Якщо термін дії включений, токен власника та TLS-облікові дані кешуються до моменту закінчення строку дії, або якщо сервер відповідає з кодом стану HTTP 401, або при завершенні процесу.
  • Якщо термін дії відсутній, токен власника та TLS-облікові дані кешуються до моменту, коли сервер відповідає з кодом стану HTTP 401 або до моменту завершення процесу.

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

Щоб дозволити втулку виконання отримувати інформацію, що специфічна для кластера, встановіть provideClusterInfo у поле user.exec в kubeconfig. Втулок потім отримає цю інформацію, специфічну для кластера, у змінній середовища KUBERNETES_EXEC_INFO. Інформацію з цієї змінної середовища можна використовувати для виконання логіки отримання облікових даних, специфічних для кластера. Наступний маніфест ExecCredential описує зразок інформації для кластера.

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "spec": {
    "cluster": {
      "server": "https://172.17.4.100:6443",
      "certificate-authority-data": "LS0t...",
      "config": {
        "arbitrary": "config",
        "this": "can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo",
        "you": ["can", "put", "anything here"]
    },
    "interactive": true
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "spec": {
    "cluster": {
      "server": "https://172.17.4.100:6443",
      "certificate-authority-data": "LS0t...",
      "config": {
        "arbitrary": "config",
        "this": "can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo",
        "you": ["can", "put", "anything", "here"]
      }
    },
    "interactive": true
  }
}

Доступ API до інформації про автентифікацію для клієнта

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

Якщо ваш кластер має включений API, ви можете використовувати API SelfSubjectReview, щоб дізнатися, як ваш кластер Kubernetes показує вашу інформацію про автентифікацію, щоб ідентифікувати вас як клієнта. Це працює незалежно від того, чи автентифікуєтеся ви як користувач (зазвичай представляючи реальну особу), чи як обліковий запис ServiceAccount.

Обʼєкти SelfSubjectReview не мають полів, які конфігуруються. При отриманні запиту API-сервер Kubernetes заповнює статус атрибутами користувача та повертає його користувачеві.

Приклад запиту (тіло буде SelfSubjectReview):

POST /apis/authentication.k8s.io/v1/selfsubjectreviews
{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview"
}

Приклад відповіді:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview",
  "status": {
    "userInfo": {
      "name": "jane.doe",
      "uid": "b6c7cfd4-f166-11ec-8ea0-0242ac120002",
      "groups": [
        "viewers",
        "editors",
        "system:authenticated"
      ],
      "extra": {
        "provider_id": ["token.company.example"]
      }
    }
  }
}

Для зручності доступний також запит kubectl auth whoami. Виконання цієї команди призведе до наступного виводу (але різні атрибути користувача будуть показані):

  • Простий приклад виводу

    ATTRIBUTE         VALUE
    Username          jane.doe
    Groups            [system:authenticated]
    
  • Складний приклад, який включає додаткові атрибути

    ATTRIBUTE         VALUE
    Username          jane.doe
    UID               b79dbf30-0c6a-11ed-861d-0242ac120002
    Groups            [students teachers system:authenticated]
    Extra: skills     [reading learning]
    Extra: subjects   [math sports]
    

За допомогою прапорця виводу також можна надрукувати JSON- або YAML-представлення результату:

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview",
  "status": {
    "userInfo": {
      "username": "jane.doe",
      "uid": "b79dbf30-0c6a-11ed-861d-0242ac120002",
      "groups": [
        "students",
        "teachers",
        "system:authenticated"
      ],
      "extra": {
        "skills": [
          "reading",
          "learning"
        ],
        "subjects": [
          "math",
          "sports"
        ]
      }
    }
  }
}

apiVersion: authentication.k8s.io/v1
kind: SelfSubjectReview
status:
  userInfo:
    username: jane.doe
    uid: b79dbf30-0c6a-11ed-861d-0242ac120002
    groups:
    - students
    - teachers
    - system:authenticated
    extra:
      skills:
      - reading
      - learning
      subjects:
      - math
      - sports

Ця функція є дуже корисною, коли використовується складний потік автентифікації в кластері Kubernetes, наприклад, якщо ви використовуєте автентифікацію за допомогою токена через webhook або автентифікацію через проксі.

Стандартно всі автентифіковані користувачі можуть створювати обʼєкти SelfSubjectReview, коли функція APISelfSubjectReview включена. Це дозволено за допомогою кластерної ролі system:basic-user.

Що далі

2 - Автентифікація за допомогою Bootstrap-токенів

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

Bootstrap-токени — це простий токен на предʼявника, який призначений для використання під час створення нових кластерів або приєднання нових вузлів до наявного кластера. Він був створений для підтримки kubeadm, але може використовуватися в інших контекстах для користувачів, які бажають створювати кластери без kubeadm. Також він призначений для роботи, через політику RBAC, з kubelet TLS Bootstrapping.

Огляд Bootstrap-токенів

Bootstrap-токени визначені як певний тип secretʼів (bootstrap.kubernetes.io/token), що знаходяться в просторі імен kube-system. Ці secret читаються Bootstrap Authenticator з API Server. Протерміновані токени видаляються контролером TokenCleaner у Controller Manager. Токени також використовуються для створення підпису для конкретного ConfigMap, який використовується у процесі "виявлення" через контролер BootstrapSigner.

Формат токена

Bootstrap-токени мають форму abcdef.0123456789abcdef. Більш формально, вони повинні відповідати регулярному виразу [a-z0-9]{6}\.[a-z0-9]{16}.

Перша частина токена — це "ID токена" і вважається публічною інформацією. Вона використовується, коли потрібно посилатися на токен без розкриття секретної частини, яка використовується для автентифікації. Друга частина — це "Секрет токена" і нею треба ділитися тільки з довіреними сторонами.

Увімкнення автентифікації за допомогою Bootstrap-токенів

Автентифікатор Bootstrap Token можна увімкнути за допомогою наступного прапорця на API-сервері:

--enable-bootstrap-token-auth

Після увімкнення, токени для завантаження можуть використовуватися як облікові дані токена на предʼявника для автентифікації запитів до API-сервера.

Authorization: Bearer 07401b.f395accd246ae52d

Токени автентифікуються як імʼя користувача system:bootstrap:<token id> і є членами групи system:bootstrappers. Додаткові групи можуть бути вказані в секреті токена.

Протерміновані токени можуть бути автоматично видалені шляхом увімкнення контролера tokencleaner у Controller Manager.

--controllers=*,tokencleaner

Формат Secretʼу Bootstrap-токена

Кожен дійсний токен підтримується секретом у просторі імен kube-system. Повний документ проєктування можна знайти тут.

Ось як виглядає цей Secret.

apiVersion: v1
kind: Secret
metadata:
  # Назва МАЄ бути у формі "bootstrap-token-<token id>"
  name: bootstrap-token-07401b
  namespace: kube-system

# Тип МАЄ бути 'bootstrap.kubernetes.io/token'
type: bootstrap.kubernetes.io/token
stringData:
  # Опис, зрозумілий людині. Необовʼязково.
  description: "Стандартний bootstrap-токен, згенерований 'kubeadm init'."

  # ID та секрет токена. Обовʼязково.
  token-id: 07401b
  token-secret: f395accd246ae52d

  # Термін дії. Необовʼязково.
  expiration: 2017-03-10T03:22:11Z

  # Дозволені використання.
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"

  # Додаткові групи для автентифікації токена. Мають починатися з "system:bootstrappers:"
  auth-extra-groups: system:bootstrappers:worker,system:bootstrappers:ingress

Тип секрету має бути bootstrap.kubernetes.io/token, а назва має бути bootstrap-token-<token id>. Він також повинен знаходитися в просторі імен kube-system.

Члени usage-bootstrap-* вказують, для чого цей секрет призначений. Значення має бути встановлено в true, щоб увімкнути відповідне використання.

  • usage-bootstrap-authentication вказує, що токен може використовуватися для автентифікації до API-сервера як токен-носій.
  • usage-bootstrap-signing вказує, що токен може використовуватися для підпису ConfigMap cluster-info, як описано нижче.

Поле expiration контролює термін дії токена. Протерміновані токени відхиляються при спробі автентифікації та ігноруються під час підпису ConfigMap. Значення терміну дії кодується як абсолютний UTC-час за стандартом RFC3339. Увімкніть контролер tokencleaner, щоб автоматично видаляти протерміновані токени.

Управління токенами за допомогою kubeadm

Ви можете використовувати інструмент kubeadm для управління токенами на запущеному кластері. Деталі дивіться в документації kubeadm token.

Підпис ConfigMap

Окрім автентифікації, токени можуть використовуватися для підпису ConfigMap. Це використовується на ранніх етапах процесу завантаження кластера до того, як клієнт довіряє API-серверу. Підписаний ConfigMap може бути автентифікований спільним токеном.

Увімкніть підпис ConfigMap, увімкнувши контролер bootstrapsigner у Controller Manager.

--controllers=*,bootstrapsigner

ConfigMap, який підписується, це cluster-info у просторі імен kube-public. Типовий процес полягає в тому, що клієнт читає цей ConfigMap без автентифікації та ігноруючи помилки TLS. Потім він перевіряє коректність ConfigMap, переглядаючи підпис, вбудований у ConfigMap.

ConfigMap може виглядати так:

apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-info
  namespace: kube-public
data:
  jws-kubeconfig-07401b: eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9..tYEfbo6zDNo40MQE07aZcQX2m3EB2rO3NuXtxVMYm9U
  kubeconfig: |
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: <дуже довгі дані сертифіката>
        server: https://10.138.0.2:6443
      name: ""
    contexts: []
    current-context: ""
    kind: Config
    preferences: {}
    users: []    

Елемент kubeconfig у ConfigMap є конфігураційним файлом, який містить лише інформацію про кластер. Ключовим моментом, що передається, є certificate-authority-data. Це може бути розширено в майбутньому.

Підпис є підписом JWS, що використовує "відокремлений" режим. Щоб перевірити підпис, користувач має закодувати вміст kubeconfig відповідно до правил JWS (закодовано base64, відкидаючи будь-які кінцеві =). Цей закодований вміст потім використовується для формування повного JWS шляхом вставки його між 2 крапками. Ви можете перевірити JWS, використовуючи схему HS256 (HMAC-SHA256) з повним токеном (наприклад, 07401b.f395accd246ae52d) як спільний секрет. Користувачі повинні перевірити, що використовується HS256.

Додаткову інформацію можна знайти в розділі докладні відомості про реалізацію kubeadm.

3 - Авторизація

Деталі механізмів авторизації Kubernetes і підтримувані режими авторизації.

Авторизація в Kubernetes відбувається після автентифікації. Зазвичай клієнт, що робить запит, має бути автентифікований (увійти в систему), перш ніж його запит може бути дозволений; однак, Kubernetes також дозволяє анонімні запити за деяких обставин.

Для огляду того, як авторизація вписується в ширший контекст контролю доступу до API, читайте Контроль доступу до Kubernetes API.

Вердикти авторизації

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

Усі частини запиту до API повинні бути дозволені деяким механізмом авторизації, щоб він міг продовжити виконання. Іншими словами: стандартно доступ заборонений.

Коли налаштовано кілька модулів авторизації, кожен перевіряється по черзі. Якщо будь-який авторизатор схвалює або відхиляє запит, це рішення негайно повертається і жоден інший авторизатор не перевіряється. Якщо всі модулі не мають думки щодо запиту, то запит відхиляється. Загальний вердикт "відхилено" означає, що API-сервер відхиляє запит і відповідає зі статусом HTTP 403 (Forbidden).

Атрибути запиту, що використовуються для авторизації

Kubernetes розглядає тільки наступні атрибути запиту до API:

  • user — Рядок user, наданий під час автентифікації.
  • group — Список імен груп, до яких належить автентифікований користувач.
  • extra — Map довільних рядкових ключів з рядковими значеннями, надана шаром автентифікації.
  • API — Вказує, чи є запит запитом на ресурс API.
  • Request path — Шлях до різних нересурсних точок доступу, таких як /api або /healthz.
  • API request verb — Дієслова API, такі як get, list, create, update, patch, watch, delete і deletecollection, використовуються для запитів до ресурсів. Щоб визначити дієслово запиту для точки доступу API ресурсу, дивіться дієслова запитів та авторизація.
  • HTTP request verb — Методи HTTP в нижньому регістрі, такі як get, post, put і delete, використовуються для нересурсних запитів.
  • Resource — Ідентифікатор або імʼя ресурсу, до якого здійснюється доступ (тільки для запитів до ресурсів). Для запитів до ресурсів, що використовують дієслова get, update, patch і delete, ви повинні надати імʼя ресурсу.
  • Subresource — Субесурс, до якого здійснюється доступ (тільки для запитів до ресурсів).
  • Namespace — Простір імен обʼєкта, до якого здійснюється доступ (тільки для запитів до ресурсів у просторі імен).
  • API group — Група API, до якої здійснюється доступ (тільки для запитів до ресурсів). Порожній рядок позначає основну групу API.

Дієслова запиту та авторизація

Нересурсні запити

Запити до точок доступу, відмінних від /api/v1/... або /apis/<group>/<version>/..., вважаються нересурсними запитами та використовують метод HTTP як дієслово в нижньому регістрі. Наприклад, виконання запиту GET за допомогою HTTP до точок доступу, таких як /api або /healthz, буде використовувати get як дієслово.

Ресурсні запити

Щоб визначити дієслово запиту для точки доступу API ресурсу, Kubernetes показує використаний HTTP метод і розглядає, чи діє запит на індивідуальний ресурс чи на колекцію ресурсів:

HTTP методдієслово запиту
POSTcreate
GET, HEADget (для індивідуальних ресурсів), list (для колекцій, включаючи повний вміст обʼєктів), watch (для спостереження за індивідуальним ресурсом або колекцією ресурсів)
PUTupdate
PATCHpatch
DELETEdelete (для індивідуальних ресурсів), deletecollection (для колекцій)

Іноді Kubernetes перевіряє авторизацію для додаткових дозволів, використовуючи спеціалізовані дієслова. Наприклад:

  • Особливі випадки автентифікації
    • Дієслово impersonate для users, groups, і serviceaccounts в основній групі API, та userextras у групі API authentication.k8s.io.
  • Авторизація CertificateSigningRequests
    • Дієслово approve для CertificateSigningRequests, та update для переглядів наявних схвалень
  • RBAC
    • Дієслова bind та escalate для ресурсів roles та clusterroles у групі API rbac.authorization.k8s.io.

Контекст авторизації

Kubernetes очікує атрибути, які є загальними для запитів до REST API. Це означає, що авторизація в Kubernetes працює з наявними системами контролю доступу на рівні організації або хмарного провайдера, які можуть обробляти інші API крім Kubernetes API.

Режими авторизації

API-сервер Kubernetes може авторизувати запит, використовуючи один з декількох режимів авторизації:

AlwaysAllow
Цей режим дозволяє всі запити, що несе ризики для безпеки. Використовуйте цей режим авторизації тільки якщо вам не потрібна авторизація для ваших запитів до API (наприклад, для тестування).
AlwaysDeny
Цей режим блокує всі запити. Використовуйте цей режим авторизації тільки для тестування.
ABAC (контроль доступу на основі атрибутів)
Режим ABAC в Kubernetes визначає парадигму управління доступом, згідно з якою права доступу надаються користувачам за допомогою політик, які обʼєднують атрибути разом. Політики можуть використовувати будь-який тип атрибутів (атрибути користувача, атрибути ресурсу, обʼєкта, середовища тощо).
RBAC (контроль доступу на основі ролей)
Kubernetes RBAC — це метод регулювання доступу до компʼютерних або мережевих ресурсів на основі ролей окремих користувачів в організації. У цьому контексті доступ — це можливість окремого користувача виконувати певне завдання, наприклад, переглядати, створювати або змінювати файл. В цьому режимі Kubernetes використовує групу API rbac.authorization.k8s.io для прийняття рішень щодо авторизації, що дозволяє вам динамічно налаштовувати політики дозволів через API Kubernetes.
Node
Спеціальний режим авторизації, який надає дозволи для kubeletʼів на основі запланованих до запуску Podʼів. Щоб дізнатися більше про режим авторизації вузла, див. Авторизація вузла.
Webhook
Kubernetes режим webhook для авторизації робить синхронний HTTP-виклик, блокуючи запит до тих пір, поки віддалений HTTP-сервіс не відповість на нього. Ви можете написати власне програмне забезпечення для обробки виклику або використовувати рішення з екосистеми.

Конфігурація режиму авторизації

Ви можете налаштувати ланцюжок авторизації API сервера Kubernetes, використовуючи або параметри командного рядка, або, як бета-функцію, використовуючи конфігураційний файл.

Ви повинні вибрати один з двох підходів до конфігурації: задати обидва шляхи --authorization-config і налаштувати вебхук авторизації за допомогою аргументів командного рядка --authorization-mode та --authorization-webhook-* не допускається. Якщо ви спробуєте це зробити, API сервер повідомить про помилку під час запуску та одразу завершить роботу.

Конфігурація режиму авторизації через командний рядок

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

Ви можете використовувати наступні режими:

  • --authorization-mode=ABAC (режим контролю доступу на основі атрибутів)
  • --authorization-mode=RBAC (режим контролю доступу на основі ролей)
  • --authorization-mode=Node (авторизатор вузлів)
  • --authorization-mode=Webhook (режим авторизації вебхуком)
  • --authorization-mode=AlwaysAllow (завжди дозволяє запити; несе ризики безпеки)
  • --authorization-mode=AlwaysDeny (завжди відхиляє запити)

Ви можете вибрати більше одного режиму авторизації; наприклад: --authorization-mode=Node,Webhook

Kubernetes перевіряє модулі авторизації на основі порядку, який ви вказуєте в командному рядку API сервера, тому раніше зазначений модуль має вищий пріоритет для дозволу або відмови в запиті.

Ви не можете поєднувати аргумент командного рядка --authorization-mode з аргументом командного рядка --authorization-config, який використовується для налаштування авторизації за допомогою локального файлу.

Для отримання додаткової інформації про аргументи командного рядка для API сервера, читайте довідник по kube-apiserver.

Налаштування API сервера за допомогою конфігураційного файлу авторизації

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

Як бета-функція, Kubernetes дозволяє налаштовувати ланцюжки авторизації, які можуть включати декілька вебхуків. Елементи авторизації в цьому ланцюжку можуть мати чітко визначені параметри, які перевіряють запити в певному порядку, пропонуючи вам тонке налаштування, наприклад, явну відмову при невдачах.

Підхід з використанням конфігураційного файлу навіть дозволяє вам вказувати правила CEL для попередньої фільтрації запитів перед їх відправленням до вебхуків, допомагаючи вам уникнути непотрібних викликів. API сервер також автоматично перезавантажує ланцюжок авторизатора при зміні конфігураційного файлу.

Ви вказуєте шлях до конфігурації авторизації за допомогою аргументу командного рядка --authorization-config.

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

Приклад конфігурації

---
#
# НЕ ВИКОРИСТОВУЙТЕ КОНФІГУРАЦІЮ ТАК, ЯК ВОНА Є. ЦЕ ПРИКЛАД.
#
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
  * type: Webhook
    # Назва, що використовується для опису авторизатора
    # Це явно використовується в механізмі моніторингу для метрик
    # Примітка:
    #   - Перевірка цього поля схожа на перевірку міток K8s на сьогодні.
    # Обовʼязково, немає стандартного значення
    name: webhook
    webhook:
      # Тривалість кешування відповідей 'authorized' від вебхука
      # авторизатора.
      # Те саме, що і встановлення прапорця `--authorization-webhook-cache-authorized-ttl`.
      # Стандартно: 5m0s
      authorizedTTL: 30s
      # Тривалість кешування відповідей 'unauthorized' від вебхука
      # авторизатора.
      # Те саме, що і встановлення прапорця `--authorization-webhook-cache-unauthorized-ttl`.
      # Стандартно: 30 с
      unauthorizedTTL: 30s
      # Тайм-аут для запиту вебхука
      # Максимально допустимий: 30 с
      # Обовʼязково, немає стандартного значення
      timeout: 3s
      # Версія API для SubjectAccessReview в authorization.k8s.io, яка
      # надсилається до вебхука та очікується від нього.
      # Те саме, що і встановлення прапорця `--authorization-webhook-version`.
      # Обовʼязково, немає стандартного значення
      # Допустимі значення: v1beta1, v1
      subjectAccessReviewVersion: v1
      # MatchConditionSubjectAccessReviewVersion визначає версію SubjectAccessReview
      # за якою оцінюються вирази CEL
      # Допустимі значення: v1
      # Обовʼязково, немає стандартного значення
      matchConditionSubjectAccessReviewVersion: v1
      # Керує рішенням авторизації, коли запит вебхука не вдалося
      # виконати або отримано некоректну відповідь або помилки під час оцінки
      # виразів matchConditions.
      # Допустимі значення:
      #   - NoOpinion: продовжувати до наступних авторизаторів, щоб перевірити, чи дозволяє один з них запит
      #   - Deny: відхиляти запит без консультації з наступними авторизаторами
      # Обовʼязково, немає стандартного значення
      failurePolicy: Deny
      connectionInfo:
        # Керує тим, як вебхук повинен спілкуватися з сервером.
        # Допустимі значення:
        # - KubeConfig: використовуйте файл, вказаний у kubeConfigFile для пошуку сервера.
        # - InClusterConfig: використовуйте конфігурацію внутрішнього кластера для виклику API SubjectAccessReview,
        #   що розміщується kube-apiserver. Цей режим не дозволяється для kube-apiserver.
        type: KubeConfig
        # Шлях до файлу KubeConfig для інформації про підключення
        # Обовʼязково, якщо connectionInfo.Type = KubeConfig
        kubeConfigFile: /kube-system-authz-webhook.yaml
        # matchConditions - це список умов, які повинні бути виконані для того, щоб запит було відправлено на цей
        # вебхук. Порожній список matchConditions підходить для всіх запитів.
        # Є максимально допустимі 64 умови відповідності.
        #
        # Логіка точного порівняння така (в порядку):
        #   1. Якщо принаймні одна matchCondition оцінюється як FALSE, тоді вебхук пропускається.
        #   2. Якщо ВСІ matchConditions оцінюються як TRUE, тоді вебхук викликається.
        #   3. Якщо принаймні одна matchCondition оцінюється як помилка (але ні одна не є FALSE):
        #      - Якщо failurePolicy=Deny, тоді вебхук відхиляє запит.
        #      - Якщо failurePolicy=NoOpinion, тоді помилка ігнорується, а вебхук пропускається.
      matchConditions:
      # expression - це вираз CEL, який оцінюється для кожного запиту. Повертає булеве значення.
      # CEL вираз має доступ до вмісту SubjectAccessReview у версії v1.
      # Якщо версія у SubjectAccessReview в запиті змінної є v1beta1, 
      # вміст буде конвертовано у v1 перед оцінкою виразу CEL.
      #
      # Документація CEL: https://kubernetes.io/docs/reference/using-api/cel/
      #
      # лише надсилати запити ресурсівв до вебхука
      * expression: has(request.resourceAttributes)
      # лише перехоплювати запити до kube-system
      * expression: request.resourceAttributes.namespace == 'kube-system'
      # не перехоплювати запити від облікових записів служб kube-system
      * expression: !('system:serviceaccounts:kube-system' in request.user.groups)
  * type: Node
    name: node
  * type: RBAC
    name: rbac
  * type: Webhook
    name: in-cluster-authorizer
    webhook:
      authorizedTTL: 5 хв
      unauthorizedTTL: 30 с
      timeout: 3 с
      subjectAccessReviewVersion: v1
      failurePolicy: NoOpinion
      connectionInfo:
        type: InClusterConfig

Під час налаштування ланцюжка авторизації за допомогою файлу конфігурації переконайтеся, що всі вузли панелі управління мають однаковий вміст файлу. Зверніть увагу на конфігурацію API сервера при оновленні / зниженні версії вашого кластера. Наприклад, якщо ви оновлюєтеся з Kubernetes 1.30 до Kubernetes 1.31, вам потрібно переконатися, що файл конфігурації має формат, який розуміє Kubernetes 1.31, перш ніж ви оновите кластер. Якщо ви знижуєте версію до 1.30, вам потрібно відповідно налаштувати конфігурацію.

Конфігурація авторизації та перезавантаження

Kubernetes перезавантажує файл конфігурації авторизації, коли API сервер виявляє зміну у файлі, а також за 60-секундним графіком, якщо події змін не спостерігаються.

Підвищення привілеїв через створення або редагування робочих навантажень

Користувачі, які можуть створювати/редагувати Podʼи в просторі імен, або безпосередньо, або через обʼєкт, що дозволяє опосередковане управління робочими навантаженнями, можуть мати можливість підвищити свої привілеї в цьому просторі імен. Потенційні шляхи до підвищення привілеїв включають розширення API Kubernetes та повʼязані з ними контролери.

Шляхи підвищення привілеїв

Існують різні способи, за якими зловмисник або ненадійний користувач може отримати додаткові привілеї в межах простору імен, якщо ви дозволяєте їм запускати довільні Podʼи в цьому просторі імен:

  • Монтування довільних Secretʼів в цьому просторі імен
    • Може бути використано для доступу до конфіденційної інформації, призначеної для інших робочих навантажень
    • Може бути використано для отримання токена службового облікового запису більш привілейованого ServiceAccount
  • Використання довільних службових облікових записів в цьому просторі імен
    • Може виконувати дії Kubernetes API як інше робоче навантаження (імперсонізація)
    • Може виконувати будь-які привілейовані дії, які має цей ServiceAccount
  • Монтування або використання ConfigMaps, призначених для інших робочих навантажень в цьому просторі імен
    • Може бути використано для отримання інформації, призначеної для інших робочих навантажень, таких як імена хостів баз даних.
  • Монтування томів, призначених для інших робочих навантажень в цьому просторі імен
    • Може бути використано для отримання інформації, призначеної для інших робочих навантажень, та її зміни.

Перевірка доступу до API

kubectl надає підкоманду auth can-i для швидкого запиту до рівня авторизації API. Команда використовує API SelfSubjectAccessReview, щоб визначити, чи може поточний користувач виконати вказану дію, і працює незалежно від режиму авторизації, який використовується.

kubectl auth can-i create deployments --namespace dev

Вивід подібний до цього:

yes
kubectl auth can-i create deployments --namespace prod

Вивід подібний до цього:

no

Адміністратори можуть поєднувати це з імперсонізацією користувача, щоб визначити, які дії можуть виконувати інші користувачі.

kubectl auth can-i list secrets --namespace dev --as dave

Вивід подібний до цього:

no

Так само, щоб перевірити, чи може ServiceAccount з іменем dev-sa в просторі імен dev отримати списки Podʼів в просторі імен target:

kubectl auth can-i list pods \
    --namespace target \
    --as system:serviceaccount:dev:dev-sa

Вивід подібний до цього:

no

SelfSubjectAccessReview є частиною групи API authorization.k8s.io, яка викладає авторизацію сервера API для зовнішніх служб. Інші ресурси у цій групі включають:

SubjectAccessReview
Перегляд доступу для будь-якого користувача, не лише поточного. Корисно для делегування рішень про авторизацію серверу API. Наприклад, kubelet та API розширень сервери використовують це для визначення доступу користувача до своїх власних API.
LocalSubjectAccessReview
Подібно до SubjectAccessReview, але обмежено для конкретного простору імен.
SelfSubjectRulesReview
Перегляд, який повертає набір дій, які користувач може виконати в межах простору імен. Корисно для користувачів для швидкого узагальнення їх власного доступу, або для інтерфейсів користувача для приховування/відображення дій.

Ці API можна опитати, створивши звичайні ресурси Kubernetes, де поле відповіді status поверненого обʼєкта є результатом запиту. Наприклад:

kubectl create -f - -o yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: apps
    resource: deployments
    verb: create
    namespace: dev
EOF

Створений SelfSubjectAccessReview схожий на:

apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
metadata:
  creationTimestamp: null
spec:
  resourceAttributes:
    group: apps
    resource: deployments
    namespace: dev
    verb: create
status:
  allowed: true
  denied: false

Що далі

4 - Використання RBAC авторизації

Контроль доступу на основі ролей (RBAC) — це метод регулювання доступу до компʼютерних або мережевих ресурсів на основі ролей окремих користувачів у вашій організації.

RBAC авторизація використовує групу API rbac.authorization.k8s.io група API для прийняття рішень щодо авторизації, дозволяючи вам динамічно налаштовувати політики через Kubernetes API.

Щоб увімкнути RBAC, запустіть API сервер з прапорцем --authorization-mode, встановленим на список, розділений комами, що включає RBAC; наприклад:

kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options

Обʼєкти API

RBAC API визначає чотири типи обʼєктів Kubernetes: Role, ClusterRole, RoleBinding та ClusterRoleBinding. Ви можете описувати або змінювати обʼєкти RBAC за допомогою таких інструментів, як kubectl, так само як і будь-який інший обʼєкт Kubernetes.

Role та ClusterRole

RBAC Role або ClusterRole містять правила, які представляють набір дозволів. Дозволи є виключно адитивними (немає правил "заборони").

Role завжди встановлює дозволи в межах певного простору імен; коли ви створюєте Role, ви повинні вказати простір імен, до якого вона належить.

ClusterRole, на відміну, є ресурсом, який не належить до простору імен. Ресурси мають різні назви (Role і ClusterRole), оскільки обʼєкт Kubernetes завжди повинен бути або привʼязаним до простору імен, або не привʼязаним до простору імен; він не може бути одночасно і тим, і іншим.

ClusterRole мають кілька використань. Ви можете використовувати ClusterRole для:

  1. визначення дозволів на ресурси, що належать до простору імен, і надання доступу в межах окремих просторів імен
  2. визначення дозволів на ресурси, що належать до простору імен, і надання доступу до всіх просторів імен
  3. визначення дозволів на ресурси, що належать до кластера

Якщо ви хочете визначити роль в межах простору імен, використовуйте Role; якщо ви хочете визначити роль для всього кластера, використовуйте ClusterRole.

Приклад Role

Ось приклад Role в просторі імен "default", яку можна використовувати для надання доступу на читання до Podʼів:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" означає основну групу API
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Приклад ClusterRole

ClusterRole може бути використана для надання тих самих дозволів, що й Role. Оскільки ClusterRole стосуються всього кластера, ви також можете використовувати їх для надання доступу до:

  • ресурсів, що належать до кластера (наприклад, вузли)

  • точок доступу, що не є ресурсами (наприклад, /healthz)

  • ресурсів, що належать до простору імен (наприклад, контейнерів), в усіх просторах імен

    Наприклад: ви можете використовувати ClusterRole для дозволу конкретному користувачу виконувати kubectl get pods --all-namespaces.

Ось приклад ClusterRole, яку можна використовувати для надання доступу на читання Secretʼів в будь-якому просторі імен або в усіх просторах імен (залежно від того, як вона звʼязана):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" пропущено, оскільки ClusterRole не привʼязані до простору імен
  name: secret-reader
rules:
- apiGroups: [""]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів Secret
  # це "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Назва обʼєкта Role або ClusterRole повинна бути дійсною назвою сегмента шляху.

RoleBinding та ClusterRoleBinding

RoleBinding надає дозволи, визначені в ролі, користувачу або групі користувачів та містить список субʼєктів (користувачів, груп або облікових записів сервісів) і посилання на роль, що надається. RoleBinding надає дозволи в межах конкретного простору імен, тоді як ClusterRoleBinding надає доступ на рівні всього кластера.

RoleBinding може посилатися на будь-яку Role в тому ж просторі імен. Альтернативно, RoleBinding може посилатися на ClusterRole і звʼязувати цю ClusterRole з простором імен RoleBinding. Якщо ви хочете звʼязати ClusterRole з усіма просторами імен у вашому кластері, використовуйте ClusterRoleBinding.

Назва обʼєкта RoleBinding або ClusterRoleBinding повинна бути дійсною назвою сегмента шляху.

Приклади RoleBinding

Ось приклад RoleBinding, який надає роль "pod-reader" користувачу "jane" в межах простору імен "default". Це дозволяє "jane" читати Podʼи в просторі імен "default".

apiVersion: rbac.authorization.k8s.io/v1
# Це рольове звʼязування дозволяє "jane" читати Podʼи в просторі імен "default".
# Ви повинні вже мати роль з назвою "pod-reader" в цьому просторі імен.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# Ви можете вказати більше одного "субʼєкта"
- kind: User
  name: jane # "name" чутливе до регістру
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" вказує на звʼязування з Role / ClusterRole
  kind: Role # має бути Role або ClusterRole
  name: pod-reader # має збігатися з назвою Role або ClusterRole, з якою ви хочете звʼязати
  apiGroup: rbac.authorization.k8s.io

RoleBinding також може посилатися на ClusterRole для надання дозволів, визначених у цій ClusterRole, на ресурси в межах простору імен RoleBinding. Такий вид посилання дозволяє визначати набір загальних ролей для всього вашого кластера, а потім використовувати їх у декількох просторах імен.

Наприклад, хоча наступний RoleBinding посилається на ClusterRole, "dave" (субʼєкт, чутливий до регістру) зможе читати Secretʼи лише в просторі імен "development", оскільки простір імен RoleBinding (у його метаданих) — "development".

apiVersion: rbac.authorization.k8s.io/v1
# Це рольове звʼязування дозволяє "dave" читати Secretʼи в просторі імен "development".
# Ви повинні вже мати ClusterRole з назвою "secret-reader".
kind: RoleBinding
metadata:
  name: read-secrets
  #
  # Простір імен RoleBinding визначає, де надаються дозволи.
  # Це надає дозволи лише в просторі імен "development".
  namespace: development
subjects:
- kind: User
  name: dave # Name чутливе до регістру
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

Приклад ClusterRoleBinding

Щоб надати дозволи на рівні всього кластера, ви можете використовувати ClusterRoleBinding. Наступний ClusterRoleBinding дозволяє будь-якому користувачу з групи "manager" читати Secretʼи в будь-якому просторі імен.

apiVersion: rbac.authorization.k8s.io/v1
# Це звʼязування кластерної ролі дозволяє будь-якому користувачу з групи "manager" читати Secretʼи в будь-якому просторі імен.
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # Name чутливе до регістру
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

Після створення звʼязування ви не можете змінити Role або ClusterRole, на які вони посилаються. Якщо ви спробуєте змінити roleRef звʼязування, ви отримаєте помилку валідації. Якщо ви дійсно хочете змінити roleRef для звʼязування, вам потрібно видалити обʼєкт звʼязування і створити новий.

Існує дві причини для цього обмеження:

  1. Роблячи roleRef незмінним, можна надати комусь дозвіл update на наявний обʼєкт звʼязування, щоб він міг керувати списком субʼєктів, без можливості змінити роль, яка надається цим субʼєктам.
  2. Звʼязування з іншою роллю є принципово іншим звʼязуванням. Вимога видалення/створення нового звʼязування для зміни roleRef гарантує, що весь список субʼєктів у звʼязуванні має намір отримати нову роль (на відміну від можливості випадково змінити лише roleRef без перевірки того, чи всі наявні субʼєкти повинні отримати дозволи нової ролі).

Команда kubectl auth reconcile створює або оновлює файл маніфесту, що містить обʼєкти RBAC, і обробляє видалення та відновлення обʼєктів звʼязування, якщо необхідно змінити роль, на яку вони посилаються. Дивіться використання команд та приклади для отримання додаткової інформації.

Посилання на ресурси

У Kubernetes API більшість ресурсів представлені та доступні за допомогою рядкового представлення їхнього імені обʼєкта, наприклад, pods для Pod. RBAC посилається на ресурси, використовуючи точно таку ж назву, яка зʼявляється в URL для відповідної точки доступу API. Деякі Kubernetes API включають субресурс, такий як логи для Pod. Запит на логи Pod виглядає так:

GET /api/v1/namespaces/{namespace}/pods/{name}/log

У цьому випадку pods є ресурсом простору імен для Pod, а log є субресурсом pods. Щоб представити це в ролі RBAC, використовуйте слеш (/) для розділення ресурсу та субресурсу. Щоб дозволити субʼєкту читати pods і також отримувати доступ до субресурсу log для кожного з цих Pod, напишіть:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

Ви також можете посилатися на ресурси за назвою для певних запитів через список resourceNames. Коли зазначено, запити можуть бути обмежені окремими екземплярами ресурсу. Ось приклад, що обмежує субʼєкт лише до get або update ConfigMap з назвою my-configmap:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: configmap-updater
rules:
- apiGroups: [""]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів ConfigMap є "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-configmap"]
  verbs: ["update", "get"]

Замість того, щоб посилатися на окремі resources, apiGroups і verbs, ви можете використовувати символ підстановки *, щоб посилатися на всі такі обʼєкти. Для nonResourceURLs ви можете використовувати символ підстановки * як суфікс для глобального збігу. Для resourceNames порожній набір означає, що все дозволено. Ось приклад, що дозволяє виконувати будь-яку поточну і майбутню дію для всіх поточних та майбутніх ресурсів в API групі example.com. Це схоже на вбудовану роль cluster-admin.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: example.com-superuser # НЕ ВИКОРИСТОВУЙТЕ ЦЮ РОЛЬ, ЦЕ ЛИШЕ ПРИКЛАД
rules:
- apiGroups: ["example.com"]
  resources: ["*"]
  verbs: ["*"]

Агреговані ClusterRole

Ви можете агрегувати декілька ClusterRole в одну комбіновану ClusterRole. Контролер, який працює як частина панелі управління кластера, слідкує за обʼєктами ClusterRole з встановленим aggregationRule. aggregationRule визначає мітку селектора, яку використовує контролер для вибору інших обʼєктів ClusterRole, що мають бути обʼєднані у поле rules цього обʼєкта.

Ось приклад агрегованої ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # Панель управління автоматично заповнює правила

Якщо ви створюєте нову ClusterRole, що відповідає селектору міток поточної агрегованої ClusterRole, це зміна ініціює додавання нових правил до агрегованої ClusterRole. Ось приклад, що додає правила до ClusterRole "monitoring" шляхом створення іншої ClusterRole з міткою rbac.example.com/aggregate-to-monitoring: true.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# При створенні ClusterRole "monitoring-endpoints",
# правила нижче будуть додані до ClusterRole "monitoring".
rules:
- apiGroups: [""]
  resources: ["services", "endpointslices", "pods"]
  verbs: ["get", "list", "watch"]

Стандартні ролі для користувачів використовують агрегацію ClusterRole. Це дозволяє вам, як адміністратору кластера, включати правила для спеціальних ресурсів, таких як ті, що надаються CustomResourceDefinitions або агрегованими API серверами, щоб розширити стандартні ролі.

Наприклад: наступні ClusterRole дозволяють стандартним ролям "admin" і "edit" керувати спеціальним ресурсом з назвою CronTab, тоді як роль "view" може виконувати лише читання ресурсів CronTab. Ви можете припустити, що обʼєкти CronTab називаються "crontabs" в URL, як це бачить API сервер.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: aggregate-cron-tabs-edit
  labels:
    # Додайте ці дозволи до стандартних ролей "admin" і "edit".
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: aggregate-cron-tabs-view
  labels:
    # Додайте ці дозволи до стандартної ролі "view".
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch"]

Приклади ролей

Наступні приклади є фрагментами обʼєктів Role або ClusterRole, що показують лише секцію rules.

Дозволити читання ресурсів "pods" у базовій API Group:

rules:
- apiGroups: [""]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів Pod
  # є "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

Дозволити читання/запис обʼєктів Deployment (на рівні HTTP: обʼєкти з "deployments" у частині ресурсу їх URL) в групах API "apps":

rules:
- apiGroups: ["apps"]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів Deployment
  # є "deployments"
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Дозволити читання Podʼів у базовій групі API, а також читання або запис ресурсів Job у групі API "batch":

rules:
- apiGroups: [""]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів Pod
  # є "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів Job
  # є "jobs"
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Дозволити читання ConfigMap з назвою "my-config" (повинно бути повʼязано з RoleBinding, щоб обмежити до одного ConfigMap в одному просторі імен):

rules:
- apiGroups: [""]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів ConfigMap
  # є "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]

Дозволити читання ресурсу "nodes" у базовій групі (оскільки Node є кластерним ресурсом, це повинно бути у ClusterRole, повʼязаної з ClusterRoleBinding, щоб бути ефективним):

rules:
- apiGroups: [""]
  #
  # на рівні HTTP, назва ресурсу для доступу до обʼєктів Node
  # є "nodes"
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]

Дозволити GET і POST запити до не-ресурсної точки доступу /healthz та всіх субшляхів (повинно бути в ClusterRole, повʼязаній з ClusterRoleBinding, щоб бути ефективним):

rules:
- nonResourceURLs: ["/healthz", "/healthz/*"] # '*' у nonResourceURL є суфіксом для глобального збігу
  verbs: ["get", "post"]

Посилання на субʼєктів

RoleBinding або ClusterRoleBinding привʼязує роль до субʼєктів. Субʼєктами можуть бути групи, користувачі або ServiceAccounts.

Kubernetes представляє імена користувачів у вигляді рядків. Це можуть бути: звичайні імена, такі як "alice"; імена в стилі електронної пошти, як "bob@example.com"; або числові ідентифікатори користувачів, представлені у вигляді рядків. Вам, як адміністратору кластера, належить налаштувати модулі автентифікації, щоб автентифікація генерувала імена користувачів у бажаному форматі.

У Kubernetes модулі Автентифікатора надають інформацію про групи. Групи, як і користувачі, представлені у вигляді рядків, і цей рядок не має жодних вимог до формату, крім того, що префікс system: зарезервований.

ServiceAccounts мають імена з префіксом system:serviceaccount:, і належать до груп, що мають імена з префіксом system:serviceaccounts:.

Приклади RoleBinding

Наступні приклади є фрагментами RoleBinding, що показують лише секцію subjects.

Для користувача з імʼям alice@example.com:

subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io

Для групи з імʼям frontend-admins:

subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io

Для стандартного службового облікового запису у просторі імен "kube-system":

subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

Для всіх службових облікових записів у просторі імен "qa":

subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io

Для всіх службових облікових записів у будь-якому просторі імен:

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

Для всіх автентифікованих користувачів:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io

Для всіх неавтентифікованих користувачів:

subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

Для всіх користувачів:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

Стандартні ролі та привʼязки ролей

API-сервери створюють набір стандартних обʼєктів ClusterRole і ClusterRoleBinding. Багато з них мають префікс system:, що вказує на те, що ресурс безпосередньо керується панеллю управління кластера. Усі стандартні ролі та привʼязки ролей мають мітку kubernetes.io/bootstrapping=rbac-defaults.

Автоматичне узгодження

При кожному запуску API-сервер оновлює стандартні ролі кластера, додаючи будь-які відсутні дозволи, та оновлює стандартні привʼязки ролей, додаючи будь-які відсутні субʼєкти. Це дозволяє кластеру виправляти випадкові зміни та допомагає підтримувати ролі та привʼязки ролей в актуальному стані, оскільки дозволи та субʼєкти змінюються у нових випусках Kubernetes.

Щоб відмовитися від цього узгодження, встановіть анотацію rbac.authorization.kubernetes.io/autoupdate на стандартній кластерній ролі або стандартній RoleBinding у значення false. Зверніть увагу, що відсутність стандартних дозволів та субʼєктів може призвести до несправних кластерів.

Автоматичне узгодження стандартно увімкнено, якщо авторизатор RBAC активний.

Ролі виявлення API

Стандартні кластерні привʼязки ролей дозволяють неавторизованим і авторизованим користувачам читати інформацію про API, яка вважається безпечною для публічного доступу (включаючи CustomResourceDefinitions). Щоб вимкнути анонімний неавторизований доступ, додайте прапорець --anonymous-auth=false до конфігурації API-сервера.

Щоб переглянути конфігурацію цих ролей через kubectl, запустіть:

kubectl get clusterroles system:discovery -o yaml
Ролі виявлення API Kubernetes RBAC
Стандартна ClusterRoleСтандартна ClusterRoleBindingОпис
system:basic-userГрупа system:authenticatedДозволяє користувачеві доступ лише для читання базової інформації про себе. До v1.14 ця роль також була стандартно повʼязана з system:unauthenticated.
system:discoveryГрупа system:authenticatedДозволяє доступ лише для читання точок доступу виявлення API, необхідних для виявлення та узгодження рівня API. До v1.14 ця роль також була стандартно повʼязана з system:unauthenticated.
system:public-info-viewerГрупи system:authenticated і system:unauthenticatedДозволяє доступ лише для читання несекретної інформації про кластер. Представлено у Kubernetes v1.14.

Ролі для користувачів

Деякі зі стандартних ClusterRoles не мають префікса system:. Вони включають ролі суперкористувача (cluster-admin), ролі, призначені для надання у всьому кластері за допомогою ClusterRoleBindings, а також ролі, призначені для надання у певних просторах імен простору імен за допомогою RoleBindings (admin, edit, view).

Ролі для користувачів використовують агрегацію ClusterRole, щоб дозволити адміністраторам включати правила для спеціальних ресурсів у ці ClusterRole. Щоб додати правила до ролей admin, edit або view, створіть ClusterRole з однією або кількома з наступних міток:

metadata:
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-view: "true"

Стандартна ClusterRoleСтандартна ClusterRoleBindingОпис
cluster-adminГрупа system:mastersДозволяє доступ суперкористувача для виконання будь-якої дії на будь-якому ресурсі. При використанні в ClusterRoleBinding, надає повний контроль над кожним ресурсом у кластері та у всіх просторах імен. При використанні в RoleBinding, надає повний контроль над кожним ресурсом у просторі імен, включаючи сам простір імен.
adminНемаєДозволяє доступ адміністратора, призначений для надання в межах простору імен за допомогою RoleBinding.

Якщо використовується в RoleBinding, дозволяє доступ для читання/запису до більшості ресурсів у просторі імен, включаючи можливість створювати ролі та привʼязки ролей у просторі імен. Ця роль не дозволяє доступу для запису до квоти ресурсів або до самого простору імен. Ця роль також не дозволяє доступу для запису до EndpointSlices (або Endpoints) у кластерах, створених за допомогою Kubernetes v1.22+. Більше інформації доступно у розділі "Достпу на запису у EndpointSlices та Endpoints".

editНемаєДозволяє доступ для читання/запису до більшості обʼєктів у просторі імен.

Ця роль не дозволяє переглядати або змінювати ролі або привʼязки ролей. Однак ця роль дозволяє доступ до Secretʼів і запуску Podʼів як будь-який ServiceAccount у просторі імен, тому вона може бути використана для отримання рівня доступу API будь-якого ServiceAccount у просторі імен. Ця роль також не дозволяє доступу для запису до EndpointSlices (або Endpoints) у кластерах, створених за допомогою Kubernetes v1.22+. Більше інформації доступно у розділі "Достпу на запису у EndpointSlices та Endpoints".

viewНемаєДозволяє доступ лише для читання до більшості обʼєктів у просторі імен. Ця роль не дозволяє переглядати ролі або привʼязки ролей. Ця роль не дозволяє переглядати Secretʼи, оскільки читання вмісту секретів дозволяє доступ до облікових даних ServiceAccount у просторі імен, що дозволило б доступ до API як будь-який ServiceAccount у просторі імен (форма ескалації привілеїв).

Ролі основних компонентів

Стандартна ClusterRoleСтандартна ClusterRoleBindingОпис
system:kube-schedulersystem:kube-scheduler користувачДозволяє доступ до ресурсів, необхідних компоненту планувальника.
system:volume-schedulersystem:kube-scheduler користувачДозволяє доступ до ресурсів обʼєму, необхідних компоненту kube-scheduler.
system:kube-controller-managersystem:kube-controller-manager користувачДозволяє доступ до ресурсів, необхідних компоненту менеджера контролерів. Дозволи, необхідні окремим контролерам, детально описані в ролях контролерів.
system:nodeНемаєДозволяє доступ до ресурсів, необхідних для kubelet, включаючи доступ на читання до всіх секретів та доступ на запис до всіх обʼєктів стану Podʼів.

Ви повинні використовувати Node authorizer та втулок допуску NodeRestriction замість ролі system:node та дозволяти надання доступу до API для kubelet на основі Podʼів, які заплановано для запуску на них.

Роль system:node існує лише для сумісності з кластерами Kubernetes, оновленими з версій до v1.8.

system:node-proxiersystem:kube-proxy користувачДозволяє доступ до ресурсів, необхідних компоненту kube-proxy.

Ролі інших компонентів

Стандартна ClusterRoleСтандартна ClusterRoleBindingОпис
system:auth-delegatorНемаєДозволяє делеговані перевірки автентифікації та авторизації. Це зазвичай використовується серверами надбудов API для уніфікованої автентифікації та авторизації.
system:heapsterНемаєРоль для компонента Heapster (застарілий).
system:kube-aggregatorНемаєРоль для компонента kube-aggregator.
system:kube-dnskube-dns службовий обліковий запис у просторі імен kube-systemРоль для компонента kube-dns.
system:kubelet-api-adminНемаєДозволяє повний доступ до API kubelet.
system:node-bootstrapperНемаєДозволяє доступ до ресурсів, необхідних для виконання початкового завантаження kubelet TLS.
system:node-problem-detectorНемаєРоль для компонента node-problem-detector.
system:persistent-volume-provisionerНемаєДозволяє доступ до ресурсів, необхідних для більшості динамічних провізорів томів.
system:monitoringsystem:monitoring групаДозволяє доступ на читання до точок доступу моніторингу панелі управління (тобто kube-apiserver точки доступу придатності та готовності (/healthz, /livez, /readyz), індивідуальні точки доступу перевірки стану (/healthz/*, /livez/*, /readyz/*) та /metrics). Зверніть увагу, що індивідуальні точки доступу перевірки стану та точка доступу метрик можуть розкривати конфіденційну інформацію.

Ролі для вбудованих контролерів

Менеджер контролерів Kubernetes запускає контролери, які вбудовані в панель управління Kubernetes. Коли його викликають з --use-service-account-credentials, kube-controller-manager запускає кожен контролер використовуючи окремий службовий обліковий запис. Для кожного вбудованого контролера існують відповідні ролі з префіксом system:controller:. Якщо менеджер контролерів не запускається з --use-service-account-credentials, він виконує всі контрольні цикли використовуючи власні облікові дані, які повинні мати всі відповідні ролі. Ці ролі включають:

  • system:controller:attachdetach-controller
  • system:controller:certificate-controller
  • system:controller:clusterrole-aggregation-controller
  • system:controller:cronjob-controller
  • system:controller:daemon-set-controller
  • system:controller:deployment-controller
  • system:controller:disruption-controller
  • system:controller:endpoint-controller
  • system:controller:expand-controller
  • system:controller:generic-garbage-collector
  • system:controller:horizontal-pod-autoscaler
  • system:controller:job-controller
  • system:controller:namespace-controller
  • system:controller:node-controller
  • system:controller:persistent-volume-binder
  • system:controller:pod-garbage-collector
  • system:controller:pv-protection-controller
  • system:controller:pvc-protection-controller
  • system:controller:replicaset-controller
  • system:controller:replication-controller
  • system:controller:resourcequota-controller
  • system:controller:root-ca-cert-publisher
  • system:controller:route-controller
  • system:controller:service-account-controller
  • system:controller:service-controller
  • system:controller:statefulset-controller
  • system:controller:ttl-controller

Запобігання ескалації привілеїв і початкове налаштування

API RBAC запобігає ескалації привілеїв користувачами шляхом редагування ролей або привʼязок ролей. Оскільки це реалізується на рівні API, це застосовується навіть коли авторизатор RBAC не використовується.

Обмеження на створення або оновлення ролей

Ви можете створити/оновити роль, тільки якщо виконується хоча б одна з наступних умов:

  1. Ви вже маєте всі дозволи, що містяться в ролі, в тій же області, що й обʼєкт, який модифікується (кластерний рівень для ClusterRole, у тому ж просторі імен або кластерний рівень для Role).
  2. Вам надано явний дозвіл виконувати дієслово escalate для ресурсу roles або clusterroles в групі API rbac.authorization.k8s.io.

Наприклад, якщо user-1 не має можливості отриамння переліку Secrets на кластерному рівні, він не може створити ClusterRole, що містить цей дозвіл. Щоб дозволити користувачу створювати/оновлювати ролі:

  1. Надайте йому роль, що дозволяє створювати/оновлювати обʼєкти Role або ClusterRole, те що треба.
  2. Надати йому дозвіл включати конкретні дозволи в ролі, які він створює/оновлює:
    • неявно, надаючи йому ці дозволи (якщо він спробує створити або модифікувати Role або ClusterRole з дозволами, які йому самому не надані, запит до API буде заборонений)
    • або явно дозволити вказувати будь-які дозволи в Role або ClusterRole, надаючи йому дозвіл виконувати дієслово escalate для ресурсів roles або clusterroles в групі API rbac.authorization.k8s.io.

Обмеження на створення або оновлення привʼязок ролей

Ви можете створити/оновити привʼязку ролі, тільки якщо вже маєте всі дозволи, що містяться в згаданій ролі (в тій же області, що й привʼязка ролі) або якщо вам надано дозвіл виконувати дієслово bind для згаданої ролі. Наприклад, якщо user-1 не має можливості отримувати перелік Secrets на кластерному рівні, він не може створити ClusterRoleBinding для ролі, яка надає цей дозвіл. Щоб дозволити користувачу створювати/оновлювати привʼязки ролей:

  1. Надати йому роль, що дозволяє створювати/оновлювати обʼєкти RoleBinding або ClusterRoleBinding, як необхідно.
  2. Надати йому дозволи, необхідні для привʼязки певної ролі:
    • неявно, надаючи йому дозволи, що м стяться в ролі.
    • явно, надаючи йому дозвіл виконувати дієслово bind для певної Role (або ClusterRole).

Наприклад, цей ClusterRole і RoleBinding дозволять user-1 надавати іншим користувачам ролі admin, edit і view у просторі імен user-1-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterroles"]
  verbs: ["bind"]
  # пропустіть resourceNames, щоб дозволити привʼязку будь-якої ClusterRole
  resourceNames: ["admin","edit","view"] 
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: role-grantor-binding
  namespace: user-1-namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: role-grantor
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: user-1

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

  • Використовуйте облікові дані з групою "system:masters", яка стандартно привʼязана до ролі суперкористувача "cluster-admin".

Утиліти командного рядка

kubectl create role

Створює обʼєкт Role, що визначає дозволи в межах одного простору імен. Приклади:

  • Створити Role з назвою "pod-reader", яка дозволяє користувачам виконувати get, watch і list для pods:

    kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
    
  • Створити Role з назвою "pod-reader" зі специфікованими resourceNames:

    kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • Створити Role з назвою "foo" зі специфікованими apiGroups:

    kubectl create role foo --verb=get,list,watch --resource=replicasets.apps
    
  • Створити Role з назвою "foo" з дозволами на субресурси:

    kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
    
  • Створити Role з назвою "my-component-lease-holder" з дозволами на отримання/оновлення ресурсу зі специфічною назвою:

    kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component
    

kubectl create clusterrole

Створює ClusterRole. Приклади:

  • Створити ClusterRole з назвою "pod-reader", яка дозволяє користувачам виконувати get, watch і list для pods:

    kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
    
  • Створити ClusterRole з назвою "pod-reader" зі специфікованими resourceNames:

    kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • Створити ClusterRole з назвою "foo" зі специфікованими apiGroups:

    kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps
    
  • Створити ClusterRole з назвою "foo" з дозволами на субресурси:

    kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
    
  • Створити ClusterRole з назвою "foo" зі специфікованими nonResourceURL:

    kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
    
  • Створити ClusterRole з назвою "monitoring" зі специфікованим aggregationRule:

    kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
    

kubectl create rolebinding

Надає Role або ClusterRole в межах певного простору імен. Приклади:

  • В межах простору імен "acme" надати дозволи з ClusterRole "admin" користувачу з імʼям "bob":

    kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme
    
  • В межах простору імен "acme" надати дозволи з ClusterRole "view" службовому обліковому запису в просторі імен "acme" з імʼям "myapp":

    kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme
    
  • В межах простору імен "acme" надати дозволи з ClusterRole "view" службовому обліковому запису в просторі імен "myappnamespace" з імʼям "myapp":

    kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole=view --serviceaccount=myappnamespace:myapp --namespace=acme
    

kubectl create clusterrolebinding

Надає ClusterRole в межах усього кластеру (всі простори імен). Приклади:

  • В межах усього кластеру надати дозволи з ClusterRole "cluster-admin" користувачу з імʼям "root":

    kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root
    
  • В межах усього кластеру надати дозволи з ClusterRole "system:node-proxier" користувачу з імʼям "system:kube-proxy":

    kubectl create clusterrolebinding kube-proxy-binding --clusterrole=system:node-proxier --user=system:kube-proxy
    
  • В межах усього кластеру надати дозволи з ClusterRole "view" службовому обліковому запису з імʼям "myapp" у просторі імен "acme":

    kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp
    

kubectl auth reconcile

Створює або оновлює обʼєкти API rbac.authorization.k8s.io/v1 з файлу маніфесту.

Відсутні обʼєкти створюються, і простір імен для обʼєктів у просторі імен створюється за потреби.

Наявні ролі оновлюються, щоб включати дозволи з вхідних обʼєктів, і видаляти зайві дозволи, якщо вказано --remove-extra-permissions.

Наявні привʼязки оновлюються, щоб включати субʼєкти з вхідних обʼєктів, і видаляти зайві субʼєкти, якщо вказано --remove-extra-subjects.

Приклади:

  • Тестове застосування файлу маніфесту обʼєктів RBAC, відображаючи зміни, які будуть зроблені:

    kubectl auth reconcile -f my-rbac-rules.yaml --dry-run=client
    
  • Застосування файлу маніфесту обʼєктів RBAC, збереження будь-яких зайвих дозволів (в ролях) і зайвих субʼєктів (в привʼязках):

    kubectl auth reconcile -f my-rbac-rules.yaml
    
  • Застосування файлу маніфесту обʼєктів RBAC, видалення будь-яких зайвих дозволів (в ролях) і зайвих субʼєктів (в привʼязках):

    kubectl auth reconcile -f my-rbac-rules.yaml --remove-extra-subjects --remove-extra-permissions
    

Права доступу ServiceAccount

Типові політики RBAC надають обмежені права компонентам панелі управління, вузлам і контролерам, але не надають жодних дозволів службовим обліковим записам за межами простору імен kube-system (поза межами дозволів, наданих ролями виявлення API).

Це дозволяє надавати конкретні ролі конкретним ServiceAccounts за необхідності. Тонке налаштування привʼязок ролей забезпечує більшу безпеку, але вимагає більше зусиль для адміністрування. Ширші привʼязки можуть надати зайвий (і потенційно ескалуючий) доступ до API ServiceAccounts, але їх легше адмініструвати.

Підходи від найбезпечніших до найменш безпечних:

  1. Надання ролі для службового облікового запису для конкретного застосунку (найкращий варіант)

    Це вимагає від застосунку зазначення serviceAccountName в його специфікації Pod, і створення службового облікового запису (через API, маніфест програми, kubectl create serviceaccount, тощо).

    Наприклад, надати права лише на читання в межах "my-namespace" службовому обліковому запису "my-sa":

    kubectl create rolebinding my-sa-view \
      --clusterrole=view \
      --serviceaccount=my-namespace:my-sa \
      --namespace=my-namespace
    
  2. Надання ролі службовому обліковому запису "default" в просторі імен

    Якщо програма не зазначає serviceAccountName, вона використовує службовий обліковий запис "default".

    Наприклад, надати права лише на читання в межах "my-namespace" службовому обліковому запису "default":

    kubectl create rolebinding default-view \
      --clusterrole=view \
      --serviceaccount=my-namespace:default \
      --namespace=my-namespace
    

    Багато надбудов працюють від імені службового облікового запису "default" у просторі імен kube-system. Щоб дозволити цим надбудовам працювати з правами суперкористувача, надайте права cluster-admin службовому обліковому запису "default" у просторі імен kube-system.

    kubectl create clusterrolebinding add-on-cluster-admin \
      --clusterrole=cluster-admin \
      --serviceaccount=kube-system:default
    
  3. Надання ролі всім службовим обліковим записам у просторі імен

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

    Наприклад, надати права лише на читання в межах "my-namespace" усім службовим обліковим записам у цьому просторі імен:

    kubectl create rolebinding serviceaccounts-view \
      --clusterrole=view \
      --group=system:serviceaccounts:my-namespace \
      --namespace=my-namespace
    
  4. Надання обмеженої ролі всім службовим обліковим записам у кластері (не рекомендується)

    Якщо ви не хочете керувати дозволами по кожному простору імен, ви можете надати кластерну роль усім службовим обліковим записам у кластері.

    Наприклад, надати права лише на читання в усіх просторах імен усім службовим обліковим записам у кластері:

    kubectl create clusterrolebinding serviceaccounts-view \
      --clusterrole=view \
      --group=system:serviceaccounts
    
  5. Надання прав суперкористувача всім службовим обліковим записам у кластері (категорично не рекомендується)

    Якщо вам не важливо розподіляти дозволи взагалі, ви можете надати права суперкористувача всім службовим обліковим записам.

    kubectl create clusterrolebinding serviceaccounts-cluster-admin \
      --clusterrole=cluster-admin \
      --group=system:serviceaccounts
    

Права запису для EndpointSlices і Endpoints

Кластери Kubernetes, створені до версії Kubernetes v1.22, включають права запису для EndpointSlices (і Endpoints) у ролях "edit" і "admin". У рамках помʼякшення наслідків CVE-2021-25740, цей доступ не є частиною агрегованих ролей у кластерах, створених із використанням Kubernetes v1.22 або пізніших версій.

Кластери, які були оновлені до Kubernetes v1.22, не підпадають під цю зміну. Оголошення CVE включає вказівки щодо обмеження цього доступу в поточних кластерах.

Якщо ви хочете, щоб нові кластери зберігали цей рівень доступу в агрегованих ролях, ви можете створити наступну ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubernetes.io/description: |-
      Add endpoints write permissions to the edit and admin roles. This was
      removed by default in 1.22 because of CVE-2021-25740. See
      https://issue.k8s.io/103675. This can allow writers to direct LoadBalancer
      or Ingress implementations to expose backend IPs that would not otherwise
      be accessible, and can circumvent network policies or security controls
      intended to prevent/isolate access to those backends.
      EndpointSlices were never included in the edit or admin roles, so there
      is nothing to restore for the EndpointSlice API.      
  labels:
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
  name: custom:aggregate-to-edit:endpoints # виможете змінити це поле, за бажанням
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "deletecollection", "patch", "update"]

Оновлення з ABAC

Кластери, що спочатку працювали на старіших версіях Kubernetes, часто використовували дозвільні політики ABAC, включаючи надання повного доступу до API всім службовим обліковим записам.

Типові політики RBAC надають обмежені права компонентам панелі управління, вузлам і контролерам, але не надають жодних дозволів службовим обліковим записам за межами простору імен kube-system (поза межами дозволів, наданих ролями виявлення API).

Хоча це набагато безпечніше, це може бути руйнівним для поточних робочих навантажень, які очікують автоматичного отримання дозволів API. Ось два підходи для управління цим переходом:

Паралельні авторизатори

Запускайте як RBAC, так і ABAC авторизатори, і вкажіть файл політики, що містить стару політику ABAC:

--authorization-mode=...,RBAC,ABAC --authorization-policy-file=mypolicy.json

Щоб детально пояснити цей перший параметр командного рядка: якщо раніші авторизатори, такі як Node, відхиляють запит, тоді авторизатор RBAC намагається авторизувати запит до API. Якщо RBAC також відхиляє цей запит до API, тоді запускається авторизатор ABAC. Це означає, що будь-який запит, дозволений будь-якою політикою RBAC або ABAC, буде дозволений.

Коли kube-apiserver запускається з рівнем логування 5 або вище для компонента RBAC (--vmodule=rbac*=5 або --v=5), ви можете побачити відмови RBAC у лозі сервера API (позначені як RBAC). Ви можете використовувати цю інформацію для визначення, які ролі потрібно надати яким користувачам, групам або службовим обліковим записам.

Після того, як ви надали ролі службовим обліковим записам і робочі навантаження працюють без повідомлень про відмову RBAC у логах сервера, ви можете видалити авторизатор ABAC.

Дозвільні права RBAC

Ви можете відтворити дозвільну політику ABAC, використовуючи привʼязки ролей RBAC.

Після переходу на використання RBAC, ви повинні налаштувати контроль доступу для вашого кластера, щоб забезпечити відповідність вашим інформаційним вимогам безпеки.

5 - Використання авторизації вузлів

Авторизація вузлів — це режим авторизації спеціального призначення, який спеціально авторизує запити API, зроблені kubelet-ами.

Огляд

Авторизатор вузлів дозволяє kubelet-ам виконувати операції з API. Це включає:

Операції читання:

  • services
  • endpoints
  • nodes
  • pods
  • secrets, configmaps, persistent volume claims та persistent volumes, що стосуються Podʼів, привʼязаних до вузла kubelet-а
СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [alpha]

Коли функціональну можливість AuthorizeNodeWithSelectors увімкнено (разом із передумовою AuthorizeWithSelectors), kubelets можуть читати лише власні обʼєкти Node і можуть читати лише Podʼи, привʼязані до їхнього вузла.

Операції запису:

  • вузли та статус вузлів (увімкніть втулок допуску NodeRestriction, щоб обмежити kubelet у зміні свого власного вузла)
  • поди та статус подів (увімкніть втулок допуску NodeRestriction, щоб обмежити kubelet у зміні подів, привʼязаних до себе)
  • події

Операції, повʼязані з авторизацією:

  • доступ на читання/запис API CertificateSigningRequests для TLS початкового запуску
  • можливість створювати TokenReview та SubjectAccessReview для делегованої автентифікації/авторизації

У майбутніх випусках авторизатор вузлів може додавати або видаляти дозволи, щоб забезпечити kubelet-и мінімальним набором дозволів, необхідних для правильної роботи.

Для того, щоб бути авторизованими авторизатором вузлів, kubelet-и повинні використовувати облікові дані, які ідентифікують їх як членів групи system:nodes, з іменем користувача system:node:<nodeName>. Цей формат групи та імені користувача відповідає ідентичності, створеній для кожного kubelet-а в рамках TLS початкового запуску kubelet-а.

Значення <nodeName> має точно відповідати імені вузла, як зареєстровано kubelet-ом. Стандартно це імʼя хосту, надане hostname, або замінене за допомогою опції kubelet --hostname-override. Однак, при використанні опції kubelet --cloud-provider, конкретне імʼя хосту може бути визначено постачальником хмарних послуг, ігноруючи локальний hostname та опцію --hostname-override. Для деталей щодо визначення імені хосту kubelet-ом, дивіться довідник з опцій kubelet.

Щоб увімкнути авторизатор вузлів, запустіть apiserver з --authorization-mode=Node.

Щоб обмежити обʼєкти API, які можуть писати kubelet-и, увімкніть втулок допуску NodeRestriction шляхом запуску apiserver з --enable-admission-plugins=...,NodeRestriction,....

Міркування щодо міграції

Kubelet-и поза групою system:nodes

Kubelet-и, що знаходяться поза групою system:nodes, не будуть авторизовані режимом авторизації Node, і їм потрібно буде продовжувати авторизацію через механізм, який їх наразі авторизує. Вутлок допуску вузлів не буде обмежувати запити від цих kubelet-ів.

Kubelet-и з недиференційованими іменами користувачів

У деяких розгортаннях kubelet-и мають облікові дані, що розміщують їх у групі system:nodes, але не ідентифікують конкретний вузол, з яким вони повʼязані, оскільки вони не мають імені користувача у форматі system:node:.... Ці kubelet-и не будуть авторизовані режимом авторизації Node, і їм потрібно буде продовжувати авторизацію через механізм, який їх наразі авторизує.

Втулок допуску NodeRestriction буде ігнорувати запити від цих kubelet-ів, оскільки стандартна реалізація ідентифікатора вузла не вважатиме це ідентичністю вузла.

6 - Режим Webhook

Webhook — це зворотний виклик HTTP: HTTP POST, який відбувається, коли щось стається; просте сповіщення про подію через HTTP POST. Вебзастосунок, що реалізує Webhook, буде надсилати POST повідомлення на URL, коли відбуваються певні події.

Коли вказано режим Webhook, Kubernetes звертається до зовнішнього REST сервісу для визначення привілеїв користувача.

Формат файлу конфігурації

Режим Webhook потребує файл для HTTP конфігурації, що вказується за допомогою прапорця --authorization-webhook-config-file=SOME_FILENAME.

Файл конфігурації використовує формат файлу kubeconfig. У файлі "users" стосується конфігурації webhook API сервера, а "clusters" — до віддаленого сервісу.

Приклад конфігурації, що використовує клієнтську автентифікацію HTTPS:

# Версія Kubernetes API
apiVersion: v1
# тип API обʼєкта
kind: Config
# кластери відносяться до віддаленого сервісу
clusters:
  - name: name-of-remote-authz-service
    cluster:
      # CA для перевірки віддаленого сервісу
      certificate-authority: /path/to/ca.pem
      # URL віддаленого сервісу для запитів. Має використовувати 'https'. Не може включати параметри.
      server: https://authz.example.com/authorize

# користувачі відносяться до конфігурації webhook API сервера
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # сертифікат для використання webhook втулком
      client-key: /path/to/key.pem          # ключ, що відповідає сертифікату

# файли kubeconfig вимагають контекст. Вкажіть один для API сервера.
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authz-service
    user: name-of-api-server
  name: webhook

Запити корисного навантаження

При ухваленні рішення про авторизацію, API сервер надсилає JSON- серіалізований обʼєкт authorization.k8s.io/v1beta1 SubjectAccessReview, що описує дію. Цей обʼєкт містить поля, що описують користувача, який намагається зробити запит, та деталі про ресурс, до якого здійснюється доступ, або атрибути запиту.

Зверніть увагу, що обʼєкти API webhook підлягають тим самим правилам сумісності версій, що й інші обʼєкти API Kubernetes. Імплементатори повинні бути обізнані з менш суворими обіцянками сумісності для бета-обʼєктів і перевіряти поле "apiVersion" запиту для забезпечення правильної десеріалізації. Додатково, API сервер повинен увімкнути групу розширень API authorization.k8s.io/v1beta1 (--runtime-config=authorization.k8s.io/v1beta1=true).

Приклад тіла запиту:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "kittensandponies",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}

Віддалений сервіс має заповнити поле status запиту і відповісти, дозволяючи або забороняючи доступ. Поле spec у відповіді ігнорується і може бути пропущене. Дозвільна відповідь виглядатиме так:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": true
  }
}

Для заборони доступу є два методи.

Перший метод є кращим у більшості випадків і вказує, що webhook авторизації не дозволяє або не має "думки" ("no opinion") щодо запиту, але якщо інші авторизатори налаштовані, вони отримують шанс дозволити запит. Якщо немає інших авторизаторів або жоден з них не дозволяє запит, запит забороняється. Webhook поверне:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
    "reason": "user does not have read access to the namespace"
  }
}

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

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
    "denied": true,
    "reason": "user does not have read access to the namespace"
  }
}

Доступ до нересурсних шляхів здійснюється як:

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "nonResourceAttributes": {
      "path": "/debug",
      "verb": "get"
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}
СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [alpha]

З увімкненою функцією AuthorizeWithSelectors поля та мітки селекторів у запиті передаються до вебхуку авторизації. Вебхук може приймати рішення про авторизацію, орієнтуючися на обмежені селектори полів і міток, якщо бажає.

Документація API SubjectAccessReview надає вказівки щодо того, як ці поля мають інтерпретуватися і оброблятися вебхуками авторизації, зокрема використанням розібраних вимог замість сирих рядків селекторів та як безпечно обробляти невідомі оператори.

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "verb": "list",
      "group": "",
      "resource": "pods",
      "fieldSelector": {
        "requirements": [
          {"key":"spec.nodeName", "operator":"In", "values":["mynode"]}
        ]
      },
      "labelSelector": {
        "requirements": [
          {"key":"example.com/mykey", "operator":"In", "values":["myvalue"]}
        ]
      }
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}

Нересурсні шляхи включають: /api, /apis, /metrics, /logs, /debug, /healthz, /livez, /openapi/v2, /readyz та /version. Клієнти потребують доступу до /api, /api/*, /apis, /apis/*, та /version для визначення, які ресурси та версії присутні на сервері. Доступ до інших нересурсних шляхів може бути заборонений без обмеження доступу до REST API.

За подальшою інформацією звертайтеся до документації SubjectAccessReview API та імплементації webhook.go.

7 - Використання ABAC авторизації

Авторизація на основі атрибутів (ABAC) визначає парадигму контролю доступу, за якою права доступу надаються користувачам за допомогою політик, які комбінують атрибути.

Формат файлу політики

Для увімкнення режиму ABAC вказуйте --authorization-policy-file=SOME_FILENAME та --authorization-mode=ABAC при запуску.

Формат файлу — один обʼєкт JSON на рядок. Не повинно бути жодного вкладеного списку або map, тільки один map на рядок.

Кожний рядок — "обʼєкт політики", де такий обʼєкт є map із такими властивостями:

  • Властивості версіювання:
    • apiVersion, типу string; допустимі значення "abac.authorization.kubernetes.io/v1beta1". Дозволяє версіювання та конвертацію формату політики.
    • kind, типу string: допустимі значення "Policy". Дозволяє версіювання та конвертацію формату політики.
  • Властивість spec встановлена на мапу з такими властивостями:
    • Властивості зіставлення субʼєкта:
      • user, типу string; рядок користувача з --token-auth-file. Якщо ви вказуєте user, він повинен збігатися з імʼям користувача автентифікованого користувача.
      • group, типу string; якщо ви вказуєте group, він повинен збігатися з однією з груп автентифікованого користувача. system:authenticated збігається з усіма автентифікованими запитами. system:unauthenticated збігається з усіма неавтентифікованими запитами.
    • Властивості зіставлення ресурсу:
      • apiGroup, типу string; група API.
        • Наприклад: apps, networking.k8s.io
        • Маска: * збігається з усіма групами API.
      • namespace, типу string; простір імен.
        • Наприклад: kube-system
        • Маска: * збігається з усіма запитами ресурсів.
      • resource, типу string; тип ресурсу
        • Наприклад: pods, deployments
        • Маска: * збігається з усіма запитами ресурсів.
    • Властивості зіставлення нересурсів:
      • nonResourcePath, типу string; шляхи запитів нересурсів.
        • Наприклад: /version або /apis
        • Маска:
          • * збігається з усіма запитами нересурсів.
          • /foo/* збігається з усіма підшляхами /foo/.
    • readonly, типу boolean, якщо true, означає, що політика, що зіставляється з ресурсом, застосовується тільки до операцій get, list, та watch, а політика, що зіставляється з нересурсами, застосовується тільки до операції get.

Алгоритм авторизації

У запиту є атрибути, які відповідають властивостям обʼєкта політики.

Коли отримано запит, визначаються атрибути. Невідомі атрибути встановлюються на нульове значення свого типу (наприклад, порожній рядок, 0, false).

Властивість, встановлена на "*", буде збігатися з будь-яким значенням відповідного атрибута.

Кортеж атрибутів перевіряється на відповідність кожній політиці в файлі політики. Якщо принаймні один рядок відповідає атрибутам запиту, тоді запит авторизований (але може зазнати невдачі під час подальшої перевірки).

Щоб дозволити будь-якому автентифікованому користувачеві зробити щось, створіть політику з властивістю group, встановленою на "system:authenticated".

Щоб дозволити будь-якому неавтентифікованому користувачеві зробити щось, створіть політику з властивістю group, встановленою на "system:unauthenticated".

Щоб дозволити користувачеві зробити все, створіть політику з властивостями apiGroup, namespace, resource та nonResourcePath, встановленими на "*".

Kubectl

Kubectl використовує точки доступу /api та /apis apiserver для виявлення сервісів, які обслуговують типи ресурсів, і перевіряє обʼєкти, надіслані до API за допомогою операцій create/update, використовуючи інформацію про схему, розташовану в /openapi/v2.

При використанні авторизації на основі атрибутів, ці спеціальні ресурси мають бути явно відкриті за допомогою властивості nonResourcePath в політиці (див. приклади нижче):

  • /api, /api/*, /apis та /apis/* для вибору версії API.
  • /version для отримання версії сервера через kubectl version.
  • /swaggerapi/* для операцій create/update.

Щоб перевірити HTTP-виклики, повʼязані з певною операцією kubectl, ви можете збільшити рівень деталізації:

kubectl --v=8 version

Приклади

  1. Alice може робити все з усіма ресурсами:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "alice", "namespace": "*", "resource": "*", "apiGroup": "*"}}
    
  2. Kubelet може читати будь-які Podʼи:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "pods", "readonly": true}}
    
  3. Kubelet може читати та записувати події:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "events"}}
    
  4. Bob може лише читати Podʼи в просторі імен "projectCaribou":

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "bob", "namespace": "projectCaribou", "resource": "pods", "readonly": true}}
    
  5. Будь-хто може робити запити тільки для читання на всі нересурсні шляхи:

    {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:authenticated", "readonly": true, "nonResourcePath": "*"}}
     {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:unauthenticated", "readonly": true, "nonResourcePath": "*"}}
    

Приклад повного файлу

Коротка нотатка про службові облікові записи

Кожен службовий обліковий запис має відповідне імʼя користувача ABAC, і імʼя цього службового облікового запису генерується згідно з конвенцією щодо найменування:

system:serviceaccount:<namespace>:<serviceaccountname>

Створюючи новий простір імен, створюється новий службовий обліковий запис у наступному форматі:

system:serviceaccount:<namespace>:default

Наприклад, якщо ви хочете надати стандартному службовому обліковому запису (у просторі імен kube-system) повні привілеї в API за допомогою ABAC, ви додаєте цей рядок до файлу політики:

{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"system:serviceaccount:kube-system:default","namespace":"*","resource":"*","apiGroup":"*"}}

apiserver повинен бути перезапущений, щоб використати нові рядки.

8 - Контролери допуску

Ця сторінка надає огляд контролерів допуску.

Що це таке?

Контролер допуску — це фрагмент коду, який перехоплює запити до сервера API Kubernetes перед збереженням обʼєкта, але після того, як запит був автентифікований та авторизований.

Контролери допуску можуть бути валідаційними, модифікуючими або обома. Модифікуючі контролери можуть змінювати обʼєкти, повʼязані з запитами, які вони допускають; валідаційні контролери не можуть цього робити.

Контролери допуску обмежують запити на створення, видалення, зміну обʼєктів. Контролери допуску також можуть блокувати нестандартні дієслова, такі як запити на підключення до Podʼа через проксі сервера API. Контролери допуску не блокують (і не можуть) запити на читання (get, watch або list) обʼєктів.

Контролери допуску в Kubernetes 1.31 складаються зі списку нижче, вбудовані у двійковий файл kube-apiserver і можуть бути налаштовані тільки адміністратором кластера. У цьому списку є два спеціальні контролери: MutatingAdmissionWebhook і ValidatingAdmissionWebhook. Вони виконують модифікуючі та валідаційні (відповідно) вебхуки контролю допуску, які налаштовуються в API.

Етапи контролю допуску

Процес контролю допуску проходить у два етапи. На першому етапі виконуються модифікуючі контролери допуску. На другому етапі виконуються валідаційні контролери допуску. Знову ж таки, деякі контролери є обома.

Якщо будь-який з контролерів на будь-якому етапі відхиляє запит, весь запит негайно відхиляється, і користувачу повертається помилка.

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

Чому вони потрібні?

Кілька важливих функцій Kubernetes вимагають увімкнення контролера допуску для належної підтримки функції. Як результат, сервер API Kubernetes, який неправильно налаштований з необхідним набором контролерів допуску, є неповним сервером і не підтримуватиме всі функції, які ви очікуєте.

Як увімкнути контролер допуску?

Прапорець сервера API Kubernetes enable-admission-plugins приймає через кому список втулків контролю допуску, які слід викликати перед зміною обʼєктів у кластері. Наприклад, наступна команда увімкне втулки контролю допуску NamespaceLifecycle та LimitRanger:

kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

Як вимкнути контролер допуску?

Прапорець сервера API Kubernetes disable-admission-plugins приймає через кому список втулків контролю допуску, які слід вимкнути, навіть якщо вони є у списку втулків, що є стандартно увімкненими.

kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...

Які втулки є стандартно увімкненими?

Щоб побачити, які втулки допуску увімкнені:

kube-apiserver -h | grep enable-admission-plugins

У Kubernetes 1.31 стандартно увімкнені такі втулки:

CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionPolicy, ValidatingAdmissionWebhook

Що робить кожен контролер допуску?

AlwaysAdmit

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

Тип: Валідаційний.

Цей контролер допуску дозволяє запуск всіх Podʼів в кластер. Він застарілий, оскільки його поведінка така ж, якби не було ніякого контролера допуску взагалі.

AlwaysDeny

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

Тип: Валідаційний.

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

AlwaysPullImages

Тип: Модифікуючий та Валідаційний.

Цей контролер допуску модифікує кожен новий Pod, щоб примусити використовувати політику завантаження образів Always. Це корисно в багатокористувацькому кластері, щоб користувачі могли бути впевнені, що їхні приватні образи можуть використовувати лише ті, хто має відповідні облікові дані для їх завантаження. Без цього контролера допуску, після того, як образи було завантажено на вузол, будь-який Pod будь-якого користувача може використовувати його, знаючи імʼя образу (за умови, що Pod розміщений на правильному вузлі), без жодної перевірки вторизації для образу. Коли цей контролер допуску увімкнений, образи завжди завантажуються перед запуском контейнерів, що означає, що потрібні дійсні облікові дані.

CertificateApproval

Тип: Валідаційний.

Цей контролер допуску спостерігає за запитами на затвердження ресурсів CertificateSigningRequest і виконує додаткові перевірки авторизації, щоб переконатися, що користувач, який затверджує, має дозвіл на затвердження запитів сертифікатів із запитаним spec.signerName у ресурсі CertificateSigningRequest.

Дивіться Запити на підписання сертифікатів для отримання додаткової інформації про дозволи, необхідні для виконання різних дій з ресурсами CertificateSigningRequest.

CertificateSigning

Тип: Валідаційний.

Цей контролер допуску спостерігає за оновленнями поля status.certificate ресурсів CertificateSigningRequest і виконує додаткові перевірки авторизації, щоб переконатися, що користувач, який підписує, має дозвіл на підписання запитів сертифікатів із запитаним spec.signerName у ресурсі CertificateSigningRequest.

Дивіться Запити на підписання сертифікатів для отримання додаткової інформації про дозволи, необхідні для виконання різних дій з ресурсами CertificateSigningRequest.

CertificateSubjectRestriction

Тип: Валідаційний.

Цей контролер допуску спостерігає за створенням ресурсів CertificateSigningRequest, які мають spec.signerName значення kubernetes.io/kube-apiserver-client. Він відхиляє будь-який запит, який вказує групу (або атрибут організації) system:masters.

DefaultIngressClass

Тип: Модифікуючий.

Цей контролер допуску спостерігає за створенням обʼєктів Ingress, які не запитують жодного конкретного класу ingress, і автоматично додає до них стандартний клас ingress. Таким чином, користувачі, які не запитують жодного спеціального класу ingress, не повинні про це турбуватися та отримають стандартний клас.

Цей контролер допуску нічого не робить, коли не налаштований жоден стандартний клас ingress. Коли більше одного класу ingress позначено як стандартний клас, він відхиляє будь-яке створення Ingress з помилкою, і адміністратор повинен переглянути свої обʼєкти IngressClass та позначити лише один як стандартний клас (з анотацією "ingressclass.kubernetes.io/is-default-class"). Цей контролер допуску ігнорує будь-які оновлення Ingress; він діє тільки при створенні.

Дивіться документацію Ingress для отримання додаткової інформації про класи ingress і як позначити один як стандартний клас.

DefaultStorageClass

Тип: Модифікуючий.

Цей контролер допуску спостерігає за створенням обʼєктів PersistentVolumeClaim, які не запитують жодного конкретного класу зберігання, і автоматично додає до них стандартний клас зберігання. Таким чином, користувачі, які не запитують жодного спеціального класу зберігання, не повинні про це турбуватися та отримають стандартний клас.

Цей контролер допуску нічого не робить, коли не налаштований жоден стандартний клас зберігання. Коли більше одного класу зберігання позначено як стандартний клас, він відхиляє будь-яке створення PersistentVolumeClaim з помилкою, і адміністратор повинен переглянути свої обʼєкти StorageClass і позначити лише один як стандартний клас. Цей контролер допуску ігнорує будь-які оновлення PersistentVolumeClaim; він діє тільки при створенні.

Дивіться документацію постійних томів про persistent volume claims та класи зберігання, а також як позначити клас зберігання як стандартний клас.

DefaultTolerationSeconds

Тип: Модифікуючий.

Цей контролер допуску встановлює стандартне значення толерантності для Podʼів, щоб терпіти taints notready:NoExecute та unreachable:NoExecute на основі параметрів введення k8s-apiserver default-not-ready-toleration-seconds та default-unreachable-toleration-seconds, якщо Podʼи ще не мають толерантності до taints node.kubernetes.io/not-ready:NoExecute або node.kubernetes.io/unreachable:NoExecute. Стандартне значення для default-not-ready-toleration-seconds та default-unreachable-toleration-seconds становить 5 хвилин.

DenyServiceExternalIPs

Тип: Валідаційний.

Цей контролер допуску відхиляє всі нові використання поля externalIPs у Service. Ця функція дуже потужна (дозволяє перехоплення мережевого трафіку) і не контролюється належним чином політиками. Коли цей контролер увімкнено, користувачі кластера не можуть створювати нові Serviceʼи, які використовують externalIPs, і не можуть додавати нові значення до externalIPs в поточних обʼєктах Service. Поточні використання externalIPs не зачіпаються, і користувачі можуть видаляти значення з externalIPs в наявних обʼєктах Service.

Більшість користувачів взагалі не потребує цієї функції, і адміністратори кластера повинні розглянути можливість її вимкнення. Кластери, які потребують цієї функції, повинні розглянути можливість використання власних політик для керування її використанням.

Цей контролер допуску стандартно вимкнено.

EventRateLimit

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

Тип: Валідаційний.

Цей контролер допуску зменшує проблему, коли API-сервер переповнюється запитами на зберігання нових подій (Events). Адміністратор кластера може вказати обмеження швидкості подій, виконавши такі кроки:

  • Увімкнути контролер допуску EventRateLimit;
  • Посилатися на конфігураційний файл EventRateLimit з файлу, наданого у командному рядку API-сервера з прапорцем --admission-control-config-file:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: EventRateLimit
    path: eventconfig.yaml
...

Є чотири типи обмежень, які можна вказати у конфігурації:

  • Server: Всі запити подій (створення або зміни), що отримує API-сервер, використовують один спільний кошик.
  • Namespace: Кожен простір імен має виділений кошик.
  • User: Кожен користувач отримує кошик.
  • SourceAndObject: Кошик призначається кожній комбінації джерела та обʼєкта події.

Нижче наведено приклад eventconfig.yaml для такої конфігурації:

apiVersion: eventratelimit.admission.k8s.io/v1alpha1
kind: Configuration
limits:
  - type: Namespace
    qps: 50
    burst: 100
    cacheSize: 2000
  - type: User
    qps: 10
    burst: 50

Дивіться API конфігурації EventRateLimit (v1alpha1) для отримання додаткових деталей.

Цей контролер допуску стандартно вимкнено.

ExtendedResourceToleration

Тип: Модифікуючий.

Цей втулок полегшує створення виділених вузлів з розширеними ресурсами. Якщо оператори хочуть створити виділені вузли з розширеними ресурсами (наприклад, GPU, FPGA тощо), вони повинні накладати taint на вузол з іменем розширеного ресурсу як ключем. Цей контролер допуску, якщо він увімкнений, автоматично додає толерантності до таких taint у Podʼи, які запитують розширені ресурси, тому користувачам не потрібно вручну додавати ці толерантності.

Цей контролер допуску стандартно вимкнено.

ImagePolicyWebhook

Тип: Валідаційний.

Контролер допуску ImagePolicyWebhook дозволяє бекенд-вебхуку приймати рішення про допуск.

Цей контролер допуску стандартно вимкнено.

Формат конфігураційного файлу

ImagePolicyWebhook використовує конфігураційний файл для налаштування поведінки бекенду. Цей файл може бути у форматі JSON або YAML і має наступний формат:

imagePolicy:
  kubeConfigFile: /path/to/kubeconfig/for/backend
  # час у секундах для кешування дозволу
  allowTTL: 50
  # час у секундах для кешування відмови
  denyTTL: 50
  # час у мілісекундах між спробами повтору
  retryBackoff: 500
  # визначає поведінку у разі відмови бекенда вебхука
  defaultAllow: true

Посилання на конфігураційний файл ImagePolicyWebhook повинно бути зазначене у файлі, який передається прапорцю командного рядка API-сервера --admission-control-config-file:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: ImagePolicyWebhook
    path: imagepolicyconfig.yaml
...

Альтернативно, ви можете вбудувати конфігурацію безпосередньо у файл:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
  - name: ImagePolicyWebhook
    configuration:
      imagePolicy:
        kubeConfigFile: <path-to-kubeconfig-file>
        allowTTL: 50
        denyTTL: 50
        retryBackoff: 500
        defaultAllow: true

Конфігураційний файл ImagePolicyWebhook повинен посилатися на файл у форматі kubeconfig, який налаштовує зʼєднання з бекендом. Бекенд повинен здійснювати комунікацію через TLS.

Поле cluster у файлі kubeconfig має посилатися на віддалений сервіс, а поле user повинно містити дані авторизації.

# clusters посилається на віддалений сервіс.
clusters:
  - name: name-of-remote-imagepolicy-service
    cluster:
      certificate-authority: /path/to/ca.pem    # CA для верифікації віддаленого сервісу.
      server: https://images.example.com/policy # URL віддаленого сервісу для запитів. Повинен використовувати 'https'.

# users посилається на конфігурацію вебхука API-сервера.
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem # сертифікат для використання контролером допуску вебхука
      client-key: /path/to/key.pem          # ключ, що відповідає сертифікату

Для додаткової конфігурації HTTP дивіться документацію kubeconfig.

Вміст запитів

Під час прийняття рішення про допуск, API-сервер надсилає POST-запит з серіалізованим у форматі JSON обʼєктом imagepolicy.k8s.io/v1alpha1 ImageReview, що описує дію. Цей обʼєкт містить поля, що описують контейнери, які підлягають допуску, а також будь-які анотації Podʼа, що відповідають *.image-policy.k8s.io/*.

Приклад тіла запиту:

{
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "kind": "ImageReview",
  "spec": {
    "containers": [
      {
        "image": "myrepo/myimage:v1"
      },
      {
        "image": "myrepo/myimage@sha256:beb6bd6a68f114c1dc2ea4b28db81bdf91de202a9014972bec5e4d9171d90ed"
      }
    ],
    "annotations": {
      "mycluster.image-policy.k8s.io/ticket-1234": "break-glass"
    },
    "namespace": "mynamespace"
  }
}

Віддалений сервіс повинен заповнити поле status запиту і відповісти з дозволом або відмовою доступу. Поле spec тіла відповіді ігнорується, і його можна не включати. Відповідь, яка дозволяє доступ, виглядатиме так:

{
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "kind": "ImageReview",
  "status": {
    "allowed": true
  }
}

Щоб відмовити у доступі, сервіс відповість так:

{
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "kind": "ImageReview",
  "status": {
    "allowed": false,
    "reason": "image currently blacklisted"
  }
}

Для додаткової документації дивіться API imagepolicy.v1alpha1.

Розширення за допомогою анотацій

Усі анотації на Podʼі, що відповідають *.image-policy.k8s.io/*, надсилаються до вебхука. Надсилання анотацій дозволяє користувачам, які знають про бекенд політики образів, надсилати додаткову інформацію до нього, а також дозволяє реалізаціям різних бекендів приймати різну інформацію.

Приклади інформації, яку ви можете тут розмістити:

  • запит на "пробиття" для обходу політики у разі надзвичайної ситуації;
  • номер квитка з системи квитків, що документує запит на "пробиття";
  • підказка серверу політики щодо imageID наданого образу для економії часу на пошук.

У будь-якому випадку, анотації надаються користувачем і не перевіряються Kubernetes будь-яким чином.

LimitPodHardAntiAffinityTopology

Тип: Валідаційний.

Цей контролер допуску забороняє будь-який Pod, який визначає ключ топології AntiAffinity відмінний від kubernetes.io/hostname у requiredDuringSchedulingRequiredDuringExecution.

Цей контролер допуску стандартно вимкнено.

LimitRanger

Тип: Модифікуючий та Валідаційний.

Цей контролер допуску спостерігає за вхідним запитом та забезпечує, щоб він не порушував жодних обмежень, перерахованих в обʼєкті LimitRange в Namespace. Якщо ви використовуєте обʼєкти LimitRange у своєму розгортанні Kubernetes, ви МАЄТЕ використовувати цей контролер допуску для забезпечення дотримання цих обмежень. LimitRanger також може використовуватися для застосування стандартних ресурсних запитів до Pod, які їх не вказують; наразі стандартний LimitRanger застосовує вимогу до 0.1 CPU до всіх Pod у default namespace.

Дивіться довідник LimitRange API та приклад LimitRange для отримання додаткової інформації.

MutatingAdmissionWebhook

Тип: Модифікуючий.

Цей контролер допуску викликає будь-які модифікуючі вебхуки, які відповідають запиту. Відповідні вебхуки викликаються послідовно; кожен з них може змінити обʼєкт, якщо це необхідно.

Цей контролер допуску (як випливає з назви) працює лише на етапі модифікації.

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

Якщо ви вимкнете MutatingAdmissionWebhook, ви також повинні вимкнути обʼєкт MutatingWebhookConfiguration у групі/версії admissionregistration.k8s.io/v1 за допомогою прапорця --runtime-config, обидва стандартно увімкнені.

Будьте обережні при створенні та встановленні модифікуючих вебхуків

  • Користувачі можуть бути спантеличені, коли обʼєкти, які вони намагаються створити, відрізняються від того, що вони отримують назад.
  • Вбудовані контрольні цикли можуть зламатися, коли обʼєкти, які вони намагаються створити, відрізняються при зворотному читанні.
    • Встановлення спочатку незаданих полів менш ймовірно викличе проблеми, ніж переписування полів, встановлених у початковому запиті. Уникайте останнього.
  • Майбутні зміни в контрольних циклах для вбудованих або сторонніх ресурсів можуть зламати вебхуки, які добре працюють сьогодні. Навіть коли API вебхука для установки буде фіналізовано, не всі можливі поведінки вебхука будуть гарантовано підтримуватися нескінченно.

NamespaceAutoProvision

Тип: Модифікуючий.

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

NamespaceExists

Тип: Валідаційний.

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

NamespaceLifecycle

Тип: Валідаційний.

Цей контролер допуску забезпечує, що Namespace, який знаходиться в процесі завершення, не може мати нові обʼєкти, створені в ньому, і забезпечує відхилення запитів у неіснуючому Namespace. Цей контролер допуску також запобігає видаленню трьох системно зарезервованих namespaces: default, kube-system, kube-public.

Видалення Namespace запускає послідовність операцій, які видаляють усі обʼєкти (Pod, Service, тощо) у цьому namespace. Для забезпечення цілісності цього процесу, ми наполегливо рекомендуємо використовувати цей контролер допуску.

NodeRestriction

Тип: Валідаційний.

Цей контролер допуску обмежує обʼєкти Node і Pod, які kubelet може змінювати. Для того, щоб бути обмеженим цим контролером допуску, kubelets повинні використовувати облікові дані у групі system:nodes, з іменем користувача у формі system:node:<nodeName>. Такі kubelets будуть мати дозвіл лише змінювати свої власні обʼєкти API Node, і лише змінювати обʼєкти API Pod, які привʼязані до їх вузла. kubelets не мають права оновлювати або видаляти taint зі свого обʼєкта API Node.

Втулок допуску NodeRestriction перешкоджає kubelets видаляти їх обʼєкт API Node, і забезпечує оновлення kubeletʼом міток з префіксами kubernetes.io/ або k8s.io/ наступним чином:

  • Перешкоджає kubelets додавання/видалення/оновлення міток з префіксом node-restriction.kubernetes.io/. Цей префікс міток зарезервовано для адміністраторів для позначення мітками своїх обʼєктів Node з метою ізоляції робочих навантажень, і kubelets не буде дозволено змінювати мітки з цим префіксом.
  • Дозволяє kubelets додавати/видаляти/оновлювати ці мітки та префікси міток:
    • kubernetes.io/hostname
    • kubernetes.io/arch
    • kubernetes.io/os
    • beta.kubernetes.io/instance-type
    • node.kubernetes.io/instance-type
    • failure-domain.beta.kubernetes.io/region (застаріло)
    • failure-domain.beta.kubernetes.io/zone (застаріло)
    • topology.kubernetes.io/region
    • topology.kubernetes.io/zone
    • мітки з префіксом kubelet.kubernetes.io/
    • мітки з префіксом node.kubernetes.io/

Використання будь-яких інших міток під префіксами kubernetes.io або k8s.io kubelets зарезервовано, і може бути заборонено або дозволено втулком допуску NodeRestriction у майбутньому.

Майбутні версії можуть додати додаткові обмеження для забезпечення того, щоб kubelets мали мінімальний набір дозволів, необхідних для правильної роботи.

OwnerReferencesPermissionEnforcement

Тип: Валідаційний.

Цей контролер допуску захищає доступ до metadata.ownerReferences обʼєкта, так що тільки користувачі з правами delete у обʼєкта можуть його змінювати. Цей контролер допуску також захищає доступ до metadata.ownerReferences[x].blockOwnerDeletion обʼєкта, так що тільки користувачі з правами update у субресурсу finalizers посилального власника можуть його змінювати.

PersistentVolumeClaimResize

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

Тип: Валідаційний.

Цей контролер допуску реалізує додаткові перевірки для перевірки вхідних запитів на зміну розміру PersistentVolumeClaim.

Рекомендується увімкнути контролер допуску PersistentVolumeClaimResize. Цей контролер допуску запобігає зміні розміру всіх вимог за замовчуванням, якщо тільки StorageClass вимоги явно не дозволяє зміну розміру, встановлюючи allowVolumeExpansion на true.

Наприклад: всі PersistentVolumeClaim, створені з наступного StorageClass, підтримують розширення тому:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gluster-vol-default
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://192.168.10.100:8080"
  restuser: ""
  secretNamespace: ""
  secretName: ""
allowVolumeExpansion: true

Для отримання додаткової інформації про persistent volume claims, дивіться PersistentVolumeClaims.

PodNodeSelector

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

Тип: Валідаційний.

Цей контролер допуску задає та обмежує, які селектори вузлів можуть використовуватися в межах namespace, шляхом читання анотації namespace та глобальної конфігурації.

Цей контролер допуску стандартно вимкнено.

Формат конфігураційного файлу

PodNodeSelector використовує конфігураційний файл для налаштування параметрів поведінки бекенду. Зверніть увагу, що формат конфігураційного файлу буде змінено на версійований файл у майбутньому випуску. Цей файл може бути у форматі json або yaml і має наступний формат:

podNodeSelectorPluginConfig:
  clusterDefaultNodeSelector: name-of-node-selector
  namespace1: name-of-node-selector
  namespace2: name-of-node-selector

Зверніться до конфігураційного файлу PodNodeSelector з файлу, наданого прапорцем командного рядка API-сервера --admission-control-config-file:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodNodeSelector
  path: podnodeselector.yaml
...

Формат анотації конфігурації

PodNodeSelector використовує ключ анотації scheduler.alpha.kubernetes.io/node-selector для призначення селекторів вузлів до namespace.

apiVersion: v1
kind: Namespace
metadata:
  annotations:
    scheduler.alpha.kubernetes.io/node-selector: name-of-node-selector
  name: namespace3

Внутрішня поведінка

Цей контролер допуску має наступну поведінку:

  1. Якщо Namespace має анотацію з ключем scheduler.alpha.kubernetes.io/node-selector, використовуйте її значення як селектор вузлів.
  2. Якщо namespace не має такої анотації, використовуйте clusterDefaultNodeSelector, визначений у конфігураційному файлі втулка PodNodeSelector, як селектор вузлів.
  3. Оцініть селектор вузлів Podʼа щодо селектора вузлів namespace на предмет конфліктів. Конфлікти призводять до відхилення.
  4. Оцініть селектор вузлів Podʼа щодо дозволеного селектора, визначеного у конфігураційному файлі плагіну для конкретного namespace. Конфлікти призводять до відхилення.

PodSecurity

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

Тип: Валідаційний.

Контролер допуску PodSecurity перевіряє нові Podʼи перед їх допуском і визначає, чи варто їх допускати на основі запитуваного контексту безпеки та обмежень щодо дозволених Стандартів безпеки для Podʼів для namespace, в якому буде знаходитися Pod.

Дивіться Pod Security Admission для отримання додаткової інформації.

PodSecurity замінив старіший контролер допуску під назвою PodSecurityPolicy.

PodTolerationRestriction

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

Тип: Модифікуючий та Валідаційний.

Контролер допуску PodTolerationRestriction перевіряє конфлікти між толерантностями Podʼа та толерантностями його namespace. Він відхиляє запит Podʼа у разі конфлікту. Потім він обʼєднує толерантності, анотовані на namespace, з толерантностями Podʼа. Отримані толерантності перевіряються щодо списку дозволених толерантностей, анотованих на namespace. Якщо перевірка пройде успішно, запит Podʼа допускається, інакше він відхиляється.

Якщо namespace Podʼа не має повʼязаних з ним стандартних толерантностей або дозволених толерантностей, анотованих, використовуються стандартні толерантності на рівні кластера або список дозволених толерантностей на рівні кластера, якщо вони зазначені.

Толерантності призначаються namespace за допомогою ключа анотації scheduler.alpha.kubernetes.io/defaultTolerations. Список дозволених толерантностей можна додати за допомогою ключа анотації scheduler.alpha.kubernetes.io/tolerationsWhitelist.

Приклад анотацій для namespace:

apiVersion: v1
kind: Namespace
metadata:
  name: apps-that-need-nodes-exclusively
  annotations:
    scheduler.alpha.kubernetes.io/defaultTolerations: '[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'
    scheduler.alpha.kubernetes.io/tolerationsWhitelist: '[{"operator": "Exists", "effect": "NoSchedule", "key": "dedicated-node"}]'

Цей контролер допуску стандатно вимкнено.

Priority

Тип: Модифікуючий та Валідаційний.

Контролер допуску Priority використовує поле priorityClassName і заповнює ціле значення пріоритету. Якщо клас пріоритету не знайдено, Pod відхиляється.

ResourceQuota

Тип: Валідаційний.

Цей контролер допуску спостерігає за вхідним запитом і гарантує, що він не порушує жодних обмежень, перерахованих у обʼєкті ResourceQuota у Namespace. Якщо ви використовуєте обʼєкти ResourceQuota у вашому розгортанні Kubernetes, ви МАЄТЕ використовувати цей контролер допуску для забезпечення дотримання квот.

Дивіться довідник ResourceQuota API та приклад Resource Quota для отримання додаткових деталей.

RuntimeClass

Тип: Модифікуючий та Валідаційний.

Якщо ви визначаєте RuntimeClass з налаштованими накладними витратами, повʼязаними з роботою Podʼів, цей контролер допуску перевіряє вхідні Podʼи. При увімкненні, цей контролер допуску відхиляє будь-які запити на створення Podʼів, які вже мають встановлений overhead. Для Podʼів, які мають налаштований і обраний у своєму .spec RuntimeClass, цей контролер допуску встановлює .spec.overhead у Pod на основі значення, визначеного у відповідному RuntimeClass.

Дивіться також Накладні витрати, повʼязані з роботою Podʼів для отримання додаткової інформації.

ServiceAccount

Тип: Модифікуючий та Валідаційний.

Цей контролер допуску реалізує автоматизацію для службових облікових записів. Проєкт Kubernetes наполегливо рекомендує увімкнути цей контролер допуску. Вам слід увімкнути цей контролер допуску, якщо ви маєте намір використовувати обʼєкти ServiceAccount в Kubernetes.

Щодо анотації kubernetes.io/enforce-mountable-secrets: Хоча назва анотації вказує, що вона стосується лише монтування Secrets, її застосування також поширюється на інші способи використання Secrets в контексті Pod. Тому важливо переконатися, що всі зазначені секрети правильно вказані в ServiceAccount.

StorageObjectInUseProtection

Тип: Модифікуючий.

Втулок StorageObjectInUseProtection додає завершувачі kubernetes.io/pvc-protection або kubernetes.io/pv-protection до новостворених Persistent Volume Claims (PVC) або Persistent Volumes (PV). У випадку видалення користувача PVC або PV PVC або PV не видаляється, поки завершувач не буде видалений з PVC або PV за допомогою контролера захисту PVC або PV. Дивіться Захист обʼєктів зберігання які використовуються для отримання детальнішої інформації.

TaintNodesByCondition

Тип: Модифікуючий.

Цей контролер допуску додає taint до новостворених вузлів таких як NotReady та NoSchedule. Це позначення доволє уникнути стану перегонів, який може призвести до того, що Podʼи будуть заплановані на нових вузлах до того, як їх taint будуть оновлені для точної відповідності їх звітніх умов.

ValidatingAdmissionPolicy

Тип: Валідаційний.

Цей контролер допуску реалізує перевірку CEL для вхідних запитів, що збігаються. Він увімкнений, коли увімкнені як функціональна можливість validatingadmissionpolicy, так і група/версія admissionregistration.k8s.io/v1alpha1. Якщо будь-яка з політик ValidatingAdmissionPolicy не вдасться, запит не вдасться.

ValidatingAdmissionWebhook

Тип: Валідаційний.

Цей контролер допуску викликає будь-які валідуючі вебхуки, які відповідають запиту. Валідуючі вебхуки викликаються паралельно; якщо будь-який з них відхиляє запит, запит не вдається. Цей контролер допуску працює лише на етапі валідації; вебхуки, які він викликає, не можуть змінювати обʼєкт, на відміну від вебхуків, які викликаються контролером допуску MutatingAdmissionWebhook.

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

Якщо ви вимкнете ValidatingAdmissionWebhook, вам також слід вимкнути обʼєкт ValidatingWebhookConfiguration у групі/версії admissionregistration.k8s.io/v1 за допомогою прапорця --runtime-config.

Так. Рекомендовані контролери допуску стандартно увімкнені (дивіться тут), тому вам не потрібно явно вказувати їх. Ви можете увімкнути додаткові контролери допуску поза стандартним набором за допомогою прапорця --enable-admission-plugins (порядок не має значення).

9 - Динамічне керування допуском

Окрім вбудованих втулків допуску, втулки допуску можуть бути розроблені як розширення і виконуватися як вебхуки, налаштовані під час роботи. Ця сторінка описує, як будувати, налаштовувати, використовувати та контролювати вебхуки допуску.

Що таке вебхуки допуску?

Вебхуки допуску — це зворотні виклики HTTP, які отримують запити на допуск та роблять з ними щось. Ви можете визначити два типи вебхуків допуску, валідаційний вебхук допуску та модифікуючий вебхук допуску. Модифікуючі вебхуки допуску викликаються першими та можуть змінювати обʼєкти, які надсилаються на сервер API, щоб застосовувати власні стандартні значення. Після завершення всіх модифікацій обʼєктів і після того, як вхідний обʼєкт перевірений сервером API, викликаються валідаційні вебхуки допуску та можуть відхиляти запити для застосування власних політик.

Експерименти з вебхуками допуску

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

Передумови

  • Переконайтеся, що контролери допуску MutatingAdmissionWebhook та ValidatingAdmissionWebhook увімкнені. Тут є рекомендований набір контролерів допуску для загального увімкнення.

  • Переконайтеся, що API admissionregistration.k8s.io/v1 увімкнено.

Написання сервера вебхуків допуску

Будь ласка, зверніться до реалізації сервера вебхуків допуску, який перевірено в е2е-тесті Kubernetes. Сервер вебхуків обробляє запити AdmissionReview, надіслані серверами API, і повертає своє рішення як обʼєкт AdmissionReview в тій же версії, що й отримав.

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

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

Приклад сервера вебхуків допуску залишає поле ClientAuth порожнім, що типово дорівнює NoClientCert. Це означає, що сервер вебхуків не автентифікує ідентичність клієнтів, які, припускається, є серверами API. Якщо вам потрібен взаємний TLS або інші способи автентифікації клієнтів, дивіться як автентифікувати сервери API.

Розгортання служби вебхуків допуску

Сервер вебхуків у е2е-тесті розгортається в кластері Kubernetes за допомогою API deployment. Тест також створює службу як фронтенд сервера вебхуків. Дивіться код.

Ви також можете розгортати свої вебхуки поза кластером. Вам потрібно буде оновити відповідно ваші конфігурації вебхуків.

Налаштування вебхуків допуску на льоту

Ви можете динамічно налаштовувати, які ресурси підлягають обробки яким вебхукам допуску через ValidatingWebhookConfiguration або MutatingWebhookConfiguration.

Приклад ValidatingWebhookConfiguration, конфігурація модифікуючого вебхуку подібна. Дивіться розділ конфігурації вебхуку для деталей про кожне поле конфігурації.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: "pod-policy.example.com"
webhooks:
- name: "pod-policy.example.com"
  rules:
  - apiGroups:   [""]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    resources:   ["pods"]
    scope:       "Namespaced"
  clientConfig:
    service:
      namespace: "example-namespace"
      name: "example-service"
    caBundle: <CA_BUNDLE>
  admissionReviewVersions: ["v1"]
  sideEffects: None
  timeoutSeconds: 5

Поле scope вказує, чи тільки ресурси з області кластера ("Cluster") або з області простору імен ("Namespaced") будуть відповідати цьому правилу. "∗" означає, що обмежень області немає.

Коли сервер API отримує запит, що відповідає одному з rules, сервер API надсилає запит admissionReview до вебхуку, як зазначено в clientConfig.

Після створення конфігурації вебхуку, системі знадобиться кілька секунд, щоб визнати нову конфігурацію.

Автентифікація серверів API

Якщо вашим вебхукам для допуску потрібна автентифікація, ви можете налаштувати сервери API для використання базової автентифікації, токенів на предʼявника (bearer token) або сертифікатів для автентифікації себе у вебхуках. Налаштування складається з трьох кроків.

  • Під час запуску сервера API вкажіть розташування файлу конфігурації керування допуском за допомогою прапорця --admission-control-config-file.

  • У файлі конфігурації керування допуском вкажіть, де контролери MutatingAdmissionWebhook та ValidatingAdmissionWebhook повинні читати облікові дані. Облікові дані зберігаються у файлах kubeConfig (так, це та сама схема, що використовується kubectl), тому назва поля — kubeConfigFile. Ось приклад файлу конфігурації керування допуском:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: WebhookAdmissionConfiguration
    kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: WebhookAdmissionConfiguration
    kubeConfigFile: "<path-to-kubeconfig-file>"

# Застаріло у v1.17 на користь apiserver.config.k8s.io/v1
apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    # Застаріло у v1.17 на користь apiserver.config.k8s.io/v1, kind=WebhookAdmissionConfiguration
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
  configuration:
    # Застаріло у v1.17 на користь apiserver.config.k8s.io/v1, kind=WebhookAdmissionConfiguration
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: "<path-to-kubeconfig-file>"

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

У файлі kubeConfig вкажіть облікові дані:

apiVersion: v1
kind: Config
users:
# name повинно бути встановлено на DNS-імʼя служби або хост (включаючи порт) URL-адреси, з якою налаштовано взаємодію вебхуку.
# Якщо для служб використовується порт, відмінний від 443, його необхідно включити до імені під час налаштування серверів API версії 1.16+.
#
# Для вебхуку, налаштованого для взаємодії зі службою настандартному порті (443), вкажіть DNS-імʼя служби:
# - name: webhook1.ns1.svc
#   user: ...
#
# Для вебхуку, налаштованого для взаємодії зі службою на нестандартному порту (наприклад, 8443), вкажіть DNS-імʼя та порт служби у версії 1.16+:
# - name: webhook1.ns1.svc:8443
#   user: ...
# і, за бажанням, створіть другу секцію, використовуючи лише DNS-імʼя служби для сумісності з серверами API версії 1.15:
# - name: webhook1.ns1.svc
#   user: ...
#
# Для вебхуків, налаштованих для взаємодії з URL-адресою, вкажіть хост (і порт), вказані в URL-адресі вебхуку. Приклади:
# Вебхук з `url: https://www.example.com`:
# - name: www.example.com
#   user: ...
#
# Вебхук з `url: https://www.example.com:443`:
# - name: www.example.com:443
#   user: ...
#
# Вебхук з `url: https://www.example.com:8443`:
# - name: www.example.com:8443
#   user: ...
#
- name: 'webhook1.ns1.svc'
  user:
    client-certificate-data: "<pem encoded certificate>"
    client-key-data: "<pem encoded key>"
# Поле `name` підтримує використання * для підстановки сегментів префікса.
- name: '*.webhook-company.org'
  user:
    password: "<password>"
    username: "<name>"
# '*' є стандартним збігом.
- name: '*'
  user:
    token: "<token>"

Звичайно, вам потрібно налаштувати сервер вебхуків для обробки цих запитів на автентифікацію.

Запит і відповідь вебхука

Запит

Вебхуки надсилаються як POST-запити з Content-Type: application/json, з об’єктом API AdmissionReview в API-групі admission.k8s.io, серіалізованим у JSON як тіло запиту.

Вебхуки можуть вказувати, які версії об’єктів AdmissionReview вони приймають, використовуючи поле admissionReviewVersions у своїй конфігурації:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  admissionReviewVersions: ["v1", "v1beta1"]

admissionReviewVersions є обов’язковим полем при створенні конфігурацій вебхуків. Вебхуки повинні підтримувати принаймні одну версію AdmissionReview, яка зрозуміла поточному та попередньому API-серверу.

API-сервери надсилають першу версію AdmissionReview зі списку admissionReviewVersions, яку вони підтримують. Якщо жодна з версій у списку не підтримується API-сервером, створення конфігурації не буде дозволено. Якщо API-сервер виявляє конфігурацію вебхука, яка була створена раніше і не підтримує жодної з версій AdmissionReview, які API-сервер може надсилати, спроби виклику вебхука будуть невдалими та підлягатимуть політиці відмови.

Цей приклад показує дані, що містяться в об’єкті AdmissionReview для запиту на оновлення субресурсу scale для Deployment з групи apps/v1:

apiVersion: admission.k8s.io/v1
kind: AdmissionReview
request:
  # Випадковий uid, що унікально ідентифікує цей виклик підтвердження
  uid: 705ab4f5-6393-11e8-b7cc-42010a800002

  # Повністю кваліфікована група/версія/тип вхідного об’єкта
  kind:
    group: autoscaling
    version: v1
    kind: Scale

  # Повністю кваліфікована група/версія/тип ресурсу, що змінюється
  resource:
    group: apps
    version: v1
    resource: deployments

  # субресурс, якщо запит стосується субресурсу
  subResource: scale

  # Повністю кваліфікована група/версія/тип вхідного об’єкта в початковому запиті до API-сервера.
  # Це відрізняється від `kind`, лише якщо вебхук вказав `matchPolicy: Equivalent` і початковий запит до API-сервера був конвертований у версію, для якої зареєстровано вебхук.
  requestKind:
    group: autoscaling
    version: v1
    kind: Scale

  # Повністю кваліфікована група/версія/тип ресурсу, що змінюється в початковому запиті до API-сервера.
  # Це відрізняється від `resource`, лише якщо вебхук вказав `matchPolicy: Equivalent` і початковий запит до API-сервера був конвертований у версію, для якої зареєстровано вебхук.
  requestResource:
    group: apps
    version: v1
    resource: deployments

  # субресурс, якщо запит стосується субресурсу
  # Це відрізняється від `subResource`, лише якщо вебхук вказав `matchPolicy: Equivalent` і початковий запит до API-сервера був конвертований у версію, для якої зареєстровано вебхук.
  requestSubResource: scale

  # Ім’я ресурсу, що змінюється
  name: my-deployment

  # Простір імен ресурсу, що змінюється, якщо ресурс має простір імен (або є об’єктом Namespace)
  namespace: my-namespace

  # операція може бути CREATE, UPDATE, DELETE або CONNECT
  operation: UPDATE

  userInfo:
    # Ім’я користувача автентифікованого користувача, що робить запит до API-сервера
    username: admin

    # UID автентифікованого користувача, що робить запит до API-сервера
    uid: 014fbff9a07c

    # Групові членства автентифікованого користувача, що робить запит до API-сервера
    groups:
      - system:authenticated
      - my-admin-group
    # Додаткова довільна інформація, пов’язана з користувачем, що робить запит до API-сервера.
    # Це заповнюється шаром автентифікації API-сервера і повинно бути включено,
    # якщо будь-які перевірки SubjectAccessReview виконуються вебхуком.
    extra:
      some-key:
        - some-value1
        - some-value2

  # object є новим об’єктом, що підлягає допуску.
  # Це null для операцій DELETE.
  object:
    apiVersion: autoscaling/v1
    kind: Scale

  # oldObject є існуючим об’єктом.
  # Це null для операцій CREATE та CONNECT.
  oldObject:
    apiVersion: autoscaling/v1
    kind: Scale

  # options містить параметри операції, що підлягає допуску, як-от meta.k8s.io/v1 CreateOptions, UpdateOptions або DeleteOptions.
  # Це null для операцій CONNECT.
  options:
    apiVersion: meta.k8s.io/v1
    kind: UpdateOptions

  # dryRun вказує, що API-запит виконується в режимі dry run і не буде збережений.
  # Вебхуки з побічними ефектами повинні уникати здійснення цих побічних ефектів, коли dryRun дорівнює true.
  # Див. http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request для додаткової інформації.
  dryRun: False

Відповідь

Вебхуки відповідають зі статус-кодом HTTP 200, Content-Type: application/json та тілом, що містить об’єкт AdmissionReview (у тій же версії, що була надіслана), з заповненою секцією response, серіалізованою у JSON.

Мінімум, секція response повинна містити такі поля:

  • uid, скопійований з request.uid, що був надісланий до вебхука
  • allowed, встановлений або в true, або в false

Приклад мінімальної відповіді від вебхука для дозволу запиту:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "allowed": true
  }
}

Приклад мінімальної відповіді від вебхука для заборони запиту:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "allowed": false
  }
}

При відхиленні запиту вебхук може налаштувати HTTP-код та повідомлення, яке повертається користувачеві, використовуючи поле status. Вказаний об’єкт статусу повертається користувачеві. Див. Довідник API для деталей про тип status. Приклад відповіді для заборони запиту з налаштуванням HTTP-коду та повідомлення, яке буде представлено користувачеві:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "allowed": false,
    "status": {
      "code": 403,
      "message": "Ви не можете це зробити, тому що сьогодні вівторок і ваше ім’я починається з літери А"
    }
  }
}

При дозволі запиту, модифікуючий вебхук допуску може за бажанням модифікувати вхідний об’єкт. Це робиться за допомогою полів patch та patchType у відповіді. Єдиний підтримуваний тип patchType наразі — JSONPatch. Див. документацію JSON patch для додаткової інформації. Для patchType: JSONPatch, поле patch містить base64-кодований масив операцій JSON patch.

Як приклад, єдина операція patch, яка встановить spec.replicas, виглядає так: [{"op": "add", "path": "/spec/replicas", "value": 3}]

Base64-кодована, вона виглядатиме так: W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=

Отже, відповідь вебхука для додавання цієї мітки буде такою:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "allowed": true,
    "patchType": "JSONPatch",
    "patch": "W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0="
  }
}

Вебхуки допуску можуть за бажанням повертати попереджувальні повідомлення, які повертаються клієнту, що зробив запит, у HTTP-заголовках Warning з кодом попередження 299. Попередження можуть бути надіслані з дозволеними або відхиленими відповідями на допуск.

Якщо ви реалізуєте вебхук, що повертає попередження:

  • Не включайте префікс "Warning:" у повідомлення
  • Використовуйте попереджувальні повідомлення для опису проблем, які клієнт, що робить API-запит, має виправити або про які має знати
  • За можливості, обмежуйте попередження до 120 символів
{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "allowed": true,
    "warnings": [
      "зазначено дублюючі записи envvar з іменем MY_ENV",
      "запит на пам’ять менший за 4 МБ зазначено для контейнера mycontainer, який не запуститься успішно"
    ]
  }
}

Конфігурація вебхука

Для реєстрації вебхуків допуску створіть об’єкти API MutatingWebhookConfiguration або ValidatingWebhookConfiguration. Назва об’єкта MutatingWebhookConfiguration або ValidatingWebhookConfiguration має бути дійсним DNS-імʼям субдомену.

Кожна конфігурація може містити один або кілька вебхуків. Якщо в одній конфігурації вказано кілька вебхуків, кожен з них повинен мати унікальну назву. Це необхідно для полегшення відповідності логів аудиту та метрик активних конфігурацій.

Кожен вебхук визначає наступні параметри.

Відповідність запитів: правила

Кожен вебхук має вказувати список правил, що використовуються для визначення, чи повинен запит до API-сервера бути надісланий до вебхука. Кожне правило вказує одну або кілька операцій, груп API, версій API та ресурсів, а також область ресурсу:

  • operations отримує перелік з однієї або кількох операцій для порівняння. Можливі значення: "CREATE", "UPDATE", "DELETE", "CONNECT" або "*" для збігу зі всіма операціям.

  • apiGroups отримує перелік з однієї або кількох груп API для порівняння. "" означає ядро групи API. "*" відповідає всім групам API.

  • apiVersions отримує перелік з однієї або кількох версій API для порівняння. "*" відповідає всім версіям API.

  • resources отримує перелік з одного або кількох ресурсів для порівняння.

    • "*" відповідає всім ресурсам, але не субресурсам.
    • "*/*" відповідає всім ресурсам і субресурсам.
    • "pods/*" відповідає всім субресурсам Podʼів.
    • "*/status" відповідає всім субресурсам статусу.
  • scope вказує область для порівняння. Допустимі значення: "Cluster", "Namespaced" та "*". Субресурси відповідають області їх батьківського ресурсу. Стандартно "*".

    • "Cluster" означає, що тільки ресурси з кластерною областю відповідатимуть цьому правилу (об’єкти Namespace мають кластерну область).
    • "Namespaced" означає, що тільки ресурси з простору імен відповідатимуть цьому правилу.
    • "*" означає, що немає обмежень щодо області.

Якщо вхідний запит відповідає одному з: operations, apiGroups, apiVersions, resources та scope для будь-якого з правил вебхука, запит надсилається до вебхука.

Ось інші приклади правил, які можуть бути використані для визначення, які ресурси слід перехоплювати.

Відповідність запитам CREATE або UPDATE до apps/v1 та apps/v1beta1 deployments і replicasets:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1", "v1beta1"]
    resources: ["deployments", "replicasets"]
    scope: "Namespaced"
  ...

Відповідність запитам на створення для всіх ресурсів (але не субресурсів) у всіх групах і версіях API:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
        scope: "*"

Відповідність запитам на оновлення для всіх субресурсів status у всіх групах і версіях API:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    rules:
      - operations: ["UPDATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*/status"]
        scope: "*"

Відповідність запитів: objectSelector

Вебхуки можуть за бажанням обмежувати, які запити перехоплюються, на основі міток обʼєктів, до яких вони будуть надіслані, вказуючи objectSelector. Якщо вказано, objectSelector оцінюється як для обʼєкта, так і для старого обʼєкта, які будуть надіслані до вебхука, і вважається відповідним, якщо будь-який з обʼєктів відповідає селектору.

Обʼєкт, що дорівнює null (oldObject у випадку створення або newObject у випадку видалення), або обʼєкт, який не може мати міток (наприклад, обʼєкт DeploymentRollback або PodProxyOptions), не вважається відповідним.

Використовуйте селектор обʼєктів тільки якщо вебхук є опціональним, тому що кінцеві користувачі можуть обійти вебхук допуску, встановлюючи мітки.

Цей приклад показує модифікуючий вебхук, який відповідатиме CREATE для будь-якого ресурсу (але не субресурсів) з міткою foo: bar:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  objectSelector:
    matchLabels:
      foo: bar
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"

Див. концепцію міток для більшої кількості прикладів селекторів міток.

Відповідність запитів: namespaceSelector

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

namespaceSelector вирішує, чи слід запускати вебхук для запиту на ресурс у межах простору імен (або обʼєкт Namespace), на основі того, чи відповідають мітки простору імен селектору. Якщо обʼєкт сам є простором імен, відповідність виконується за object.metadata.labels. Якщо обʼєкт є кластерним ресурсом, відмінним від Namespace, namespaceSelector не впливає.

Цей приклад показує модифікуючий вебхук, який відповідає CREATE для будь-якого ресурсу в межах простору імен, який не має мітки "runlevel" зі значенням "0" або "1":

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    namespaceSelector:
      matchExpressions:
        - key: runlevel
          operator: NotIn
          values: ["0","1"]
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
        scope: "Namespaced"

Цей приклад показує валідаційний вебхук, який відповідає CREATE для будь-якого ресурсу в межах простору імен, асоційованого з "environment" зі значенням "prod" або "staging":

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    namespaceSelector:
      matchExpressions:
        - key: environment
          operator: In
          values: ["prod","staging"]
    rules:
      - operations: ["CREATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["*"]
        scope: "Namespaced"

Див. концепцію міток для більшої кількості прикладів селекторів міток.

Відповідність запитів: matchPolicy

API сервери можуть робити обʼєкти доступними через кілька API груп або версій.

Наприклад, якщо вебхук визначає правило для певних API груп/версій (наприклад, apiGroups:["apps"], apiVersions:["v1","v1beta1"]), і запит робиться для зміни ресурсу через іншу API групу/версію (наприклад, extensions/v1beta1), запит не буде надіслано до вебхука.

matchPolicy дозволяє вебхуку визначити, як його rules використовуються для відповідності вхідним запитам. Допустимі значення — Exact або Equivalent.

  • Exact означає, що запит повинен бути перехоплений тільки в тому випадку, якщо він точно відповідає зазначеному правилу.
  • Equivalent означає, що запит повинен бути перехоплений, якщо він модифікує ресурс, зазначений в rules, навіть через іншу API групу або версію.

У наведеному вище прикладі, вебхук, зареєстрований тільки для apps/v1, може використовувати matchPolicy:

  • matchPolicy: Exact означає, що запит extensions/v1beta1 не буде надіслано до вебхука.
  • matchPolicy: Equivalent означає, що запит extensions/v1beta1 буде надіслано до вебхука (з обʼєктами, конвертованими до версії, яку вебхук визначив: apps/v1).

Вказування Equivalent рекомендовано і забезпечує, що вебхуки продовжують перехоплювати очікувані ресурси, коли оновлення включають нові версії ресурсу в API сервері.

Коли ресурс перестає обслуговуватись API сервером, він більше не вважається еквівалентним іншим версіям цього ресурсу, які все ще обслуговуються. Наприклад, extensions/v1beta1 розгортання були спочатку визнані застарілими, а потім видалені (у Kubernetes v1.16).

З моменту цього видалення, вебхук з правилом apiGroups:["extensions"], apiVersions:["v1beta1"], resources:["deployments"] не перехоплює розгортання, створені через API apps/v1. З цієї причини, вебхуки повинні віддавати перевагу реєстрації для стабільних версій ресурсів.

Цей приклад показує валідаційний вебхук, який перехоплює модифікації розгортань (незалежно від API групи або версії) і завжди отримує обʼєкт Deployment версії apps/v1:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  matchPolicy: Equivalent
  rules:
  - operations: ["CREATE","UPDATE","DELETE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments"]
    scope: "Namespaced"

matchPolicy для вебхуків допуску за замовчуванням дорівнює Equivalent.

Відповідність запитів: matchConditions

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

Ви можете визначити умови відповідності для вебхуків, якщо вам потрібна точніша фільтрація запитів. Ці умови корисні, якщо правила відповідності, objectSelectors та namespaceSelectors не надають необхідної фільтрації при викликах по HTTP. Умови відповідності є CEL виразами. Всі умови відповідності повинні оцінюватися як true для виклику вебхука.

Ось приклад, що ілюструє декілька різних способів використання умов відповідності:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    matchPolicy: Equivalent
    rules:
      - operations: ['CREATE','UPDATE']
        apiGroups: ['*']
        apiVersions: ['*']
        resources: ['*']
    failurePolicy: 'Ignore' # Fail-open (опційно)
    sideEffects: None
    clientConfig:
      service:
        namespace: my-namespace
        name: my-webhook
      caBundle: '<omitted>'
    # Ви можете мати до 64 умов відповідності на вебхук
    matchConditions:
      - name: 'exclude-leases' # Кожна умова відповідності повинна мати унікальне імʼя
        expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # Відповідність ресурсам, які не є lease.
      - name: 'exclude-kubelet-requests'
        expression: '!("system:nodes" in request.userInfo.groups)' # Відповідність запитам, зробленим не користувачами вузлів.
      - name: 'rbac' # Пропуск запитів RBAC, які обробляються другим вебхуком.
        expression: 'request.resource.group != "rbac.authorization.k8s.io"'
  
  # Цей приклад ілюструє використання 'authorizer'. Перевірка авторизації дорожча за простий вираз, тому в цьому прикладі вона обмежена лише запитами RBAC, використовуючи другий вебхук. Обидва вебхуки можуть обслуговуватись одним кінцевим пунктом.
  - name: rbac.my-webhook.example.com
    matchPolicy: Equivalent
    rules:
      - operations: ['CREATE','UPDATE']
        apiGroups: ['rbac.authorization.k8s.io']
        apiVersions: ['*']
        resources: ['*']
    failurePolicy: 'Fail' # Fail-closed (стандартно)
    sideEffects: None
    clientConfig:
      service:
        namespace: my-namespace
        name: my-webhook
      caBundle: '<omitted>'
    # Ви можете мати до 64 умов відповідності на вебхук
    matchConditions:
      - name: 'breakglass'
        # Пропуск запитів, зроблених користувачами, авторизованими для 'breakglass' на цьому вебхуку.
        # Дієслово API 'breakglass' не повинно існувати за межами цієї перевірки.
        expression: '!authorizer.group("admissionregistration.k8s.io").resource("validatingwebhookconfigurations").name("my-webhook.example.com").check("breakglass").allowed()'

Умови відповідності мають доступ до наступних CEL змінних:

  • object — Обʼєкт з вхідного запиту. Значення є null для DELETE запитів. Версія обʼєкта може бути конвертована на основі matchPolicy.
  • oldObject — Наявний обʼєкт. Значення є null для CREATE запитів.
  • request — Частина запиту AdmissionReview, виключаючи object та oldObject.
  • authorizer — CEL Authorizer. Може використовуватись для виконання перевірок авторизації для головного облікового запису (автентифікованого користувача) запиту. Дивіться Authz у документації бібліотеки Kubernetes CEL для додаткової інформації.
  • authorizer.requestResource — Скорочення для перевірки авторизації, налаштованої на ресурс запиту (група, ресурс, (субресурс), простір імен, імʼя).

Для отримання додаткової інформації про CEL вирази, зверніться до довідника Загальна мова виразів у Kubernetes.

У разі помилки оцінки умови відповідності вебхук ніколи не викликається. Чи відхилити запит визначається наступним чином:

  1. Якщо будь-яка умова відповідності оцінилася як false (незалежно від інших помилок), API сервер пропускає вебхук.
  2. Інакше:
    • для failurePolicy: Fail, запит відхиляється (без виклику вебхука).
    • для failurePolicy: Ignore, продовжити запит, але пропустити вебхук.

Звернення до вебхука

Коли сервер API визначає, що запит повинен бути надісланий до вебхука, йому потрібно знати, як звʼязатися з вебхуком. Це визначається в розділі clientConfig конфігурації вебхука.

Вебхуки можуть викликатися через URL або посилання на сервіс, і можуть за бажанням включати власний набір CA для використання для перевірки TLS-зʼєднання.

URL

Поле url вказує місцезнаходження вебхука у стандартній формі URL (scheme://host:port/path).

Поле host не повинно вказувати на сервіс, що працює в кластері; використовуйте посилання на сервіс, вказуючи поле service. Хост може бути вирішений через зовнішній DNS на деяких серверах API (наприклад, kube-apiserver не може вирішувати DNS всередині кластера, оскільки це буде порушенням шарів). host також може бути IP-адресою.

Зверніть увагу, що використання localhost або 127.0.0.1 як host є ризикованим, якщо ви не забезпечите запуск цього вебхука на всіх хостах, які запускають сервер API, який може потребувати викликів до цього вебхука. Такі інсталяції, ймовірно, будуть непереносними або не будуть легко запускатися в новому кластері.

Схема повинна бути "https"; URL повинен починатися з "https://".

Спроба використання користувача або базової автентифікації (наприклад, user:password@) не дозволена. Фрагменти (#...) та параметри запиту (?...) також не дозволені.

Ось приклад конфігурації модифікуючого вебхука для виклику URL (і очікується, що TLS-сертифікат буде перевірено за допомогою системних коренів довіри, тому не вказується caBundle):

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  clientConfig:
    url: "https://my-webhook.example.com:9443/my-webhook-path"

Посилання на сервіс

Розділ service всередині clientConfig є посиланням на сервіс для цього вебхука. Якщо вебхук працює всередині кластера, вам слід використовувати service замість url. Необхідно вказати простір імен та імʼя сервісу. Порт є необовʼязковим і стандартно дорівнює 443. Шлях є необовʼязковим і стандартно дорівнює "/".

Ось приклад конфігурації модифікуючого вебхука для виклику сервісу на порту "1234" за підшляхом "/my-path", і для перевірки TLS-зʼєднання проти ServerName my-service-name.my-service-namespace.svc з використанням власного набору CA:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  clientConfig:
    caBundle: <CA_BUNDLE>
    service:
      namespace: my-service-namespace
      name: my-service-name
      path: /my-path
      port: 1234

Побічні ефекти

Вебхуки зазвичай працюють тільки з вмістом AdmissionReview, надісланого їм. Однак деякі вебхуки здійснюють зміни поза межами під час обробки запитів на допуск.

Вебхуки, які здійснюють зміни поза межами ("побічні ефекти"), повинні також мати механізм узгодження (наприклад, контролер), який періодично визначає фактичний стан системи та коригує дані поза межами, змінені вебхуком допуску, щоб відобразити реальність. Це тому, що виклик до вебхука допуску не гарантує, що прийнятий обʼєкт буде збережений таким, яким він є, або взагалі. Пізніші вебхуки можуть змінити вміст обʼєкта, може виникнути конфлікт під час запису в сховище, або сервер може вимкнутися до збереження обʼєкта.

Крім того, вебхуки з побічними ефектами повинні пропускати ці побічні ефекти, коли обробляються запити допуску з dryRun: true. Вебхук повинен явно вказати, що він не матиме побічних ефектів при запуску з dryRun, інакше запит dry-run не буде надіслано до вебхука, і API-запит замість цього не вдасться.

Вебхуки вказують, чи мають вони побічні ефекти, за допомогою поля sideEffects у конфігурації вебхука:

  • None: виклик вебхука не матиме побічних ефектів.
  • NoneOnDryRun: виклик вебхука може мати побічні ефекти, але якщо до вебхука надіслано запит з dryRun: true, вебхук придушить побічні ефекти (вебхук враховує dryRun).

Ось приклад конфігурації вебхука перевірки, який вказує, що він не має побічних ефектів при запитах з dryRun: true:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    sideEffects: NoneOnDryRun

Тайм-аути

Оскільки вебхуки додають затримку до запитів API, вони повинні оцінюватися якомога швидше. timeoutSeconds дозволяє налаштувати, скільки часу сервер API повинен чекати на відповідь вебхука, перш ніж розглядати виклик як невдалий.

Якщо тайм-аут закінчується до того, як вебхук відповість, виклик вебхука буде проігноровано або API-запит буде відхилено відповідно до політики помилок.

Значення тайм-ауту повинно бути між 1 і 30 секунд.

Ось приклад конфігурації вебхука перевірки з кастомним тайм-аутом у 2 секунди:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
  - name: my-webhook.example.com
    timeoutSeconds: 2

Тайм-аут для вебхука допуску стандартно становить 10 секунд.

Політика повторного виклику

Одне впорядкування втулків модифікуючого допуску (включаючи вебхуки) не підходить для всіх випадків (див. приклад у https://issue.k8s.io/64333). Модифікуючий вебхук може додати нову підструктуру до обʼєкта (наприклад, додати container до pod), а інші модифікуючи втулки, які вже виконані, можуть мати вплив на ці нові структури (наприклад, встановити imagePullPolicy для всіх контейнерів).

Щоб дозволити втулкам модифікуючого допуску спостерігати за змінами, внесеними іншими втулками, вбудовані модифікуючи втулки допуску повторно запускаються, якщо модифікуючий вебхук змінює обʼєкт, і модифікуючи вебхуки можуть вказати reinvocationPolicy для контролю того, чи будуть вони повторно викликані.

reinvocationPolicy може бути встановлено на Never або IfNeeded. Стандартно встановлено у Never.

  • Never: вебхук не повинен викликатися більше одного разу в рамках однієї оцінки допуску.
  • IfNeeded: вебхук може бути викликаний знову в рамках оцінки допуску, якщо обʼєкт, що авторизується, змінюється іншими втулками допуску після початкового виклику вебхука.

Важливі моменти, на які слід звернути увагу:

  • Кількість додаткових викликів не гарантується як точно одна.
  • Якщо додаткові виклики призводять до подальших змін обʼєкта, не гарантується, що вебхуки будуть викликані знов.
  • Вебхуки, які використовують цей параметр, можуть бути переупорядковані для мінімізації кількості додаткових викликів.
  • Щоб перевірити обʼєкт після того, як всі модифікації гарантовано завершені, використовуйте вебхук допуску (рекомендується для вебхуків з побічними ефектами).

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

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  reinvocationPolicy: IfNeeded

Модифікуючи вебхуки повинні бути ідемпотентними, здатними успішно обробити обʼєкт, який вони вже авторизували і потенційно змінили. Це стосується всіх модифікуючих вебхуків допуску, оскільки будь-яка зміна, яку вони можуть внести в обʼєкт, вже могла існувати в обʼєкті, наданому користувачем, але це є особливо важливим для вебхуків, які вибирають повторний виклик.

Політика обробки помилок

failurePolicy визначає, як обробляються невизнані помилки та помилки тайм-ауту від вебхука допуску. Допустимі значення: Ignore або Fail.

  • Ignore означає, що помилка при виклику вебхука ігнорується, і запит API дозволяється продовжити.
  • Fail означає, що помилка при виклику вебхука призводить до невдачі допуску та відхилення запиту API.

Ось приклад конфігурації модифікуючого вебхука, налаштованого на відхилення запиту API, якщо виникають помилки під час виклику вебхука допуску:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
  failurePolicy: Fail

Стандартна політика обробки помилок failurePolicy для вебхуків допуску Fail.

Моніторинг вебхуків авторизації

Сервер API надає способи моніторингу поведінки вебхуків допуску. Ці механізми моніторингу допомагають адміністраторам кластерів відповісти на запитання, як:

  1. Який модифікуючий вебхук змінив обʼєкт у запиті API?

  2. Яку зміну модифікуючий вебхук застосував до обʼєкта?

  3. Які вебхуки часто відхиляють запити API? Яка причина відхилення?

Анотації аудиту модифікуючих вебхуків

Іноді корисно знати, який модифікуючий вебхук змінив обʼєкт у запиті API, і яку зміну вебхук застосував.

Сервер API Kubernetes виконує аудит кожного виклику модифікуючого вебхука. Кожен виклик генерує анотацію аудиту, яка відображає, чи був обʼєкт запиту змінений викликом, і, за необхідності, генерує анотацію із застосованим патчем з відповіді вебхука допуску. Анотації встановлюються в подію аудиту для даного запиту на даній стадії його виконання, яка потім попередньо обробляється відповідно до певної політики та записується в бекенд.

Рівень аудиту події визначає, які анотації будуть записані:

  • На рівні аудиту Metadata або вище записується анотація з ключем mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx} з JSON-наватаженням, яке вказує, що вебхук був викликаний для даного запиту і чи змінив він обʼєкт чи ні.

    Наприклад, наступна анотація записується для вебхука, який повторно викликається. Вебхук є третім у ланцюгу модифікуючих вебхуків і не змінив обʼєкт запиту під час виклику.

    # записана подія аудиту
    {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "annotations": {
            "mutation.webhook.admission.k8s.io/round_1_index_2": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook.example.com\",\"mutated\": false}"
            # інші анотації
            ...
        }
        # інші поля
        ...
    }
    
    # десеріалізоване значення анотації
    {
        "configuration": "my-mutating-webhook-configuration.example.com",
        "webhook": "my-webhook.example.com",
        "mutated": false
    }
    

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

    # записана подія аудиту
    {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "annotations": {
            "mutation.webhook.admission.k8s.io/round_0_index_0": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"mutated\": true}"
            # інші анотації
            ...
        }
        # інші поля
        ...
    }
    
    # десеріалізоване значення анотації
    {
        "configuration": "my-mutating-webhook-configuration.example.com",
        "webhook": "my-webhook-always-mutate.example.com",
        "mutated": true
    }
    
  • На рівні аудиту Request або вище записується анотація з ключем patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx} з JSON-навантаженням, яке вказує, що вебхук був викликаний для даного запиту і який патч був застосований до обʼєкта запиту.

    Наприклад, наступна анотація записується для вебхука, який повторно викликається. Вебхук є четвертим у ланцюгу модифікуючих вебхуків і відповів JSON-патчем, який був застосований до обʼєкта запиту.

    # записана подія аудиту
    {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "annotations": {
            "patch.webhook.admission.k8s.io/round_1_index_3": "{\"configuration\":\"my-other-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"patch\":[{\"op\":\"add\",\"path\":\"/data/mutation-stage\",\"value\":\"yes\"}],\"patchType\":\"JSONPatch\"}"
            # інші анотації
            ...
        }
        # інші поля
        ...
    }
    
    # десеріалізоване значення анотації
    {
        "configuration": "my-other-mutating-webhook-configuration.example.com",
        "webhook": "my-webhook-always-mutate.example.com",
        "patchType": "JSONPatch",
        "patch": [
            {
                "op": "add",
                "path": "/data/mutation-stage",
                "value": "yes"
            }
        ]
    }
    

Метрики вебхуків допуску

Сервер API надає метрики Prometheus з точки доступу /metrics, які можна використовувати для моніторингу та діагностики стану сервера API. Наведені нижче метрики фіксують стан, повʼязаний з вебхуками допуску.

Лічильник відхилення запитів вебхука допуску сервера API

Іноді корисно знати, які вебхуки допуску часто відхиляють запити API, та причину відхилення.

Сервер API надає метрику лічильника Prometheus, яка фіксує відхилення вебхуків допуску. Метрики мають підписи, що ідентифікують причини відхилення запитів вебхуками:

  • name: назва вебхука, який відхилив запит.

  • operation: тип операції запиту, може бути одним із CREATE, UPDATE, DELETE та CONNECT.

  • type: тип вебхука допуску, може бути одним із admit та validating.

  • error_type: визначає, чи сталася помилка під час виклику вебхука, яка призвела до відхилення. Його значення може бути одним із:

    • calling_webhook_error: невизнані помилки або помилки тайм-ауту від вебхука допуску сталися, і політика помилки вебхука встановлена на Fail.
    • no_error: помилка не сталася. Вебхук відхилив запит з allowed: false у відповіді допуску. Підписи метрики rejection_code записують значення .status.code, встановлене в відповіді допуску.
    • apiserver_internal_error: сталася внутрішня помилка сервера API.
  • rejection_code: HTTP-код статусу, встановлений у відповіді допуску, коли вебхук відхилив запит.

Приклад метрик лічильника відхилення:

# HELP apiserver_admission_webhook_rejection_count [ALPHA] Лічильник відхилення вебхуків авторизації, ідентифікований за назвою та розділений для кожного типу авторизації (валідація чи допуск) та операції. Додаткові підписи вказують тип помилки (calling_webhook_error або apiserver_internal_error, якщо виникла помилка; no_error інакше) та, за потреби, ненульовий код відхилення, якщо вебхук відхилив запит із HTTP-кодом статусу (врахований сервером API, якщо код більший або рівний 400). Коди, більші за 600, обрізаються до 600, щоб обмежити кардинальність метрик.
# TYPE apiserver_admission_webhook_rejection_count counter
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="always-timeout-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalid-admission-response-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="no_error",name="deny-unwanted-configmap-data.example.com",operation="CREATE",rejection_code="400",type="validating"} 13

Найкращі практики та попередження

Ідемпотентність

Ідемпотентний вебхук допуску, що змінює дані, може успішно обробляти обʼєкт, який він вже допустив і, можливо, змінив. Допуск можна застосовувати кілька разів, не змінюючи результат поза початковою обробкою.

Приклади ідемпотентних модифікуючих вебхуків допуску:

  1. Для запиту CREATE Pod задайте поле .spec.securityContext.runAsNonRoot в значення true, щоб застосувати найкращі практики безпеки.

  2. Для запиту запиту CREATE Pod стандартно встановлюються обмеження ресурсів, якщо поле .spec.containers[].resources.limits контейнера не встановлено.

  3. Для запиту запиту CREATE Pod додається додатковий контейнер з імʼям foo-sidecar, якщо контейнер із імʼям foo-sidecar ще не існує.

У випадках, наведених вище, вебхук може безпечно повторно викликатися або допускати обʼєкт, у якого вже встановлені ці поля.

Приклади неідемпотентних модифікуючих вебхуків допуску:

  1. Для запиту CREATE Pod додається sidecar контейнер з імʼям foo-sidecar, суфіксований поточним часовим позначенням (наприклад, foo-sidecar-19700101-000000).

  2. Для CREATE/UPDATE Pod відхиляються запити, якщо позначка "env" установлена, в іншому випадку додається позначка "env": "prod".

  3. Для запиту CREATE Pod додається контейнер під назвою foo-sidecar без перевірки наявності контейнера foo-sidecar у специфікації Pod.

У першому випадку вебхук може додавати однаковий контейнер декілька разів до Pod, кожного разу з різним іменем контейнера. Аналогічно, вебхук може додавати дубльовані контейнери, якщо вони вже існують у Podʼах користувачів.

У другому випадку повторний виклик вебхука призведе до помилки вихідних даних вебхука.

У третьому випадку повторний виклик вебхука призведе до дублювання контейнерів у специфікації Pod, що робить запит недійсним та відхиленим сервером API.

Перехоплення всіх версій обʼєкта

Рекомендується, щоб вебхуки допуску завжди перехоплювали всі версії обʼєкта, встановлюючи .webhooks[].matchPolicy на Equivalent. Також рекомендується, щоб вебхуки допуску завжди реєструвалися для стабільних версій ресурсів. Невдала спроба перехопити всі версії обʼєкта може призвести до того, що політика допуску не буде застосовуватися до деяких версій запитів. Дивіться Відповідність запитів: matchPolicy для прикладів.

Доступність

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

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

Гарантування остаточного стану обʼєкта

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

Наприклад, модифікуючий вебхук допуску налаштований на впровадження контейнера sidecar з імʼям "foo-sidecar" на кожний запит CREATE pod. Якщо необхідно, щоб контейнер був присутнім, також слід налаштувати вебхук допуску для перехоплення запитів CREATE pod, і перевірити, що контейнер з імʼям "foo-sidecar" з необхідною конфігурацією існує в обʼєкті, який має бути створений.

Уникнення блокування вебхуків у самостійно розміщених вебхуках

Вебхук, що працює всередині кластера, може призвести до блокування власного deployment, якщо він налаштований для перехоплення ресурсів, необхідних для запуску власних Podʼів.

Наприклад, модифікуючий вебхук допуску налаштований на допуск запитів на створення CREATE pod лише в тому випадку, якщо у pod встановлено певну мітку (наприклад, "env": "prod"). Сервер розгортання вебхука працює в deployment, яке не встановлює мітку "env". Коли вузол, на якому запущено Podʼи вебхука, стає непрацездатним, deployment вебхука спробує перепланувати Podʼи на інший вузол. Однак запити будуть відхилені існуючим сервером вебхука, оскільки мітка "env" не встановлена, і міграція не може відбутися.

Рекомендується виключати простір імен, де працює ваш вебхук, за допомогою namespaceSelector.

Побічні ефекти

Рекомендується, щоб вебхуки допуску уникати побічних ефектів, якщо це можливо, що означає, що вебхуки працюють тільки з вмістом AdmissionReview, надісланим до них, і не вносять зміни поза цими рамками. Поле .webhooks[].sideEffects має бути встановлене на None, якщо у вебхука немає жодних побічних ефектів.

Якщо побічні ефекти потрібні під час оцінки допуску, їх потрібно приглушити при обробці обʼєкта AdmissionReview з dryRun встановленим на true, і поле .webhooks[].sideEffects має бути встановлене на NoneOnDryRun. Дивіться Побічні ефекти для докладнішої інформації.

Уникнення операцій у просторі імен kube-system

Простір імен kube-system містить обʼєкти, створені компонентами панелі управління Kubernetes, наприклад, службові облікові записи для компонентів панелі управління, Podʼи такі як kube-dns. Ненавмисна зміна або відхилення запитів у просторі імен kube-system може призвести до того, що компоненти панелі управління перестануть працювати або будуть внесені невідомі зміни. Якщо ваші вебхуки допуску не мають на меті змінювати поведінку панелі управління Kubernetes, виключіть простір імен kube-system з перехоплення за допомогою namespaceSelector.

10 - Управління службовими обліковими записами

Службовий обліковий запис (ServiceAccount) надає ідентифікацію для процесів, що виконуються в Pod.

Процес всередині Pod може використовувати ідентифікацію свого повʼязаного службового облікового запису для автентифікації у API сервері кластера.

Для ознайомлення зі службовими обліковими записами, прочитайте про конфігурування службових облікових записів.

Цей посібник пояснює деякі концепції, повʼязані зі ServiceAccount. Також у посібнику розглядається, як отримати або відкликати токени, що представляють ServiceAccounts.

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

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

Щоб точно виконати ці кроки, переконайтеся, що у вас є простір імен під назвою examplens. Якщо ні, створіть його, виконавши команду:

kubectl create namespace examplens

Облікові записи користувачів та службові облікові записи

Kubernetes розрізняє поняття облікового запису користувача та службового облікового запису з кількох причин:

  • Облікові записи користувачів призначені для людей. Службові облікові записи призначені для процесів застосунків, які (для Kubernetes) виконуються в контейнерах, що є частиною Pod.
  • Облікові записи користувачів мають бути глобальними: імена повинні бути унікальними у всіх просторах імен кластера. Незалежно від того, який простір імен ви розглядаєте, певний обліковий запис користувача представляє того самого користувача. У Kubernetes службові облікові записи є привʼязаними до простору імен: два різні простори імен можуть містити ServiceAccountʼи з однаковими іменами.
  • Як правило, облікові записи користувачів кластера можуть синхронізуватися з корпоративною базою даних, де створення нового облікового запису користувача вимагає спеціальних привілеїв і повʼязане зі складними бізнес-процесами. Навпаки, створення службових облікових записів повинно бути легшим, дозволяючи користувачам кластера створювати службові облікові записи для конкретних завдань за запитом. Відділення створення ServiceAccount від кроків для реєстрації користувачів полегшує дотримання принципу найменших привілеїв для робочих навантажень.
  • Вимоги до аудиту для облікових записів користувачів (людей) та службових облікових записів можуть відрізнятися; розділення полегшує досягнення цих вимог.
  • Конфігураційний пакет для складної системи може містити визначення різних службових облікових записів для компонентів цієї системи. Оскільки службові облікові записи можуть створюватися без багатьох обмежень і мають імена, привʼязані до простору імен, така конфігурація зазвичай є переносимою.

Привʼязані токени службових облікових записів

Токени ServiceAccount можуть бути привʼязані до API обʼєктів, що існують у kube-apiserver. Їх можна використовувати для звʼязування дійсності токена з існуванням іншого API обʼєкта. Підтримувані типи обʼєктів наступні:

  • Pod (використовується для projected томів, див. нижче)
  • Secret (може використовуватися для відкликання токена шляхом видалення Secret)
  • Node (у версії v1.30 створення нових токенів, привʼязаних до вузлів, є альфа-функцією, використання наявних токенів, привʼязаних до вузлів, є бета-функцією)

Коли токен привʼязаний до обʼєкта, metadata.name і metadata.uid цього обʼєкта зберігаються як додаткові "приватні заявки" у виданому JWT.

Коли привʼязаний токен представляється kube-apiserver, автентифікатор службового облікового запису витягне і перевірить ці заявки. Якщо вказаний обʼєкт або ServiceAccount знаходяться в процесі видалення (наприклад, через завершувач), то протягом будь-якого моменту, через 60 секунд (або більше) після дати .metadata.deletionTimestamp, автентифікація з використанням цього токена буде неуспішною. Якщо обʼєкт, на який він посилається, більше не існує (або його metadata.uid не збігається), запит не буде автентифікований.

Додаткові метадані в токенах, повʼязаних з Pod

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

Коли токен службового облікового запису привʼязаний до обʼєкта Pod, додаткові метадані також вбудовуються в токен, що вказує на значення поля spec.nodeName повʼязаного Pod і uid цього вузла, якщо це можливо.

Ця інформація про вузол не перевіряється kube-apiserver, коли токен використовується для автентифікації. Вона включена, щоб інтегратори не повинні були отримувати обʼєкти API Pod або Node для перевірки повʼязаного імені вузла та uid при інспекції JWT.

Перевірка та інспекція приватних заявок

API TokenReview може використовуватися для перевірки та вилучення приватних заявок з токена:

  1. Спочатку припустимо, що у вас є Pod з назвою test-pod і службовий обліковий запис з назвою my-sa.

  2. Створіть токен, привʼязаний до цього Pod:

    kubectl create token my-sa --bound-object-kind="Pod" --bound-object-name="test-pod"
    
  3. Скопіюйте цей токен у новий файл з назвою tokenreview.yaml:

    apiVersion: authentication.k8s.io/v1
    kind: TokenReview
    spec:
      token: <токен з кроку 2>
    
  4. Надішліть цей ресурс до apiserver для перевірки:

    kubectl create -o yaml -f tokenreview.yaml # ми використовуємо '-o yaml', щоб можна було перевірити вихідні дані
    

Ви повинні побачити вихідні дані, подібні до наведених нижче:

apiVersion: authentication.k8s.io/v1
kind: TokenReview
metadata:
  creationTimestamp: null
spec:
  token: <token>
status:
  audiences:
  - https://kubernetes.default.svc.cluster.local
  authenticated: true
  user:
    extra:
      authentication.kubernetes.io/credential-id:
      - JTI=7ee52be0-9045-4653-aa5e-0da57b8dccdc
      authentication.kubernetes.io/node-name:
      - kind-control-plane
      authentication.kubernetes.io/node-uid:
      - 497e9d9a-47aa-4930-b0f6-9f2fb574c8c6
      authentication.kubernetes.io/pod-name:
      - test-pod
      authentication.kubernetes.io/pod-uid:
      - e87dbbd6-3d7e-45db-aafb-72b24627dff5
    groups:
    - system:serviceaccounts
    - system:serviceaccounts:default
    - system:authenticated
    uid: f8b4161b-2e2b-11e9-86b7-2afc33b31a7e
    username: system:serviceaccount:default:my-sa

Механізм томів привʼязаного токена службового облікового запису

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

Стандартно, панель управління Kubernetes (зокрема, ServiceAccount admission controller) додає projected том до Pod, і цей том містить токен для доступу до API Kubernetes.

Ось приклад, як це виглядає для запущеного Pod:

...
  - name: kube-api-access-<random-suffix>
    projected:
      sources:
        - serviceAccountToken:
            path: token # має збігатися з шляхом, який очікує застосунок
        - configMap:
            items:
              - key: ca.crt
                path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace

Цей фрагмент маніфесту визначає projected том, що складається з трьох джерел. У цьому випадку, кожне джерело також представляє один шлях у цьому томі. Три джерела такі:

  1. Джерело serviceAccountToken, яке містить токен, який kubelet отримує з kube-apiserver. Kubelet отримує токени з обмеженим терміном дії за допомогою API TokenRequest. Токен, наданий для TokenRequest, закінчується або при видаленні Pod, або після визначеного терміну дії (стандартно, це 1 година). Kubelet також оновлює цей токен перед тим, як термін його дії закінчиться. Токен привʼязаний до конкретного Pod і має kube-apiserver як свою аудиторію. Цей механізм замінив попередній механізм, який додавав том на основі Secret, де Secret представляв ServiceAccount для Pod, але не мав терміну дії.
  2. Джерело configMap. ConfigMap містить набір даних центру сертифікації. Pod можуть використовувати ці сертифікати, щоб упевнитись, що вони підключаються до kube-apiserver вашого кластера (а не до проміжного блоку або випадково неправильно налаштованого колеги).
  3. Джерело downwardAPI, яке шукає імʼя простору імен, що містить Pod, і надає цю інформацію про імʼя для коду застосунку, що виконується всередині Pod.

Будь-який контейнер у Pod, який монтує цей том, може отримати доступ до вищевказаної інформації.

Ручне управління Secret для ServiceAccounts

Версії Kubernetes до v1.22 автоматично створювали облікові дані для доступу до API Kubernetes. Цей старіший механізм був заснований на створенні токенів Secret, які потім могли бути змонтовані в запущені Podʼи.

У новіших версіях, включаючи Kubernetes v1.31, API облікові дані отримуються безпосередньо за допомогою TokenRequest, і монтуються в Podʼи за допомогою projected тому. Токени, отримані за допомогою цього методу, мають обмежений термін дії та автоматично анулюються, коли Pod, в який вони змонтовані, видаляється.

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

Після того, як ви вручну створите Secret і звʼяжете його зі службовим обліковим записом, панель управління Kubernetes автоматично заповнює токен у цьому Secret.

Автоматичне очищення застарілих токенів ServiceAccount

До версії 1.24 Kubernetes автоматично генерував токени на основі Secret для ServiceAccount. Щоб розрізнити автоматично згенеровані токени та створені вручну, Kubernetes перевіряє посилання з поля секретів ServiceAccount. Якщо Secret згадується в полі secrets, він вважається автоматично згенерованим застарілим токеном. В іншому випадку він вважається вручну створеним застарілим токеном. Наприклад:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
  namespace: default
secrets:
  - name: build-robot-secret # зазвичай НЕ присутній для вручну створеного токена

Починаючи з версії 1.29, застарілі токени службових облікових записів, які були згенеровані автоматично, будуть позначені як недійсні, якщо вони залишатимуться невикористаними протягом певного періоду часу (стандартно один рік). Токени, які продовжують залишатися невикористаними протягом цього визначеного періоду (знову ж таки, стандартно один рік), будуть згодом видалені панеллю управління.

Якщо користувачі використовують анульований автоматично згенерований токен, валідатор токенів:

  1. додасть анотацію аудиту для пари ключ-значення authentication.k8s.io/legacy-token-invalidated: <secret name>/<namespace>,
  2. збільшить лічильник метрики invalid_legacy_auto_token_uses_total,
  3. оновить мітку Secret kubernetes.io/legacy-token-last-used з новою датою,
  4. поверне помилку, вказуючи, що токен був анульований.

При отриманні цієї помилки валідації користувачі можуть оновити Secret, щоб видалити мітку kubernetes.io/legacy-token-invalid-since, щоб тимчасово дозволити використання цього токена.

Ось приклад автоматично згенерованого застарілого токена, який був позначений мітками kubernetes.io/legacy-token-last-used і kubernetes.io/legacy-token-invalid-since:

apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  namespace: default
  labels:
    kubernetes.io/legacy-token-last-used: 2022-10-24
    kubernetes.io/legacy-token-invalid-since: 2023-10-25
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token

Деталі панелі управління

Контролер ServiceAccount

Контролер ServiceAccount керує ServiceAccount всередині просторів імен та забезпечує наявність ServiceAccount з іменем "default" у кожному активному просторі імен.

Контролер токенів

Контролер токенів службових облікових записів працює як частина kube-controller-manager. Цей контролер діє асинхронно. Він:

  • відстежує видалення ServiceAccount та видаляє всі відповідні Secretʼи токенів ServiceAccount.
  • відстежує додавання Secretʼу токенів ServiceAccount та забезпечує наявність відповідного ServiceAccount, додає токен до Secretʼу за потреби.
  • відстежує видалення Secretʼу та видаляє посилання з відповідного ServiceAccount за потреби.

Необхідно передати файл приватного ключа службового облікового запису контролеру токенів у kube-controller-manager, використовуючи прапорець --service-account-private-key-file. Приватний ключ використовується для підпису згенерованих токенів службових облікових записів. Аналогічно, необхідно передати відповідний публічний ключ у kube-apiserver, використовуючи прапорець --service-account-key-file. Публічний ключ буде використовуватися для перевірки токенів під час автентифікації.

Контролер допуску ServiceAccount

Зміна Podʼів здійснюється через втулок, що викликається Контролером допуску. Він є частиною сервера API. Цей контролер допуску діє синхронно для зміни Podʼів під час їх створення. Коли цей втулок активний (а він є стандартно активним у більшості дистрибутивів), то під час створення Pod він виконує наступні дії:

  1. Якщо у Pod не встановлено значення .spec.serviceAccountName, контролер допуску встановлює імʼя ServiceAccount default для цього Pod.
  2. Контролер допуску забезпечує наявність ServiceAccount, на який посилається Pod. Якщо не існує ServiceAccount з відповідним імʼям, контролер допуску відхиляє Pod. Ця перевірка застосовується навіть для default ServiceAccount.
  3. Якщо поле automountServiceAccountToken у ServiceAccount або в Podʼі не встановлено в false:
    • контролер допуску змінює Pod, додаючи додатковий том, що містить токен для доступу до API.
    • контролер допуску додає volumeMount до кожного контейнера в Podʼі, пропускаючи контейнери, які вже мають визначений шлях для монтування тому /var/run/secrets/kubernetes.io/serviceaccount. Для Linux-контейнерів цей том монтується за адресою /var/run/secrets/kubernetes.io/serviceaccount; на Windows-вузлах монтування знаходиться ну еквівалентному шляху.
  4. Якщо в специфікації Pod не містяться жодні imagePullSecrets, контролер допуску додає imagePullSecrets, копіюючи їх з ServiceAccount.

Контролер відстеження токенів застарілих ServiceAccount

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

Цей контролер створює ConfigMap з назвою kube-system/kube-apiserver-legacy-service-account-token-tracking у просторі імен kube-system. ConfigMap фіксує мітку часу, коли система почала відстежувати застарілі токени службових облікових записів.

Очищувач токенів застарілих ServiceAccount

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

Очищувач токенів застарілих ServiceAccount працює як частина kube-controller-manager і перевіряє кожні 24 години, чи не використовувався будь-який автоматично згенерований застарілий токен службового облікового запису протягом визначеного часу. Якщо так, очищувач позначає ці токени як недійсні.

Очищувач працює, спершу перевіряючи ConfigMap, створений панеллю управління (за умови, що LegacyServiceAccountTokenTracking увімкнено). Якщо поточний час перевищує визначений час після дати в ConfigMap, очищувач переглядає список Secretʼів у кластері та оцінює кожен Secret, що має тип kubernetes.io/service-account-token.

Якщо Secret відповідає всім наступним умовам, очищувач позначає його як недійсний:

  • Secret створено автоматично, що означає що він двонаправлено згадується ServiceAccount.
  • Secret не змонтовано жодним Podʼом.
  • Secret не використовувався протягом визначеного часу з моменту створення або останнього використання.

Очищувач позначає Secret як недійсний, додаючи мітку kubernetes.io/legacy-token-invalid-since до Secret, з поточною датою. Якщо недійсний Secret не використовується протягом визначеного часу, очищувач видаляє його.

API TokenRequest

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

Ви використовуєте субресур TokenRequest з ServiceAccount, щоб отримати токен з обмеженим часом дії для цього ServiceAccount. Вам не потрібно викликати його для отримання API-токена для використання в контейнері, оскільки kubelet налаштовує це для вас, використовуючи projected том.

Якщо ви хочете використовувати API TokenRequest через kubectl, див. Ручне створення API-токена для ServiceAccount.

Панель управління Kubernetes (зокрема, контролер допуску ServiceAccount) додає projected том до Podʼів, а kubelet забезпечує, що цей том містить токен, який дозволяє контейнерам автентифікуватися як відповідний ServiceAccount.

(Цей механізм замінив попередній механізм, який додавав том на основі Secret, де Secret представляв ServiceAccount для Pod, але не мав терміну дії.)

Ось приклад того, як це виглядає для запущеного Pod:

...
  - name: kube-api-access-<random-suffix>
    projected:
      defaultMode: 420 # десятичний еквівалент вісімкового 0644
      sources:
        - serviceAccountToken:
            expirationSeconds: 3607
            path: token
        - configMap:
            items:
              - key: ca.crt
                path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace

Цей фрагмент маніфесту визначає projected том, який обʼєднує інформацію з трьох джерел:

  1. Джерело serviceAccountToken, що містить токен, який kubelet отримує від kube-apiserver. Kubelet отримує токени з обмеженим часом дії, використовуючи API TokenRequest. Токен, виданий для TokenRequest, спливає або коли Pod видаляється, або через визначений термін життя (стандартно — 1 година). Токен привʼязаний до конкретного Podʼа та має kube-apiserver як свою аудиторію.
  2. Джерело configMap. ConfigMap містить пакет даних сертифікаційного центру. Podʼи можуть використовувати ці сертифікати, щоб переконатися, що вони підключаються до kube-apiserver вашого кластера (а не до проміжного блоку або випадково неправильно налаштованого колеги).
  3. Джерело downwardAPI. Цей том downwardAPI робить імʼя простору імен, що містить Pod, доступним для коду програми, що працює всередині Podʼа.

Будь-який контейнер у Podʼі, що монтує цей том, може отримати доступ до вищезазначеної інформації.

Створення додаткових API токенів

Для створення постійного API токена для ServiceAccount, створіть Secret типу kubernetes.io/service-account-token з анотацією, що посилається на ServiceAccount. Панель управління потім генерує довгостроковий токен і оновлює цей Secret з даними згенерованого токена.

Ось приклад маніфесту для такого Secret:

apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: mysecretname
  annotations:
    kubernetes.io/service-account.name: myserviceaccount

Для створення Secret на основі цього прикладу, виконайте:

kubectl -n examplens create -f https://k8s.io/examples/secret/serviceaccount/mysecretname.yaml

Для перегляду деталей цього Secret, виконайте:

kubectl -n examplens describe secret mysecretname

Результат буде подібним до:

Name:           mysecretname
Namespace:      examplens
Labels:         <none>
Annotations:    kubernetes.io/service-account.name=myserviceaccount
                kubernetes.io/service-account.uid=8a85c4c4-8483-11e9-bc42-526af7764f64

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1362 bytes
namespace:      9 bytes
token:          ...

Якщо ви запустите новий Pod у просторі імен examplens, він може використовувати Secret токену службового облікового запису myserviceaccount, який ви щойно створили.

Видалення/деактивація токена ServiceAccount

Якщо ви знаєте назву Secret, що містить токен, який ви хочете видалити:

kubectl delete secret name-of-secret

Інакше спочатку знайдіть Secret для ServiceAccount.

# Це передбачає, що у вас вже є простір імен 'examplens'
kubectl -n examplens get serviceaccount/example-automated-thing -o yaml

Результат буде подібним до:

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}}      
  creationTimestamp: "2019-07-21T07:07:07Z"
  name: example-automated-thing
  namespace: examplens
  resourceVersion: "777"
  selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing
  uid: f23fd170-66f2-4697-b049-e1e266b7f835
secrets:
  - name: example-automated-thing-token-zyxwv

Потім видаліть Secret, назву якого ви тепер знаєте:

kubectl -n examplens delete secret/example-automated-thing-token-zyxwv

Прибирання

Якщо ви створили простір імен examplens для експериментів, ви можете його видалити:

kubectl delete namespace examplens

Що далі

11 - Сертифікати та запити на їх підписування

API для сертифікатів та наборів довіри Kubernetes дозволяють автоматизувати створення облікових даних X.509, надаючи програмний інтерфейс для клієнтів API Kubernetes для запиту та отримання X.509 сертифікатів від Центру сертифікації (CA).

Також є експериментальна (альфа) підтримка розподілу наборів довіри.

Запити на підписання сертифікатів

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

Ресурс CertificateSigningRequest (CSR) використовується для запиту підписання сертифіката від вказаного підписувача, після чого запит може бути схвалений або відхилений перед остаточним підписанням.

Процес підписання запиту

Ресурс типу CertificateSigningRequest дозволяє клієнту запросити видачу сертифіката X.509 на основі запиту на підписання. Обʼєкт CertificateSigningRequest містить PEM-кодований запит на підпис у форматі PKCS#10 у полі spec.request. CertificateSigningRequest вказує підписувача (одержувача, до якого робиться запит) за допомогою поля spec.signerName. Зверніть увагу, що після версії API certificates.k8s.io/v1 ключ spec.signerName є обовʼязковим. У Kubernetes v1.22 та пізніших версіях клієнти можуть за бажанням встановити поле spec.expirationSeconds, щоб запросити певний термін дії виданого сертифіката. Мінімальне допустиме значення для цього поля — 600, тобто десять хвилин.

Після створення CertificateSigningRequest його необхідно схвалити перед підписанням. Залежно від обраного підписувача, CertificateSigningRequest може бути автоматично схвалений контролером. В іншому випадку CertificateSigningRequest слід схвалити вручну через API REST (або client-go) або за допомогою команди kubectl certificate approve. Аналогічно CertificateSigningRequest також може бути відхилений, що повідомляє налаштованому підписувачу, що він не повинен підписати запит.

Для схвалених сертифікатів наступним кроком є підписання. Відповідний контролер підпису перевіряє, чи виконуються умови підписання, а потім створює сертифікат. Після цього контролер підпису оновлює CertificateSigningRequest, зберігаючи новий сертифікат у полі status.certificate наявного обʼєкта CertificateSigningRequest. Поле status.certificate CertificateSigningRequest може бути порожнім або містити сертифікат X.509, кодований у форматі PEM. Поле status.certificate CertificateSigningRequest залишається порожнім, доки підписувач не зробить це.

Після заповнення поля status.certificate запит вважається завершеним, і клієнти тепер можуть отримати PEM-дані підписаного сертифіката з ресурсу CertificateSigningRequest. Підписувачі також можуть відхилити підпис сертифіката, якщо умови схвалення не виконані.

Для зменшення кількості застарілих ресурсів CertificateSigningRequest в кластері періодично запускається контролер збору сміття. Він видаляє CertificateSigningRequests, які не змінювали стан протягом певного періоду:

  • Схвалені запити: автоматично видаляються після 1 години
  • Відхилені запити: автоматично видаляються після 1 години
  • Невдалі запити: автоматично видаляються після 1 години
  • Запити в очікуванні: автоматично видаляються після 24 годин
  • Усі запити: автоматично видаляються після того, як видача сертифіката закінчиться після спливання часу дії

Авторизація підпису сертифікатів

Для можливості створення запиту на підпис сертифіката та отримання будь-якого запиту на підпис сертифіката:

  • Дієслова: create, get, list, watch, група: certificates.k8s.io, ресурс: certificatesigningrequests

Наприклад:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-creator
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - create
  - get
  - list
  - watch

Для можливості схвалення запиту на підпис сертифіката:

  • Дієслова: get, list, watch, група: certificates.k8s.io, ресурс: certificatesigningrequests
  • Дієслова: update, група: certificates.k8s.io, ресурс: certificatesigningrequests/approval
  • Дієслова: approve, група: certificates.k8s.io, ресурс: signers, resourceName: <signerNameDomain>/<signerNamePath> або <signerNameDomain>/*

Наприклад:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-approver
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/approval
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - signers
  resourceNames:
  - example.com/my-signer-name # example.com/* може використовуватись для авторизації всіх підписувачів в домені 'example.com'
  verbs:
  - approve

Для можливості підписання запиту на підпис сертифіката:

  • Дієслова: get, list, watch, група: certificates.k8s.io, ресурс: certificatesigningrequests
  • Дієслова: update, група: certificates.k8s.io, ресурс: certificatesigningrequests/status
  • Дієслова: sign, група: certificates.k8s.io, ресурс: signers, resourceName: <signerNameDomain>/<signerNamePath> або <signerNameDomain>/*
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-signer
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/status
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - signers
  resourceNames:
  - example.com/my-signer-name # example.com/* може використовуватись для авторизації всіх підписувачів в домені 'example.com'
  verbs:
  - sign

Підписувачі

Підписувачі абстрактно представляють сутність або сутності, які можуть підписувати або вже підписали сертифікат.

Будь-який підписувач, який доступний за межами конкретного кластера, повинен надавати інформацію про те, як працює підписувач, щоб споживачі могли зрозуміти, що це означає для CertificateSigningRequests та (якщо це увімкнено) ClusterTrustBundles. Це охоплює:

  1. Розподіл довіри: як розподіляються якорі довіри (CA-сертифікати або набори сертифікатів).
  2. Дозволені субʼєкти: будь-які обмеження та поведінка, коли запитано недопустимий субʼєкт.
  3. Дозволені розширення x509: включаючи IP subjectAltNames, DNS subjectAltNames, Email subjectAltNames, URI subjectAltNames тощо, та поведінка, коли запитано недопустиме розширення.
  4. Дозволені використання ключів / розширені використання ключів: будь-які обмеження та поведінка, коли використання, відмінне від використання, визначеного підписувачем, вказане в CSR.
  5. Термін дії / термін життя сертифіката: чи він фіксується підписувачем, настроюваний адміністратором, визначений полем spec.expirationSeconds CSR тощо, та поведінка, коли термін дії, визначений підписувачем, відрізняється від поля spec.expirationSeconds CSR.
  6. Дозволені / заборонені прапорці CA: та поведінка, якщо CSR містить запит на отримання сертифіката CA, коли підписувач не пропускає його.

Зазвичай поле status.certificate обʼєкта CertificateSigningRequest містить один PEM-кодований сертифікат X.509, як тільки CSR схвалено, і сертифікат видається. Деякі підписувачі зберігають кілька сертифікатів у полі status.certificate. У цьому випадку документація для підписувача повинна вказувати значення додаткових сертифікатів; наприклад, це може бути сертифікат плюс проміжні сертифікати, які представляються під час рукостискання TLS.

Якщо ви хочете зробити якір довіри (кореневий сертифікат) доступним, це слід зробити окремо від CertificateSigningRequest та його поля status.certificate. Наприклад, ви можете використовувати ClusterTrustBundle.

Формат підпису PKCS#10 не має стандартного механізму для вказання терміну дії або терміну життя сертифіката. Термін дії або термін життя має бути встановлено через поле spec.expirationSeconds обʼєкта CSR. Вбудовані підписувачі використовують параметр конфігурації ClusterSigningDuration, який стандартно становить 1 рік, (прапорець командного рядка --cluster-signing-duration kube-controller-manager) в як стандартне значення, коли не вказано spec.expirationSeconds. Коли вказано spec.expirationSeconds, використовується мінімум з spec.expirationSeconds та ClusterSigningDuration.

Підписувачі Kubernetes

Kubernetes надає вбудовані підписувачі для підпису сертифікатів, кожен з яких має широко відоме імʼя підписувача signerName:

  1. kubernetes.io/kube-apiserver-client: підписує сертифікати, які мають вважатись сертифікатами клієнтів сервером API. Ніколи автоматично не затверджуються kube-controller-manager.

    1. Розподіл довіри: підписані сертифікати мають вважатись клієнтськими сертифікатами для доступу до API-сервера. Набір ЦС не поширюється жодним іншим способом.
    2. Дозволені субʼєкти: немає обмежень для субʼєктів, однак затверджувачі та підписувачі можуть відхилити запити на затвердження та підпис. Певні субʼєкти подібні до користувачів та груп на рівні кластера є різними поміж різними дистрибутивами, що вимагає додаткових перевірок перед затвердженням та підписуванням. Втулок допуску CertificateSubjectRestrictions є стандартно увімкненим для обмеження system:masters, але в кластері є не тільки субʼєкти рівня адміністраторів кластера.
    3. Дозволені розширення x509: враховують subjectAltNames та використання ключів, відкидаючи інші розширення.
    4. Використання дозволених ключів: мають включати ["client auth"]. Не мають містити використання ключів поза ["digital signature", "key encipherment", "client auth"].
    5. Термін дії / термін життя сертифіката: для реалізації підписувача kube-controller-manager, встановлюється у мінімальне значення з --cluster-signing-duration або, якщо вказано, поля spec.expirationSeconds обʼєкта CSR.
    6. Біт ЦС дозволено / заборонено: не дозволяється.
  2. kubernetes.io/kube-apiserver-client-kubelet: підписує сертифікати, які мають вважатись сертифікатами клієнтів сервером API. Можуть бути автоматично затверджені kube-controller-manager.

    1. Розподіл довіри: підписані сертифікати мають вважатись клієнтськими сертифікатами для доступу до API-сервера. Набір ЦС не поширюється жодним іншим способом.
    2. Дозволені субʼєкти: організації є безумовно ["system:nodes"], загальні імена — "system:node:${NODE_NAME}".
    3. Дозволені розширення x509: враховують розширення з використанням ключів, забороняють розширення subjectAltNames та відкидає інші розширення.
    4. Дозволені використання ключів: ["key encipherment", "digital signature", "client auth"] або ["digital signature", "client auth"].
    5. Термін дії / термін життя сертифіката: для реалізації підписувача kube-controller-manager, встановлюється у мінімальне значення з --cluster-signing-duration або, якщо вказано, поля spec.expirationSeconds обʼєкта CSR.
    6. Біт ЦС дозволено / заборонено: не дозволяється.
  3. kubernetes.io/kubelet-serving: підписує сертифікати, які мають вважатись сертифікатами, які обслуговуються kubelet, але не мають жодних гарантій. Ніколи автоматично не затверджуються kube-controller-manager.

    1. Розподіл довіри: підписані сертифікати мають вважатись API сервером дійсними для обробки зʼєднань з kubelet. Набір ЦС не поширюється жодним іншим способом.
    2. Дозволені субʼєкти: організації є безумовно ["system:nodes"], загальні імена  — "system:node:${NODE_NAME}".
    3. Дозволені розширення x509: враховують використання ключів та розширень DNSName/IPAddress subjectAltName extensions, забороняють розширення EmailAddress та URI subjectAltName, відкидають інші розширення. Принаймні один субʼєкт DNS чи IP повинен бути у subjectAltNames.
    4. Дозволені використання ключів: ["key encipherment", "digital signature", "server auth"] або ["digital signature", "server auth"].
    5. Термін дії / термін життя сертифіката: для реалізації підписувача kube-controller-manager, встановлюється у мінімальне значення з --cluster-signing-duration або, якщо вказано, поля spec.expirationSeconds обʼєкта CSR.
    6. Біт ЦС дозволено / заборонено: не дозволяється.
  4. kubernetes.io/legacy-unknown: не має гарантій довіри взагалі. Деякі сторонні дистрибутиви Kubernetes можуть використовувати сертифікати клієнтів, підписані ним. Стабільний API CertificateSigningRequest (версії certificates.k8s.io/v1 та пізніше) не дозволяють встановлювати signerName на kubernetes.io/legacy-unknown. Ніколи автоматично не затверджується kube-controller-manager.

    1. Розподіл довіри: Немає. Для цього підписувача не існує стандартної довіри або розподілу в кластері Kubernetes.
    2. Дозволені субʼєкти: будь-які
    3. Дозволені розширення x509: враховуються subjectAltNames та використання ключів, відкидаються інші розширення.
    4. Дозволені використання ключів: будь-які
    5. Термін дії / термін життя сертифіката: для реалізації підписувача kube-controller-manager, встановлюється у мінімальне значення з --cluster-signing-duration або, якщо вказано, поля spec.expirationSeconds обʼєкта CSR.
    6. Біт ЦС дозволено / заборонено: не дозволяється.

kube-controller-manager реалізує підписування панелю управління для кожного з вбудованих підписувачів. Збої для всіх цих операцій повідомляються лише в логах kube-controller-manager.

Розподіл довіри відбувається поза рамками для цих підписувачів. Будь-яка довіра за межами описаного вище є строго випадковою. Наприклад, деякі дистрибутиви можуть приймати kubernetes.io/legacy-unknown як клієнтські сертифікати для kube-apiserver, але це не є стандартом. Жодне з цих використань не повʼязане з токенами секретів ServiceAccount .data[ca.crt]. Цей пакет CA гарантовано лише для верифікації зʼєднання з API-сервером за допомогою стандартного Service (kubernetes.default.svc).

Власні підписувачі

Ви можете ввести власних підписувачів, які матимуть схожі імена з префіксами, але такі, що вказують на ваш власний домен. Наприклад, якщо ви є представником проєкту з відкритими сирцями, який використовує доменне імʼя open-fictional.example, тоді ви можете використовувати issuer.open-fictional.example/service-mesh як імʼя підписувача.

Власний підписувач використовує API Kubernetes для випуску сертифікатів. Дивіться підписувачі на основі API для деталей.

Підписування

Підписування панеллю управління

Панель управління Kubernetes реалізує кожного з підписувачів Kubernetes як частину kube-controller-manager.

Підписувачі на основі API

Користувачі REST API можуть підписувати CSRs, надсилаючи запит UPDATE до субресурсу status CSR, який потрібно підписати.

У рамках цього запиту поле status.certificate повинно бути встановлено, щоб містити підписаний сертифікат. Це поле містить один або більше сертифікатів, закодованих у форматі PEM.

Всі PEM блоки повинні мати мітку "CERTIFICATE", не містити заголовків, а закодовані дані повинні бути структурою сертифіката BER, закодованого в ASN.1, як описано в розділі 4 RFC5280.

Приклад вмісту сертифіката:

-----BEGIN CERTIFICATE-----
MIIDgjCCAmqgAwIBAgIUC1N1EJ4Qnsd322BhDPRwmg3b/oAwDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCeHgxCjAIBgNVBAgMAXgxCjAIBgNVBAcMAXgxCjAIBgNV
BAoMAXgxCjAIBgNVBAsMAXgxCzAJBgNVBAMMAmNhMRAwDgYJKoZIhvcNAQkBFgF4
MB4XDTIwMDcwNjIyMDcwMFoXDTI1MDcwNTIyMDcwMFowNzEVMBMGA1UEChMMc3lz
dGVtOm5vZGVzMR4wHAYDVQQDExVzeXN0ZW06bm9kZToxMjcuMC4wLjEwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDne5X2eQ1JcLZkKvhzCR4Hxl9+ZmU3
+e1zfOywLdoQxrPi+o4hVsUH3q0y52BMa7u1yehHDRSaq9u62cmi5ekgXhXHzGmm
kmW5n0itRECv3SFsSm2DSghRKf0mm6iTYHWDHzUXKdm9lPPWoSOxoR5oqOsm3JEh
Q7Et13wrvTJqBMJo1GTwQuF+HYOku0NF/DLqbZIcpI08yQKyrBgYz2uO51/oNp8a
sTCsV4OUfyHhx2BBLUo4g4SptHFySTBwlpRWBnSjZPOhmN74JcpTLB4J5f4iEeA7
2QytZfADckG4wVkhH3C2EJUmRtFIBVirwDn39GXkSGlnvnMgF3uLZ6zNAgMBAAGj
YTBfMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMB
Af8EAjAAMB0GA1UdDgQWBBTREl2hW54lkQBDeVCcd2f2VSlB1DALBgNVHREEBDAC
ggAwDQYJKoZIhvcNAQELBQADggEBABpZjuIKTq8pCaX8dMEGPWtAykgLsTcD2jYr
L0/TCrqmuaaliUa42jQTt2OVsVP/L8ofFunj/KjpQU0bvKJPLMRKtmxbhXuQCQi1
qCRkp8o93mHvEz3mTUN+D1cfQ2fpsBENLnpS0F4G/JyY2Vrh19/X8+mImMEK5eOy
o0BMby7byUj98WmcUvNCiXbC6F45QTmkwEhMqWns0JZQY+/XeDhEcg+lJvz9Eyo2
aGgPsye1o3DpyXnyfJWAWMhOz7cikS5X2adesbgI86PhEHBXPIJ1v13ZdfCExmdd
M1fLPhLyR54fGaY+7/X8P9AZzPefAkwizeXwe9ii6/a08vWoiE4=
-----END CERTIFICATE-----

Не-PEM вміст може зʼявлятися до або після блоків CERTIFICATE PEM і не перевіряється, щоб дозволити пояснювальний текст, як описано в розділі 5.2 RFC7468.

При кодуванні в JSON або YAML це поле закодоване в base-64. Запит на підпис сертифіката (CertificateSigningRequest), що містить приклад сертифіката вище, виглядатиме так:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  certificate: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS..."

Схвалення або відхилення

Перед тим, як підписувач видасть сертифікат на основі запиту на підписання сертифіката (CertificateSigningRequest), підписувач зазвичай перевіряє, що видача для цього CSR була схвалена.

Автоматичне схвалення панелі управління

kube-controller-manager поставляється з вбудованим схвалювачем для сертифікатів з іменем підписувача kubernetes.io/kube-apiserver-client-kubelet, який делегує різні дозволи на CSRs для облікових даних вузлів до авторизації. kube-controller-manager надсилає ресурси SubjectAccessReview до API-сервера для перевірки авторизації на схвалення сертифіката.

Схвалення або відхилення за допомогою kubectl

Адміністратор Kubernetes (з відповідними дозволами) може вручну схвалювати (або відхиляти) запити на підписання сертифікатів (CertificateSigningRequests) за допомогою команд kubectl certificate approve та kubectl certificate deny.

Щоб схвалити CSR за допомогою kubectl:

kubectl certificate approve <certificate-signing-request-name>

Аналогічно, щоб відхилити CSR:

kubectl certificate deny <certificate-signing-request-name>

Схвалення або відхилення за допомогою API Kubernetes

Користувачі REST API можуть схвалювати CSRs, надсилаючи запит UPDATE до субресурсу approval CSR, який потрібно схвалити. Наприклад, ви можете написати оператор, який слідкує за певним видом CSR, а потім надсилає UPDATE для їх схвалення.

Коли ви робите запит на схвалення або відхилення, встановіть або умову статусу Approved, або Denied залежно від визначеного стану:

Для схвалених CSR:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  conditions:
  - lastUpdateTime: "2020-02-08T11:37:35Z"
    lastTransitionTime: "2020-02-08T11:37:35Z"
    message: Approved by my custom approver controller
    reason: ApprovedByMyPolicy # Ви можете вказати тут будь-який рядок
    type: Approved

Для відхилених CSR:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  conditions:
  - lastUpdateTime: "2020-02-08T11:37:35Z"
    lastTransitionTime: "2020-02-08T11:37:35Z"
    message: Denied by my custom approver controller
    reason: DeniedByMyPolicy # Ви можете вказати тут будь-який рядок
    type: Denied

Зазвичай встановлюється в поле status.conditions.reason код причини, зручний для машинного зчитування, використовуючи TitleCase; це є умовністю, але ви можете встановити тут будь-яке значення. Якщо ви хочете додати примітку для читання людьми, використовуйте поле status.conditions.message.

Пакети довіри кластера

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

ClusterTrustBundles — це обʼєкт масштабу кластера для розподілу якорів довіри X.509 (кореневих сертифікатів) до робочих навантажень у кластері. Вони розроблені для гарної роботи з концепцією підписувача із запитів на підписання сертифікатів (CertificateSigningRequests).

ClusterTrustBundles можна використовувати у двох режимах: звʼязаний з підписувачем та незвʼязаний з підписувачем.

Загальні властивості та валідація

Усі обʼєкти ClusterTrustBundle мають сувору валідацію вмісту їхнього поля trustBundle. Це поле повинно містити один або більше сертифікатів X.509, серіалізованих у DER, кожен з яких обгорнутий у блок PEM CERTIFICATE. Сертифікати повинні аналізуватися як дійсні сертифікати X.509.

Езотеричні функції PEM, такі як дані між блоками та заголовки всередині блоків, або відхиляються під час валідації обʼєкта, або можуть ігноруватися споживачами обʼєкта. Крім того, споживачі можуть перевпорядковувати сертифікати в пакеті за своїм власним довільним, але стабільним порядком.

Обʼєкти ClusterTrustBundle слід вважати загальнодоступними в межах кластера. Якщо ваш кластер використовує авторизацію RBAC, усі ServiceAccounts стандартно мають дозволи get, list та watch для всіх обʼєктів ClusterTrustBundle. Якщо ви використовуєте власний механізм авторизації та ввімкнули ClusterTrustBundles у своєму кластері, вам слід налаштувати еквівалентне правило для того, щоб ці обʼєкти були загальнодоступними в межах кластера, щоб вони працювали належним чином.

Якщо ви не маєте стандартного дозволу для отримання переліку пакетів довіри кластера у вашому кластері, ви можете діяти від імені службового облікового запису, до якого у вас є доступ, щоб побачити доступні ClusterTrustBundles:

kubectl get clustertrustbundles --as='system:serviceaccount:mynamespace:default'

ClusterTrustBundles, звʼязані з підписувачем

ClusterTrustBundles, звʼязані з підписувачем, асоціюються з імʼям підписувача, як тут:

apiVersion: certificates.k8s.io/v1alpha1
kind: ClusterTrustBundle
metadata:
  name: example.com:mysigner:foo
spec:
  signerName: example.com/mysigner
  trustBundle: "<... PEM data ...>"

ClusterTrustBundles призначені для підтримки контролера, специфічного для підписувача в кластері, тому вони мають кілька функцій безпеки:

  • Щоб створити або оновити ClusterTrustBundle, звʼязаний з підписувачем, ви повинні мати дозвіл підтвердити підписувача (спеціальне дієслово авторизації attest, група API certificates.k8s.io; шлях ресурсу signers). Ви можете налаштувати авторизацію для конкретного імені ресурсу <signerNameDomain>/<signerNamePath> або відповідати шаблону, наприклад <signerNameDomain>/*.
  • ClusterTrustBundles, звʼязані з підписувачем, повинні бути названі з префіксом, отриманим з їхнього поля spec.signerName. Слеші (/) замінюються на двокрапки (:), а в кінці додається двокрапка. За цим слідує довільне імʼя. Наприклад, підписувач example.com/mysigner може бути звʼязаний з ClusterTrustBundle example.com:mysigner:<arbitrary-name>.

ClusterTrustBundles, звʼязані з підписувачем, зазвичай використовуються у робочих навантаженнях за допомогою комбінації селектора полів за іменем підписувача та окремого селектора міток.

ClusterTrustBundles, незвʼязані з підписувачем

ClusterTrustBundles, незвʼязані з підписувачем, мають порожнє поле spec.signerName, як це:

apiVersion: certificates.k8s.io/v1alpha1
kind: ClusterTrustBundle
metadata:
  name: foo
spec:
  # signerName не вказано, тому поле порожнє
  trustBundle: "<... PEM data ...>"

Вони призначені головним чином для випадків використання конфігурації кластера. Кожен ClusterTrustBundle, незвʼязаний з підписувачем, є незалежним обʼєктом, на відміну від звичайної групової поведінки ClusterTrustBundles, звʼязаних з підписувачем.

ClusterTrustBundles, незвʼязані з підписувачем, не мають вимоги щодо дієслова attest. Натомість, ви контролюєте доступ до них безпосередньо за допомогою звичайних механізмів, таких як контроль доступу на основі ролей.

Щоб відрізнити їх від ClusterTrustBundles, звʼязаних з підписувачем, назви ClusterTrustBundles, незвʼязаних з підписувачем, не повинні містити двокрапку (:).

Доступ до ClusterTrustBundles з Podʼів

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

Вміст ClusterTrustBundles може бути впроваджений у файлову систему контейнера, подібно до ConfigMaps та Secrets. Дивіться джерело projected томів clusterTrustBundle для отримання додаткової інформації.

Як видати сертифікат для користувача

Для того, щоб звичайний користувач міг автентифікуватися та викликати API, потрібно виконати кілька кроків. Спершу цей користувач повинен мати сертифікат, виданий кластером Kubernetes, а потім надати цей сертифікат API Kubernetes.

Створення приватного ключа

Наступні скрипти показують, як згенерувати приватний ключ PKI та CSR. Важливо встановити значення CN та O атрибута CSR. CN — це імʼя користувача, а O — це група, до якої належатиме цей користувач. Ви можете звернутися до RBAC по стандартні групи.

openssl genrsa -out myuser.key 2048
openssl req -new -key myuser.key -out myuser.csr -subj "/CN=myuser"

Створення запиту на підписання сертифікату

Створіть запит на підписання сертифікату (CertificateSigningRequest) і подайте його до кластера Kubernetes через kubectl. Нижче наведено скрипт для CertificateSigningRequest.

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: myuser
spec:
  request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZVzVuWld4aE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTByczhJTHRHdTYxakx2dHhWTTJSVlRWMDNHWlJTWWw0dWluVWo4RElaWjBOCnR2MUZtRVFSd3VoaUZsOFEzcWl0Qm0wMUFSMkNJVXBGd2ZzSjZ4MXF3ckJzVkhZbGlBNVhwRVpZM3ExcGswSDQKM3Z3aGJlK1o2MVNrVHF5SVBYUUwrTWM5T1Nsbm0xb0R2N0NtSkZNMUlMRVI3QTVGZnZKOEdFRjJ6dHBoaUlFMwpub1dtdHNZb3JuT2wzc2lHQ2ZGZzR4Zmd4eW8ybmlneFNVekl1bXNnVm9PM2ttT0x1RVF6cXpkakJ3TFJXbWlECklmMXBMWnoyalVnald4UkhCM1gyWnVVV1d1T09PZnpXM01LaE8ybHEvZi9DdS8wYk83c0x0MCt3U2ZMSU91TFcKcW90blZtRmxMMytqTy82WDNDKzBERHk5aUtwbXJjVDBnWGZLemE1dHJRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBR05WdmVIOGR4ZzNvK21VeVRkbmFjVmQ1N24zSkExdnZEU1JWREkyQTZ1eXN3ZFp1L1BVCkkwZXpZWFV0RVNnSk1IRmQycVVNMjNuNVJsSXJ3R0xuUXFISUh5VStWWHhsdnZsRnpNOVpEWllSTmU3QlJvYXgKQVlEdUI5STZXT3FYbkFvczFqRmxNUG5NbFpqdU5kSGxpT1BjTU1oNndLaTZzZFhpVStHYTJ2RUVLY01jSVUyRgpvU2djUWdMYTk0aEpacGk3ZnNMdm1OQUxoT045UHdNMGM1dVJVejV4T0dGMUtCbWRSeEgvbUNOS2JKYjFRQm1HCkkwYitEUEdaTktXTU0xMzhIQXdoV0tkNjVoVHdYOWl4V3ZHMkh4TG1WQzg0L1BHT0tWQW9FNkpsYWFHdTlQVmkKdjlOSjVaZlZrcXdCd0hKbzZXdk9xVlA3SVFjZmg3d0drWm89Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400  # один день
  usages:
  - client auth
EOF

Декілька моментів, на які варто звернути увагу:

  • usages має бути 'client auth'.
  • expirationSeconds можна зробити довшим (наприклад, 864000 для десяти днів) або коротшим (наприклад, 3600 для однієї години).
  • request — це base64-кодоване значення вмісту файлу CSR. Ви можете отримати цей вміст за допомогою такої команди:
cat myuser.csr | base64 | tr -d "\n"

Схвалення CertificateSigningRequest

Використовуйте kubectl, щоб створити CSR та схвалити його.

Отримайте список CSR:

kubectl get csr

Схваліть CSR:

kubectl certificate approve myuser

Отримання сертифіката

Отримайте сертифікат з CSR:

kubectl get csr/myuser -o yaml

Значення сертифіката знаходиться в форматі Base64-кодування в status.certificate.

Експортуйте виданий сертифікат з CertificateSigningRequest.

kubectl get csr myuser -o jsonpath='{.status.certificate}'| base64 -d > myuser.crt

Створення Role та RoleBinding

Зі створеним сертифікатом, час визначити Role та RoleBinding для цього користувача для доступу до ресурсів кластера Kubernetes.

Ось приклад команди для створення Role для цього нового користувача:

kubectl create role developer --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=pods

Ось приклад команди для створення RoleBinding для цього нового користувача:

kubectl create rolebinding developer-binding-myuser --role=developer --user=myuser

Додавання до kubeconfig

Останній крок — додати цього користувача до файлу kubeconfig.

Спершу, вам потрібно додати нові облікові дані:

kubectl config set-credentials myuser --client-key=myuser.key --client-certificate=myuser.crt --embed-certs=true

Потім, вам потрібно додати контекст:

kubectl config set-context myuser --cluster=kubernetes --user=myuser

Для перевірки, змініть контекст на myuser:

kubectl config use-context myuser

Що далі

12 - Зіставлення PodSecurityPolicies зі стандартами безпеки Podʼів

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

Для кожного параметра, до якого це застосовується, перераховані допустимі значення для Baseline та Restricted профілів. Все, що перебуває за межами допустимих значень для цих профілів, підпадає під Privileged профіль. "Немає думки" означає, що всі значення допустимі для всіх стандартів безпеки Podʼів.

Для покрокового керівництва міграції див. Міграція з PodSecurityPolicy до вбудованого контролера допуску PodSecurity.

Специфікація PodSecurityPolicy

Поля, перераховані в цій таблиці, є частиною PodSecurityPolicySpec, яка вказана в шляху поля .spec.

Зіставлення політики безпеки PodSecurityPolicySpec зі стандартами безпеки Podʼів
PodSecurityPolicySpecТипЕквівалент стандартів безпеки Podʼів
privilegedПеревіркаBaseline & Restricted: false / undefined / nil
defaultAddCapabilitiesЗміна & ПеревіркаВимоги відповідають allowedCapabilities нижче.
allowedCapabilitiesПеревірка

Baseline: підмножина

  • AUDIT_WRITE
  • CHOWN
  • DAC_OVERRIDE
  • FOWNER
  • FSETID
  • KILL
  • MKNOD
  • NET_BIND_SERVICE
  • SETFCAP
  • SETGID
  • SETPCAP
  • SETUID
  • SYS_CHROOT

Обмежений: пустий / undefined / nil АБО список, що містить тільки NET_BIND_SERVICE

requiredDropCapabilitiesЗміна & Перевірка

Baseline: немає думки

Baseline: повинен містити ALL

volumesПеревірка

Baseline: будь-що крім

  • hostPath
  • *

Restricted: підмножина

  • configMap
  • csi
  • downwardAPI
  • emptyDir
  • ephemeral
  • persistentVolumeClaim
  • projected
  • secret
hostNetworkПеревіркаBaseline & Restricted: false / undefined / nil
hostPortsПеревіркаBaseline & Restricted: undefined / nil / пустий
hostPIDПеревіркаBaseline & Restricted: false / undefined / nil
hostIPCПеревіркаBaseline & Restricted: false / undefined / nil
seLinuxЗміна & Перевірка

Baseline & Restricted: seLinux.rule is MustRunAs, з наступними options

  • user не встановлено ("" / undefined / nil)
  • role не встановлено ("" / undefined / nil)
  • type не встановлено або один із: container_t, container_init_t, container_kvm_t, container_engine_t
  • level є будь-чим
runAsUserЗміна & Перевірка

Baseline: Будь-що

Restricted: rule є MustRunAsNonRoot

runAsGroupЗміна (MustRunAs) & ПеревіркаНемає думки
supplementalGroupsЗміна & ПеревіркаНемає думки
fsGroupЗміна & ПеревіркаНемає думки
readOnlyRootFilesystemЗміна & ПеревіркаНемає думки
defaultAllowPrivilegeEscalationЗмінаНемає думки
allowPrivilegeEscalationЗміна & Перевірка

Лише зміна, якщо встановлено false

Baseline: Немає думки

Restricted: false

allowedHostPathsПеревіркаНемає думки (пріоритет мають volumes)
allowedFlexVolumesПеревіркаНемає думки (пріоритет мають volumes)
allowedCSIDriversПеревіркаНемає думки (пріоритет мають volumes)
allowedUnsafeSysctlsПеревіркаBaseline & Restricted: undefined / nil / empty
forbiddenSysctlsПеревіркаНемає думки
allowedProcMountTypes
(альфа-функція)
ПеревіркаBaseline & Restricted: ["Default"] АБО undefined / nil / empty
runtimeClass
 .defaultRuntimeClassName
ЗмінаНемає думки
runtimeClass
 .allowedRuntimeClassNames
ПеревіркаНемає думки

Анотації PodSecurityPolicy

Анотації, перераховані в цій таблиці, можуть бути вказані у .metadata.annotations обʼєкту PodSecurityPolicy.

Зіставлення анотацій PodSecurityPolicy зі стандартами безпеки Podʼів
Анотація PSPТипЕквівалент стандартів безпеки Podʼів
seccomp.security.alpha.kubernetes.io
/defaultProfileName
ЗмінаНемає думки
seccomp.security.alpha.kubernetes.io
/allowedProfileNames
Перевірка

Baseline: "runtime/default," (Кома в кінці, щоб встановити unset)

Restricted: "runtime/default" (Без коми в кінці)

Значення localhost/* також дозволені як для Baseline, так і для Restricted.

apparmor.security.beta.kubernetes.io
/defaultProfileName
ЗмінаНемає думки
apparmor.security.beta.kubernetes.io
/allowedProfileNames
Перевірка

Baseline: "runtime/default," (Кома в кінці, щоб встановити unset)

Restricted: "runtime/default" (Без коми в кінці)

Значення localhost/* також дозволені як для Baseline, так і для Restricted.

13 - Автентифікація/авторизація kubelet

Огляд

HTTP-запити до HTTPS-точки доступу kubelet надають доступ до даних різного рівня чутливості та дозволяють виконувати операції з різними рівнями повноважень на вузлі та в контейнерах.

У цьому документі описано, як автентифікувати та авторизувати доступ до HTTPS-точки доступу kubelet.

Автентифікація kubelet

Стандартно запити до HTTPS-точки доступу kubelet, які не відхилені іншими налаштованими методами автентифікації, розглядаються як анонімні запити та отримують імʼя користувача system:anonymous та групу system:unauthenticated.

Щоб вимкнути анонімний доступ та надсилати відповіді 401 Unauthorized на невідомі запити:

  • запустіть kubelet з прапорцем --anonymous-auth=false

Щоб увімкнути автентифікацію за допомогою клієнтських сертифікатів X509 до HTTPS-точки доступу kubelet:

  • запустіть kubelet з прапорцем --client-ca-file, надаючи набір кореневих сертифікатів для перевірки клієнтських сертифікатів
  • запустіть apiserver з прапорцями --kubelet-client-certificate та --kubelet-client-key
  • див. документацію з автентифікації apiserver для отримання додаткових відомостей

Щоб увімкнути використання API-токенів на предʼявника (включаючи токени службових облікових записів) для автентифікації до HTTPS-точки доступу kubelet:

  • переконайтеся, що група API authentication.k8s.io/v1beta1 ввімкнена в apiserver
  • запустіть kubelet з прапорцями --authentication-token-webhook та --kubeconfig
  • kubelet викликає API TokenReview на налаштованому apiserver, щоб визначити інформацію про користувача з токенів на предʼявника

Авторизація kubelet

Будь-який запит, який успішно автентифікується (включаючи анонімний запит), потім авторизується. Стандартний режим авторизації — AlwaysAllow, який дозволяє всі запити.

Є багато можливих причин для розподілу доступу до API kubelet:

  • ввімкнено анонімну автентифікацію, але потрібно обмежити можливості анонімних користувачів викликати API kubelet
  • ввімкнено автентифікацію з використанням токенів на предʼявника, але потрібно обмежити можливості довільних користувачів API (наприклад, службові облікові записи) викликати API kubelet
  • ввімкнено автентифікацію за допомогою клієнтських сертифікатів, але дозволено використовувати API kubelet тільки деяким сертифікатам клієнтів, які підписані налаштованим кореневим сертифікатом

Щоб розділити доступ до API kubelet, делегуйте авторизацію apiserver:

  • переконайтеся, що група API authorization.k8s.io/v1beta1 ввімкнена в apiserver
  • запустіть kubelet з прапорцями --authorization-mode=Webhook та --kubeconfig
  • kubelet викликає API SubjectAccessReview на налаштованому apiserver, щоб визначити, чи авторизований кожний запит

Kubelet авторизує запити до API, використовуючи той самий підхід до атрибутів запиту, що й apiserver.

Дієслово визначається з HTTP-дії вхідного запиту:

HTTP-діяДієслово запиту
POSTcreate
GET, HEADget
PUTupdate
PATCHpatch
DELETEdelete

Ресурс та субресурс визначаються з шляху вхідного запиту:

Kubelet APIРесурсСубресурс
/stats/*nodesstats
/metrics/*nodesmetrics
/logs/*nodeslog
/spec/*nodesspec
всі іншіnodesproxy

Атрибути простору імен та групи API завжди є порожніми рядками, а імʼя ресурсу завжди є імʼям обʼєкта Node kubelet.

При використанні цього режиму переконайтеся, що користувач, визначений прапорцями --kubelet-client-certificate та --kubelet-client-key, переданими до apiserver, має дозвіл наступних атрибутів:

  • verb=*, resource=nodes, subresource=proxy
  • verb=*, resource=nodes, subresource=stats
  • verb=*, resource=nodes, subresource=log
  • verb=*, resource=nodes, subresource=spec
  • verb=*, resource=nodes, subresource=metrics

14 - Початкове завантаження TLS

У кластері Kubernetes компоненти на вузлах робочих навантажень, kubelet та kube-proxy, повинні взаємодіяти з компонентами панелі управління Kubernetes, зокрема з kube-apiserver. Для забезпечення приватності комунікації, її невтручання та переконання, що кожен компонент кластера спілкується з іншим довіреним компонентом, ми наполегливо рекомендуємо використовувати TLS-сертифікати клієнтів на вузлах.

Звичайний процес ініціалізації цих компонентів, особливо робочих вузлів, які потребують сертифікати для безпечної комунікації з kube-apiserver, може бути складним, оскільки він часто виходить за межі Kubernetes і вимагає значної додаткової роботи. Це, своєю чергою, може ускладнити ініціалізацію або масштабування кластера.

З метою спрощення процесу, починаючи з версії 1.4, Kubernetes ввів API запиту та підпису сертифікатів. Пропозицію можна знайти тут.

У цьому документі описано процес ініціалізації вузла, спосіб налаштування початкового завантаження TLS-сертифікатів клієнтів для kubelet та його роботу.

Процес ініціалізації

Коли робочий вузол запускається, kubelet виконує наступне:

  1. Шукає свій файл kubeconfig.
  2. Отримує URL-адресу сервера API та облікові дані, зазвичай ключ TLS та підписаний сертифікат з файлу kubeconfig.
  3. Намагається спілкуватися з сервером API, використовуючи облікові дані.

Припускаючи, що kube-apiserver успішно перевіряє облікові дані kubelet, він буде вважати kubelet дійсним вузлом та почне призначати для нього Podʼи.

Зауважте, що вищевказаний процес залежить від:

  • Існування ключа та сертифіката на локальному хості у kubeconfig.
  • Підписання сертифіката центром сертифікації (CA), якому довіряє kube-apiserver.

Всі перелічені нижче обовʼязки покладаються на того, хто налаштовує та керує кластером:

  1. Створення ключа та сертифіката CA.
  2. Розповсюдження сертифіката CA на вузли панелі управління, де запущений kube-apiserver.
  3. Створення ключа та сертифіката для кожного kubelet; наполегливо рекомендується мати унікальний ключ з унікальним CN для кожного kubelet.
  4. Підписання сертифіката kubelet за допомогою ключа CA.
  5. Розповсюдження ключа та підписаного сертифіката kubelet на конкретний вузол, на якому працює kubelet.

Початкове завантаження TLS, описане у цьому документі, призначене спростити та частково або повністю автоматизувати кроки з 3 по 14, оскільки вони є найбільш поширеними при ініціалізації або масштабуванні кластера.

Ініціалізація початкового завантаження

Під час процесу ініціалізації початкового завантаження відбувається наступне:

  1. kubelet починає роботу.
  2. kubelet бачить, що у нього немає файлу kubeconfig.
  3. kubelet шукає та знаходить файл bootstrap-kubeconfig.
  4. kubelet читає свій файл ініціалізації початкового завантаження, отримуючи URL-адресу сервера API та обмежений "токен".
  5. kubelet підключається до сервера API, автентифікується за допомогою токена.
  6. у kubelet тепер є обмежені облікові дані для створення та отримання запиту на підпис сертифіката (CSR).
  7. kubelet створює CSR для себе з встановленим signerName kubernetes.io/kube-apiserver-client-kubelet.
  8. CSR затверджується одним з двох способів:
    • Якщо налаштовано, kube-controller-manager автоматично затверджує CSR.
    • Якщо налаштовано, зовнішній процес, можливо, людина, затверджує CSR за допомогою API Kubernetes або через kubectl.
  9. Сертифікат створюється для kubelet.
  10. Сертифікат видано для kubelet.
  11. kubelet отримує сертифікат.
  12. kubelet створює належний kubeconfig з ключем та підписаним сертифікатом.
  13. kubelet починає нормальну роботу.
  14. Опціонально: якщо налаштовано, kubelet автоматично запитує поновлення сертифіката, коли той наближається до закінчення строку дії.
  15. Поновлений сертифікат затверджується та видається, або автоматично, або вручну, залежно від налаштувань.

Усі інші розділи цього документу описують необхідні кроки для налаштування початкового завантаження TLS та його обмежень.

Налаштування

Для налаштування початкового завантаження TLS та опціонального автоматичного затвердження потрібно налаштувати параметри на наступних компонентах:

  • kube-apiserver
  • kube-controller-manager
  • kubelet
  • ресурси в кластері: ClusterRoleBinding та, можливо, ClusterRole

Крім того, вам потрібен ваш центр сертифікації Kubernetes (CA).

Центр сертифікації

Як і без початкового завантаження, вам знадобиться ключ та сертифікат центру сертифікації (CA). Як і раніше, вони будуть використовуватися для підпису сертифіката kubelet. Як і раніше, ваша відповідальність полягає в їх розповсюджені на вузли панелі управління.

Для цілей цього документу ми припускаємо, що вони розповсюджені на вузли панелі управління за шляхом /var/lib/kubernetes/ca.pem (сертифікат) та /var/lib/kubernetes/ca-key.pem (ключ). Ми будемо посилатися на них як "сертифікат та ключ CA Kubernetes".

Всі компоненти Kubernetes, які використовують ці сертифікати — kubelet, kube-apiserver, kube-controller-manager — припускають, що ключ та сертифікат закодовані у PEM-форматі.

Налаштування kube-apiserver

У kube-apiserver є кілька вимог для активації початкового завантаження TLS:

  • Визнання CA, який підписує сертифікат клієнта
  • Автентифікація початкового завантаження kubelet у групу system:bootstrappers
  • Авторизація початкового завантаження kubelet для створення запиту на підпис сертифіката (CSR)

Визнання сертифікатів клієнтів

Це є нормою для всієї автентифікації сертифікатів клієнтів. Якщо це ще не налаштовано, додайте прапорець --client-ca-file=ФАЙЛ до команди kube-apiserver, щоб активувати автентифікацію за сертифікатом клієнта, посилаючись на пакет сертифікатів центру сертифікації, наприклад --client-ca-file=/var/lib/kubernetes/ca.pem.

Початкова автентифікація початкового завантаження

Для того, щоб процес початкового завантаження kubelet міг підʼєднатися до kube-apiserver та запросити сертифікат, спочатку йому потрібно автентифікуватися на сервері. Ви можете використовувати будь-який автентифікатор, який може автентифікувати kubelet.

Хоча будь-яка стратегія автентифікації може бути використана для початкових облікових даних kubelet, рекомендується використовувати наступні два автентифікатори для полегшення надання прав.

  1. Токени початкового завантаження
  2. Файл автентифікації токенів

Використання токенів початкового завантаження є простішим та зручнішим способом автентифікації kubelet і не вимагає додаткових прапорів при запуску kube-apiserver.

Який би метод ви не обрали, вимога полягає в тому, щоб kubelet міг автентифікуватися як користувач з правами на:

  1. створення та отримання CSRs
  2. автоматичне затвердження запиту клієнтських сертифікатів вузлів, якщо увімкнено автоматичне затвердження.

Kubelet, який автентифікується за допомогою початкових токенів, автентифікується як користувач у групі system:bootstrappers, що є стандартним методом використання.

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

Токени початкового завантаження

Токени початкового завантаження докладно описані тут. Це токени, які зберігаються як секрети в кластері Kubernetes і потім видаються окремим kubelet. Ви можете використовувати один токен для всього кластера або видавати по одному на кожен робочий вузол.

Процес складається з двох етапів:

  1. Створити Secret Kubernetes з ідентифікатором токена, секретом і областю(ями).
  2. Видати токен kubelet.

З погляду kubelet, один токен такий самий, як інший і не має особливого значення. З погляду kube-apiserver, однак, початковий токен є особливим. Завдяки його type, namespace і name, kube-apiserver розпізнає його як спеціальний токен і надає будь-кому, хто автентифікується з цим токеном, особливі права початкового завантаження, зокрема, розглядаючи їх як члена групи system:bootstrappers. Це виконує основну вимогу для початкового завантаження TLS.

Деталі щодо створення секрету доступні тут.

Якщо ви хочете використовувати токени початкового завантаження, ви повинні увімкнути їх на kube-apiserver з прапорцем:

--enable-bootstrap-token-auth=true

Файл автентифікації токенів

kube-apiserver має можливість приймати токени для автентифікації. Ці токени можуть бути довільними, але повинні представляти щонайменше 128 біт ентропії, отриманих з надійного генератора випадкових чисел (наприклад, /dev/urandom у більшості сучасних Linux-систем). Є кілька способів генерації токена. Наприклад:

head -c 16 /dev/urandom | od -An -t x | tr -d ' '

Це згенерує токени, які виглядають так: 02b50b05283e98dd0fd71db496ef01e8.

Файл токенів повинен виглядати як у наступному прикладі, де перші три значення можуть бути будь-якими, а імʼя групи в лапках повинно бути таким, як показано:

02b50b05283e98dd0fd71db496ef01e8,kubelet-bootstrap,10001,"system:bootstrappers"

Додайте прапорець --token-auth-file=FILENAME до команди kube-apiserver (можливо, у вашому файлі systemd), щоб увімкнути файл токенів. Докладніше дивіться тут.

Авторизація kubelet на створення CSR

Тепер, коли вузол початкового завантаження автентифікований як частина групи system:bootstrappers, його потрібно авторизувати на створення запиту на підпис сертифіката (CSR), а також на його отримання після завершення. На щастя, Kubernetes постачається з ClusterRole, який має саме ці (і тільки ці) дозволи, system:node-bootstrapper.

Щоб зробити це, потрібно лише створити ClusterRoleBinding, що звʼязує групу system:bootstrappers з кластерною роллю system:node-bootstrapper.

# увімкнення створення CSR для вузлів початкового завантаження
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: create-csrs-for-bootstrapping
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:node-bootstrapper
  apiGroup: rbac.authorization.k8s.io

Налаштування kube-controller-manager

Поки apiserver отримує запити на сертифікати від kubelet і автентифікує ці запити, controller-manager відповідає за видачу фактичних підписаних сертифікатів.

Controller-manager виконує цю функцію через цикл управління видачею сертифікатів. Це реалізується у вигляді локального підписувача cfssl, який використовує активи на диску. Зараз всі видані сертифікати стандартно мають один рік дійсності та набір ключів для використання.

Для того, щоб controller-manager міг підписувати сертифікати, йому потрібно наступне:

  • доступ до "ключа та сертифіката Kubernetes CA", який ви створили та розповсюдили
  • увімкнення підписування CSR

Доступ до ключа та сертифіката

Як описано раніше, вам потрібно створити ключ і сертифікат Kubernetes CA і розповсюдити їх на вузли панелі управління. Ці сертифікати будуть використовуватися controller-manager для підписування сертифікатів kubelet.

Оскільки ці підписані сертифікати, своєю чергою, будуть використовуватися kubelet для автентифікації як звичайного kubelet до kube-apiserver, важливо, щоб CA, наданий controller-manager на цьому етапі, також був довірений kube-apiserver для автентифікації. Це надається kube-apiserver за допомогою прапорця --client-ca-file=FILENAME (наприклад, --client-ca-file=/var/lib/kubernetes/ca.pem), як описано в розділі конфігурації kube-apiserver.

Щоб надати ключ і сертифікат Kubernetes CA для kube-controller-manager, використовуйте наступні прапорці:

--cluster-signing-cert-file="/etc/path/to/kubernetes/ca/ca.crt" --cluster-signing-key-file="/etc/path/to/kubernetes/ca/ca.key"

Наприклад:

--cluster-signing-cert-file="/var/lib/kubernetes/ca.pem" --cluster-signing-key-file="/var/lib/kubernetes/ca-key.pem"

Тривалість дійсності підписаних сертифікатів можна налаштувати за допомогою прапорця:

--cluster-signing-duration

Затвердження

Щоб затвердити CSR, потрібно вказати controller-manager, що їх можна затверджувати. Це робиться шляхом надання прав доступу RBAC потрібній групі.

Існують два різні набори дозволів:

  • nodeclient: Якщо вузол створює новий сертифікат для вузла, тоді у нього ще немає сертифіката. Він автентифікується за допомогою одного з токенів, зазначених вище, і таким чином є частиною групи system:bootstrappers.
  • selfnodeclient: Якщо вузол оновлює свій сертифікат, тоді у нього вже є сертифікат (за визначенням), який він використовує для автентифікації як частина групи system:nodes.

Щоб дозволити kubelet запитувати та отримувати новий сертифікат, створіть ClusterRoleBinding, що звʼязує групу, в якій є членом вузол початкового завантаження, system:bootstrappers, з ClusterRole, що надає їй дозвіл, system:certificates.k8s.io:certificatesigningrequests:nodeclient:

# Затвердження всіх CSR для групи "system:bootstrappers"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-csrs-for-group
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
  apiGroup: rbac.authorization.k8s.io

Щоб дозволити kubelet оновлювати власний клієнтський сертифікат, створіть ClusterRoleBinding, що звʼязує групу, в якій є членом повнофункціональний вузол, system:nodes, з ClusterRole, що надає їй дозвіл, system:certificates.k8s.io:certificatesigningrequests:selfnodeclient:

# Затвердження запитів на оновлення CSR для групи "system:nodes"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-renewals-for-nodes
subjects:
- kind: Group
  name: system:nodes
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  apiGroup: rbac.authorization.k8s.io

Контролер csrapproving, який постачається як частина kube-controller-manager, стандартно увімкнено. Контролер використовує API SubjectAccessReview для визначення, чи авторизований користувач для запиту CSR, а потім затверджує на основі результатів авторизації. Щоб уникнути конфліктів з іншими затверджувачами, вбудований затверджувач не відхиляє CSR явним чином. Він лише ігнорує неавторизовані запити. Контролер також видаляє прострочені сертифікати в рамках збору сміття.

Налаштування kubelet

Нарешті, з правильно налаштованими вузлами панелі управління та всією необхідною автентифікацією та авторизацією, ми можемо налаштувати kubelet.

Для початкового завантаження kubelet потрібна наступна конфігурація:

  • Шлях для зберігання ключа та сертифіката, які він генерує (опціонально, можна використовувати стандартні)
  • Шлях до файлу kubeconfig, який ще не існує; тут буде збережено конфігураційний файл після початкового завантаження
  • Шлях до початкового файлу kubeconfig, що містить URL сервера та початкові облікові дані, наприклад, початковий токен
  • Опціонально: інструкції щодо ротації сертифікатів

Початковий файл kubeconfig має бути в шляху, доступному для kubelet, наприклад /var/lib/kubelet/bootstrap-kubeconfig.

Його формат ідентичний звичайному файлу kubeconfig. Приклад файлу може виглядати наступним чином:

apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority: /var/lib/kubernetes/ca.pem
    server: https://my.server.example.com:6443
  name: bootstrap
contexts:
- context:
    cluster: bootstrap
    user: kubelet-bootstrap
  name: bootstrap
current-context: bootstrap
preferences: {}
users:
- name: kubelet-bootstrap
  user:
    token: 07401b.f395accd246ae52d

Важливі елементи:

  • certificate-authority: шлях до файлу CA, використовується для перевірки сертифіката сервера, представленого kube-apiserver
  • server: URL до kube-apiserver
  • token: токен для використання

Формат токена не має значення, головне, щоб він відповідав очікуванням kube-apiserver. У наведеному прикладі ми використали початковий токен. Як зазначалося раніше, будь-який дійсний метод автентифікації може бути використаний, а не тільки токени.

Оскільки початковий kubeconfig є стандартним kubeconfig, ви можете використовувати kubectl для його створення. Щоб створити вищезазначений приклад файлу:

kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-cluster bootstrap --server='https://my.server.example.com:6443' --certificate-authority=/var/lib/kubernetes/ca.pem
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=07401b.f395accd246ae52d
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig use-context bootstrap

Щоб вказати kubelet використовувати початковий kubeconfig, використовуйте наступний прапорець:

--bootstrap-kubeconfig="/var/lib/kubelet/bootstrap-kubeconfig" --kubeconfig="/var/lib/kubelet/kubeconfig"

Під час запуску kubelet, якщо файл, вказаний через --kubeconfig, не існує, початковий kubeconfig, вказаний через --bootstrap-kubeconfig, використовується для запиту клієнтського сертифіката від API сервера. Після затвердження запиту на сертифікат і його отримання kubelet, конфігураційний файл kubeconfig, що посилається на згенерований ключ і отриманий сертифікат, буде записаний у шлях, вказаний за допомогою --kubeconfig. Файл сертифіката і ключа буде розміщено в теці, вказаній прапорцем --cert-dir.

Клієнтські та серверні сертифікати

Все вищезазначене стосується клієнтських сертифікатів kubelet, зокрема сертифікатів, які kubelet використовує для автентифікації до kube-apiserver.

kubelet також може використовувати серверні сертифікати. Сам kubelet відкриває https-точку доступу для певних функцій. Для їх захисту, kubelet може робити одне з наступного:

  • використовувати наданий ключ та сертифікат через прапорці --tls-private-key-file та --tls-cert-file
  • створити самопідписаний ключ та сертифікат, якщо ключ та сертифікат не надані
  • запитати серверні сертифікати у сервера кластера через API CSR

Клієнтський сертифікат, наданий під час початкового завантаження TLS, стандартно підписується лише для client auth і, отже, не може використовуватися як серверний сертифікат, або server auth.

Однак, ви можете увімкнути його серверний сертифікат, принаймні частково, через ротацію сертифікатів.

Ротація сертифікатів

З версії Kubernetes v1.8 та вище kubelet реалізує функції для увімкнення ротації його клієнтських і/або серверних сертифікатів. Зверніть увагу, що ротація серверного сертифіката є бета функцією та потребує функціональної можливості RotateKubeletServerCertificate на kubelet (стандартно увімкнено).

Ви можете налаштувати kubelet для ротації його клієнтських сертифікатів, створюючи нові CSRs при закінченні терміну дії його поточних облікових даних. Щоб увімкнути цю функцію, використовуйте поле rotateCertificates у файлі конфігурації kubelet або передайте наступний аргумент командного рядка kubelet (застаріло):

--rotate-certificates

Увімкнення RotateKubeletServerCertificate призводить до того, що kubelet одночасно запитує серверний сертифікат після початкового завантаження своїх клієнтських облікових даних і ротує цей сертифікат. Щоб увімкнути цю поведінку, використовуйте поле serverTLSBootstrap у файлі конфігурації kubelet або передайте наступний аргумент командного рядка kubelet (застаріло):

--rotate-server-certificates

Інші складові автентифікації

Усі процеси завантаження TLS, описані у цьому документі, стосуються kubelet. Однак інші компоненти можуть потребувати прямого звʼязку з kube-apiserver. Особливо важливим є kube-proxy, який є частиною компонентів вузла Kubernetes і запускається на кожному вузлі, але може також включати інші компоненти, такі як моніторинг чи роботу з мережею.

Подібно до kubelet, цим іншим компонентам також потрібен метод автентифікації у kube-apiserver. У вас є кілька варіантів для генерації цих облікових даних:

  • Традиційний спосіб: Створіть і розповсюдьте сертифікати так само як ви це робили для kubelet перед завантаженням TLS.
  • DaemonSet: Оскільки сам kubelet завантажується на кожний вузол і достатньо для запуску базових служб, ви можете запускати kube-proxy та інші служби, специфічні для вузла, не як самостійний процес, а як daemonset у просторі імен kube-system. Оскільки він буде в кластері, ви можете надати йому відповідний службовий обліковий запис з відповідними дозволами для виконання своїх дій. Це може бути найпростішим способом налаштування таких служб.

Затвердження за допомогою kubectl

Запити на сертифікати можна затвердити поза процесом затвердження, вбудованим у контролер керування.

Контролер підпису не негайно підписує всі запити на сертифікати. Замість цього він чекає, доки вони не будуть позначені статусом "Approved" відповіднbv привілейованим користувачем. Цей процес призначений для того, щоб дозволити автоматичне затвердження, яке обробляється зовнішнім контролером затвердження або controller-manager, реалізованим в основному controller-manager. Однак адміністратори кластера також можуть вручну затверджувати запити на сертифікати за допомогою kubectl. Адміністратор може отримати перелік CSRs за допомогою kubectl get csr та детально описати один з них за допомогою kubectl describe csr <name>. Адміністратор може затвердити або відхилити CSR за допомогою kubectl certificate approve <name> та kubectl certificate deny <name>.

15 - Правила перевірки допуску

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

Ця сторінка надає огляд правил перевірки допуску.

Що таке правила перевірки допуску?

Правила перевірки допуску пропонують декларативну, вбудовану альтернативу веб-хукам перевірки допуску.

Правила перевірки допуску використовують мову загальних виразів (Common Expression Language, CEL). Правила перевірки допуску мають високу налаштовуваність, що дозволяє авторам визначати правила, які можуть бути параметризовані та обмежені ресурсами за необхідності адміністраторами кластера.

Які ресурси складають правила

Зазвичай правило складається з трьох ресурсів:

  • ValidatingAdmissionPolicy описує абстрактну логіку правил (наприклад: "ці праивла переконуються, що певна мітка встановлена у певне значення").

  • ValidatingAdmissionPolicyBinding повʼязує вищезазначені ресурси разом і надає обмеження області дії. Якщо вам потрібно вимагати встановлення мітки owner для Pods, привʼязка визначає, де ви будете вказувати це обмеження.

  • Ресурс параметра надає інформацію для ValidatingAdmissionPolicy, щоб зробити його конкретним висловленням (наприклад, "мітка owner повинна бути встановлена на щось, що закінчується на .company.com"). Вбудований тип, такий як ConfigMap або CRD, визначає схему ресурсу параметра. Обʼєкти ValidatingAdmissionPolicy вказують, який Kind вони очікують для свого ресурсу параметру.

Для того щоб правила мали ефект, обовʼязково повинні бути визначені принаймні ValidatingAdmissionPolicy та відповідне ValidatingAdmissionPolicyBinding.

Якщо ValidatingAdmissionPolicy не потребує налаштування через параметри, просто залиште spec.paramKind в ValidatingAdmissionPolicy не вказаним.

Початок роботи з правилами перевірки допуску

Правила перевірки допуску є частиною панелі управління кластера. Ви повинні писати та розгортати їх з великою обережністю. Нижче наведено інструкції щодо швидкого експерименту з правилами перевірки допуску.

Створення ValidatingAdmissionPolicy

Нижче наведено приклад ValidatingAdmissionPolicy.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "demo-policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - expression: "object.spec.replicas <= 5"

spec.validations містить вирази CEL, які використовують Мову загальних виразів (CEL), щоб перевірити запит. Якщо вираз обчислюється як false, перевірка валідації застосовується згідно з полем spec.failurePolicy.

Для налаштування правил перевірки допуску для використання в кластері потрібна привʼязка. Нижче наведено приклад ValidatingAdmissionPolicyBinding.:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "demo-binding-test.example.com"
spec:
  policyName: "demo-policy.example.com"
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: test

Спробувавши створити Deployment з репліками, які не відповідають виразу валідації, буде повернута помилка з повідомленням:

ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5

Вище наведено простий приклад використання ValidatingAdmissionPolicy без налаштованого параметра.

Дії валідації

Кожний ValidatingAdmissionPolicyBinding повинен вказати одну або декілька validationActions, щоб визначити, як validations правила будуть застосовані.

Підтримувані validationActions:

  • Deny: Невдалий результат валідації призводить до відхиленого запиту.
  • Warn: Невдалий результат валідації повідомляється клієнту запиту як попередження.
  • Audit: Невдалий результат валідації включається в подію аудиту для запиту до API.

Наприклад, щоб одночасно попереджувати клієнтів про невдалий результат валідації та аудитувати невдалий результат валідації, використовуйте:

validationActions: [Warn, Audit]

Deny та Warn не можуть бути використані разом, оскільки ця комбінація надмірно дублює невдалий результат валідації як у тілі відповіді API, так і в HTTP заголовках попередження.

validation, який оцінюється як false, завжди застосовується відповідно до цих дій. Невдачі, визначені полем failurePolicy, застосовуються відповідно до цих дій тільки у випадку, якщо failurePolicy встановлено на Fail (або не вказано), інакше невдачі ігноруються.

Див. Анотації аудиту: невдалий результат валідації для отримання додаткових відомостей щодо аудиту невдалих результатів валідації.

Ресурси параметрів

Ресурси параметрів дозволяють відокремити конфігурацію правил від їх визначення. Правило може визначити paramKind, який визначає GVK ресурсу параметра, а потім привʼязка правила повʼязує його за іменем (через policyName) з певним ресурсом параметра через paramRef.

Якщо потрібна конфігурація параметра, наведено приклад ValidatingAdmissionPolicy з конфігурацією параметра.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "replicalimit-policy.example.com"
spec:
  failurePolicy: Fail
  paramKind:
    apiVersion: rules.example.com/v1
    kind: ReplicaLimit
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - expression: "object.spec.replicas <= params.maxReplicas"
      reason: Invalid

Поле spec.paramKind ValidatingAdmissionPolicy вказує на вид використовуваних ресурсів для параметризації цього правила. У цьому прикладі це налаштовано за допомогою ресурсів ReplicaLimit. Зверніть увагу в цьому прикладі, як вираз CEL посилається на параметри через змінну CEL params, наприклад, params.maxReplicas. spec.matchConstraints вказує, для яких ресурсів ця правило призначена для валідації. Зверніть увагу, що як параметр можуть використовуватися і стандартні типи, такі як ConfigMap.

Поля spec.validations містять вирази CEL. Якщо вираз оцінюється як false, то валідаційна перевірка здійснюється відповідно до поля spec.failurePolicy.

Автор правил перевірки допуску відповідає за надання параметра CRD ReplicaLimit.

Для налаштування правил перевірки допуску для використання в кластері створюються привʼязка та ресурс параметра. Наведено приклад ValidatingAdmissionPolicyBinding який використовує кластерний параметр — той самий параметр буде використовуватися для валідації кожного запиту до ресурсу, який відповідає привʼязці:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "replicalimit-binding-test.example.com"
spec:
  policyName: "replicalimit-policy.example.com"
  validationActions: [Deny]
  paramRef:
    name: "replica-limit-test.example.com"
    namespace: "default"
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: test

Зверніть увагу, що ця привʼязка застосовує параметр до правил для всіх ресурсів, які знаходяться в середовищі test.

Ресурс параметра може мати наступний вигляд:

apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
  name: "replica-limit-test.example.com"
  namespace: "default"
maxReplicas: 3

Цей ресурс параметра правил обмежує Deployments до максимуму 3 репліки.

В правилі допуску може бути кілька привʼязок. Щоб привʼязати всі інші середовища до обмеження maxReplicas 100, створіть інший ValidatingAdmissionPolicyBinding:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "replicalimit-binding-nontest"
spec:
  policyName: "replicalimit-policy.example.com"
  validationActions: [Deny]
  paramRef:
    name: "replica-limit-prod.example.com"
    namespace: "default"
  matchResources:
    namespaceSelector:
      matchExpressions:
      - key: environment
        operator: NotIn
        values:
        - test

Зверніть увагу, що ця привʼязка застосовує різний параметр до ресурсів, які не знаходяться в середовищі test.

Та має ресурс параметра:

apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
  name: "replica-limit-prod.example.com"
maxReplicas: 100

Для кожного запиту на допуск, сервер API оцінює вирази CEL кожної комбінації (правило, привʼязка, параметр), які відповідають запиту. Для того, щоб запит був прийнятий, він повинен пройти всі оцінки.

Якщо кілька привʼязок відповідають запиту, правило буде оцінено для кожної, і вони всі повинні пройти оцінку, щоб правило вважали пройденим.

Якщо кілька параметрів відповідають одній привʼязці, правила будуть оцінені для кожного параметра, і вони також повинні всі пройти, щоб привʼязка вважалася пройденою. Привʼязки можуть мати критерії відповідності, що перекриваються. Правило оцінюється для кожної відповідної комбінації привʼязка-параметр. Правило може бути оцінено навіть кілька разів, якщо йому відповідає кілька привʼязок, або одна привʼязка, яка відповідає кільком параметрам.

Обʼєкт params, який представляє ресурс параметра, не буде встановлений, якщо ресурс параметра не був привʼязаний, тому для правил, які потребують ресурсу параметра, може бути корисно додати перевірку, щоб забезпечити його привʼязку. Ресурс параметра не буде привʼязаний і params буде null, якщо paramKind правила або paramRef привʼязки не вказані.

Для випадків використання, що потребують конфігурації параметра, ми рекомендуємо додати перевірку параметра в spec.validations[0].expression:

- expression: "params != null"
  message: "params missing but required to bind to this policy"

Необовʼязкові параметри

Буває зручно мати можливість мати необовʼязкові параметри як частину ресурсу параметра і валідувати їх лише в разі їх присутності. У CEL є has(), який перевіряє, чи існує переданий ключ. Крім того, CEL реалізує булеве скорочення. Якщо перша половина логічного ОБО відноситься до true, то друга половина не оцінюється (оскільки результат усього ОБО буде true в будь-якому випадку).

Поєднуючи ці дві можливості, ми можемо забезпечити можливість валідації необовʼязкових параметрів:

!has(params.optionalNumber) || (params.optionalNumber >= 5 && params.optionalNumber <= 10)

Тут спочатку ми перевіряємо, що необовʼязковий параметр присутній за допомогою !has(params.optionalNumber).

  • Якщо optionalNumber не був визначений, то вираз скорочується, оскільки !has(params.optionalNumber) оцінюється як true.
  • Якщо optionalNumber був визначений, тоді друга половина CEL виразу буде оцінена, і optionalNumber буде перевірений, щоб забезпечити, що він містить значення від 5 до 10 включно.

Параметри на рівні простору імен

Як автор ValidatingAdmissionPolicy та його ValidatingAdmissionPolicyBinding, ви можете вибрати, чи вказувати параметри на рівні кластера або на рівні простору імен. Якщо ви вказуєте namespace для paramRef привʼязки, панель управління шукає параметри лише в цьому просторі імен.

Проте, якщо namespace не вказано в ValidatingAdmissionPolicyBinding, сервер API може шукати відповідні параметри в просторі імен, до якого відноситься запит. Наприклад, якщо ви робите запит на зміну ConfigMap у просторі імен default, і існує відповідна ValidatingAdmissionPolicyBinding без вказаного namespace, то сервер API шукає обʼєкт параметра в default. Цей дизайн дозволяє конфігурувати правило, що залежить від простору імен ресурсу, який обробляється, для отримання більш точного контролю.

Селектор параметрів

Крім вказання параметра у привʼязці за допомогою name, ви можете вибрати замість цього вказати селектор міток, таким чином, всі ресурси paramKind правила та namespace параметра (якщо застосовується), які відповідають селектору міток, вибираються для оцінки. Див. селектор для отримання додаткової інформації про те, як селектори міток відбирають ресурси.

Якщо умову виконується для декількох параметрів, правила оцінюються для кожного знайденого параметра, а результати будуть оцінені разом через логічне І (AND).

Якщо надано namespace, для вибору допускаються лише обʼєкти paramKind у вказаному просторі імен. В іншому випадку, коли namespace порожній, а paramKind обмежений простором імен, використовується namespace, використаний у запиті для допуску.

Перевірка авторизації

Ми ввели перевірку авторизації для ресурсів параметрів. Очікується, що користувач матиме доступ на читання до ресурсів, на які посилається paramKind у ValidatingAdmissionPolicy та paramRef у ValidatingAdmissionPolicyBinding.

Зверніть увагу, що якщо ресурс у paramKind не вдасться знайти через restmapper, потрібен доступ на читання до всіх ресурсів груп.

Правило помилок

failurePolicy визначає, як обробляються неправильні конфігурації та вирази CEL, що викликають помилку в правилах перевірки допуску. Допустимі значення: Ignore або Fail.

  • Ignore означає, що помилка під час виклику ValidatingAdmissionPolicy ігнорується, і запит API може продовжуватися.
  • Fail означає, що помилка під час виклику ValidatingAdmissionPolicy призводить до відмови в прийнятті та відхилення запиту API.

Зверніть увагу, що failurePolicy визначається всередині ValidatingAdmissionPolicy:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
spec:
...
failurePolicy: Ignore # стандартно "Fail"
validations:
- expression: "object.spec.xyz == params.x"  

Вирази валідації

spec.validations[i].expression представляє вираз, який буде оцінений за допомогою CEL. Для отримання додаткової інформації див. Специфікацію мови CEL. Вирази CEL мають доступ до вмісту запиту/відповіді допуску, організованого в змінні CEL, а також деяких інших корисних змінних:

  • 'object' — Обʼєкт з вхідного запиту. Значення null для запитів DELETE.
  • 'oldObject' — Наявний обʼєкт. Значення null для запитів CREATE.
  • 'request' — Атрибути запиту допуску.
  • 'params' — Ресурс параметра, на який посилається привʼязка правила, яке оцінюється. Значення null, якщо ParamKind не вказано.
  • namespaceObject — Простір імен, як ресурс Kubernetes, до якого належить вхідний обʼєкт. Значення null, якщо вхідний обʼєкт має область видимості кластера.
  • authorizer — Авторизатор CEL. Може використовуватися для виконання перевірок авторизації для принципала (автентифікованого користувача) запиту. Див. AuthzSelectors та Authz в документації бібліотеки Kubernetes CEL для отримання додаткових відомостей.
  • authorizer.requestResource — Скорочення для перевірки авторизації, налаштоване з ресурсом запиту (група, ресурс, (субресурс), простір імен, імʼя).

apiVersion, kind, metadata.name і metadata.generateName завжди доступні з кореня обʼєкта. Інші властивості метаданих не доступні.

Рівність у масивах із типом списку 'set' або 'map' ігнорує порядок елементів, тобто [1, 2] == [2, 1]. Конкатенація у масивах з x-kubernetes-list-type використовує семантику типу списку:

  • 'set': X + Y виконує обʼєднання, де позиції масиву всіх елементів в X зберігаються, а неперетинаючися елементи в Y додаються, зберігаючи їх частковий порядок.
  • 'map': X + Y виконує злиття, де позиції масиву всіх ключів в X зберігаються, але значення перезаписуються значеннями в Y, коли множини ключів X і Y перетинаються. Елементи в Y з неперетинаючимися ключами додаються, зберігаючи їх частковий порядок.

Приклади виразів валідації

ВиразПризначення
object.minReplicas <= object.replicas && object.replicas <= object.maxReplicasПеревірка правильного порядку трьох полів, що визначають репліки
'Available' in object.stateCountsПеревірка наявності запису з ключем 'Available' в map
(size(object.list1) == 0) != (size(object.list2) == 0)Перевірка, що один із двох списків не порожній, але не обидва
!('MY_KEY' in object.map1) || object['MY_KEY'].matches('^[a-zA-Z]*$')Перевірка значення map для певного ключа, якщо він є в map
object.envars.filter(e, e.name == 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')Перевірка поля 'value' запису у listMap, де значення ключа 'name' - 'MY_ENV'
has(object.expired) && object.created + object.ttl < object.expiredПеревірка, що дата 'expired' пізніше дати 'create' плюс тривалість 'ttl'
object.health.startsWith('ok')Перевірка, що рядок 'health' починається з префікса 'ok'
object.widgets.exists(w, w.key == 'x' && w.foo < 10)Перевірка, що поле 'foo' запису у listMap з ключем 'x' менше 10
type(object) == string ? object == '100%' : object == 1000Перевірка поля типу int або string на значення int та string
object.metadata.name.startsWith(object.prefix)Перевірка, що імʼя обʼєкта має префікс значення іншого поля
object.set1.all(e, !(e in object.set2))Перевірка, що два listSet не перетинаються
size(object.names) == size(object.details) && object.names.all(n, n in object.details)Перевірка, що map 'details' має ключі, визначені елементами списку 'names'
size(object.clusters.filter(c, c.name == object.primary)) == 1Перевірка, що властивість 'primary' має лише один випадок в listMap 'clusters'

Для отримання додаткової інформації щодо правил CEL прочитайте Supported evaluation on CEL.

spec.validation[i].reason представляє машинночитаний опис причини невдачі цієї валідації. Якщо це перша валідація в списку, яка завершується невдачею, ця причина, а також відповідний код відповіді HTTP використовуються у відповіді HTTP клієнту. Підтримувані зараз причини: Unauthorized, Forbidden, Invalid, RequestEntityTooLarge. Якщо не встановлено, StatusReasonInvalid використовується у відповіді клієнту.

Відповідність запитів: matchConditions

Ви можете визначити умови відповідності для ValidatingAdmissionPolicy, якщо вам потрібно дотримуватися детального фільтрування запитів. Ці умови корисні, якщо ви виявите, що правила відповідності, objectSelectors та namespaceSelectors все ще не забезпечують потрібного фільтрування. Умови відповідності — це вирази CEL. Усі умови відповідності повинні оцінюватися як true для ресурсу, який має бути оцінений.

Нижче наведено приклад, що ілюструє кілька різних використань умов відповідності:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "demo-policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups:   ["*"]
        apiVersions: ["*"]
        operations:  ["CREATE", "UPDATE"]
        resources:   ["*"]
  matchConditions:
    - name: 'exclude-leases' # Імʼя кожної умови має бути унікальним
      expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # Шукати збіг із запитами non-lease.
    - name: 'exclude-kubelet-requests'
      expression: '!("system:nodes" in request.userInfo.groups)' # Шукати збіг із запитами від користувачів, які не є користувачами вузла.
    - name: 'rbac' # Оминати запити RBAC.
      expression: 'request.resource.group != "rbac.authorization.k8s.io"'
  validations:
    - expression: "!object.metadata.name.contains('demo') || object.metadata.namespace == 'demo'"

Умови відповідності мають доступ до тих самих змінних CEL, що й вирази валідації.

У випадку помилки при оцінці умови відповідності правило не оцінюється. Рішення про відхилення запиту визначається наступним чином:

  1. Якщо будь-яка умова відповідності оцінюється як false (незалежно від інших помилок), сервер API пропускає політику.
  2. В іншому випадку:
    • для failurePolicy: Fail, відхилити запит (без оцінки правил).
    • для failurePolicy: Ignore, продовжити з запитом, але пропустити політику.

Анотації аудиту

auditAnnotations можна використовувати для включення анотацій аудиту в аудит-подію запиту API.

Наприклад, ось правила допуску з анотацією аудиту:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "demo-policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - key: "high-replica-count"
      expression: "object.spec.replicas > 50"
      messageExpression: "'Deployment spec.replicas set to ' + string(object.spec.replicas)"

Коли запит API перевіряється за цим правилом допуску, отримана подія аудиту буде виглядати так:

# Записана подія аудиту
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "annotations": {
        "demo-policy.example.com/high-replica-count": "Deployment spec.replicas set to 128"
        # інші анотації
        ...
    }
    # інші поля
    ...
}

У цьому прикладі анотація буде включена лише тоді, коли spec.replicas у Deployment більше 50, в іншому випадку вираз CEL оцінюється як null, і анотація не буде включена.

Зверніть увагу, що ключі анотацій аудиту мають префікс з імʼям ValidatingAdmissionWebhook та /. Якщо інший контролер допуску, такий як вебхук допуску, використовує точно такий самий ключ анотації аудиту, то значення першого контролера допуску, який включає анотацію аудиту, буде включено в аудит-подію, а всі інші значення будуть ігноруватися.

Вираз повідомлення

Щоб повертати більш дружнє повідомлення, коли правило відхиляє запит, ми можемо використовувати вираз CEL для створення повідомлення з spec.validations[i].messageExpression. Подібно до виразу валідації, вираз повідомлення має доступ до object, oldObject, request, params та namespaceObject. На відміну від валідації, вираз повідомлення має повертати рядок.

Наприклад, щоб краще інформувати користувача про причину відхилення, коли правило посилається на параметр, ми можемо мати наступну валідацію:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "deploy-replica-policy.example.com"
spec:
  paramKind:
    apiVersion: rules.example.com/v1
    kind: ReplicaLimit
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
  - expression: "object.spec.replicas <= params.maxReplicas"
    messageExpression: "'object.spec.replicas must be no greater than ' + string(params.maxReplicas)"
    reason: Invalid

Після створення обʼєкту параметрів, який обмежує репліки до 3 та налаштування привʼязки, коли ми спробуємо створити Deployment з 5 репліками, ми отримаємо наступне повідомлення.

$ kubectl create deploy --image=nginx nginx --replicas=5
error: failed to create deployment: deployments.apps "nginx" is forbidden: ValidatingAdmissionPolicy 'deploy-replica-policy.example.com' with binding 'demo-binding-test.example.com' denied request: object.spec.replicas must be no greater than 3

Це більш інформативно, ніж статичне повідомлення "занадто багато реплік".

Вираз повідомлення має перевагу над статичним повідомленням, визначеним у spec.validations[i].message, якщо обидва визначені. Однак, якщо вираз повідомлення не вдається оцінити, буде використано статичне повідомлення. Крім того, якщо вираз повідомлення оцінюється як багаторядковий рядок, результат оцінки буде відкинутий, і використовуватиметься статичне повідомлення, якщо воно присутнє. Зауважте, що статичне повідомлення перевіряється на відповідність багаторядковим рядкам.

Перевірка типів

Під час створення або оновлення визначення правил валідації процес валідації аналізує вирази, які він містить, та повідомляє про будь-які синтаксичні помилки, відхиляючи визначення, якщо виявлено помилки. Потім перевіряються змінні, на які є посилання, на наявність помилок типів, включаючи відсутні поля та плутанину типів, відносно відповідних типів spec.matchConstraints. Результат перевірки типів можна отримати з status.typeChecking. Наявність status.typeChecking вказує на завершення перевірки типів, а порожнє status.typeChecking означає, що помилок не виявлено.

Наприклад, з наступним визначенням правил:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "deploy-replica-policy.example.com"
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
  - expression: "object.replicas > 1" # має бути "object.spec.replicas > 1"
    message: "must be replicated"
    reason: Invalid

Статус надасть таку інформацію:

status:
  typeChecking:
    expressionWarnings:
    - fieldRef: spec.validations[0].expression
      warning: |-
        apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas'
         | object.replicas > 1
         | ......^        

Якщо в spec.matchConstraints збігається кілька ресурсів, всі ресурси, які мають збіг, будуть перевірені. Наприклад, наступне визначення правил:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "replica-policy.example.com"
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments","replicasets"]
  validations:
  - expression: "object.replicas > 1" # має бути "object.spec.replicas > 1"
    message: "must be replicated"
    reason: Invalid

буде мати декілька типів та результат перевірки типів кожного типу у повідомленні про попередження.

status:
  typeChecking:
    expressionWarnings:
    - fieldRef: spec.validations[0].expression
      warning: |-
        apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas'
         | object.replicas > 1
         | ......^
        apps/v1, Kind=ReplicaSet: ERROR: <input>:1:7: undefined field 'replicas'
         | object.replicas > 1
         | ......^        

Перевірка типів має такі обмеження:

  • Відсутнє зіставлення за шаблоном. Якщо spec.matchConstraints.resourceRules містить "*" у будь-якому з apiGroups, apiVersions або resources, типи, які відповідають "*", не будуть перевірені.
  • Кількість збігів типів обмежена до 10. Це робиться для запобігання використанню правил, що вручну вказує занадто багато типів, що може споживати занадто багато обчислювальних ресурсів. Порядок спадання групи, версії, а потім ресурсу, 11-а комбінація та після буде ігноруватися.
  • Перевірка типів не впливає на поведінку правил жодним чином. Навіть якщо під час перевірки типів виявляються помилки, політика буде продовжувати оцінюватися. Якщо під час оцінки виникають помилки, політика вибере свій результат.
  • Перевірка типів не застосовується до CRD, включаючи відповідні типи CRD та посилання на paramKind. Підтримка CRD зʼявиться у майбутній версії.

Склад змінних

Якщо вираз стає занадто складним або частина виразу повторно використовується та має високі обчислювальні витрати, ви можете винести частину виразів у змінні. Змінна — це вираз з назвою, на який можна посилатися пізніше в variables в інших виразах.

spec:
  variables:
    - name: foo
      expression: "'foo' in object.spec.metadata.labels ? object.spec.metadata.labels['foo'] : 'default'"
  validations:
    - expression: variables.foo == 'bar'

Змінна оцінюється ліниво (lazily), під час першого посилання на неї. Про будь-яку помилку, що виникає під час оцінки, буде повідомлено під час оцінки вказаного виразу, на який посилається змінна. Як результат, так і можлива помилка запамʼятовуються і лише один раз враховуються при оцінці під час виконання.

Порядок змінних важливий, оскільки змінна може посилатися на інші змінні, які визначені перед нею. Це упорядкування запобігає циклічним посиланням.

Наведено більш складний приклад змінних, що забезпечує відповідність імен репозиторіїв образів середовищу, визначеному у просторі імен.

# Це правило забезпечує виконання умови, що всі контейнери deployment повинні мати збіг репозиторієв образів з міткою середовища його простору імен.
# За винятком deployment, позначених як "exempt", або будь-яких контейнерів, які не належать організації "example.com" (наприклад, загальні sidecar).
# Наприклад, якщо у просторі імен є мітка {"environment": "staging"}, всі контейнери повинні мати репозиторій, який є або staging.example.com/*
# або не містить "example.com" взагалі, якщо deployment має мітку {"exempt": "true"}.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "image-matches-namespace-environment.policy.example.com"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  variables:
  - name: environment
    expression: "'environment' in namespaceObject.metadata.labels ? namespaceObject.metadata.labels['environment'] : 'prod'"
  - name: exempt
    expression: "'exempt' in object.metadata.labels && object.metadata.labels['exempt'] == 'true'"
  - name: containers
    expression: "object.spec.template.spec.containers"
  - name: containersToCheck
    expression: "variables.containers.filter(c, c.image.contains('example.com/'))"
  validations:
  - expression: "variables.exempt || variables.containersToCheck.all(c, c.image.startsWith(variables.environment + '.'))"
    messageExpression: "'only ' + variables.environment + ' images are allowed in namespace ' + namespaceObject.metadata.name"

З правилом, привʼязаним до простору імен default, який має мітку environment: prod, спроба створити Deployment буде відхилена.

kubectl create deploy --image=dev.example.com/nginx invalid

Повідомлення про помилку буде схоже на таке.

error: failed to create deployment: deployments.apps "invalid" is forbidden: ValidatingAdmissionPolicy 'image-matches-namespace-environment.policy.example.com' with binding 'demo-binding-test.example.com' denied request: only prod images are allowed in namespace default