Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
# Changelog

## v4.1.2 — 2026-09-06

Два дефекта ре-рана, из-за которых состояние ноды расходилось с тем, что печатал установщик
([#44](https://github.com/jestivald/node-accelerator/pull/44)). Дефолты не менялись, новых ручек
нет — правки только в поведении `protect` и `optimize` при повторном прогоне.

### 🟠 `protect` включал в автозагрузку чужой `nftables.service`

Персист правил через ребут у тулкита свой: `na-firewall.service` грузит `na_filter.nft` и никогда
не трогает `/etc/nftables.conf`. Но рядом `protect` включал и дистрибутивный `nftables.service`,
конфиг которого не писал. У этого юнита `ExecStop=nft flush ruleset`, а его дефолтный
`/etc/nftables.conf` начинается с `flush ruleset` — то есть любой `stop`, `restart` или `reload`
юнита, который оператор считал «нашим», сносит **все** таблицы: `na_filter`, CrowdSec, Docker.
На самой загрузке порядок детерминирован (дистрибутивный юнит стартует в `sysinit.target`, наш —
после `network-pre.target`), так что штатный бут не страдал; риск был в жизненном цикле юнита,
за который никто не отвечал.

Теперь `protect` включает только `na-firewall.service`, а состояние `nftables.service` не меняет
ни на первом прогоне, ни на повторных: если оператор включил его сам и ведёт свой
`/etc/nftables.conf` — это его решение.

**Миграция существующих нод.** `nftables.service` там уже включён прошлыми версиями. Если
`/etc/nftables.conf` — нетронутый шаблон дистрибутива, снимите юнит с автозагрузки:

```bash
systemctl disable nftables.service # БЕЗ --now и БЕЗ stop: ExecStop = flush ruleset
```

Если конфиг ведётся осознанно — оставьте как есть.

### 🟠 `optimize` на ре-ране не применял обновлённый RPS, но рапортовал успех

`na-rps.service` — `Type=oneshot` с `RemainAfterExit=yes`. Для уже активного такого юнита
`systemctl enable --now` ничем не отличается от `enable`: `ExecStart` повторно не запускается.
Поэтому ре-ран `optimize` переписывал хелпер `na-rps-setup` (в v4.1 — фикс гонки с маршрутом
на буте, #30), а применить его не мог: на трёхъядерной ноде после апгрейда `rps_cpus` так и
оставался `0`, при этом установщик печатал «RPS/RFS/XPS включены».

Теперь после перезаписи хелпера юнит явно перезапускается (`enable` + `restart`), и результат
честный: `ok` только при успешном применении, иначе `warn` с отсылкой к
`journalctl -u na-rps.service`. Перезапуск меняет только настройки очередей NIC — процесс
VPN-ноды не трогается.

### 🧪 Тесты

`tests/rps-unit.sh`: секция активации `optimize` исполняется против модели уже активного
oneshot — маска `rps_cpus` реально выставляется, отказ применения даёт `warn` без `ok`;
регрессия против тега `v4.1.1` (маска пуста при рапорте об успехе). `tests/apply-smoke.sh`:
стаб `systemctl` пишет журнал вызовов — `protect` включает `na-firewall.service` и не трогает
`nftables.service` ни одной операцией.

## v4.1.1 — 2026-09-02

Два хвоста того же аудита, заведённые уже после сборки v4.1
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ curl -fsSL https://raw.githubusercontent.com/jestivald/node-accelerator/main/ins

# прод-режим: пиньте тег через NA_REF — компрометация ветки main тогда не утечёт
# сразу на весь флот (скрипты тянутся из того же тега):
export NA_REF=v4.1.1
export NA_REF=v4.1.2
curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF/install.sh" | sudo -E bash -s all

# максимум: + проверка minisign-подписей модулей (подписи лежат в дереве с v3.6):
export NA_REF=v4.1.1 NA_REQUIRE_SIG=1 \
export NA_REF=v4.1.2 NA_REQUIRE_SIG=1 \
NA_MINISIGN_PUBKEY="RWQrJghT9nkdBC3ntiEXF29zrS8o429WhObHKq6I7CKoftVDhQBrBscu"
curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF/install.sh" | sudo -E bash -s all
```
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# curl-bash:
# curl -fsSL https://raw.githubusercontent.com/jestivald/node-accelerator/main/install.sh | sudo bash -s all
# # прод: пиньте тег (компрометация ветки main тогда не утечёт сразу на весь флот):
# export NA_REF=v4.1.1; curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF/install.sh" | sudo -E bash -s all
# export NA_REF=v4.1.2; curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF/install.sh" | sudo -E bash -s all
# # + подписи модулей: NA_REQUIRE_SIG=1 NA_MINISIGN_PUBKEY=<ключ из README>
#
# ENV: NA_REF=<ветка|тег> NA_REPO_URL=<база raw-URL, для форка/зеркала>
Expand Down
6 changes: 3 additions & 3 deletions scripts/diagnose.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.1.1
untrusted comment: node-accelerator v4.1.2
RUQrJghT9nkdBENoxhnU9rwsJzsLv3J7D9SkM1oe+0YmmPdKLZWtjkaSKCLIQJyFN6bIsc+I6+50gMTg6NHare9Bwo6SMiIB9AY=
trusted comment: timestamp:1788349350 file:diagnose.sh hashed
Kn3mUHqRXJ3TrbY2sTWuRegSUPxmAGUiC58AhBamEvfgFWFxmfcHS6pXKwfG5xCPdSznjgxXGwC5OFtLbCXlCw==
trusted comment: timestamp:1788647488 file:diagnose.sh hashed
vD5w6tc+/j+lAHtgd4marGeKQL+6LIMeYjVB/BA2N+oQm+9s24iSz9md+jdy8U1O6xnh4xMdS9Tyj6QLfCj0Bg==
2 changes: 1 addition & 1 deletion scripts/lib/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Версия тулкита — ЕДИНСТВЕННЫЙ источник. Пишется в installed-маркеры и отдаётся
# в na-diagnose/na-report --json, чтобы флот-мониторинг видел version-drift по нодам.
# shellcheck disable=SC2034
NA_VERSION="4.1.1"
NA_VERSION="4.1.2"

# shellcheck disable=SC2034
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
Expand Down
8 changes: 4 additions & 4 deletions scripts/lib/common.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.1.1
RUQrJghT9nkdBPMjRYxTeyDRPHNs+d5y7lErzQAsoal8gZpryUTA0Ad09waeYLWIWZWthxvOsUaocoZ/qPl1PPhUPoRKACUfPwo=
trusted comment: timestamp:1788349350 file:common.sh hashed
l4F98FXGpy8Umba+MEjtxykaeq298rquHNSNvaJLxaZGqT33SYSK8WlY4IGmT997kPLSBZHWA1KFM74UU8gTBw==
untrusted comment: node-accelerator v4.1.2
RUQrJghT9nkdBL9iAGv1f9+ZiVf16X9beZhz659G4TXQS+g5bw0e8RQHeluighIIV7kWOSE1PkubOQ4CbWBrsX8vNQZfVr/78A4=
trusted comment: timestamp:1788647488 file:common.sh hashed
zcWQlx5JcCMnl4bxHUPCFV60li61IHyh95+obuk9BUH4CsixVLy4HejZ7qcufzajDQUMLLppiIGJBLMW53LyDA==
6 changes: 3 additions & 3 deletions scripts/na-report.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.1.1
untrusted comment: node-accelerator v4.1.2
RUQrJghT9nkdBOrdxdxfyqJUywTv/RLU7ZreVDZAtLeliMCTEsY5M0vWhsR1gF5MEbykzE8/QRKRqFN3Odck90tt1E7RC8oxow8=
trusted comment: timestamp:1788349350 file:na-report.sh hashed
YAktBFahm+IuuKRNinUKv9J3BcJl7TZCMGxC9s4dxl0kOsmWTV09BLbSb11ln/Yt5DmgNZ71xxnwjIXaIk8NCQ==
trusted comment: timestamp:1788647488 file:na-report.sh hashed
Q4FyevSLXC7c7wgxf7XHYHYBS2S/z6fZaL/xNM6vMBLToDX/UtMaCNABbpWWYzGqMvj+oe/EGdja+GRlQYOICQ==
11 changes: 9 additions & 2 deletions scripts/optimize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -619,8 +619,15 @@ RestartSec=5
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now na-rps.service >/dev/null 2>&1 || true
ok "RPS/RFS/XPS включены ($(nproc) ядер, NIC=${NIC:-автодетект на буте})"
# Уже активный oneshot с RemainAfterExit=yes `enable --now` повторно НЕ запускает —
# ре-ран переписывал хелпер, а rps_cpus оставался старым (после гонки с маршрутом на
# буте — нулём), при этом печатался ok. Поэтому: enable + явный restart на каждом прогоне.
systemctl enable na-rps.service >/dev/null 2>&1 || true
if systemctl restart na-rps.service >/dev/null 2>&1; then
ok "RPS/RFS/XPS включены ($(nproc) ядер, NIC=${NIC:-автодетект на буте})"
else
warn "na-rps.service: не удалось применить RPS — проверь journalctl -u na-rps.service"
fi

# ─── 6. NIC tuning ───────────────────────────────────────────────────────────
title "NIC tuning (ring buffer, offloads)"
Expand Down
8 changes: 4 additions & 4 deletions scripts/optimize.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.1.1
RUQrJghT9nkdBFzx1P7p5JoJ79BCF7lkOYOkx8yobz7G1kvaOM7gT7Hk/iUIzrPMfYXe/Mq/BRshJyPUnp3BVc41zXTWz5gSxAE=
trusted comment: timestamp:1788349350 file:optimize.sh hashed
6/gfjc4FoY6moWYtPxteYrF7qI8QudC1R2PNtirQZSs+6JnwE0mPGi48iPOtPUKMJg1VjTK+IBEDEAkgatyeAA==
untrusted comment: node-accelerator v4.1.2
RUQrJghT9nkdBGrIA8t3qUQSL2vbCsTwMaj68kto3cF+M9kaELTIV7p3rC3tHaM2hK3IKdIAp1mrk4z0A3GyasaldaUxstpXzAo=
trusted comment: timestamp:1788647488 file:optimize.sh hashed
Bgtevmgdu7iPsHegOuE+DOg3nmauiXmQwG19F8Dt+/GE09Oa6RLJO1xHq7/MS6zSoeBXKvPfHjdfHwoixf0UCw==
7 changes: 6 additions & 1 deletion scripts/protect.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,12 @@ else
fi
systemctl daemon-reload
systemctl enable na-firewall.service >/dev/null 2>&1 || true
systemctl enable nftables >/dev/null 2>&1 || true
# Дистрибутивный nftables.service НЕ включаем (до v4.1.2 включали «для персиста»):
# его конфиг /etc/nftables.conf тулкит не пишет, а дефолтный шаблон начинается с
# `flush ruleset` и ExecStop у юнита = `nft flush ruleset` — любой stop/restart/reload
# такого «ничейного» юнита снёс бы na_filter, таблицы CrowdSec и Docker. Персист даёт
# только na-firewall.service; состояние nftables.service — решение оператора, не трогаем
# ни на первом прогоне, ни на ре-ране.
ok "na-firewall.service включён (правила переживут reboot — если не сработает сейфти-таймер: он теперь снимает и автозагрузку)"

fi # ═══ конец блока файрвола (FW_MODE=skip его пропускает) ═══
Expand Down
8 changes: 4 additions & 4 deletions scripts/protect.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.1.1
RUQrJghT9nkdBCM9IR9iMEwQS6sDcwVTfzg4UV7E98JvNxD41mLD2QfmRf0TyPypOeHE83iUW2N3726gl0RL3SkQH1Sem0cEKwY=
trusted comment: timestamp:1788349350 file:protect.sh hashed
bF0SKfnBtn/+pe7g2Ti46F2QL1yc3RPBMYaeVV1YWrk3MMXXfiIxlWnAZgzpjRnEfswFkWtoigZfJ3snz+gOAA==
untrusted comment: node-accelerator v4.1.2
RUQrJghT9nkdBJqIVt61VwZTKJhdE0N9X+OAtg5EGgMFJOI+zHuMzLKXLkvnGEc2SC+dinSfa29iiA2zRt3rrVQ9BPFg6iEREg4=
trusted comment: timestamp:1788647488 file:protect.sh hashed
9w+0564+2LwgBKvoPS/n+a7UP4qccb7K/MVtieSjv1TFDw6vjLJbVUuSRRyVDLw8RVUcsvvkeQQLmUyi/pPbAA==
4 changes: 2 additions & 2 deletions scripts/rollback.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ rollback_protect() {
/usr/local/sbin/na-fleet-sync /usr/local/sbin/na-blocklist-update /usr/local/sbin/na-ctguard \
/usr/local/sbin/na-fw-safety-revert
rm -f "$STATE_DIR/safety-fired.last" "$STATE_DIR/protect.lock"
# nftables.service мы включали (boot-persist), но выключать не будем: он лишь грузит
# /etc/nftables.conf, который тулкит никогда не писал — трогать чужой конфиг нельзя.
# nftables.service до v4.1.2 включал сам protect (boot-persist), но выключать не будем: он
# лишь грузит /etc/nftables.conf, который тулкит никогда не писал — трогать чужой конфиг нельзя.
systemctl is-enabled --quiet nftables 2>/dev/null && info "nftables.service оставлен включённым (грузит ваш /etc/nftables.conf, наших правил там нет)"
rm -f /etc/modules-load.d/na-synproxy.conf "$STATE_DIR/.synproxy-degraded"
# конфиги: persisted protect.conf, ctguard.conf, токен панели fleet.env (custom-blocklist.txt — данные оператора, оставляем)
Expand Down
8 changes: 4 additions & 4 deletions scripts/rollback.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.1.1
RUQrJghT9nkdBB27AWybm39DtJ+7UApLPni5ByAeqgET7Ghc3BDFfn5Idz5Cc9n5WK99g5bU/FN1ZvxW8NaK7oLw+MMpBCXNcws=
trusted comment: timestamp:1788349350 file:rollback.sh hashed
LMN68xED+5PySBeLNUgVIFf1y7T8VTMLZ0JpHeUYLs92MACXIF4ZvFWe7PxvCq9RwM9Tmjh7En/Adbb6tdQDCQ==
untrusted comment: node-accelerator v4.1.2
RUQrJghT9nkdBMsr5fhI1fYTi7I7wBpqvnimavufi/5cvOoomiqN/XsfcRtJWW4VVRfH6Kzhw5S/0psSDogosrTIfeISuCnI0AY=
trusted comment: timestamp:1788647488 file:rollback.sh hashed
IE75JmhRbtIM0cM4OsXG8F1LiDLTblJ8S+7/onPT727coriGziz0R7w45E66LdAbvlnO9Hotky+RbjHd4zPOBA==
14 changes: 14 additions & 0 deletions tests/apply-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ sed -e "s#/etc/systemd/system/#$T/sys/#g" \
for c in systemctl modprobe nft systemd-run sysctl conntrack; do
printf '#!/bin/sh\nexit 0\n' > "$T/bin/$c"; chmod +x "$T/bin/$c"
done
# Стаб systemctl пишет журнал вызовов: protect включает СВОЙ na-firewall.service и не
# трогает дистрибутивный nftables.service (его stop/restart/reload = flush ruleset).
export NA_TEST_SYSTEMCTL_LOG="$T/systemctl.log"
cat > "$T/bin/systemctl" <<'SYSTEMCTL'
#!/bin/sh
printf '%s\n' "$*" >> "$NA_TEST_SYSTEMCTL_LOG"
exit 0
SYSTEMCTL
# curl падает → сетевые fetch (crowdsec/blocklist/fleet) деградируют мягко, не висят.
printf '#!/bin/sh\nexit 1\n' > "$T/bin/curl"; chmod +x "$T/bin/curl"
# docker падает → детект node-порта детерминированно идёт по .env/ss-веткам
Expand Down Expand Up @@ -71,6 +79,12 @@ rc=$?
set -e

fail=0
grep -qx 'enable na-firewall.service' "$NA_TEST_SYSTEMCTL_LOG" \
|| { echo "[x] protect не включил свой na-firewall.service"; fail=1; }
if grep -Eq '^(enable|start|restart|reload|stop|disable|mask)( [^ ]+)* nftables(\.service)?( |$)' "$NA_TEST_SYSTEMCTL_LOG"; then
echo "[x] protect тронул чужой nftables.service (риск flush ruleset)"
fail=1
fi
if [ "$rc" -ne 0 ]; then echo "[x] apply упал (exit $rc)"; fail=1; fi
if grep -qiE 'unbound variable|bad substitution' "$LOG"; then echo "[x] найдена unbound-переменная:"; grep -iE 'unbound variable|bad substitution' "$LOG"; fail=1; fi
# ключевые блоки должны были отработать
Expand Down
47 changes: 47 additions & 0 deletions tests/rps-unit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,53 @@ expect "NIC детектится ДО генерации юнита" \
test "$(grep -n 'NIC="\$(default_iface || true)"' "$REPO_ROOT/scripts/optimize.sh" | cut -d: -f1)" \
-lt "$(grep -n 'ExecStart=/usr/local/sbin/na-rps-setup' "$REPO_ROOT/scripts/optimize.sh" | cut -d: -f1)"

# Активный oneshot `enable --now` повторно не исполняет. Гоняем саму секцию активации
# optimize.sh в этом состоянии, а не хелпер напрямую.
echo "== ре-ран при уже активном na-rps.service =="
extract_activation() {
awk '
/^cat > \/etc\/systemd\/system\/na-rps.service <<EOF$/ { unit=1; next }
unit && /^EOF$/ { unit=0; activation=1; next }
activation && /^# .* NIC tuning/ { exit }
activation { print }
' "$1" > "$2"
}
extract_activation "$REPO_ROOT/scripts/optimize.sh" "$T/activation.sh"
cat > "$T/activation-driver.sh" <<'DRIVER'
. "$REPO_ROOT/scripts/lib/common.sh"
systemctl() {
case "$1" in
enable)
# oneshot с RemainAfterExit=yes уже активен: enable (с --now или без)
# ExecStart повторно не запускает.
return 0;;
restart)
[ "${RPS_FORCE_FAILURE:-0}" = 1 ] && return 1
"$WBASH" "$RPS" "$NIC";;
*) return 0;;
esac
}
. "$ACTIVATION"
DRIVER
run_activation() {
REPO_ROOT="$REPO_ROOT" WBASH="$WBASH" RPS="$RPS" NIC=ens18 \
ACTIVATION="$1" "$WBASH" "$T/activation-driver.sh" > "$T/activation.out" 2>&1
}
reset_net; mk_net ens18 1
run_activation "$T/activation.sh"
expect "ре-ран применяет RPS даже при активном старом oneshot" test "$(mask_of ens18)" = 7
reset_net; mk_net ens18 1
RPS_FORCE_FAILURE=1 run_activation "$T/activation.sh"
expect "отказ применения — warn" grep -q 'не удалось применить RPS' "$T/activation.out"
expect_not "при отказе нет рапорта об успехе" grep -q 'RPS/RFS/XPS включены' "$T/activation.out"
if git -C "$REPO_ROOT" show v4.1.1:scripts/optimize.sh > "$T/v411-optimize.sh" 2>/dev/null; then
extract_activation "$T/v411-optimize.sh" "$T/v411-activation.sh"
reset_net; mk_net ens18 1
run_activation "$T/v411-activation.sh"
expect "регрессия v4.1.1: активный oneshot не применён (маска пуста)" test -z "$(mask_of ens18)"
expect "регрессия v4.1.1: при этом рапортовал успех" grep -q 'RPS/RFS/XPS включены' "$T/activation.out"
fi

# ── #28: ничего «курсорного» в не-терминал ──────────────────────────────────────
echo "== курсорный вывод только в терминал (#28) =="
expect_not "голых 'tput ' в коде optimize.sh не осталось" bare_tput "$REPO_ROOT/scripts/optimize.sh"
Expand Down
Loading