From 9acd54ff2a9cfd99d8a15284c5d45905c7d6fdc0 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 25 Jul 2026 12:54:15 +0300 Subject: [PATCH 01/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 62990c6..b8ed701 100644 --- a/domains.txt +++ b/domains.txt @@ -193,4 +193,5 @@ netlify.com netlifydns.com netlifyusercontent.com www.ubuntu-touch.io -ubports.com \ No newline at end of file +ubports.com +4pda.to \ No newline at end of file From a3e755c0a497004b8ce96bac6606644688c5a13e Mon Sep 17 00:00:00 2001 From: goodvin Date: Tue, 28 Jul 2026 00:50:44 +0300 Subject: [PATCH 02/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index b8ed701..ab8fa96 100644 --- a/domains.txt +++ b/domains.txt @@ -194,4 +194,5 @@ netlifydns.com netlifyusercontent.com www.ubuntu-touch.io ubports.com -4pda.to \ No newline at end of file +4pda.to +www.patreon.com \ No newline at end of file From 20c484dc59aaf22b04b6caeb96ed95fa5e7f17e2 Mon Sep 17 00:00:00 2001 From: goodvin Date: Fri, 31 Jul 2026 16:54:09 +0300 Subject: [PATCH 03/20] Update domains.txt --- domains.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index ab8fa96..6412acc 100644 --- a/domains.txt +++ b/domains.txt @@ -195,4 +195,6 @@ netlifyusercontent.com www.ubuntu-touch.io ubports.com 4pda.to -www.patreon.com \ No newline at end of file +www.patreon.com +registry-1.docker.io +docker.io \ No newline at end of file From 0865df3de3eb3d380d1af3978fe18d46779c025d Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 1 Aug 2026 14:20:27 +0300 Subject: [PATCH 04/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 6412acc..6aa6cb2 100644 --- a/domains.txt +++ b/domains.txt @@ -197,4 +197,5 @@ ubports.com 4pda.to www.patreon.com registry-1.docker.io -docker.io \ No newline at end of file +docker.io +downloads.claude.ai \ No newline at end of file From c9c1e8b1eea7d5891e9c416e5d7f1a076b8f9769 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 1 Aug 2026 18:57:11 +0300 Subject: [PATCH 05/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 6aa6cb2..ff4cff3 100644 --- a/domains.txt +++ b/domains.txt @@ -198,4 +198,5 @@ ubports.com www.patreon.com registry-1.docker.io docker.io -downloads.claude.ai \ No newline at end of file +downloads.claude.ai +claude.com \ No newline at end of file From 38d5bdf0ceb59fbbc986cac098470b736c3e9c8c Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 1 Aug 2026 22:31:10 +0300 Subject: [PATCH 06/20] Update domains.txt --- domains.txt | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index ff4cff3..ec08c31 100644 --- a/domains.txt +++ b/domains.txt @@ -199,4 +199,71 @@ www.patreon.com registry-1.docker.io docker.io downloads.claude.ai -claude.com \ No newline at end of file +claude.com +30bf3790.obrut.show +amedia.online +anime1.best +animego.me +animevost.org +api.anilibria.app +api.anilibria.tv +api.apbugall.org +api.bhcesh.me +api.cdnlibs.org +api.filmix.tv +api.g1.plvideo.ru +api.lumex.space +api.luxembd.ws +api.moonanime.art +api.rgshows.me +api.rstprgapipt.com +api.srvkp.com +api.vokino.org +apivb.com +base.ashdi.vip +cdnmovies-stream.online +chaturbate.com +coldcdn.xyz +ee.bongacams.com +embed.su +eneyida.tv +filmixapp.cyou +getstv.com +go.zet-flix.online +hdrezka.me +iframe.video +in.tizam.info +iptv.online +kinobase.org +kinogo.luxury +kinogo.media +kinoukr.com +kinovibe.vip +kodikapi.com +m.hqporner.com +megaoblako.com +moviesapi.club +myfanserial.net +plapi.cdnvideohub.com +player.autoembed.cc +player.smashystream.com +player.videasy.net +portal.lumex.host +redheadsound.studio +rt.pornhub.com +rt.pornhubpremium.com +ru.spankbang.com +ru.xhamster.com +rus.runetki5.com +vibix.org +videoseed.tv +vidfast.pro +vidlink.pro +vidora.su +vidsrc.cc +www.ebalovo.pro +www.eporner.com +www.porntrex.com +www.xnxx.com +www.xvideos.com +www.xvideos.red \ No newline at end of file From 660a31c35e2788f0773916f79ca8e7e9a44b1e7c Mon Sep 17 00:00:00 2001 From: goodvin Date: Wed, 5 Aug 2026 20:58:33 +0300 Subject: [PATCH 07/20] =?UTF-8?q?Add=20warm-nftset.sh=20=E2=80=94=20period?= =?UTF-8?q?ic=20nftset=20warmer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Elements in bbrkn_v4/bbrkn_v6 carry a 1d timeout and are only refreshed when a query reaches the host dnsmasq on :5350, where the nftset= directives are applied. Idle domains — or ones answered from the pihole FTL cache — age out of the sets and their traffic silently falls back to the plain WAN route instead of the tunnel. warm-nftset.sh re-resolves every domain from the deployed 90-nftset.conf (base + related subdomains) directly against 127.0.0.1:5350, bypassing the FTL cache. Sourcing the domain list from the deployed config means there is no second list to keep in sync. Ping is off by default: packets originating on the gateway itself go through OUTPUT, never prerouting_mangle, so they leave unmarked and almost all time out — a misleading signal, not a real failure. Run on the gateway via `make warm` or hourly cron. Measured: 1059 domains in ~1m45s at PARALLEL=8, v4 526->2203, v6 305->1440 elements. Co-Authored-By: Claude Opus 5 --- Makefile | 9 ++- README.md | 52 ++++++++++++++++- scripts/warm-nftset.sh | 128 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 2 deletions(-) create mode 100755 scripts/warm-nftset.sh diff --git a/Makefile b/Makefile index 8758a4d..3d814f7 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ CACHE_TTL_DAYS ?= 15 export NFTSET_CONF RESOLVE_CONF CHROME_SERVER DOMAINS_FILE DNS_SERVER NFTSET_TARGET_DIR CACHE_DIR CACHE_TTL_DAYS # --- Основные цели --- -.PHONY: all clean cache-clean check generate deploy +.PHONY: all clean cache-clean check generate deploy warm all: generate deploy @echo "✅ Конфиги успешно сгенерированы и задеплоены" @@ -55,3 +55,10 @@ deploy: @echo "🚀 Деплой конфигов в систему" @chmod +x scripts/deploy-to-gateway.sh @./scripts/deploy-to-gateway.sh + +# Прогрев nft-сетов. Запускать на шлюзе — читает задеплоенный конфиг и +# ходит в host-инстанс dnsmasq на 127.0.0.1:5350. +warm: + @echo "🔥 Прогрев nft-сетов" + @chmod +x scripts/warm-nftset.sh + @./scripts/warm-nftset.sh diff --git a/README.md b/README.md index d63fb2b..2c08766 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,8 @@ bbrkn/ ├── Makefile # управление сборкой и деплоем ├── scripts/ │ ├── generate-configs.sh # генерация dnsmasq-конфигов -│ └── deploy-to-gateway.sh # деплой в Pi-hole + rollback +│ ├── deploy-to-gateway.sh # деплой в Pi-hole + rollback +│ └── warm-nftset.sh # прогрев nft-сетов по cron (на шлюзе) └── .forgejo/workflows/deploy.yaml # CI/CD на self-hosted runner ``` @@ -97,6 +98,7 @@ make deploy # только задеплоить в Pi-hole make clean # удалить сгенерированные конфиги из /tmp make cache-clean # очистить кэш API-ответов (.cache/api/) make check # проверить синтаксис domains.txt +make warm # прогреть nft-сеты (запускать на шлюзе) ``` --- @@ -173,6 +175,54 @@ CACHE_TTL_DAYS=0 make generate --- +## Прогрев nft-сетов (`warm-nftset.sh`) + +Элементы сетов `bbrkn_v4`/`bbrkn_v6` живут по `timeout` (сейчас `1d`) и обновляются **только** когда запрос доходит до host-инстанса `:5350` — именно там применяются директивы `nftset=`. Если домен долго никто не открывал (или ответ отдал кэш pihole FTL), элемент истекает, и трафик к этому домену уходит мимо туннеля напрямую в WAN. + +`scripts/warm-nftset.sh` закрывает эту дыру: заново резолвит **все** домены из задеплоенного `90-nftset.conf` (база + связанные субдомены) **напрямую** через `dig @127.0.0.1 -p 5350`, минуя кэш FTL, — каждый запрос гарантированно доходит до тегирующего инстанса. + +> **Запускать только на шлюзе.** Инстанс `:5350` слушает `listen-address=127.0.0.1` и с других хостов LAN недоступен. Список доменов берётся из задеплоенного конфига, так что скрипт всегда работает с актуальным после последнего деплоя списком. + +```bash +# разово, вручную +make warm +./scripts/warm-nftset.sh + +# ежечасно по cron (пользователь на шлюзе, sudo без пароля для nft) +crontab -e +17 * * * * /path/to/bbrkn/scripts/warm-nftset.sh >/dev/null 2>&1 +``` + +### Переменные окружения + +| Переменная | По умолчанию | Описание | +|---|---|---| +| `NFTSET_CONF` | `/etc/dnsmasq.d/90-nftset.conf` | Задеплоенный конфиг — источник списка доменов | +| `RESOLVER` / `RESOLVER_PORT` | `127.0.0.1` / `5350` | Host-инстанс dnsmasq, применяющий `nftset=` | +| `PARALLEL` | `8` | Одновременных запросов к `:5350` | +| `DIG_TIMEOUT` / `DIG_TRIES` | `3` / `2` | Таймаут и число попыток на домен | +| `WARM_PING` | `0` | `1` — дополнительно пинговать первый A-адрес (только для отладки) | +| `STATE_DIR` | `~/.local/state/bbrkn` | Лог `warm.log` и lock-файл | +| `LOG_MAX_BYTES` | `4194304` | Порог ротации лога в `warm.log.1` | + +Параллельные запуски блокируются через `flock` — медленный прогон не наложится на следующий по cron. + +### Лог + +``` +2026-08-05T20:46:57+03:00 START domains=1059 conf=/etc/dnsmasq.d/90-nftset.conf v4=526 v6=305 +2026-08-05T20:48:44+03:00 DONE ok=1036 noping=0 dnsfail=23 v4=526->2203 v6=305->1440 +2026-08-05T20:48:44+03:00 dnsfail: azureedge.net cdn-telegram.org kinozal.tv ... +``` + +`v4=X->Y` — число элементов в сете до и после прогона (считается через `sudo -n nft list set`; без прав sudo вместо числа будет `?`, на сам прогрев это не влияет). Элементов больше, чем доменов — у CDN на один домен приходится несколько адресов. Домены в `dnsfail` не резолвятся вообще (мёртвые записи, NXDOMAIN) — кандидаты на удаление из `domains.txt`. + +> `WARM_PING=1` на шлюзе почти всегда даёт `noping`: пакеты, рождённые на самом шлюзе, идут через `OUTPUT` и не проходят цепочку `prerouting_mangle`, то есть уходят в WAN без метки. Это диагностический режим, не показатель проблемы. + +Полный прогон 1059 доменов при `PARALLEL=8` занимает ~1 мин 45 с. + +--- + ## Запуск вручную ```bash diff --git a/scripts/warm-nftset.sh b/scripts/warm-nftset.sh new file mode 100755 index 0000000..52295c6 --- /dev/null +++ b/scripts/warm-nftset.sh @@ -0,0 +1,128 @@ +#!/bin/bash +set -uo pipefail + +# ============================== +# Прогрев nft-сетов (запускать НА ШЛЮЗЕ) +# ============================== +# Заново резолвит все домены, которые dnsmasq помечает директивами nftset=, +# обращаясь напрямую к host-инстансу 127.0.0.1#5350. Прямой запрос обходит +# кэш pihole FTL: запрос, попавший в кэш FTL, до :5350 не доходит и элементы +# в inet filter bbrkn_v4/bbrkn_v6 не обновляет — они молча истекают по timeout +# и домен уходит мимо туннеля. +# +# Источник списка доменов — сам задеплоенный конфиг, поэтому отдельный список +# синхронизировать не нужно: скрипт всегда работает с тем, что реально +# загружен в dnsmasq после последнего успешного деплоя. + +# --- Конфигурация через переменные окружения --- +# Задеплоенный конфиг с директивами nftset= (источник списка доменов) +NFTSET_CONF="${NFTSET_CONF:-/etc/dnsmasq.d/90-nftset.conf}" +# Host-инстанс dnsmasq, который применяет nftset= +RESOLVER="${RESOLVER:-127.0.0.1}" +RESOLVER_PORT="${RESOLVER_PORT:-5350}" + +# Состояние и лог +STATE_DIR="${STATE_DIR:-${HOME}/.local/state/bbrkn}" +LOG="${LOG:-${STATE_DIR}/warm.log}" +LOCK="${LOCK:-${STATE_DIR}/warm.lock}" +LOG_MAX_BYTES="${LOG_MAX_BYTES:-4194304}" # ротация лога, 4 МиБ + +# Параметры опроса +PARALLEL="${PARALLEL:-8}" # одновременных запросов к :5350 +DIG_TIMEOUT="${DIG_TIMEOUT:-3}" # секунд на попытку +DIG_TRIES="${DIG_TRIES:-2}" + +# Пинг выключен по умолчанию: пакеты, рождённые на самом шлюзе, идут через +# OUTPUT и не проходят цепочку prerouting_mangle — уходят в обычный WAN без +# метки и почти все отваливаются по таймауту. Включать только для отладки. +WARM_PING="${WARM_PING:-0}" +PING_TIMEOUT="${PING_TIMEOUT:-2}" + +NFT="${NFT:-/usr/sbin/nft}" + +mkdir -p "$STATE_DIR" + +# Один инстанс: медленный прогон не должен наложиться на следующий по cron. +exec 9>"$LOCK" +if ! flock -n 9; then + echo "$(date -Is) SKIP предыдущий запуск ещё выполняется" >>"$LOG" + exit 0 +fi + +log() { echo "$(date -Is) $*" >>"$LOG"; } + +# Количество элементов в сете. У каждого элемента есть timeout, поэтому +# "expires" встречается ровно один раз на элемент. Ошибка здесь не фатальна — +# это только метрика прогресса. +set_count() { + sudo -n "$NFT" list set inet filter "$1" 2>/dev/null | grep -c 'expires' || echo "?" +} + +if [ ! -r "$NFTSET_CONF" ]; then + log "FATAL не читается $NFTSET_CONF" + echo "Ошибка: не читается $NFTSET_CONF (скрипт запускается на шлюзе)" >&2 + exit 1 +fi + +mapfile -t HOSTS < <(sed -n 's|^nftset=/\([^/]*\)/.*|\1|p' "$NFTSET_CONF" | sort -u) +if [ "${#HOSTS[@]}" -eq 0 ]; then + log "FATAL в $NFTSET_CONF не найдено ни одной директивы nftset=" + echo "Ошибка: в $NFTSET_CONF не найдено ни одной директивы nftset=" >&2 + exit 1 +fi + +BEFORE_V4=$(set_count bbrkn_v4) +BEFORE_V6=$(set_count bbrkn_v6) +log "START domains=${#HOSTS[@]} conf=$NFTSET_CONF v4=$BEFORE_V4 v6=$BEFORE_V6" + +warm_one() { + local host="$1" a aaaa + a=$(dig +short +timeout="$DIG_TIMEOUT" +tries="$DIG_TRIES" \ + @"$RESOLVER" -p "$RESOLVER_PORT" A "$host" 2>/dev/null \ + | grep -E '^[0-9]+\.' | head -1) + aaaa=$(dig +short +timeout="$DIG_TIMEOUT" +tries="$DIG_TRIES" \ + @"$RESOLVER" -p "$RESOLVER_PORT" AAAA "$host" 2>/dev/null \ + | grep -E '^[0-9a-fA-F]*:' | head -1) + + if [ -z "$a" ] && [ -z "$aaaa" ]; then + echo "dnsfail $host" + return + fi + + # Пинг только подтверждает проходимость маршрута; в сет IP попал уже на + # шаге резолва выше. + if [ "$WARM_PING" = "1" ] && [ -n "$a" ]; then + if ping -n -q -c1 -W "$PING_TIMEOUT" "$a" >/dev/null 2>&1; then + echo "ok $host $a" + else + echo "noping $host $a" + fi + else + echo "ok $host ${a:-$aaaa}" + fi +} +export -f warm_one +export RESOLVER RESOLVER_PORT DIG_TIMEOUT DIG_TRIES WARM_PING PING_TIMEOUT + +RESULTS="$(printf '%s\n' "${HOSTS[@]}" \ + | xargs -r -P "$PARALLEL" -I{} bash -c 'warm_one "$@"' _ {})" + +OK=$(grep -c '^ok ' <<<"$RESULTS" || true) +NOPING=$(grep -c '^noping ' <<<"$RESULTS" || true) +DNSFAIL=$(grep -c '^dnsfail ' <<<"$RESULTS" || true) + +AFTER_V4=$(set_count bbrkn_v4) +AFTER_V6=$(set_count bbrkn_v6) +log "DONE ok=$OK noping=$NOPING dnsfail=$DNSFAIL v4=${BEFORE_V4}->${AFTER_V4} v6=${BEFORE_V6}->${AFTER_V6}" +if [ "$DNSFAIL" -gt 0 ]; then + log "dnsfail: $(grep '^dnsfail ' <<<"$RESULTS" | cut -d' ' -f2 | tr '\n' ' ')" +fi + +echo "✔ Прогрето доменов: $OK, не резолвится: $DNSFAIL; bbrkn_v4 ${BEFORE_V4}->${AFTER_V4}, bbrkn_v6 ${BEFORE_V6}->${AFTER_V6}" + +# Ротация лога +if [ -f "$LOG" ] && [ "$(stat -c%s "$LOG")" -gt "$LOG_MAX_BYTES" ]; then + mv -f "$LOG" "${LOG}.1" +fi + +exit 0 From 0ac0aa0cc4b78801b064649351a51c07e43ef200 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 8 Aug 2026 12:24:13 +0300 Subject: [PATCH 08/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index ec08c31..f6f2f5c 100644 --- a/domains.txt +++ b/domains.txt @@ -266,4 +266,5 @@ www.eporner.com www.porntrex.com www.xnxx.com www.xvideos.com -www.xvideos.red \ No newline at end of file +www.xvideos.red +gemini.google.com \ No newline at end of file From 5643ba5615bc1e5be0ac372e09ca0fc70feb9e2a Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 8 Aug 2026 15:24:08 +0300 Subject: [PATCH 09/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index f6f2f5c..38efac8 100644 --- a/domains.txt +++ b/domains.txt @@ -267,4 +267,5 @@ www.porntrex.com www.xnxx.com www.xvideos.com www.xvideos.red -gemini.google.com \ No newline at end of file +gemini.google.com +nakarte.me \ No newline at end of file From 47f91b519179caca46ffb4e42f0171f41bc76ff4 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sun, 16 Aug 2026 20:49:35 +0300 Subject: [PATCH 10/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 38efac8..d5d07dc 100644 --- a/domains.txt +++ b/domains.txt @@ -268,4 +268,5 @@ www.xnxx.com www.xvideos.com www.xvideos.red gemini.google.com -nakarte.me \ No newline at end of file +nakarte.me +a-api.anthropic.com \ No newline at end of file From 19fbb5c1a25509ee5fcf4191fa6d469049cc0417 Mon Sep 17 00:00:00 2001 From: goodvin Date: Mon, 17 Aug 2026 07:39:55 +0300 Subject: [PATCH 11/20] ADD Google Video domains --- domains.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index d5d07dc..5734b1b 100644 --- a/domains.txt +++ b/domains.txt @@ -269,4 +269,19 @@ www.xvideos.com www.xvideos.red gemini.google.com nakarte.me -a-api.anthropic.com \ No newline at end of file +a-api.anthropic.com +youtube.com +youtu.be +youtube-nocookie.com +ytimg.com +ggpht.com +googlevideo.com +gvt1.com +gvt2.com +gvt3.com +googleusercontent.com +gstatic.com +jnn-pa.googleapis.com +play.googleapis.com +youtubei.googleapis.com +googleapis.com \ No newline at end of file From ae8295546bacbdf9a38b7ebc09ea388c28bb3265 Mon Sep 17 00:00:00 2001 From: goodvin Date: Fri, 28 Aug 2026 20:59:23 +0300 Subject: [PATCH 12/20] Update domains.txt --- domains.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 5734b1b..f2ddaa5 100644 --- a/domains.txt +++ b/domains.txt @@ -284,4 +284,7 @@ gstatic.com jnn-pa.googleapis.com play.googleapis.com youtubei.googleapis.com -googleapis.com \ No newline at end of file +googleapis.com +api.bambulab.com +bambulab.com +e.bambulab.com \ No newline at end of file From 76a941f0df64fb3b12c89513af70cc92ef00034f Mon Sep 17 00:00:00 2001 From: goodvin Date: Sat, 5 Sep 2026 16:53:55 +0300 Subject: [PATCH 13/20] Update domains.txt --- domains.txt | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/domains.txt b/domains.txt index f2ddaa5..2e5d212 100644 --- a/domains.txt +++ b/domains.txt @@ -23,7 +23,6 @@ claude.ai dell.com download.freebsd.org duckduckgo.com -echo.msk.ru fast.com flibusta.is forum.xda-developers.com @@ -63,15 +62,6 @@ seasonvar.ru sketchup.com snap.licdn.com sonic-net.github.io -spoti.fi -spotifycharts.com -spotifycodes.com -spotify.com -spotify.design -spotify-everywhere.com -spotifyforbrands.com -spotifyjobs.com -spotify.link static.cdninstagram.com static.rutracker.cc store.akamai.steamstatic.com @@ -250,23 +240,12 @@ player.smashystream.com player.videasy.net portal.lumex.host redheadsound.studio -rt.pornhub.com -rt.pornhubpremium.com -ru.spankbang.com -ru.xhamster.com -rus.runetki5.com vibix.org videoseed.tv vidfast.pro vidlink.pro vidora.su vidsrc.cc -www.ebalovo.pro -www.eporner.com -www.porntrex.com -www.xnxx.com -www.xvideos.com -www.xvideos.red gemini.google.com nakarte.me a-api.anthropic.com @@ -287,4 +266,8 @@ youtubei.googleapis.com googleapis.com api.bambulab.com bambulab.com -e.bambulab.com \ No newline at end of file +e.bambulab.com +public-cdn.bblmw.com +makerworld.com +makerworld.bblmw.com +bblmw.com \ No newline at end of file From ed9fa34daeff31eff07b680a99beb88088313bd8 Mon Sep 17 00:00:00 2001 From: goodvin Date: Mon, 7 Sep 2026 23:34:23 +0300 Subject: [PATCH 14/20] Update domains.txt --- domains.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 2e5d212..1ee0f16 100644 --- a/domains.txt +++ b/domains.txt @@ -270,4 +270,6 @@ e.bambulab.com public-cdn.bblmw.com makerworld.com makerworld.bblmw.com -bblmw.com \ No newline at end of file +bblmw.com +blog.3mdeb.com +3mdeb.com \ No newline at end of file From 1166f4200e59ec2f6b87d426cf8a758d77160060 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sun, 11 Oct 2026 15:22:41 +0300 Subject: [PATCH 15/20] Update domains.txt --- domains.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/domains.txt b/domains.txt index 1ee0f16..3c99654 100644 --- a/domains.txt +++ b/domains.txt @@ -264,12 +264,4 @@ jnn-pa.googleapis.com play.googleapis.com youtubei.googleapis.com googleapis.com -api.bambulab.com -bambulab.com -e.bambulab.com -public-cdn.bblmw.com -makerworld.com -makerworld.bblmw.com -bblmw.com -blog.3mdeb.com -3mdeb.com \ No newline at end of file +printables.com \ No newline at end of file From 28f5c01dde1d7edb03ea78022a89a4b9353a4e52 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sun, 11 Oct 2026 15:24:04 +0300 Subject: [PATCH 16/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index 3c99654..aaf41d4 100644 --- a/domains.txt +++ b/domains.txt @@ -264,4 +264,5 @@ jnn-pa.googleapis.com play.googleapis.com youtubei.googleapis.com googleapis.com -printables.com \ No newline at end of file +printables.com +files.printables.com \ No newline at end of file From 1dff606865181ede531e6a4c22734328bf05f6d5 Mon Sep 17 00:00:00 2001 From: goodvin Date: Sun, 11 Oct 2026 18:37:04 +0300 Subject: [PATCH 17/20] Update domains.txt --- domains.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index aaf41d4..be5006e 100644 --- a/domains.txt +++ b/domains.txt @@ -265,4 +265,13 @@ play.googleapis.com youtubei.googleapis.com googleapis.com printables.com -files.printables.com \ No newline at end of file +files.printables.com +api.bambulab.com +bambulab.com +e.bambulab.com +public-cdn.bblmw.com +makerworld.com +makerworld.bblmw.com +bblmw.com +blog.3mdeb.com +3mdeb.com \ No newline at end of file From a5ee057c593937022d777538f8153a4c8bd6b16b Mon Sep 17 00:00:00 2001 From: goodvin Date: Tue, 13 Oct 2026 20:54:19 +0300 Subject: [PATCH 18/20] deploy: keep config backups out of dnsmasq conf dirs, cap at 10 Backups were written as .backup. next to the live file. Both dnsmasq instances load every file in their conf dir (the host excludes only .dpkg-*), so each backup was parsed as live config, resurrecting removed domains and piling up one stale copy per deploy. Write them to BACKUP_DIR (/var/backups/bbrkn) instead and keep the newest BACKUP_KEEP (10) per file. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DR7zpSsdX28T4Kn6v4jPSS --- README.md | 2 ++ scripts/deploy-to-gateway.sh | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c08766..a5cb7db 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,8 @@ make warm # прогреть nft-сеты (запускать на шл | `TARGET_DIR` | `/opt/appdata/pihole/etc/dnsmasq.d` | Директория dnsmasq в Pi-hole (для `92-resolve`) | | `DOCKER_CONTAINER` | `pihole` | Имя Docker-контейнера Pi-hole | | `HOST_DNSMASQ_SVC` | `dnsmasq` | systemd-сервис host-инстанса dnsmasq | +| `BACKUP_DIR` | `/var/backups/bbrkn` | Куда класть бэкапы конфигов перед деплоем (НЕ в `dnsmasq.d` — оттуда грузится всё) | +| `BACKUP_KEEP` | `10` | Сколько последних бэкапов хранить на каждый файл | | `NFT_SETS` | `inet filter bbrkn_v4;inet filter bbrkn_v6` | Сеты для flush после деплоя (через `;`) | | `DNS_LISTEN_ADDR` | `127.0.0.1` | Адрес, на котором Pi-hole слушает DNS (:53) | | `DNS_CHECK_DOMAIN` | `google.com` | Домен базового DNS health check | diff --git a/scripts/deploy-to-gateway.sh b/scripts/deploy-to-gateway.sh index beadb0f..89763c7 100644 --- a/scripts/deploy-to-gateway.sh +++ b/scripts/deploy-to-gateway.sh @@ -14,6 +14,11 @@ RESOLVE_CONF="${RESOLVE_CONF:-/tmp/92-resolve-bbrkn.conf}" NFTSET_TARGET_DIR="${NFTSET_TARGET_DIR:-/etc/dnsmasq.d}" RESOLVE_TARGET_DIR="${RESOLVE_TARGET_DIR:-${TARGET_DIR:-/opt/appdata/pihole/etc/dnsmasq.d}}" +# Бэкапы — ВНЕ conf-dir: dnsmasq (и pihole) грузит из dnsmasq.d все файлы, +# кроме исключённых расширений, так что *.backup.* там стал бы живым конфигом. +BACKUP_DIR="${BACKUP_DIR:-/var/backups/bbrkn}" +BACKUP_KEEP="${BACKUP_KEEP:-10}" # сколько последних копий хранить на каждый файл + DOCKER_CONTAINER="${DOCKER_CONTAINER:-pihole}" # systemd-сервис host-инстанса dnsmasq (:5350) HOST_DNSMASQ_SVC="${HOST_DNSMASQ_SVC:-dnsmasq}" @@ -82,16 +87,31 @@ rollback() { # ============================== # Бэкап существующих файлов # ============================== +# Оставляет BACKUP_KEEP самых свежих копий .backup. +# (ts вида YYYYmmdd-HHMMSS сортируется лексикографически). +prune_backups() { # + local name="$1" old + find "$BACKUP_DIR" -maxdepth 1 -type f -name "${name}.backup.*" -printf '%f\n' \ + | sort -r | tail -n +"$((BACKUP_KEEP + 1))" \ + | while read -r old; do + rm -f -- "$BACKUP_DIR/$old" + echo " pruned old backup $old" + done +} + +mkdir -p "$BACKUP_DIR" ts=$(date +%Y%m%d-%H%M%S) if [ -f "$nftset_dest" ]; then - NFTSET_BACKUP="${nftset_dest}.backup.${ts}" + NFTSET_BACKUP="$BACKUP_DIR/${nftset_name}.backup.${ts}" cp "$nftset_dest" "$NFTSET_BACKUP" echo "Backed up $nftset_dest → $NFTSET_BACKUP" + prune_backups "$nftset_name" fi if [ -f "$resolve_dest" ]; then - RESOLVE_BACKUP="${resolve_dest}.backup.${ts}" + RESOLVE_BACKUP="$BACKUP_DIR/${resolve_name}.backup.${ts}" cp "$resolve_dest" "$RESOLVE_BACKUP" echo "Backed up $resolve_dest → $RESOLVE_BACKUP" + prune_backups "$resolve_name" fi # ============================== From 8598a1a48f920005cf9a5d6583fb93d72ea70cd0 Mon Sep 17 00:00:00 2001 From: goodvin Date: Tue, 13 Oct 2026 21:26:49 +0300 Subject: [PATCH 19/20] warm: self-heal wedged dnsmasq, run after deploy, shared state dir dnsmasq adds IPs to an nft set only on a fresh upstream reply; a cache hit never refreshes the set. On Archie the :5350 instance also wedged: TCP child processes first, then the main process, logged 'nftset ... netlink: Error: cache initialization failed: Protocol error' on every add and stopped filling bbrkn_v4/v6 until restarted. warm-nftset.sh now counts those errors for the main PID during the run (journalctl _PID=) and, if any, restarts the service and warms again; exit 1 if it still fails. The state dir defaults to /var/lib/bbrkn so the root cron job and the deploy share one lock (LOCK_WAIT lets deploy wait). deploy-to-gateway.sh runs the warmer right after flushing the sets, since clients keep old IPs cached and would bypass the tunnel until the next fresh resolve. A warm failure only warns; it does not roll back. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01DR7zpSsdX28T4Kn6v4jPSS --- CLAUDE.md | 3 +- README.md | 23 +++++++--- scripts/deploy-to-gateway.sh | 17 ++++++++ scripts/warm-nftset.sh | 82 +++++++++++++++++++++++++++++++----- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 424ec83..9854d86 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ Two bash scripts, driven by env vars (defaults in each script's header, overridd - **Ordering pass** emits each base domain, a `# domain — N subdomains` comment, then its not-yet-written related domains; a related domain is attributed to whichever base comes first alphabetically. A final uniqueness check `exit 5`s (config NOT written) if any domain would appear twice — treat that as a real bug in the ordering logic, not a data issue. - Output lines: `nftset=//$NFTSET_SPEC` (NFTSET_CONF → `90-nftset.conf`; default `NFTSET_SPEC=4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6`) and `server=//$DNS_SERVER` (RESOLVE_CONF → `92-resolve-bbrkn.conf`; `DNS_SERVER` default `127.0.0.1#5350`, the host resolver pihole delegates to). -**`scripts/deploy-to-gateway.sh`** — deploys to **two targets**: `90-nftset.conf` → host `$NFTSET_TARGET_DIR` (`/etc/dnsmasq.d`, the `:5350` instance) and `92-resolve-bbrkn.conf` → pihole `$RESOLVE_TARGET_DIR` (`/opt/appdata/pihole/etc/dnsmasq.d`). Flow: timestamped-backup both → copy → **`systemctl restart $HOST_DNSMASQ_SVC` + `docker restart $DOCKER_CONTAINER`** (a full restart is required — dnsmasq **SIGHUP does not re-read `nftset=`/`server=` directives**, only hosts files) → wait for `:5350` listening + pihole `running` → `nft flush set` on `bbrkn_v4`/`bbrkn_v6` → DNS health check via pihole `:53` → end-to-end nftset-capture check (resolve a bbrkn domain via `:5350`, assert the set populated). **On any failure it calls `rollback()`**, restoring both backups and restarting both instances. Edits must preserve that rollback path — a broken deploy can break DNS for every client on the gateway. +**`scripts/deploy-to-gateway.sh`** — deploys to **two targets**: `90-nftset.conf` → host `$NFTSET_TARGET_DIR` (`/etc/dnsmasq.d`, the `:5350` instance) and `92-resolve-bbrkn.conf` → pihole `$RESOLVE_TARGET_DIR` (`/opt/appdata/pihole/etc/dnsmasq.d`). Flow: timestamped-backup both → copy → **`systemctl restart $HOST_DNSMASQ_SVC` + `docker restart $DOCKER_CONTAINER`** (a full restart is required — dnsmasq **SIGHUP does not re-read `nftset=`/`server=` directives**, only hosts files) → wait for `:5350` listening + pihole `running` → `nft flush set` on `bbrkn_v4`/`bbrkn_v6` → DNS health check via pihole `:53` → end-to-end nftset-capture check (resolve a bbrkn domain via `:5350`, assert the set populated). Then it runs `warm-nftset.sh` (non-fatal) to refill the just-flushed sets. Backups go to `$BACKUP_DIR` (`/var/backups/bbrkn`, newest `BACKUP_KEEP`=10) — never into a dnsmasq conf dir, which would load them as live config. **On any failure it calls `rollback()`**, restoring both backups and restarting both instances. Edits must preserve that rollback path — a broken deploy can break DNS for every client on the gateway. ## Deployment / CI @@ -46,6 +46,7 @@ Two bash scripts, driven by env vars (defaults in each script's header, overridd ## Gotchas +- **dnsmasq only adds IPs to an nft set on a fresh upstream reply**, never on a cache hit — hence `cache-size=0` on the `:5350` instance and the hourly warmer (`/etc/cron.d/bbrkn-warm` → `/usr/local/sbin/bbrkn-warm`, a manually installed copy of `scripts/warm-nftset.sh`). The `:5350` main process can also wedge (`nftset … cache initialization failed: Protocol error` on every add until restart); the warmer detects that via `journalctl _PID=` and restarts it. - `domains.txt` is the primary data file; commits are typically domain additions. Run `make check` after editing it. - `IGNORE_PARTS` is space-separated substrings; any domain (base or related) containing one is dropped. - **`#` gotcha**: `DNS_SERVER=127.0.0.1#5350` — `#` starts a comment in Makefiles (escape as `127.0.0.1\#5350`) and can start one in YAML (quote it). The scripts themselves take the plain value. diff --git a/README.md b/README.md index a5cb7db..57c09d7 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ CACHE_TTL_DAYS=0 make generate 6. Сбрасывает nft-сеты `bbrkn_v4`/`bbrkn_v6` (устаревшие IP иначе висят до timeout 24 ч). 7. Health check: базовый DNS через pihole `:53` + end-to-end проверка nftset-захвата (резолв bbrkn-домена через `:5350` → сет наполнился). 8. При ошибке на шагах 5–7 — **автоматически откатывается**: восстанавливает оба бэкапа и перезапускает оба инстанса со старой конфигурацией. +9. После успешного деплоя запускает `warm-nftset.sh` — сеты только что очищены, а клиенты держат старые IP в кэше. Ошибка прогрева деплой не откатывает (только `WARN`). --- @@ -183,6 +184,10 @@ CACHE_TTL_DAYS=0 make generate `scripts/warm-nftset.sh` закрывает эту дыру: заново резолвит **все** домены из задеплоенного `90-nftset.conf` (база + связанные субдомены) **напрямую** через `dig @127.0.0.1 -p 5350`, минуя кэш FTL, — каждый запрос гарантированно доходит до тегирующего инстанса. +> **У `:5350` должен быть выключен кэш** (`cache-size=0` в `/etc/dnsmasq.conf` на шлюзе). dnsmasq добавляет IP в сет **только при свежем ответе upstream** — ответ из собственного кэша сет не обновляет. Кэширует pihole перед ним. + +**Самовосстановление.** dnsmasq иногда «клинит» на записи в сеты: основной процесс на каждую попытку пишет в журнал `nftset inet filter bbrkn_v4 netlink: Error: cache initialization failed: Protocol error` и больше ничего в сеты не добавляет до рестарта (наблюдалось под нагрузкой; начинается с дочерних TCP-процессов). Скрипт после прогона считает такие ошибки у основного процесса (`journalctl _PID=…`); если они есть — `systemctl restart $HOST_DNSMASQ_SVC` и повторный прогрев. Если не помогло — `exit 1` и `FAIL` в логе. + > **Запускать только на шлюзе.** Инстанс `:5350` слушает `listen-address=127.0.0.1` и с других хостов LAN недоступен. Список доменов берётся из задеплоенного конфига, так что скрипт всегда работает с актуальным после последнего деплоя списком. ```bash @@ -190,11 +195,14 @@ CACHE_TTL_DAYS=0 make generate make warm ./scripts/warm-nftset.sh -# ежечасно по cron (пользователь на шлюзе, sudo без пароля для nft) -crontab -e -17 * * * * /path/to/bbrkn/scripts/warm-nftset.sh >/dev/null 2>&1 +# ежечасно по cron — так стоит на Archie: +install -m 0755 scripts/warm-nftset.sh /usr/local/sbin/bbrkn-warm +cat /etc/cron.d/bbrkn-warm +17 * * * * root /usr/local/sbin/bbrkn-warm >/dev/null 2>&1 ``` +Копия в `/usr/local/sbin` **не обновляется деплоем** — после правок скрипта переустановить вручную. Деплой (CI) запускает скрипт из репо, `STATE_DIR` у них общий, поэтому lock тоже общий. + ### Переменные окружения | Переменная | По умолчанию | Описание | @@ -204,8 +212,11 @@ crontab -e | `PARALLEL` | `8` | Одновременных запросов к `:5350` | | `DIG_TIMEOUT` / `DIG_TRIES` | `3` / `2` | Таймаут и число попыток на домен | | `WARM_PING` | `0` | `1` — дополнительно пинговать первый A-адрес (только для отладки) | -| `STATE_DIR` | `~/.local/state/bbrkn` | Лог `warm.log` и lock-файл | +| `STATE_DIR` | `/var/lib/bbrkn` | Лог `warm.log` и lock-файл (общие для cron и деплоя) | | `LOG_MAX_BYTES` | `4194304` | Порог ротации лога в `warm.log.1` | +| `LOCK_WAIT` | `0` | Секунд ждать идущий прогон (`0` — сразу выйти с `SKIP`; деплой ждёт `WARM_LOCK_WAIT=300`) | +| `HOST_DNSMASQ_SVC` | `dnsmasq` | systemd-сервис `:5350` — для детектора заклинивания и рестарта | +| `SELF_HEAL` | `1` | `0` — только сообщать о заклинивании, не перезапускать dnsmasq | Параллельные запуски блокируются через `flock` — медленный прогон не наложится на следующий по cron. @@ -213,11 +224,11 @@ crontab -e ``` 2026-08-05T20:46:57+03:00 START domains=1059 conf=/etc/dnsmasq.d/90-nftset.conf v4=526 v6=305 -2026-08-05T20:48:44+03:00 DONE ok=1036 noping=0 dnsfail=23 v4=526->2203 v6=305->1440 +2026-08-05T20:48:44+03:00 DONE ok=1036 noping=0 dnsfail=23 nftset_errors=0 healed=0 v4=526->2203 v6=305->1440 2026-08-05T20:48:44+03:00 dnsfail: azureedge.net cdn-telegram.org kinozal.tv ... ``` -`v4=X->Y` — число элементов в сете до и после прогона (считается через `sudo -n nft list set`; без прав sudo вместо числа будет `?`, на сам прогрев это не влияет). Элементов больше, чем доменов — у CDN на один домен приходится несколько адресов. Домены в `dnsfail` не резолвятся вообще (мёртвые записи, NXDOMAIN) — кандидаты на удаление из `domains.txt`. +`v4=X->Y` — число элементов в сете до и после прогона (считается через `nft list set`; не под root — через `sudo -n`, без прав будет `0`, на сам прогрев это не влияет). `nftset_errors` — ошибки записи в сет у основного процесса dnsmasq, `healed=1` — был рестарт. Элементов больше, чем доменов — у CDN на один домен приходится несколько адресов. Домены в `dnsfail` не резолвятся вообще (мёртвые записи, NXDOMAIN) — кандидаты на удаление из `domains.txt`. > `WARM_PING=1` на шлюзе почти всегда даёт `noping`: пакеты, рождённые на самом шлюзе, идут через `OUTPUT` и не проходят цепочку `prerouting_mangle`, то есть уходят в WAN без метки. Это диагностический режим, не показатель проблемы. diff --git a/scripts/deploy-to-gateway.sh b/scripts/deploy-to-gateway.sh index 89763c7..ca82ac9 100644 --- a/scripts/deploy-to-gateway.sh +++ b/scripts/deploy-to-gateway.sh @@ -232,6 +232,23 @@ else fi fi +# ============================== +# Прогрев nft-сетов: после flush сеты пусты, а у клиентов в кэше (Edge, +# Windows) остаются старые IP — без прогрева их трафик идёт мимо туннеля до +# первого свежего резолва. Ошибка прогрева деплой не откатывает: конфиги уже +# проверены, а cron-прогрев повторит попытку. +# ============================== +WARM_SCRIPT="${WARM_SCRIPT:-$(dirname "$0")/warm-nftset.sh}" +if [ -f "$WARM_SCRIPT" ]; then + echo "Warming nft sets ($WARM_SCRIPT)..." + if ! NFTSET_CONF="$nftset_dest" HOST_DNSMASQ_SVC="$HOST_DNSMASQ_SVC" \ + LOCK_WAIT="${WARM_LOCK_WAIT:-300}" bash "$WARM_SCRIPT"; then + echo " WARN: warm failed — sets will refill on client queries / hourly cron" + fi +else + echo " WARN: $WARM_SCRIPT not found, skipping warm" +fi + echo "Deployment completed successfully" DOMAIN_COUNT=$(grep -c '^nftset=' "$nftset_dest" || echo "0") echo "Applied nftset configuration for $DOMAIN_COUNT domain directives" diff --git a/scripts/warm-nftset.sh b/scripts/warm-nftset.sh index 52295c6..8e26669 100755 --- a/scripts/warm-nftset.sh +++ b/scripts/warm-nftset.sh @@ -13,6 +13,13 @@ set -uo pipefail # Источник списка доменов — сам задеплоенный конфиг, поэтому отдельный список # синхронизировать не нужно: скрипт всегда работает с тем, что реально # загружен в dnsmasq после последнего успешного деплоя. +# +# Самовосстановление: dnsmasq добавляет IP в сет только при свежем ответе +# upstream и иногда "клинит" — основной процесс начинает отвечать на каждую +# попытку записи "nftset ... netlink: Error: cache initialization failed: +# Protocol error" и больше сеты не наполняет до рестарта. Если за прогон в +# журнале основного процесса появилась такая ошибка — рестартуем dnsmasq и +# прогреваем ещё раз. # --- Конфигурация через переменные окружения --- # Задеплоенный конфиг с директивами nftset= (источник списка доменов) @@ -21,11 +28,12 @@ NFTSET_CONF="${NFTSET_CONF:-/etc/dnsmasq.d/90-nftset.conf}" RESOLVER="${RESOLVER:-127.0.0.1}" RESOLVER_PORT="${RESOLVER_PORT:-5350}" -# Состояние и лог -STATE_DIR="${STATE_DIR:-${HOME}/.local/state/bbrkn}" +# Состояние и лог. Общий каталог для cron и деплоя — lock у них один. +STATE_DIR="${STATE_DIR:-/var/lib/bbrkn}" LOG="${LOG:-${STATE_DIR}/warm.log}" LOCK="${LOCK:-${STATE_DIR}/warm.lock}" LOG_MAX_BYTES="${LOG_MAX_BYTES:-4194304}" # ротация лога, 4 МиБ +LOCK_WAIT="${LOCK_WAIT:-0}" # секунд ждать чужой прогон (0 — сразу выйти) # Параметры опроса PARALLEL="${PARALLEL:-8}" # одновременных запросов к :5350 @@ -40,11 +48,19 @@ PING_TIMEOUT="${PING_TIMEOUT:-2}" NFT="${NFT:-/usr/sbin/nft}" +# systemd-сервис host-инстанса dnsmasq (:5350) и самовосстановление +HOST_DNSMASQ_SVC="${HOST_DNSMASQ_SVC:-dnsmasq}" +SELF_HEAL="${SELF_HEAL:-1}" + +# Под root (cron.d, CI) sudo не нужен +SUDO="" +[ "$(id -u)" -eq 0 ] || SUDO="sudo -n" + mkdir -p "$STATE_DIR" # Один инстанс: медленный прогон не должен наложиться на следующий по cron. exec 9>"$LOCK" -if ! flock -n 9; then +if ! flock -w "$LOCK_WAIT" 9; then echo "$(date -Is) SKIP предыдущий запуск ещё выполняется" >>"$LOG" exit 0 fi @@ -55,7 +71,19 @@ log() { echo "$(date -Is) $*" >>"$LOG"; } # "expires" встречается ровно один раз на элемент. Ошибка здесь не фатальна — # это только метрика прогресса. set_count() { - sudo -n "$NFT" list set inet filter "$1" 2>/dev/null | grep -c 'expires' || echo "?" + $SUDO "$NFT" list set inet filter "$1" 2>/dev/null | grep -c 'expires' || true +} + +main_pid() { + systemctl show -p MainPID --value "$HOST_DNSMASQ_SVC" 2>/dev/null +} + +# Ошибки записи в nft-сет у основного процесса dnsmasq с момента . +# Ошибки дочерних (TCP) процессов не считаем — они разовые; заклинивание +# видно именно по основному. +nftset_errors() { # + $SUDO journalctl -q --no-pager _PID="$1" --since "$2" 2>/dev/null \ + | grep -c 'nftset.*Protocol error' || true } if [ ! -r "$NFTSET_CONF" ]; then @@ -71,10 +99,6 @@ if [ "${#HOSTS[@]}" -eq 0 ]; then exit 1 fi -BEFORE_V4=$(set_count bbrkn_v4) -BEFORE_V6=$(set_count bbrkn_v6) -log "START domains=${#HOSTS[@]} conf=$NFTSET_CONF v4=$BEFORE_V4 v6=$BEFORE_V6" - warm_one() { local host="$1" a aaaa a=$(dig +short +timeout="$DIG_TIMEOUT" +tries="$DIG_TRIES" \ @@ -104,8 +128,37 @@ warm_one() { export -f warm_one export RESOLVER RESOLVER_PORT DIG_TIMEOUT DIG_TRIES WARM_PING PING_TIMEOUT -RESULTS="$(printf '%s\n' "${HOSTS[@]}" \ - | xargs -r -P "$PARALLEL" -I{} bash -c 'warm_one "$@"' _ {})" +# Один прогон по всем доменам. Заполняет RESULTS и ERRORS (ошибки записи в +# сет у основного процесса dnsmasq за время прогона). +warm_all() { + local since pid + since=$(date '+%Y-%m-%d %H:%M:%S') + pid=$(main_pid) + RESULTS="$(printf '%s\n' "${HOSTS[@]}" \ + | xargs -r -P "$PARALLEL" -I{} bash -c 'warm_one "$@"' _ {})" + sleep 1 # журнал пишется асинхронно + ERRORS=0 + if [ -n "$pid" ] && [ "$pid" != "0" ]; then + ERRORS=$(nftset_errors "$pid" "$since") + fi +} + +BEFORE_V4=$(set_count bbrkn_v4) +BEFORE_V6=$(set_count bbrkn_v6) +log "START domains=${#HOSTS[@]} conf=$NFTSET_CONF v4=$BEFORE_V4 v6=$BEFORE_V6" + +warm_all +HEALED=0 +if [ "$ERRORS" -gt 0 ] && [ "$SELF_HEAL" = "1" ]; then + log "WEDGE $HOST_DNSMASQ_SVC pid=$(main_pid) nftset_errors=$ERRORS — рестарт и повторный прогрев" + if $SUDO systemctl restart "$HOST_DNSMASQ_SVC"; then + sleep 2 + HEALED=1 + warm_all + else + log "FATAL не удалось перезапустить $HOST_DNSMASQ_SVC" + fi +fi OK=$(grep -c '^ok ' <<<"$RESULTS" || true) NOPING=$(grep -c '^noping ' <<<"$RESULTS" || true) @@ -113,16 +166,23 @@ DNSFAIL=$(grep -c '^dnsfail ' <<<"$RESULTS" || true) AFTER_V4=$(set_count bbrkn_v4) AFTER_V6=$(set_count bbrkn_v6) -log "DONE ok=$OK noping=$NOPING dnsfail=$DNSFAIL v4=${BEFORE_V4}->${AFTER_V4} v6=${BEFORE_V6}->${AFTER_V6}" +log "DONE ok=$OK noping=$NOPING dnsfail=$DNSFAIL nftset_errors=$ERRORS healed=$HEALED v4=${BEFORE_V4}->${AFTER_V4} v6=${BEFORE_V6}->${AFTER_V6}" if [ "$DNSFAIL" -gt 0 ]; then log "dnsfail: $(grep '^dnsfail ' <<<"$RESULTS" | cut -d' ' -f2 | tr '\n' ' ')" fi echo "✔ Прогрето доменов: $OK, не резолвится: $DNSFAIL; bbrkn_v4 ${BEFORE_V4}->${AFTER_V4}, bbrkn_v6 ${BEFORE_V6}->${AFTER_V6}" +[ "$HEALED" = "1" ] && echo "⚠ dnsmasq заклинило на записи в nft-сет — перезапущен ($HOST_DNSMASQ_SVC)" # Ротация лога if [ -f "$LOG" ] && [ "$(stat -c%s "$LOG")" -gt "$LOG_MAX_BYTES" ]; then mv -f "$LOG" "${LOG}.1" fi +# Не ноль, если сеты так и не наполняются — деплой покажет предупреждение. +if [ "$ERRORS" -gt 0 ]; then + log "FAIL nftset_errors=$ERRORS после прогрева" + echo "Ошибка: dnsmasq не пишет в nft-сеты (nftset_errors=$ERRORS), см. journalctl -u $HOST_DNSMASQ_SVC" >&2 + exit 1 +fi exit 0 From 00c70090002698d83ad7047610af8da317e7f535 Mon Sep 17 00:00:00 2001 From: goodvin Date: Fri, 30 Oct 2026 08:51:51 +0300 Subject: [PATCH 20/20] Update domains.txt --- domains.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains.txt b/domains.txt index be5006e..bbd336a 100644 --- a/domains.txt +++ b/domains.txt @@ -274,4 +274,5 @@ makerworld.com makerworld.bblmw.com bblmw.com blog.3mdeb.com -3mdeb.com \ No newline at end of file +3mdeb.com +withpersona.com \ No newline at end of file