
Рано или късно в експлоатацията на всяка система възниква въпросът за сигурността: осигуряване на аутентификация, разделение на права, одит и други задачи. За 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
След гореспоменатите манипулации, в файла
- описание на кластера (посочете адреса и местоположението на файла на сертификата CA за конкретната инсталация на кластера):
.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 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;
- , в която въпросите за сертификатите са разгледани практически.
- по аутентификация в Kubernetes.
Авторизация
Авторизираният профил по подразбиране няма права за действия в клъстера. За предоставянето на разрешения в Kubernetes е реализиран механизъм за авторизация.
До версия 1.6 в Kubernetes беше използван тип авторизация, наречен ABAC (Контрол на достъпа, базиран на атрибути). Подробности за него можете да намерите в . В момента този подход се счита за остарял (legacy), но все пак можете да го използвате едновременно с други типове авторизация.
Актуалният (и по-гъвкав) метод за разделение на правата за достъп до клъстера се нарича RBAC (). Той беше обявен за стабилен с версия . 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, отговорен за обработката на заявки, е api-server. Всички операции с клъстера преминават през него. Повече за тези вътрешни механизми можете да прочетете в статията „».
Аудитът на системата е интересна функция в 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
Но няма да се спираме на тях — всички детайли могат да бъдат намерени в .
Както вече споменахме, всички параметри се задават в манифеста с конфигурацията на api-server (по подразбиране /etc/kubernetes/manifests/kube-apiserver.yaml), в секцията command. Нека се върнем към трите задължителни параметъра и да ги разгледаме:
-
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 -
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 -
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 е стартиран като , командата 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 е приведена като пример за добри практики в :
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Другой добър пример за политика за одит — .
За бързо реагиране на събития от одита е възможно да се опише webhook. Този въпрос е обсъден в , ще го оставя извън обсъждането на тази статия.
Итог
Статията предлага преглед на механизмите за основна защита в Kubernetes клъстери, които позволяват на потребителите да създават персонализирани акаунти, да разделят правата си и да регистрират действията си. Надявам се, че ще бъде полезна за тези, които срещат подобни въпроси в теорията или вече в практиката. Препоръчвам също да се запознаете със списъка на други материали по темата за безопасността в Kubernetes, предоставен в „P.S.“, — може би сред тях ще намерите необходимите подробности за актуалните за вас проблеми.
P.S.
Прочетете също в нашия блог:
- «»;
- «»;
- «»;
- «»;
- «».
Източник: habr.com
