Ground_Zerro Posted November 2 Share Posted November 2 (edited) Готовое решение (ЖМИ). Тема на 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 Sunday at 11:36 PM by Ground_Zerro add git Quote Link to comment Share on other sites More sharing options...
Frans Posted November 2 Share Posted November 2 (edited) У себя сделал так: установить 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 yesterday at 06:49 PM by Frans Теперь порт и адрес удаленного пира берутся из конфигурации WG, их не нужно указывать 1 Quote Link to comment Share on other sites More sharing options...
Ground_Zerro Posted 18 hours ago Author Share Posted 18 hours ago (edited) В 02.11.2024 в 21:44, Frans сказал: Теперь порт и адрес удаленного пира берутся из конфигурации WG, их не нужно указывать Только вчера подумал о том, что это уже есть в роутере и нужно просто вытащить. Но было поздно - лег спать )) Исправлю на гит. Спасибо. Может есть смысл ещё более упростить скрипт? Например пусть он проходит по всем nwg интерфейсам, чтобы не нужно было их явно указывать в самом скрипте. Не думаю что на домашнем роутере их больше одного - трёх. А пингует какой-нибудь четко заданный узел, условный 8.8.8.8, который не закроет icmp от пары пингов в минуту, посчитав это за ddos. Вариативности будет меньше, в том смысле например если нужно проверять доступность каких-то конкретных узлов сети через WG. Но в таком случае скрипт становится максимально простым для использования бОльшим числом пользователей. Полностью отпадёт необходимость тонкой настройки самого скрипта. Можно будет написать opkg или sh который нужно будет просто один раз установить и всё. Edited 17 hours ago by Ground_Zerro Quote Link to comment Share on other sites More sharing options...
Frans Posted 7 hours ago Share Posted 7 hours ago (edited) 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 6 hours ago by Frans исправил ошибку sed и оптимизировал Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.