Skip to content

diagnose: счётчики autoban/suspect считают строки вывода nft, а не адреса — вечная «1» на пустом наборе #32

Closed

Description

@jestivald

Симптом

na-diagnose показывает адреса в наборах автобана и наблюдения, когда наборы пустые, и завышает счёт на единицу, когда они непустые.

Пустые наборы, а счётчики ненулевые (три ноды OVH, свежий ребут):

# na-diagnose | grep -E 'autoban|suspect'
[*] autoban: v4=3  v6=1
[*] ban-once: suspect (наблюдение) v4=1

# nft list set inet na_filter suspect_v4
table inet na_filter {
        set suspect_v4 {
                type ipv4_addr
                flags dynamic,timeout
                timeout 30m
        }
}

В suspect_v4 нет ни одного элемента, но диагностика печатает v4=1. То же с autoban_v6 — v6=1 при пустом наборе.

Непустой набор считается неверно: на одной ноде autoban_v4 содержит 2 адреса, na-diagnose печатает v4=3; на другой 4 адреса → v4=5.

Причина

scripts/diagnose.sh:484-486 и :517-519:

AB4=$(nft list set inet na_filter autoban_v4 2>/dev/null | grep -c 'timeout\|expires')
AB6=$(nft list set inet na_filter autoban_v6 2>/dev/null | grep -c 'timeout\|expires')
...
SUSP=$(nft list set inet na_filter suspect_v4 2>/dev/null | grep -c timeout)

Считаются строки вывода, а не адреса. Две ошибки складываются:

  1. В заголовке любого динамического набора всегда есть строки flags dynamic,timeout и timeout 30m — они матчатся всегда, отсюда постоянная единица на пустом наборе (grep -c 'timeout\|expires' даёт 1, потому что -c считает совпавшие строки, а flags dynamic,timeout — одна из них).
  2. nft печатает элементы по несколько в строке и переносит длинные списки, так что соответствие «строка = адрес» не выполняется и на непустых наборах.

Рядом, в той же функции, аналогичный счётчик сделан правильно (:487):

WLN=$(nft list set inet na_filter whitelist_v4 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | wc -l | tr -d ' ')

Почему это стоит чинить

Автобан — единственное место, где видно, кого нода забанила сама. Вечная единица приучает считать строку шумом, и настоящий первый бан (в том числе собственного адреса оператора или адреса панели) теряется на её фоне. Цифра уходит и в --json (scripts/diagnose.sh:151), то есть попадает во флот-мониторинг — Zabbix/Prometheus получают ненулевой ряд там, где банов нет.

Предложение

Считать адреса тем же способом, что и в соседнем WLN, например:

count_set_elems() {  # $1 — имя набора, $2 — regex адреса
    nft list set inet na_filter "$1" 2>/dev/null | sed -n '/elements = {/,/}/p' \
        | grep -oE "$2" | sort -u | wc -l | tr -d ' '
}
AB4=$(count_set_elems autoban_v4 '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')

Либо, надёжнее и без разбора текста, — nft -j list set … и подсчёт .nftables[].set.elem | length через jq (в v4 jq уже используется в PRE-шагах). Заодно имеет смысл печатать сами адреса при ненулевом счёте: оператору важно не «сколько», а «кто».

Окружение

node-accelerator 4.0.0 (по исходникам v4.0.1 код не менялся), Debian 12/13, XanMod 6.18.47, FW_MODE=strict, ENABLE_BANONCE=1, ENABLE_PORTSCAN_BAN=1. Проверено на 7 нодах.

Activity

  1. jestivald commented on Sep 2, 2026

    @jestivald
    OwnerAuthor

    Добавляю ещё два места с тем же корнем — «grep -c по выводу утилиты, где есть строка-заголовок». Нашлись при аудите того же флота (14 нод), стоит чинить одним заходом.

    1. CrowdSec decisions (активные баны) тоже завышен на 1 — scripts/diagnose.sh:601:

    DEC="$(cscli decisions list -o raw 2>/dev/null | grep -vc '^$')"

    cscli decisions list -o raw печатает CSV с заголовком, и он попадает в счёт. Замер на четырёх нодах: диагностика показывает 1 / 4 / 2 / 7, фактических решений 0 / 3 / 1 / 6. Особенно неудачен случай пустого списка: decisions: 1 читается как «один бан есть», хотя банов нет.

    Лечится либо tail -n +2, либо переходом на cscli decisions list -o json | jq length.

    2. В JSON-ветке та же ошибка, причём без варианта expires — scripts/diagnose.sh:62-64:

    ab4="$(nft list set inet na_filter autoban_v4 2>/dev/null | grep -c timeout)"
    ab6="$(nft list set inet na_filter autoban_v6 2>/dev/null | grep -c timeout)"
    susp="$(nft list set inet na_filter suspect_v4 2>/dev/null | grep -c timeout)"

    Это тот путь, который читает флот-мониторинг, так что ненулевой ряд там, где банов нет, приезжает прямо в Zabbix/Prometheus. Если делать общий хелпер подсчёта элементов набора (предложение из тела issue), он закроет и человекочитаемую ветку, и JSON разом.

    Мелочь рядом, на усмотрение: na-report молча оставляет колонку ASN как ? на боксах, где нет dig/whois (у нас — на 2 нодах из 4 в одной группе, на остальных колонка заполнена). В шапке na-report.sh:6 это описано как осознанный best-effort, так что не баг, но одна строка вида «ASN не обогащён: нет dig/whois» избавила бы от догадок, почему у соседних нод отчёты выглядят по-разному.

  2. jestivald commented on Sep 2, 2026

    @jestivald
    OwnerAuthor

    Исправлено в v4.1 (PR #42, merge 427b13f): https://github.com/jestivald/node-accelerator/releases/tag/v4.1 — разбор в CHANGELOG, секция v4.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions