IPMan: Kubernetes-оператор для IPSec VPN-туннелей

Логотип IPMan

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, однако содержит дополнительные поля:

  1. secretRef — заменяет секцию secrets конфигурационного файла StrongSwan. Ссылается на секрет, созданный на шаге 1, который хранит общий ключ (PSK).

  2. groupRef — связывает данное соединение с группой, определённой на шаге 2.

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

  4. if_id — должен быть уникальным в пределах одного узла, поскольку задаёт идентификатор XFRM-интерфейса, используемого StrongSwan и ядром Linux для маршрутизации IPSec-пакетов.

  5. 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:
      # Спецификация пода

Оператор автоматически:

  1. Выделит IP-адреса из указанного пула

  2. Настроит маршрутизацию для рабочих нагрузок

  3. Сконфигурирует записи FDB моста для обеспечения связи

Если приложению требуется привязаться к конкретному IP-адресу, а в пуле их несколько, заранее неизвестно, какой под получит какой адрес. Чтобы решить эту проблему, во всех рабочих подах устанавливается переменная окружения VXLAN_IP. В данном примере под может получить адрес 10.0.2.3/24 из пула, и эта переменная будет содержать значение 10.0.2.3.

Справочник по конфигурации

Устранение неполадок

При возникновении проблем с IPSec-соединениями:

  1. Проверьте статус IPSecConnection:

    kubectl get ipsecconnection -n ipman-system
    kubectl describe ipsecconnection example-connection -n ipman-system
  2. Проверьте журналы оператора:

    kubectl logs -n ipman-system -l app=ipman-controller
  3. Убедитесь, что аннотации подов соответствуют конфигурации IPSecConnection.

Мониторинг

IPMan теперь поддерживает мониторинг через Prometheus. Чтобы его включить:

  1. Установите global.monitoring.enabled в значение true в параметрах Helm

  2. Установите global.monitoring.release в имя, под которым установлен оператор Prometheus (например, если установка выполнялась командой helm install kps prometheus-community/kube-prometheus-stack, укажите значение "kps")

Дополнительные параметры конфигурации см. в файле helm/values.yaml.

Архитектура

Схема архитектуры IPMan

Описание

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].

© 2026 meganuke