Pritunl: запуск VPN в Kubernetes

Автор: | 05/10/2022
 

Pritunl – VPN-сервер с пачкой дополнительных возможностей по безопасности и управлению доступами.

По сути – является просто обёрткой над OpenVPN, добавляя к нему такие себе Access Control Lists в виде Организаций, юзеров и роутов.

Задача – развернуть тестовый инстанс Pritunl в Kubernetes, что бы потрогать его изнутри.

Пока использовать будем бесплатную версию, попозже глянем платную. Различия и стоимость можно посмотреть вот тут>>>.

Запускать будем в Minikube, а для установки используем Helm-чарт от Dysnix.

Запуск Pritunl в Kubernetes

Создаём неймспейс:

[simterm]

$ kubectl create ns pritunl-local
namespace/pritunl-local created

[/simterm]

Добавляем репозиторий:

[simterm]

$ helm repo add dysnix https://dysnix.github.io/charts

[/simterm]

И устанавливаем чарт с Pritunl:

[simterm]

$ helm -n pritunl-local install pritunl dysnix/pritunl
...
Pritunl default access credentials:

export POD_ID=$(kubectl get pod --namespace pritunl-local -l app=pritunl,release=pritunl -o jsonpath='{.items[0].metadata.name}')
kubectl exec -t -i --namespace pritunl-local  $POD_ID pritunl default-password
...
export VPN_IP=$(kubectl get svc --namespace pritunl-local pritunl --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
echo "VPN access IP address: ${VPN_IP}"

[/simterm]

Проверяем поды:

[simterm]

$ kubectl -n pritunl-local get pod
NAME                               READY   STATUS    RESTARTS   AGE
pritunl-54dd47dc4d-672xw           1/1     Running   0          31s
pritunl-mongodb-557b7cd849-d8zmj   1/1     Running   0          31s

[/simterm]

Получаем логин-пароль из мастер-пода:

[simterm]

$ kubectl exec -t -i --namespace pritunl-local pritunl-54dd47dc4d-672xw pritunl default-password
...
Administrator default password:
  username: "pritunl"
  password: "zZymAt1tH2If"

[/simterm]

Находим Сервисы:

[simterm]

$ kubectl -n pritunl-local get svc
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
pritunl           LoadBalancer   10.104.33.93    <pending>     1194:32350/TCP   116s
pritunl-mongodb   ClusterIP      10.97.144.132   <none>        27017/TCP        116s
pritunl-web       ClusterIP      10.98.31.71     <none>        443/TCP          116s

[/simterm]

Тут LoadBalancer pritunl – для доступа клиентов к серверу ВПН, а сервис pritunl-web ClusterIP – для доступа к веб-интерфейсу.

Пробрасываем порт к Веб:

[simterm]

$ kubectl -n pritunl-local port-forward svc/pritunl-web 8443:443
Forwarding from 127.0.0.1:8443 -> 443
Forwarding from [::1]:8443 -> 443

[/simterm]

Открываем https://localhost:8443:

Логинимся, и попадаем в начальные настройки:

Тут в Public Address будет автоматом задан публичный адрес хоста, на котором запущен сам Притунл, и потом будет подставляться в клиентские конфиги как адрес хоста VPN.

Так как Pritunl у нас работает в Kubernetes, который работает в VirtualBox, который работает на Linux на обычном домашнем PC – то нам он не подходит, но к нему вернёмся позже. Пока можно оставить, как есть.

Остальные настройки нам пока не интересны.

Настройка Pritunl VPN

Organization, Users

См. Initial Setup.

Для объединения юзеров есть Группы – но они в полной версии, её увидим потом.

Также, юзеров можно сгруппировать через Organizations.

Переходим в Users, добавляем Organization:

Добавляем юзера:

PIN, email – опциональны, сейчас не нужны.

Pritunl Server и роуты

См. Server configuration.

Переходим в Servers, добавляем новый:

Тут:

  • DNS Server: к какому ДНС будем отправлять клиентов
  • Port, Protocol: порт и протокол для OpenVPN, который будет запущен “внутри” Притунла и будет принимать подключения от наших юзеров
  • Virtual Network: сеть, из пула адресов которой будем выделять приватные IP для клиентов

Virtual Network я бы выделил 172.16.0.0 – тогда у нас домашняя сеть, сеть Кубера и клиентские IP будут различаться – удобнее будет дебажить, см. IPv4 Private Address Space and Filtering.

При этом важно, что бы порт Сервера тут совпадал с портом и протоколом на LoadBalancer – 1194 TCP.

Т.е. запрос с рабочей машины пойдет по маршруту:

  • 192.168.3.0/24 – домашняя сеть
  • попадёт в сеть VirtualBox 192.168.59.1/24 (см. Proxy)
  • пойдёт на LoadBalancer в сети Кубера 10.96.0.0/12
  • а LoadBalancer отроутит запрос в Kubernetes Pod, в котором у нас OpenVPN слушает TCP порт 1194

Проверяем сам LoadBalancer:

[simterm]

$ kubectl -n pritunl-local get svc pritunl
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
pritunl   LoadBalancer   10.104.33.93   <pending>     1194:32350/TCP   22m

[/simterm]

Port 1194 – TCP. Со статусом Pending разберёмся чуть позже.

Указываем Virtual Network, порт и протокол для Server:

Далее, подключаем Организацию со всеми её пользователями:

Стартуем сервер:

Проверяем процесс и порт в Kubernetes Pod – видим наш OpenVNP Сервер на порту 1194:

[simterm]

$ kubectl -n pritunl-local exec -ti pritunl-54dd47dc4d-672xw -- netstat -anp | grep 1194
Defaulted container "pritunl" out of: pritunl, alpine (init)
tcp6       0      0 :::1194                 :::*                    LISTEN      1691/openvpn

[/simterm]

И идём фиксить LoabBalancer.

minikube tunnel

См. Kubernetes: Minikube, та LoadBalancer в статусі “Pending” для полной информации, сейчас просто вызываем minikube tunnel:

[simterm]

$ minikube tunnel
[sudo] password for setevoy: 
Status:
        machine: minikube
        pid: 1467286
        route: 10.96.0.0/12 -> 192.168.59.108
        minikube: Running
        services: [pritunl]
    errors: 
                minikube: no errors
                router: no errors
                loadbalancer emulator: no errors
...

[/simterm]

Проверяем Loadbalancer:

[simterm]

$ kubectl -n pritunl-local get svc pritunl
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
pritunl   LoadBalancer   10.104.33.93   10.104.33.93   1194:32350/TCP   139m

[/simterm]

Появился EXTERNAL-IP – проверяем подключение:

[simterm]

$ telnet 10.104.33.93 1194
Trying 10.104.33.93...
Connected to 10.104.33.93.
Escape character is '^]'.

[/simterm]

Возвращаемся к основным Settings, указываем Public Address == LoadBalancer IP :

OpenVPN – подключение к серверу

Переходим в Users, кликаем Download profile:

Распаковываем архив:

[simterm]

$ tar xfp local-user.tar

[/simterm]

И подключаемся с помощью обычного OpenVPN клиента:

[simterm]

$ sudo openvpn --config local-org_local-user_local-server.ovpn 
[sudo] password for setevoy: 
...
2022-10-04 15:58:32 Attempting to establish TCP connection with [AF_INET]10.104.33.93:1194 [nonblock]
2022-10-04 15:58:32 TCP connection established with [AF_INET]10.104.33.93:1194
...
2022-10-04 15:58:33 net_addr_v4_add: 172.16.0.2/24 dev tun0
2022-10-04 15:58:33 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
2022-10-04 15:58:33 Initialization Sequence Completed

[/simterm]

Но сейчас сеть работать не будет:

[simterm]

$ traceroute 1.1.1.1
traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
...

[/simterm]

Так как у нас в ВПН роут в 0.0.0.0/0 направлен через тот же хост, на котором собственно ВПН и работает – получается “кольцо”.

Переходим в Servers, останавливаем сервер и удаляем Default route:

Кликаем Add Route – добавим маршрут к 1.1.1.1 через наш ВПН, а все остальные запросы с клиента будут идти обычными маршрутами:

Запускаем подключение заново:

[simterm]

$ sudo openvpn --config local-org_local-user_local-server.ovpn

[/simterm]

Проверяем роуты на хост-машине, локально:

[simterm]

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.3.1     0.0.0.0         UG    100    0        0 enp38s0
1.1.1.1         172.16.0.1      255.255.255.255 UGH   0      0        0 tun0
...

[/simterm]

И проверяем сеть – запрос пошёл через VPN:

[simterm]

$ traceroute 1.1.1.1
traceroute to 1.1.1.1 (1.1.1.1), 30 hops max, 60 byte packets
 1  172.16.0.1 (172.16.0.1)  0.211 ms  41.141 ms  41.146 ms
 2  * * *
...

[/simterm]

“It works!” (с)

Готово.