Shadowsocks. Почему лучше, чем обычный SOCKS, и практика использования.
Что такое SOCKS?
SOCKS - это протокол проксирования (посредования) соединений между клиентом и сервером, который призван обеспечить максимальную прозрачность для приложений. SOCKS в целом разрабатывался с целью обеспечения контролируемых соединений между хостами в приватной сети, которые находятся за фаерволом, и хостами в публичной сети Интернет. В настоящее время используемая версия протокола - это SOCKS версии 5, которая была разработана в 1996 году, и спецификация которой представлена в RFC1928. SOCKS был популярен как метод выхода в “большую сеть” до нынешнего NAT. Если сравнивать SOCKS с NAT, то можно отметить следующие моменты:
-
SOCKS работает как “прослойка” между приложением и траспортом, и на модели OSI его принято ставить на сессионный уровень. Отсюда возникает необходимость, чтобы само приложение поддерживало SOCKS, что может быть далеко не всегда. А еще возникает проблема распространения настроек SOCKS-сервера на хосты, что часто делается просто ручным вводом в приложение. В NAT такой проблемы не возникает. У хоста есть IP, шлюз по умолчанию и DNS-сервер (которые чаще всего приходят по DHCP), и для него взаимодейсвие с сетью Интернет прозрачно. Всей работой по трансляции адресов и удержанием сессий занимается устройство на периметре внутренней сети (обычно роутер или фаервол).
-
Но с другой стороны, SOCKS, так сказать, никого не обманывает. Когда устанавливается SOCKS-сессия, то SOCKS-прокси сообщает клиенту внешний IP-адрес и порт, который закреплен за сессией клиента. Это открывает возможность таким протоколам как FTP или SIP, которые содержат в теле своих пакетов инфомацию о IP-адресации, подставлять правильные данные сразу на выходе от клиента, без трансформации пакета устройствами “по пути”. Это также позволяет более простую эксплуатацию p2p-приложений, и стоит упомянуть поддержку со стороны SOCKS “реверс-прокси” и протокола UDP. В NAT чаще всего для такого включаются дополнительные фичи “сверху” основных функций, которые лезут в пакеты уровня приложения и трансформируют их. В вопросе p2p-приложений в случае маленькой сети проблема решается протоколом UPnP, который может не всегда поддерживаться периметровым устройством или самим приложением. А в жизни часто требуется ручной проброс портов.
-
С некоторой точки зрения, SOCKS обеспечивает больший контроль за трафиком (из-за самого факта разрыва соединения), а также поддерживает аутентификацию пользователя. В случае NAT контроль обеспечивается меньший, и нет аутентификации (если без применения дополнительных методов).
Но NAT естественным образом победил - несмотря на некоторые недостатки, эксплуатировать его оказалось гораздо легче, чем SOCKS, да и работает NAT быстрее.
После распространения NAT, SOCKS используется чаще как средство сокрытия реальных IP-адресов, обхода ограничений доступа в Интернет, а также как удобный транспорт для состыкования приложений и оверлейных сетей, таких как TOR и I2P. Все браузеры и многие IM-приложения имеют в своем распоряжении SOCKS-клиент.
Какие недостатки имеет SOCKS с точки зрения проксирования? Как минимум - SOCKS, в большинстве реализаций, не шифрует проксируемый трафик. Конечно, можно сказать, что поддержка шифрования - задача самого приложения или “прослойки” уровня выше, что чаще всего TLS/SSL. Это, в целом, правильно, но тут возникает другая проблема - проблема идентификации трафика средствами DPI (как самого SOCKS, так и проксируемых приложений), что может вести к ограничению применения SOCKS в некоторых сетях. И все-таки отсутствие универсальности шифрования для трафика, в том случае, если прокси используется как метод доступа к внутренним ресурсам приватной сети (если применение VPN невозможно или неоптимально по каким-либо причинам). И более того - при использовании метода аутентификации 0x02 “логин/пароль”, эти логин/пароль улетают на сервер в виде открытого текста, что вызывает риск их перехвата. Для правоты стоит сказать, что для безопасной аутентификации и защиты трафика, SOCKS поддерживает GSSAPI. Только вот GSSAPI штука довольно старая и сложная в настройке, и я, честно говоря, пока что не видел его поддержки в каком-либо приложении при настройке SOCKS, кроме curl.
Как же можно подключаться к прокси с шифрованием? Например, использовать S… SSH!
SSH-тоннели
SSH, как известно, поддерживает проксирование TCP-трафика “внутри” текущего SSH-соединения. Таких типов проксирования три:
- Local Port Forwarding
- Remote Port Forwarding
- Dynamic Port Forwarding
Первые 2 рассматривать в данной статье я не буду, но остановлю внимание на типе Dynamic Port Forwarding. SSH Dynamic Port Forwarding позволяет сделать проксирование SOCKS-over-SSH. Работает это так - при соединении SSH-клиента и SSH-сервера со стороны SSH-клиента поднимается SOCKS-прокси, например, на localhost’е, на который можно указывать приложениям с поддержкой SOCKS. Само проксирование будет через SSH-сервер, с которым вы соединяетесь. В сумме - Интернет вас будет видеть от имени SSH-сервера, соединение между SSH-клиентом и SSH-сервером зашифровано, так что не видно вложенных данных приложения, а для приложения все выглядит как обращение к обычному SOCKS-прокси.
На Linux выполняется это очень просто:
ssh -D <адрес локального интерфейса>:<порт socks> <пользователь>@<ssh-сервер>
Чтобы поднять SOCKS-прокси на локалхосте, обычно делают так:
ssh -D 1080 <пользователь>@<ssh-сервер>
Приложению остается указать SOCKS-сервер localhost (127.0.0.1) и порт 1080, без аутентификации. Например, в Firefox вот так:
Хороший тон - это поставить проксирование DNS-запросов!
А еще лучше для большего контроля и удобства рекомендую использовать плагин FoxyProxy, использование которого описано в статье “Настройка универсального узла связи для выхода в оверлейные сети” (см. раздел по настройке I2P)
Чтобы расшарить прокси во внешнюю сеть, например устройствам в локальной сети, можно поставить адрес внешнего интерфейса:
ssh -D 192.168.1.100:1080 <пользователь>@<ssh-сервер>
Устройствам в локальной сети и самой локальной машине нужно будет обращаться к 192.168.1.100
в данном случае. Только не забудьте настроить локальный фаерволл, если он есть, так как он может блокировать обращения извне.
Работает эта фишка и на Windows - достаточно настроить клиент PuTTY. На Andoid поддержка есть в JuiceSSH, но только в платной версии. Хотя возможно есть аналоги.
Также стоит рассмотреть фичу SSH “PermitTunnel”, которая позволяет поднять полноценный L3-тоннель. Описана фича, например, тут. Но многие жалуются, что из-за TCP-over-TCP сильно проседает производительность, и рекомендуется как временное решение.
Какие же недостатки у SSH Dynamic Port Forwarding?
-
SSH будет плохо работать в условиях мобильной сети - потребуются постоянные ручные реконнекты;
-
SSH легко идентифицируется средствами DPI;
-
Видел информацию, что SSH-прокси развивает довольно маленькую скорость доступа, но сам я не проверял;
-
Для пользователей, плохо знакомых с командной строкой и SSH, может быть достаточно сложно;
-
По моему есть проблема разграничения доступа. Хотелось бы некоторым пользователям давать доступ к прокси и только к прокси, выдав некоторые простые настройки (IP, порт, пароль). В случае SSH потребуется настройка разграничений доступа по пользователям и настройка SSH-сервера.
И вот на этом моменте перейдем к рассмотру такого инструмента, как Shadowsocks.
Shadowsocks
A secure socks5 proxy, designed to protect your Internet traffic.
Так кратко описывает инструмент веб-страничка проекта shadowsocks.org. Какими же преимуществами нам представляют shadowsocks разработчики данного проекта?
1) Super Fast. Bleeding edge techniques using Asynchronous I/O and Event-driven programming.
Отмечу, что в момент нагрузки ни сервер ни клиент много ресурсов не потребляют:
Нагрузка на сервере
Нагрузка на клиенте
Насчет скорости. По speedtest.net существенной просадки по скорости относительно обычного SOCKS5-прокси danted не обнаружил.
2) Flexible Encryption. Secured with industry level encryption algorithm. Flexible to support custom algorithms.
Ну, список алгоритмов стандартной поставки действительно немаленький:
Encrypt method: rc4-md5,
aes-128-gcm, aes-192-gcm, aes-256-gcm,
aes-128-cfb, aes-192-cfb, aes-256-cfb,
aes-128-ctr, aes-192-ctr, aes-256-ctr,
camellia-128-cfb, camellia-192-cfb,
camellia-256-cfb, bf-cfb,
chacha20-ietf-poly1305,
xchacha20-ietf-poly1305,
salsa20, chacha20 and chacha20-ietf.
The default cipher is chacha20-ietf-poly1305.
Такой список алгоритмов поддерживается с целью подбора оптимального под большее число устройств. Например, для процессоров без аппаратной поддержки AES может быть более оптимален chacha20, и наоборот. Вообще, шифрование тут больше не для сокрытия трафика, а для обхода средств DPI. Подробнее об этом в следующем пункте, а об обфускации трафика в следующем разделе.
3) Mobile Ready. Optimized for mobile device and wireless network, without any keep-alive connections.
Shadowsocks, в отличие от SSH, не обязательно удерживать текущее TCP-соединение. Вы можете спокойно переходить из одной сети в другую (из сотовой в WiFi и обратно, менять WiFi-сеть) и при этом ручного переподключения или переподключения по тайм-ауту не требуется. На каждое новое соединение shadowsocks открывает новую TCP-сессию, также как и обычный SOCKS. Первоначальная установка соединения, аутентификация, согласование параметров - этого в shadowsocks не требуется вовсе. Данные, главным образом, шифруются симметричным ключом, который указывается в конфиге на стороне сервера и клиента. Клиент “вслепую” шифрует данные ключом, указанным в конфиге и посылает серверу, без всякого согласования. И если ключ не совпадает и shadowsocks-сервер не может расшифровать данные - сервер просто сбросит соединение, ответив RST-пакетом. Да, ну и можно упомянуть, что один ключ может работать с любым количеством устройств - никакой конкуренции между устройствами нет.
4) Cross Platform. Available on most platforms, including Windows, Linux, Mac, Android, iOS, and OpenWRT.
Не брешут. Клиентов и серверов действительно большое разнообразие. Linux, Windows, Android - имеются. Есть даже на OpenWRT.
5) Open Source. Totally free and open source. A worldwide community devoted to deliver bug-free code and long-term support.
Опенсурс. Что еще добавить?
6) Easy Deployment. Easy deployment with pip, aur, freshports and many other package manager systems.
Развертка действительно очень простая. По крайней мере, я настраивал быстрее, чем danted. Покажу на примере Ubuntu Server 16.04. Для начала несколько слов о работе. Принцип примерно такой же, как и у SSH - на клиентской машине поднимается локальный SOCKS-прокси, на который ссылаются приложения, а shadowsocks обеспечивает шифрованный транспорт между клиентом и прокси-сервером.
Заметьте, что shadowsocks тут не “стрелочка”, а просто “труба”, потому что shadowsocks своих собственных поверхностных соединений, которые требуется удерживать, в отличие от SSH, не устанавливает.
Устанавливаем из стандартного репо shadowsocks на С - shadowsocks-libev:
# apt install shadowsocks-libev
Открываем конфиг файл по умолчанию /etc/shadowsocks-libev/config.json
и задаем несколько параметров
{
"server":"123.123.123.123",
"server_port":12345,
"password":"password",
"timeout":60,
"method":"aes-256-cfb"
}
По порядку:
server
- внешний адрес, который будет прослушиваться сервером;
server_port
- внешний порт, который будет прослушиватся сервером;
password
- общий ключ-пароль между клиентом и сервером;
method
- алгоритм шифрования.
Перезапускаем сервер:
systemctl restart shadowsocks-libev.service
И все, на этом закончили c сервером. Переходим к клиенту.
Устанавливаем на машине-клиенте тот же пакет:
# apt install shadowsocks-libev
Делаем конфиг-файл, например в домашней директории - ~/shsocks.json
:
{
"server":"123.123.123.123",
"server_port":12345,
"local_port":1080,
"password":"password",
"timeout":60,
"method":"aes-256-cfb"
}
Единственное отличие - это наличие local_port
. Настройка указывает, какой порт на локалхосте будет использовать локальный SOCKS-сервер.
Запускаем:
ss-local -c ~/shsocks.json
Теперь можем увидеть, что на локалхосте на указанном порту поднялся наш локальный SOCKS-сервер:
~$ sudo ss -tlpn | grep "ss-local"
LISTEN 0 128 127.0.0.1:1080 0.0.0.0:* users:(("ss-local",pid=24855,fd=5))
Приложениям, проксируемым через SOCKS, указываем SOCKS-прокси с адресом 127.0.0.1
и портом из local_port
- 1080
. Для FF например нужно задать настройки как на скриншоте выше. ss-local
при желании можно например загнать в cron или в штатный для системы автостарт, чтобы он сразу стартовал при запуске машины. Быстро проверить работу прокси можно например вот так:
curl --socks5 127.0.0.1:1080 ident.me
Теперь насчет мобильных девайсов. Я использую бета-версию Shadowsocks на Android. Удобные фичи - несколько режимов работы (прокси, VPN, transproxy), проксирование отдельных приложений, чтение конфига сервера из QR-кода. По последнему - насколько я знаю, генерировать QR можно в GUI-версиях Shadowsocks, а можно и такой простой командой:
pip install qr
echo -n "ss://"`echo -n aes-256-cfb:password@123.123.123.123:12345 | base64 -w 0` | qr
Только подставьте свои алгоритм, пароль, IP и порт сервера.
SOCKS можно расшарить во внешнюю сеть, просто привязав SOCKS-сервер на клиентской машине к адресу внешнего интерфейса. Удобно, если вы запускаете shadowsocks на сервере в своей локальной сети.
~$ ss-local -c ~/shsocks.json -b 192.168.1.100 -l 1080
Обфускация трафика
Shadowsocks предоставляет возможность подключить плагин обфускации трафика simple-obfs. Плагин маскирует прокси-трафик под HTTP или TLS/SSL, чтобы эффективно обходить системы DPI. Для этого нужно внести всего несколько настроек на стороне сервера и клиента.
В первую очередь на обеих сторонах нужно установить плагин:
apt install simple-obfs
Второй момент - чтобы разрешить прослушивать серверу ss-server
порты <1024 без запуска из-под root, нужно выполнить следующую команду:
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ss-server
Начнем с маскировки под HTTP.
Для маскировки под HTTP на стороне сервера дополним конфиг /etc/shadowsocks-libev/config.json
:
{
"server":"123.123.123.123",
"server_port":80,
"password":"password",
"timeout":60,
"method":"aes-256-cfb",
"plugin":"obfs-server",
"plugin_opts":"obfs=http;failover=204.79.197.200:80"
}
Конфиг дополнен строками:
server_port
- так как маскируемся под HTTP, то ставим порт 80;
plugin
- подключение плагина simple-obfs, и именно серверную его часть obfs-server
;
plugin_opts
- опции плагина, из которых:
obfs=http
- задает маскировку трафика под HTTP;
failover=204.79.197.200:80
- проксирование запроса на заданный сервер, если на прокси-сервер придет HTTP-запрос не-simple-obfs (обычный HTTP-запрос). Сделано это для дополнительной маскировки вашего прокси-сервера, и может помочь при автоматизированной проверке прокси-сервера на наличие правильно сформированного HTTP-ответа. В данном случае маскируемся под bing.com, не забываем этот момент - о нем будет упоминание далее.
После перезапускаем сервер:
systemctl restart shadowsocks-libev.service
Теперь конфиг со стороны клиента, например создаем файл ~/shsocks-obfs-http.json
:
{
"server":"123.123.123.123",
"server_port":80,
"local_port":1080,
"password":"password",
"timeout":60,
"method":"aes-256-cfb",
"plugin":"obfs-local",
"plugin_opts":"obfs=http;obfs-host=www.bing.com"
}
Конфиг дополнен строками:
server_port
- на забываем поставить порт как в конфиге сервера - 80;
plugin
- подключение плагина simple-obfs, и именно клиентскую его часть obfs-local
;
plugin_opts
- опции плагина, из которых:
obfs=http
- задает маскировку трафика под HTTP;
obfs-host=www.bing.com
- обращение к прокси будет выглядеть как GET-запрос к хосту www.bing.com. Поэтому, прежде в конфиге сервера мы указывали его IP-адрес в опции failover=204.79.197.200:80
. Хотя DNS-запись указывает, что bing.com находится на адресе 204.79.197.200, обращение по HTTP к адресу прокси сервера ("server":"123.123.123.123"
), если это не специально сформированный пакет simple-obfs, вернет всю туже самую страничку www.bing.com. В данном случае подозрение вызывает несоответствие DNS-записи, которая не указывает на наш прокси-сервер.
Запускаем клиента, проверяем работу прокси:
ss-local -c ~/shsocks-obfs-http.json
curl --socks5 127.0.0.1:1080 ident.me
Прокси-трафик теперь будет выглядеть как обычный обмен по HTTP. Для сравнения, сверху - замаскированный под HTTP прокси-трафик, внизу - настоящий запрос с помощью curl:
Cверху - замаскированный под HTTP прокси-трафик, внизу - настоящий запрос с помощью curl
Теперь замаскируем прокси-трафик под TLS/SSL, что будет более приближено к жизни, нежели HTTP. На стороне сервера меняется следующее:
{
"server":"123.123.123.123",
"server_port":443,
"password":"password",
"timeout":60,
"method":"aes-256-cfb",
"plugin":"obfs-server",
"plugin_opts":"obfs=tls;failover=204.79.197.200:443"
}
Конфиг дополнен строками:
server_port
- маскируемся под HTTPS, то ставим порт 443;
obfs=tls
- задает маскировку трафика под TLS/SSL;
failover=204.79.197.200:443
- аналогично тому, что ставили прежде в этой опции при маскировке под HTTP. Не забываем про порт - 443.
Перезапускаем сервер:
systemctl restart shadowsocks-libev.service
На стороне клиента меняется следующее (сделаем например еще один файл ~/shsocks-obfs-tls.json
):
{
"server":"123.123.123.123",
"server_port":443,
"local_port":1080,
"password":"password",
"timeout":60,
"method":"aes-256-cfb",
"plugin":"obfs-local",
"plugin_opts":"obfs=tls;obfs-host=www.bing.com"
}
Конфиг дополнен строками:
server_port
- на забываем поставить порт как в конфиге сервера - 443;
obfs=tls
- задает маскировку трафика под HTTPS-SSL/TLS;
obfs-host=www.bing.com
- запросы TLS будут к указанному хосту;
Запускаем клиент, проверяем работу прокси:
ss-local -c ~/shsocks-obfs-tls.json
curl --socks5 127.0.0.1:1080 ident.me
Если залезть в дамп трафика, то вот например “Client Hello” обфусцированного трафика:
Теперь насчет мобильного клиента. На Android плагин simple-obfs к Shadowsocks называется как Simple Obfuscation. Устанавливаете и задаете в нем такие же параметры, как и в случае конфигов выше. А точнее там настраивается два параметра - метод обфускации и obfs-host
.
Чтобы несколько вариантов настроек shadowsocks на сервере работали параллельно, можно например создать под каждый вариант юнит в systemd.
Еще можно сказать пару слов о опции plugin_opts:failover
. С помощью этой опции можно замаскировать прокси под www-сервер на этой же машине. Достаточно запустить www-сервер на 127.0.0.1:8443
(TLS) или 127.0.0.1:8080
(HTTP), а в опциях plugin_opts
указать failover=127.0.0.1:8443
или failover=127.0.0.1:8080
соответственно. В таком случае обычные HTTP-запросы или TLS-запросы будут выдавать, например, стандартную “заглушку” ненастроенного, только-что-из-коробки, сервера, или какую-либо простую страничку, или вообще редирект на другой адрес. При этом отпадает проблема несоответствия DNS-имени и запрашиваемого хоста.
По теме
© 2021 netwhood.online ― Сайт построен на Jekyll и Textlog theme