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...
Для ротации учётных данных:
- Обновить
.env - Перезапечатать:
kubectl create secret generic vpn-secrets --from-env-file=.env --dry-run=client -o yaml | kubeseal ... - Закоммитить новый SealedSecret
- 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