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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ jobs:
# v4-mapped пиры (`[::ffff:…]` от ss) находятся в live-lookup: обе ошибки
# молча ломали ctguard и рвали пользовательские сессии (issue #22).
run: bash tests/ctguard-unit.sh
- name: logrotate unit (ИСПОЛНЯЕТ секцию ротации optimize.sh против стабов)
# Ре-ран не падает на backup_file без 2-го аргумента (#24), конфликт масок
# решает вердикт самого logrotate, а не сравнение строк (#25), и глобы
# NA_LOG_PATHS не раскрываются шеллом в явные пути.
run: bash tests/logrotate-unit.sh

# ── Smoke-тесты на матрице дистрибутивов ────────────────────────────────────
# Ловит ровно тот класс регрессий, из-за которых появился этот PR: установка
Expand Down
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
# Changelog

## v4.0.1 — 2026-08-31

Два бага секции ротации логов, пойманные в день релиза при раскатке v4.0 на флот
([#24](https://github.com/jestivald/node-accelerator/issues/24),
[#25](https://github.com/jestivald/node-accelerator/issues/25)): оба жили в
конфигурациях, которых не было в CI — повторный прогон и чужие стансы logrotate.
Плюс латентный третий, который вскрыл новый тест.

### 🔴 Ре-ран optimize падал на уже настроенной ноде (#24)

`backup_file` звался с одним аргументом вместо двух. На первом прогоне стансы ещё нет,
`[[ -f ]]` коротит цепочку — баг невидим; на повторном файл существует, и голый `$2` под
`set -u` ронял модуль посреди секции ротации (всё после неё не выполнялось). То есть
падал ровно тот прогон, которым конфигурацию чинят: сузить `NA_LOG_PATHS` ре-раном было
нельзя. Аргумент передаётся, а сам хелпер теперь требует каталог назначения через
`${2:?…}` — будущий одноаргументный вызов упадёт сразу и с внятным текстом на любом
прогоне, а не «unbound variable» из глубины на второй день жизни ноды.

### 🔴 Конфликт масок с чужими стансами не ловился (#25)

Дубль-детект сравнивал СТРОКИ масок (`grep -F` по `/etc/logrotate.d/`), а logrotate
дедуплицирует по РАСКРЫТЫМ файлам. Чужая станса с явными путями, пакетная станса nginx
или пересекающийся глоб проходили мимо текстового сравнения; logrotate отвечал
`duplicate log entry`, целиком пропускал ту стансу, которой файл достался вторым, и
выходил с ошибкой — `na-logrotate.service` уходил в failed при внешне здоровом таймере.
Теперь арбитр — сам logrotate: станса пишется целиком, прогоняется `logrotate -d` по
всему набору, и конфликтные маски отдаются чужим стансам с предупреждением (у чужой
стансы может быть сигнальный reload вместо `copytruncate` — ей и владеть). Дубликаты,
не задевающие наши маски, — конфликт между чужими стансами: о нём предупреждаем, но
чинить его не нам.

### 🟠 Глобы из `NA_LOG_PATHS` раскрывались шеллом в явные пути

Смежный люк, вскрытый новым тестом: голый word-split `$NA_LOG_PATHS` заодно раскрывал
глобы по живым файлам, и на ноде с существующими логами станса записывалась с ЯВНЫМИ
путями вместо масок — лог нового vhost'а никогда не начал бы ротироваться. Теперь маски
идут в стансу как заданы (`read -a`).

### 🧪 Тест

`tests/logrotate-unit.sh` извлекает секцию ротации из `optimize.sh` и ИСПОЛНЯЕТ её в
песочнице против стабов `logrotate`/`systemctl`: первый прогон, ре-ран, конфликт с
чужой стансой, чужой дубликат вне наших масок, полная уступка всех масок. На коде v4.0
он красный по всем трём пунктам. Добавлен в CI.

## v4.0 — 2026-08-31

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

# прод-режим: пиньте тег через NA_REF — компрометация ветки main тогда не утечёт
# сразу на весь флот (скрипты тянутся из того же тега):
export NA_REF=v4.0
export NA_REF=v4.0.1
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.0 NA_REQUIRE_SIG=1 \
export NA_REF=v4.0.1 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.0; curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF/install.sh" | sudo -E bash -s all
# export NA_REF=v4.0.1; 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.0
untrusted comment: node-accelerator v4.0.1
RUQrJghT9nkdBLJfnqldUYYax1SmBwgDdXRiDFhrsI/FzxS/JgJj3fpgeZhq1UgmtIIIawExiiZNCPN/sHzr5+je/5GdATHZGAU=
trusted comment: timestamp:1788161062 file:diagnose.sh hashed
Wm6jzaGDtAQE6f98W4X14ojFAwa0e16h5bvv9MHOo2p1Zpr6OedPG6B1ROdHbqxT5Hpq+aRa+USbDAIpCaOADg==
trusted comment: timestamp:1788195409 file:diagnose.sh hashed
p5ofh+JC9BbY/T1Ctzr1u4sfyJIXbK1tU+tjD1sc62MuFzHn8K6hAn3Qp+YXnTr3gpnfhXTIH+M6P472fVN9Ag==
11 changes: 9 additions & 2 deletions 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.0.0"
NA_VERSION="4.0.1"

# shellcheck disable=SC2034
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
Expand Down Expand Up @@ -108,7 +108,14 @@ backup_dir() {
mkdir -p "$d"
echo "$d"
}
backup_file() { [[ -f "$1" ]] && cp -a "$1" "$2/"; return 0; }
# ${2:?}: одноаргументный вызов обязан падать сразу и с внятным текстом — а не
# «$2: unbound variable» из глубины, и только на ре-ране, когда файл впервые
# существует и [[ -f ]] перестаёт коротить цепочку (issue #24).
backup_file() {
local dst="${2:?backup_file: нужен каталог назначения}"
[[ -f "$1" ]] && cp -a "$1" "$dst/"
return 0
}

apt_install() {
export DEBIAN_FRONTEND=noninteractive
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.0
RUQrJghT9nkdBKP1CAbKOjEUxpArA9TpYLGsjA4KF/awmetbs0LAG7xbesa7r7D4HEBrXhVjzkUfOXmUEsGfC9PdKlB22cmRBQE=
trusted comment: timestamp:1788161062 file:common.sh hashed
sDkHwJrZcY82mNlucEyEP2J2tQU8mGmwW8gW6c44YlQbc8jV7Btu7RcNwHrLur0DySY8KEpSVX0xRudKfCYADQ==
untrusted comment: node-accelerator v4.0.1
RUQrJghT9nkdBKwOKY4N7laoNFGudKyYKokpkqAVMO8ZL4A4qfqQhZaOGhlEmr2CoQzYBKkDBi1zCCr7T7s8AqmpXRfAmxzUVA4=
trusted comment: timestamp:1788195409 file:common.sh hashed
beUfhY5bkXI9hMQ1bSyzrwTsN0DcXEW9QoB904FiGp9eGTHqx9uGmdTCGOXuRszMuwG4ihcCtzPUPpchydRNDg==
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.0
untrusted comment: node-accelerator v4.0.1
RUQrJghT9nkdBNmjwr6wpd5z//F4lgTsIa6dWD/8N/aZY869CdDgKV+Sz4Y8fqWUgyQVQCj30hO7g1POm4jwkgYMS0cODVv5pQY=
trusted comment: timestamp:1788161062 file:na-report.sh hashed
5DcMG4/Kr7XfH68anHa4vn2ETtyqqYbRoYxbYWqjGKCf63kD1HvyPc25lb1Zg4tO7DXwNCtxddLnhUguM4aUBw==
trusted comment: timestamp:1788195409 file:na-report.sh hashed
MZDoiNSUoUk2WboLttANFBlRp8DTtrGN+nroi6Bgo39ZrsYD3cjX7h/OwWsV/w/CPg7VLJ0RNmxkrU7acItzAw==
74 changes: 51 additions & 23 deletions scripts/optimize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -705,20 +705,24 @@ else
apt_install logrotate || warn "logrotate не доустановился — ротация работать не будет"
fi

# Маски, которые уже держит ЧУЖАЯ станса, забирать себе нельзя: logrotate на дубликат
# пути отвечает «duplicate log entry» и пропускает НАШ файл целиком. Коварно то, что
# проверка одного файла (`logrotate -d <наш>`) при этом рапортует, что всё в порядке.
# Маски, уже покрытые ЧУЖОЙ стансой, забирать себе нельзя: logrotate на дубликат пути
# отвечает «duplicate log entry», целиком пропускает ту стансу, которой файл достался
# вторым, и выходит с ошибкой — na-logrotate.service уходит в failed при внешне
# здоровом таймере. Сверять маски СТРОКАМИ бесполезно: logrotate дедуплицирует по
# РАСКРЫТЫМ файлам, и чужая станса с явными путями или другим глобом проходит мимо
# текстового сравнения. Единственный честный арбитр — сам logrotate (`-d` по ВСЕМУ
# набору): пишем стансу целиком, спрашиваем его и конфликтные маски отдаём чужим
# стансам — у них может быть сигнальный reload вместо copytruncate, им и владеть.
# read -a, а не голый word-split: иначе шелл сам раскроет глобы по живым файлам,
# и в стансу лягут ЯВНЫЕ пути — лог нового vhost'а никогда не начнёт ротироваться.
_want=(); _dup=()
for _m in $NA_LOG_PATHS; do
if grep -rqsF -- "$_m" /etc/logrotate.d/ --exclude="$(basename "$LR_CONF")" 2>/dev/null; then
_dup+=("$_m")
else
_want+=("$_m")
fi
done
read -r -a _want <<<"$NA_LOG_PATHS"

if [[ "${#_want[@]}" -gt 0 ]]; then
backup_file "$LR_CONF"
_na_lr_write() {
if [[ "${#_want[@]}" -eq 0 ]]; then
rm -f "$LR_CONF"
return 0
fi
{
printf '%s ' "${_want[@]}"
cat <<LRC
Expand All @@ -736,15 +740,46 @@ else
LRC
} > "$LR_CONF"
chmod 0644 "$LR_CONF"
}

# copytruncate, а не reopen-сигнал: nginx и ядро ноды живут в контейнерах, послать им
# USR1 из хостовой ротации некому.
backup_file "$LR_CONF" "$BACKUP"
_na_lr_write

if command -v logrotate >/dev/null 2>&1; then
while [[ "${#_want[@]}" -gt 0 ]]; do
_dupf="$({ logrotate -d /etc/logrotate.conf 2>&1 || true; } \
| sed -n 's/.*duplicate log entry for //p' | sort -u)"
[[ -n "$_dupf" ]] || break
_keep=()
for _m in "${_want[@]}"; do
_hit=0
while IFS= read -r _f; do
[[ -n "$_f" ]] || continue
# shellcheck disable=SC2053 # маска без кавычек — намеренный glob-матч
if [[ "$_f" == $_m ]]; then _hit=1; break; fi
done <<<"$_dupf"
if [[ "$_hit" -eq 1 ]]; then _dup+=("$_m"); else _keep+=("$_m"); fi
done
if [[ "${#_keep[@]}" -eq "${#_want[@]}" ]]; then
# наших масок дубликаты не задевают — это конфликт между чужими
# стансами, чинить его не нам
warn "дубликаты путей вне наших масок — часть чужих станс пропускается: logrotate -d /etc/logrotate.conf"
break
fi
_want=("${_keep[@]}")
_na_lr_write
done
fi

if [[ "${#_want[@]}" -gt 0 ]]; then
ok "стансa $LR_CONF: ${_want[*]} (maxsize $NA_LOG_MAXSIZE, rotate $NA_LOG_ROTATE)"
else
rm -f "$LR_CONF"
info "все заданные пути уже покрыты другими стансами — свою не создаю"
fi
# copytruncate, а не reopen-сигнал: nginx и ядро ноды живут в контейнерах, послать им
# USR1 из хостовой ротации некому.
for _m in "${_dup[@]}"; do
warn "путь $_m уже покрыт чужой стансой в /etc/logrotate.d — проверь, что там задан maxsize"
warn "маска $_m уже покрыта чужой стансой в /etc/logrotate.d — отдана ей; проверь, что там задан maxsize"
done

cat > /etc/systemd/system/na-logrotate.service <<'EOF'
Expand Down Expand Up @@ -772,13 +807,6 @@ EOF
systemctl enable --now na-logrotate.timer >/dev/null 2>&1 \
&& ok "na-logrotate.timer: прогон $NA_LOG_INTERVAL" \
|| warn "na-logrotate.timer не включился"

# Валидировать можно только весь набор: конфликт масок виден лишь в общем прогоне.
if command -v logrotate >/dev/null 2>&1; then
if logrotate -d /etc/logrotate.conf 2>&1 | grep -qi 'duplicate log entry'; then
warn "logrotate сообщает о дубликате путей — часть станс будет пропущена: logrotate -d /etc/logrotate.conf"
fi
fi
fi

# ─── 9. THP off ──────────────────────────────────────────────────────────────
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.0
RUQrJghT9nkdBEEPH+fmt7Ut3CWAoeVlx1XNLe6962P9kFFpWl+qMPHydIb7tEFqZ/N+hsEe1TlPy4d5dkV1GnZXvNsU2IC6Hg8=
trusted comment: timestamp:1788161062 file:optimize.sh hashed
1wdr1EAlc/58tLmaYyRXY95DWdsGt7U0HrzcZJjQz521WpISU1C/QpDOPNGRE/MXr5URKFGHZvX/c305WhoeDg==
untrusted comment: node-accelerator v4.0.1
RUQrJghT9nkdBAWiEgKBFnAYZ9EfYbT1tC/09FL5a79BZ5+u6e7FmsylSJ/kVOIAzHWpdCHnsxO9thv+pZ+8VftRqcv7FzzCBwI=
trusted comment: timestamp:1788195409 file:optimize.sh hashed
kGEu8xrDSP4JIp61fjLkWuzvPaPk8xnHdkWHDCCuiLSZeFjiQ8qKgZKn17/jlk6Z1ICJMykSKw0IBX+T5tjZCA==
6 changes: 3 additions & 3 deletions scripts/protect.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.0
untrusted comment: node-accelerator v4.0.1
RUQrJghT9nkdBJW7QbjT9v5Dk6an8bokwh6Gr4iIZoMQBo1F5KKnSg73UhWoWBCLp/iabhet9WHP/SiR00YJ9QBWdnmXTuhkDAw=
trusted comment: timestamp:1788161062 file:protect.sh hashed
m2ADFdtUHbq+3VBiUc9BCVBiPjsWpdb7oayReUR74X5eXjVsbPpdPQY/bv4I9E97jyYhZ+mLixFMAjl1gDfNCQ==
trusted comment: timestamp:1788195409 file:protect.sh hashed
LZVWgqk1U6+p2WHdRhrxog/LmwDJfoQTcGN1bZZZRV48oK44J/i7isxqsNVbvzkmPBw2ppufPDbJH+gb0FSMBw==
6 changes: 3 additions & 3 deletions scripts/rollback.sh.minisig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
untrusted comment: node-accelerator v4.0
untrusted comment: node-accelerator v4.0.1
RUQrJghT9nkdBHtUwNrCoDinG8eRDo4TrqXAq0Hwzrh4mZGVTElcs2zYKR/xOS808sfl3QbCPUy1zVF++n61QNMXVXUvMX3J6AU=
trusted comment: timestamp:1788161062 file:rollback.sh hashed
aS1HWHEAT7C/J/CEt7uDzOgcUbXYqmhmVDn9SToYoaSFyu29WwzG8JZjKkts162HOsMleWF7hZ7lBihQsUHlCw==
trusted comment: timestamp:1788195409 file:rollback.sh hashed
nDy52eEyypysmejwrXtouFE11L2zaZwcS2IC6E7CWV7QAhcFyWofGnhB6AZcGScjHojnESJMG2WObTAxmH7KAw==
Loading
Loading