From f5f1ce2da8920217a4eb8c07490fe4c00b24de90 Mon Sep 17 00:00:00 2001 From: jestivald Date: Wed, 24 Jun 2026 18:37:25 +0300 Subject: [PATCH 1/2] na-report: add --proxyware self-audit (residential-proxy / proxyware-SDK) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read-only proof that a node is not a residential proxy and carries no bandwidth-monetization SDK — evidence to answer hoster abuse tickets. - signature scan (IPIDEA/PacketSDK/Honeygain/EarnApp/Peer2Profit/...) across processes, systemd units, cron, files and docker images - established connections to known proxyware C2 (resolve domains -> match ESTABLISHED) - external-facing listeners (operator context) - verdict clean|suspect; `--proxyware --json` emits one object (CI single-line safe) - signatures are product names only; generic "proxy" is NOT matched, so the node's own xray/VLESS does not false-positive Tested on a live node: bash -n + shellcheck clean, verdict=clean, JSON one line. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 11 ++++ scripts/na-report.sh | 132 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 960d7d9..08dc613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v3.4 — 2026-06-24 + +Self-audit на residential-proxy / proxyware-SDK (read-only) — доказательная база для ответа на abuse-тикеты хостеров. + +### 🕵 na-report — новый режим `--proxyware` +- проверка сигнатур известного proxyware (IPIDEA/PacketSDK/Honeygain/EarnApp/Peer2Profit/…) в процессах, systemd-юнитах, cron, файлах и docker-образах; +- поиск **установленных** соединений к C2-инфраструктуре proxyware (резолв доменов → match по ESTABLISHED peer-адресам); +- список внешне-доступных listener'ов (контекст для оператора); +- вердикт `clean|suspect`; `--proxyware --json` — один объект для панели/флота. +- Сигнатуры — только конкретные имена продуктов (generic «proxy» не матчим, чтобы не ловить сам xray/VLESS). + ## v3.3.1 — 2026-06-16 ### 🐞 Фикс diff --git a/scripts/na-report.sh b/scripts/na-report.sh index 48e189f..74d4418 100755 --- a/scripts/na-report.sh +++ b/scripts/na-report.sh @@ -11,12 +11,18 @@ # na-report.sh --top 20 — сколько top-IP/ASN показывать # na-report.sh --ip 1.2.3.4 — глубокий вердикт по IP (rDNS, nft-сеты, conntrack, таймлайн) # na-report.sh --port 443 — топ дроп-источников по порту + кто слушает +# na-report.sh --proxyware — self-audit: следы proxyware/residential-proxy (для abuse-тикетов; + --json) # # JSON-схема: # {window_hours, generated_at, events_total, ban_rate_5m, # drops_by_reason{portscan,synflood,ssh-flood,badflags,crowdsec}, # timeline[12], # последние 60 мин, бакеты по 5 мин # top_asn[{asn,name,country,pct}], top_ips[{ip,asn,country,hits,verdict}]} +# +# JSON-схема (--proxyware --json): +# {verdict:"clean|suspect", generated_at, +# hits{processes[],services[],cron[],files[],docker[]}, +# c2_connections[], external_listeners[]} set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -24,7 +30,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" . "$SCRIPT_DIR/lib/common.sh" # ─── Аргументы ──────────────────────────────────────────────────────────────── -JSON=0; HOURS=24; TOPN=15; FOCUS_IP=""; FOCUS_PORT="" +JSON=0; HOURS=24; TOPN=15; FOCUS_IP=""; FOCUS_PORT=""; PROXYAUDIT=0 while [[ $# -gt 0 ]]; do case "$1" in --json) JSON=1 ;; @@ -32,7 +38,8 @@ while [[ $# -gt 0 ]]; do --top) TOPN="${2:-15}"; shift ;; --ip) FOCUS_IP="${2:-}"; shift ;; --port) FOCUS_PORT="${2:-}"; shift ;; - -h|--help) sed -n '2,20p' "$0"; exit 0 ;; + --proxyware) PROXYAUDIT=1 ;; + -h|--help) awk 'NR==1{next} /^#/{sub(/^# ?/,""); print; next} {exit}' "$0"; exit 0 ;; *) err "Неизвестный аргумент: $1"; exit 1 ;; esac shift @@ -296,7 +303,128 @@ port_focus() { else info "ss недоступен"; fi } +# ─── Self-audit: proxyware / residential-proxy (`--proxyware`) ────────────────── +# Read-only доказательство «нода НЕ резидентный прокси и proxyware-SDK на ней нет». +# Для ответа на abuse-тикеты (IPIDEA/PacketSDK/Honeygain/EarnApp/…). Сигнатуры — +# ТОЛЬКО конкретные имена продуктов: generic «proxy» не матчим, иначе ложно +# сработает на самом xray/VLESS (нода легально — прокси-сервер). +PROXYWARE_SIG='packetsdk|ipidea|honeygain|earnapp|peer2profit|proxyrack|iproyal|pawnsapp|repocket|packetstream|traffmonetizer|bitping|earnfm|proxylite|infatica|nodemaven|gaganode|urnetwork' +PROXYWARE_C2='api-seed.packetsdk.net packetsdk.net api-seed.packetsdk.xyz martianinc.co dnsnb8.net' + +# shellcheck disable=SC2009 # нужен match по args, не по имени — pgrep не подходит +pw_proc() { ps -eo pid=,args= 2>/dev/null | grep -iE "$PROXYWARE_SIG" | grep -ivE 'na-report|--proxyware| grep ' | sed 's/^ *//' | head -20; } +pw_services() { { systemctl list-units --type=service --all --no-legend 2>/dev/null | awk '{print $1}' + systemctl list-unit-files --type=service --no-legend 2>/dev/null | awk '{print $1}' + grep -rilE "$PROXYWARE_SIG" /etc/systemd/system /lib/systemd/system /run/systemd/system 2>/dev/null + } | grep -iE "$PROXYWARE_SIG" | sort -u | head -20; } +pw_cron() { { crontab -l 2>/dev/null + while IFS=: read -r _u _; do crontab -l -u "$_u" 2>/dev/null; done < /etc/passwd + grep -rhsiE "$PROXYWARE_SIG" /etc/crontab /etc/cron.d /etc/cron.* /var/spool/cron 2>/dev/null + } | grep -iE "$PROXYWARE_SIG" | sort -u | head -20; } +pw_files() { timeout 20 find /usr/local /opt /usr/bin /usr/sbin /root /home -maxdepth 4 -xdev -type f 2>/dev/null \ + | grep -iE "$PROXYWARE_SIG" | head -20; } +pw_docker() { command -v docker >/dev/null 2>&1 || return 0 + docker ps -a --format '{{.Names}} {{.Image}}' 2>/dev/null | grep -iE "$PROXYWARE_SIG" | head -20; } +# Коннекты к C2: резолвим домены → IP, ищем среди ESTABLISHED peer-адресов. +pw_c2conn() { + local ips + ips="$(for d in $PROXYWARE_C2; do getent hosts "$d" 2>/dev/null | awk '{print $1}'; done | sort -u)" + [[ -z "$ips" ]] && return 0 + ss -tnH state established 2>/dev/null | awk '{print $4}' \ + | sed -E 's/:[0-9]+$//; s/^\[//; s/\]$//' \ + | grep -Fxf <(printf '%s\n' "$ips") 2>/dev/null | sort -u | head -20 +} +# Внешне-доступные listener'ы — контекст: оператор сверяет, что незнакомых нет. +pw_listeners() { + ss -tlnHp 2>/dev/null | awk ' + $4 ~ /(^0\.0\.0\.0:)|(^\*:)|(^\[::\]:)|(^\[::ffff:0\.0\.0\.0\]:)/ { + p=$0; sub(/.*users:\(\(\"/,"",p); sub(/\".*/,"",p); if(p==$0)p="?"; + print $4" ["p"]" }' | sort -u | head -30 +} + +# stdin (строка = элемент) → JSON-массив ["a","b"] (json_str экранирует). +_json_arr() { + local out="" line + while IFS= read -r line; do + [[ -z "$line" ]] && continue + out+="${out:+,}\"$(json_str "$line")\"" + done + printf '[%s]' "$out" +} + +# Секция аудита: печатает и возвращает 1, если есть хиты. +pw_section() { + local label="$1" data="$2" + if [[ -n "$data" ]]; then + status_line FAIL "$label:"; printf '%s\n' "$data" | sed 's/^/ /'; return 1 + fi + status_line OK "$label — чисто"; return 0 +} + +proxyware_audit() { + clear 2>/dev/null || true + printf "%b" "$BOLD" + cat <<'B' + ┌────────────────────────────────────────────┐ + │ 🕵 node-accelerator — proxyware self-audit │ + └────────────────────────────────────────────┘ +B + printf "%b" "$NC" + info "Read-only проверка на residential-proxy / bandwidth-SDK (для abuse-тикетов)." + + local hit=0 + title "Сигнатуры известного proxyware" + pw_section "процессы" "$(pw_proc)" || hit=1 + pw_section "systemd-юниты" "$(pw_services)" || hit=1 + pw_section "cron" "$(pw_cron)" || hit=1 + pw_section "файлы" "$(pw_files)" || hit=1 + pw_section "docker-образы" "$(pw_docker)" || hit=1 + + title "Соединения к C2-инфраструктуре proxyware" + local c2; c2="$(pw_c2conn)" + if [[ -n "$c2" ]]; then + status_line FAIL "установленные коннекты к C2:"; printf '%s\n' "$c2" | sed 's/^/ /'; hit=1 + else + status_line OK "активных соединений к C2 нет (проверено: $PROXYWARE_C2)" + fi + + title "Внешне-доступные слушающие порты (контекст)" + local lst; lst="$(pw_listeners)" + if [[ -n "$lst" ]]; then printf '%s\n' "$lst" | sed 's/^/ • /'; else info "внешних listener'ов нет"; fi + info "для VPN-ноды :443 и порт входа моста — норма; убедись, что незнакомых служб нет" + + hr + if [[ "$hit" -eq 0 ]]; then + ok "ВЕРДИКТ: признаков proxyware / residential-proxy НЕ найдено — нода чиста." + else + err "ВЕРДИКТ: есть индикаторы proxyware (см. ✘ выше) — разобраться вручную." + fi + hr +} + +proxyware_json() { + local proc svc cron files dock c2 lst verdict + proc="$(pw_proc)"; svc="$(pw_services)"; cron="$(pw_cron)" + files="$(pw_files)"; dock="$(pw_docker)"; c2="$(pw_c2conn)"; lst="$(pw_listeners)" + if [[ -n "$proc$svc$cron$files$dock$c2" ]]; then verdict="suspect"; else verdict="clean"; fi + printf '{' + printf '"verdict":"%s","generated_at":%s,' "$verdict" "$NOW" + printf '"hits":{"processes":%s,"services":%s,"cron":%s,"files":%s,"docker":%s},' \ + "$(printf '%s\n' "$proc" | _json_arr)" \ + "$(printf '%s\n' "$svc" | _json_arr)" \ + "$(printf '%s\n' "$cron" | _json_arr)" \ + "$(printf '%s\n' "$files" | _json_arr)" \ + "$(printf '%s\n' "$dock" | _json_arr)" + printf '"c2_connections":%s,"external_listeners":%s}\n' \ + "$(printf '%s\n' "$c2" | _json_arr)" \ + "$(printf '%s\n' "$lst" | _json_arr)" +} + # ─── Диспетч ──────────────────────────────────────────────────────────────────── +if [[ "$PROXYAUDIT" == "1" ]]; then + if [[ "$JSON" == "1" ]]; then proxyware_json; else proxyware_audit; fi + exit 0 +fi if [[ "$JSON" == "1" ]]; then emit_json; exit 0; fi [[ -n "$FOCUS_PORT" ]] && { port_focus; exit 0; } [[ -n "$FOCUS_IP" ]] && { focus_ip; exit 0; } From c59d4ec4063ecf6ba9795855b5e6555186b37ac6 Mon Sep 17 00:00:00 2001 From: jestivald Date: Sat, 27 Jun 2026 00:13:44 +0300 Subject: [PATCH 2/2] v3.5: CT_EST_TIMEOUT tunable + SYNPROXY relay-redundancy note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - optimize: nf_conntrack_tcp_timeout_established вынесен в ENV CT_EST_TIMEOUT (default 7440 без изменения поведения; валидируется 120..432000; персистится в optimize.conf — ре-ран без ENV не сбрасывает). idle-туннели/мосты могут ↑. - protect/README: SYNPROXY задокументирован как избыточный на VPN-relay (syncookies + per-IP ct-лимиты уже дают анти-спуф); остаётся opt-in/default-off. - CHANGELOG: v3.5. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 19 +++++++++++++++++++ README.md | 7 ++++--- scripts/optimize.sh | 16 +++++++++++----- scripts/protect.sh | 8 ++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08dc613..78f4444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v3.5 — 2026-06-27 + +Мелкий тюнинг-апдейт: одна новая ручка + уточнение по SYNPROXY. Поведение по умолчанию НЕ меняется. + +### ⚙️ optimize.sh — `CT_EST_TIMEOUT` (новая ENV-ручка) +- `nf_conntrack_tcp_timeout_established` вынесен из хардкода в ENV `CT_EST_TIMEOUT` (секунды, + дефолт `7440` — поведение прежнее). Валидируется (целое 120…432000, иначе дефолт) и + **персистится** в `optimize.conf` (ре-ран без ENV не сбрасывает значение). +- Зачем: idle-туннели/мосты без частого keepalive можно поднять (напр. `14400`=4ч), не патча + скрипт; ноды под флуд-профилем — оставить дефолт (агрессивнее реклеймит брошенные / + connect-and-hold established, не раздувая conntrack-таблицу). + +### 📝 SYNPROXY — уточнение (код не меняется) +- Зафиксирован вывод: на VPN-relay SYNPROXY **избыточен** — анти-спуф уже дают + `tcp_syncookies=1` + per-IP ct-лимиты, а издержки (`be_liberal`, поломка TFO на защищённых + портах, per-packet overhead) не оправданы; против connect-and-hold / PPS-флуда он не + помогает. Остаётся **opt-in, default off**; включать только под подтверждённый спуфнутый + SYN-флуд. (Комментарий в `protect.sh` + строка в README.) + ## v3.4 — 2026-06-24 Self-audit на residential-proxy / proxyware-SDK (read-only) — доказательная база для ответа на abuse-тикеты хостеров. diff --git a/README.md b/README.md index 9a4e37c..9cda484 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ - **conntrack phantom-eviction** *(opt-in)* — защита от **distributed connect-and-hold** по живым сокетам (`conntrack ≫ ss`), CGNAT-safe, observe-режим по умолчанию. - **SYNPROXY** *(opt-in, done-right)* — `notrack` только host-local (`fib daddr type local`, - не ломает Docker/транзит), verify ядра/модуля, fail-loud при недоступности. + не ломает Docker/транзит), verify ядра/модуля, fail-loud при недоступности. На VPN-relay + обычно избыточен (syncookies + per-IP ct-лимиты уже дают анти-спуф) — **default off**. - **статич-блоклисты** *(opt-in)* — Spamhaus DROP + FireHOL L1 (+ Tor), bogon-фильтр, таймер. - **Remnawave fleet auto-sync** *(opt-in)* — ноды флота сами держат IP друг друга в whitelist (с панели). - **ICMP rate-limit** (пинг жив, флуд режется). @@ -104,7 +105,7 @@ curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF | `ENABLE_PORTSCAN_BAN` | `1` | автобан за скан закрытых портов | | `PORTSCAN_RATE`/`PORTSCAN_BURST` | `15`/`30` | порог скана (SYN на закрытые порты/мин, per-IP) до бана — ниже порога просто дроп, без бана | | `ENABLE_CROWDSEC` | `1` | ставить CrowdSec + bouncer | -| `ENABLE_SYNPROXY` | `0` | nft synproxy на сервисные порты (advanced) | +| `ENABLE_SYNPROXY` | `0` | nft synproxy на сервисные порты (advanced). На VPN-relay избыточен — syncookies + per-IP ct-лимиты уже дают анти-спуф; включать под подтверждённый спуф-SYN-флуд | | `CROWDSEC_ENROLL_KEY` | _пусто_ | enroll в CrowdSec Console | | `SAFETY_DELAY` | `300` | сек до авто-сброса правил, если не подтвердить SSH | | `DRY_RUN` | `0` | `1` — только сгенерировать + `nft -c`, не применять | @@ -121,7 +122,7 @@ curl -fsSL "https://raw.githubusercontent.com/jestivald/node-accelerator/$NA_REF | `NA_CTG_ENFORCE` | `0` | `0` — observe (только лог), `1` — эвиктить фантомы | | `NA_CTG_PHANTOM_MIN` / `NA_CTG_LIVE_FLOOR` | `4000` / `2` | порог conntrack-холдера / порог живых сокетов | -`optimize.sh`: `ENABLE_XANMOD=1`, `XANMOD_FLAVOR=lts|main|edge|rt`, `XANMOD_PKG=...`, `REMNAWAVE_SWAP_SIZE=2G`, `TCP_ECN_MODE=2` (0/1/2), `DISABLE_TFO=0`, `ENABLE_MSS_CLAMP=0` (для routed/WireGuard-нод), `SETUP_NO_ZRAM=0`. Буферы/conntrack/somaxconn — **tier-aware** (масштаб от RAM). +`optimize.sh`: `ENABLE_XANMOD=1`, `XANMOD_FLAVOR=lts|main|edge|rt`, `XANMOD_PKG=...`, `REMNAWAVE_SWAP_SIZE=2G`, `TCP_ECN_MODE=2` (0/1/2), `DISABLE_TFO=0`, `CT_EST_TIMEOUT=7440` (conntrack established-timeout, сек; ↑ напр. до `14400` для idle-туннелей/мостов без частого keepalive), `ENABLE_MSS_CLAMP=0` (для routed/WireGuard-нод), `SETUP_NO_ZRAM=0`. Буферы/conntrack/somaxconn — **tier-aware** (масштаб от RAM). `XANMOD_PROBE=1` — проверить, что репозиторий+ключ+сборка ядра резолвятся на этой ОС, **без установки** (для CI и быстрой проверки совместимости). --- diff --git a/scripts/optimize.sh b/scripts/optimize.sh index 46bc4cf..2e568cb 100755 --- a/scripts/optimize.sh +++ b/scripts/optimize.sh @@ -237,6 +237,12 @@ TCP_ECN_MODE="${TCP_ECN_MODE:-2}"; [[ "$TCP_ECN_MODE" =~ ^[012]$ ]] || TCP_ECN_M # TFO можно выключить (DISABLE_TFO=1): часть сетей режет SYN с TFO-payload. DISABLE_TFO="${DISABLE_TFO:-0}"; [[ "$DISABLE_TFO" =~ ^[01]$ ]] || DISABLE_TFO=0 TFO_VAL=3; [[ "$DISABLE_TFO" == "1" ]] && TFO_VAL=0 +# nf_conntrack_tcp_timeout_established: за сколько «established» без трафика реклеймится. +# Дефолт 7440с (124 мин) — выше keepalive живого VLESS-мультиплекса, но много короче +# стокового 5-суточного потолка ядра: брошенные / connect-and-hold сессии не раздувают +# таблицу. idle-туннели/мосты без частого keepalive могут поднять (напр. 14400=4ч). +CT_EST_TIMEOUT="${CT_EST_TIMEOUT:-7440}" +[[ "$CT_EST_TIMEOUT" =~ ^[0-9]+$ ]] && [[ "$CT_EST_TIMEOUT" -ge 120 && "$CT_EST_TIMEOUT" -le 432000 ]] || CT_EST_TIMEOUT=7440 # overcommit: на tier1 (≤1.2G) heuristic (0) безопаснее агрессивного always-overcommit (1). OVERCOMMIT=1; [[ "$TIER" -le 1 ]] && OVERCOMMIT=0 info "RAM-tier $TIER (~${_mem_mb} MB): sock_max=$SOCK_MAX def=$SOCK_DEF ecn=$TCP_ECN_MODE tfo=$TFO_VAL overcommit=$OVERCOMMIT" @@ -298,10 +304,10 @@ net.ipv4.ip_forward = 1 net.ipv4.conf.all.forwarding = 1 net.ipv6.conf.all.forwarding = 1 -# --- Conntrack: timeout здесь; ёмкость (max/buckets) — отдельным drop-in ниже, -# масштабируется от RAM (99-node-accelerator-conntrack.conf), чтобы мелкая VPS под -# флудом не словила OOM в ядре --- -net.netfilter.nf_conntrack_tcp_timeout_established = 7440 +# --- Conntrack: timeout здесь (tunable CT_EST_TIMEOUT); ёмкость (max/buckets) — +# отдельным drop-in ниже, масштабируется от RAM (99-node-accelerator-conntrack.conf), +# чтобы мелкая VPS под флудом не словила OOM в ядре --- +net.netfilter.nf_conntrack_tcp_timeout_established = $CT_EST_TIMEOUT # --- SYN flood (ядро) --- net.ipv4.tcp_syncookies = 1 @@ -643,7 +649,7 @@ EOF # Персист конфига оптимизатора → ре-ран без ENV не сбрасывает выбор сборки/флейвора. save_conf "$CONF_DIR/optimize.conf" \ ENABLE_XANMOD XANMOD_FLAVOR REMNAWAVE_SWAP_SIZE \ - DISABLE_TFO TCP_ECN_MODE ENABLE_MSS_CLAMP SETUP_NO_ZRAM + DISABLE_TFO TCP_ECN_MODE ENABLE_MSS_CLAMP SETUP_NO_ZRAM CT_EST_TIMEOUT title "ГОТОВО" ok "Оптимизатор применён." diff --git a/scripts/protect.sh b/scripts/protect.sh index d94f57b..056e188 100755 --- a/scripts/protect.sh +++ b/scripts/protect.sh @@ -277,6 +277,14 @@ if [[ "$ENABLE_PORTSCAN_BAN" == "1" ]]; then fi # ─── SYNPROXY (опционально, done-right) ────────────────────────────────────── +# ⚠️ На VPN-relay (профиль connect-and-hold / PPS-флуд) SYNPROXY обычно ИЗБЫТОЧЕН: +# его единственный реальный плюс — анти-спуф SYN — уже закрыт tcp_syncookies=1 + +# per-IP ct-лимитами (CONN_LIMIT/SYN_RATE), а издержки (обязательный be_liberal=1, +# per-packet overhead, поломка TFO на защищённых портах) не оправданы. Против самого +# распространённого вектора (connect-and-hold / реальный PPS) он не помогает вовсе. +# Поэтому default OFF; включать ТОЛЬКО под подтверждённый спуфнутый SYN-флуд. Оставлен +# opt-in для не-relay сценариев (голый L4-фронт без syncookies-достаточности). +# # notrack ТОЛЬКО для трафика к самому хосту (fib daddr type local): иначе правило в # prerouting цепляет conntrack/NAT ТРАНЗИТА (Docker-контейнер панели → удалённая нода) # и ломает его. Требует ядро ≥5.14 + модуль nf_synproxy. Запрошен, но недоступен →