Технологии безопасности

Сайт посвященный вопросам безопасности

Опыт маскировки OpenVPN-туннеля с помощью obfsproxy

Преамбула

В связи с наметившимися тенденциями решил я обфусцировать свой скромный OpenVPN-туннель, просто чтобы набить руку — мало ли пригодится…

Дано: дешевая VPS с белым IP, работающая под Ubuntu Trusty Server Edition и служащая OpenVPN сервером.
Требуется: по-возможности замаскировать OpenVPN туннель под обычный трафик, желательно без изобретения велосипедов.

obfsproxy

Поскольку на VPS уже была поднята Tor-нода, мысль об obfsproxy пришла сама собой. Как оказалось, утилита действительно умеет притворяться SOCKS-proxy, а значит, обфусцировать не только соединения Tor, но и практически произвольный трафик. Пошарившись по Сети, я нашел несколько мануалов, но многие были либо устаревшими, либо выпускали из виду необходимые подробности.

Важно! Версии.

OpenVPN версий младше 2.3.4 содержит баг, мешающей нормальной работе с SOCKS прокси. Если вы хотите использовать режим socks, убедитесь, что у вас установлена подходящая версия OpenVPN.
Судя по всему, obfsproxy до сих пор не включен в репозиторий Tor, а в репозиториях Debian/Ubuntu могут лежать старые версии без подержки scramblesuit. В этом случае obfsproxy нужно устанавливать с помощью утилиты pip (пакет python-pip):
pip install obfsproxy

По окончании установки убедитесь, что используется версия obfsproxy с поддержкой scramblesuit:

obfsproxy scramblesuit -h

Версию obfsproxy для Windows можно извлечь из пакета Tor Browser, найдя подкаталог TorPluggableTransports.

Из man obfsproxy можно почерпнуть следующее

obfsproxy [logging_options] [—data-dir path] transport [—dest destination] [—password BASE32PASS] mode listen_addr

Чуть подробнее:

—data-dir statepath — каталог для размещения временных данных. Необходим, если obfsproxy работает не в managed режиме.
transport — выбирает метод обфускации трафика и режим его передачи. Нас интересуют следующие транспорты:

scramblesuit — сравнительно новый метод, который мы и будем использовать.
dummy — отладочный метод, при котором трафик передаётся как есть, без обфускации.

mode — выбирает режим работы прокси. Нас интересуют следующие режимы:

server — принимает клиентские соединения, деобфусцирует и перенаправляет на адрес destination. Этот режим пригодится на стороне сервера.
client — прозрачное проксирование: принимает данные, обфусцирует и отправляет серверу по адресу destination. То, что мы будем использовать на клиентской стороне.
socks — имитация socks-прокси.

Разница между режимами client и socks в том, что в первом случае адрес удаленного хоста, к которому пытается подключиться целевое приложение, должен быть заменен на адрес opbfsproxy-клиента, тогда как во втором случае целевое приложение использует obfsproxy как SOCKS-прокси. Для наших целей подойдёт и client.

—dest destination — для сервера: задаёт пару адрес-порт, куда будут перенаправляться деобфусцированные соединения. Для клиента: задаёт адрес obfsproxy сервера.
—password BASE32PASS — задание ключа(pre-shared key) для метода scramblesuit. Ключ должен быть строкой длиной не менее 20 символов, закодированной в base32, и совпадать на клиенте и на сервере. Подходящий ключ можно сгенерировать так:
python -c "import os,base64; print base64.b32encode(os.urandom(20))"

—password-file filepath — альтернативный метод задания ключа для метода scramblesuit, на случай если вы не хотите писать pre-shared key в открытом виде.
log_options включает в себя следующие ключи:

—log-file path — задает расположение журнала.
—log-min-severity level — События какого уровня (debug, info, warning, error) записывать.
—no-safe-logging — по умолчанию obfsproxy вырезает адреса из лога, но данный ключ позволяет изменить это поведение.
—no-log — отключает ведение журнала совсем (по умолчанию).

Сам VPN-сервер можно либо спрятать за obfsproxy полностью (заставив слушать 127.0.0.1), либо оставить его на внешнем сетевом интерфейсе на случай отказа obfsproxy. Я выбрал второй вариант как более надежный.
Таким образом, для запуска obfsproxy на серверной стороне достаточно выполнить что-то вроде:

obfsproxy —log-file /var/log/obfsproxy-openvpn.log —log-min-severity info scramblesuit —password BASE32LONGPASS —dest 1.2.3.4:1800 server 1.2.3.4:81

где 1.2.3.4 — внешний IP хоста, 1800 — порт, который слушает OpenVPN, 81 — порт, который будет слушать obfsproxy.
Серьезным недостатком является неумение obfsproxy запускаться в режиме демона, но это можно обойти.

Запуск на клиенте практически идентичен серверу:

obfsproxy scramblesuit —password BASE32LONGPASS —dest 1.2.3.4:81 {client|socks} 127.0.0.1:81

где 1.2.3.4:81 — адрес, который слушает obfsproxy-сервер, 127.0.0.1:81 — адрес, который слушает клиент.

Настройка OpenVPN

Важно: OpenVPN должен использовать протокол TCP.
Если мы используем режим client, то просто заменяем адрес удаленного сервера в конфигурационном файле openvpn на адрес локального obfsproxy:

#remote 1.2.3.4 1800 #заменяем старый адрес сервера
remote 127.0.0.1 81 #вместо этого пытаемся подключиться к клиентской части obfsproxy

Режим socks
Для работы в режиме socks настраиваем OpenVPN немножко по-другому:
#remote 1.2.3.4 1800 #заменяем старый адрес сервера
remote 1.2.3.4 81 #на адрес obfsproxy-сервера (важно! иначе работать не будет)
socks-proxy-retry
socks-proxy 127.0.0.1 81 #пытаемся задействовать socks proxy

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

Если openvpn-трафик блокируется лишь иногда (например, только в рабочей сети), можно использовать следующий прием. В файле конфигурации OpenVPN запишем следующее:

<connection>
remote 1.2.3.4 1800 #сначала пытаемся подключиться напрямую
</connection>
<connection>
remote 127.0.0.1 1800 #если не удалось, подключаемся через obfsproxy
</connection>

OpenVPN будет пытаться задействовать каждую секцию <connection> поочередно, пока не произведет успешное подключение.

Что касается запуска obfsproxy, к сожалению, сам OpenVPN не поддерживает pre-connect скрипты (запуск команды перед попыткой подключения). Вы можете попытаться установить один из неофициальных патчей, запускать obfsproxy вручную, либо воспользоваться одним из следующих методов.

openvpn-gui для Windows

Если вы используете обертку openvpn-gui для подключения вручную (или автоматически с ключом —connect), то проще всего будет создать в папке config файл XXXXXX_pre.bat, где XXXXXX — имя файла конфигурации (без расширения .ovpn). Этот файл будет автоматически выполнен перед тем как openvpn-gui попытается подключиться. Содержимое файла будет выглядеть примерно так:

start "window title" /MIN "%USERPROFILE%Tor BrowserTorPluggableTransportsobfsproxy.exe" —log-min-severity info —data-dir "%TEMP%obfs-openvpn" scramblesuit —password-file obfsproxy.key —dest 1.2.3.4:1801 client 127.0.0.1:1800

window title — заголовок окна, необходим.
%USERPROFILE%DesktopTor BrowserTorPluggableTransportsobfsproxy.exe — путь к исполняемому файлу obfsproxy. По умолчанию Tor Browser ставится на рабочий стол, хотя это и не обязательно.
%TEMP%obfs-openvpn — путь для размещения файлов состояния obfsproxy.
obfsproxy.key — путь к файлу с ключом, если вы его используете.
1.2.3.4:1801 — адрес, который слушает серверная часть obfsproxy.
127.0.0.1:1800 — адрес, который будет слушать клиентская часть.

Использование команды start (без ключа /wait она работает как аналог оператора & в shell) необходимо, так как openvpn-gui ожидает завершения pre-up скрипта перед попыткой подключения. Недостатком этого метода является консольное окно, постоянно висящее в фоне. При желании его можно спрятать утилитой hidcon или аналогичной.

obfsproxy как сервис

Если OpenVPN запускается как сервис, то имеет смысл организовать запуск obfsproxy таким же образом.
Создать сервис можно утилитой srvany от MS или какой-либо альтернативной утилитой, вроде NSSM. Вопрос запуска приложения как службы Windows выходит за рамки статьи, так что я просто укажу, что такой подход позволит задавать зависимости от и для других сервисов, тем самым гарантируя что obfsproxy будет доступен в момент запуска OpenVPN.

Этот подход можно совместить с предыдущим, используя bat-файл, запускаемый openvpn-gui, для запуска созданного сервиса командой net start.

Влияние на скорость

Проверка проводилась посредством старого доброго speedtest.net используя один и тот же сервер в Лондоне (OpenVPN-сервер хостится в Латвии, клиент — в европейской части России) с клиентской машины под Windows 7.
С обфускацией: 105ms, 7Mbps/5Mbps в среднем.
Без обфускации: 105ms, 16Mbps/3.2Mbps(sic!) (скорость down гуляла от 14 до 18Mbps, скорость up была стабильна)
Без VPN: 55ms, 25Mbps/14Mbps (down от 23 до 26Mbps)

Честно говоря, медленный upload при отсутствии обфускации обескураживает — провайдер ничего не говорит о возможных урезаниях VPN трафика. Но в целом результаты вполне приемлемые. Остается надеяться, что мне не придется этим воспользоваться.