Kubernetes является фактическим лидером среди систем оркестрации. С ростом популярности микросервисной разработки Kubernetes проявил себя незаменимым инструментом для управления крупномасштабными приложениями и их развёртыванием. Правда, с обилием возможностей, которые предоставляет Kubernetes, этой системой подчас непросто управлять. В этой статье мы заострим внимание на журналах аудита Kubernetes, чтобы записи всех протекающих событий в нашем кластере можно было заполучить по щелчку пальцев.
Журнал аудита Kubernetes: что это и с чем его едят?
Если наш навык работы с Kubernetes ещё не утерян, то мы должны помнить, что все взаимодействия между составными частями системы, включая команды, выполняемые пользователями, являются вызовами REST API. API-сервер Kubernetes — это компонент, который обрабатывает все эти запросы. Так, при каждом запуске kubectl, эта команда по большому счёту служит обёрткой для API-вызова, посылаемого на API-сервер.
Журналы аудита фиксируют все эти API-обращения, адресованные API-серверу, включая API-вызовы, которые совершают разные пользователи, и другие вызовы, выполняемые различными компонентами Kubernetes. Подобные журналы — кладезь информации, ведь они предоставляют уйму полезных сведений об API-запросе, а именно: IP-адрес источника, время запроса, имя пользователя, который совершил запрос, тип этого запроса и ответ API-сервера, — красота да и только!
Для чего нужно настраивать журналы аудита Kubernetes?
Журналы аудита отражают события по мере их появления в кластере Kubernetes и служат незыблемой основой для обеспечения безопасности и соответствия всем требованиям и нормативам аудита. Настроив такой журнал надлежащим образом, мы можем мигом определить подозрительную активность в нашем кластере, будь то неудачные попытки входа в систему или желание заполучить наши секреты Kubernetes (Kubernetes Secrets). Аудит, или системный осмотр, позволяет нам оперативно принимать меры против вредоносной активности в противовес разрозненному управлению. Систематическое отслеживание содержимого журнала событий помогает также облегчить последствия неправильных настроек кластера и усилить его защиту.
Политика аудита Kubernetes
Запись в журналы аудита ведётся на основе настроенной политики аудита — она определяет, каким данным следует находиться в таком журнале и какие события должны быть в него внесены.
Установленные в политике аудита правила обрабатываются по порядку, и первое совпадающее правило выставляет уровень аудита события, с которым может быть записан каждый запрос. Ведение журнала аудита доступно для нижеследующих стадий:
RequestReceived: тут события автоматически создаются как только обработчик аудита получит запрос.
ResponseStarted: здесь — после отправки заголовков ответа, но до отправки его тела.
ResponseComplete: тело ответа было отправлено.
Panic: события, возникающие в результате появления критических ошибок.
Сбор информации о каждом событии зависит от уровня аудита, который мы установили — коротко их опишем:
None: не фиксировать события, соответствующие этому правилу.
Metadata: вносить метаданные событий в журнал регистрации, опуская сведения о теле запроса с телом ответа.
Request: регистрировать метаданные событий и тело запроса.
RequestResponse: заносить в файл отчёта тело запроса с телом ответа, включая метаданные событий.
Настраиваем аудит Kubernetes
Пришло время засучить-таки рукава и перейти к практике! Аудит изначально не задействован в кластере, который мы подняли самостоятельно. В случае с управляемым кластером Kubernetes (managed Kubernetes cluster) нам потребуется прошерстить техническую документацию нашего поставщика, чтобы узнать, активирована ли функция аудита по умолчанию. Используемый нами кластер имеет по одной копии control plane и узла (node); описанные ниже меры полностью применимы к minikube-кластерам и другим низкоуровневым кластерам Kubernetes (bare metal).
Шаг первый: подключаемся к control plane
Подключаемся к одной из control plane нод и создаём директорию для наших будущих журналов аудита с политикой аудита:
mkdir /etc/kubernetes/audit
Шаг второй: создаём политику аудита
Теперь нам предстоит создать файл политики аудита с именем /etc/kubernetes/audit/policy.yaml и следующим наполнением:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: None
verbs: ["get", "watch", "list"]
- level: None
resources:
- group: "" # core
resources: ["events"]
- level: None
users:
- "system:kube-scheduler"
- "system:kube-proxy"
- "system:apiserver"
- "system:kube-controller-manager"
- "system:serviceaccount:gatekeeper-system:gatekeeper-admin"
- level: None
userGroups: ["system:nodes"]
- level: RequestResponse
Шаг третий: добавляем желаемые элементы
Заносим следующий фрагмент кода в файл с именем /etc/kubernetes/manifests/kube-apiserver.yaml:
- --audit-policy-file=/etc/kubernetes/audit/policy.yaml
- --audit-log-path=/etc/kubernetes/audit/audit.log
- --audit-log-maxsize=500
- --audit-log-maxbackup=3
Листаем вниз до раздела volumes (тома) и добавляем в него наш том:
- hostPath:
path: /etc/kubernetes/audit
type: DirectoryOrCreate
name: audit
Монтируем этот том, внося следующие записи в группу volumeMounts:
- mountPath: /etc/kubernetes/audit
name: audit
С настройкой аудита разобрались! Поскольку мы видоизменили манифест kube-apiserver, наш kube-apiserver под будет создан заново. Убедившись, что под запущен и работает, создадим service account с помощью этой команды:
kubectl create sa test
Этот API-запрос будет зафиксирован в журнале аудита (/etc/kubernetes/audit/audit.log), где мы можем увидеть несколько записей подобного рода:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "RequestResponse",
"auditID": "a6029022-4ff0-4c54-97ed-4099d0ca1923",
"stage": "RequestReceived",
"requestURI": "/api/v1/namespaces/default/serviceaccounts?fieldManager=kubectl-create",
"verb": "create",
"user": {
"username": "kubernetes-admin",
"groups": ["system:masters", "system:authenticated"]
},
"sourceIPs": ["172.31.22.88"],
"userAgent": "kubectl/v1.23.6 (linux/amd64) kubernetes/ad33385",
"objectRef": {
"resource": "serviceaccounts",
"namespace": "default",
"apiVersion": "v1"
},
"requestReceivedTimestamp": "2022-07-31T08:36:48.679291Z",
"stageTimestamp": "2022-07-31T08:36:48.679291Z"
}
и
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "RequestResponse",
"auditID": "a6029022-4ff0-4c54-97ed-4099d0ca1923",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/default/serviceaccounts?fieldManager=kubectl-create",
"verb": "create",
"user": {
"username": "kubernetes-admin",
"groups": ["system:masters", "system:authenticated"]
},
"sourceIPs": ["172.31.22.88"],
"userAgent": "kubectl/v1.23.6 (linux/amd64) kubernetes/ad33385",
"objectRef": {
"resource": "serviceaccounts",
"namespace": "default",
"name": "test",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"code": 201
},
"requestObject": {
"kind": "ServiceAccount",
"apiVersion": "v1",
"metadata": {
"name": "test",
"creationTimestamp": null
}
},
"responseObject": {
"kind": "ServiceAccount",
"apiVersion": "v1",
"metadata": {
"name": "test",
"namespace": "default",
"uid": "d6ea858a-206d-4b4a-aca0-499e22f00729",
"resourceVersion": "1676",
"creationTimestamp": "2022-07-31T08:36:48Z"
}
},
"requestReceivedTimestamp": "2022-07-31T08:36:48.679291Z",
"stageTimestamp": "2022-07-31T08:36:48.684377Z",
"annotations": {
"authorization.k8s.io/decision": "allow",
"authorization.k8s.io/reason": ""
}
}
Содержимое полей auditID остаётся неизменным для обоих событий (a6029022-4ff0-4c54-97ed-4099d0ca1923), поскольку обе записи, первая из которых принадлежит стадии RequestReceived, а вторая — ResponseComplete, относятся к одному и тому же действию. Располагая этой информацией, мы можем вмиг определить, что это был за запрос и кто его сделал — поля sourceIPs, username, requestURI, responseStatus и responseObject нам в этом помогут.
Если в политике аудита изменить уровень аудита с RequestResponse на MetaData, то полей requestObject и responseObject мы не увидим. Чтобы увидеть поле requestObject, нам потребуется использовать Request в качестве уровня аудита, однако поле responseObject фиксироваться в файле журнала не будет!
Вернёмся к нашим событиям: 201-ый код HTTP-ответа поля responseStatus сигнализирует о создании одного или нескольких ресурсов на сервере в результате POST-запроса HTTP — в нашем случае это service account. Если пользователь с ограниченными правами попытается её создать, то в журнале аудита мы обнаружим содержимое такого рода:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "RequestResponse",
"auditID": "605e41ff-394a-4b8d-bd32-86ffa984d55a",
"stage": "RequestReceived",
"requestURI": "/api/v1/namespaces/default/serviceaccounts?fieldManager=kubectl-create",
"verb": "create",
"user": {
"username": "myuser",
"groups": ["Dev", "system:authenticated"]
},
"sourceIPs": ["172.31.22.88"],
"userAgent": "kubectl/v1.23.6 (linux/amd64) kubernetes/ad33385",
"objectRef": {
"resource": "serviceaccounts",
"namespace": "default",
"apiVersion": "v1"
},
"requestReceivedTimestamp": "2022-07-31T09:00:44.262246Z",
"stageTimestamp": "2022-07-31T09:00:44.262246Z"
}
и
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "RequestResponse",
"auditID": "605e41ff-394a-4b8d-bd32-86ffa984d55a",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/default/serviceaccounts?fieldManager=kubectl-create",
"verb": "create",
"user": {
"username": "myuser",
"groups": ["Dev", "system:authenticated"]
},
"sourceIPs": ["172.31.22.88"],
"userAgent": "kubectl/v1.23.6 (linux/amd64) kubernetes/ad33385",
"objectRef": {
"resource": "serviceaccounts",
"namespace": "default",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"status": "Failure",
"reason": "Forbidden",
"code": 403
},
"responseObject": {
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "serviceaccounts is forbidden: User \"myuser\" cannot create resource \"serviceaccounts\" in API group \"\" in the namespace \"default\"",
"reason": "Forbidden",
"details": {
"kind": "serviceaccounts"
},
"code": 403
},
"requestReceivedTimestamp": "2022-07-31T09:00:44.262246Z",
"stageTimestamp": "2022-07-31T09:00:44.274511Z",
"annotations": {
"authorization.k8s.io/decision": "forbid",
"authorization.k8s.io/reason": ""
}
}
На выходе мы видим, что пользователь myuser попытался создать service account, не имея для этого достаточно прав.
Содержимое событий представлено в формате JSON, поэтому мы можем воспользоваться утилитой jq для поиска записей в журнале аудита:
tail -f /etc/kubernetes/audit/audit.log | jq '.| select(.responseStatus.code | contains(403) )'
tail -f /etc/kubernetes/audit/audit.log | jq '.| select(.user.username | contains("myuser") )'
Практические рекомендации по работе с аудитом Kubernetes
Брать на вооружение передовые методы аудита Kubernetes, когда дело касается прода — наша святая обязанность, поэтому вкратце их перечислим:
создаём комплексную политику аудита, отталкиваясь от наших требований к сбору данных
используем webhook backends для отправки результатов аудита на эндпоинты, чтобы не хранить файлы журнала на диске
контролируем доступ к логам аудита, чтобы их нельзя было подделать
настраиваем алерты и рисуем графики на основе журналов аудита, чтобы не упустить важные события, такие как удаление секретов и т.д.
Заключительные соображения
Аудит играет важную роль в безопасности кластеров Kubernetes: он помогает нам поднять правильно настроенный кластер и обезопасить его, а также показывает целостную картину событий, протекающих в таком кластере. Из этой статьи мы получили представление о настройке и специфике аудита, выделив его значимость.
Безопасность — это вечный процесс, где всегда найдётся место улучшениям. Этот материал послужит добротным подспорьем в укреплении наших кластеров и откроет пути для дальнейших исследований в этом направлении.
В проде сбои не нужны, поэтому важно выбрать эффективный инструмент управления логами, чтобы всегда оставаться в курсе общего состояния системы и её производительности. Kubernetes предлагает нам изящный способ управления нашими ресурсами для масштабирования облачных приложений в нужный момент. Мониторинг и управление ресурсами идут рука об руку, поэтому для действенного контроля над системой стоит обратить внимание на SigNoz — букет из метрик, трассировки и логов с открытым исходным кодом.
Базовые настройки Kubernetes и тонкости работы на практике мы разбираем на курсе DevOps Upgrade.
Программа рассчитана на 5 месяцев динамичного обучения. За это время вы сможете сменить профессию или вырасти в должности (доказано нашими студентами).