From 81b527260f29416ed3af4c1dc957be3baedf3fc5 Mon Sep 17 00:00:00 2001 From: jestivald Date: Wed, 2 Sep 2026 14:42:30 +0300 Subject: [PATCH 1/2] =?UTF-8?q?v4.1.1:=20=D1=83=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BC=D0=B0=D1=81=D0=BE=D0=BA=20logrotate=20?= =?UTF-8?q?=D0=B2=D0=B8=D0=B4=D0=BD=D0=B0=20(=D0=B2=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D1=86/maxsize/=D1=81=D0=BE=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=8F=D0=BD=D0=B8=D0=B5,=20diagnose=20=E2=96=B2)=20(#40);=20na?= =?UTF-8?q?-report=20=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D1=82=20=D0=B2=D1=81?= =?UTF-8?q?=D0=B5=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20(-b?= =?UTF-8?q?=20all)=20+=20=D0=B3=D0=BB=D1=83=D0=B1=D0=B8=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B6=D1=83=D1=80=D0=BD=D0=B0=D0=BB=D0=B0=20(#41);=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D1=8B:=20=D1=80=D0=B5=D0=B3=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D1=81=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D1=82=D0=B8=D0=B2?= =?UTF-8?q?=20=D1=82=D0=B5=D0=B3=D0=B0=20v4.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 11 ++++++ CHANGELOG.md | 44 ++++++++++++++++++++++++ README.md | 11 +++--- install.sh | 2 +- scripts/diagnose.sh | 29 ++++++++++++++-- scripts/lib/common.sh | 2 +- scripts/na-report.sh | 25 ++++++++++++-- scripts/optimize.sh | 74 ++++++++++++++++++++++++++++++++++++---- tests/diagnose-unit.sh | 33 ++++++++++++++++++ tests/logrotate-unit.sh | 37 ++++++++++++++++++-- tests/psi-unit.sh | 6 ++-- tests/rps-unit.sh | 4 +-- 12 files changed, 253 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b45554..146c356 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,17 @@ jobs: # whitelist относительно protect.conf (#38); PSI три состояния (#37); серты в # /opt/*/certs и снятые с renew (#39); глубина журнала (#35); RPS-юнит vs факт (#30). run: bash tests/diagnose-unit.sh + - name: na-report читает журнал всех загрузок (-b all), не только текущей (#41) + # `-k` подразумевает `-b`: после ребута отчёт «за 24ч» молча становился «с момента + # загрузки» (×31 занижение на ноде флота). Оба вызова journalctl -k — с -b all. + run: | + N="$(grep -cE 'journalctl -k -b all --since' scripts/na-report.sh)" + [ "$N" -ge 2 ] || { echo "в na-report.sh journalctl -k без -b all (найдено $N из 2)"; exit 1; } + if grep -nE 'journalctl -k( |$)' scripts/na-report.sh | grep -vq -- '-b all'; then + echo "остался journalctl -k без -b all:"; grep -nE 'journalctl -k( |$)' scripts/na-report.sh | grep -v -- '-b all'; exit 1 + fi + bash scripts/na-report.sh --json | grep -q '"journal_span_h":' || { echo "na-report --json без journal_span_h"; exit 1; } + echo "ok: -b all в обоих вызовах, journal_span_h в json" # ── Smoke-тесты на матрице дистрибутивов ──────────────────────────────────── # Ловит ровно тот класс регрессий, из-за которых появился этот PR: установка diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b71ba4..e16108f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # Changelog +## v4.1.1 — 2026-09-02 + +Два хвоста того же аудита, заведённые уже после сборки v4.1 +([#40](https://github.com/jestivald/node-accelerator/issues/40), +[#41](https://github.com/jestivald/node-accelerator/issues/41)). Оба — про молчаливую +подмену смысла: «ротация настроена» при отсутствующей стансе и «за 24 часа» при отчёте +с момента загрузки. + +### 🟠 Уступив маски чужой стансе, тулкит рапортовал зелёным (#40) + +Само поведение «отдать путь чужой стансе» правильное — спор решает `logrotate`, и в v4.0.1 он +решается честно. Дефект был в том, что уступка не доводилась до оператора там, где он смотрит: +`optimize` печатал «проверь, что там задан maxsize» и заканчивался `ok`, `na-logrotate.timer` +включался, `na-diagnose` светил `✔ ротация логов: часовой таймер активен` — при отсутствующей +`na-node-logs`. На трёх нодах флота ротацию при этом держала ручная станса `weekly` без +`maxsize`, то есть никакого капа не было, а диагностика была полностью зелёной. + +Теперь `optimize` **сам находит стансу-владельца** (по реальным путям, которые `logrotate` назвал +дубликатами) и **сам смотрит в неё**: есть `maxsize`/`size` — info «кап работает», нет — +`warn` с именем файла и периодом («`weekly` БЕЗ maxsize/size — размер ничем не ограничен») и +советом. Полная уступка всех масок — `warn`, а не info, и повторяется в итоге прогона. Факт +уступки пишется в состояние (`logrotate.ceded`: маска, владелец, кап; `logrotate.owned`: что +держит наша станса) — `na-diagnose` разделяет «таймер активен» и «станса существует»: ▲ при +уступке (с владельцами и пометкой у кого нет капа) и ▲ при активном таймере без стансы; в +`--json` — `logrotate_owned_masks`, `logrotate_ceded_masks`, `logrotate_ceded_nocap`. +`ENABLE_LOGROTATE=0` снимает состояние, чтобы диагностика не пересказывала прошлые прогоны. + +### 🟠 `na-report` после ребута занижал события в разы (#41) + +`journalctl -k` подразумевает `-b` — только текущая загрузка, а окно задаётся через `--since`. +После перезагрузки отчёт «за 24 часа» молча становился отчётом «с момента загрузки»: на ноде +флота 2 298 событий против 72 485 реальных (×31), причём ровно в те сутки, когда в отчёт +смотрят чаще всего. Оба вызова (`collect_events` и `--port`) теперь с `-b all` — граница по +времени остаётся, границы загрузок перестают резать окно. В шапке — фактическая глубина журнала +(самая старая запись): если она меньше окна, отчёт предупреждает, что «событий мало» может значить +«журнал вытеснен»; в `--json` — `journal_span_h`. + +### 🧪 Тесты + +`tests/logrotate-unit.sh`: владелец уступленной маски назван и проверен на `maxsize` (кейсы +«без капа» / «с капом»), состояние `ceded`/`owned` пишется и снимается, полная уступка — warn. +`tests/diagnose-unit.sh`: ▲ и поля `--json` при уступке. CI: статическая проверка, что оба +`journalctl -k` в `na-report` — с `-b all`, и `journal_span_h` в его `--json`. + ## v4.1 — 2026-09-02 Релиз по итогам аудита боевого флота (11 нод + панель на v4.0.1, Debian 12/13, XanMod 6.18): diff --git a/README.md b/README.md index cea9d87..639722f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,10 @@ сертификат). Штатной ротации мало: `maxsize` проверяется только в момент запуска logrotate, а тот суточный, поэтому файл проскакивает лимит между прогонами — наблюдалось превышение заявленного капа в девять раз. Ставится сам пакет `logrotate` (на минимальных образах его нет, и станса лежит - мёртвым грузом), станса на пути ноды и `na-logrotate.timer` с часовым прогоном. + мёртвым грузом), станса на пути ноды и `na-logrotate.timer` с часовым прогоном. Маску, которую уже + держит чужая станса, тулкит отдаёт ей (спор решает сам `logrotate`), **сам проверяет в ней + `maxsize`/`size`** и пишет факт уступки в состояние — `na-diagnose` показывает, кто ротирует и есть ли + кап, вместо зелёного «таймер активен». ### 🛡 Защита (`scripts/protect.sh`) `nftables`-движок в **своей** таблице `inet na_filter` (не `flush ruleset` — сосуществует с CrowdSec и Docker): @@ -86,7 +89,7 @@ Read-only отчёт: ядро/BBR, sysctl, лимиты, conntrack, NIC/RPS, swap/THP/governor, firewall, CrowdSec, порты, RTT — с итогом ✔/▲/✘ и рекомендациями. Плюс сенсоры **стека ноды**: контейнер `remnanode` (статус/рестарты/`SPAWN_ERROR` за час — ловит коллизию node-address в панели), **рассинхрон порта node-агента с файрволом** (агент слушает `:3000`, а strict-правила держат `:2222` → «нода недоступна» для панели; в отчёте — с командой пожарного фикса, в `--json` — `node_port_detected`/`node_port_fw`), **сроки TLS-сертификатов** (LE/acme.sh/`/opt/*/certs`; серты, снятые с renew в acme.sh, не считаются; свои пути — `NA_CERT_PATHS="глоб1 глоб2"`), **whitelist в трёх местах** (живой сет vs `na_filter.nft` vs `protect.conf`: адрес, который переживёт ребут, но не ре-ран `protect`, называется по имени), **датчик `CONN_LIMIT` по тому же срезу, что и правило** (только входящие на сервисные порты, без loopback/whitelist — раньше на ноде с внутренним nginx он горел всегда), **PSI** (различает «не собран», «выключен в сборке ядра» и «работает»), **ретеншен journald в часах**, **замороженные дефолты в сохранённом конфиге**, **свежесть fleet-sync/blocklist**, IPv6 default-route, UDP `RcvbufErrors` (QUIC/Hysteria2). После установки доступна как команда **`na-diagnose`** (`--json` для мониторинга/панели — теперь с `na_version`, `hostname`, `uptime_s`, `load1`, `mem_used_pct`, WAN rx/tx-байтами; `--retrans [--window N]` — разбор причин TCP-retransmits). ### 🔥 Форензика атак (`scripts/na-report.sh`) -Read-only: кто/откуда/чем/когда — из журнала ядра, nft-сетов и CrowdSec. **`na-report`** (человекочитаемо) или **`na-report --json`**: `drops_by_reason`, `timeline`, `top_ips` с вердиктом, `top_asn` (ASN/гео — best-effort через Team Cymru whois). Флаги: `--hours N`, `--top N`, `--ip `. +Read-only: кто/откуда/чем/когда — из журнала ядра, nft-сетов и CrowdSec. **`na-report`** (человекочитаемо) или **`na-report --json`**: `drops_by_reason`, `timeline`, `top_ips` с вердиктом, `top_asn` (ASN/гео — best-effort через Team Cymru whois). Флаги: `--hours N`, `--top N`, `--ip `. Журнал читается по всем загрузкам (`-b all`), а не с момента ребута; в шапке — фактическая глубина журнала (`journal_span_h`), чтобы «событий мало» не путать с «журнал вытеснен». --- @@ -118,11 +121,11 @@ curl -fsSL https://raw.githubusercontent.com/jestivald/node-accelerator/main/ins # прод-режим: пиньте тег через NA_REF — компрометация ветки main тогда не утечёт # сразу на весь флот (скрипты тянутся из того же тега): -export NA_REF=v4.1 +export NA_REF=v4.1.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.1 NA_REQUIRE_SIG=1 \ +export NA_REF=v4.1.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 ``` diff --git a/install.sh b/install.sh index db98c71..c41f605 100755 --- a/install.sh +++ b/install.sh @@ -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; curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF/install.sh" | sudo -E bash -s all +# export NA_REF=v4.1.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, для форка/зеркала> diff --git a/scripts/diagnose.sh b/scripts/diagnose.sh index a2c73d6..6ac66a4 100755 --- a/scripts/diagnose.sh +++ b/scripts/diagnose.sh @@ -345,6 +345,12 @@ emit_json() { dklogmax="$(find /var/lib/docker/containers -xdev -type f -name '*-json.log' -printf '%s\n' 2>/dev/null | sort -rn | head -1)" [[ "$dklogmax" =~ ^[0-9]+$ ]] || dklogmax=0 lrt=0; systemctl is-active --quiet na-logrotate.timer 2>/dev/null && lrt=1 + # Таймер ≠ станса: маски, отданные чужим стансам, тулкит не ротирует, а таймер при + # этом активен (issue #40). owned/ceded пишет optimize; пусто = станс/уступок нет. + lro="$(paste -sd' ' "$STATE_DIR/logrotate.owned" 2>/dev/null || true)"; lro="${lro:-}" + lrc="$(cut -f1 "$STATE_DIR/logrotate.ceded" 2>/dev/null | paste -sd' ' - || true)"; lrc="${lrc:-}" + lrcn="$(awk -F'\t' '$3=="none"' "$STATE_DIR/logrotate.ceded" 2>/dev/null | grep -c .)"; [[ "$lrcn" =~ ^[0-9]+$ ]] || lrcn=0 + lro="$(json_escape "$lro")"; lrc="$(json_escape "$lrc")" # Каждое строковое значение — через json_escape. Раньше каждое поле полагалось на то, # что источник «и так чистый», и одного сырого перевода строки из docker хватило, @@ -375,8 +381,10 @@ emit_json() { # мониторинг. −1 везде = «не измерено», а не «ноль». printf '"max_conn_per_ip":%s,"journal_span_h":%s,"portscan_log_lines_boot":%s,"psi":"%s",' \ "$mcpi" "$jsp" "$psl" "$psist" - printf '"whitelist_drift_conf":%s,"conf_stale_defaults":%s,"cert_min_file":"%s"}\n' \ + printf '"whitelist_drift_conf":%s,"conf_stale_defaults":%s,"cert_min_file":"%s",' \ "$wdc" "$csd" "$certf" + printf '"logrotate_owned_masks":"%s","logrotate_ceded_masks":"%s","logrotate_ceded_nocap":%s}\n' \ + "$lro" "$lrc" "$lrcn" } if [[ "${1:-}" == "--json" ]]; then emit_json; exit 0; fi @@ -1062,7 +1070,24 @@ DKBIG="$(find /var/lib/docker/containers -xdev -type f -name '*-json.log' -size if ! command -v logrotate >/dev/null 2>&1; then wrn "logrotate не установлен — стансы в /etc/logrotate.d не выполняются вообще" elif systemctl is-active --quiet na-logrotate.timer 2>/dev/null; then - pass "ротация логов: часовой таймер активен" + # «Таймер активен» и «станса существует» — разные факты (issue #40): уступив все маски + # чужим стансам, optimize свою не создаёт, а таймер включает — и на трёх нодах флота + # диагностика светила ✔ при ротации, которую держала ручная станса weekly без maxsize. + LR_EN="$(sed -nE 's/.*\{ENABLE_LOGROTATE:=([^}]*)\}.*/\1/p' "$CONF_DIR/optimize.conf" 2>/dev/null | tail -1)" + if [[ -s "$STATE_DIR/logrotate.ceded" ]]; then + LR_CEDED="$(awk -F'\t' '{printf "%s%s → %s%s", (NR>1?", ":""), $1, $2, ($3=="none"?" (БЕЗ maxsize/size!)":($3=="unknown"?" (кап неизвестен)":""))}' "$STATE_DIR/logrotate.ceded" 2>/dev/null)" + LR_NOCAP="$(awk -F'\t' '$3=="none"' "$STATE_DIR/logrotate.ceded" 2>/dev/null | grep -c .)" + if [[ "${LR_NOCAP:-0}" -gt 0 ]]; then + wrn "ротация: таймер активен, но маски отданы чужим стансам, и у $LR_NOCAP из них НЕТ капа по размеру: $LR_CEDED — добавь maxsize в чужую стансу или сузь NA_LOG_PATHS (ре-ран optimize)" + else + wrn "ротация: таймер активен, но часть масок ротируют чужие стансы (у них кап есть): $LR_CEDED" + fi + [[ -s "$STATE_DIR/logrotate.owned" ]] && info "наша станса держит: $(paste -sd' ' "$STATE_DIR/logrotate.owned" 2>/dev/null)" + elif [[ "${LR_EN:-1}" == "1" && ! -s /etc/logrotate.d/na-node-logs ]]; then + wrn "ротация: таймер активен, а стансы /etc/logrotate.d/na-node-logs нет — тулкит ничего не ротирует (ре-ран optimize)" + else + pass "ротация логов: часовой таймер активен, станса на месте$( [[ -s "$STATE_DIR/logrotate.owned" ]] && echo " ($(paste -sd' ' "$STATE_DIR/logrotate.owned" 2>/dev/null))")" + fi else info "часовой таймер ротации не активен — работает только суточный logrotate.timer (maxsize проверяется раз в сутки)" fi diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 1f4708e..1221a01 100755 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -6,7 +6,7 @@ # Версия тулкита — ЕДИНСТВЕННЫЙ источник. Пишется в installed-маркеры и отдаётся # в na-diagnose/na-report --json, чтобы флот-мониторинг видел version-drift по нодам. # shellcheck disable=SC2034 -NA_VERSION="4.1" +NA_VERSION="4.1.1" # shellcheck disable=SC2034 RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' diff --git a/scripts/na-report.sh b/scripts/na-report.sh index fc47969..2518f39 100755 --- a/scripts/na-report.sh +++ b/scripts/na-report.sh @@ -49,14 +49,28 @@ done [[ -z "$FOCUS_PORT" || "$FOCUS_PORT" =~ ^[0-9]+$ ]] || { err "--port должен быть числом"; exit 1; } NOW="$(date +%s)" +# Глубина журнала в часах (возраст самой старой записи; -1 = не измерить). Без неё +# оператор не отличает «атак не было» от «журнал уже вытеснен» — на публичной ноде +# лог анти-скана съедал 300M меньше чем за сутки (issue #35/#41). +journal_span_h() { + local first + command -v journalctl >/dev/null 2>&1 || { echo -1; return 0; } + first="$(journalctl -q --no-pager -o short-unix 2>/dev/null | head -n1 | awk '{print $1}' | cut -d. -f1)" + [[ "$first" =~ ^[0-9]+$ && "$NOW" -gt "$first" ]] || { echo -1; return 0; } + echo $(( (NOW - first) / 3600 )) +} +JSPAN="$(journal_span_h)" TMP="$(mktemp "${TMPDIR:-/tmp}/na-report.XXXXXX")" trap 'rm -f "$TMP" "$TMP".ab' EXIT # ─── 1. Парс журнала: nft log "[na ]" → TSV epoch \t reason \t src ────── # Только loggable-причины несут SRC= (autoban/blocklist дропают без лога — для них # есть счётчики/сеты). Окно --hours. +# `-b all` обязателен: `-k` подразумевает `-b` (ТОЛЬКО текущая загрузка), и после ребута +# отчёт «за 24ч» молча становился отчётом «с момента загрузки» — на ноде флота 2 298 +# событий против 72 485 реальных, ×31 (issue #41). Граница по времени (--since) остаётся. collect_events() { - journalctl -k --since "-${HOURS}h" --no-pager -o short-unix 2>/dev/null \ + journalctl -k -b all --since "-${HOURS}h" --no-pager -o short-unix 2>/dev/null \ | grep -aF '[na ' \ | sed -nE 's/^([0-9]+)\.[0-9]+ .*\[na ([a-z-]+)\].*SRC=([0-9a-fA-F.:]+).*/\1\t\2\t\3/p' \ > "$TMP" || true @@ -209,7 +223,7 @@ emit_json() { )" printf '{' - printf '"na_version":"%s","window_hours":%s,"generated_at":%s,"events_total":%s,"ban_rate_5m":%s,' "${NA_VERSION:-?}" "$HOURS" "$NOW" "$total" "$rate" + printf '"na_version":"%s","window_hours":%s,"journal_span_h":%s,"generated_at":%s,"events_total":%s,"ban_rate_5m":%s,' "${NA_VERSION:-?}" "$HOURS" "$JSPAN" "$NOW" "$total" "$rate" printf '"drops_by_reason":{"portscan":%s,"synflood":%s,"ssh-flood":%s,"badflags":%s,"crowdsec":%s},' \ "$portscan" "$synflood" "$sshflood" "$badflags" "$crowd" printf '"timeline":[%s],' "$tl" @@ -266,6 +280,11 @@ B printf "%b" "$NC" local total rate; total="$(events_total)"; rate="$(ban_rate_5m)" info "Окно: последние ${HOURS}ч · событий в drop-логе: $total · за 5 мин: $rate" + if [[ "$JSPAN" -ge 0 && "$JSPAN" -lt "$HOURS" ]]; then + warn "журнал хранит только ~${JSPAN}ч (самая старая запись) — окно ${HOURS}ч фактически урезано; «событий мало» может значить «журнал вытеснен» (снизь PORTSCAN_LOG_RATE / подними NA_JOURNAL_MAX_USE)" + elif [[ "$JSPAN" -ge 0 ]]; then + info "глубина журнала: ~${JSPAN}ч (все загрузки, -b all)" + fi title "Дропы по причине" for r in portscan synflood ssh-flood badflags; do @@ -326,7 +345,7 @@ crowdsec_scenarios() { port_focus() { local p="$FOCUS_PORT" since="-${HOURS}h" lines n title "🔌 Порт $p — дроп-источники (за ${HOURS}ч)" - lines="$(journalctl -k --since "$since" --no-pager 2>/dev/null | grep -aF '[na ' | grep -aF "DPT=$p ")" + lines="$(journalctl -k -b all --since "$since" --no-pager 2>/dev/null | grep -aF '[na ' | grep -aF "DPT=$p ")" n="$(printf '%s\n' "$lines" | grep -c .)" info "drop-событий на DPT=$p: $n" if [[ "$n" -gt 0 ]]; then diff --git a/scripts/optimize.sh b/scripts/optimize.sh index ff958e5..214bc0f 100755 --- a/scripts/optimize.sh +++ b/scripts/optimize.sh @@ -824,8 +824,10 @@ NA_LOG_ROTATE="${NA_LOG_ROTATE:-4}" NA_LOG_INTERVAL="${NA_LOG_INTERVAL:-hourly}" LR_CONF=/etc/logrotate.d/na-node-logs +LR_CEDED_N=0 if [[ "$ENABLE_LOGROTATE" != "1" ]]; then info "ротация логов пропущена (ENABLE_LOGROTATE=0)" + rm -f "$STATE_DIR/logrotate.ceded" "$STATE_DIR/logrotate.owned" 2>/dev/null || true else [[ "$NA_LOG_MAXSIZE" =~ ^[0-9]+[kKMG]$ ]] || { err "NA_LOG_MAXSIZE='$NA_LOG_MAXSIZE' — ожидается размер вида 200M"; exit 1; } [[ "$NA_LOG_ROTATE" =~ ^[0-9]+$ ]] || { err "NA_LOG_ROTATE='$NA_LOG_ROTATE' — ожидается целое число"; exit 1; } @@ -849,7 +851,7 @@ else # стансам — у них может быть сигнальный reload вместо copytruncate, им и владеть. # read -a, а не голый word-split: иначе шелл сам раскроет глобы по живым файлам, # и в стансу лягут ЯВНЫЕ пути — лог нового vhost'а никогда не начнёт ротироваться. - _want=(); _dup=() + _want=(); _dup=(); _dup_paths="" read -r -a _want <<<"$NA_LOG_PATHS" _na_lr_write() { @@ -892,7 +894,7 @@ LRC while IFS= read -r _f; do [[ -n "$_f" ]] || continue # shellcheck disable=SC2053 # маска без кавычек — намеренный glob-матч - if [[ "$_f" == $_m ]]; then _hit=1; break; fi + if [[ "$_f" == $_m ]]; then _hit=1; _dup_paths+="${_dup_paths:+$'\n'}$_f"; fi done <<<"$_dupf" if [[ "$_hit" -eq 1 ]]; then _dup+=("$_m"); else _keep+=("$_m"); fi done @@ -907,14 +909,71 @@ LRC done fi + # Уступка — не конец истории (issue #40). Раньше модуль печатал «проверь, что там + # задан maxsize» и рапортовал зелёным, а na-diagnose видел только активный таймер: + # на трёх нодах флота ротацию держала ручная станса `weekly` без maxsize — то есть + # никакого капа не было, а всё выглядело настроенным. Теперь: ищем стансу-владельца, + # САМИ смотрим в ней maxsize/size, и пишем факт уступки в состояние — + # его показывает na-diagnose (текст и --json), а не только лог этого прогона. + # + # _na_lr_owner <путь> — файл в /etc/logrotate.d (кроме нашего), чья маска покрывает + # путь. Шапка стансы — строки, начинающиеся с «/», до «{»; кавычки снимаем. + _na_lr_owner() { + local f pat + for f in /etc/logrotate.d/*; do + [[ -f "$f" && "$f" != "$LR_CONF" ]] || continue + while IFS= read -r pat; do + [[ -n "$pat" ]] || continue + # shellcheck disable=SC2053 # маска без кавычек — намеренный glob-матч + if [[ "$1" == $pat ]]; then echo "$f"; return 0; fi + done < <(sed -nE 's/^[[:space:]]*(\/[^{]*).*/\1/p' "$f" 2>/dev/null | tr -d '"' | tr ' \t' '\n\n') + done + return 1 + } + # _na_lr_cap <файл-стансы> — есть ли в стансе ограничение по размеру: maxsize (по + # размеру ИЛИ периоду) либо size (только по размеру). minsize капом НЕ является — + # он лишь запрещает ротировать мелкие файлы. + _na_lr_cap() { grep -qE '^[[:space:]]*(maxsize|size)[[:space:]]+[0-9]' "$1" 2>/dev/null; } + + mkdir -p "$STATE_DIR" + : > "$STATE_DIR/logrotate.ceded.tmp" + for _m in "${_dup[@]}"; do + # владельца ищем по РЕАЛЬНЫМ путям, которые logrotate назвал дубликатами и + # которые покрыты этой маской: у чужой стансы маска может быть другой + _owner="" + while IFS= read -r _f; do + [[ -n "$_f" ]] || continue + # shellcheck disable=SC2053 + [[ "$_f" == $_m ]] || continue + _owner="$(_na_lr_owner "$_f" || true)"; [[ -n "$_owner" ]] && break + done <<<"${_dup_paths:-}" + if [[ -z "$_owner" ]]; then + warn "маска $_m отдана чужой стансе, но владелец в /etc/logrotate.d не найден (станса в /etc/logrotate.conf?) — проверь, что там задан maxsize" + printf '%s\t%s\t%s\n' "$_m" "?" "unknown" >> "$STATE_DIR/logrotate.ceded.tmp" + elif _na_lr_cap "$_owner"; then + info "маска $_m уже покрыта чужой стансой $_owner — отдана ей (там есть maxsize/size — кап на размер работает)" + printf '%s\t%s\t%s\n' "$_m" "$_owner" "capped" >> "$STATE_DIR/logrotate.ceded.tmp" + else + # `|| true` обязателен: под set -e -o pipefail пустой grep ронял бы весь прогон + _period="$(grep -owE '(hourly|daily|weekly|monthly|yearly)' "$_owner" 2>/dev/null | head -1 || true)" + warn "маска $_m отдана чужой стансе $_owner: там ${_period:-период не задан} БЕЗ maxsize/size — размер логов ничем не ограничен; добавь в неё 'maxsize $NA_LOG_MAXSIZE' или сузь NA_LOG_PATHS" + printf '%s\t%s\t%s\n' "$_m" "$_owner" "none" >> "$STATE_DIR/logrotate.ceded.tmp" + fi + done + unset _owner _period + if [[ -s "$STATE_DIR/logrotate.ceded.tmp" ]]; then + mv -f "$STATE_DIR/logrotate.ceded.tmp" "$STATE_DIR/logrotate.ceded" + else + rm -f "$STATE_DIR/logrotate.ceded.tmp" "$STATE_DIR/logrotate.ceded" + fi + LR_CEDED_N="${#_dup[@]}" if [[ "${#_want[@]}" -gt 0 ]]; then + printf '%s\n' "${_want[@]}" > "$STATE_DIR/logrotate.owned" ok "стансa $LR_CONF: ${_want[*]} (maxsize $NA_LOG_MAXSIZE, rotate $NA_LOG_ROTATE)" else - info "все заданные пути уже покрыты другими стансами — свою не создаю" + rm -f "$STATE_DIR/logrotate.owned" + warn "все заданные пути уже покрыты чужими стансами — свою НЕ создаю: ротацию логов ноды держат они (см. выше, есть ли там maxsize); таймер ниже гоняет весь /etc/logrotate.conf, т.е. и их" fi - for _m in "${_dup[@]}"; do - warn "маска $_m уже покрыта чужой стансой в /etc/logrotate.d — отдана ей; проверь, что там задан maxsize" - done cat > /etc/systemd/system/na-logrotate.service <<'EOF' [Unit] @@ -1028,3 +1087,6 @@ if [[ "$REBOOT_NEEDED" == "1" ]]; then fi fi warn "Часть лимитов применится после перелогина/reboot (DefaultLimit* для systemd-сервисов)." +if [[ "${LR_CEDED_N:-0}" -gt 0 ]]; then + warn "РОТАЦИЯ ЛОГОВ: $LR_CEDED_N маск(и) отданы чужим стансам logrotate — тулкит их НЕ ротирует; кто и с каким капом: cat $STATE_DIR/logrotate.ceded (см. предупреждения секции «ротация логов ноды»)" +fi diff --git a/tests/diagnose-unit.sh b/tests/diagnose-unit.sh index 559db63..adad745 100755 --- a/tests/diagnose-unit.sh +++ b/tests/diagnose-unit.sh @@ -72,6 +72,7 @@ sandbox_paths() { -e "s#/var/lib/docker#$T/var/lib/docker#g" \ -e "s#/etc/systemd/system/#$T/systemd/#g" \ -e "s#/etc/logrotate.conf#$T/etc/logrotate.conf#g" \ + -e "s#/etc/logrotate.d#$T/etc/logrotate.d#g" \ -e "s#/etc/os-release#$T/etc/os-release#g" \ -e "s#/tmp/na-fw-safety.pid#$T/na-fw-safety.pid#g" \ -e "s#/etc/letsencrypt#$T/etc/letsencrypt#g" \ @@ -276,6 +277,7 @@ cat > "$BIN/systemctl" <<'SC' #!/bin/sh case "$*" in "is-active --quiet na-rps.service") exit 0 ;; # active (exited) + "is-active --quiet na-logrotate.timer") exit 0 ;; # таймер активен — станса отдельно (#40) "is-active --quiet crowdsec") exit 0 ;; "is-active --quiet crowdsec-firewall-bouncer") exit 0 ;; "is-enabled --quiet na-firewall.service") exit 0 ;; @@ -285,6 +287,13 @@ esac exit 1 SC +# logrotate — только чтобы diagnose не ушёл в ветку «не установлен» раньше сенсора стансы (#40); +# `-d` по пустому conf в песочнице → тишина, дубликатов нет. +cat > "$BIN/logrotate" <<'LR' +#!/bin/sh +exit 0 +LR + cat > "$BIN/uname" <<'UN' #!/bin/sh [ "$1" = "-r" ] && { echo "6.18.47-x64v3-xanmod1"; exit 0; } @@ -459,6 +468,30 @@ echo "== 6. CrowdSec: 3 решения = 3, а не 4 (#32) ==" env TERM=dumb NA_TEST_DECISIONS=3 "$WBASH" "$DIAG" > "$T/out6.txt" 2>/dev/null || true grep_ok "три решения считаются как 3" "CrowdSec decisions (активные баны): 3" "$T/out6.txt" +echo "== 7. ротация: «таймер активен» ≠ «станса есть» (#40) ==" +LRSTATE="$T/var/lib/node-accelerator"; mkdir -p "$T/etc/logrotate.d" +printf '/var/log/nginx/*.log\t/etc/logrotate.d/vpn-node-logs\tnone\n' > "$LRSTATE/logrotate.ceded" +printf '/var/log/remnanode/*.log\n' > "$LRSTATE/logrotate.owned" +printf '/var/log/remnanode/*.log {\n daily\n}\n' > "$T/etc/logrotate.d/na-node-logs" +env "$SSHENV" TERM=dumb "$WBASH" "$DIAG" --json > "$T/out7.json" 2>/dev/null || true +check "json: logrotate_ceded_nocap=1" 1 "$(jget "$T/out7.json" logrotate_ceded_nocap)" +check "json: logrotate_ceded_masks — отданная маска" "/var/log/nginx/*.log" "$(jget "$T/out7.json" logrotate_ceded_masks)" +check "json: logrotate_owned_masks — наша маска" "/var/log/remnanode/*.log" "$(jget "$T/out7.json" logrotate_owned_masks)" +env "$SSHENV" TERM=dumb "$WBASH" "$DIAG" > "$T/out7.txt" 2>/dev/null || true +grep_ok "текст: ▲ уступка, владелец без капа назван" "vpn-node-logs (БЕЗ maxsize/size!)" "$T/out7.txt" +grep_not "текст: зелёного «станса на месте» при уступке нет" "часовой таймер активен, станса на месте" "$T/out7.txt" +# уступок нет, но стансы тоже нет (пустая) — таймер крутится вхолостую +rm -f "$LRSTATE/logrotate.ceded" "$LRSTATE/logrotate.owned"; : > "$T/etc/logrotate.d/na-node-logs" +env "$SSHENV" TERM=dumb "$WBASH" "$DIAG" > "$T/out7b.txt" 2>/dev/null || true +grep_ok "текст: таймер активен, стансы нет → ▲" "na-node-logs нет" "$T/out7b.txt" +check "json: без уступок ceded_nocap=0" 0 "$(env "$SSHENV" TERM=dumb "$WBASH" "$DIAG" --json 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["logrotate_ceded_nocap"])')" +# станса есть, уступок нет — ✔ +printf '/var/log/remnanode/*.log {\n daily\n}\n' > "$T/etc/logrotate.d/na-node-logs" +printf '/var/log/remnanode/*.log\n' > "$LRSTATE/logrotate.owned" +env "$SSHENV" TERM=dumb "$WBASH" "$DIAG" > "$T/out7c.txt" 2>/dev/null || true +grep_ok "текст: станса на месте → ✔ с перечнем масок" "часовой таймер активен, станса на месте (/var/log/remnanode/*.log)" "$T/out7c.txt" +rm -f "$LRSTATE/logrotate.owned" + echo echo " прогон: $PASS ok, $FAIL fail" if [[ "$FAIL" -ne 0 ]]; then echo "DIAGNOSE-UNIT: FAIL"; exit 1; fi diff --git a/tests/logrotate-unit.sh b/tests/logrotate-unit.sh index 97fadce..070b52a 100644 --- a/tests/logrotate-unit.sh +++ b/tests/logrotate-unit.sh @@ -56,8 +56,10 @@ cat > "$T/wrap.sh" < "$T/bin/systemctl" <<'ST' @@ -138,8 +140,24 @@ expect "секция отработала (rc=0)" run_section expect "станса создана (не снесена целиком)" test -f "$STANZA" expect_not "конфликтная маска nginx ОТДАНА чужой стансе" grep -qF "$T/var/log/nginx" "$STANZA" expect "неконфликтная маска remnanode осталась" grep -qF "$T/var/log/remnanode/*.log" "$STANZA" -expect "warn про уступку маски напечатан" grep -q "уже покрыта чужой стансой" "$T/out.log" +expect "warn про уступку маски напечатан" grep -q "отдана чужой стансе" "$T/out.log" expect "после переписывания стансы вердикт перепроверен" test "$(wc -l < "$REC/logrotate.argv")" -ge 2 +# issue #40: владелец найден, у него нет maxsize — об этом сказано САМИМ модулем, а не «проверь сам» +expect "владелец уступленной маски назван (nginx-legacy)" grep -q "nginx-legacy" "$T/out.log" +expect "у владельца daily БЕЗ maxsize — warn об этом" grep -q "daily БЕЗ maxsize/size" "$T/out.log" +expect "состояние уступки записано (logrotate.ceded)" test -s "$STATE/logrotate.ceded" +expect "в ceded: маска → владелец → none" grep -qE "^$T/var/log/nginx/\*\.log $LRD/nginx-legacy none$" "$STATE/logrotate.ceded" +expect "в owned: оставшаяся маска remnanode" grep -qF "$T/var/log/remnanode/*.log" "$STATE/logrotate.owned" +expect_not "в owned НЕТ уступленной маски nginx" grep -qF "$T/var/log/nginx" "$STATE/logrotate.owned" + +# ── Кейс 3b: у чужой стансы maxsize ЕСТЬ — уступка без тревоги о капе ─────────── +echo "== чужая станса с maxsize (кап есть) ==" +rm -f "$STANZA" +printf '%s\n' "$T/var/log/nginx/*" '{' ' weekly' ' maxsize 500M' '}' > "$LRD/nginx-legacy" +expect "секция отработала (rc=0)" run_section +expect "уступка есть, кап найден — info, не warn" grep -q "кап на размер работает" "$T/out.log" +expect_not "warn «БЕЗ maxsize» НЕ напечатан" grep -q "БЕЗ maxsize/size" "$T/out.log" +expect "в ceded: capped" grep -qE " capped$" "$STATE/logrotate.ceded" # ── Кейс 4: чужой дубликат вне наших масок ────────────────────────────────────── echo "== чужой дубликат (не наши маски) ==" @@ -150,15 +168,28 @@ expect "секция отработала (rc=0)" run_section expect "наши маски не тронуты" grep -qF "$T/var/log/nginx/*.log" "$STANZA" expect "warn про чужой конфликт напечатан" grep -q "вне наших масок" "$T/out.log" expect "цикл не зациклился (ровно один вызов -d)" test "$(wc -l < "$REC/logrotate.argv")" -eq 1 +expect_not "уступок нет — logrotate.ceded снят" test -e "$STATE/logrotate.ceded" +expect "owned содержит обе маски" test "$(grep -c . "$STATE/logrotate.owned")" -eq 2 # ── Кейс 5: все маски уже у чужих станс ───────────────────────────────────────── echo "== полная уступка всех масок ==" rm -f "$STANZA" : > "$REC/foreign-dup.txt" printf '%s\n' "$T/var/log/nginx/access.log" "$T/var/log/remnanode/node.log" > "$REC/foreign-claims.txt" +printf '%s\n' "$T/var/log/nginx/*" '{ daily }' > "$LRD/nginx-legacy" +printf '%s\n' "$T/var/log/remnanode/*.log" '{ weekly }' > "$LRD/vpn-node-logs" expect "секция отработала (rc=0)" run_section expect "станса не создана (все пути чужие)" test ! -f "$STANZA" -expect "инфо-сообщение напечатано" grep -q "свою не создаю" "$T/out.log" +expect "полная уступка — это WARN, а не info (#40)" grep -q "\[!\].*свою НЕ создаю" "$T/out.log" +expect "ceded перечисляет обе маски" test "$(grep -c . "$STATE/logrotate.ceded")" -eq 2 +expect "второй владелец (vpn-node-logs, weekly без maxsize) назван" grep -q "vpn-node-logs: там weekly БЕЗ maxsize" "$T/out.log" +expect_not "owned снят (нашей стансы нет)" test -e "$STATE/logrotate.owned" + +# ── Кейс 6: ENABLE_LOGROTATE=0 — состояние прошлых прогонов не должно врать ──── +echo "== ENABLE_LOGROTATE=0 снимает состояние ==" +: > "$REC/logrotate.argv" +ENABLE_LOGROTATE=0 NA_LOG_PATHS="$MASKS" "$WBASH" "$T/wrap.sh" > "$T/out.log" 2>&1 || true +expect_not "ceded снят при ENABLE_LOGROTATE=0" test -e "$STATE/logrotate.ceded" if [ "$fail" -ne 0 ]; then echo "LOGROTATE-UNIT: FAIL"; exit 1; fi -echo "LOGROTATE-UNIT: OK (ре-ран жив, конфликты решает вердикт logrotate, глобы не раскрываются, чужие дубликаты не наши)" +echo "LOGROTATE-UNIT: OK (ре-ран жив, конфликты решает вердикт logrotate, глобы не раскрываются, чужие дубликаты не наши, уступка видна: владелец/кап/состояние)" diff --git a/tests/psi-unit.sh b/tests/psi-unit.sh index d016280..73d62ac 100755 --- a/tests/psi-unit.sh +++ b/tests/psi-unit.sh @@ -217,11 +217,11 @@ expect "psi=1 на месте" test "$(cmdline)" = 'GRUB_CMDLINE_LINUX_DEFAULT=" expect "update-grub не звался" test "$(ug_calls)" -eq 0 # ── Регрессия: на v4.0.1 ручки не было вовсе ─────────────────────────────────── -echo "== код v4.0.1 (регрессия; пропускается без ветки main) ==" -if git -C "$REPO_ROOT" show main:scripts/optimize.sh > "$T/old-optimize.sh" 2>/dev/null; then +echo "== код v4.0.1 (регрессия; пропускается без тега v4.0.1) ==" +if git -C "$REPO_ROOT" show v4.0.1:scripts/optimize.sh > "$T/old-optimize.sh" 2>/dev/null; then expect_not "в v4.0.1 секции PSI нет" grep -q '^# ─── 2b\.' "$T/old-optimize.sh" expect_not "в v4.0.1 ручки ENABLE_PSI нет" grep -q 'ENABLE_PSI' "$T/old-optimize.sh" - git -C "$REPO_ROOT" show main:scripts/rollback.sh > "$T/old-rollback.sh" 2>/dev/null || true + git -C "$REPO_ROOT" show v4.0.1:scripts/rollback.sh > "$T/old-rollback.sh" 2>/dev/null || true expect_not "в v4.0.1 откат psi=1 не делался" grep -q 'psi=1' "$T/old-rollback.sh" else echo " • ветка main недоступна — сравнение с v4.0.1 пропущено" diff --git a/tests/rps-unit.sh b/tests/rps-unit.sh index 12c5cda..11c49dd 100755 --- a/tests/rps-unit.sh +++ b/tests/rps-unit.sh @@ -182,8 +182,8 @@ expect "и мусорных \\r тоже нет" test ! -s "$T/tty.out" # ── Регрессия: на коде v4.0.1 те же кейсы обязаны падать ─────────────────────── # Не в каждом чекауте есть ветка main (shallow-клон CI) — тогда просто пропускаем. -echo "== код v4.0.1 (регрессия; пропускается без ветки main) ==" -if git -C "$REPO_ROOT" show main:scripts/optimize.sh > "$T/old-optimize.sh" 2>/dev/null \ +echo "== код v4.0.1 (регрессия; пропускается без тега v4.0.1) ==" +if git -C "$REPO_ROOT" show v4.0.1:scripts/optimize.sh > "$T/old-optimize.sh" 2>/dev/null \ && extract_rps "$T/old-optimize.sh" "$T/na-rps-old"; then reset_net; mk_net ens18 1 ROUTE_AFTER=3 ROUTE_IFACE=ens18 run_rps "$T/na-rps-old" From 5717c25ddbe5d50bc457300ab8fa2d229c0fc53a Mon Sep 17 00:00:00 2001 From: jestivald Date: Wed, 2 Sep 2026 14:42:30 +0300 Subject: [PATCH 2/2] release: sign 6 modules with minisign (v4.1.1) --- scripts/diagnose.sh.minisig | 8 ++++---- scripts/lib/common.sh.minisig | 8 ++++---- scripts/na-report.sh.minisig | 8 ++++---- scripts/optimize.sh.minisig | 8 ++++---- scripts/protect.sh.minisig | 6 +++--- scripts/rollback.sh.minisig | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/scripts/diagnose.sh.minisig b/scripts/diagnose.sh.minisig index 2bc7b2d..bd8ecb0 100644 --- a/scripts/diagnose.sh.minisig +++ b/scripts/diagnose.sh.minisig @@ -1,4 +1,4 @@ -untrusted comment: node-accelerator v4.1 -RUQrJghT9nkdBAhVc5hN2qvpWxRqKBW7EivyAkjnlvEChQZ9XIWepJk1Eq7I9rhUhLo9FN7CNY0aWk7tuyrlqRtg89jVpMO9cAY= -trusted comment: timestamp:1788348398 file:diagnose.sh hashed -hZZu8QDEvt0VgKsIkQ8PfxOIrdw9HxrN0WUwYeZKjOQ/Nf/mrkjENA/oXkBGS1A5CdS/2gjheaSU7fGhBlfDDw== +untrusted comment: node-accelerator v4.1.1 +RUQrJghT9nkdBENoxhnU9rwsJzsLv3J7D9SkM1oe+0YmmPdKLZWtjkaSKCLIQJyFN6bIsc+I6+50gMTg6NHare9Bwo6SMiIB9AY= +trusted comment: timestamp:1788349350 file:diagnose.sh hashed +Kn3mUHqRXJ3TrbY2sTWuRegSUPxmAGUiC58AhBamEvfgFWFxmfcHS6pXKwfG5xCPdSznjgxXGwC5OFtLbCXlCw== diff --git a/scripts/lib/common.sh.minisig b/scripts/lib/common.sh.minisig index 14c2083..51585b9 100644 --- a/scripts/lib/common.sh.minisig +++ b/scripts/lib/common.sh.minisig @@ -1,4 +1,4 @@ -untrusted comment: node-accelerator v4.1 -RUQrJghT9nkdBDoV6iLC2aa+udx6asRQD+/4Jh2dHmhHFi2DwCsnKjDnugZ4sje0QhSLZCqfNewWNCx54rN3wX+dzt+3a62rgwU= -trusted comment: timestamp:1788348398 file:common.sh hashed -4C7yF1qh32Hfa8+0wiIibiEOJyYYSPJrWj1o+OxojOGDPXvcBwTNUKn5Nt5eBMuS1sPCV5aHm4eRzx7FT2sICQ== +untrusted comment: node-accelerator v4.1.1 +RUQrJghT9nkdBPMjRYxTeyDRPHNs+d5y7lErzQAsoal8gZpryUTA0Ad09waeYLWIWZWthxvOsUaocoZ/qPl1PPhUPoRKACUfPwo= +trusted comment: timestamp:1788349350 file:common.sh hashed +l4F98FXGpy8Umba+MEjtxykaeq298rquHNSNvaJLxaZGqT33SYSK8WlY4IGmT997kPLSBZHWA1KFM74UU8gTBw== diff --git a/scripts/na-report.sh.minisig b/scripts/na-report.sh.minisig index 37ab426..3c49499 100644 --- a/scripts/na-report.sh.minisig +++ b/scripts/na-report.sh.minisig @@ -1,4 +1,4 @@ -untrusted comment: node-accelerator v4.1 -RUQrJghT9nkdBJsxTo1G46BJw9b/ROKYvO+GiPKwL0s0yRThLQdZF7IbLOjOYyrJDXpes7ngwsc+NYx0/eIw5ZB5ZmEaFtMxKAA= -trusted comment: timestamp:1788348398 file:na-report.sh hashed -AZfQN3jm1+flcRBGuimlHHLALVewa0S4xwowVFJb9O+lXAuDSyY/ORXkI8zCrcA5EeEOENbFdHScJ8UE+2aWAA== +untrusted comment: node-accelerator v4.1.1 +RUQrJghT9nkdBOrdxdxfyqJUywTv/RLU7ZreVDZAtLeliMCTEsY5M0vWhsR1gF5MEbykzE8/QRKRqFN3Odck90tt1E7RC8oxow8= +trusted comment: timestamp:1788349350 file:na-report.sh hashed +YAktBFahm+IuuKRNinUKv9J3BcJl7TZCMGxC9s4dxl0kOsmWTV09BLbSb11ln/Yt5DmgNZ71xxnwjIXaIk8NCQ== diff --git a/scripts/optimize.sh.minisig b/scripts/optimize.sh.minisig index c6b4a25..b76315c 100644 --- a/scripts/optimize.sh.minisig +++ b/scripts/optimize.sh.minisig @@ -1,4 +1,4 @@ -untrusted comment: node-accelerator v4.1 -RUQrJghT9nkdBPL3gKFq93BQCX78f+pqykpXsUpYxkYuPRSgp46CuXV9bcXjEXtHOblnfLqVPbW0qBmHCTLnwFcaaGD/Lb+XQwg= -trusted comment: timestamp:1788348398 file:optimize.sh hashed -Yd7kr9+TUfp61M5hc5GmxFZuQov2zrOuZiMI0D+iLFNCDx5HABvYhNZHKrf7mhDCyIgUw3CV/FgqKvxUjMW5DQ== +untrusted comment: node-accelerator v4.1.1 +RUQrJghT9nkdBFzx1P7p5JoJ79BCF7lkOYOkx8yobz7G1kvaOM7gT7Hk/iUIzrPMfYXe/Mq/BRshJyPUnp3BVc41zXTWz5gSxAE= +trusted comment: timestamp:1788349350 file:optimize.sh hashed +6/gfjc4FoY6moWYtPxteYrF7qI8QudC1R2PNtirQZSs+6JnwE0mPGi48iPOtPUKMJg1VjTK+IBEDEAkgatyeAA== diff --git a/scripts/protect.sh.minisig b/scripts/protect.sh.minisig index aae0380..42a0aa0 100644 --- a/scripts/protect.sh.minisig +++ b/scripts/protect.sh.minisig @@ -1,4 +1,4 @@ -untrusted comment: node-accelerator v4.1 +untrusted comment: node-accelerator v4.1.1 RUQrJghT9nkdBCM9IR9iMEwQS6sDcwVTfzg4UV7E98JvNxD41mLD2QfmRf0TyPypOeHE83iUW2N3726gl0RL3SkQH1Sem0cEKwY= -trusted comment: timestamp:1788348398 file:protect.sh hashed -ggstRnICDPxx7aRN65Hx+Maj1WBpDpF019pgSYD3eX91Ub8EsCawEq2SGYNIo6IPZOSvoSGFSATjm9HdhaCzCg== +trusted comment: timestamp:1788349350 file:protect.sh hashed +bF0SKfnBtn/+pe7g2Ti46F2QL1yc3RPBMYaeVV1YWrk3MMXXfiIxlWnAZgzpjRnEfswFkWtoigZfJ3snz+gOAA== diff --git a/scripts/rollback.sh.minisig b/scripts/rollback.sh.minisig index 02678e3..a25bf77 100644 --- a/scripts/rollback.sh.minisig +++ b/scripts/rollback.sh.minisig @@ -1,4 +1,4 @@ -untrusted comment: node-accelerator v4.1 +untrusted comment: node-accelerator v4.1.1 RUQrJghT9nkdBB27AWybm39DtJ+7UApLPni5ByAeqgET7Ghc3BDFfn5Idz5Cc9n5WK99g5bU/FN1ZvxW8NaK7oLw+MMpBCXNcws= -trusted comment: timestamp:1788348398 file:rollback.sh hashed -P98lXulSmOThhYnLo/URvGT9p3ieavFZMHQiXwuKVTnD4gawt0DU9H/Kvlb80z1MK4iJX469XCv9FxhS6MccCw== +trusted comment: timestamp:1788349350 file:rollback.sh hashed +LMN68xED+5PySBeLNUgVIFf1y7T8VTMLZ0JpHeUYLs92MACXIF4ZvFWe7PxvCq9RwM9Tmjh7En/Adbb6tdQDCQ==