Сетевые политики на защите рабочих нагрузок в кластере Kubernetes

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В кластере Kubernetes нам доступен любой сервис в любом пространстве имён, то есть по умолчанию pod открыт для любого трафика.

Мы можем определить сетевую политику для пространства имён или pod’а, чтобы защитить рабочие нагрузки в кластере. Например, разделить рабочие нагрузки в мультитенантном кластере по проектам, командам или организациям.

Сценарий

Представьте, что в пространствах имён Kubernetes мы развёртываем приложение на трёх уровнях: фронтенд, бэкенд и база данных.

Фронтенд будет общедоступным. Приложения будут предоставляться через балансировщик нагрузки, поэтому обращаться к фронтенду мы будем по DNS-имени или IP-адресу этого балансировщика.

Бэкенд будет содержать всю логику приложения.

База данных будет базой данных.

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

Давайте настроим архитектуру, создав три новых пространства имён, в которых мы будем развёртывать сервисы и развёртывания.

Для простоты мы будем использовать образ nginx для развёртывания pod’ов в развёртываниях.

1. Настраиваем новые пространства имён

Создаём пространства имён и добавляем метки к каждому, чтобы потом можно было применять сетевые политики по этим меткам.

# Create "frontend" namespace and add a label 
> kubectl create ns frontend 
> kubectl label ns frontend tier=frontend

# Create "backend" namespace and add a label 
> kubectl create ns backend 
> kubectl label ns backend tier=backend
 
# Create "database" namespace and add a label 
> kubectl create ns database 
> kubectl label ns database tier=database

2. Развёртываем сервисы и развёртывания

2.1 Уровень базы данных

# Deploy a deployment named "database" with 2 replicas
> kubectl create deploy database -n database  --image=nginx --replicas=2
# List the pods of "database" deployments
> kubectl get pods -n database
----------------------------------------------------------------------------------------------------
NAME                        READY   STATUS    RESTARTS   AGE
database-7d94797799-b9sdw   1/1     Running   0          23h
database-7d94797799-jc4xt   1/1     Running   0          23h
----------------------------------------------------------------------------------------------------

# Create a service (cluster ip) named "database" for accessing the pods of the "database" deployment
> kubectl create service clusterip database --tcp=80 -n database
# List the service  
> kubectl get svc -n database
----------------------------------------------------------------------------------------------------
NAME       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
database   ClusterIP   10.106.137.165   <none>        80/TCP    23h
----------------------------------------------------------------------------------------------------

2.2 Уровень бэкенда

# Deploy a deployment named "backend" with 2 replicas
> kubectl create deploy backend -n backend  --image=nginx --replicas=2
# List the pods of "backend" deployments
> kubectl get pods -n backend
----------------------------------------------------------------------------------------------------
NAME                       READY   STATUS    RESTARTS   AGE
backend-5c5c74cbf6-h4d9p   1/1     Running   0          23h
backend-5c5c74cbf6-jf6fj   1/1     Running   0          23h
----------------------------------------------------------------------------------------------------

# Create a service (cluster ip) named "backend" for accessing the pods of the "backend" deployment
> kubectl create service clusterip backend --tcp=80 -n backend
# List the service  
> kubectl get svc -n backend
----------------------------------------------------------------------------------------------------
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
backend   ClusterIP   10.101.154.161   <none>        80/TCP    23h
----------------------------------------------------------------------------------------------------

2.3 Уровень фронтенда

# Deploy a deployment named "frontend" with 2 replicas
> kubectl create deploy frontend -n frontend  --image=nginx --replicas=2
# List the pods of "frontend" deployments
> kubectl get pods -n frontend
----------------------------------------------------------------------------------------------------
NAME                        READY   STATUS    RESTARTS   AGE
frontend-5d7445bdb8-g8rpb   1/1     Running   0          24h
frontend-5d7445bdb8-kqtnc   1/1     Running   0          24h
----------------------------------------------------------------------------------------------------

# Create a service (load balancer) named "frontend" for accessing the pods of the "frontend" deployment, 
# From internet through the load balancer IP or DNS
> kubectl create service loadbalancer frontend --tcp=80:80 -n frontend
# List the service, and note down the external IP  
> kubectl get svc -n frontend 
----------------------------------------------------------------------------------------------------
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
frontend   LoadBalancer   10.102.187.203   45.76.197.98   80:30008/TCP   30m
----------------------------------------------------------------------------------------------------

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

3. Риски для безопасности

3.1 Проблема

Сейчас к pod’ам и пространствам имён не применяются никакие политики. Все pod’ы в кластере могут общаться друг с другом. А что если это мультитенантный кластер или там хранятся конфиденциальные данные? Любой злоумышленник с уровня фронтенда получит прямой доступ к базе данных и всем пространствам имён в кластере. Это серьёзный риск для безопасности.

3.2 Решение

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

4. Применяем сетевые политики

4.1 Уровень базы данных

  • По умолчанию запрещаем весь входящий и исходящий трафик
    Мы может создать политику по умолчанию, которая будет запрещать весь входящий и исходящий трафик в этом пространстве имён.

# Deny all ingress and egress traffic
> kubectl create -f \
 https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-deny-all-database.yaml
 
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny-all-database
  namespace: database
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Ingress
  - Egress
---

Уровень базы данных теперь будет изолирован от остальных пространств имён в кластере.

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

# Allow ingress traffic from "backend-tier" using the following manifest file:
> kubectl create -f  \
https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-allow-ingress-from-backend.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-from-backend
  namespace: database
spec:
  podSelector:
    matchLabels: {}
  ingress:
    - from:
      - podSelector:
          matchLabels: {}
        namespaceSelector:
           matchLabels:
              tier: backend
---

4.2 Уровень бэкенда

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

# Deny all ingress and egress traffic
> kubectl create -f \
https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-deny-all-backend.yaml

---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny-all-backend
  namespace: backend
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Ingress
  - Egress
---

● Разрешим исходящий трафик к базе данных
На уровне базы данных мы разрешили входящий трафик только с бэкенда, а на предыдущем шаге запретили весь трафик в обе стороны для бэкенда. Поэтому уровень базы данных готов принимать входящий трафик от бэкенда, а самому бэкенду запрещено отправлять исходящий трафик. Чтобы наладить взаимодействие между бэкендом и базой данных, нужно разрешить трафик в этом направлении.

Плюс мы используем сервисы для доступа к pod’ам, а значит нужно создать для исходящего трафика ещё одно правило, чтобы разрешать DNS-имена сервисов. В кластере Kubernetes сервер DNS представляет собой набор pod’ов в пространстве имён kube-system. Получается, нужно разрешить исходящий трафик к пространству kube-system, но не ко всему, а только к уровню kube-dns. Теперь pod’ы на бэкенде смогут разрешать DNS-имена сервисов.

Пишем политику, чтобы разрешить исходящий трафик с бэкенда в базу данных, а также на порт 53 пространства имён kube-system:

# allow egress from backend-tier to database-tier and allow dns resolving of services
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-allow-egress-to-database.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-database
  namespace: backend
spec:
  podSelector:
    matchLabels: {}
  egress:
    - to:
      - podSelector:
          matchLabels: {}
        namespaceSelector:
           matchLabels:
              tier: database
    - to:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: kube-system
        podSelector:
          matchLabels:
            k8s-app: kube-dns
      ports:
      - protocol: UDP
        port: 53
---

Вот что мы в итоге получаем:

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

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

# Allow ingress from frontend-tier
> kubectl create -f \
https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-allow-ingress-from-frontend.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-from-frontend
  namespace: backend
spec:
  podSelector:
    matchLabels: {}
  ingress:
    - from:
      - podSelector:
          matchLabels: {}
        namespaceSelector:
           matchLabels:
              tier: frontend
---

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

4.3 Уровень фронтенда

● По умолчанию запрещаем весь трафик
Как и для предыдущих уровней, применим сетевую политику, которая полностью запретит весь входящий и исходящий трафик.

# Deny all ingress and egress traffic
> kubectl create -f \
https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-deny-all-frontend.yaml

---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny-all-frontend
  namespace: frontend
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Ingress
  - Egress
---

● Разрешаем исходящий трафик на бэкенд
Сейчас весь трафик для фронтенда запрещён. Для отправки трафика на бэкенд нужно разрешить исходящий трафик на бэкенд и в пространство имён kube-system, чтобы разрешать DNS-имена сервисов, как мы говорили чуть выше.

# Allow egress to backend-tier
> kubectl create -f \
  https://github.com/shamimice03/Network_Policies_Kubernetes/blob/main/netpol-allow-egress-to-backend.yaml
  
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-backend
  namespace: frontend
spec:
  podSelector:
    matchLabels: {}
  egress:
    - to:
      - podSelector:
          matchLabels: {}
        namespaceSelector:
           matchLabels:
              tier: backend
    - to:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: kube-system
        podSelector:
          matchLabels:
            k8s-app: kube-dns
      ports:
      - protocol: UDP
        port: 53
---

Вот что мы получаем теперь:

● Разрешаем входящий трафик из интернета
Раз мы обращаемся к фронтенду через сервис балансировки нагрузки, запрос будет поступать с IP-адреса внешнего балансировщика нагрузки. Мы запретили весь входящий трафик, так что пока не сможем получить доступ к приложению на фронтенде через внешний IP. Нужно создать сетевую политику, которая разрешит входящий трафик из любого места, кроме частных IP-адресов pod’ов. Так мы запретим трафик с pod’ов, которые находятся в других пространствах имён.

# Allow ingress from everywhere except pod-network
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Network_Policies_Kubernetes/main/netpol-allow-ingress-from-everywhere.yaml
  
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-from-loadbalancer
  namespace: frontend
spec:
  podSelector:             
    matchLabels: {}
  ingress:                   
    - from:
      - ipBlock:           
          cidr: 0.0.0.0/0    
          except:            # Restrict the Private IP CIDR Block of your pod network. 
           - 10.0.0.0/8      # So that, pods from other namespaces cannot send ingress traffic.
      ports:                
      - protocol: TCP
        port: 80 
---

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

5. Проверяем, что получилось

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

● Для начала попробуем получить к приложению доступ через внешний IP-адрес сервиса балансировки нагрузки.

Мы видим, что приложение во фронтенде доступно с внешнего IP-адреса.

Практический курс, охватывает все аспекты безопасности проекта на Kubernetes.

● Давайте попробуем через pod во фронтенде получить доступ к сервисам в бэкенде и базе данных. Первое должно получиться, второе — нет.

# Dive into a pod of the "frontend" deployment
> kubectl exec -it frontend-5d7445bdb8-g8rpb  -n frontend bash
# Try to access a service(clusterIP) named "backend", resides in the backend-tier 
>>  curl backend.backend     
---------------------------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>                                 #Successfully Accessed
<title>Welcome to nginx!</title>
<style>
...
---------------------------------------------------------------------------------
 # Try to access a service(clusterIP) named "database", resides in the database-tier 
>>  curl database.database 
---------------------------------------------------------------------------------
# curl: (28) Failed to connect to database.database port 80: Connection timed out
---------------------------------------------------------------------------------

● Теперь проверим сетевые политики через pod на бэкенде.

# Dive into a pod of the "backend" deployment
> kubectl exec -it backend-5c5c74cbf6-h4d9p  -n backend bash
# Try to access a service(clusterIP) named "frontend", resides in the frontend-tier 
>>  curl frontend.frontend    
---------------------------------------------------------------------------------
# curl: (28) Failed to connect to frontend.frontend port 80: Connection timed out
---------------------------------------------------------------------------------

# Try to access a service(clusterIP) named "database", resides in the database-tier 
>>  curl database.database 
----------------------------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>                                 #Successfully Accessed
<title>Welcome to nginx!</title>
<style>
...
----------------------------------------------------------------------------------

● Наконец, сделаем то же самое из pod’а в базе данных.

# Dive into a pod of the "database" deployment
> kubectl exec -it database-7d94797799-b9sdw   -n database bash
# Try to access a service(clusterIP) named "backend", resides in the backend-tier 
>>  curl backend.backend     
---------------------------------------------------------------------------------
# curl: (28) Failed to connect to backend.backend port 80: Connection timed out
---------------------------------------------------------------------------------

# Try to access a service(clusterIP) named "frontend", resides in the frontend-tier 
>>  curl frontend.frontend    
---------------------------------------------------------------------------------
# curl: (28) Failed to connect to frontend.frontend port 80: Connection timed out
---------------------------------------------------------------------------------

Как видите, все наши сетевые политики работают, как ожидалось.

Безопасность в Kubernetes: как прокачать навыки

В учебном центре Слёрм открыта запись на курс «Безопасность в Kubernetes» для инженеров безопасности, DevOps’ов, SRE и разработчиков, самостоятельно работающих в Kubernetes.

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

Все знания вы можете закрепить на практике на стендах — проработаете теорию и будете уверены в решениях.

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

Что будет?

  • авторизация в кластере

  • настройка autoscaling

  • резервное копирование

  • Stateful приложения в кластере

  • интеграция Kubernets и Vault для хранения секретов

  • HorizontalPodAutoscaler

  • ротация сертификатов в кластере

  • Blue-Green Deploy и Canary Deploy

  • настройка Service mesh

«Мега» подойдет всем, кому предстоит запускать Kubernetes в продакшн и отвечать за работу проекта в дальнейшем: специалистам по безопасности, системным инженерам, администраторам, архитекторам, DevOps и др. Пройдите бесплатный курс, чтобы научиться устанавливать Kubernetes в ручном режиме.

Комплектом дешевле:

Мы предлагаем комплекты видеокурсов (тариф "Стандарт") со скидкой от 20%

«Безопасность» + «Мега» = 90 000 рублей вместо 130 000

Узнать подробности и записаться: «Безопасность в Kubernetes», «Kubernetes Мега».

Источник: https://habr.com/ru/company/southbridge/blog/718262/


Интересные статьи

Интересные статьи

На Хабре ни для кого не секрет, что в текущей повестке практически все сферы частного бизнеса вынуждены реагировать на происходящие изменения. Большое количество привычных всем нам зарубежных сервисов...
Если кратко, то MTA-STS — это способ дополнительно защитить письма от перехвата (т.е. атак злоумышленник-в-середине aka MitM) при передаче между почтовыми серверами. Он частично р...
В настоящее время всё больше компаний переводят свою инфраструктуру с железных серверов и собственных виртуалок в облака. Такое решение легко объяснить: нет необходимости заботиться о железе,...
Во времена, когда Kubernetes был ещё v1.0.0, существовали плагины для томов (volume plugins). Нужны они были для подключения к Kubernetes систем для хранения персистентных (постоянных) данных...
Реализация ORM в ядре D7 — очередная интересная, перспективная, но как обычно плохо документированная разработка от 1с-Битрикс :) Призвана она абстрагировать разработчика от механики работы с табл...