Как стать автором
Обновить

GOST: швейцарский нож для туннелирования и обхода блокировок

Уровень сложностиСредний
Время на прочтение13 мин
Количество просмотров117K

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

Сегодня я расскажу о замечательном инструменте под названием GOST. Не пугайтесь, он не имеет никакого отношения к ГОСТ-шифрованию или чему-то подобному, на самом деле это Go Simple Tunnel. Он действительно simple (простой) в использовании и настройке, но при этом невероятно мощный, поскольку поддерживает огромное количество протоколов и транспортов, из которых вы при желании сможете построить самые упоротые и бронебойные комбинации.

Поддерживаемые протоколы (в скобках указывается версия, начиная с которой поддерживается):

  • http - HTTP

  • http2 - HTTP2

  • socks4 - SOCKS4 (2.4+)

  • socks4a - SOCKS4A (2.4+)

  • socks5 - SOCKS5

  • ss - Shadowsocks

  • ss2 - Shadowsocks c AEAD (2.8+)

  • sni - SNI (2.5+)

  • forward - специальный "протокол" для переадресации коннектов на определенный порт или хост

  • relay - собственный протокол GOST, поддерживает проксирование TCP и UDP, реверс-прокси, и т.д., этакий SOCKS5 на стероидах

Поддерживаемые транспорты:-

  • tcp - просто TCP

  • tls - TLS

  • mtls - мультиплексированный TLS (несколько потоков в рамках одного TLS-коннекта) (2.5+)

  • ws - Websocket

  • mws - мультиплексированный Websocket (по аналогии с mtls) (2.5+)

  • wss - Websocket с TLS-шифрованием

  • mwss - мультиплексированный Websocket с TLS-шифрованием :) (2.5+)

  • kcp - протокол KCP, о котором я рассказывал в одной из статей (2.3+)

  • quic - QUIC (2.4+)

  • ssh - SSH (2.4+)

  • h2 - HTTP2 (2.4+)

  • h2c - HTTP2 без шифрования (2.4+)

  • obfs4 - OBFS4, используемый, например, Tor-бриджами (2.4+)

  • ohttp - HTTP Obfuscation (2.7+)

  • otls - TLS Obfuscation (2.11+)

Ну и как я сказал, протоколы и транспорты можно комбинировать, например relay+kcp relay+tls relay+mtls http+kcp http+tls

Благодаря всему этому GOST является отличным инструментом как для повседневного использования, так и для экспериментов - когда цензоры вдруг начали активно банить то, чем вы пользуетесь, можно быстро и просто экспериментировать с разными комбинациями протоколов чтобы найти те, что не заблокированы.

Итак, где можно скачать и почитать про это чудо? Существует две версии GOST: v3 и v2. v3 - активно развивающаяся в настоящий момент, представляет собой глубокий рефакторинг версии v2. v2 - уже вроде как объявлена deprecated. Между собой они совместимы не полностью, поэтому мажорные версии на клиенте и сервере должны совпадать.

Github v3:  https://github.com/go-gost/gost/
Сайт с документацией v3: https://gost.run/
Релизы для скачивания v3: https://github.com/go-gost/gost/releases

ну и если вам зачем-то понадобится v2

https://github.com/ginuerzh/gost
https://v2.gost.run/
https://github.com/ginuerzh/gost/releases
Будьте внимательные, у v2 и v3 может отличаться синтаксис конфига и параметров (например, probeResistance в v3, и probe_resist в v2)

Со ссылками закончили, теперь попробуем сделать что-нибудь интересное (спойлер: самое вкусное будет ближе к концу статьи!).

Конфигурацию в GOST можно задавать как аргументами командной строки, так и в виде YAML-файла. С командной строкой очень удобно, не надо изучать синтаксис конфига, и можно легко экспериментировать, просто меняя параметры при перезапусках. Конфиг - если вы хотите солидно и надолго. Я буду приводить примеры с командной строкой, если интересно, как сделать то же самое с конфигом - на официальном сайте с документацией для каждой фичи есть примеры и того и того, плюс объяснена основная логика конфига (там есть всякие Listener'ы, Handler'ы, и т.д.).

Будьте внимательны, довольно часто параметры командной строки GOST содержат символ & (когда вы передаете сразу несколько параметров), как в URL'ах, а в линуксовых шеллах этот символ имеет специальное сакральное значение - в итоге у вас в GOST передается только то что слева от этого символа. Я об этом совершенно забыл, когда экспериментировал, что привело к возгоранию стула в попытках осознать, почему ничего не работает как надо. Решение простое - берите такие параметры в кавычки. В разных примерах вы можете увидеть разные типы написания аргументов, -L "something" и -L="something" - по факту это одно и то же, работает и так и так.

Два основных типа аргументов у GOST: -L и -F. -L (listen) - это когда процесс слушает входящие подключения на каком-то порту по какому-то протоколу. Аргументов -L может быть несколько, в таком случае он будет слушать их одновременно. А -F (forward) - это когда процесс будет перенаправлять полученные запросы на какой-то сервер по какому-то протоколу. Аргументов -F может быть тоже несколько - тогда у вас получится цепочка серверов (клиент подключится сначала к первому серверу, потом через него ко второму, и т.д.). Есть также варианты балансировки и автоматического выбора серверов из списка, но об этом будет чуть позже.

Итак, что же мы можем сделать?

Начнем, например, с банальнейшего HTTP-прокси.

// На сервере:
gost -L http://user:pass@:8080

// На клиенте: 
gost -L http://:8080 -L socks5://:1080  -F http://user:pass@SERVER_IP:8080

Мы можем подключаться или сразу к серверу на 8080 порт как к HTTP-прокси, или же подключиться к клиенту на 8080 порт как HTTP-прокси, и на 1080 порт как с SOCKS-прокси, а клиент передаст наше подключение на сервер.

Но так не интересно. Просто нешифрованный HTTP — ну кто так делает в наше время? Добавим TLS!

Если вы просто активируете TLS в Gost, то он при запуске сгенерирует самоподписанный сертификат с дефолтным именем хоста gost.run - для тестов локально это окей, но в реальной жизни использовать такое я не советую. Можно опцией конфигурации генерировать сертификат с другим именем, но мы, как приличные люди, будем использовать нормальные полноценные сертификаты для своего домена, чтобы вызывать меньше подозрений. Домен можно получить даже на DynDNS-сервисах бесплатно, а TLS-сертификат можно получить с помощью Let's Encrypt, инструкций в интернете очень много.
Сервер GOST автоматически подхватит ваши сертификаты, если в его рабочей директории обнаружит файлы cert.pem, key.pem и ca.pem, либо можно указать пути до них вручную в аргументах (обратите внимание на кавычки, про которые я говорил ранее):

// На сервере: 
gost -L "http+tls://:443?certFile=cert.pem&keyFile=key.pem&caFile=ca.pem"

// На клиенте:
gost -L "http://:8080 -F tls://SERVER_IP_OR_DOMAIN:443?secure=true"

Так, теперь у нас есть HTTP-прокси, работающий через TLS - как будто вы обращаетесь к какому-то обычному веб-сайту. В качестве SNI при TLS-соединении будет использоваться адрес сервера, который вы указали в строке подключения, если вам надо его переопределить, например, для domain fronting, можно добавить опцию &serverName=yourdomain.com

Не хватает еще, чтобы сервер вел себя как обычный веб-сайт при попытке зайти к нему браузером. GOST умеет и такое тоже:

// На сервере, одно из:
gost -L "http+tls://user:pass@:443?probeResistance=host:www.example.com:80"
gost -L "http+tls://user:pass@:443?probeResistance=code:403"
gost -L "http+tls://user:pass@:443?probeResistance=file:/var/www/index.html"

В первом случае все не-прокси запросы будут переданы на сайт www.example.com (лучше всего использовать для маскировки какой-нибудь реальный, но непопулярный сайт), во втором случае сервер будет отвечать на все запросы 403-м HTTP-кодом, в третьем случае - выдавать содержимое страницы из файла по указанному пути.

Помните замечательную статью "Безопасный HTTP-прокси менее чем за 10 минут" от @YourChief? Мы сейчас сделали ровно то же самое. У нас благодаря GOST есть прокси, который работает по стандартному HTTP протоколу поверх стандартного TLS. А это значит, что вы можете легко использовать его безо всяких клиентов, прямо из браузера. Можно задать его как системный прокси в Windows, можно задать его как основной прокси в Firefox и Chromium, можно задать его как прокси в расширениях типа SwitchyOmega с возможностью его удобно включать/выключать, задавать правила для доменов, которые вы хотите посещать через прокси, или без него напрямую, и так далее. Можно даже использовать его на Android через AdGuard - обо всем этом можно прочитать в статье по ссылке выше. Инициатором подключения к прокси будет являться сам браузер, а значит TLS fingerprint у такого подключения будет ровно такой же, как у вашего браузера - это даже лучше чем XRay c uTLS. А для сторонних наблюдаетелей, желающих проверить сервер методом active probing, он будет отдавать фейковый веб-сайт.

Чего-то не хватает? Ах да, если какие-нибудь пронырливые личности сделают к нашему серверу HTTP CONNECT-запрос, то он демаскирует себя ответом "407 Proxy authentication required". Добавим опцию "knock", которая говорит о том, что GOST не будет реагировать на CONNECT-метод без предоставления логина/пароля до тех пор, пока запрос с этим методом не поступит для какого-то только нам известного секретного адреса:

// на сервере:
gost -L "http+tls://user:pass@:443?probeResistance=code:403&knock=www.secretaddr.com"

В этом случае при задании прокси в браузере ничего сразу работать не будет (точно так же как для внешних наблюдателей), зато когда вы попробуете открыть через прокси этот секретный адрес, сервер наконец-то ответит "HTTP 407 Proxy authentication required", ваш браузер покажет окошко с предложением ввести логин и пароль, закэширует их, и в дальнейшем уже сам будет подставлять эти реквизиты при каждом запросе на прокси - все будет работать как часы. В итоге у нас получился простой, но тем не менее устойчивый к блокировкам прокси, почти не хуже чем XRay с VLESS (а до тех мест, где он хуже, наш родной Роскомнадзор дорастет еще очень нескоро).

Ну а мы идем дальше. То что будет дальше, уже, к сожалению, не получится использовать с одним только браузером, обязательно нужен GOST-клиент.

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

Можно вместо TCP и TLS туннелироваться по UDP с пакетами типа "непонятное нечто" - мы будем использовать протокол KCP, который, кстати, создан в том числе и для эффективной работы через нестабильные каналы связи с большими потерями.

// на сервере:
gost -L "relay+kcp://:5555?kcp.keepalive=10&kcp.mode=fast3&kcp.key=abababab"

// на клиенте:
gost -L http://:8080 -L socks5://:1080 -F "relay+kcp://SERVER_ADDR:5555?kcp.keepalive=10&kcp.mode=fast3&kcp.key=abababab"

"kcp.key" поменяйте на свой, порт - тоже.
По умолчанию используется aes-шифрование, можно задать его параметром kcp.crypt с возможными значениями aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none.
Если у вас нет необходимости работать через плохие каналы связи, можно добавить параметры kcp.rcvwnd и kcp.sndwnd со значением 2048 - будет работать быстрее.

Если вдруг цензоры режут все неопознанные UDP-протоколы, можно использовать в качестве транспорта DTLS, который тоже работает по UDP и используется, например, в WebRTC:

// на сервере: 
gost -L=relay+dtls://:5555

// на клиенте:
gost -L http://:8080 -L socks5://:1080  -F relay+dtls://SERVER_IP:5555

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

// на сервере:
gost -L "relay+wss://:443?path=/secretpath"

// на клиенте:
gost -L http://:8080 -L socks5://:1080 -F "relay+wss://SERVERHOST:443?path=/secretpath"

Поскольку мы используем "wss" (вебсокеты с шифрованием, в отличие от просто ws, которые без TLS), не забудьте подсунуть TLS сертификаты как я описывал в начале статьи, либо указать в дополнительных опциях на сервере пути к ним. Если вам нужно переопределить поле "Host" запроса (например, для domain fronting), можно добавить опцию &host=www.someotherdomain.com

К сожалению, в случае с вебсокетами заставить GOST отдавать фейковый веб-сайт не получится, но никто не запрещает поставить его за Nginx или Caddy и проксировать вебсокеты на GOST с помощью них, так же как я описывал в уже упомянутой статье.

Можно использовать мультиплексированные вебсокеты - замените "ws" на "mws" или "wss" на "mwss" - тогда разные потоки будут объединяться в рамках одного websocket-подключения, что экономит время при установлении соединений с сервером через прокси.

Можно проксироваться через gRPC, как мы это делали с XRay:

// на сервере
gost -L "relay+grpc://:443?path=/Haha/MySecretTun"

// на клиенте
gost -L http://:8080 -L socks5://:1080 -F "relay+grpc://SERVER_ADDR:443?path=/Haha/MySecretTun"

Все сказанное выше в контексте вебсокетов про TLS-сертификаты и про возможность установки GOST за Nginx справедливо и тут.

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

// на клиенте
gost -L http://:8080 -L socks5://:1080  -F socks5://192.168.1.1:1080,192.168.1.2:1080?strategy=rand&maxFails=3&failTimeout=60s

Само собой, прокси могут быть не только socks5, а любых протоколов, которые поддерживает GOST.

Возможные стратегии (strategy):

  • round - "по кругу", чтобы никто не ушел обиженным

  • rand - выбирать сервер случайным образом

  • fifo - использовать второй сервер, если через первый уже выполняется запрос, использовать третий если через второй уже выполняется запрос, и т.д.

  • hash - на основе хеша IP-адреса или домена назначения (запросы к одному и тоже же IP/домену будут всегда идти через один и тот же прокси).

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

И еще одна фича, о которой не грех будет напомнить еще раз - цепочки прокси:

// на клиенте:
gost -L http://:8080 -L socks5://:1080  -F http://FIRSTPROXYADDR:8080 -F "relay+wss://SECONDPROXYADDR:443?path=/secretpath"

Когда вы задаете несколько -F-опций, то GOST подключится сначала к первому серверу, потом через него ко второму, и т.д., и только потом к серверу назначения. Это может пригодиться если вам нужно достучаться до вашего прокси на VPS через местный корпоративный прокси-сервер, либо если вы хотите строить цепочки серверов (например, сначала подключиться к серверу в России, а уже потом через него до прокси-сервера за рубежом).

Впрочем, что мы все о прокси да о прокси... GOST поддерживает TUN-интерфейсы! Немного магии....

// на сервере:
gost -L tun://:5555?net=192.168.123.1/24

// на клиенте
gost -L tun://:0/SERVER_IP:5555?net=192.168.123.2/24

...и вот уже у вас полноценный VPN между двумя машинами - в системе появляется tun-интерфейс, через который мы можете коммуницировать с другой стороной тоннеля, маршрутизировать туда и оттуда трафик, и т.д. В GOST для tun кроме "net" есть еще много разных опций, можно добавлять маршруты, и т.д., короче говоря, смотрите документацию.

Да, важная вещь, при использовании TUN под Windows нужно положить рядом с бинарником GOST библиотеку wintun.dll

Трафик будет бегать по UDP, но без шифрования, имейте в виду. Что делать, если хочется по TCP, или с шифрованием? Сама реализация TUN в GOST работает только по UDP, но никто не запрещает завернуть ее в relay-протокол и туннелировать как угодно. Например, по TCP:

// на сервере:
gost -L tun://:5555?net=192.168.123.1/24  -L relay+tcp://:4444?bind=true

// на клиенте:
gost -L tun://:0/:5555?net=192.168.123.2/24 -F relay+tcp://SERVER_ADDR:4444

(в данном случае порт 5555 используется для внутренних нужд, а TCP-подключение идет по 4444 порту).

Можно сделать то же самое поверх TCP и TLS, тогда у вас будет шифрование и прилично выглядящий снаружи трафик:

// на сервере:
gost -L tun://:5555?net=192.168.123.1/24  -L relay+tls://:443?bind=true

// на клиенте:
gost -L tun://:0/:5555?net=192.168.1.2/24 -F relay+tls://SERVER_ADDR:443

Можно также использовать для TUN транспорт поверх KCP или DTLS, как уже было описано выше.


А можно даже сделать так:

// на сервере:
gost -L tun://:8421?net=192.168.123.1/24 -L "relay+wss://:443?bind=true&?path=/secretpath"
// на клиенте:
gost -L tun://:0/:8421?net=192.168.123.2/24 -F "relay+wss://SERVER_IP:443?path=/secretpath"

Догадались, что это получилось? Полноценный VPN поверх вебсокетов, в том числе с возможностью работать через CDN, да. Прелесть, правда?

Окей, а что если у нас заблокировано вообще все? Например, суровый корпопротивный корпоративный прокси или обезумевшие цензоры производят man-in-the-middle перешифровку всего трафика и не пропускают вообще ничего подозрительного?

Тадааам!

// на сервере:
gost -L relay+pht://:80?authorizePath=/authorize&pushPath=/push&pullPath=/pull

// на клиенте:
gost -L http://:8000 -L socks5://:1080  -F relay+pht://SERVERADDR:80?authorizePath=/authorize&pushPath=/push&pullPath=/pull

PHT - это plain HTTP tunnel. То есть туннель, работающий через простой HTTP. Никакого метода CONNECT, никаких вебсокетов - только обычные GET-запросы. Максимально просто, максимально тупо, максимально надежно - пролезет через что угодно :) Да, скорость будет низкая (по сути дела у нас полудуплексная коммуникация), но это лучше, чем ничего. И да, таким образом можно, например, туннелировать через CDN, которые не поддерживают вебсокеты (или просят за них денег, например Fastly) - желательно при этом выключить в настройках CDN кэширование, мы же не звери какие-то. Плюс PHT можно спрятать за Nginx с фейковым сайтом.

А что если даже HTTP не работает? Помните недавнюю статью "Пакуем весь трафик в Ping message, чтобы не платить за интернет" про ICMP-туннель, когда информация передается внутри обычных пингов? GOST тоже так может (в v3)!

Сначала отключим ответы на пинги на уровне операционной системы на сервере, иначе мы будем получать в два раза больше ответов чем нужно:

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

а потом (клиент и сервер должны быть запущены от root-пользователя, чтобы иметь возможность низкоуровнево работать с сокетами):

// на сервере
gost -L relay+icmp://:0

// на клиенте
gost -L http://8080 -L socks5://:1080  -F "relay+icmp://SERVERADDR:12345?keepalive=true&ttl=10s

Да, работать будет еще медленее (ICMP обычно идут с минимальным приоритетом, могут теряться по пути, и т.д.). Но это тоже лучше чем ничего. И как подметили в упомянутой статье, иногда таким образом можно вылезти в интернет, даже когда доступ в интернет вообще запрещен - например, отключен за неуплату или требует авторизации.

На этом остановимся, но нужно отметить что то, что я рассказал выше - это едва ли половина возможностей GOST. Еще он может:

И еще кучу кучу всего. Еще раз напоминаю сайт с документацией: https://gost.run/en/

И да, если вам интересно, как использовать вебсокет- или plain-HTTP-туннели для работы через CDN, маскируясь под чужие домены, смотрите мою следущую статью "Domain fronting для чайников, и как его использовать для обхода блокировок".

А теперь немного советов, как это запустить.

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

Можно создать systemd-юнит в Linux
[Unit]
Description=GO Simple Tunnel
After=network.target
Wants=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/gost -L=:8080
Restart=always

[Install]
WantedBy=multi-user.target

Можно установить его как сервис в Windows

sc create gost binpath= "C:\gost.exe -L :8080" start= auto

А можно добавить его даже в наш любимый Nekoray:

Идем в Basic Settings -> Extra core. Нажимаем Add, вводим имя ("gost"). Появляется новая строка, там кликаем Select и выбираем наш бинарник GOST:

Чтобы добавить новое подключение, идем в Server -> New profile, выбираем там тип Custom (extra core), выбираем наш core "gost", вводим адрес и порт сервера, а в "Command" можно вставить что-то типа

-L socks://:%socks_port% -F relay+tls://%server_addr%:%server_port%

Естественно, используйте протокол и параметры, которые нужны вам. Обратите внимание, здесь уже никаких кавычек.

Нажав на Preview, можно посмотреть, какую команду для запуска GOST сгененировал Nekoray, чтобы убедиться, что все правильно.

Другой вариант - вставить в большое поле в окне ваш YAML-конфиг, выбрать "Config suffix" "yaml", а в команде запуска упомянуть %config% - тогда Nekoray создаст файл с нужным содержимым и передаст путь к нему в этот аргумент.

А что с мобильными клиентами... с ними пока не очень. Есть плагин GOST для клиента Shadowsocks-Android, но он позволяет только заворачивать в него Shadowsocks, плюс он основан на старой версии GOST и не поддерживает самое вкусное (PHT- и ICMP-туннели).

На iOS GOST поддерживается в великолепном клиенте Shadowrocket, правда, судя по всему, тоже на базе старой версии и без вкусностей. Я не тестировал.

И существуют пакеты для OpenWRT, но они, кажется, сделаны для v2:
https://github.com/kenzok8/openwrt-packages/tree/master/gost
https://github.com/kenzok8/openwrt-packages/tree/master/luci-app-gost

На этом всё.

Удачи, и да прибудет с вами сила.

Если вы хотите сказать спасибо автору — сделайте пожертвование в один из благотворительных фондов: "Подари жизнь", "Дом с маяком", "Антон тут рядом".

Теги:
Хабы:
Всего голосов 145: ↑142 и ↓3+174
Комментарии114

Публикации

Истории

Работа

Ближайшие события