В связи с последними событиями ко мне часто обращаются знакомые и друзья с просьбами помочь получить доступ к Instagram, Facebook, YouTube и остальным недоступным сайтам, где могла остаться важная информация. Чтобы помочь им, я всегда использовал OpenVPN Access Server, но у него есть ряд ограничений, которые мы попробуем обойти в этой статье.
Еще по теме: Установка OpenVPN на Debian
Что такое OpenVPN Access Server
OpenVPN Access Server — удобный инструмент, который устанавливается на сервере в пару команд и позволяет комфортно работать с клиентами: изменять подсети, профили, пароли и другие настройки. Делать все это вручную было бы намного тяжелее.
В бесплатной версии OpenVPN Access Server есть ограничение на число подключений: одновременно могут подключаться только два клиента. Платная лицензия, кроме того, что стоит дорого, ее теперь невозможно физически оплатить.
Как обойти ограничения OpenVPN Access Server
Мы не будем изобретать велосипед, а посмотрим, какое решение предлагает поисковик. Гуглим запрос «OpenVPN Access Server license unlimited». Находим один удаленный с GitHub репозиторий. Находим зеркало и читаем описание: требуется версия CentOS 7. В скрипте установки после инсталляции самого пакета openvpn-as меняется файл pyovpn-2.0-py2.7.egg по пути:
1 |
/usr/local/openvpn/python/sites-enabled |
Я бы не стал менять вслепую этот важный элемент системы, который отвечает том числе за веб‑фронтенди. Тем более в ПО, связанным с конфиденциальностью. Поэтому для начала глянем, что там внутри.
Сравним модифицированные варианты файлов с исходными.
Вытащим оригинальный RPM в поисках pyovpn-2.0-py2.7.egg. Для распаковки .rpm сгодиться обычный tar:
1 |
$ tar xf openvs.rpm |
А .egg — обычный ZIP, который находится по адресу:
1 |
pyovpn-2.0-py2.7/usr/local/openvpn_as/lib/python/ |
Далее распаковываем модифицированный pyovpn-2.0-py2.7.egg из комплекта:
1 |
$ unzip pyovpn-2.0-py2.7.egg |
Попробуем отыскать файлы, в которых что‑то менялось.
1 2 3 4 |
$ diff -rq ./pyovpn-2.0-py2.7_hacked ./pyovpn-2.0-py2.7_original Files ./pyovpn-2.0-py2.7_hacked/pyovpn/lic/uprop.pyo and ./pyovpn-2.0-py2.7_original/pyovpn/lic/uprop.pyo differ Only in ./pyovpn-2.0-py2.7_hacked/pyovpn/lic: uprop2.pyo Files ./pyovpn-2.0-py2.7_hacked/pyovpn/production.pyo and ./pyovpn-2.0-py2.7_original/pyovpn/production.pyo differ |
Теперь сравним сами файлы, но прежде их нужно декомпилировать, потому что .pyc — это байт‑код. Для этого будем использовать утилиту decompile6, которая прекрасно работает с версиями Python 2.7, 3.7, 3.8.
В моем случае используется macOS, но в ОС Linux команды вряд ли отличаются.
1 2 3 |
$ pip install decompyle6 $ uncompyle6 /Users/n0a/Work/openvpn_decompile/test_diff/pyovpn-2.0-py2.7_hacked/pyovpn/lic/uprop.pyo > uprop.py $ cat uprop.py |
Глянем содержимое декомпилированного файла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ cat uprop.py import uprop2 old_figure = None def new_figure(self, licdict): ret = old_figure(self, licdict) ret['concurrent_connections'] = 1024 return ret for x in dir(uprop2): if x[:2] == '__': continue if x == 'UsageProperties': exec 'old_figure = uprop2.UsageProperties.figure' exec 'uprop2.UsageProperties.figure = new_figure' exec '%s = uprop2.%s' % (x, x) |
Интересно! Цикл for — это перебор всех атрибутов uprop2. Те атрибуты, названия которых начинаются с двух подчеркиваний, пропускаются. Функция old_figure становится ссылкой на метод figure класса UsageProperties, а функция в классе UsageFigure ссылается на new_figure.
Сложно сказать, зачем это сделано. Могу предположить, что класс UsageProperties используется где‑то еще, и, чтобы не менять везде, сделали такой неочевидный трюк.
Декомпилируем uprop2 и понимаем, что это оригинальный uprop, в котором и выполняется проверка лицензии.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
... class UsageProperties(object): def figure(self, licdict): proplist = set(('concurrent_connections',)) good = set() ret = None if licdict: for key, props in licdict.items(): if 'quota_properties' not in props: print 'License Manager: key %s is missing usage properties' % key continue proplist.update(props['quota_properties'].split(',')) good.add(key) for prop in proplist: v_agg = 0 v_nonagg = 0 if licdict: for key, props in licdict.items(): if key in good: if prop in props: try: nonagg = int(props[prop]) except: raise Passthru('license property %s (%s)' % (prop, props.get(prop).__repr__())) v_nonagg = max(v_nonagg, nonagg) prop_agg = '%s_aggregated' % prop agg = 0 if prop_agg in props: try: agg = int(props[prop_agg]) except: raise Passthru('aggregated license property %s (%s)' % (prop_agg, props.get(prop_agg).__repr__())) v_agg += agg if DEBUG: print 'PROP=%s KEY=%s agg=%d(%d) nonagg=%d(%d)' % (prop, key, agg, v_agg, nonagg, v_nonagg) apc = self._apc() v_agg += apc if ret == None: ret = {} ret[prop] = max(v_agg + v_nonagg, bool('v_agg') + bool('v_nonagg')) ret['apc'] = bool(apc) if DEBUG: print "ret['%s'] = v_agg(%d) + v_nonagg(%d)" % (prop, v_agg, v_nonagg) return ret |
Разработчик кряка использовал подмену объекта, не трогая основной файл. Принцип понятен.
Теперь попробуем провернуть то же самое, только для актуальной версии OpenVPN Aсcess Server. Экспериментировать буду на VPS с актуальной Debian 11 Bullseye.
Скачиваем последнюю версию с сайта разработчика. Устанавливаем или распаковываем архив .deb и смотрим версию Python, для которой написан pyovpn:
1 |
$ ls /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.9.egg |
Ага, 3.9, что не очень подходит, так как декомпиляция для этой версии Python пока не реализована.
Вы можете поддержать разработчика декомпилятора и помочь в работе над проектом. Подробнее — в его обращении.
Что‑то мне подсказывает, что версия для Debian 10 будет подходящей, так как, изучив вики, узнаем, что в 11-й версии (Bullseye) уже Python 3.9, а в 10-й (Buster) — 3.7.
Установка в Debian 10
Поскольку VPS новая, просто поменяю ОС на десятую версию и посмотрю доступные версии OpenVPS-AS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ apt update && apt -y install ca-certificates wget net-tools gnupg wget -qO - https://as-repository.openvpn.net/as-repo-public.gpg | apt-key add - echo "deb http://as-repository.openvpn.net/as/debian buster main">/etc/apt/sources.list.d/openvpn-as-repo.list $ apt update $ apt policy openvpn-as openvpn-as: Installed: (none) Candidate: 2.10.1-d5bffc76-Debian10 Version table: 2.10.1-d5bffc76-Debian10 500 500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages 2.10.0-ca1e86b5-Debian10 500 500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages 2.9.6-1090f6b3-Debian10 500 500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages 2.9.5-82d54e5b-Debian10 500 500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages 2.9.4-8b3ce898-Debian10 500 ... |
Отлично, версия последняя, как в 11-й, а значит, актуальная. Устанавливаем и смотрим версию Python, которая используется в pyovpn. Должно быть 3.7.
1 2 3 |
$ apt -y install openvpn-as $ ls /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg |
Так и есть: это последняя версия (2.10.1) и она использует Python 3.7. Все складывается. Давайте проверим, многое ли изменилось по сравнению с версией 2.0.5, которая была изначально найдена похеканной. Чтобы не таскать файлы туда‑сюда, ставлю на сервере python-decompile3:
1 2 3 |
$ git clone https://github.com/rocky/python-decompile3 $ cd python-decompile3 $ pip3 install -e |
И снова распаковываем .egg:
1 2 3 4 5 6 |
$ mkdir /opt/ovpn && cd ovpn $ cp /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg ./ $ cp /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg pyovpn-2.0-py3.7.zip $ unzip pyovpn-2.0-py3.7.zip && rm pyovpn-2.0-py3.7.zip $ ls EGG-INFO pyovpn |
Затем декомпилируем:
1 2 3 |
$ cd ./pyovpn/lic $ decompyle3 uprop.pyc > uprop.py $ cat uprop.py |
Смотрим отличия и видим, что все то же самое, кроме небольших расхождений в синтаксисе. Но что имел в виду автор хака, когда использовал два файла? Почему просто не указать явно в конце функции figure количество соединений, минуя все проверки?
Давайте попробуем более простой вариант. Добавляем ret['concurrent_connections'] = 1337 перед возвратом (ret):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ nano uprop.py ... apc = self._apc() v_agg += apc if ret == None: ret = {} ret[prop] = max(v_agg + v_nonagg, bool('v_agg') + bool('v_nonagg')) ret['apc'] = bool(apc) if DEBUG: print("ret['%s'] = v_agg(%d) + v_nonagg(%d)" % (prop, v_agg, v_nonagg)) ret['concurrent_connections'] = 1337 return ret def _apc(self): ... |
Сохраняем файл, компилируем:
1 2 3 4 |
$ python3 -m compileall uprop.py $ rm uprop.pyc uprop.py $ cp __pycache__/uprop.cpython-37.pyc ./uprop.pyc $ rm -Rf __pycache__ |
Архивируем и заменяем пакет .egg:
1 2 3 4 |
$ cd /opt/ovpn $ zip -r * $ sudo rm /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg $ sudo common.zip /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg |
И перезагружаем openvpn-as. Я удалял логи, так как ловил ошибки во время экспериментов. Это делать необязательно.
1 2 3 |
$ sudo service openvpnas stop $ sudo rm /var/log/openvpnas.log $ sudo touch /var/log/openvpnas.log |
Запускаем сервис openvpnas:
1 |
$ service openvpnas start |
Убеждаемся, что все в порядке и ошибок нет:
1 |
cat /var/log/openvpnas.log |
Идем в админку на порте 943/admin и видим, что нам доступно 1337 подключений.
Если вы вдруг потеряли пароль и не можете попасть в админку, напишите passwd openvpn.
Тестирование показало отличную работу с двумя и более устройствами.
Заключение
Многие VPN сейчас под блокировками или не могут принять оплату, поэтому Access Server — неплохой способ быстро развернуть собственный аналог с возможностью управлять профилями пользователей. Пользоваться ли таким методом активации — решать вам, но это, как оказалось, совсем не сложно.
ПОЛЕЗНЫЕ ССЫЛКИ:
Спасибо огромное! Работает!
$ sudo common.zip /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg
Здесь после sudo нужно поставить команду cp. Автор пропустил видимо. А так все четко. Работет.
Добрый день! Не работает, вероятно что-то упущено. После воспроизведения действий становится 0 подключений, Access Server version: 2.13.1
Я так понимаю что по состоянию на 09 08 2024 уже не работает
Еще бы почистить pycache в директории с исходниками и переустановить сам openvpn сервер.
Плюс, если есть активный ключ, то его удалить.
20.12.24 — работает