[Перевод] Журналы аудита Kubernetes: лучшие практики и настройка

fb07af4dea9fd3b6681ffa11b6905cb7.png

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 месяцев динамичного обучения. За это время вы сможете сменить профессию или вырасти в должности (доказано нашими студентами).

© Habrahabr.ru