Compare commits
13 commits
migrate-ip
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ae8295546b | |||
| 19fbb5c1a2 | |||
| 47f91b5191 | |||
| 5643ba5615 | |||
| 0ac0aa0cc4 | |||
| 660a31c35e | |||
| 38d5bdf0ce | |||
| c9c1e8b1ee | |||
| 0865df3de3 | |||
| 20c484dc59 | |||
| a3e755c0a4 | |||
| 9acd54ff2a | |||
| b4df267892 |
4 changed files with 282 additions and 3 deletions
9
Makefile
9
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
|
||||
|
|
|
|||
52
README.md
52
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
|
||||
|
|
|
|||
94
domains.txt
94
domains.txt
|
|
@ -194,3 +194,97 @@ netlifydns.com
|
|||
netlifyusercontent.com
|
||||
www.ubuntu-touch.io
|
||||
ubports.com
|
||||
4pda.to
|
||||
www.patreon.com
|
||||
registry-1.docker.io
|
||||
docker.io
|
||||
downloads.claude.ai
|
||||
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
|
||||
gemini.google.com
|
||||
nakarte.me
|
||||
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
|
||||
api.bambulab.com
|
||||
bambulab.com
|
||||
e.bambulab.com
|
||||
128
scripts/warm-nftset.sh
Executable file
128
scripts/warm-nftset.sh
Executable file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue