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!” (с)

Готово.