Repository navigation
Conversation
Аудит документации против кода: пункт меню 12, баннер подменю, строка помощи CLI и
описания в docs/{,ru/,zh-CN/}configuration.md и docs/{,ru/,zh-CN/}architecture.md
называли бэкенд «AmneziaWG 2.0», хотя с этапа 3.1 формат включён по умолчанию —
в реестре `three_enabled: true`, в конфиге HeaderProtectionKey, RandomTrailers,
S3/S4. Это тот же класс ошибки, что уже починили в баннере установки: экран
противоречил своему же junk-блоку, а документация — поведению по умолчанию.
Теперь ярлык нейтральный — «AmneziaWG (системный VPN)», а версия формата указана
там, где она действительно полезна: в документации «3.1 по умолчанию», а в
подменю — переключатель режима с текущим состоянием.
Ширина рамки баннера сохранена: inner 47 символов, как у соседнего меню Hysteria,
поэтому подменю не разъезжается.
Проверено: bash -n xrayebator; validation/test-awg-lifecycle.sh — зелёный;
проверки документов (ссылки/якоря, таблицы, кодировка) — 0 расхождений.
Фолбэк-ветка установки (когда в системе нет add-apt-repository) писала /etc/apt/sources.list.d/amneziawg.list без ключа. На живом сервере это дало `apt-get update` rc=100 («NO_PUBKEY 4166F2C257290828 / repository is not signed»): переставали работать ЛЮБЫЕ apt-операции — в том числе установка плагина certbot для DNS-01 — а amneziawg из PPA больше не находился. Модуль на том сервере собрался ручной сборкой, поэтому дефект не всплыл сразу и жил с 19.10. Теперь репозиторий пишется единственным путём — после того как ключ подписи лёг в брелок (signed-by). Нет ключа или сети — репозиторий не добавляется вовсе: apt остаётся рабочим, а установка честно уходит на ручную сборку. add-apt-repository после неудачи ещё и откатывается, потому что он умеет оставить за собой неподписанный PPA. Уже испорченные серверы чинит разовая миграция migrate_awg_ppa_signed_2026 (main_menu, quickstart, happ-setup): подписывает репозиторий, а если ключ не достать — убирает его. Xray не перезапускается: правка не касается его конфига, поэтому это не run_migration(), а отдельная миграция по маркеру. Guarded by validation/test-awg-ppa-repo.sh: строка репозитория проверяется функционально, ремонт — на временных путях с подменённым apt-get update.
Публичный SSH-порт на VPN-сервере перебирают постоянно. Замер на живом сервере 2.26.125.228 (2026-10-23): 9 953 неудачных попытки за сутки со 115 адресов, топ — по 1 197 попыток с шести адресов 109.160.32.0/24, при этом root пускался по паролю, а ufw разрешал 22/tcp без limit. Добавлена команда `xrayebator ssh-protect` (--status/--enable/--disable, --unban IP, --quiet для скриптов): ставит fail2ban с джейлом sshd (journald-backend, 4 неудачи за 10 минут → бан 30 минут, повторным — до суток) и drop-in sshd с MaxAuthTries 3 / LoginGraceTime 30. Парольный вход осознанно НЕ выключается: GUI и деплой ходят на сервер по SSH-паролю, режим key-only сломал бы управление сервером из приложения. Ключи — отдельное решение оператора. Включение идемпотентно: файлы перезаписываются, конфиг sshd проверяется `sshd -t` до применения (битый drop-in откатывается к прежнему), ssh перечитывает конфиг через reload, а не restart — открытые сессии GUI не рвутся. В меню появился пункт 15 «Безопасность», защита включается сама при установке (install.sh) и при деплое (quickstart; в JSON добавлено поле ssh_protect, чтобы GUI знал результат). Guarded by validation/test-ssh-protect.sh (конфиги, сохранение парольного входа, статус-JSON, снятие защиты, проводка) и обновлённым test-main-menu-numbering.sh (теперь 15 пунктов).
Защита SSH уже есть командой, но оператор живёт в GUI: без кнопки он о ней не узнает. На странице сервера появилась четвёртая вкладка «Безопасность» с карточкой: состояние fail2ban, действующие лимиты sshd (MaxAuthTries / LoginGraceTime), неудачные попытки за сутки, сколько адресов в бане и какие именно, кнопки «Включить/Выключить защиту» и «Обновить». Проводка стандартная для серверных операций: MaintenanceManager → IPC (`security:sshProtectStatus|Enable|Disable`) → preload → экран. Статус тянется best-effort параллельно с профилями: сервер со старым менеджером защиту не знает, и это не должно ронять страницу. Парольный вход приложение не выключает и прямо пишет об этом в карточке — иначе оно само потеряло бы доступ к серверу (authMethod: password). Ключи остаются осознанным решением оператора, а не побочным эффектом кнопки. Ключи i18n добавлены во все три локали (ru/en/zh) — паритет проверяется тестом; серверный ответ разбирается с защитными значениями по умолчанию, поэтому частичный JSON не ломает экран. Guarded by tests/unit/server-settings-render.test.ts: разделов теперь четыре, у каждого своя панель с aria-связкой.
Описано то, что появилось в двух предыдущих коммитах, во всех трёх локалях: - security.md — раздел «Brute-force protection (fail2ban)»: цифры живого замера (9 953 неудачи за сутки со 115 адресов), что именно пишется в систему, почему парольный вход остаётся включён (GUI управляет сервером по SSH-паролю), где это доступно (пункт меню 15, вкладка «Безопасность», автовключение в install.sh и quickstart) и как разбанить себя; - configuration.md — строка в таблице команд с флагами и поведением; - desktop-gui.md — `ssh-protect` в списке команд, которые вызывает приложение, и абзац про вкладку «Безопасность»; - testing.md — два новых скрипта валидации; - troubleshooting.md — раздел про падающую установку AWG: симптом `NO_PUBKEY … / repository is not signed`, причина (репозиторий без ключа), что чинит миграция и как поправить руками; - README ×3 — строка в таблице возможностей; - CHANGELOG — Added (защита SSH) и Fixed (неподписанный PPA). Проверено: check-links (0 битых ссылок и расхождений колонок), check-encoding (35 файлов, вне алфавита 0), parity (расхождения заголовков и таблиц не появились; остаётся прежний шум переводов в zh-CN).
По скриншоту с живого сервера: отдельная четвёртая вкладка «Безопасность» оказалась лишним кликом ради одной карточки, а вкладка «Подписка и TLS» и без того про безопасность периметра. Теперь карточка защиты SSH — второй блок внутри той же панели, а панель называется «Безопасность и TLS» (Security and TLS, 安全与 TLS). Панель остаётся одна: разделы снова create/backends/subscription, отдельного секции security нет — тест разделов возвращён к трём id, а карточки друг под другом держит sectionPane с gap 20px. Ключ settings.sectionSecurity удалён из всех трёх локалей (стал не нужен), значение sectionSubscription переписано.
Доки ушли на dev ещё в 3fc6d78, но после переезда карточки внутрь панели (ddadbd7) в них осталось старое имя вкладки — «Безопасность» / Security / 安全. Это тот же класс расхождения, что мы уже ловили в ярлыке AmneziaWG: написанное перестало соответствовать экрану через один коммит. Теперь везде «Безопасность и TLS» (Security and TLS, 安全与 TLS) и явно сказано, что карточка защиты SSH — блок внутри этой панели, а не отдельная вкладка: security.md ×3, desktop-gui.md ×3, configuration.md ×3, README ×3, CHANGELOG. Карточка подписки внутри панели по-прежнему называется «Подписка и TLS» — это её собственный заголовок (settings.dns01Title), и он не менялся. Проверено: ссылки/якоря 0 битых, таблицы 0 расхождений, кодировка 35/35, parity без расхождений заголовков и таблиц.
… пароль Две проблемы со скриншота живого сервера. 1. Строка «Парольный вход оставлен — без него приложение потеряет доступ к серверу» читалась как «включил защиту — потерял вход по паролю», хотя fail2ban вообще не трогает способ аутентификации: она только банит источник перебора. Это была моя пояснительная ремарка про архитектурное решение, и в интерфейсе она только пугает. Из карточки убрана совсем; обоснование осталось там, где ему место — в docs/security.md. 2. Состояние читалось только по бейджу, а иконка была одна на всё. Теперь щит говорит сам: зелёный — защита работает, перечёркнутый красноватый — установлена и остановлена, нейтральный — нет данных. При включённой защите подзаголовок показывает само правило («4 неудачи за 10 минут → бан 30 минут, повторным — до суток»), при выключенной — что именно сделает включение. Заодно убран обрезанный четвёртый столбец «ДЖЕЙЛ» («fail2ban · sshd, 4 неудачи за 10 …»): правило переехало в подзаголовок, а в сетке остались три числовых значения — неудачи за сутки, баны сейчас и лимиты sshd. Проверено offscreen-прогоном собранного renderer'а в обоих состояниях (включено/выключено) — layout не разъезжается, обрезаний нет.
Две правки формулировок в карточке защиты SSH по замечаниям с экрана. 1. Бейдж выключенного состояния: «остановлена» → «выключена». «Остановлена» читается как «служба упала», а не как «оператор её выключил» — при том что рядом стоит кнопка «Включить защиту». 2. Правило под заголовком: «4 неудачи за 10 минут → бан 30 минут, повторным — до суток» → «4 неудачи за 10 минут → бан 30 минут, повторная попытка перебора → бан до суток». «Повторным» без пояснения читалось как «повторным неудачам», хотя речь про повторный заход уже забаненного адреса (bantime.increment в fail2ban). Локали: enabled/disabled и правило переписаны в ru/en/zh одинаково по смыслу. Логика и серверная часть не менялись — правка только в строках интерфейса.
Живой случай 2026-10-24 (свежий VPS, Ubuntu 22.04 + Xray 26.10.21): install.sh падал на «Сгенерированный config.json не прошёл валидацию — live-конфиг не изменён». Причина не в самом конфиге, а в имени временного файла. Ядро 26.10.21 определяет формат конфига по РАСШИРЕНИЮ файла, а флаг `-format json` (на который опирался install.sh) его больше не переопределяет: Failed to start: ... > main/confloader/external: unsupported config file extension: /usr/local/etc/xray/config.json.tmp.GbsZMJ mktemp даёт имя вида config.json.tmp.XXXXXX, то есть расширение «GbsZMJ», — валидация падала на ЛЮБОЙ чистой установке с этим ядром. На проде стоит 26.3.27, где -format json ещё работал, поэтому баг не всплывал раньше. Теперь временный конфиг создаётся как config.json.tmp.XXXXXX.json (mktemp --suffix=.json) с фолбэком на переименование, если mktemp старый. Тот же приём применён к двум временным конфигам в update.sh (правка DNS там молча пропускалась по той же причине). Заодно install.sh теперь печатает вывод `xray run -test` при провале: без него сообщение «не прошёл валидацию» не говорит, что именно не понравилось ядру, и причина ищется вслепую. Guarded by validation/test-config-temp-json-suffix.sh: статически проверяет суффикс в install.sh/update.sh и печать вывода ядра, функционально — что mktemp --suffix=.json действительно даёт *.json (и что установленный xray отвергает конфиг без расширения).
Описан дефект, из-за которого чистая установка падала на Xray 26.10.21: ядро определяет формат конфига по расширению файла и игнорирует `-format json`, а установщик проверял mktemp-файл без .json. - CHANGELOG → Fixed: симптом, причина, что исправлено в install.sh и update.sh, и что провал теперь печатает вывод ядра; - testing.md ×3 → строка нового скрипта `test-config-temp-json-suffix.sh` и счётчик скриптов 42 → 45 (в трёх местах: вводный абзац, список и CI-раздел). Проверки документов: ссылки/якоря 0 битых, таблицы 0 расхождений, кодировка 35/35, parity без новых расхождений.
Защита включается автоматически при установке (install.sh, quickstart), а записывала настройки в секцию [DEFAULT] конфига fail2ban. На сервере, где оператор уже держит свой fail2ban с другими джейлами, это молча меняло бы bantime/findtime/maxretry и bantime.increment для ВСЕХ джейлов — то есть автоматическая защита вмешивалась бы в чужую настройку. Теперь все ключи живут внутри [sshd]: backend=systemd, bantime=30m, findtime=10m, maxretry=4, bantime.increment/factor/maxtime. Поведение нашей защиты не меняется, чужие джейлы не затрагиваются. Тест дополнен: `[DEFAULT]` в файле запрещён, а maxretry обязан идти после заголовка секции — иначе регрессия вернётся молча.
Досинхронизировал то, чего не хватало после фикса установщика: - troubleshooting.md ×3 — раздел про «Сгенерированный config.json не прошёл валидацию — live-конфиг не изменён» на Xray 26.10.21+: симптом, настоящая причина (имя временного файла и определение формата по расширению), что исправлено и что делать на старой сборке. Раньше об этой ошибке в доках не было ничего, хотя это полный стоп установки; - security.md ×3 — уточнено, что настройки джейла лежат внутри `[sshd]`, поэтому уже настроенный оператором fail2ban с другими джейлами не меняется. Это прямое следствие того, что защита включается автоматически при установке. Проверки документов: ссылки/якоря 0 битых, таблицы 0 расхождений, кодировка 35/35, parity без новых расхождений.
Защита включается автоматически, и это создаёт новый способ запереться: 4 неверных пароля за 10 минут — и собственный адрес в бане на 30 минут, причём GUI ходит с того же адреса. Для оператора это полчаса без управления сервером, а на свежей машине без консоли хостера — вообще тупик. Теперь при включении защиты адрес, с которого пришла установка/деплой, попадает в ignoreip джейла на 24 часа (SSH_CONNECTION/SSH_CLIENT — sshd отдаёт его и для неинтерактивных команд, поэтому GUI-деплой тоже виден). Рядом пишется `# xrayebator-ignore-until: <epoch>`, а таймер xrayebator-ssh-ignoreip (каждые 30 минут + при загрузке) вызывает `ssh-protect --prune-ignore` и снимает строку по истечении срока. Детали, которые важны: - `ignoreip` живёт внутри `[sshd]` — чужие джейлы fail2ban не затрагиваются; - базовый `ignoreip = 127.0.0.1/8 ::1` пишется ВСЕГДА: без строки политика игнора джейла становится пустой (проверено live: после снятия адреса `fail2ban-client get sshd ignoreip` показывал «No IP address/network»); - если включение идёт не по SSH (консоль хостера), действующее исключение сохраняется, а не выкидывается; - `--disable` снимает и таймер, и исключение; - статус-JSON получил `ignore_ip`/`ignore_until` (в GUI не показываем — по просьбе, это служебное поле для CLI и тестов). Проверено live на тестовом сервере: после `--enable` по SSH `fail2ban-client get sshd ignoreip` показывает наш адрес, таймер активен, `--prune-ignore` при будущем сроке ничего не делает, а после подмены срока на истёкший — снимает строку, перезагружает джейл и удаляет юниты таймера. Guarded by validation/test-ssh-protect.sh: конфиг со сроком, разбор адреса из SSH_CONNECTION/SSH_CLIENT, отказ для loopback, снятие по сроку и сохранение loopback-базы.
Описал новое поведение защиты SSH во всех трёх локалях: - security.md — адрес, с которого шла установка/деплой, исключён из банов на 24 часа: `ignoreip` + метка `# xrayebator-ignore-until`, снятие таймером `xrayebator-ssh-ignoreip.timer` (30 минут), базовая строка loopback остаётся навсегда; это деталь CLI/JSON и в GUI не показывается; - configuration.md — в строке команды добавлен `--prune-ignore` и краткое описание исключения; - architecture.md — новый пункт про то, как устроены джейл, исключение и таймер (рядом с трафиком и мониторингом); - testing.md — строка `test-ssh-protect.sh` дополнена покрытием исключения: разбор адреса из SSH_CONNECTION/SSH_CLIENT, отказ для loopback, снятие по сроку, сохранение loopback-базы; - CHANGELOG — тот же смысл в записи про защиту SSH. Проверки документов: ссылки/якоря 0 битых, таблицы 0 расхождений, кодировка 35/35, parity без новых расхождений.
Живая находка на Ubuntu 24.04: после цикла «срок истёк → prune удалил юниты
таймера → повторный --enable создал их заново» таймер оставался active, но
следующий запуск не планировался:
TimersMonotonic={ OnUnitActiveUSec=30min ; next_elapse=0 }
NextElapseUSecMonotonic=infinity
То есть исключение из банов могло не сняться само — ровно в том сценарии,
ради которого таймер и заведён (24 часа прошли, строка осталась). Причина:
OnUnitActiveSec считается от последней активации сервиса, а после
пересоздания юнита systemd не видит для неё будущего момента.
Теперь расписание календарное: OnCalendar=*:0/30 (:00 и :30 каждый час) плюс
OnBootSec=10min и Persistent=true. Проверено на сервере: после prune →
--enable list-timers показывает NEXT 16:00, сервис по запуску отдаёт
{"pruned":false,"active":true}, исключение на месте.
Тест дополнен: таймер обязан использовать OnCalendar, иначе тихая регрессия
(активный таймер без расписания) вернётся незамеченной.
Живой случай 2026-10-24 (Debian 12, ядро 6.1.0-45-amd64, установка через GUI):
пакет amneziawg из PPA встал, но модуль не собрался. Причина — DKMS без
заголовков: Debian не ставит linux-headers по умолчанию, а точного пакета
`linux-headers-6.1.0-45-amd64` в репозитории уже нет (там 47/50/53/54).
`dkms status` показывал «amneziawg/1.0.0: added», DKMS молчал, и установка
падала общим «Не удалось установить amneziawg (PPA и ручная сборка)» — по
такому сообщению причину не найти.
Что сделано:
- новый шаг `_awg_install_kernel_headers`: сначала точный пакет запущенного
ядра, затем метапакеты `linux-headers-<arch>` + `linux-image-<arch>` (на
Ubuntu — `linux-headers-generic`). Образ ставится вместе с заголовками
намеренно: без него DKMS собрал бы модуль под ядро, в которое сервер никогда
не загрузится;
- PPA-путь при отсутствии модуля теперь сначала проверяет заголовки, потом
пересобирает DKMS, и отдельно распознаёт «собрано для другого ядра»
(`return 2`), а `_awg_install` превращает это в `return 3`;
- `awg-install` на этот случай отдаёт `{"ok":false,"reboot_required":true,
"running_kernel":…,"built_for":…,"error":"…"}` с кодом 0: GUI показывает
текст из JSON, а не первую строку прогресса из stderr. В stderr печатается
пошаговая инструкция (перезагрузить и повторить awg-install);
- ручная сборка больше не линкует архив: `linux-source` на Debian/Ubuntu лежит
как `linux-source-*.tar.xz`, его распаковывают в рабочий каталог и на дерево
ссылаются уже оттуда;
- в ручной сборке заголовки ставятся тем же хелпером.
Проверено на живом Debian 12: после фикса `awg-install` доустановил заголовки
6.1.0-54 вместе с образом, DKMS собрал модуль именно под него и вернул
`reboot_required` вместо «не удалось установить».
Guarded by validation/test-awg-lifecycle.sh (хелпер заголовков, порядок
«заголовки → DKMS», отдельный статус перезагрузки, распаковка linux-source).
Описал живой случай 2026-10-24 (Debian 12, ядро 6.1.0-45, установка через GUI): - troubleshooting.md ×3 — новый пункт в «Установка AWG падает»: `dkms status` показывает «amneziawg/1.0.0: added», а модуля нет. Причина — нет заголовков ядра: Debian/Ubuntu не ставят их по умолчанию, а для старого ядра точного пакета уже нет в репозитории. Что делает установщик (ставит заголовки, при необходимости собирает под новое ядро вместе с его образом), что значит `reboot_required: true` и что делать дальше; - configuration.md ×3 — в разделе AmneziaWG добавлено, что DKMS нужны заголовки, что образ нового ядра ставится вместе с заголовками (иначе ребут не поможет) и что после перезагрузки команда завершает настройку; - CHANGELOG → Fixed с тем же содержанием и упоминанием распаковки linux-source в ручной сборке. Проверки документов: ссылки/якоря 0 битых, таблицы 0 расхождений, кодировка 35/35, parity без новых расхождений.
Живой случай 2026-10-24, установка AmneziaWG из GUI на Debian 12: сервер
вернул `{"ok":false,"reboot_required":true,"error":"…Перезагрузите сервер…"}`,
но карточка просто осталась «Не установлено» — без единого слова о причине.
Причина в `runBackendAction`: он ждал только выброшенное исключение, а
`{ok:false, error}` из JSON молча проглатывал. Выглядело как «нажал —
ничего не произошло».
Теперь результат разбирается: `backendActionFailure` (новый чистый модуль)
возвращает `{failed, error}`, и при `ok:false` GUI показывает текст от сервера
в общем блоке ошибки, не перерисовывая карточку. Правило действует для ВСЕХ
операций бэкендов (установка/запуск/гранты/тумблеры), потому что все они идут
через `runBackendAction`.
Ключ `settings.backendActionFailed` добавлен во все три локали — на случай,
когда сервер не приложил текст.
Проверено: ui-contracts получил контракт разбора (ok:false с текстом, без
текста, пробельный текст, ok:true, undefined, null, строка);
typecheck чистый, сборка собрана.
Патч-релиз: защита SSH от перебора и исправления, без которых ломалось
развёртывание на новых ядрах и дистрибутивах.
- package.json / package-lock.json → 0.7.1;
- CHANGELOG: Unreleased превращён в [0.7.1] - 2026-10-24 с вводным абзацем,
добавлен Fixed про GUI, который терял отказ {ok:false};
- docs/releases/v0.7.1.md и v0.7.1.en.md — релизные заметки (английская
версия используется workflow как тело релиза);
- troubleshooting ×3: формулировки «после 0.7.0» → «в 0.7.1», потому что фиксы
теперь действительно входят в релиз.
Цифры в заметках — проверяемые: 45 Bash-валидаций, 174 unit-теста (173
зелёных на Windows, известный win32-кейс shell-command), живые прогоны на
Ubuntu 22.04 (прод), Ubuntu 24.04 (с нуля, 8/8 маршрутов) и Debian 12
(установка AWG с путём reboot_required).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Что это
Патч-релиз
0.7.1: одна новая возможность (защита SSH от перебора) и пять исправлений, без которых разворачивание ломалось на новых ядрах и дистрибутивах. 20 коммитов,46 files changed, 2238 insertions(+), 54 deletions(-).Блоки изменений
1. Защита SSH от перебора (
8078d7b,aa39e03,0054cf6,0d64df8, GUI28a24db,ddadbd7,b846d84,e972a2c)Замер на живом сервере 23.10: 9 953 неудачных входа за сутки со 115 адресов,
rootпускал по паролю,ufw—22/tcpбезlimit.xrayebator ssh-protect(--status,--enable,--disable,--unban IP,--prune-ignore,--quiet) ставитfail2banс джейломsshd: 4 неудачи за 10 минут → бан 30 минут, повторным — до суток; плюс drop-insshd_config.dсMaxAuthTries 3/LoginGraceTime 30.[sshd], а не в[DEFAULT]: иначе автоматическая установка меняла бы чужие джейлы fail2ban.rootпо SSH-паролю. Режим «только ключи» — отдельное решение оператора.sshd -tдо применения,sshперезапускается черезreload— открытые сессии не рвутся.ignoreip+ метка срока, снятие таймеромxrayebator-ssh-ignoreip.timer(OnCalendar=*:0/30). Живая проверка: 6 неверных паролей с исключённого адреса — счётчики fail2ban нулевые, бана нет.install.shиquickstart(полеssh_protectв JSON).2. Чистая установка на Xray 26.10+ (
ae14797)install.shвалидировалmktemp-файлconfig.json.tmp.XXXXXX. С 26.10 ядро определяет формат по расширению и игнорирует-format json→main/confloader/external: unsupported config file extension, установка падала на любом чистом сервере (на проде стоит 26.3.27, где флаг ещё работал). Теперь временный конфиг*.json; тот же дефект исправлен в двух временных конфигахupdate.sh(там молча пропускалось обновление DNS); при провале печатается вывод ядра.3. PPA AmneziaWG без ключа ломал
apt(1b4ee20)В ветке без
add-apt-repositoryрепозиторий писался без ключа →apt-get updateвозвращалrc=100, ломались все apt-операции (включая плагин certbot для DNS-01), аapt install amneziawgне находил пакет. Теперь репозиторий — только сsigned-by, нет ключа — нет репозитория; испорченные серверы чинит миграцияawg_ppa_signed_2026(main_menu/quickstart/happ-setup).4. AmneziaWG на Debian 12 (
8595a0c)Пакет из PPA ставился, но DKMS не собирал модуль: Debian не ставит
linux-headers, а для ядра старше репозитория точного пакета нет (живой случай: запущено6.1.0-45, в репо47/50/53/54),dkms statusоставалсяadded. Теперь установщик ставит заголовки сам; если доступны только новые — собирает под новое ядро, ставит и образ и отдаёт{ok:false, reboot_required:true, running_kernel, built_for, error}. Ручная сборка распаковываетlinux-sourceвместо симлинка на архив.5. GUI больше не теряет отказ бэкенда (
72562e6)runBackendActionждал только исключение, а{ok:false, error}из JSON проглатывал: на живом сервереawg-installвернулreboot_requiredс текстом, а карточка осталась «Не установлено». Теперь результат разбирается общим хелпером и причина показывается в интерфейсе — для всех операций бэкендов.6. Ярлык AmneziaWG (
84b59c9)«AmneziaWG 2.0» в пункте меню, баннере, справке CLI и доках → нейтральный ярлык, версия формата указана там, где полезна.
Документация
security.md,configuration.md,desktop-gui.md,testing.md,troubleshooting.md,architecture.md— все три локали; README ×3; CHANGELOG; релизные заметкиdocs/releases/v0.7.1.{md,en.md}. Проверки: ссылки/якоря 0 битых, расхождения таблиц 0, кодировка чистая, parity без расхождений.Как проверено
test-ssh-protect.sh,test-config-temp-json-suffix.sh; расширеныtest-awg-lifecycle.sh,test-main-menu-numbering.sh),bash -nвсех скриптов.shell-commandбез/bin/sh),npm run typecheck,npm run build.reboot_requiredдо перезагрузки, после — модуль,awg0, 3.1 с HPK/RandomTrailers, пир, 7/7 маршрутов).Риски и ограничения
ssh-protect --enable/ кнопка в GUI). Это указано в релизных заметках.После мёржа
v0.7.1на merge-коммит → workflow соберёт и опубликует релиз (нужен секретAP3X0сContents: Read and write, иначе релиз снова выйдет отgithub-actions[bot]).docs/releases/v0.7.1.en.md(preflight проверяет его наличие).