IKEv2 и WireGuard в отдельных Kubernetes-namespace

Published: 2026-06-12

Оба VPN-протокола работают внутри кластера k0s — не как обычные процессы на хосте, а как Kubernetes-поды. Это нетипично (большинство гайдов запускают VPN прямо на хосте), но делает управление секретами и деплой согласованным с остальной инфраструктурой.


IKEv2 / strongSwan

04-setup-vpn.sh разворачивает hwdsl2/ipsec-vpn-server в namespace vpn. Этот образ оборачивает strongSwan и настраивается автоматически через переменные окружения.

Ключевые настройки Deployment:

yamlspec:
  template:
    spec:
      hostNetwork: true
      containers:
        - name: vpn
          image: hwdsl2/ipsec-vpn-server
          securityContext:
            privileged: true
            capabilities:
              add: ["NET_ADMIN", "SYS_MODULE"]
          env:
            - name: VPN_IPSEC_PSK
              valueFrom:
                secretKeyRef:
                  name: vpn-secrets
                  key: VPN_IPSEC_PSK
            - name: VPN_USER
              valueFrom:
                secretKeyRef:
                  name: vpn-secrets
                  key: VPN_USER
            - name: VPN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: vpn-secrets
                  key: VPN_PASSWORD
          volumeMounts:
            - name: vpn-data
              mountPath: /opt/vpn-data
      volumes:
        - name: vpn-data
          hostPath:
            path: /opt/vpn-data
            type: DirectoryOrCreate

Зачем hostNetwork: true: strongSwan должен слушать UDP-порты хоста 500 и 4500 для IKE-согласования. Эти порты должны быть напрямую доступны клиентам — пропустить трафик через Kubernetes Service нельзя, потому что IKE использует source IP клиента как часть handshake.

Зачем privileged: true: нужен для загрузки модулей ядра IPsec. Это главный компромисс с точки зрения безопасности: привилегированный под может выбраться из контейнера на хост. Альтернатива — запустить strongSwan прямо на хосте — потребовала бы управлять им вне Kubernetes, что ломает операционную модель.


WireGuard

08-setup-wireguard.sh разворачивает linuxserver/wireguard в namespace wireguard. В отличие от strongSwan, WireGuard работает полностью в user space через реализацию wireguard-go в контейнере.

yamlspec:
  template:
    spec:
      hostNetwork: true
      containers:
        - name: wireguard
          image: linuxserver/wireguard
          securityContext:
            capabilities:
              add: ["NET_ADMIN", "SYS_MODULE"]
          env:
            - name: PEERS
              value: "phone,laptop,tablet"
            - name: SERVERURL
              value: "91.184.248.13"
            - name: SERVERPORT
              value: "51820"
            - name: PEERDNS
              value: "1.1.1.1"
            - name: INTERNAL_SUBNET
              value: "10.0.0.0/24"
          volumeMounts:
            - name: wg-data
              mountPath: /config
      volumes:
        - name: wg-data
          hostPath:
            path: /opt/wireguard-data

Переменная PEERS автоматически генерирует клиентский конфиг и QR-код для каждого пира. Получение конфигов:

bash# Получить конфиг WireGuard для пира laptop
kubectl exec -n wireguard deploy/wireguard -- cat /config/peer_laptop/peer_laptop.conf

# Получить QR-код для пира phone
kubectl exec -n wireguard deploy/wireguard -- cat /config/peer_phone/peer_phone.conf | qrencode -t ansiutf8

WireGuard использует UDP 51820. Поскольку UDP-трафик не может балансироваться iptables-правилами kube-proxy, hostNetwork: true тоже нужен.


Управление секретами

VPN-учётные данные хранятся как SealedSecrets:

yamlapiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: vpn-secrets
  namespace: vpn
spec:
  encryptedData:
    VPN_IPSEC_PSK: AgB...
    VPN_USER: AgB...
    VPN_PASSWORD: AgB...

Для ротации учётных данных:

  1. Обновить .env
  2. Перезапечатать: kubectl create secret generic vpn-secrets --from-env-file=.env --dry-run=client -o yaml | kubeseal ...
  3. Закоммитить новый SealedSecret
  4. Flux применяет изменения, под перезапускается с новыми учётными данными

Правила файрвола

Оба VPN-протокола требуют открытых портов:

bash# IKEv2
ufw allow 500/udp
ufw allow 4500/udp

# WireGuard
ufw allow 51820/udp

Зачем запускать VPN внутри k8s?

Критерий Kubernetes-под systemd-сервис на хосте
Управление секретами Kubernetes Secrets (как всё остальное) Конфиг-файл на хосте
Авторестарт restartPolicy: Always Restart=always (может навсегда зависнуть)
Наблюдаемость kubectl logs, pod-метрики journalctl + кастомный exporter
Безопасность Privileged pod (IKEv2) Процесс на хосте
Деплой kubectl apply + Flux SSH + systemctl

Реальный компромисс — privileged: true на поде IKEv2. WireGuard чище: только NET_ADMIN и SYS_MODULE, а в Linux 5.6+ модуль ядра уже встроен, так что SYS_MODULE даже не нужен. Можно дополнительно ужесточить настройки кастомным seccomp-профилем.


Проверка статуса VPN

bash# Статус IKEv2
kubectl exec -n vpn deploy/ipsec-vpn-server -- ipsec status

# Статус WireGuard
kubectl exec -n wireguard deploy/wireguard -- wg show

# Логи подов
kubectl logs -n vpn deploy/ipsec-vpn-server
kubectl logs -n wireguard deploy/wireguard