IPMan — это оператор Kubernetes, который упрощает управление IPSec-соединениями и обеспечивает безопасный обмен данными между рабочими нагрузками Kubernetes и внешним миром. Он автоматизирует настройку IPSec VPN-туннелей с помощью StrongSwan, позволяя безопасно открывать доступ к подам из внешних сетей.
Что умеет IPMan
-
Создаёт IPSec VPN-соединения между узлами Kubernetes и удалёнными конечными точками и управляет ими
-
Автоматически настраивает маршрутизацию
-
Управляет пулами IP-адресов для рабочих нагрузок
Установка
Предварительные требования
-
Кластер Kubernetes версии 1.20 и выше
-
Helm 3.0 и выше
-
Модуль ядра Linux XFRM
-
Плагин CNI с поддержкой межузлового взаимодействия подов (например, Calico или Cilium)
Установка через Helm
# Добавить репозиторий
helm repo add ipman https://dialohq.github.io/ipman
# Установить чарт
helm install ipman ipman/ipman -n ipman-system --create-namespace
Использование
Шаг 1: Создать Secret с общим ключом (Pre-shared Key)
IPMan требует секрет для аутентификации IPSec:
kubectl create secret generic ipsec-secret -n default --from-literal=example=yourpresharedkey
Шаг 2: Создать группу Charon (Charon Group)
Группа Charon может содержать несколько IPSec-соединений. Как правило, она выглядит следующим образом:
apiVersion: ipman.dialo.ai/v1
kind: CharonGroup
metadata:
name: charongroup1
namespace: default
spec:
hostNetwork: true
nodeName: node1
Здесь мы указываем, что другая сторона VPN-соединений использует IP-адрес, назначенный хостовому интерфейсу на одном из наших узлов — node1.
Например, на узле node1 может быть интерфейс enp0s1 с адресом 192.168.10.201 — дальнейшие шаги исходят именно из этого.
Примечание: несмотря на то что здесь указывается nodeName, он используется только для публичного IP-адреса. Поды рабочих нагрузок, взаимодействующие через этот VPN, могут располагаться на любом узле. Визуальное пояснение см. в разделе «Архитектура».
Шаг 3: Создать IPSecConnection
Создайте пользовательский ресурс (Custom Resource, CR) IPSecConnection для установки VPN-соединения:
apiVersion: ipman.dialo.ai/v1
kind: IPSecConnection
metadata:
name: example-connection
namespace: ipman-system
spec:
name: "example"
remoteAddr: 192.168.10.204
remoteId: 192.168.10.204
localAddr: 192.168.10.201
localId: 192.168.10.201
secretRef:
name: "ipsec-secret"
namespace: default
key: "example"
groupRef:
name: charongroup1
namespace: default
children:
example-child:
name: "example-child"
extra:
esp_proposals: aes256-sha256-ecp256
start_action: start
dpd_action: restart
local_ips:
- "10.0.2.0/24"
remote_ips:
- "10.0.1.0/24"
xfrm_ip: "10.0.2.1/24"
vxlan_ip: "10.0.2.2/24"
if_id: 101
ip_pools:
primary:
- "10.0.2.3/24"
- "10.0.2.4/24"
Этот CR во многом напоминает конфигурационный файл StrongSwan, однако содержит дополнительные поля:
-
secretRef— заменяет секциюsecretsконфигурационного файла StrongSwan. Ссылается на секрет, созданный на шаге 1, который хранит общий ключ (PSK). -
groupRef— связывает данное соединение с группой, определённой на шаге 2. -
xfrm_ipиvxlan_ip— значения в значительной мере произвольны, за исключением того, что должны принадлежать подсети, указанной вlocal_ips. В большинстве случаев их можно выбрать произвольно, главное — убедиться в отсутствии конфликтов между соединениями. -
if_id— должен быть уникальным в пределах одного узла, поскольку задаёт идентификатор XFRM-интерфейса, используемого StrongSwan и ядром Linux для маршрутизации IPSec-пакетов. -
ip_pools— список IP-адресов, которые будут выданы подам, участвующим в VPN. Они также должны быть из диапазонаlocal_ips. Адреса разбиваются на именованные пулы: в примере пул называетсяprimary, но можно использовать любое имя. Это удобно, когда через один VPN открывается доступ к нескольким сервисам: можно создать пулыservice1иservice2, поместив в каждый те адреса, по которым удалённая сторона ожидает эти сервисы.
Шаг 3: Развернуть рабочие нагрузки с использованием VPN-соединения
Чтобы направить трафик рабочей нагрузки через VPN-туннель, добавьте специальные аннотации к подам или Deployment-ресурсам. Эти аннотации указывают IPMan выделить IP-адреса из настроенных пулов и настроить необходимую маршрутизацию.
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
template:
metadata:
annotations:
ipman.dialo.ai/childName: "example-child" # Должно совпадать с именем child в IPSecConnection
ipman.dialo.ai/ipmanName: "example-connection" # Должно совпадать с именем IPSecConnection
ipman.dialo.ai/poolName: "primary" # Используемый пул IP-адресов (определён в IPSecConnection)
spec:
# Спецификация пода
Оператор автоматически:
-
Выделит IP-адреса из указанного пула
-
Настроит маршрутизацию для рабочих нагрузок
-
Сконфигурирует записи FDB моста для обеспечения связи
Если приложению требуется привязаться к конкретному IP-адресу, а в пуле их несколько, заранее неизвестно, какой под получит какой адрес. Чтобы решить эту проблему, во всех рабочих подах устанавливается переменная окружения VXLAN_IP. В данном примере под может получить адрес 10.0.2.3/24 из пула, и эта переменная будет содержать значение 10.0.2.3.
Справочник по конфигурации
Устранение неполадок
При возникновении проблем с IPSec-соединениями:
-
Проверьте статус IPSecConnection:
kubectl get ipsecconnection -n ipman-system kubectl describe ipsecconnection example-connection -n ipman-system -
Проверьте журналы оператора:
kubectl logs -n ipman-system -l app=ipman-controller -
Убедитесь, что аннотации подов соответствуют конфигурации IPSecConnection.
Мониторинг
IPMan теперь поддерживает мониторинг через Prometheus. Чтобы его включить:
-
Установите
global.monitoring.enabledв значениеtrueв параметрах Helm -
Установите
global.monitoring.releaseв имя, под которым установлен оператор Prometheus (например, если установка выполнялась командойhelm install kps prometheus-community/kube-prometheus-stack, укажите значение"kps")
Дополнительные параметры конфигурации см. в файле helm/values.yaml.
Архитектура
Описание
IPMan обеспечивает безопасное соединение между удалёнными площадками и подами рабочих нагрузок, внедряя вторичные сетевые интерфейсы, привязанные к сети локального домена шифрования. Входящий трафик поступает на сетевой интерфейс хоста, проходит через выделенный XFRM-интерфейс и маршрутизируется внутри изолированного сегмента VXLAN для повышения безопасности и разграничения трафика.
Charon — демон IKE из состава StrongSwan — работает на указанных пользователем узлах, а количество его экземпляров определяется конфигурацией IPSec. Каждое дочернее соединение выполняется в отдельном поде, оснащённом собственным XFRM-интерфейсом и сегментом VXLAN. Такая архитектура обеспечивает гибкое развёртывание рабочих нагрузок по всему кластеру, абстрагируя физическую инфраструктуру для беспрепятственного масштабирования. Для безопасного взаимодействия открыты только порты 500 (IKE) и 4500 (обход NAT / IPSec). Charon и сервис restctl, управляющий сокетом Charon и конфигурацией XFRM-интерфейса, работают в сетевом пространстве имён хоста и открывают только сокеты, смонтированные в файловой системе хоста. Управляющие сокеты, доступные через прокси, обеспечивают кластерное управление без необходимости открывать дополнительные порты.
Благодарности
Отдельная благодарность LarsTi за репозиторий ipsec_exporter, который был адаптирован для данного проекта.
TODO
-
❏ Отказаться от разбора вывода
swanctlв restctl (можно использовать TDT-AG/swanmon)
Лицензия
Проект распространяется под ./LICENSE[лицензией MIT].