Jump to content

Обход блокировки протокола WireGuard, в т.ч. AmneziaWG


Recommended Posts

Готовое решение (ЖМИ). 
Тема на github.


=== Ниже - первый пост ===
Умельцы придумали скрипт для микротика, который в случае отсутствия рукопожатия у WG рвет соединение и пытается переподключить его добавляя отправку на WG порт сервера небольшого мусора непосредственно перед самими рукопожатием.

Пишут, что многим удалось таким образом реанимировать WG соединения на узлах где ТСПУ блокирует WG протокол.

Скрипт для Микротик:

Скрытый текст
:global Tx
:global Rx
/interface wireguard peers
:foreach i in=[find where disabled=no and responder!=yes] do={
    :local LocalTx [get $i tx]
    :local LocalRx [get $i rx]
    :local LastHandshake [get $i last-handshake]
    :if (([:tostr $LastHandshake] = "") or (($LastHandshake > [:totime "2m20s"]) and ($Rx->[:tostr $i] = $LocalRx))) do={
        :local PeerName [get $i name]
        :local Interface [get $i interface]
        :local EndpointAddress [get $i endpoint-address]
        :local EndpointIP [get $i current-endpoint-address]
        :local DstPort [get $i current-endpoint-port]
        :local RawHeader [:rndstr length=4 from=123456789abcdef]
        
        #Reset source port
        /interface wireguard set $Interface listen-port=0
        :local SrcPort [/interface wireguard get $Interface listen-port]
        
        #Log peer info
        :log warning ("Peer: $PeerName, Interface: $Interface")
        :log warning ("Endpoint Address: $EndpointAddress, Endpoint IP: $EndpointIP")
        :log warning ("Src Port: $SrcPort, Dst Port: $DstPort, Last Handshake: $LastHandshake")
        :log warning ("Last Rx: " . $Rx->[:tostr $i] . ", Current Rx: $LocalRx")
        :log warning ("Last Tx: " . $Tx->[:tostr $i] . ", Current Tx: $LocalTx")
        
        #Disable peer
        :log warning ("Disable peer: $PeerName")
        set $i disabled=yes
        :delay 1
        
        #Generating spam
        :log warning ("Generating spam")
        /tool traffic-generator stream remove [find]
        /tool traffic-generator packet-template remove [find]
        :delay 1
        /tool traffic-generator packet-template add header-stack=mac,ip,udp,raw ip-dst=$EndpointIP name=packet-template-wg raw-header=$RawHeader special-footer=no udp-dst-port=$DstPort udp-src-port=$SrcPort
        :delay 1
        /tool traffic-generator stream add disabled=no mbps=1 name=stream1 id=3 packet-size=1450 pps=0 tx-template=packet-template-wg
        :delay 1
        /tool traffic-generator quick duration=4
        
        #Enable peer
        :log warning ("Enable peer: $PeerName")
        set $i disabled=no
    }
    :set ($Tx->[:tostr $i]) $LocalTx
    :set ($Rx->[:tostr $i]) $LocalRx
}

Как работает:
 

Цитата
  • Перебор активных пиров WireGuard:

    • Скрипт проходит по всем пир-соединениям WireGuard, которые включены и не находятся в режиме респондера.
  • Проверка активности соединения:

    • Для каждого пира скрипт проверяет время последнего рукопожатия (last-handshake). Если рукопожатия не было в течение 2 минут 20 секунд, и количество полученных данных (rx) для пира не изменилось с прошлого цикла, это означает, что соединение может быть "зависшим".
  • Сброс порта и перезапуск соединения:

    • Скрипт сбрасывает локальный порт WireGuard на 0, чтобы обновить его.
    • Логирует информацию о пире: имя, интерфейс, адрес конечной точки, порты и время последнего рукопожатия, чтобы отслеживать статус.
    • Отключает пир (переводит его в состояние disabled=yes), чтобы инициировать повторное соединение.
  • Генерация трафика для восстановления соединения:

    • После отключения пира скрипт использует генератор трафика (/tool traffic-generator) для создания "пакетов мусора", которые отправляются на IP-адрес и порт конечной точки пира. Этот шаг может помочь "разбудить" пир и заставить его инициировать повторное подключение.
    • Генерация пакетов осуществляется с помощью шаблона (packet-template-wg), который включает заголовки для сетевого и транспортного уровней и отправляет трафик по порту назначения WireGuard.
  • Повторное включение пира:

    • После кратковременного трафика на пир, скрипт снова включает его (устанавливает disabled=no), чтобы возобновить соединение.
  • Обновление состояния трафика:

    • Скрипт обновляет глобальные переменные Tx и Rx для отслеживания количества переданных и полученных данных на следующем цикле выполнения скрипта.

Попросил ИИ адаптировать код для Keenetic с entware, вот что получилось:

Скрытый текст

 

#!/bin/bash

# Параметры WireGuard
INTERFACE="wg0"  # Укажите интерфейс WireGuard
CHECK_INTERVAL=140  # Время проверки активности (секунды)
TMP_FILE="/tmp/wg_last_status.txt"

# Функция для логирования
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') $1"
}

# Генерация трафика с использованием hping3
generate_traffic() {
    local endpoint_ip=$1
    local dst_port=$2
    local src_port=$3
    local raw_header=$4
    
    log "Генерация трафика для $endpoint_ip:$dst_port с исходным портом $src_port и заголовком $raw_header"

    # Отправка пакетов UDP с raw header, имитирующих данные
    hping3 --udp -p "$dst_port" -s "$src_port" -d 120 --sign "$raw_header" "$endpoint_ip" -c 10
}

# Получение состояния WireGuard и проверка активности
check_wg_peers() {
    local updated=false

    # Проверка текущего состояния пиров
    wg show "$INTERFACE" endpoints transfer latest-handshake > "$TMP_FILE"

    while read -r line; do
        local peer=$(echo "$line" | awk '{print $1}')
        local endpoint=$(echo "$line" | awk '{print $2}')
        local rx=$(echo "$line" | awk '{print $3}')
        local tx=$(echo "$line" | awk '{print $4}')
        local last_handshake=$(echo "$line" | awk '{print $5}')
        local endpoint_ip=$(echo "$endpoint" | cut -d':' -f1)
        local dst_port=$(echo "$endpoint" | cut -d':' -f2)
        local raw_header=$(head /dev/urandom | tr -dc A-F0-9 | head -c4)  # Генерация случайного заголовка

        # Проверка времени последнего рукопожатия и активности передачи
        if [[ -z "$last_handshake" || $(( $(date +%s) - last_handshake )) -gt $CHECK_INTERVAL ]]; then
            log "Peer $peer на $endpoint не активен, перезапуск..."

            # Перезапуск соединения
            ifdown "$INTERFACE"
            sleep 1
            ifup "$INTERFACE"

            # Генерация трафика после перезапуска
            generate_traffic "$endpoint_ip" "$dst_port" "$dst_port" "$raw_header"

            log "Peer $peer перезапущен"
            updated=true
        fi
    done < "$TMP_FILE"

    if ! $updated; then
        log "Все пиры активны"
    fi
}

# Основной цикл проверки
while true; do
    check_wg_peers
    sleep "$CHECK_INTERVAL"
done

 

Пояснения по работе:

Цитата
  • Получение данных о пире:

    • В MikroTik мы получаем информацию о пире через встроенные команды. В Linux мы используем wg show для получения состояния интерфейса WireGuard, и анализируем результаты командой awk.
  • Отправка пробуждающего трафика:

    • В оригинальном скрипте использовался генератор трафика MikroTik, который отсутствует в Linux. Вместо этого используется hping3 для генерации трафика к конечной точке.
  • Перезапуск интерфейса WireGuard:

    • Поскольку в Entware отсутствует команда для перезапуска отдельных пиров, мы перезапускаем весь интерфейс WireGuard с помощью ifdown и ifup.

 


Сможет кто-то доработать и дать ума идее на Keenetic устройствах?

Если уже есть аналогичное готовое - дайте пожалуйста ссылку.

Edited by Ground_Zerro
add git
Link to comment
Share on other sites

У себя сделал так:

установить cron и nping:
opkg update && opkg upgrade
opkg install cron nping

В файле /opt/etc/crontab удалите неиспользуемые строки, нам нужна cron.1min:
*/1 * * * * root /opt/bin/run-parts /opt/etc/cron.1min
*/5 * * * * root /opt/bin/run-parts /opt/etc/cron.5mins
01 * * * * root /opt/bin/run-parts /opt/etc/cron.hourly
02 4 * * * root /opt/bin/run-parts /opt/etc/cron.daily
22 4 * * 0 root /opt/bin/run-parts /opt/etc/cron.weekly
42 4 1 * * root /opt/bin/run-parts /opt/etc/cron.monthly

создаем скрипт, например с именем "pinger" в папке /opt/etc/cron.1min/, который будет запускаться кроном каждую минуту

Сам скрипт: (для Amnezia он тоже будет работать)

в переменной fey задаем номера интерфейсов WG через пробел, в кавычках(не больше 5), которые мы будем проверять каждую минуту, номера интерфейсов можете посмотреть командой: ip a | sed -n 's/[^ ]* \(nwg\)/\1/p'

Теперь порт и адрес удаленного пира берутся из конфигурации WG, их не нужно указывать,
если в конфигурации WG добавлено несколько пиров, данные берутся от первого.

В переменных gateX указывается адрес который будет пинговаться через интерфейс WG, обычно это шлюз внутри сети WG или любой другой адрес в интернете отвечающий на пинг, 
число X должно совпадать с номером интерфейса WG для которого вы указываете адрес для пинговки

Каждую минуту проверяется включен ли интерфейс nwgХ, если включен, то

пингом проверяется доступность указанного в gateX адреса, если через интерфейс WG на него не прошел пинг четыре раза подряд,

запускается генерация случайного порта из диапазона 2000-65000 с проверкой его занятости, если занят, генерируется другой порт
запускается пинговка 9 раз по UDP с нового порта на пир WG
на интерфейсе WG устанавливается новый порт:

Скрытый текст
#!/bin/sh
PATH=/opt/sbin:/opt/bin:/opt/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

fey="0 2 3"
gate0=172.16.0.1
gate2=191.225.216.1
gate3=10.8.0.1

gnip()
{
! ping -I nwg$1 -s0 -qc1 -W1 $2 >/dev/null 2>&1
}

for i in $fey; do
 ip a s nwg$i | grep -q UP || continue
 gate=$(eval echo \$gate$i)
 if gnip $i $gate && gnip $i $gate && gnip $i $gate && gnip $i $gate; then
  rem=$(echo $(ndmc -c "show interface Wireguard$i" | sed -n 's/.*remote.*: \(.*\)/\1/p'))
  port=$(awk 'BEGIN{srand();print int(rand()*63000)+2000}')
  while netstat -nlu | grep -qw $port; do
   port=$(awk 'BEGIN{srand();print int(rand()*63000)+2000}')
  done
  nping --udp --count 9 --source-port $port --data-length 64 --dest-port $(echo $rem | cut -f2 -d' ') ${rem%% *} >/dev/null 2>&1
  ndmc -c "interface Wireguard$i wireguard listen-port $port" >/dev/null 2>&1
 fi
done >/dev/null 2>&1

 


после создания файла дайте ему разрешения командой:
chmod 755 /opt/etc/cron.1min/pinger

Проверяем возможные команды и запускаем крон:
/opt/etc/init.d/S10cron -?
Usage: /opt/etc/init.d/S10cron (start|stop|restart|check|status|kill|reconfigure)
/opt/etc/init.d/S10cron start

Edited by Frans
Теперь порт и адрес удаленного пира берутся из конфигурации WG, их не нужно указывать
  • Upvote 1
Link to comment
Share on other sites

  • Ground_Zerro changed the title to Обход блокировки протокола WireGuard, в т.ч. AmneziaWG
В 02.11.2024 в 21:44, Frans сказал:

Теперь порт и адрес удаленного пира берутся из конфигурации WG, их не нужно указывать

Только вчера подумал о том, что это уже есть в роутере и нужно просто вытащить. Но было поздно - лег спать ))

Исправлю на гит.
Спасибо.

 

Может есть смысл ещё более упростить скрипт?

Например пусть он проходит по всем nwg интерфейсам, чтобы не нужно было их явно указывать в самом скрипте. Не думаю что на домашнем роутере их больше одного - трёх.

А пингует какой-нибудь четко заданный узел, условный  8.8.8.8, который не закроет icmp  от пары пингов в минуту, посчитав это за ddos.

Вариативности будет меньше, в том смысле например если нужно проверять доступность каких-то конкретных узлов сети через WG. Но в таком случае скрипт становится максимально простым для использования бОльшим числом пользователей. Полностью отпадёт необходимость тонкой настройки самого скрипта. Можно будет написать opkg или sh который нужно будет просто один раз установить и всё.

Edited by Ground_Zerro
Link to comment
Share on other sites

12 часа назад, Ground_Zerro сказал:

Может есть смысл ещё более упростить скрипт?

Например пусть он проходит по всем nwg интерфейсам, чтобы не нужно было их явно указывать в самом скрипте. Не думаю что на домашнем роутере их больше одного - трёх.

А пингует какой-нибудь четко заданный узел, условный  8.8.8.8, который не закроет icmp  от пары пингов в минуту, посчитав это за ddos.

У меня на KN-1011 настроено шесть WG, три из них работают в качестве серверов, из них 2 работают круглосуточно, остальные три в качестве клиентов.
На серверах совершенно не нужна проверка и смена порта.
Сделал так:
Проверяются интерфейсы WG, исключая отключенные или работающие как сервера WG с пирами имеющими адрес 0.0.0.0 и/или портом равным 0
Адресом для проверки прохождения пинга через интерфейс WG установил 1.1.1.1

 

Скрытый текст
#!/bin/sh
PATH=/opt/sbin:/opt/bin:/opt/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

gnip()
{
! ping -I nwg$1 -s0 -qc1 -W1 1.1.1.1 >/dev/null 2>&1
}

for i in $(ip a | sed -n 's/.*nwg\(.*\): <.*UP.*/\1/p'); do
 rem=$(echo $(ndmc -c "show interface Wireguard$i" | sed -n 's/.*remote.*: \(.*\)/\1/p'))
 echo $rem | grep -q '^0\| 0' && continue
 if gnip $i && gnip $i && gnip $i && gnip $i; then
  port=$(awk 'BEGIN{srand();print int(rand()*63000)+2000}')
  while netstat -nlu | grep -qw $port; do
   port=$(awk 'BEGIN{srand();print int(rand()*63000)+2000}')
  done >/dev/null 2>&1
  nping --udp --count 9 --source-port $port --data-length 64 --dest-port $(echo $rem | cut -f2 -d' ') ${rem%% *} >/dev/null 2>&1
  ndmc -c "interface Wireguard$i wireguard listen-port $port" >/dev/null 2>&1
 fi
done

 

 

Edited by Frans
исправил ошибку sed и оптимизировал
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...