- PVSM.RU - https://www.pvsm.ru -

Рано или поздно в эксплуатации любой системы встаёт вопрос безопасности: обеспечения аутентификации, разделения прав, аудита и других задач. Для Kubernetes уже создано множество решений [1], которые позволяют добиться соответствия стандартам даже в весьма требовательных окружениях… Этот же материал посвящён базовым аспектам безопасности, реализованным в рамках встроенных механизмов K8s. В первую очередь он будет полезен тем, кто начинает знакомиться с Kubernetes, — как отправная точка для изучения вопросов, связанных с безопасностью.
В Kubernetes есть два типа пользователей:
Основное отличие этих типов в том, что для Service Accounts существуют специальные объекты в Kubernetes API (они так и называются — ServiceAccounts), которые привязаны к пространству имён и набору авторизационных данных, хранящихся в кластере в объектах типа Secrets. Такие пользователи (Service Accounts) предназначены в основном для управления правами доступа к Kubernetes API процессов, работающих в кластере Kubernetes.
Обычные же Users не имеют записей в Kubernetes API: управление ими должно осуществляться внешними механизмами. Они предназначены для людей или процессов, живущих вне кластера.
Каждый запрос к API привязан либо к Service Account, либо к User, либо считается анонимным.
Аутентификационные данные пользователя включают в себя:
Kubernetes может использовать большое количество механизмов аутентификации: сертификаты X509, Bearer-токены, аутентифицирующий прокси, HTTP Basic Auth. При помощи этих механизмов можно реализовать большое количество схем авторизации: от статичного файла с паролями до OpenID OAuth2.
Более того, допускается использование нескольких схем авторизации одновременно. По умолчанию в кластере используются:
Вопрос про управление ServiceAccounts выходит за рамки данной статьи, а желающим подробнее ознакомиться с этим вопросом рекомендую начать со страницы официальной документации [2]. Мы же рассмотрим подробнее вопрос работы сертификатов X509.
Классический способ работы с сертификатами предполагает:
mkdir -p ~/mynewuser/.certs/
openssl genrsa -out ~/.certs/mynewuser.key 2048
openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company"
/etc/kubernetes/pki/ca.key):
openssl x509 -req -in ~/.certs/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/.certs/mynewuser.crt -days 500
kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
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-authorityclient-certificateclient-key
Для этого можно закодировать указанные в них файлы при помощи base64 и прописать их в конфиге, добавив в название ключей суффикс -data, т.е. получив certificate-authority-data и т.п.
С релизом Kubernetes 1.15 [3] работа с сертификатами стала значительно проще благодаря альфа-версии её поддержки в утилите kubeadm [4]. Например, вот как теперь может выглядеть генерация конфигурационного файла с ключами пользователя:
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 (Attribute-based access control). Подробности о нём можно найти в официальной документации [8]. В настоящее время этот подход считается устаревшим (legacy), однако вы всё ещё можете использовать его одновременно с другими типами авторизации.
Актуальный же (и более гибкий) способ разделения прав доступа к кластеру называется RBAC (Role-based access control [9]). Он был объявлен стабильным с версии Kubernetes 1.8 [10]. 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), но их рассмотрение оставим за рамками материала.
К слову, мы уже публиковали статью [11] с достаточно подробным рассказом о принципах и особенностях работы с 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, что позволяет создать набор типовых разрешений и управлять доступом с их помощью.
Роли описывают права при помощи наборов правил, содержащих:
kubectl api-resources;pod, namespace, deployment и т.п.);set, update и т.п.).resourceNames) — для случая, когда нужно предоставить доступ к какому-то определённому ресурсу, а не ко всем ресурсам этого типа.Более подробный разбор авторизации в Kubernetes можно найти на странице официальной документации [13]. Вместо этого (а точнее — в дополнение к этому) приведу примеры, которые иллюстрируют её работу.
Простая 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, что находится в том же namespace,
# или имя ClusterRole, использование которой
# хотим разрешить пользователю
apiGroup: rbac.authorization.k8s.io
Схематично архитектуру Kubernetes можно представить следующим образом:

Ключевой компонент Kubernetes, отвечающий за обработку запросов, — api-server. Все операции над кластером проходят через него. Подробнее об этих внутренних механизмах можно почитать в статье «Что происходит в Kubernetes при запуске kubectl run? [14]».
Аудит системы — интересная фича в Kubernetes, которая по умолчанию выключена. Она позволяет логировать все обращения к Kubernetes API. Как легко догадаться, через этот API производятся все действия, связанные с контролем и изменением состояния кластера. Хорошее описание её возможностей можно (как обычно) найти в официальной документации [15] 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 [16].
Как уже упоминалось, все параметры выставляются в манифесте с конфигурацией api-server (по умолчанию /etc/kubernetes/manifests/kube-apiserver.yaml), в секции command. Вернемся к 3 обязательным параметрам и разберём их:
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’a, поэтому аналогично описываем его монтирование:
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. Но есть важная деталь: изменения в файле policy будут им игнорироваться. После внесения изменений в файл policy потребуется перезапустить api-server вручную. Поскольку api-server запущен как static pod [17], команда kubectl delete не приведёт к его перезапуску. Придется вручную сделать docker stop на kube-master’ах, где изменена политика аудита:
docker stop $(docker ps | grep k8s_kube-apiserver | awk '{print $1}')
При включении аудита важно помнить, что на kube-apiserver повышается нагрузка. В частности, увеличивается потребление памяти для хранения контекста запросов. Запись в лог начинается только после отправки заголовка ответа. Также нагрузка зависит от конфигурации политики аудита.
Разберём структуру файлов policy на примерах.
Вот простой файл policy, чтобы логировать всё на уровне Metadata:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
В policy можно указывать перечень пользователей (Users и 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);get, update, delete и прочие);pod, configmaps и т.п.) и группы ресурсов (apiGroups).Обратите внимание! Ресурсы и группы ресурсов (группы API, т.е. apiGroups), а также их версии, установленные в кластере, можно получить при помощи команд:
kubectl api-resources
kubectl api-versions
Следующий audit policy приведён в качестве демонстрации лучших практик в документации Alibaba Cloud [18]:
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
Другой хороший пример audit policy — профиль, используемый в GCE [19].
Для оперативного реагирования на события аудита есть возможность описать webhook. Этот вопрос раскрыт в официальной документации [20], оставлю его за рамками данной статьи.
В статье дан обзор механизмов базового обеспечения безопасности в кластерах Kubernetes, позволяющих создавать персонифицированные учетные записи пользователям, разделять их права, а также регистрировать их действия. Надеюсь, он пригодится тем, кто столкнулся с такими вопросами в теории или уже на практике. Рекомендую также ознакомиться со списком других материалов по теме безопасности в Kubernetes, что приведен в «P.S.», — возможно, среди них вы найдёте нужные подробности по актуальным для вас проблемам.
Читайте также в нашем блоге:
Автор: Oleg Voznesensky
Источник [24]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/bezopasnost/331362
Ссылки в тексте:
[1] множество решений: https://habr.com/ru/company/flant/blog/465141/
[2] страницы официальной документации: https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/
[3] Kubernetes 1.15: https://habr.com/ru/company/flant/blog/456084/
[4] утилите kubeadm: https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-alpha/
[5] отдельная статья: https://kubernetes.io/docs/concepts/cluster-administration/certificates/
[6] хорошая статья от Bitnami: https://docs.bitnami.com/kubernetes/how-to/configure-rbac-in-your-kubernetes-cluster/
[7] общая документация: https://kubernetes.io/docs/reference/access-authn-authz/authentication/
[8] официальной документации: https://kubernetes.io/docs/reference/access-authn-authz/abac/
[9] Role-based access control: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
[10] Kubernetes 1.8: https://habr.com/ru/company/flant/blog/338230/
[11] статью: https://habr.com/ru/company/flant/blog/422801/
[12] официальную документацию: https://kubernetes.io/docs/reference/using-api/api-overview/#api-groups
[13] официальной документации: https://kubernetes.io/docs/reference/access-authn-authz/authorization/
[14] Что происходит в Kubernetes при запуске kubectl run?: https://habr.com/ru/company/flant/blog/342658/
[15] официальной документации: https://kubernetes.io/docs/tasks/debug-application-cluster/audit/
[16] документации по kube-apiserver: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/
[17] static pod: https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/
[18] документации Alibaba Cloud: https://www.alibabacloud.com/help/doc-detail/91406.htm
[19] профиль, используемый в GCE: https://github.com/kubernetes/kubernetes/blob/0ef45b4fcf7697ea94b96d1a2fe1d9bffb692f3a/cluster/gce/gci/configure-helper.sh#L722-L862
[20] официальной документации: https://kubernetes.io/docs/tasks/debug-application-cluster/audit/#webhook-backend
[21] Введение в сетевые политики Kubernetes для специалистов по безопасности: https://habr.com/ru/company/flant/blog/443190/
[22] 9 лучших практик по обеспечению безопасности в Kubernetes: https://habr.com/ru/company/flant/blog/436300/
[23] 11 способов (не) стать жертвой взлома в Kubernetes: https://habr.com/ru/company/flant/blog/417905/
[24] Источник: https://habr.com/ru/post/468679/?utm_source=habrahabr&utm_medium=rss&utm_campaign=468679
Нажмите здесь для печати.