From 6fdacd71c3601d9b1f2bb25caa6f1e339f3f519c Mon Sep 17 00:00:00 2001 From: Ivan Vasilev Date: Mon, 17 Aug 2026 18:48:38 +0300 Subject: [PATCH] Fix 3.1 rollout on servers with an older loaded module and UFW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three defects surfaced on a real upgrade from 2.0. The module gate read modinfo, which reports the version on disk. apt --only-upgrade rebuilds the DKMS module but does not replace the one already loaded, so the installer wrote a 3.1 conf that the running 2.0 module rejected with 'Unable to modify interface: Invalid argument'. It now compares /sys/module/amneziawg/version, reloads the module when it lags behind the disk, and decides the generation from the loaded version only. Bringing the interface up is the only real proof the kernel accepted the 3.1 parameters, so a failure there no longer aborts the install: it enables dynamic debug, retries, prints the amneziawg lines from dmesg, then strips the 3.x keys and comes up on 2.0. The rejected conf is kept as awg1.conf.awg31. PostUp appends 'iptables -A FORWARD', but UFW inserts its jumps at the head of the chain, so client traffic was dropped with '[UFW BLOCK] IN=awg1 OUT=eth0' — the tunnel connected and had no internet. When UFW is active the installer now adds 'ufw route allow in on awg1 out on eth0' and opens the AWG and UI ports. Version 0.1.4.1. --- awg-ctrl/package.json | 2 +- awg-ui/package.json | 2 +- awg-ui/src/App.tsx | 2 +- cli/package.json | 2 +- cli/src/index.ts | 2 +- install.sh | 91 ++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 91 insertions(+), 10 deletions(-) diff --git a/awg-ctrl/package.json b/awg-ctrl/package.json index 9fc6cc0..bae8a83 100644 --- a/awg-ctrl/package.json +++ b/awg-ctrl/package.json @@ -1,6 +1,6 @@ { "name": "awg-control", - "version": "0.1.4", + "version": "0.1.4+1", "description": "", "main": "dist/index.js", "scripts": { diff --git a/awg-ui/package.json b/awg-ui/package.json index 4628989..7822f52 100644 --- a/awg-ui/package.json +++ b/awg-ui/package.json @@ -1,6 +1,6 @@ { "name": "awg-ui", - "version": "0.1.4", + "version": "0.1.4+1", "private": true, "scripts": { "dev": "vite", diff --git a/awg-ui/src/App.tsx b/awg-ui/src/App.tsx index 4558083..482786d 100644 --- a/awg-ui/src/App.tsx +++ b/awg-ui/src/App.tsx @@ -214,7 +214,7 @@ export default function App() { >
Forgetting - Alpha 0.1.4 + Alpha 0.1.4.1

{TABS.map(t => ( diff --git a/cli/package.json b/cli/package.json index 23bfe96..2cd4955 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "awg-cli", - "version": "0.1.4", + "version": "0.1.4+1", "private": true, "dependencies": { "tsx": "^4.7.0", diff --git a/cli/src/index.ts b/cli/src/index.ts index cb02996..11602b8 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -287,7 +287,7 @@ async function interactiveMenu() { const printMenu = () => { console.clear(); - console.log(`\n${bold(`${C.blue}── Forgetting Alpha 0.1.4 ──${C.reset}`)}\n`); + console.log(`\n${bold(`${C.blue}── Forgetting Alpha 0.1.4.1 ──${C.reset}`)}\n`); for (const name of ALL) { const running = isRunning(name); diff --git a/install.sh b/install.sh index 43546bb..296e38c 100644 --- a/install.sh +++ b/install.sh @@ -19,7 +19,7 @@ trap 'rc=$?; echo -e "\n ${RED}✗ НЕОЖИДАННАЯ ОШИБКА${NC} с [[ $EUID -ne 0 ]] && fail "Запусти от root: sudo bash install.sh" [[ -z "${BASH_VERSION:-}" ]] && fail "Нужен bash: bash install.sh" -VERSION="0.1.4" +VERSION="0.1.4.1" GH_REPO="maeneko/forgetting" BASE_URL="https://github.com/${GH_REPO}/releases/download/v${VERSION}" PROJECT="/opt/awg-control" @@ -469,18 +469,48 @@ kernel_lt_5_5() { [[ "$maj" -lt 5 || ( "$maj" -eq 5 && "$min" -lt 5 ) ]] } -MOD_VER=$(modinfo -F version amneziawg 2>/dev/null || echo "") +# 🛑 Версий у модуля две, и путать их нельзя: +# modinfo — что лежит на диске (после apt/DKMS); +# /sys/module/amneziawg/version — что реально загружено в ядро. +# `apt --only-upgrade` пересобирает модуль, но НЕ переставляет уже загруженный. +# Ориентироваться на modinfo — значит написать 3.1-конфиг, который старый +# загруженный модуль отвергнет: `awg setconf` вернёт «Unable to modify +# interface: Invalid argument», и awg-quick up упадёт. Решает перезагрузка модуля. +disk_mod_ver() { modinfo -F version amneziawg 2>/dev/null || echo ""; } +loaded_mod_ver() { cat /sys/module/amneziawg/version 2>/dev/null || echo ""; } + +MOD_VER=$(disk_mod_ver) if [[ "${MOD_VER%%.*}" != "3" ]]; then warn "Модуль amneziawg версии '${MOD_VER:-неизвестно}' — для AmneziaWG 3.1 нужна 3.x. Пробуем обновить." apt-get update >/dev/null 2>&1 || true DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a \ apt-get install -y --only-upgrade amneziawg amneziawg-dkms amneziawg-tools >/dev/null 2>&1 || true - MOD_VER=$(modinfo -F version amneziawg 2>/dev/null || echo "") + MOD_VER=$(disk_mod_ver) fi +# Загруженный отстал от дискового — перезагружаем. Интерфейс держит модуль, +# поэтому сначала опускаем его; awg1 на этом шаге ещё не нужен. +LOADED_VER=$(loaded_mod_ver) +if [[ -n "$LOADED_VER" && "$LOADED_VER" != "$MOD_VER" ]]; then + warn "В ядре загружен модуль $LOADED_VER, на диске $MOD_VER — перезагружаем модуль." + awg-quick down "$IFACE" 2>/dev/null || true + ip link delete dev "$IFACE" 2>/dev/null || true + if modprobe -r amneziawg 2>/dev/null && modprobe amneziawg 2>/dev/null; then + LOADED_VER=$(loaded_mod_ver) + ok "Модуль перезагружен: в ядре теперь $LOADED_VER" + else + warn "Не удалось перезагрузить модуль (занят?). Нужна перезагрузка сервера." + fi +fi + +# Дальше решает ТОЛЬКО загруженная версия — именно она обслуживает интерфейс. +[[ -n "$LOADED_VER" ]] && MOD_VER="$LOADED_VER" + if [[ "${MOD_VER%%.*}" != "3" ]]; then AWG3="n" - warn "Модуль остался на версии '${MOD_VER:-неизвестно}' — ставим сервер на AmneziaWG 2.0." + warn "В ядре модуль версии '${MOD_VER:-неизвестно}' — ставим сервер на AmneziaWG 2.0." + [[ "$(disk_mod_ver)" == 3.* ]] && \ + warn "На диске уже 3.x — после перезагрузки сервера можно переустановить и получить 3.1." elif kernel_lt_5_5; then AWG3="n" warn "Ядро $KERNEL старше 5.5 — header protection не соберётся (upstream issue #210)." @@ -637,7 +667,41 @@ if awg show "$IFACE" &>/dev/null; then awg-quick down "$IFACE" 2>/dev/null || true fi -awg-quick up "$IFACE" || fail "awg-quick up $IFACE завершился с ошибкой" +# Подъём интерфейса — единственная настоящая проверка, что ядро приняло +# 3.1-параметры. Модуль на отказ отвечает лишь «Invalid argument», причину +# печатает только в dmesg и только при включённом dynamic debug. Поэтому: +# упали → включаем debug, пробуем ещё раз, показываем причину, и если и это не +# помогло — снимаем 3.x-ключи и поднимаемся на 2.0, а не валим установку. +AWG3_KEY_RE='^(HeaderProtectionKey|ContentPaddingAddition|RekeyAfterTime|RekeyTimeout|RejectAfterTime|KeepaliveTimeout|MaxHandshakeAttempts|RandomTrailers|DisableCookies) *=' + +if ! awg-quick up "$IFACE"; then + ip link delete dev "$IFACE" 2>/dev/null || true + + if [[ "$AWG3" != "y" ]]; then + fail "awg-quick up $IFACE завершился с ошибкой" + fi + + warn "Ядро отвергло конфиг AmneziaWG 3.1. Выясняем причину." + echo "module amneziawg +p" > /sys/kernel/debug/dynamic_debug/control 2>/dev/null || true + + if awg-quick up "$IFACE"; then + ok "Со второй попытки интерфейс поднялся" + else + ip link delete dev "$IFACE" 2>/dev/null || true + echo " ── dmesg (последние 15 строк) ─────────────────" + dmesg 2>/dev/null | grep -i amneziawg | tail -n 15 | sed 's/^/ /' || true + echo " ───────────────────────────────────────────────" + + cp "$AWG_CONF" "${AWG_CONF}.awg31" + sed -i -E "/$AWG3_KEY_RE/d" "$AWG_CONF" + AWG3="n"; NEW_GEN="2.0" + warn "Откатываемся на AmneziaWG 2.0. Конфиг 3.1 сохранён: ${AWG_CONF}.awg31" + + awg-quick up "$IFACE" \ + || fail "awg-quick up $IFACE не работает даже на 2.0 — смотри dmesg выше" + ok "Интерфейс поднят на AmneziaWG 2.0" + fi +fi # awg-quick не всегда применяет приватный ключ из [Interface]/PostUp # (наблюдалось: awg show public-key = none сразу после up). Если ключ не @@ -655,6 +719,23 @@ else fail "Ключ на $IFACE не совпал: ${RUNNING_PUB:-none} ≠ $PUB_KEY" fi +# UFW. PostUp делает `iptables -A FORWARD` — правило ДОПИСЫВАЕТСЯ в конец +# цепочки, а UFW вставляет свои переходы в начало, поэтому до нашего ACCEPT +# дело не доходит и трафик клиентов наружу режется: в dmesg сыплется +# «[UFW BLOCK] IN=awg1 OUT=eth0». Симптом — «VPN подключается, интернета нет». +# Лечится штатным `ufw route allow`, менять DEFAULT_FORWARD_POLICY не нужно. +if command -v ufw &>/dev/null && ufw status 2>/dev/null | grep -qi '^Status: active'; then + echo " → UFW активен, открываем маршрут и порты" + ufw route allow in on "$IFACE" out on "$NET_IFACE" >/dev/null 2>&1 \ + || warn "ufw route allow не сработал — трафик клиентов может резаться" + ufw allow "${AWG_PORT}/udp" >/dev/null 2>&1 || warn "не удалось открыть ${AWG_PORT}/udp" + ufw allow "${UI_PORT}/tcp" >/dev/null 2>&1 || warn "не удалось открыть ${UI_PORT}/tcp" + ufw reload >/dev/null 2>&1 || true + ok "UFW: форвардинг $IFACE → $NET_IFACE, порты ${AWG_PORT}/udp и ${UI_PORT}/tcp" +else + echo " → UFW не активен, правила фаервола не трогаем" +fi + step "6/7 npm install" npm_install_all