В предыдущей статье мы рассмотрели архитектуру Kubernetes, и обсудили те виды уязвимостей, которые можно встретить в его программных компонентах и их настройках. Теперь перейдем к практическим аспектам защиты и поговорим о том, как обнаружить уязвимости и как грамотно от них защититься.

Искать уязвимости в настройках K8s самостоятельно занятие не слишком благодарное, ведь для этого нужно знать каким уязвимостям подвержена та или иная версия, какие ошибки можно встретить в настройках и многое другое. Гораздо удобнее осуществлять поиск уязвимостей в автоматическом режиме с помощью сканеров уязвимостей. В качестве примера таких сканеров я приведу пару проектов.

Kube-bench

Начнем с простой утилиты kube-bench. Эта утилита предназначена для проверки того, насколько безопасно развернута инфраструктура Kubernetes. В качестве критериев для проверки используется документ CIS Kubernetes Benchmark. Подробнее об этом документе мы поговорим чуть позже, когда будем рассматривать построение защиты, а пока вернемся к сканерам.

Итак, в kube-bench для проверки используются специальные тесты, которые настраиваются с помощью файлов YAML, что упрощает обновление этого инструмента по мере развития спецификаций тестов.

По умолчанию kube-bench пытается автоматически определить запущенную версию Kubernetes и сопоставить ее с соответствующей версией CIS Benchmark. Например, версия Kubernetes 1.15 сопоставляется с эталонной версией CIS cis-1.15, действительной для Kubernetes 1.15.

Утилита также пытается идентифицировать компоненты, запущенные на сервере, K8s для того, чтобы понимать, какие тесты следует запускать (например, запускать тесты главного узла только в том случае, если на нем запущен сервер API).

Утилиту можно запустить внутри пода как отдельную задачу, но ему потребуется доступ к пространству имен PID хоста для проверки запущенных процессов, а также доступ к некоторым каталогам на хосте, где хранятся конфигурационные файлы и другие файлы.

В качестве примера подготовим job.yaml файл, который может быть применен для выполнения проверок в качестве задачи. Как можно понять далее из этого файла, для его работы потребуется монтировать несколько внешних каталогов.

---
apiVersion:   batch/v1
kind: Job
metadata:
    name: kube-bench
spec:
    template:
      metadata:
        labels:
          app: kube-bench
      spec:
        hostPID: true
        containers:
          - name: kube-bench
            image: aquasec/kube-bench:v0.6.8
            command:   ["kube-bench"]
            volumeMounts:
              - name: var-lib-etcd
                mountPath: /var/lib/etcd
                readOnly: true
              - name: var-lib-kubelet
                mountPath: /var/lib/kubelet
                readOnly: true
              - name: var-lib-kube-scheduler
                mountPath: /var/lib/kube-scheduler
                readOnly:   true
              - name: var-lib-kube-controller-manager
                mountPath: /var/lib/kube-controller-manager
                readOnly:   true
              - name: etc-systemd
                mountPath: /etc/systemd
                readOnly: true
              - name: lib-systemd
                mountPath: /lib/systemd/
                readOnly: true
              - name: srv-kubernetes
                mountPath: /srv/kubernetes/
                readOnly: true
              - name: etc-kubernetes
                mountPath: /etc/kubernetes
                readOnly: true
                # /usr/local/mount-from-host/bin is mounted to access kubectl / kubelet, for   auto-detecting the Kubernetes version.
                # You can omit this mount if you specify --version as part of the command.
              - name:   usr-bin
                mountPath: /usr/local/mount-from-host/bin
                readOnly:   true
              - name: etc-cni-netd
                mountPath: /etc/cni/net.d/
                readOnly:   true
              - name: opt-cni-bin
                mountPath: /opt/cni/bin/
                readOnly: true
        restartPolicy: Never
        volumes:
          - name: var-lib-etcd
            hostPath:
              path: "/var/lib/etcd"
          - name: var-lib-kubelet
            hostPath:
              path: "/var/lib/kubelet"
        -   name: var-lib-kube-scheduler
            hostPath:
              path: "/var/lib/kube-scheduler"
        -   name: var-lib-kube-controller-manager
            hostPath:
              path: "/var/lib/kube-controller-manager"
        - name: etc-systemd
            hostPath:
              path: "/etc/systemd"
          - name: lib-systemd
            hostPath:
              path: "/lib/systemd"
          - name: srv-kubernetes
            hostPath:
              path: "/srv/kubernetes"
          - name: etc-kubernetes
            hostPath:
              path: "/etc/kubernetes"
          - name: usr-bin
            hostPath:
              path: "/usr/bin"
          - name: etc-cni-netd
            hostPath:
              path: "/etc/cni/net.d/"
        - name: opt-cni-bin
            hostPath:
              path: "/opt/cni/bin/"

Запустить эту задачу на выполнение можно следующим образом:

$ kubectl apply -f job.yaml

style='background:white'>job.batch/kube-bench created<o:p></o:p>

Далее, проверим корректность запуска пода:

$ kubectl get pods

NAME                      READY   STATUS              RESTARTS   AGE

kube-bench-j76s9   0/1     ContainerCreating   0          3s

Через несколько секунд проверяем что под работает:

# Wait for a few seconds for the job to complete

$ kubectl get pods

NAME                      READY   STATUS      RESTARTS   AGE

kube-bench-j76s9   0/1     Completed   0          11s

И смотрим результаты сканирования в логах пода:

# The results are held in the pod's logs

kubectl logs kube-bench-j76s9

[INFO] 1 Master Node Security Configuration

[INFO] 1.1 API Server

...

На рисунке ниже приведены результаты реального сканирования K8s с помощью Kube-bench.

Сканер Checkov

Если kube-bench является небольшой утилитой, работающей непосредственно в качестве задачи Kubernetes, то сканер Checkov, является гораздо более мощным инструментом.

Checkov не только проверяет манифест Kubernetes, но также позволяет выявлять неправильные настройки инфраструктуры, такие как общедоступные ресурсы, а также помогает поддерживать лучшие практики безопасности облачной инфраструктуры.

Например, с помощью проверок манифестов Kubernetes можно поддерживать рекомендации по обеспечению безопасности ресурсов Kubernetes и выявлять такие проблемы, как чрезмерно привилегированные контейнеры, неправильные методы жизненного цикла образов, неправильная настройка QoS, проверки работоспособности и многое другое.

Данный сканер имеет бесплатную редакцию, предназначенную для небольших организаций до 50 узлов. Для большего количества узлов предлагается использовать платные редакции, в которых сканер взаимодействует с облачным сервисом bridgecrew и позволяет в автоматическом режиме не только выявлять, но и устранять уязвимости, обеспечивая тем, самым полный цикл управления уязвимостями.

Для установки сканера необходимо выполнить следующие команды:

pip install checkov

#install via homebrew:

brew tap bridgecrewio/checkov https://github.com/bridgecrewio/checkov

brew update

brew install checkov

Далее, для запуска сканирования выполним:

checkov -l --framework kubernetes

Посмотрим работу сканера на нескольких примерах.

git clone https://github.com/kubernetes/examples

checkov -d examples/

Результат проверок представляет собой список пунктов из все того же CIS Kubernetes Benchmark и статус их проверки. Во фрагменте ниже мы видим одно успешное прохождение проверки и две уязвимости, при этом для каждой из них приводится проблемный фрагмент JSON-файла:

…

kubernetes scan results:

Passed checks: 1650, Failed checks: 2502, Skipped checks: 0

Check: CKV_K8S_27: "Do not expose the docker daemon socket to containers"

        PASSED for resource: Deployment.selenium-hub.default

        File: /staging/selenium/selenium-hub-deployment.yaml:1-37

Check: CKV_K8S_8: "Liveness Probe Should be Configured"

        FAILED for resource: Pod.mongo.default (container 0)

        File: /staging/meteor/mongo-pod.json:22-36

 

                22 |       {

                23 |         "name": "mongo",

                24 |         "image": "mongo:latest",

                25 |         "ports": [

                26 |           {

                27 |             "name": "mongo",

                28 |             "containerPort": 27017

                29 |           }

                30 |         ],

                31 |         "volumeMounts": [

                32 |           {

                33 |             "name": "mongo-disk",

                34 |             "mountPath": "/data/db"

                35 |           }

                36 |         ]

 

Check: CKV_K8S_12: "Memory requests should be set"

        FAILED for resource: Pod.mongo.default (container 0)

        File: /staging/meteor/mongo-pod.json:22-36

 

                22 |       {

                23 |         "name": "mongo",

                24 |         "image": "mongo:latest",

                25 |         "ports": [

                26 |           {

                27 |             "name": "mongo",

                28 |             "containerPort": 27017

                29 |           }

                30 |         ],

                31 |         "volumeMounts": [

                32 |           {

                33 |             "name": "mongo-disk",

                34 |             "mountPath": "/data/db"

                35 |           }

                36 |         ]

При необходимости, ненужные проверки можно отключить, например в случае, когда тот или иной проверяемый сервис не используется в рабочей среде Kubernetes.

Например, приведенная ниже команда пропускает проверку CKV_AWS_20.

checkov -d . --skip-check CKV_AWS_20

Строим защиту

Со средствами для поиска уязвимостей я думаю все понятно, помимо двух описанных выше на просторах Интернета можно найти описания еще десятка аналогичных утилит. Но теперь перейдем к тому, как обеспечить защиту K8s.

Начнем с документа CIS Kubernetes Benchmark.

Основными раздела, касающимися Kubernetes в этом документе являются следующие:

  1. Enable Kubernetes Role-Based Access Control (RBAC).

  2. Use Third-Party Authentication for API Server.

  3. Protect etcd with TLS, Firewall and Encryption.

  4. Isolate Kubernetes Nodes.

  5. Monitor Network Traffic to Limit Communications.

  6. Use Process Whitelisting.

  7. Turn on Audit Logging.

  8. Keep Kubernetes Version Up to Date.

  9. Lock Down Kubelet.

Рассмотрим требования каждого из разделов более подробно.

Ролевая модель

Ролевая модель доступа (RBAC) может помочь вам определить, кто имеет доступ к API Kubernetes и какие у них есть разрешения. RBAC обычно включен по умолчанию в Kubernetes 1.6 и выше. Поскольку Kubernetes объединяет контроллеры авторизации, при включении RBAC необходимо также отключить устаревшее управление доступом на основе атрибутов (ABAC).

При использовании RBAC отдавайте предпочтение разрешениям, специфичным для пространства имен, а не разрешениям для всего кластера. Даже при отладке не предоставляйте права администратора кластера. Безопаснее разрешать доступ только тогда, когда это необходимо в вашей конкретной ситуации.

Для включения RBAC необходимо запустить API сервер с флагом --authorization-mode flag как в примере ниже:

kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options

Вот пример настройки роли для дефолтного пространства имен, которая может использоваться для предоставления прав на чтение для пода:

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

  namespace: default

  name: pod-reader

rules:

- apiGroups: [""] # "" indicates the core API group

  resources: ["pods"]

  verbs: ["get", "watch", "list"]

Более подробно о настройках ролей можно почитать на сайте.

Использование сторонней аутентификации для API сервера

Рекомендуется интегрировать Kubernetes со сторонним поставщиком аутентификации. Это обеспечивает дополнительные функции безопасности, такие как многофакторная аутентификация, и гарантирует, что kube-apiserver не изменяется при добавлении или удалении пользователей. Если возможно, убедитесь, что управление пользователями не осуществляется на уровне API. Вы также можете использовать протокол OAuth 2.0, и провайдер Dex.

Защита etcd

Etcd – это распределенное хранилище значений ключей, которое используется для хранения состояния кластера. Поскольку etc хранит состояние кластера и его секретные данные, он является чувствительным ресурсом и привлекательной мишенью для злоумышленников. Если неавторизованные пользователи получат доступ к etcd, они могут захватить весь кластер. Доступ на чтение также представляет опасность, поскольку злоумышленники могут использовать его для повышения привилегий.

Для настройки взаимодействия клиент-сервер по TLS для etcd, используйте следующие параметры конфигурации:

  • cert-file=: сертификат, используемый для TLS;

  • --key-file=: незашифрованный ключ сертификата;

  • --client-cert-auth: Указывает на то, что etcd должен проверять входящие HTTPS запросы на наличие подписи доверенного CA;

  • --trusted-ca-file=<path>: Доверенный центр сертификации;

  • --auto-tls: Использовать самоподписанный сертификат для клиентских подключений.

Для настройки TLS в соединениях etcd сервер-сервер используйте следующие настройки:

  • --peer-cert-file=<path>: сертификат используемый для SSL/TLS соединений между серверами;

  • --peer-key-file=<path>: незашифрованный ключ сертификата;

  • --peer-client-cert-auth: когда эта опция установлена, etcd проверяет валидность подписей клиентских сертификатов для всех входящих запросов;

  • --peer-trusted-ca-file=<path>: Доверенный центр сертификации;

  • --peer-auto-tls: Использовать автоматически сгенерированный самоподписанный сертификат для соединений типа точка-точка.

Также, необходимо установить межсетевой экран между API сервером и etcd кластером.

Изоляция узлов Kubernetes

При построении сетевой архитектуры K8s следует помнить, что узлы Kubernetes должны находиться в отдельной сети и не должны напрямую подключаться к общедоступным сетям. Еще лучше, избегать прямых подключений к общей корпоративной сети. Но для этого необходимо полностью разнести в отдельные подсети трафик управления Kubernetes и трафик данных изолировав их друг от друга. Сделать это можно с помощью создания ACL на межсетевом экране с разделением трафика на отдельные подсети. Более подробно по настройке сетевых политик можно прочесть в документе.

Мониторинг сетевого трафика

Контейнерные приложения обычно широко используют кластерные сети. Наблюдайте за активным сетевым трафиком и сравнивайте его с трафиком, разрешенным сетевой политикой Kubernetes, чтобы понять, как взаимодействует ваше приложение, и выявить аномальные связи.

В то же время, если вы сравните активный трафик с разрешенным трафиком, вы можете определить сетевые политики, которые не используются активно рабочими нагрузками кластера. Эта информация может быть использована для дальнейшего усиления разрешенной сетевой политики, удаления ненужных подключений для уменьшения поверхности атаки.

Настройка белого списков процессов

Белый список процессов — это эффективный способ выявления неожиданно запущенных процессов. Сначала необходимо понаблюдать за приложением в течение определенного периода времени, чтобы определить все процессы, запущенные при нормальном поведении приложения. Затем можно использовать этот список в качестве своего белого списка для будущего анализа корректности поведения приложения. Неожиданное появление сторонних процессов, без внесения явных изменений в работу приложения явный признак того, что с приложением что-то не так.

Включение журналов аудита

Ведение журнала аудита должно быть обязательно включено, для того, чтобы отслеживать  необычные или нежелательные вызовы API, особенно неудачные попытки аутентификации. Отказ в авторизации может означать, что злоумышленник пытается использовать украденные учетные данные или подобрать корректный пароль.

При передаче файлов на kube-apiserver вы можете использовать флаг –audit-policy-file, чтобы включить ведение журнала аудита, а также точно определить, какие события должны регистрироваться. Можно установить один из следующих уровней ведения журнала:

  • No, только метаданные,

  • Request, который регистрирует метаданные и запрос, но не ответы,

  • RequestResponse, который регистрирует все три.

Регулярное обновление компонентов

Регулярно обновляйте компоненты инфраструктуры Kubernetes. Несмотря на очевидность данного совета, многие по-прежнему им пренебрегают.

На верхнем уровне процесс обновления состоит из следующих шагов:

  • Обновления основного узла управления.

  • Обновления дополнительных узлов управления.

  • Обновления рабочих узлов.

Процесс обновления компонентов не является простой задачей, поэтому за техническими подробностями можно рекомендовать обратиться к документации.

Безопасная настройка агента Kubelet

Kubelet — это агент, работающий на каждом узле, который взаимодействует с средой выполнения контейнера для запуска модулей и отчетов о показателях для узлов и модулей. Каждый kubelet в кластере предоставляет интерфейс API, который вы можете использовать для запуска и остановки модулей и выполнения других операций. Если неавторизованный пользователь получит доступ к этому API (на любом узле) и сможет запускать код в кластере, он может скомпрометировать весь кластер.

Вот основные действия, которые можно выполнить для защиты Kubelet:

Отключите анонимный доступ к агенту с помощью --anonymous-auth=false, чтобы запросы, не прошедшие проверку подлинности, получали ответ с ошибкой. Чтобы сделать это, сервер API должен идентифицировать себя с kubelet. Это можно сделать, добавив флаги -kubelet-clientcertificate и --kubelet-client-key.

Для параметра --authorization mode необходимо установить значение, отличное от AlwaysAllow, чтобы убедиться, что все поступающие запросы авторизованы.

Включите NodeRestriction в параметр API server –admission-control, чтобы ограничить разрешения kubelet. Это позволяет только kubelet изменять модули, привязанные к его собственному объекту узла.

Установите --read-only-port=0, чтобы закрыть порты, доступные только для чтения. Это предотвращает доступ анонимных пользователей к информации о выполняемых рабочих нагрузках. Этот порт не позволяет хакерам вносить изменения в кластере, но может быть использован на этапе разведки атаки.

Отключите параметр cAdvisor, который использовался в старых версиях Kubernetes для предоставления метрик и был заменен статистикой API Kubernetes. Установите -cadvisor-port=0, чтобы избежать раскрытия информации о запущенных рабочих нагрузках. Это значение по умолчанию для Kubernetes версии v1.11.

Заключение

В этой достаточно объемной статье мы рассмотрели основные моменты, связанные с автоматизацией поиска уязвимостей в кластере Kubernetes, а также дали базовые рекомендации по настройке безопасности компонентов K8s.


Всех желающих приглашаем на открытое занятие в Otus «От виртуализации к контейнеризации. Введение в Docker». На нем мы попытаемся разобраться в контейнеризации и рассмотрим ее отличие от виртуализации. Рассмотрим самый популярный инструмент по работе с контейнеризацией — Docker. Регистрация доступна по ссылке.

Комментарии (0)