Азбука за сигурност в Kubernetes: автентикация, авторизация, аудит

Азбука за сигурност в Kubernetes: автентикация, авторизация, аудит

Рано или късно в експлоатацията на всяка система възниква въпросът за сигурността: осигуряване на аутентификация, разделение на права, одит и други задачи. За Kubernetes вече са създадени множество решения, които позволяват постигане на съответствие със стандартите дори в много изискващи среди... Този материал е посветен на основните аспекти на сигурността, реализирани в рамките на вградените механизми на K8s. Първо, той ще бъде полезен на тези, които започват да се запознават с Kubernetes, — като отправна точка за изучаване на въпросите, свързани със сигурността.

Аутентикация

В Kubernetes има два типа потребители:

  • Сервизни акаунти — акаунти, управлявани от Kubernetes API;
  • Потребители — "нормални" потребители, управлявани от външни, независими услуги.

Основната разлика между тези типове е, че за Сервисни акаунти съществуват специални обекти в Kubernetes API (те така и се наричат — ServiceAccounts), които са свързани с пространство от имена и набор от авторизационни данни, съхранявани в клъстера в обекти от тип Secrets. Такъв тип потребители (Сервисни акаунти) е предназначен основно за управление на правата за достъп до Kubernetes API от процеси, работещи в клъстера Kubernetes.

Обикновените Потребители обаче нямат записи в Kubernetes API: управлението им трябва да се извършва от външни механизми. Те са предназначени за хора или процеси, които живеят извън клъстера.

Всяко искане към API е свързано либо със Сервисен акаунт, либо с Потребител, или се счита за анонимен.

Аутентикационните данни на потребителя включват:

  • Потребителско име — име на потребителя (зависи от регистра!);
  • UID — машинно четима низ за идентификация на потребителя, който е "по-постоянен и уникален от потребителското име";
  • Groups — списък с групи, към които принадлежи потребителят;
  • Допълнителни — допълнителни полета, които могат да бъдат използвани от механизма за авторизация.

Kubernetes може да използва множество механизми за аутентикация: сертификати X509, Bearer токени, аутентифициращ прокси, HTTP Basic Auth. Чрез тези механизми може да се реализира голямо разнообразие от схеми за авторизация: от статичен файл с пароли до OpenID OAuth2.

Освен това, е разрешено използването на няколко схеми за авторизация едновременно. По подразбиране в клъстера се използват:

  • токени на сервисни акаунти — за Сервисни акаунти;
  • X509 — за Потребители.

Въпросът за управлението на ServiceAccounts излиза извън рамките на тази статия, а на желаещите да се запознаят по-подробно с този въпрос, препоръчвам да започнат от страницата на официалната документация. Ние ще разгледаме по-подробно въпроса за работата със сертификати X509.

Сертификати за потребители (X.509)

Класическият начин за работа със сертификати предполага:

  • генериране на ключ:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/mynewuser.key 2048
  • генериране на заявка за сертификат:
    openssl req -new -key ~/mynewuser.key -out ~/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • обработка на заявката за сертификат с помощта на ключовете CA на Kubernetes кластера, получаване на сертификата за потребителя (за получаване на сертификат е необходимо да се използва акаунт, който има достъп до ключа на сертификатния център на Kubernetes, който по подразбиране се намира в /etc/kubernetes/pki/ca.key):
    openssl x509 -req -in ~/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/mynewuser.crt -days 500
  • създаване на конфигурационен файл:
    • описание на кластера (посочете адреса и местоположението на файла на сертификата CA за конкретната инсталация на кластера):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • или — най-препоръчителният вариант — можете да не посочвате корневия сертификат (тогава kubectl няма да проверява валидността на api-сервера на кластера): неkubectl config set-cluster kubernetes --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
      добавяне на потребителя в конфигурационния файл:
    • kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt --client-key=.certs/mynewuser.key
      добавяне на контекста:
    • kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
      назначаване на контекста по подразбиране:
    • kubectl config use-context mynewuser-context
      След гореспоменатите манипулации, в файла

.kube/config ще бъде създаден конфиг с вида: apiVersion: v1 clusters: - cluster: certificate-authority: /etc/kubernetes/pki/ca.crt server: https://192.168.100.200:6443 name: kubernetes contexts: - context: cluster: kubernetes namespace: target-namespace user: mynewuser name: mynewuser-context current-context: mynewuser-context kind: Config preferences: {} users: - name: mynewuser user: client-certificate: /home/mynewuser/.certs/mynewuser.crt client-key: /home/mynewuser/.certs/mynewuser.key

За улесняване на преноса на конфига между акаунти и сървъри, полезно е да се редактират стойностите на следните ключове:

certificate-authority

  • client-certificate
  • client-key
  • За целта може да се кодира указаният в тях файл с помощта на base64 и да се запише в конфигурацията, добавяйки суфикс

-data , т.е. получавайкиcertificate-authority-data Сертификати с kubeadm и т.н.

С релиза

Kubernetes 1.15 Kubernetes 1.15 Работата с сертификатите стана значително по-лесна благодарение на алфа версията на поддръжката в инструмента kubeadm. Например, ето как може да изглежда генерирането на конфигурационен файл с потребителски ключове:

kubeadm alpha kubeconfig user --client-name=mynewuser --apiserver-advertise-address 192.168.100.200

NB: Изисквано advertise address може да бъде видяно в конфигурацията на api-сървъра, който по подразбиране се намира в /etc/kubernetes/manifests/kube-apiserver.yaml.

Резултатният конфиг ще бъде изведен в stdout. Трябва да се запази в ~/.kube/config потребителския профил или в файл, посочен в променливата на средата KUBECONFIG.

Да се задълбочим

За тези, които желаят да се запознаят по-добре с описаните въпроси:

Авторизация

Авторизираният профил по подразбиране няма права за действия в клъстера. За предоставянето на разрешения в Kubernetes е реализиран механизъм за авторизация.

До версия 1.6 в Kubernetes беше използван тип авторизация, наречен ABAC (Контрол на достъпа, базиран на атрибути). Подробности за него можете да намерите в официалната документация. В момента този подход се счита за остарял (legacy), но все пак можете да го използвате едновременно с други типове авторизация.

Актуалният (и по-гъвкав) метод за разделение на правата за достъп до клъстера се нарича RBAC (Контрол на достъпа, базиран на роли). Той беше обявен за стабилен с версия Kubernetes 1.8. RBAC реализира модел на права, при който всичко, което не е разрешено изрично, е забранено.
За да включите RBAC, трябва да стартирате Kubernetes api-сървъра с параметъра --authorization-mode=RBAC. Параметрите се задават в манифеста с конфигурацията на api-сървъра, който по подразбиране се намира на пътя /etc/kubernetes/manifests/kube-apiserver.yaml, в секцията command. Въпреки това, по подразбиране RBAC е вече включен, така че вероятно не е нужно да се притеснявате за това: можете да се уверите в това по стойността на authorization-mode (в вече споменатия kube-apiserver.yaml). Между другото, сред неговите стойности могат да бъдат и други типове авторизация (node, webhook, винаги позволява), но разглеждането им ще оставим извън обхвата на материала.

Между другото, вече публикувахме статия с достатъчно подробен разказ за принципите и особеностите на работа с RBAC, така че по-нататък ще се огранича до кратко изброяване на основите и примери.

За управление на достъпа в Kubernetes чрез RBAC се използват следните API същности:

  • Role и ClusterRole — роли, които служат за описание на правата за достъп:
  • Role позволява да се опишат правата в рамките на пространство от имена;
  • ClusterRole — в рамките на клъстера, включително специфични за клъстера обекти като възли, non-resources urls (т.е. не свързани с ресурсите на Kubernetes — например, /version, /logs, /api*);
  • RoleBinding и ClusterRoleBinding — служи за свързване Role и ClusterRole към потребител, група потребители или ServiceAccount.

Сущностите Role и RoleBinding са ограничени в пространството от имена, т.е. трябва да бъдат в рамките на едно пространство от имена. Въпреки това RoleBinding може да се позове на ClusterRole, което позволява да се създаде набор от типови разрешения и да се управлява достъпът с тяхна помощ.

Ролите описват правата с помощта на набори от правила, съдържащи:

  • групи API — виж. официалната документация за apiGroups и изхода kubectl api-resources;
  • ресурси (resources: pod, namespace, deployment и т.н.);
  • глаголи (verbs: set, актуализиране и т.н.).
  • имена на ресурси (resourceNames) — за случая, когато трябва да предоставите достъп до определен ресурс, а не до всички ресурси от този тип.

По-подробно разглеждане на авторизацията в Kubernetes можете да намерите на страницата официалната документация. Вместо това (по-точно — в допълнение към това) ще дам примери, които илюстрират как работи.

Примери на RBAC сущности

Проста Role, която позволява да се получи списък и статус на pod’ове и да се следят в пространството от имена target-namespace:

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

Пример ClusterRole, което позволява да се получи списък и статус на pod’ове и да се следят в целия клъстер:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # секцията "namespace" не е необходима, тъй като ClusterRole обхваща целия клъстер
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Пример RoleBinding, което позволява на потребителя mynewuser да "чете" pod’ове в пространството от имена my-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: target-namespace
subjects:
- kind: User
  name: mynewuser # името на потребителя е чувствително към регистъра!
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # тук трябва да бъде “Role” или “ClusterRole”
  name: pod-reader # името на Role, което се намира в същото пространство от имена,
                   # или името на ClusterRole, използването на която
                   # искаме да разрешим на потребителя
  apiGroup: rbac.authorization.k8s.io

Аудит на събития

Схематично архитектурата на Kubernetes може да се представи по следния начин:

Азбука за сигурност в Kubernetes: автентикация, авторизация, аудит

Ключовият компонент на Kubernetes, отговорен за обработката на заявки, е api-server. Всички операции с клъстера преминават през него. Повече за тези вътрешни механизми можете да прочетете в статията „Какво се случва в Kubernetes при стартиране на kubectl run?».

Аудитът на системата е интересна функция в Kubernetes, която по подразбиране е деактивирана. Тя позволява да се регистрират всички обаждания към Kubernetes API. Както лесно можете да се досетите, през този API се извършват всички действия, свързани с контрола и промяната на състоянието на клъстера. Добро описание на възможностите може да се намери (както обикновено) в официалната документация K8s. По-долу ще се опитам да изложа темата по-просто.

И така, за да активирате аудит, трябва да предадем на контейнера в api-server три задължителни параметъра, подробно за които е обяснено по-долу:

  • --audit-policy-file=\/etc\/kubernetes\/policies\/audit-policy.yaml
  • --audit-log-path=\/var\/log\/kube-audit\/audit.log
  • --audit-log-format=json

Освен тези три необходими параметра, съществуват множество допълнителни настройки, свързващи се с аудита: от ротация на логовете до описания на webhook. Пример за параметри за ротация на логовете:

  • --audit-log-maxbackup=10
  • --audit-log-maxsize=100
  • --audit-log-maxage=7

Но няма да се спираме на тях — всички детайли могат да бъдат намерени в документацията за kube-apiserver.

Както вече споменахме, всички параметри се задават в манифеста с конфигурацията на api-server (по подразбиране /etc/kubernetes/manifests/kube-apiserver.yaml), в секцията command. Нека се върнем към трите задължителни параметъра и да ги разгледаме:

  1. audit-policy-file — път до YAML-файла с описанието на политиката (policy) на аудита. Към съдържанието му ще се върнем по-късно, а засега отбелязвам, че файлът трябва да е достъпен за четене от процеса на api-server. Затова е необходимо да го монтираме вътре в контейнера, за което можем да добавим следния код в съответните секции на конфигурацията:
      volumeMounts:
        - mountPath: \/etc\/kubernetes\/policies
          name: policies
          readOnly: true
      volumes:
      - hostPath:
          path: \/etc\/kubernetes\/policies
          type: DirectoryOrCreate
        name: policies
  2. audit-log-path — път до файл с лога. Пътят също трябва да бъде достъпен от процеса на api-server, затова аналогично описваме неговото монтиране:
      volumeMounts:
        - mountPath: \/var\/log\/kube-audit
          name: logs
          readOnly: false
      volumes:
      - hostPath:
          path: \/var\/log\/kube-audit
          type: DirectoryOrCreate
        name: logs
  3. audit-log-format — формат на логовете за одит. По подразбиране това е json, но е наличен и остарял текстов формат (legacy).

Политика на аудита

Сега за споменатия файл с описанието на политиката за логиране. Първото понятие на audit policy — това е level, нивото на логиране. Те могат да бъдат следните:

  • None — не логиране;
  • Metadata — логвайте метаданни на заявките: потребител, време на заявката, целеви ресурс (pod, namespace и т.н.), тип действие (verb) и т.н.;
  • Request — логвайте метаданни и съдържание на заявките;
  • RequestResponse — логвайте метаданни, съдържание на заявките и съдържание на отговорите.

Последните два нива (Request и RequestResponse) не логват заявки, които не са насочени към ресурси (обаждания към така наречените non-resources urls).

Също така всички заявки преминават през няколко етапа:

  • RequestReceived — етап, когато заявката е получена от обработчика и все още не е предадена по-нататък в поредицата от обработчици;
  • ResponseStarted — заглавията на отговора са изпратени, но преди изпращането на съдържанието на отговора. Генерира се за дългосрочни заявки (например, watch);
  • ResponseComplete — съдържанието на отговора е изпратено, повече информация да не бъде изпращана;
  • Panic — събитията се генерират, когато се открие извънредна ситуация.

За пропускане на някои етапи може да се използва omitStages.

В файла с политики можем да опишем няколко секции с различни нива на логване. Ще се приложи първото подходящо правило, намерено в описанието на policy.

Демонът kubelet следи за промени в манифеста с конфигурацията на api-server и при открива подобни променя перезарежда контейнера с api-server. Но има важен детайл: промените в файла с политики ще бъдат игнорирани от него. След направените промени в файла с политики ще бъде необходимо ръчно да се перезапусне api-server. Тъй като api-server е стартиран като static pod, командата kubectl delete няма да доведе до неговото перезапускане. Ще се наложи ръчно да се изпълни docker stop на kube-master-ите, където е променена политиката за одит:

docker stop $(docker ps | grep k8s_kube-apiserver | awk '{print $1}')

При активиране на одита е важно да запомните, че натоварването на kube-apiserver нараства. По-специално, увеличава се консумацията на памет за съхранение на контекста на заявките. Записването в лог започва след изпращането на заглавието на отговора. Освен това, натоварването зависи от конфигурацията на политиката за одит.

Примери за политики

Нека разгледаме структурата на файловете с политики с примери.

Ето прост файл policy, за да логвате всичко на ниво Metadata:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

В policy може да се посочи списък с потребители (Потребители и ServiceAccounts) и групи потребители. Например, така можем да игнорираме системните потребители, но да логваме всичко друго на ниво Request:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: None
    userGroups:
      - "system:serviceaccounts"
      - "system:nodes"
    users:
      - "system:anonymous"
      - "system:apiserver"
      - "system:kube-controller-manager"
      - "system:kube-scheduler"
  - level: Request

Съществува и възможност за описание на целевите:

  • пространства от имена (namespaces);
  • глаголи (verbs: get, актуализиране, delete и други);
  • ресурси (resources, а именно: pod, configmaps и т.н.) и групи ресурси (apiGroups).

Обърнете внимание! Ресурсите и групите ресурси (групи API, т.е. apiGroups), както и техните версии, инсталирани в клъстера, могат да бъдат получени с командите:

kubectl api-resources
kubectl api-versions

Следната audit policy е приведена като пример за добри практики в документацията на Alibaba Cloud:

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# Не записывайте стадию RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # Не записывать события, которые считаются малозначительными и не опасными:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # это api группа с пустым именем, к которой относятся
                  # основные ресурсы Kubernetes, называемые “core”
        resources: ["endpoints", "services"]
  - level: None
    users: ["system:unsecured"]
    namespaces: ["kube-system"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["configmaps"]
  - level: None
    users: ["kubelet"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["nodes"]
  - level: None
    userGroups: ["system:nodes"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["nodes"]
  - level: None
    users:
      - system:kube-controller-manager
      - system:kube-scheduler
      - system:serviceaccount:kube-system:endpoint-controller
    verbs: ["get", "update"]
    namespaces: ["kube-system"]
    resources:
      - group: "" # core
        resources: ["endpoints"]
  - level: None
    users: ["system:apiserver"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["namespaces"]
  # Не записывать обращения к URL с доступом только для чтения:
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /swagger*
  # Не записывать события, относящиеся к типу ресурсов “события”:
  - level: None
    resources:
      - group: "" # core
        resources: ["events"]
  # Ресурсы типа Secret, ConfigMap и TokenReview могут содержать  секретные данные,
  # поэтому записываем только метаданные связанных с ними запросов
  - level: Metadata
    resources:
      - group: "" # core
        resources: ["secrets", "configmaps"]
      - group: authentication.k8s.io
        resources: ["tokenreviews"]
  # Действия типа get, list и watch могут быть ресурсоёмкими; не записываем их
  - level: Request
    verbs: ["get", "list", "watch"]
    resources:
      - group: "" # core
      - group: "admissionregistration.k8s.io"
      - group: "apps"
      - group: "authentication.k8s.io"
      - group: "authorization.k8s.io"
      - group: "autoscaling"
      - group: "batch"
      - group: "certificates.k8s.io"
      - group: "extensions"
      - group: "networking.k8s.io"
      - group: "policy"
      - group: "rbac.authorization.k8s.io"
      - group: "settings.k8s.io"
      - group: "storage.k8s.io"
  # Уровень логирования по умолчанию для стандартных ресурсов API
  - level: RequestResponse
    resources:
      - group: "" # core
      - group: "admissionregistration.k8s.io"
      - group: "apps"
      - group: "authentication.k8s.io"
      - group: "authorization.k8s.io"
      - group: "autoscaling"
      - group: "batch"
      - group: "certificates.k8s.io"
      - group: "extensions"
      - group: "networking.k8s.io"
      - group: "policy"
      - group: "rbac.authorization.k8s.io"
      - group: "settings.k8s.io"
      - group: "storage.k8s.io"
  # Уровень логирования по умолчанию для всех остальных запросов
  - level: Metadata

Другой добър пример за политика за одит — профил, използван в GCE.

За бързо реагиране на събития от одита е възможно да се опише webhook. Този въпрос е обсъден в официалната документация, ще го оставя извън обсъждането на тази статия.

Итог

Статията предлага преглед на механизмите за основна защита в Kubernetes клъстери, които позволяват на потребителите да създават персонализирани акаунти, да разделят правата си и да регистрират действията си. Надявам се, че ще бъде полезна за тези, които срещат подобни въпроси в теорията или вече в практиката. Препоръчвам също да се запознаете със списъка на други материали по темата за безопасността в Kubernetes, предоставен в „P.S.“, — може би сред тях ще намерите необходимите подробности за актуалните за вас проблеми.

P.S.

Прочетете също в нашия блог:

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster