WireGuard через TCP-туннель на порту 443, обход заблокированных портов, решение для OpenWRT

Вопрос или проблема

Я столкнулся с проблемой – я хотел бы подключить множество встроенных устройств с OpenWRT к своему серверу Wireguard, но все порты, кроме 80, 443 и некоторых других, заблокированы. Более того, в сети разрешен только TCP.

Похоже, мне нужно закапывать WireGuard, который работает по UDP, в TCP. На своем сервере я хотел бы использовать сервис SSHL (обмен портами), чтобы одновременно запустить HTTPS сервер и туннель для WireGuard на порту 443 (или другом, разрешенном брандмауэром). Я хотел бы иметь многопользовательский туннель, поэтому подозреваю, что быстрые хаки с socat не сработают для большего количества клиентов.

  • Я пробовал wireguard-proxy, он отлично работает с обменом портами через SSLH, но написан на Rust, и я не могу развернуть его на OpenWRT (я пробовал, и получил ошибки компиляции).
    – Я также протестировал udp2raw, и он тоже хорошо работает на выделенном порту, но я не могу к нему подключиться, когда использую мультиплексор портов SSLH.

Я ищу какое-то программное обеспечение для туннелирования, написанное на C/C++, которое можно перекомпилировать в системе сборки OpenWRT и использовать с моими модифицированными сетевыми устройствами.

Вы можете использовать socat в качестве программного обеспечения для туннелирования.

Вот типичная настройка (выполняется интерактивно) с WireGuard, который слушает на порту 51820. Вам все равно придется выполнить адекватную интеграцию в сервисы запуска.

OpenWRT (но на самом деле протестировано на другой Linux)

  • Включите интерфейс Wireguard, как обычно

    … кроме MTU: смотрите описание в Примечаниях ниже. Давайте использовать MTU 1420-64=1356. Пример (это должно быть сделано в wg0.conf, когда используется wg-quick):

    ip link set wg0 mtu 1356
    
  • терминал1:

    sslh -n --listen 0.0.0.0:443 \
        --tls 127.0.0.1:8443 \
        --anyprot 127.0.0.1:51820 \
        -f
    

    sslh не распознает протокол WireGuard, поэтому соединение будет направлено на --anyprot.

  • терминал2:

    socat -T 120 \
        tcp4-listen:51820,bind=127.0.0.1,reuseaddr,nodelay,fork \
        udp4-connect:127.0.0.1:51820
    

    Как socat предлагает для другого случая:

    Примечание: Если вы намереваетесь передавать пакеты между двумя “проводами” Socat, вам нужен протокол, который сохраняет границы пакетов, например, UDP; TCP может работать с опцией nodelay.

    Хотя sslh не использует nodelay, оба socat будут это делать, что hopefully поможет в этой части.

    -T 120 – это задержка неактивности перед завершением форкнутых socat. Полезно, если клиент перемещается и теряет соединение. Это значение должно быть выше, чем ожидаемый постоянный keepalive WireGuard клиента. В любом случае, это не сильно поможет в случае DDoS-атаки без TLS: будет несколько форкнутых socat, и это может помочь только при DoS (сам WireGuard игнорирует такой трафик).

Клиент

  • терминал1:

    socat -T 120 \
        udp4-listen:51821,bind=127.0.0.1,fork \
        tcp4:public-endpoint:51820,nodelay
    

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

  • Интерфейс Wireguard

    Как и прежде, уменьшите MTU. В Linux это снова будет:

    ip link set wg0 mtu 1356
    

    И измените конечную точку пир-узла, чтобы переключиться на вход туннеля, который теперь находится на локальной системе (это также должно быть сделано в wg0.conf клиента):

    wg set wg0 peer XXXXXXXXX= endpoint 127.0.0.1:51821
    

Теперь, как только клиент отправляет любой пакет внутри туннеля и аутентифицируется, WireGuard сервера обновит конечную точку своего клиента (на 127.0.0.1:randomport от локального socat) и сможет ответить. Это создает полудуплексное взаимодействие:

client ⟺ WG ⟺ UDP ⟺ socat ⟺ TCP через интернет 
                                          ⇧
                                          ⇩
server ⟺ WG ⟺ UDP ⟺ socat ⟺ TCP ⟺ sslh

Примечания

  • Соображения по MTU

    В дополнение к обычным 80 байтам накладных расходов (1500-80=1420), WireGuard резервирует, необходимо добавить на накладные расходы TCP. Это чтобы избежать того, что внешний туннельный уровень: socat+sslh, которые не осведомлены о нижележащем протоколе, получили слишком большой полезный груз, разбитый на два TCP пакета: это может разрушить как минимум второй инкапсулированный UDP-полезный груз, вызывая повторные передачи.

    Эта накладная плата на самом деле динамическая. Для IPv4 это как минимум 20 для IP плюс 20 для TCP. Затем любое современное TCP-соединение использует 2+8+2=12 байтов для метки времени и будет использовать случайное количество SACK, когда пакеты теряются. Давайте оставим один (2+2*4+2=12): 20+20+12+12=64. Точная величина не так важна (действительно ли между этими двумя опциями TCP есть два NOP для выравнивания? Я не знаю). Таким образом, в конечном итоге я решил, что MTU WireGuard будет 1420-64=1356.

    Некоторые тесты, проведенные по фактической физической локальной сети с использованием ping -M do -s $MTU-28 -f 10.x.x.x, показали, что поддержание MTU=1420 на интерфейсах приводило к потере ~ 40% пакетов, и что MTU=1356 не терял никаких, даже если более высокие MTU также казались адекватными.

  • случай ограничения на port 443, требующий также SSL

    Если брандмауэр, ограничивающий использование порта 443, ожидает только SSL-трафик на этом порту, то дело усложняется. Следует заменить sslh на haproxy или nginx (обе, как кажется, упакованы на OpenWRT, я не знаю, были ли доступны необходимые функции) и использовать их функцию TLS pass-through для маршрутизации TLS-соединения в зависимости от полученного ClientHello’s SNI. Оба socat тогда заменят tcp-listen/tcp на openssl-listen/openssl для общения, но я не уверен, как MTU будет затронут в этом случае.

Ответ или решение

Для решения вашей задачи о туннелировании WireGuard через TCP на порту 443, учитывая ограничения сетевых портов и предпочитаемый протокол TCP, я предлагаю использовать сочетание утилит sslh и socat. Данная схема позволяет организовать многопользовательский туннель для WireGuard, обрабатывая соединения на порту 443 без необходимости использования дополнительных UDP портов.

Настройка на сервере OpenWRT

  1. Установите необходимые пакеты:
    Убедитесь, что у вас установлены wireguard, sslh и socat. Вы можете установить их с помощью opkg:

    opkg update
    opkg install wireguard sslh socat
  2. Настройка интерфейса WireGuard:
    Создайте файл конфигурации WireGuard (например, /etc/wireguard/wg0.conf) и настройте интерфейс. Обратите внимание на MTU:

    [Interface]
    Address = 10.0.0.1/24
    ListenPort = 51820
    PrivateKey = <ваш_приватный_ключ>
    
    [Peer]
    PublicKey = <публичный_ключ_клиента>
    AllowedIPs = 10.0.0.2/32

    Настройте MTU интерфейса:

    ip link set wg0 mtu 1356
  3. Настройка sslh:
    Запустите sslh, чтобы он слушал на порту 443 и перенаправлял трафик:

    sslh -n --listen 0.0.0.0:443 \
       --tls 127.0.0.1:8443 \
       --anyprot 127.0.0.1:51820

    Обратите внимание, что в случае WireGuard SSLH не способен распознать его протокол, поэтому трафик будет передан на --anyprot.

  4. Настройка socat:
    Создайте туннель с помощью socat для перенаправления UDP-пакетов через TCP:

    socat -T 120 \
       tcp4-listen:51820,bind=127.0.0.1,reuseaddr,nodelay,fork \
       udp4-connect:127.0.0.1:51820

    Данный вызов socat будет слушать TCP-соединения и пересылать их на WireGuard.

Настройка клиента OpenWRT

  1. Настройка socat на клиенте:
    На стороне клиента настройте socat, чтобы он слушал UDP-пакеты и отправлял их на сервер:

    socat -T 120 \
       udp4-listen:51821,bind=127.0.0.1,fork \
       tcp4:<публичный_IP_сервера>:51820,nodelay
  2. Настройка WireGuard клиента:
    Измените конфигурацию клиента, чтобы использовать локальный сокет:

    [Interface]
    Address = 10.0.0.2/24
    PrivateKey = <приватный_ключ_клиента>
    
    [Peer]
    PublicKey = <публичный_ключ_сервера>
    Endpoint = 127.0.0.1:51821
    AllowedIPs = 10.0.0.0/24

    Настройте MTU так же, как на сервере:

    ip link set wg0 mtu 1356

Заметки и рекомендации

  1. MTU: Убедитесь, что значение MTU корректное, чтобы избежать фрагментации пакетов. Используйте указанный выше размер (1356) для стабильной передачи.

  2. Дополнительные ограничения на порту 443: Если ваша сеть требует, чтобы на порту 443 использовался только SSL, вам потребуется заменить SSLH на такой прокси, который может учитывать SNI, например, haproxy или nginx, с соответствующими настройками для TLS.

  3. Перезапуск служб: Не забывайте включить эти службы при старте системы, чтобы ваши настройки работали автоматически при загрузке маршрутизатора.

Эта инструкция предоставляет вам полное решение для установки туннеля WireGuard через TCP на OpenWRT, позволяя подключаться к многопользовательским клиентам через ограниченные порты.

Оцените материал
Добавить комментарий

Капча загружается...