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

Обеспечение сетевой безопасности в кластере Kubernetes

Обеспечение сетевой безопасности в кластере Kubernetes - 1

Сетевые политики (Network Policies) — это новая функциональность Kubernetes, которая за счет создания брандмауэров позволяет настроить сетевое взаимодействие между группами подов и других узлов сети. В этом руководстве я постараюсь объяснить особенности, не описанные в официальной документации по сетевым политикам Kubernetes.

Функциональность сетевых политик стабилизировалась в Kubernetes 1.7. В этой статье их работа объясняется в теории и на практике. При желании вы можете сразу перейти к репозиторию с примерами kubernetes-networkpolicy-tutorial [1] или к документации [2].

Что можно делать с сетевыми политиками

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

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

В настоящее время исходящий от подов трафик таким образом контролировать невозможно. Эта функциональность запланирована в Kubernetes 1.8.

При этом проект с открытым исходным кодом под названием Istio [3] является хорошей альтернативой с поддержкой фильтрации исходящего трафика, а также многими другими возможностями, включая встроенную поддержку Kubernetes.

Чем хороши сетевые политики

Сетевые политики — это еще одно имя для используемых в сфере ИТ уже не одно десятилетие списков контроля доступа (англ. ACL). В кластере Kubernetes с их помощью настраиваются списки контроля доступа для подов. Так же, как и все остальные ресурсы в кластере Kubernetes, сетевые политики настраиваются с помощью декларативных манифестов. Они являются частью приложения, расположены в его репозитории и разворачиваются в Kubernetes вместе с приложением.

Сетевые политики применяются практически в реальном времени. Если между подами есть открытые соединения, применение новой политики, запрещающей такие соединения, приведет к их немедленному разрыву. Правда, за такую оперативность приходится расплачиваться небольшими потерями производительности. См. более подробную информацию и результаты эталонных тестов в этой статье [4].

Примеры использования

Ниже представлено несколько распространенных примеров использования сетевых политик Kubernetes. Дополнительные примеры и соответствующие манифесты можно найти на GitHub: kubernetes-networkpolicy-tutorial [1].

Запрещение всего трафика к приложению (DENY)

Эта политика приведет к отбрасыванию (drop) всего трафика к подам приложения, выбранным с помощью селекторов подов (Pod Selector).

Сценарии использования:

  • Вы хотите запустить под, но при этом запретить другим подам с ним взаимодействовать.
  • Вы хотите временно изолировать трафик к сервису от других подов.

Обеспечение сетевой безопасности в кластере Kubernetes - 2

Пример

Запустим nginx-под с метками app=web и env=prod, а также откроем его 80-й порт:

kubectl run web --image=nginx --labels app=web,env=prod --expose --port 80

Запустим временный под и выполним запрос к сервису web:

$ kubectl run --rm -i -t --image=alpine test-$RANDOM -- sh
/ # wget -qO- http://web
<!DOCTYPE html>
<html>
<head>
...

Работает! Теперь сохраним следующий манифест в файл web-deny-all.yaml и применим его к кластеру:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: web-deny-all
spec:
  podSelector:
    matchLabels:
      app: web
      env: prod

$ kubectl apply -f web-deny-all.yaml
networkpolicy "access-nginx" created

Проверка

Запустите контейнер еще раз и попробуйте сделать запрос к сервису web:

$ kubectl run --rm -i -t --image=alpine test-$RANDOM -- sh
/ # wget -qO- --timeout=2 http://web
wget: download timed out

Трафик заблокирован!


Замечания

В вышеприведенном манифесте для достижения желаемого мы назначили подам специальные метки app=web,env=prod. Однако в этом файле не хватает поля spec.ingress. Поэтому к поду запрещен весь трафик.

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

Если есть хотя бы одна сетевая политика с разрешающими трафик правилами, этот трафик пойдет по разрешенному маршруту, невзирая на существование запрещающих правил.

Очистка

kubectl delete deploy web
kubectl delete service web
kubectl delete networkpolicy web-deny-all

Ограничение трафика к приложению (LIMIT)

Вы можете создать сетевую политику, ограничивающую трафик только от определенных подов.

Сценарии использования:

Предоставление доступа к сервису только тем микросервисам, которым это нужно.

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

Обеспечение сетевой безопасности в кластере Kubernetes - 3

Пример

Предположим, что в нашем приложении есть сервер REST API c метками app=bookstore и role=api:

kubectl run apiserver --image=nginx --labels app=bookstore,role=api --expose --port 80

Сохраните следующую сетевую политику в файл api-allow.yaml. Она разрешает доступ лишь подам (например, другим микросервисам) с меткой app=bookstore:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: bookstore
      role: api
  ingress:
  - from:
      - podSelector:
          matchLabels:
            app: bookstore

$ kubectl apply -f api-allow.yaml
networkpolicy "api-allow" created

Проверка

Давайте убедимся, что подам без метки app=bookstore доступ запрещен:

$ kubectl run test-$RANDOM --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://apiserver
wget: download timed out

Трафик заблокирован!

А теперь давайте проверим, что от подов с меткой app=bookstore трафик разрешен:

$ kubectl run test-$RANDOM --rm -i -t --image=alpine --labels app=bookstore,role=frontend -- sh
/ # wget -qO- --timeout=2 http://apiserver
<!DOCTYPE html>
<html><head>

Трафик разрешен.

Очистка

kubectl delete deployment apiserver
kubectl delete service apiserver
kubectl delete networkpolicy api-allow

Запрещение (DENY) всего не внесенного в белый список трафика в текущем пространстве имен

Сценарий использования

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

Всерьез подумайте о том, чтобы применить соответствующий манифест во всех пространствах имен, в которых развернута рабочая нагрузка (но не в kube-system).

С помощью этой политики можно настроить доступ типа «отклонять все по умолчанию» (default "deny all"). Таким образом можно четко определить, какие компоненты зависят от других компонентов, и внедрить сетевые политики, по которым можно построить графы зависимостей между компонентами.

Обеспечение сетевой безопасности в кластере Kubernetes - 4

Манифест

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny-all
  namespace: default
spec:
  podSelector:
    matchLabels:

Несколько замечаний:

  • namespace — по умолчанию применяйте эту политику в пространстве имен default.
  • matchLabels — не заполнено, это означает соответствие всем подам. Таким образом, политика будет применена ко всем подам в указанном пространстве имен.
  • ingress-правила не определены, поэтому отклоняться будет трафик, идущий к выбранным (всем) подам.

Сохраните этот манифест в файл default-deny-all.yaml и примените политику:

$ kubectl apply -f default-deny-all.yaml
networkpolicy "default-deny-all" created

Очистка

kubectl delete networkpolicy default-deny-all

Запрет (DENY) всего трафика из других пространств имен

(также известно под именем LIMIT — ограничение трафика к текущему пространству имен)

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

Сценарии использования

  • Вы не хотите, чтобы приложения из пространства имен test могли случайно направить какой-либо трафик сервисам или базам данных в пространстве имен prod.
  • У вас в разных пространствах имен размещены приложения разных клиентов, и вы хотите изолировать их друг от друга.

Обеспечение сетевой безопасности в кластере Kubernetes - 5

Пример

Создайте новое пространство имен под названием secondary и запустите веб-сервис:

kubectl create namespace secondary

kubectl run web --namespace secondary --image=nginx 
    --labels=app=web --expose --port 80

Сохраните следующий манифест в файл web-deny-other-namespaces.yaml и примените его к кластеру:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  namespace: secondary
  name: web-deny-other-namespaces
spec:
  podSelector:
    matchLabels:
  ingress:
  - from:
    - podSelector: {}

$ kubectl apply web-deny-other-namespaces.yaml
networkpolicy "web-deny-other-namespaces" created"

Несколько замечаний по поводу манифеста:

  • namespace: secondary приведет к применению манифеста в пространстве имен secondary;
  • политика будет применена ко всем подам в пространстве имен secondary, поскольку spec.podSelector.matchLabels значения не содержит, выбирая таким образом все поды;
  • разрешен трафик от всех подов в пространстве имен secondary, поскольку spec.ingress.from.podSelector значения не содержит, что также означает соответствие всем подам.

Проверка

Отправьте запрос к этому веб-сервису из пространства имен default:

$ kubectl run test-$RANDOM --namespace=default --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.secondary
wget: download timed out

Трафик из пространства имен default заблокирован!

При этом любой под из пространства имен secondary доступ получит:

$ kubectl run test-$RANDOM --namespace=secondary --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.secondary
<!DOCTYPE html>
<html>

Очистка

kubectl delete deployment web -n secondary
kubectl delete service web -n secondary
kubectl delete networkpolicy web-deny-other-namespaces -n secondary
kubectl delete namespace secondary

Разрешение (ALLOW) любого трафика из пространства имен

Эта политика похожа на разрешение трафика из всех пространств имен [5], при этом она позволяет выбрать определенное пространство имен.

Сценарии использования:

  • Ограничение трафика к production-базе данных таким образом, чтобы он был разрешен только от пространств имен, в которых развернуты production-приложения.
  • Развертывание средств мониторинга, которым разрешено собирать метрики текущего пространства имен в специально для этого созданном отдельном пространстве имен.

Обеспечение сетевой безопасности в кластере Kubernetes - 6

Пример

Запустите веб-сервер в пространстве имен по умолчанию:

kubectl run web --image=nginx 
    --labels=app=web --expose --port 80

Теперь предположим, что у вас есть вот такие пространства имен:

  • default — создано Kubernetes, здесь развернут ваш API;
  • prod — здесь развернуты другие production-сервисы; на него установлена метка purpose=prod;
  • dev — это dev/test-окружение; на него установлена метка purpose=testing.

Создайте пространства имен prod и dev:

kubectl create namespace dev
kubectl label namespace/dev purpose=testing
kubectl create namespace prod
kubectl label namespace/prod purpose=production

Следующий манифест разрешит трафик только от подов, находящихся в пространстве имен с меткой purpose=production. Сохраните его в web-allow-prod.yaml и примените к кластеру:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: web-allow-prod
spec:
  podSelector:
    matchLabels:
      app: web
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          purpose: production

$ kubectl apply -f web-allow-prod.yaml
networkpolicy "web-allow-prod" created

Проверка

Сделайте запрос к веб-серверу из пространства имен dev, убедитесь, что трафик заблокирован:

$ kubectl run test-$RANDOM --namespace=dev --rm -i -t --image=alpine -- sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out

(трафик заблокирован)

Теперь сделайте запрос из пространства имен prod, убедитесь, что запрос проходит:

$ kubectl run test-$RANDOM --namespace=prod --rm -i -t --image=alpine -- sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.default
<!DOCTYPE html>
<html>
<head>
...
(трафик разрешен)

Очистка

kubectl delete networkpolicy web-allow-prod
kubectl delete deployment web
kubectl delete service web
kubectl delete namespace {prod,dev}

Разрешение (ALLOW) трафика от внешних клиентов

Эта сетевая политика позволяет внешним клиентам получать доступ к поду через балансировщик нагрузки или напрямую из Интернет.

Сценарии использования:

Обеспечение сетевой безопасности в кластере Kubernetes - 7

Пример

Запустите под и откройте его 80-й порт для доступа из Интернет через балансировщик нагрузки:

kubectl run web --image=nginx 
    --labels=app=web --port 80

kubectl expose deployment/web --type=LoadBalancer

Дождитесь появления EXTERNAL-IP в выводе kubectl get service. Откройте http://[EXTERNAL-IP] в браузере и убедитесь в наличии доступа к ресурсу.

Следующий манифест разрешает трафик из любых источников (как внутри кластера, так и из внешних источников). Сохраните его в файл web-allow-external.yaml и примените к кластеру:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: web-allow-external
spec:
  podSelector:
    matchLabels:
      app: web
  ingress:
  - from: []

$ kubectl apply -f web-allow-external.yaml
networkpolicy "web-allow-external" created

Снова откройте в браузере http://[EXTERNAL-IP] и убедитесь, что он по-прежнему работает.

Замечания

В этом манифесте определено одно ingress-правило для подов с меткой app=web. Поскольку конкретные podSelector или namespaceSelector не указаны, будет пропускаться трафик от любых источников, в том числе и внешних.

Чтобы разрешить доступ извне только к 80-му порту, воспользуйтесь следующим ingress-правилом:

  ingress:
  - ports:
    - port: 80
    from: []

Очистка

kubectl delete deployment web
kubectl delete service web
kubectl delete networkpolicy web-allow-external

Как применяются сетевые политики

Сетевые политики не являются базовой функциональностью Kubernetes. И хотя вы можете отправить объект NetworkPolicy на мастер Kubernetes, политика не сработает, если соответствующая функциональность не реализована в сетевом плагине.

Примеры сетевых плагинов, поддерживающих сетевые политики, можно найти на этой странице [7]. Также сетевые политики поддерживаются плагинами Calico [8] и Weave Net [9].

В Google Container Engine (GKE) [10] поддержка сетевых политик реализована в начальной стадии (alpha) путем предустановки сетевого плагина Calico.

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

Анатомия NetworkPolicy

NetworkPolicy — это один из объектов Kubernetes API. В кластере может быть создано множество таких объектов. В NetworkPolicy есть две основные составляющие:

  1. Целевые поды — поды, входящие (ingress) сетевые соединения которых должны подчиняться соответствующим политикам. Эти поды отбираются по меткам.
  2. Входящие (ingress) правила определяют, какие поды могут подключаться к целевым подам. Они также отбираются по меткам или по пространствам имен.

Вот конкретный пример манифеста NetworkPolicy:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: bookstore
      role: api
  ingress:
  - from:
      - podSelector:
          matchLabels:
            app: bookstore
  - from:
      - podSelector:
          matchLabels:
            app: inventory

В этой политике подам с метками app=bookstore или app=inventory разрешено подключаться к подам с метками app=bookstore или role=api. Ее можно озвучить следующим образом: «Предоставить микросервисам приложения bookstore доступ к bookstore API».

Как работают сетевые политики

Несмотря на то что проектная документация [11] и справка по API [12] сетевых политик весьма сложны для понимания, мне удалось выделить несколько простых правил:

  • Если в NetworkPolicy выбран под, то предназначенный для этого пода трафик будет ограничиваться.
  • Если для пода не определен объект NetworkPolicy, к этому поду смогут подключаться все поды из всех пространств имен. То есть, если для конкретного пода не определена сетевая политика, по умолчанию неявно подразумевается поведение «разрешить все (allow all)».
  • Если трафик к поду А ограничен, а под Б должен к нему подключиться, необходимо создать объект NetworkPolicy, в котором выбран под А, а также есть ingress-правило, в котором выбран под Б.

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

  • Сетевые политики действуют только на сетевые подключения к тем подам, которые находятся в одном пространстве имен с NetworkPolicy.
  • В разделе ingress-правила podSelector можно выбрать только поды из того же пространства имен, в котором развернут объект NetworkPolicy.
  • Если поду А необходимо подключиться к находящемуся в другом пространстве имен поду Б и сетевые подключения к поду Б ограничены сетевой политикой, под А должен быть выбран в поле namespaceSelector сетевой политики пода Б.

Насколько безопасны сетевые политики?

Сетевые политики ограничивают сетевой обмен данными между подами и являются важной частью работы по обеспечению безопасности трафика и приложений в кластере Kubernetes. Однако, в отличие от брандмауэров, в рамках сетевых политик не выполняется глубокий анализ пакетов (deep packet inspection).

В деле обеспечения безопасности трафика между подами в кластере не стоит полагаться только на сетевые политики. Рекомендую также присмотреться к таким методам, как TLS (transport layer security) со взаимной аутентификацией, который позволяет зашифровать трафик и выполнять аутентификацию при взаимодействии микросервисов.

Взгляните на Google Cloud Security Whitepaper [13] (выделение сделано мной):

Глубоко эшелонированная оборона (Defense in depth) описывает множество уровней системы безопасности, которая защищает сеть Google от атак извне. Проход разрешен только авторизованным сервисам и протоколам, отвечающим нашим требованиям безопасности. Все остальное отбрасывается автоматически. Для разделения сетей используются брандмауэры промышленного уровня и списки контроля доступа (ACLs). Весь трафик направляется через специальным образом настроенные серверы GFE (Google Front End), что позволяет выявлять и останавливать вредоносные запросы и DDoS-атаки. Также GFE-серверам разрешено взаимодействовать лишь с внутренними серверами, находящимися в особом списке. Такая политика типа «запретить по умолчанию (default deny)» позволяет предотвратить доступ с GFE-серверов к тем ресурсам, которые им не нужны. […]

При передаче через Интернет и внутренние сети данные становятся уязвимы, и к ним может быть осуществлен неавторизованный доступ. […] Серверы Google Front End (GFE) поддерживают надежные протоколы шифрования, такие как TLS, что позволяет обеспечить безопасность соединений между устройствами клиентов и веб-сервисами Google.

Упомянутые мною ранее связанные с сервисными сетками (service mesh) проекты, такие как Istio [3] и linkerd [14], обещают качественные улучшения в этой области. Например, Istio может шифровать трафик между микросервисами с использованием TLS и применять сетевые политики прозрачно, без необходимости менять код приложения.

Дополнительная информация

Если вы хотите попробовать сетевые политики в действии, легче всего будет начать с создания кластера GKE [15]. Также можно почитать следующее:


Благодарю Matthew DeLio и Daniel Nardo за проверку черновиков этой статьи.

Ссылки:

  1. Оригинал: Securing Kubernetes Cluster Networking [16].

Автор: olemskoi

Источник [17]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/sistemnoe-administrirovanie/263311

Ссылки в тексте:

[1] kubernetes-networkpolicy-tutorial: https://github.com/ahmetb/kubernetes-networkpolicy-tutorial

[2] документации: https://kubernetes.io/docs/concepts/services-networking/network-policies/

[3] Istio: https://istio.io/

[4] этой статье: http://blog.kubernetes.io/2016/09/high-performance-network-policies-kubernetes.html

[5] разрешение трафика из всех пространств имен: https://github.com/ahmetb/kubernetes-networkpolicy-tutorial/blob/master/05-allow-traffic-from-all-namespaces.md

[6] при этом запрещая весь трафик, не внесенный в белый список: https://github.com/ahmetb/kubernetes-networkpolicy-tutorial/blob/master/03-deny-all-non-whitelisted-traffic-in-the-namespace.md

[7] на этой странице: https://kubernetes.io/docs/concepts/cluster-administration/networking/

[8] Calico: https://kubernetes.io/docs/tasks/administer-cluster/calico-network-policy/

[9] Weave Net: https://kubernetes.io/docs/tasks/administer-cluster/weave-network-policy/

[10] Google Container Engine (GKE): https://cloud.google.com/container-engine

[11] проектная документация: https://github.com/kubernetes/community/blob/b77ecdb66c2c77d1fc36b0b87e8caa5bca6aff88/contributors/design-proposals/network-policy.md#behavior

[12] справка по API: https://kubernetes.io/docs/api-reference/v1.7/#networkpolicyspec-v1-networking

[13] Google Cloud Security Whitepaper: https://cloud.google.com/security/whitepaper#a_global_network_with_unique_security_benefits

[14] linkerd: https://linkerd.io/

[15] создания кластера GKE: https://github.com/ahmetb/kubernetes-networkpolicy-tutorial/blob/master/00-create-cluster.md

[16] Securing Kubernetes Cluster Networking: https://ahmet.im/blog/kubernetes-network-policy/?imm_mid=0f591b&cmp=em-webops-na-na-newsltr_20170818

[17] Источник: https://habrahabr.ru/post/337088/