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

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

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

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

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

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

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

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

Всеки запитване към API е свързано или с Service Account, или с User, или се счита за анонимно.

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

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

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

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

  • service account tokens — за Service Accounts;
  • 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-server на кластера):
      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 работата с сертификати стана значително по-лесна благодарение на алфа версията на поддръжката в утилитата kubeadm. Например, ето как може да изглежда генерирането на конфигурационен файл с потребителски ключове:

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

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

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

Пробийте по-дълбоко

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

Авторизация

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

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

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

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

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

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

Съществата Role и RoleBinding са ограничени до namespace, т.е. трябва да бъдат в рамките на едно пространство на имена. Въпреки това, 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, ниво на логване. Те могат да бъдат следните:

  • Няма — не логвай;
  • 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

В политиката могат да се посочват списъци с потребители (Потребители и 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, актуализиране, изтрий и др.);
  • ресурси (resources, именно: pod, configmaps и т.н.) и групи ресурси (apiGroups).

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

kubectl api-resources
kubectl api-versions

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

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# Не записвайте стадията RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # Не записвайте събития, считани за маловажни и неопасни:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # това е api group с празно име, към което принадлежат
                  # основните ресурси на 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"]
  # Не записвайте достъпи до read-only URLs:
  - 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