Pritunl error management socket exception

VPN уровня Enterprise для всех Привет! Мне захотелось раскрыть потенциал Pritunl, прикрыв его недостатки некоторыми доработками. Осмотрев все доступные по стоимости решения, Pritunl оказался единственным сервисом, который смог закрыть наши потребности. В этой статье описан процесс сборки кластера и базовые настройки, чтобы пользователь подключался с паролем от AD DS и вторым фактором в виде […]

Содержание

  1. VPN уровня Enterprise для всех
  2. Почему VPN и Pritunl
  3. Как это устроено
  4. Подготовка
  5. Установка
  6. Настройка после установки
  7. Подключение
  8. Автоматизируем рутину встроенным API
  9. Заключение

VPN уровня Enterprise для всех

Привет! Мне захотелось раскрыть потенциал 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 сервера)
Для наглядности на схеме отображены как разные ВМ

Схема сети 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

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

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

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

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

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

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

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

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

При каждом входе с полученным ранее паролем, вас будут встречать окно первичной настройки — укажите новый пароль, и публичный адрес хоста (тот, который сейчас показывает админку). 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

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

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

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

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

<< external_ip >> — локальный IP адрес интерфейса, который смотрит в интернет

<< external_gateway >> — локальный IP шлюза интерфейса, который смотрит в интернет

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

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

Подключение

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

В разделе 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.

Заключение

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

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

Upd: Добавил записи iptables, разрешающие пересылку в сессиях.

Источник

from pritunl.server.listener import * from pritunl.constants import * from pritunl.helpers import * from pritunl import settings from pritunl import logger from pritunl import utils from pritunl import mongo from pritunl import clients from pritunl import ipaddress from pritunl import monitoring from pritunl import database import os import time import datetime import threading import socket import uuid import random import bson class ServerInstanceCom(object): def __init__(self, svr, instance): self.server = svr self.instance = instance self.sock = None self.sock_lock = threading.Lock() self.socket_path = instance.management_socket_path self.bytes_lock = threading.Lock() self.bytes_recv = 0 self.bytes_sent = 0 self.client = None self.clients = clients.Clients(svr, instance, self) self.client_bytes = {} self.client_bytes_period = {} self.cur_timestamp = utils.now() self.bandwidth_rate = settings.vpn.bandwidth_update_rate @cached_static_property def users_ip_collection(cls): return mongo.get_collection(‘users_ip’) def sock_send(self, data): self.sock_lock.acquire() try: self.sock.send(data.encode()) finally: self.sock_lock.release() def client_kill(self, client_id): self.clients.disconnected(client_id) self.sock_send(‘client-kill %sn % client_id) def send_client_auth(self, client_id, key_id, client_conf): self.sock_send(‘client-auth %s %sn%snENDn % ( client_id, key_id, client_conf)) def send_client_deny(self, client_id, key_id, reason, client_reason=None): self.sock_send(‘client-deny %s %s «%s»%sn % (client_id, key_id, reason, ((‘ «%s»‘ % client_reason) if client_reason else »))) self.push_output(‘ERROR User auth failed «%s»‘ % reason) def push_output(self, message): self.server.output.push_message(message) def parse_bytecount(self, client_id, bytes_recv, bytes_sent): self.bytes_lock.acquire() try: _, bytes_recv_prev, bytes_sent_prev = self.client_bytes.get( client_id, (None, 0, 0)) cur_bytes_recv, cur_bytes_sent = self.client_bytes_period.get( client_id, (0, 0)) self.client_bytes[client_id] = ( self.cur_timestamp, bytes_recv, bytes_sent) self.client_bytes_period[client_id] = ( cur_bytes_recv + (bytes_recv bytes_recv_prev), cur_bytes_sent + (bytes_sent bytes_sent_prev), ) self.bytes_recv += bytes_recv bytes_recv_prev self.bytes_sent += bytes_sent bytes_sent_prev finally: self.bytes_lock.release() def parse_line(self, line): if self.client: if line == ‘>CLIENT:ENV,END’: cmd = self.client[‘cmd’] if cmd == ‘connect’: self.clients.connect(self.client) elif cmd == ‘reauth’: self.clients.connect(self.client, reauth=True) elif cmd == ‘connected’: self.clients.connected(self.client.get(‘client_id’)) elif cmd == ‘disconnected’: self.clients.disconnected(self.client.get(‘client_id’)) self.client = None elif line.startswith(‘>CLIENT:ENV’): env_key, env_val = line[12:].split(‘=’, 1) if env_key == ‘tls_id_0’: tls_env = ».join(x for x in env_val if x in VALID_CHARS) o_index = tls_env.find(‘O=’) cn_index = tls_env.find(‘CN=’) if o_index < 0 or cn_index < 0: self.send_client_deny(self.client, ‘Failed to parse org_id and user_id’) self.client = None return if o_index > cn_index: org_id = tls_env[o_index + 2:] user_id = tls_env[3:o_index] else: org_id = tls_env[2:cn_index] user_id = tls_env[cn_index + 3:] self.client[‘org_id’] = database.ParseObjectId(org_id) self.client[‘user_id’] = database.ParseObjectId(user_id) elif env_key == ‘IV_HWADDR’: self.client[‘mac_addr’] = utils.filter_str(env_val) elif env_key == ‘untrusted_ip’: self.client[‘remote_ip’] = utils.filter_str(env_val) elif env_key == ‘untrusted_ip6’: remote_ip = utils.filter_str(env_val) if remote_ip.startswith(‘::ffff:’): remote_ip = remote_ip.split(‘:’)[1] self.client[‘remote_ip’] = remote_ip elif env_key == ‘IV_PLAT’ and not self.client.get(‘platform’): if ‘chrome’ in env_val.lower(): env_val = ‘chrome’ self.client[‘device_id’] = uuid.uuid4().hex self.client[‘device_name’] = ‘chrome-os’ self.client[‘platform’] = utils.filter_str(env_val) elif env_key == ‘UV_ID’: self.client[‘device_id’] = utils.filter_str(env_val) elif env_key == ‘UV_NAME’: self.client[‘device_name’] = utils.filter_str(env_val) elif env_key == ‘UV_PLATFORM’: self.client[‘platform’] = utils.filter_str(env_val) elif env_key == ‘username’: self.client[‘username’] = utils.filter_str(env_val)[:128] elif env_key == ‘password’: self.client[‘password’] = env_val else: self.push_output(‘CCOM> %s’ % line[1:]) elif line.startswith(‘>BYTECOUNT_CLI’): client_id, bytes_recv, bytes_sent = line.split(‘,’) client_id = client_id.split(‘:’)[1] self.parse_bytecount(client_id, int(bytes_recv), int(bytes_sent)) elif line.startswith(‘>CLIENT:CONNECT’): _, client_id, key_id = line.split(‘,’) self.client = { ‘cmd’: ‘connect’, ‘client_id’: client_id, ‘key_id’: key_id, } elif line.startswith(‘>CLIENT:REAUTH’): _, client_id, key_id = line.split(‘,’) self.client = { ‘cmd’: ‘reauth’, ‘client_id’: client_id, ‘key_id’: key_id, } elif line.startswith(‘>CLIENT:ESTABLISHED’): _, client_id = line.split(‘,’) self.client = { ‘cmd’: ‘connected’, ‘client_id’: client_id, } elif line.startswith(‘>CLIENT:DISCONNECT’): _, client_id = line.split(‘,’) self.client = { ‘cmd’: ‘disconnected’, ‘client_id’: client_id, } elif line.startswith(‘SUCCESS:’): self.push_output(‘COM> %s’ % line) def wait_for_socket(self): for _ in range(10000): if os.path.exists(self.socket_path): return time.sleep(0.001) if self.instance.sock_interrupt: return logger.error(‘Server management socket path not found’, ‘server’, server_id=self.server.id, instance_id=self.instance.id, socket_path=self.socket_path, ) def on_msg(self, evt): msg = evt[‘message’] event_type = msg[0] if event_type == ‘user_disconnect’: user_id = msg[1] self.clients.disconnect_user(user_id) elif event_type == ‘user_disconnect_id’: user_id = msg[1] client_id = msg[2] server_id = None if len(msg) > 3: server_id = msg[3] self.clients.disconnect_user_id(user_id, client_id, server_id) elif event_type == ‘user_disconnect_mac’: user_id = msg[1] host_id = msg[2] mac_addr = msg[3] server_id = None if len(msg) > 4: server_id = msg[4] self.clients.disconnect_user_mac(user_id, host_id, mac_addr, server_id) elif event_type == ‘user_reconnect’: user_id = msg[1] host_id = msg[2] server_id = None if len(msg) > 3: server_id = msg[3] self.clients.reconnect_user(user_id, host_id, server_id) elif event_type == ‘route_advertisement’: server_id = msg[1] vpc_region = msg[2] vpc_id = msg[3] network = msg[4] if server_id != self.server.id: return self.instance.reserve_route_advertisement( vpc_region, vpc_id, network) @interrupter def _watch_thread(self): try: while True: self.cur_timestamp = utils.now() timestamp_ttl = self.cur_timestamp datetime.timedelta( seconds=180) for client_id, (timestamp, _, _) in list( self.client_bytes.items()): if timestamp < timestamp_ttl: self.client_bytes.pop(client_id, None) self.client_bytes_period.pop(client_id, None) else: self.bytes_lock.acquire() bytes_sent, bytes_recv = self.client_bytes_period.get( client_id, (0, 0)) self.client_bytes_period[client_id] = (0, 0) self.bytes_lock.release() client = self.clients.get_client(client_id) if client: monitoring.insert_point(‘user_bandwidth’, { ‘org_id’: client.get(‘org_id’), ‘org_name’: client.get(‘org_name’), ‘user_id’: client.get(‘user_id’), ‘user_name’: client.get(‘user_name’), ‘device_id’: client.get(‘device_id’), ‘device_name’: client.get(‘device_name’), }, { ‘bytes_sent’: bytes_sent, ‘bytes_recv’: bytes_recv, }) self.bytes_lock.acquire() bytes_recv = self.bytes_recv bytes_sent = self.bytes_sent self.bytes_recv = 0 self.bytes_sent = 0 self.bytes_lock.release() monitoring.insert_point(‘server_bandwidth’, { ‘host’: settings.local.host.name, ‘server’: self.server.name, }, { ‘bytes_sent’: bytes_sent, ‘bytes_recv’: bytes_recv, }) monitoring.insert_point(‘server’, { ‘host’: settings.local.host.name, ‘server’: self.server.name, }, { ‘device_count’: self.clients.clients.count({}), }) if bytes_recv != 0 or bytes_sent != 0: self.server.bandwidth.add_data( utils.now(), bytes_recv, bytes_sent) yield interrupter_sleep(self.bandwidth_rate) if self.instance.sock_interrupt: return except GeneratorExit: raise except: try: self.push_output(‘ERROR Management rate thread error’) except: pass logger.exception(‘Error in management rate thread’, ‘server’, server_id=self.server.id, instance_id=self.instance.id, ) self.instance.stop_process() def _socket_thread(self): try: self.connect() time.sleep(1) self.sock_send(‘bytecount %sn % self.bandwidth_rate) add_listener(self.instance.id, self.on_msg) data = » while True: data += self.sock.recv(SOCKET_BUFFER).decode() if not data or self.instance.sock_interrupt: if not self.instance.sock_interrupt and not check_global_interrupt(): self.instance.stop_process() self.push_output( ‘ERROR Management socket exited unexpectedly’) logger.error(‘Management socket exited unexpectedly’) return lines = data.split(n) data = lines.pop() for line in lines: line = line.strip() if not line: continue try: self.parse_line(line) except: logger.exception(‘Failed to parse line from vpn com’, ‘server’, server_id=self.server.id, instance_id=self.instance.id, line=line, ) except: if not self.instance.sock_interrupt: self.push_output(‘ERROR Management socket exception’) logger.exception(‘Error in management socket thread’, ‘server’, server_id=self.server.id, instance_id=self.instance.id, socket_path=self.socket_path, ) time.sleep(0.3) self.instance.stop_process() finally: remove_listener(self.instance.id) self.clients.stop() def _stress_thread(self): try: i = 0 for org in self.server.iter_orgs(): for user in org.iter_users(): if user.type != CERT_CLIENT: continue i += 1 client = { ‘client_id’: i, ‘key_id’: 1, ‘org_id’: org.id, ‘user_id’: user.id, ‘mac_addr’: utils.rand_str(16), ‘remote_ip’: str( ipaddress.ip_address(100000000 + random.randint( 0, 1000000000))), ‘platform’: ‘linux’, ‘device_id’: str(bson.ObjectId()), ‘device_name’: utils.random_name(), } self.clients.connect(client) except: logger.exception(‘Error in stress thread’, ‘server’, server_id=self.server.id, instance_id=self.instance.id, socket_path=self.socket_path, ) def connect(self): self.wait_for_socket() if self.instance.sock_interrupt: return self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(self.socket_path) def start(self): thread = threading.Thread(target=self._socket_thread) thread.daemon = True thread.start() thread = threading.Thread(target=self._watch_thread) thread.daemon = True thread.start() thread = threading.Thread(target=self.clients.ping_thread) thread.daemon = True thread.start() self.clients.start() if settings.vpn.stress_test: thread = threading.Thread(target=self._stress_thread) thread.daemon = True thread.start()

Я установил pritunl на своем VPS. Теперь я не могу получить доступ к веб-интерфейсу. Невозможно запустить услугу (посмотрите журналы ниже). Там есть Mariadb DB, кроме mongodb, который включен в pritunl, это проблема? (Мне нужен мариадб для других приложений, вот почему) Я следовал официальному руководству для CentOS. Кажется, есть некоторые недостающие предпосылки, но на самом деле я не знаю. Кто-нибудь может мне помочь? :-)

Благодарю! Moejoe

pritunl logs
[undefined][2017-01-30 21:45:51,211][ERROR] Pritunl setup failed
Traceback (most recent call last):
File "/usr/lib/pritunl/lib/python2.7/site-packages/pritunl/setup/__init__.py", line 68, in setup_db
setup_mongo()
File "/usr/lib/pritunl/lib/python2.7/site-packages/pritunl/setup/mongo.py", line 65, in setup_mongo
serverSelectionTimeoutMS=MONGO_SOCKET_TIMEOUT,
File "/usr/lib/pritunl/lib/python2.7/site-packages/pymongo/mongo_client.py", line 345, in __init__
seeds.update(uri_parser.split_hosts(entity, port))
File "/usr/lib/pritunl/lib/python2.7/site-packages/pymongo/uri_parser.py", line 244, in split_hosts
raise ConfigurationError("Empty host "
ConfigurationError: Empty host (or extra comma in host list).
Traceback (most recent call last):
File "/usr/bin/pritunl", line 9, in <module>
load_entry_point('pritunl==1.26.1231.99', 'console_scripts', 'pritunl')()
File "/usr/lib/pritunl/lib/python2.7/site-packages/pritunl/__main__.py", line 264, in main
setup.setup_db()
File "/usr/lib/pritunl/lib/python2.7/site-packages/pritunl/setup/__init__.py", line 68, in setup_db
setup_mongo()
File "/usr/lib/pritunl/lib/python2.7/site-packages/pritunl/setup/mongo.py", line 65, in setup_mongo
serverSelectionTimeoutMS=MONGO_SOCKET_TIMEOUT,
File "/usr/lib/pritunl/lib/python2.7/site-packages/pymongo/mongo_client.py", line 345, in __init__
seeds.update(uri_parser.split_hosts(entity, port))
File "/usr/lib/pritunl/lib/python2.7/site-packages/pymongo/uri_parser.py", line 244, in split_hosts
raise ConfigurationError("Empty host "
pymongo.errors.ConfigurationError: Empty host (or extra comma in host list).

Понравилась статья? Поделить с друзьями:
  • Prison architect ошибка связи со steam
  • Print cancelled error
  • Prisma generate error
  • Print a message of the thrown exception to system error
  • Prism3d engine ошибка как исправить