Привет! Мне захотелось раскрыть потенциал Pritunl, прикрыв его недостатки некоторыми доработками. Осмотрев все доступные по стоимости решения, Pritunl оказался единственным сервисом, который смог закрыть наши потребности.

В этой статье описан процесс сборки кластера и базовые настройки, чтобы пользователь подключался с паролем от AD DS и вторым фактором в виде OTP кода. Мы получили возможность ограничивать доступ групп пользователей не только по ip адресам, но и по портам, а также идентифицировать их по подсетям на конечных сервисах (а при желании и по связке ip с логином) внутри облака, не потеряв отказоустойчивость.

Дополнительно пара слов о том, как использовать API.

Почему VPN и Pritunl

Суть облачных сервисов всегда заключается в том, что ими кто-то пользуется. Наиболее безопасный способ защиты приватных сервисов - это ограничение сетевого доступа до этих сервисов.

Самые популярные из них:

  • Ограничить по ip адресам - если нужно пользоваться сервисом только из офиса или филиалов, где провайдер предоставил статический публичный адрес;

  • Спрятать за Zero-trust прокси - Удобный способ, где аутентификация выполняется на стороне SSO провайдера (например, через Google Workspace). Требует минимальной настройки рабочей станции, но нужно оплачивать/содержать оба сервиса, и возможность пользования ограничена только браузерами. При интеграции с третьим сервисом позволяет выполнять проверки конечного устройства на соответствие политикам безопасности;

  • Не открывать доступ из интернета, и использовать VPN подключение для доступа к ресурсам - нужно оплачивать/содержать VPN сервер и совместимое хранилище пользователей.

Решения VPN серверов, предлагаемые зарубежными вендорами сетевого оборудования очень часто решают почти все пожелания администраторов, но и стоят весьма ощутимо или даже неподъемно для стартапов. При этом, такое вложение не всегда окупается.

Как начинающая организация, мы обошли все популярные VPN решения, которые выдал поисковик, и выбрали Pritunl.

Плюсы этого решения:

  • Встроенный второй фактор. Мы не зависим от стоимости подписок на DUO или VPN сервис в условиях резкого роста пользователей;

  • Возможность разграничивать доступ группам пользователей. Реализовано с использованием нескольких сущностей серверов (в отличие от классического механизма с одной сущностью). Менять маршруты из коробки можно только останавливая сервис, но наша доработка, на той же основе, позволяет менять доступы без простоя, и ограничить по типу протоколов и портам;

  • Кластеризация. Пользователь подключается к любому из серверов, готовых принять соединение, есть возможность работы в режиме master-slave;

  • Простая установка профиля. Достаточно отправить пользователю ссылку, по которой можно отсканировать QR код с ключом для OTP, а клиент скачает профиль.
    По этой же ссылке можно скачать профиль вручную;

  • Интеграция с Active Directory. Реализована через Radius, но присутствует. Забирать группы пользователя не получится;

  • Кроссплатформенность. Pritunl создает конфиги, сертификаты, маршруты, и выступает в роли посредника при аутентификации для OpenVPN сервера. Можно использовать как Pritunl клиент, так и OpenVPN Connect, хотя на практике во втором не хватает функций вроде автообновления конфига или кеширования токена, вместо OTP.

  • Наличие API. Позволяет автоматизировать рутину по управлению пользователями, и выдаче ссылок;

  • Низкая стоимость. Лицензирование подписки на Enterprise версию стоит $70/месяц, бесплатно для образовательных учреждений;

  • Открытый исходный код.

Минусы:

  • Сама по себе установка VPN клиента и импорт профиля выглядят для конечного пользователя сложнее, чем прокси - вам нужно время и инженеры, чтобы решать проблемы пользователей с подключением и потерянными паролями или OTP секретами.

  • Иногда клиент не работает на устаревших ОС Windows - решается использованием OpenVPN Connect;

  • Разграничение доступа выполнено в формате разных сущностей серверов
    (в статье - на основе назначенных подсетей);

  • Ссылка с профилем и QR кодом истекает через сутки. Из коробки пользователь может зайти в админку с одним паролем и посмотреть второй фактор. Решается включением поддержки DUO или если открыть в интернет только ссылки с профилем и QR кодом;

  • Сервер некорректно распределяет пул адресов в маленьких подсетях, т.е. на сущности серверов нужно назначать подсети с маской /24 и ниже.

Как это устроено

Установку и настройку мы проводили в Яндекс Облаке, используя две зоны доступности, и дублируя все элементы:

  • Контроллер домена (с установленной ролью Radius сервера)
    Для наглядности на схеме отображены как разные ВМ

  • VPN сервер

  • MongoDB

Схема сети Pritunl
Схема сети Pritunl

VPN сервер создан с двумя интерфейсами - в приватной сети облака, и с публичным адресом. Такая схема позволяет отправлять весь трафик в интернет через NAT инстанс, т.е. не терять контроль над исходящим в интернет трафиком (фильтровать в одном месте по портам, ip адресам, или доменам).

Возможно у читателя возникнет вопрос зачем так много подсетей, если яндекс все равно маршрутизирует трафик между ними?

- Для ограничения широковещания и трафика на втором уровне. Коллеги из поддержки облака сказали, что группы безопасности фильтруют только на третьем уровне - по ip адресам. К тому же, подсети меняются гораздо реже, чем ip адреса машин - это позволяет реже менять правила на фаерволе.

Подготовка

Предполагаю, что у вас уже созданы и настроены:

  • Доступ в интернет из облака через NAT инстанс или виртуальный роутер,

  • Контроллеры домена с учетными записями,

  • NPS роль (Radius) на отдельной ВМ или контроллерах: docs.pritunl.com,

  • MongoDB кластер из двух хостов (управляемый кластер в я.облаке создается в три клика, есть возможность использовать ВМ с долей CPU),

  • Сервис, в который нужно предоставить доступ пользователям (может быть как внутри, так и снаружи),

  • ВМ для VPN серверов в обеих зонах - в рамках статьи будем ориентироваться на Ubuntu, как на самый распространенный и упрощенный вариант.

  • А так же, есть утвержденный пул приватных адресов:
    Например, для зоны B мы используем 10.10.0.0/16, а для зоны C - 10.11.0.0/16. Чтобы виртуальные машины понимали куда возвращать трафик, нужно создать таблицу маршрутизации в облаке, в которой будет маршрут до подсети клиента через VPN сервер. Pritunl, как и OpenVPN в целом, не умеет назначать разные подсети в зависимости от хоста, т.е. у клиентов в разных зонах будет одинаковая подсеть, и пакет сможет вернуться только на какую-то одну ноду.

    Чтобы решить эту проблему, можно использовать Netmap средствами iptables - для этого мы создаем подсети клиентов в диапазоне 10.12.0.0/16, и адреса внутри него будут конвертироваться под зону хоста.

    Более классический вариант - просто использовать NAT, и все наши сервисы за VPN будут видеть только ip адрес Pritunl сервера (в рамках статьи этот вариант не рассматриваем).

Установка

Если вы уже используете Ansible для настройки серверов - то шаги установки можно посмотреть в репозитории: github.com

Сам по себе процесс установки Pritunl крайне прост - установить пакетным менеджером и указать путь для доступа к БД.

  • Список репозиториев: docs.pritunl.com

  • Пример установки от разработчика: docs.pritunl.com

    Важно обратить внимание на отключение ufw - он будет мешать Pritunl создавать правила iptables для доступа клиентов, но при этом вы лишаете ВМ фаерволла.
    В этой статье мы стираем правила Pritunl и пишем свои, но не лишним будет ограничить доступ только к определенным портам и средствами облака.

  • Дополнительно я рекомендую закрыть Web интерфейс за nginx прокси, чтобы строго задать протоколы шифрования: github.com

    (Pritunl использует свой прокси на Go, и кроме сертификата у него нет настроек).

  • Указать ссылку на подключение к БД можно через браузер или в конфиге: docs.pritunl.com или github.com

    Предварительно рекомендую положить сертификат яндекса в доверенные, чтобы не усложнять конфиг подключения: github.com

    Строку подключения можно получить в свойствах кластера я.облака, в разделе Python. Указывать нужно обе ноды кластера - приложение само опросит их и выберет мастера.

Настройка после установки

После подключения к MongoDB, можно логиниться в консоль и настраивать сам сервис. Логин по умолчанию - pritunl. Чтобы получить пароль в первый раз, выполните команду:

sudo pritunl default-password

При каждом входе с полученным ранее паролем, вас будут встречать окно первичной настройки - укажите новый пароль, и публичный адрес хоста (тот, который сейчас показывает админку). LetsEncrypt домен имеет смысл указывать если вы решили не использовать обратный прокси - Pritunl будет сам перевыпускать сертификат для https, используя проверки по http: docs.pritunl.com

Лицензирование

Почти весь функционал, который мы будем использовать, доступен только с подпиской, так что первым делом стоит её активировать. Пробный период можно оформить с валидной картой на 10 дней. По истечении этого срока весь расширенный функционал пропадет из админки, но сервер будет работать как обычно (v1.30.3116.68).

В качестве альтернативы, в целях обучения и для личного использования, можно взглянуть подробнее на эти кусочки кода:

Раздел Hosts

Проверьте, что все ноды на месте. Они появляются автоматически, после того, как успешно подключаются к базе данных. Какое-то взаимодействие между ними не нужно - все задачи назначаются сами, записями в БД.

Каждой из них нужно присвоить комфортное для вас название, и публичный адрес - это может быть ip или DNS имя (с одной или двумя записями). Мы решили использовать один fqdn для обоих хостов, чтобы можно было легко подключиться с OpenVPN клиента на смартфонах.

Раздел Users

Создайте организацию, внутри которой будут существовать пользователи и сущности VPN серверов. Мы сделали две организации - одна для пользователей, вторая для роутеров (site-to-site соединений).

Разделе Settings

Включите аудирование, и выключите Pin. При использовании прокси, включите Allow Reverse Proxy - чтобы сервер смотрел на ip из заголовка.

В поле Single Sign-On выберите Radius + DUO Security, укажите адрес Radius хоста (с указание порта), секрет, и выберите организацию по умолчанию.
Поля для DUO можно заполнить настоящими или вымышленными - это нужно для того, чтобы запретить пользователям доступ в админку Pritunl (т.к. она без второго фактора), и при этом сохранить возможность загрузки профилей и сканирования QR кодов, а так же доступ администраторам со вторым фактором. Не включая DUO, можно запретить пути на nginx, но вы закроете себе доступ в админку из вне - в таком случае должна быть запасная ssh прокси на случай потери пароля или сертификата.

Так же, обратите внимание на параметры кеширования - оно работает с OTP кодами посредством токена, хотя и не всегда docs.pritunl.com.

Галочка "Drop OpenVPN Permission" нам ничего не сломала и закрыла один из путей злоумышленникам.

Раздел Servers

Здесь живут настройки сущностей OpenVPN серверов с разными портами.
Для добавления новой нажмите Add Server. Мы создали их для каждой группы доступа. Одновременно пользователь подключается только к одному серверу, т.е. имеет только один набор прав. Например, 1С и HR портал, или только 1С для внешних сотрудников.

  • Названия удобно использовать по ролям сотрудников или списку ресурсов.

  • В поле DNS можно указать адрес контроллера домена, если ваших сервисов нет в публичных зонах.

  • Выберите протокол tcp, вместо udp, и укажите подсеть с маской не выше /24, т.е. 253 адреса и более (Pritunl закрепляет адреса за пользователями, и в маленьких подсетях начинаются проблемы с подключением не смотря на то, что пул свободен).

  • Поставьте галочку "Enable Google Authenticator" - вместе с авторизацией пользователя потребуется OTP код при подключении.

  • В разделе Groups напишите название сервера и пробел. Его же нужно указать в группах пользователя.

  • Если нажать Advanced, можно увидеть много интересных параметров:

    • Запрет на подключение с телефона/компьютера (чтобы не путать пользователей - мы делали доступ с телефона без OTP к единственному ресурсу, которые выполняет проверку сам)

    • Лимит на количество одновременно подключенных девайсов (Max Devices).

    • Обязательно поставьте в параметре "Replication Count" количество нод сервера - чтобы они принимали подключения одновременно (по умолчанию будет master-slave).

    • "Pre-connection message" лучше не добавлять, если планируете использовать OpenVPN Connect - он ругается на длинные файлы профилей.

Нажмите Add и прикрепите к созданному серверу организацию, добавьте оба хоста, и маршруты (без галочки NAT).

Далее мы будем настраивать правила iptables на подсеть этого сервера, так что маршруты можно добавить во все облако - 10.10.0.0/16 и 10.11.0.0/16. Удалите маршрут в 0.0.0.0 - чтобы в туннель не сворачивался весь трафик с устройства.

Теперь можно нажимать Start Server.
Чтобы внести какие-то изменения, сервер понадобится выключить и снова включить.

iptables

Pritunl ограничивает трафик на основе правил iptables, но для их изменения нужно останавливать сущность VPN сервера на всех нодах.

Мы будем использовать аналогичный механизм, не зависящий от маршрутов сервера/клиента, т.е. для внесения правок перезапуск больше не нужен. В отличие от Pritunl, наши правила будут привязаны к подсетям клиентов, а не к интерфейсам VPN серверов. Дополнительно появляется возможность ограничивать трафик по протоколам и портам.

Удалите или отключите ufw - это надстройка над iptables, которая упрощает его использование, но в нашем случае только создаст непредвиденное поведение.

Установите пакет iptables-persistent, и включите автозапуск его сервиса netfilter-persistent - он будет задавать правила iptables при запуске системы из файла /etc/iptables/rules.v4 и /etc/iptables/rules.v6.

Теперь пришло время создать rules.v4 и указать в нем наши правила. Сначала мы указываем таблицу, в которой будут созданы правила. Далее правило по умолчанию, которое будет применяться после проверки всех наших правил (разрешающие или запрещающие).

Схема прохождения пакета через таблицы в iptables: rakhesh.com

Подробнее о Connection states, которые мы используем, чтобы лишний раз не фильтровать трафик в одной сессии: access.redhat.com

*filter
# Запрещаем входящий трафик
:INPUT DROP
# Запрещаем пересылку пакетов (функционал роутера)
:FORWARD DROP
# Исходящий трафик блокируется в группах безопасности облака,
# и в NAT-инстансе или прокси, через который указан маршрут в 0/0
:OUTPUT ACCEPT

# Разрешаем ВМ стучаться в саму себя (например, nginx в 8443)
-A INPUT -i lo -j ACCEPT
# Разрешаем пакеты в установленных сессиях, или связанных с ними
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Дропаем пакеты, у которых потерялась связь сессии
-A INPUT -m conntrack --ctstate INVALID -j DROP

# Разрешаем пинг внутри облака - по желанию
-A INPUT -i eth0 -p icmp -j ACCEPT

# Разрешаем SSH из подсети VPN администраторов, и из Bastion ВМ. Используем три подсети, т.к. трафик может прийти с другой ноды
-A INPUT -s 10.10.65.0/24,10.11.65.0/24,10.12.65.0/24,10.10.14.3/32 -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Разрешаем https в админку Pritunl, и http для редиректа/выпуска Let's Encrypt сертификата из интерфейса с белым ip
-A INPUT -i eth1 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i eth1 -p tcp -m tcp --dport 443 -j ACCEPT

# Разрешаем tcp порт для созданного VPN сервера (сущности). Порт выбран автоматически, можно поменять в свойствах в админке Pritunl
-A INPUT -i eth1 -p tcp -m tcp --dport 15172 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# Разрешаем https для всех VPN клиентов на внутренний сервис смены пароля
-A FORWARD -s 10.12.64.0/18 -d 10.10.11.5/32 -p tcp -m tcp --dport 443 -j ACCEPT
-A FORWARD -s 10.12.64.0/18 -d 10.11.11.5/32 -p tcp -m tcp --dport 443 -j ACCEPT

# Разрешаем DNS в контроллеры домена для конкреной группы пользователей
-A FORWARD -s 10.12.66.0/24 -d 10.10.11.3/32 -p tcp -m tcp --dport 53 -j ACCEPT
-A FORWARD -s 10.12.66.0/24 -d 10.10.11.3/32 -p udp -m udp --dport 53 -j ACCEPT
-A FORWARD -s 10.12.66.0/24 -d 10.11.11.3/32 -p tcp -m tcp --dport 53 -j ACCEPT
-A FORWARD -s 10.12.66.0/24 -d 10.11.11.3/32 -p udp -m udp --dport 53 -j ACCEPT

# Для них же разрешаем доступ в Ландыш
-A FORWARD -s 10.12.66.0/24 -d 10.10.15.3/32 -p tcp -m tcp --dport 80 -j ACCEPT
-A FORWARD -s 10.12.66.0/24 -d 10.10.15.3/32 -p tcp -m tcp --dport 443 -j ACCEPT

# Другая группа, которая использует VPN как NAT прокси для сервиса, который еще не состоит в сети облака, и использует фильтр по ip
-A FORWARD -s 10.12.68.0/23 -d 93.184.216.34/32 -p tcp -m tcp --dport 80 -j ACCEPT
-A FORWARD -s 10.12.68.0/23 -d 93.184.216.34/32 -p tcp -m tcp --dport 443 -j ACCEPT

# Завершаем содержимое таблицы
COMMIT

*nat
# Фильтрация должна выполняться в соответствующей таблице.
:PREROUTING ACCEPT
:INPUT ACCEPT
:OUTPUT ACCEPT
:POSTROUTING ACCEPT

# вместо {{ dc_prefix }} должна быть указана подсеть текущей зоны
# Network mapping, чтобы сервисы внутри облака знали какой ноде возвращать ответ
-A POSTROUTING -s 10.12.64.0/18 -o eth0 -j NETMAP --to {{ dc_prefix }}.64.0/18
# Network mapping обратно, т.к. эта подсеть не маршрутизируется внутри наших VPN интерфейсов
-A PREROUTING -d {{ dc_prefix }}.64.0/18 -i eth0 -j NETMAP --to 10.12.64.0/18

# Включаем NAT, чтобы клиенты из 10.12.68.0/23 смогли открыть внешний сервис через интернет с порта VPN сервера - 
# по желанию, убираем точку отказа в виде NAT инстанса/прокси
-A POSTROUTING -s 10.12.64.0/18 -o eth1 -j MASQUERADE

# Чиним автообновление профиля в клиенте, которое сломалось когда мы сменили порт, чтобы его смог занять nginx прокси
-A PREROUTING -i eth1 -p tcp -m tcp --dport 8443 -j REDIRECT --to-port 443

# Завершаем содержимое таблицы
COMMIT

Теперь мы можем применить эти правила, используя iptables-restore. Чтобы посмотреть текущие правила, можно воспользоваться iptables-save.

iptables-restore < /etc/iptables/rules.v4

Pritunl меняет содержимое таблиц при старте серверов, поэтому нам нужно создать задачу, которая будет их перезаписывать. На практике, ежеминутного запуска оказалось достаточно:

# /etc/cron.d/iptables_cron
* * * * * root /bin/bash -c 'if [ $(/usr/bin/grep -c pritunl <(/usr/sbin/iptables-save)) -ne 0 ]; then /usr/sbin/iptables-restore /etc/iptables/rules.v4; fi'

Маршрутизация с двумя интерфейсами

В нашей схеме ВМ для Pritunl создана с двумя интерфейсами - первый для внутренней сети, второй смотрит в интернет. Зададим статические маршруты, чтобы прохождение трафика внутри ВМ было предсказуемым:

# /etc/iproute2/rt_tables

# reserved values
#
255	local
254	main
253	default
0	unspec
#
# local
#
#1	inr.ruhep

# Добавим таблицу для пакетов, пришедших на внешний адрес
300 external

{{ external_ip }} - локальный IP адрес интерфейса, который смотрит в интернет

{{ external_gateway }} - локальный IP шлюза интерфейса, который смотрит в интернет

# /etc/netplan/60-external-route-table.yaml

network:
  version: 2
  renderer: networkd
  ethernets:
    eth1:
      dhcp4-overrides:
        # Удаляем маршрут в 0/0 через этот интерфейс
        use-routes: false
      
      routing-policy:
      # Создаем правило для трафика, пришедшего на внешний интерфейс,
      # в котором указываем таблицу 300 - чтобы он не пошел на шлюз приватного интерфейса
      - from: {{ external_ip }}
        table: 300
        priority: 300
      
      routes:
      # В таблице 300 указываем маршрут до шлюза внешнего интерфейса,
      # который должен был прийти по DHCP
      - to: default
        via: {{ external_gateway }}
        table: 300

      # Указываем маршрут до внешнего сервиса через второй интерфейс - убирает
      # шлюз по умолчанию как лишнюю точку отказа, по желанию
      - to: 93.184.216.34
        via: {{ external_gateway }}

Маршруты в облаке

Осталось создать таблицу маршрутизации в облаке, и привязать ее к подсетям, в которых находятся остальные сервисы - чтобы они знали куда возвращать или отправлять трафик для VPN клиентов. Пример для зоны B:

resource "yandex_vpc_route_table" "proxy-vpn-b" {
  name       = "proxy-vpn-b"
  network_id = "${yandex_vpc_network.office.id}"

  static_route {
    destination_prefix = "0.0.0.0/0"
    next_hop_address   = "${yandex_compute_instance.nat-01-b.network_interface.0.ip_address}"
  }
  static_route {
    destination_prefix = "10.10.64.0/18"
    next_hop_address   = "10.10.13.3" # vpn-01-b
  }
  static_route {
    destination_prefix = "10.11.64.0/18"
    next_hop_address   = "10.11.13.3" # vpn-01-c
  }
}

Подключение

Теперь, когда мы все настроили, можно проверить работает ли это как задумано.

В разделе Users нажмите Add user и в поле name укажите логин пользователя из AD DS. Нажмите Advanced, и в разделе Type укажите Radius. В поле groups укажите группу так же, как и в сущности сервера, к которому пользователь будет подключаться.

Обратите внимание, что пользователь с типом Local будет авторизован без пароля - только с OTP кодом.
Сервер без опции "Enable Google Authenticator" не будет запрашивать OTP код. Это подойдет для подключения устройств типа роутера или телевизора - подключение выполняется автоматически, только на основе загруженного сертификата, при этом область сетевого доступа должна быть максимально ограничена, а доступ на такие устройства закрыт дополнительными средствами.

После того, как вы убедились, что все работает, нажмите на значок ссылки и передайте пользователю Temporary url to view profile links. В течение суток с этого момента, ему нужно отсканировать QR код приложением на телефоне и импортировать профиль в клиенте по этой ссылке.

Сам клиент можно загрузить здесь: client.pritunl.com

В случае ошибок с подключением, в свойствах пользователя можно посмотреть лог - там регистрируются ошибки авторизации. Обратите внимание, что при использовании OpenVPN Connect регистрируются сразу две ошибки - Radius и OTP (даже если проблема с чем-то одним).

Во вкладке Logs сверху можно увидеть ошибки соединения с Radius или DUO, если таковые будут.

Так же, присутствует лог сущности VPN сервера, но проблемы подключения клиентов он не покрывает.

При проблемах с проверкой OTP кодов, убедитесь, что на сервере и устройстве время синхронизируется и соответствует UTC. В Google Authenticator есть функция синка времени по запросу.

Не забудьте включить второй фактор для всех администраторов, имеющих доступ в админку Pritunl - в разделе Administrators.

Автоматизируем рутину встроенным API

По большей части, Pritunl разрабатывает один человек. Не смотря на это, документация описана в два раза полнее, чем у OpenVPN Access Server. Однако, API описан крайне сухо.

Базовую функцию для подписи запросов на Python можно найти по ссылке: pritunl.com.
Доступные обработчики автор продукта рекомендует смотреть прямо в коде: github.com.

Чтобы получить токен, перейдите во вкладку Administrators и нажмите на нужного пользователя. Поставьте галочку "Enable Token Authentication", затем задайте переменные API_TOKEN и API_SECRET полученными данными в вашей среде, и кликните Modify.

Такая вкладка есть только у пользователей с ролью Super User.

Далее укажите адрес админки Pritunl в переменную BASE_URL.

# Перед манипуляциями с пользователями, нам нужно узнать идентификатор организации:
auth_request('GET','/organization').content
# Задайте идентификатор нужной группы в переменную org_id

# Теперь попробуем создать пользователя.
# Импортируйте библиотеку json, т.к. в этом формате мы будем отправлять запрос на создание.
import json
# Переводим массив данных о новом пользователе в json, и выполняем запрос:
data = json.dumps({
    "name": t.testov,
    "auth_type": "radius",
    "groups": ["Landysh"]
  })
headers = {
  'Content-Type': 'application/json'
}
request = auth_request('POST', '/user/'+org_id, headers, data)

# Выполним новый запрос, чтобы получить ссылку, которую передадим пользователю:
response = json.loads(request.content)
user_id = response[0]['id']
request = auth_request('GET', '/key/'+org_id+'/'+user_id)
response = json.loads(request.content)
user_link = BASE_URL + response['view_url']


# Другой пример: Проводим аудит пользователей, у которых не назначен тип авторизации Radius
request = auth_request('GET','/user/'+org_id)
response = json.loads(request.content)
for user in response:
  if user['auth_type'] != 'radius':
    print(user['name'])

# Третий пример - находим дубли и выводим в формате csv
from datetime import date
users = {}
users_extra = {}
request = auth_request('GET','/user/'+org_id)
response = json.loads(request.content)
for user in response:
  if user['last_active'] != None:
    lastactive = date.fromtimestamp(user['last_active'])
    lastactive = lastactive.isoformat()
  else:
    lastactive = "Never"
  try: users[user["name"]]
  except KeyError:
    users[user["name"]] = lastactive
  else:
    try: users_extra[user["name"]]
    except KeyError:
      users_extra[user["name"]] = [users[user["name"]]]
      users_extra[user["name"]].append(lastactive)
    else:
      users_extra[user["name"]].append(lastactive)

for key in users_extra:
  print(key+", ",end="")
  print(*users_extra[key], sep=", ")

Заключение

Искренне надеюсь, что публикация была для вас полезна, а время, потраченное на развертывание оправдалось отличным ожидаемым результатом.

Буду рад ответить на Ваши вопросы и присмотреться к мыслям со стороны.
Пожалуйста, заполните опросник - это поможет мне лучше ориентироваться при написании следующих статей.

Комментарии (17)


  1. aborouhin
    05.09.2022 01:03

    В текущих условиях VPN, зависящий от сторонней инфраструктуры, да ещё и платный, - сомнительное решение.

    Может отвалиться возможность за него платить (кто уж как эту проблему сейчас решает, но мало у кого прямо железобетонные варианты, которые не могут перестать работать). Могут забанить со стороны поставщика услуг или заблокировать доступ к его серверам из России (обходить-то умеем, но обычно для этого VPN и используется...)

    В общем, начинал я в январе разворачивать Tailscale с Azure AD. Про Printunl как-то не знал, может и его бы выбрал вместо Tailscale тогда... Но в итоге в марте переполз на self-hosted OpenVPN + OpenLDAP/FreeRADIUS. Возможностей поменьше, но так как-то спокойнее.


    1. OneManStudio
      05.09.2022 08:17

      Pritunl и так есть бесплатно self-hosted с установкой в 2 клика. Единственная сейчас проблемка в том, что требуется репозиторий mongodb который немножко недоступен в рф.

      Мне Pritunl нравиться своей простотой, особенно для soho сегмента. Установить, настроить и пользоваться может даже аникей без опыта.


      1. inkvizitor68sl
        07.09.2022 00:43

        Ну бесплатный self-hosted только на 1 хост, если что.

        А с оплатой лицензии на pritunl проблемы всё те же - нужно намайнить нормальную банковскую карту (что для легального российского юр лица невозможно).


        1. OneManStudio
          07.09.2022 08:52

          Думаю если вам нужно уже не soho решение, то у вас уже не аникей+ работает. А дальше или нанимаются специалисты на N зарплату плюс опенсорс продукт или этой зарплатой оплачиваются корпоративные готовые решения плюс аникей поддерживающих(работающий с саппортом продукта). Бесплатно в любом случае не получится.


          1. inkvizitor68sl
            07.09.2022 12:56

            Проблема-то не в платности, а в том, что способов оплаты легальных нет для юр лица из РФ. $60 usd в месяц за такой продукт - фигня, только их как-то донести туда нужно ещё.


        1. Hello11 Автор
          07.09.2022 09:15

          Из-за этих проблем в России сейчас не проводят проверки - можно покапаться в исходном коде и жить так, пока не появится возможность оплатить. Соглашусь, что сумма небольшая для таких усложнений, но это вынужденная мера в текущей ситуации.


    1. Hello11 Автор
      05.09.2022 09:41
      +1

      @aborouhin Для нас это была как раз та причина, по которой пришлось изучить исходный код через месяц после развертывания)
      Про Tailscale поищу - не встретился при поиске решения


      1. aborouhin
        05.09.2022 10:16

        Т.е. централизованный сервер используется только чтобы проверить статус лицензии, а для основного функционала (включая SSO, OTP и прочие нужные в хозяйстве фишки) он никак не нужен? Тогда это интересно, посмотрю обязательно подробнее. У Tailscale по-другому, там-таки их сервер. Есть полностью self-hosted форк (headscale), но там свои приколы (типа необходимости приложение под Android пересобирать вручную, что делает его деплой и обновление, особенно для удалённых пользователей, не вполне тривиальной задачей)


        1. Hello11 Автор
          05.09.2022 10:28
          +1

          Централизованный сервер используется, чтобы:
          * Проверить лицензию (и скачивать каждый раз css стили, которые раскрывают расширенный функционал в админке на стороне клиента)
          * Выполнить авторизацию через SSO (подумываю переписать этот кусок на локальный Keycloak, чтобы прикрутить что-то вроде opswat)
          * Узнать публичные ip - дополнительный функционал, чтобы не указывать руками

          Все остальное - OTP, Radius, сам VPN работают без сервера.

          DUO работает через свой сервер - мы используем его только как дополнительный уровень для админов, без него все и дальше будет работать.

          Спасибо за наводку на headscale - с виду тоже выглядит заманчиво, будет что порекомендовать коллегам, не решившимся на пританул)


        1. inkvizitor68sl
          07.09.2022 00:47

          Для SSO/OTP нужен сервер pritunl: "SSO will not work with this api version! As Pritunls own authentication servers handle the whole SSO stuff, track instance ids and verify users I won't implement this part for privacy concerns (and also this would need to be securly implemented and need a database)."


          1. Hello11 Автор
            07.09.2022 09:23

            Это цитата из фейкового API, которое в целом я стараюсь не упоминать, дабы уважать чувства человека, который самостоятельно написал весь Pritunl и опубликовал для нас исходный код, не продавая закрыто подписки.
            Как я описывал в прошлом комментарии, для функционала SSO используется публичный сервер, организующий мост между VPN клиентом, сервером, и SSO провайдером. При этом, для использования Radius или DUO он не нужен.
            Для OTP в такой схеме необходимости так же нет - пользователь получает токен, на основе которого генерятся коды, а сервер сверяет эти коды, сгенерив их на основе этого же токена.


            1. inkvizitor68sl
              07.09.2022 12:53

              Не, ну я же специально намекал ну очень прозрачно, что существует и неофициальное API -)

              Мы лицензию себе продлили, но это сейчас, мягко говоря, не просто. Хорошо хоть владельцы явно не заворачивают российские лицухи.


    1. Hello11 Автор
      05.09.2022 09:45

      @OneManStudio Бесплатный Pritunl из коробки не позволяет использовать кластер и авторизацию через сторонний сервис - вы будете ограничены одной зоной доступности и пином в 8 цифр. Сертификат как способ в этом случае не считаю, т.к. он хранится и передается слишком открыто.


  1. karavan_750
    05.09.2022 02:55

    Pritunl оказался единственным сервисом, который смог закрыть наши потребности

    Потребности могли бы перечислить в явном виде.
    Непонятно, связка с AD и наличие OTP - это весь набор или его часть.


    1. Hello11 Автор
      05.09.2022 09:49

      Тут приходится ограничиваться и путаться из-за NDA, да и кому интересно что хотели от нас разные отделы?)
      Большинство из них точно были в плюсах.


  1. inkvizitor68sl
    07.09.2022 00:54

    Интеграция с Active Directory. Реализована через Radius, но присутствует. Забирать группы пользователя не получится;

    А она у вас заработала для самого VPN?

    А то я когда radius тестил - пароль радиусный только для входа в вебморду подходил, а сам openvpn шлёпал AUTH_FAILED в логи и в radius не приходил.
    Вроде фиксы были, но уже не проверял.


    1. Hello11 Автор
      07.09.2022 08:56

      Если работает в вебке - должен работать и при подключении. Может быть у вас два радиуса или контроллера, которые отвечают по-разному, или проблема в группах/втором факторе