Migrate bbrkn from legacy ipset to nftables sets
The gateway (Archie) routes bbrkn domains via nft sets bbrkn_v4/bbrkn_v6 (inet filter), not the legacy ipset. bbrkn was still emitting dead `ipset=/domain/bbrkn` directives (no such ipset exists) plus a 92-resolve pointing at 8.8.8.8, both overridden by hand-maintained files on the host. This makes bbrkn the generator of record for the real scheme. generate-configs.sh: - emit `nftset=/domain/$NFTSET_SPEC` (default 4#inet#filter#bbrkn_v4, 6#inet#filter#bbrkn_v6) into 90-nftset.conf instead of ipset= into 91-ipset-bbrkn.conf - DNS_SERVER default 8.8.8.8 -> 127.0.0.1#5350 (host dnsmasq pihole delegates bbrkn domains to for VPN resolution + nftset capture) deploy-to-gateway.sh: two targets, two instances - 90-nftset.conf -> host /etc/dnsmasq.d (:5350), 92-resolve -> pihole - full restart of both (dnsmasq SIGHUP does NOT re-read nftset=/server=) - flush nft sets bbrkn_v4/bbrkn_v6 instead of `ipset flush bbrkn` - add end-to-end nftset-capture health check via :5350 - rollback restores both files and restarts both instances Makefile/workflow: rename IPSET_CONF->NFTSET_CONF, add NFTSET_TARGET_DIR and HOST_DNSMASQ_SVC, DNS_SERVER=127.0.0.1#5350 (escaped `\#` in Make, quoted in YAML), note runner is ephemeral (cold gekata crawl). Docs: README + new CLAUDE.md describe the two-dnsmasq / nft-set model; exit-node DPI failover (10.77.1.2/10.77.2.2) documented as external (wg-ha.service), not owned by bbrkn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ef30817cf8
commit
d81a4ab3b2
6 changed files with 271 additions and 111 deletions
|
|
@ -15,21 +15,28 @@ jobs:
|
||||||
DOMAINS_FILE: domains.txt
|
DOMAINS_FILE: domains.txt
|
||||||
|
|
||||||
# Временные конфиги (куда пишутся generate-configs.sh)
|
# Временные конфиги (куда пишутся generate-configs.sh)
|
||||||
IPSET_CONF: /tmp/91-ipset-bbrkn.conf
|
NFTSET_CONF: /tmp/90-nftset.conf
|
||||||
RESOLVE_CONF: /tmp/92-resolve-bbrkn.conf
|
RESOLVE_CONF: /tmp/92-resolve-bbrkn.conf
|
||||||
|
|
||||||
# DNS-сервер для резолвинга
|
# Резолвер, которому pihole делегирует bbrkn-домены (host dnsmasq :5350).
|
||||||
DNS_SERVER: 8.8.8.8
|
# Кавычки обязательны — '#' иначе может трактоваться как комментарий.
|
||||||
|
DNS_SERVER: "127.0.0.1#5350"
|
||||||
|
|
||||||
# Gekata
|
# Gekata
|
||||||
CHROME_SERVER: http://10.77.1.2:3000
|
CHROME_SERVER: http://10.77.1.2:3000
|
||||||
|
|
||||||
# Директория конфигов на шлюзе
|
# nftset-таргет: host-инстанс dnsmasq (:5350) грузит /etc/dnsmasq.d
|
||||||
|
NFTSET_TARGET_DIR: /etc/dnsmasq.d
|
||||||
|
|
||||||
|
# resolve-таргет: pihole (docker) делегирует bbrkn-домены на :5350
|
||||||
TARGET_DIR: /opt/appdata/pihole/etc/dnsmasq.d
|
TARGET_DIR: /opt/appdata/pihole/etc/dnsmasq.d
|
||||||
|
|
||||||
# Имя контейнера Pi-hole
|
# Имя контейнера Pi-hole
|
||||||
DOCKER_CONTAINER: pihole
|
DOCKER_CONTAINER: pihole
|
||||||
|
|
||||||
|
# systemd-сервис host-инстанса dnsmasq
|
||||||
|
HOST_DNSMASQ_SVC: dnsmasq
|
||||||
|
|
||||||
IGNORE_PARTS: "doubleclick yandex mail.ru bigsv.ru mts.ru rambler.ru yadro.ru aif.ru vkvideo.ru tns-counter.ru st.vk.com vk.com"
|
IGNORE_PARTS: "doubleclick yandex mail.ru bigsv.ru mts.ru rambler.ru yadro.ru aif.ru vkvideo.ru tns-counter.ru st.vk.com vk.com"
|
||||||
|
|
||||||
# Кэш API-ответов (self-hosted runner сохраняет workspace между запусками)
|
# Кэш API-ответов (self-hosted runner сохраняет workspace между запусками)
|
||||||
|
|
@ -51,6 +58,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: dnsmasq-configs
|
name: dnsmasq-configs
|
||||||
path: |
|
path: |
|
||||||
${{ env.IPSET_CONF }}
|
${{ env.NFTSET_CONF }}
|
||||||
${{ env.RESOLVE_CONF }}
|
${{ env.RESOLVE_CONF }}
|
||||||
${{ env.DEBUG_LOG }}
|
${{ env.DEBUG_LOG }}
|
||||||
|
|
|
||||||
53
CLAUDE.md
Normal file
53
CLAUDE.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
Generates **Pi-hole / dnsmasq** configs that route blocked domains through a WireGuard VPN via **nftables sets**. Input is `domains.txt`; output is two dnsmasq files deployed to **two different dnsmasq instances** on the Archie gateway (see Architecture). README.md (Russian) is the authoritative reference — read it for the classification table, env-var descriptions, and report format.
|
||||||
|
|
||||||
|
> **Routing model (Archie gateway):** client → pihole (docker, `:53`) delegates bbrkn domains via `92-resolve-bbrkn.conf` (`server=/domain/127.0.0.1#5350`) to a **host dnsmasq on `:5350`**, which resolves via VPN and applies `90-nftset.conf` (`nftset=…#bbrkn_v4,…#bbrkn_v6`) to populate nft sets `inet filter bbrkn_v4`/`bbrkn_v6`. An nft mangle chain marks packets to those set IPs (`0x1`/`0x2`) and policy-routing sends them out a WireGuard exit. Dual-exit DPI-bypass failover (`10.77.1.2`/`10.77.2.2`) is handled by `wg-ha.service`, **external to bbrkn** — bbrkn only maintains domain→set membership.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all # generate + deploy (full cycle)
|
||||||
|
make generate # generate configs into /tmp only
|
||||||
|
make deploy # deploy existing configs to Pi-hole
|
||||||
|
make check # validate domains.txt syntax (regex per line)
|
||||||
|
make clean # remove generated /tmp configs
|
||||||
|
make cache-clean # wipe .cache/api/
|
||||||
|
|
||||||
|
./scripts/generate-configs.sh --dry-run # generate, print report, write nothing
|
||||||
|
DEBUG=1 make generate # verbose log to $DEBUG_LOG
|
||||||
|
STRICT_MODE=1 make generate # exit 1 if any domain's API query failed
|
||||||
|
CACHE_TTL_DAYS=0 make generate # bypass API cache for one run
|
||||||
|
```
|
||||||
|
|
||||||
|
No test suite. Verification = `--dry-run` + reading the DEBUG REPORT (see README) and diffing generated `/tmp` configs.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Two bash scripts, driven by env vars (defaults in each script's header, overridden by Makefile and CI):
|
||||||
|
|
||||||
|
**`scripts/generate-configs.sh`** — classifies each domain by querying a Chromium headless API (`$CHROME_SERVER/domains?domain=`), builds the ordered domain list, writes both config files in one batch. Key logic:
|
||||||
|
|
||||||
|
- **Classification** (drives whether subdomains are added): API JSON with `relatedDomains` → *site* (base + related subdomains); `ERR_NAME_NOT_RESOLVED`/`Timeout` → *dead*, skipped entirely; any other API error → *service* (base only, no subdomains); API fully unreachable → *fallback* (base only, recorded under `API FAILURES`). **Invariant: every domain in `domains.txt` reaches the output unless it's dead.**
|
||||||
|
- **State is held in bash associative arrays** (`DOM_ROLE`, `EXPANDED`, `SOURCES`, `SITE_RELATED`, …). The main loop deliberately avoids subshells so counters survive — do not wrap counter-mutating code in `$(...)` or pipes.
|
||||||
|
- **Caching**: successful API responses cached to `$CACHE_DIR/<domain>.json`, reused while newer than `CACHE_TTL_DAYS`; corrupt or expired entries refetch. `find -mtime +N` decides freshness.
|
||||||
|
- **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=/<domain>/$NFTSET_SPEC` (NFTSET_CONF → `90-nftset.conf`; default `NFTSET_SPEC=4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6`) and `server=/<domain>/$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.
|
||||||
|
|
||||||
|
## Deployment / CI
|
||||||
|
|
||||||
|
`.forgejo/workflows/deploy.yaml` runs on push to `main` (and `workflow_dispatch`) on a **self-hosted `forgejo-runner` on the gateway itself** — no SSH, deploy is local. Note: the runner is **ephemeral** (no persisted `.cache/api` was found on the host despite the older "persists workspace" claim), so a fresh CI run performs a **cold gekata crawl of every domain** (~30-70s each, serialized) — the first run after a cache reset is slow and heavy on gekata. CI runs `make clean && make all` and uploads the generated configs + debug log as artifacts even on failure. CI-only values (`NFTSET_TARGET_DIR`, `TARGET_DIR`, `CHROME_SERVER`, `DOCKER_CONTAINER`, `HOST_DNSMASQ_SVC`, `DNS_SERVER`, `IGNORE_PARTS`) live in the workflow `env:` block, not the scripts.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
- The nft sets (`bbrkn_v4`/`bbrkn_v6`, `inet filter`) and mangle/routing rules live on the gateway (`/etc/nftables.d/`, Ansible-managed) — bbrkn only fills the sets via dnsmasq, it does not define them.
|
||||||
|
- Requires `bash` 4.2+ (associative arrays, `${var,,}`), `curl`, `jq`, `docker`, `nft`, `systemctl`; `dig`/`nslookup` optional (health checks skipped if absent).
|
||||||
13
Makefile
13
Makefile
|
|
@ -3,16 +3,19 @@
|
||||||
# ===============================
|
# ===============================
|
||||||
|
|
||||||
# --- Переменные окружения (с дефолтами для локального запуска) ---
|
# --- Переменные окружения (с дефолтами для локального запуска) ---
|
||||||
IPSET_CONF ?= /tmp/91-ipset-bbrkn.conf
|
NFTSET_CONF ?= /tmp/90-nftset.conf
|
||||||
RESOLVE_CONF ?= /tmp/92-resolve-bbrkn.conf
|
RESOLVE_CONF ?= /tmp/92-resolve-bbrkn.conf
|
||||||
CHROME_SERVER ?= http://10.100.1.2:3000
|
CHROME_SERVER ?= http://10.77.1.2:3000
|
||||||
DOMAINS_FILE ?= domains.txt
|
DOMAINS_FILE ?= domains.txt
|
||||||
DNS_SERVER ?= 8.8.8.8
|
# Резолвер, которому pihole делегирует bbrkn-домены (host dnsmasq :5350).
|
||||||
|
# ВНИМАНИЕ: '#' в Makefile — начало комментария, экранируем как '\#'.
|
||||||
|
DNS_SERVER ?= 127.0.0.1\#5350
|
||||||
|
NFTSET_TARGET_DIR ?= /etc/dnsmasq.d
|
||||||
CACHE_DIR ?= .cache/api
|
CACHE_DIR ?= .cache/api
|
||||||
CACHE_TTL_DAYS ?= 15
|
CACHE_TTL_DAYS ?= 15
|
||||||
|
|
||||||
# Экспортируем переменные, чтобы они были доступны внутри shell-скриптов
|
# Экспортируем переменные, чтобы они были доступны внутри shell-скриптов
|
||||||
export IPSET_CONF RESOLVE_CONF CHROME_SERVER DOMAINS_FILE DNS_SERVER CACHE_DIR CACHE_TTL_DAYS
|
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
|
||||||
|
|
@ -22,7 +25,7 @@ all: generate deploy
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@echo "🧹 Очистка временных файлов"
|
@echo "🧹 Очистка временных файлов"
|
||||||
@rm -f $(IPSET_CONF) $(RESOLVE_CONF)
|
@rm -f $(NFTSET_CONF) $(RESOLVE_CONF)
|
||||||
|
|
||||||
cache-clean:
|
cache-clean:
|
||||||
@echo "🗑 Очистка кэша API ($(CACHE_DIR))"
|
@echo "🗑 Очистка кэша API ($(CACHE_DIR))"
|
||||||
|
|
|
||||||
89
README.md
89
README.md
|
|
@ -1,6 +1,6 @@
|
||||||
# bbrkn — DNS Bypass Config Generator
|
# bbrkn — DNS Bypass Config Generator
|
||||||
|
|
||||||
Автоматизирует генерацию конфигураций **Pi-hole / dnsmasq** для маршрутизации трафика заблокированных доменов через VPN-туннель (WireGuard).
|
Автоматизирует генерацию конфигураций **Pi-hole / dnsmasq** для маршрутизации трафика заблокированных доменов через VPN-туннель (WireGuard) с помощью **nftables-сетов**.
|
||||||
|
|
||||||
## Как это работает
|
## Как это работает
|
||||||
|
|
||||||
|
|
@ -8,25 +8,29 @@
|
||||||
flowchart TD
|
flowchart TD
|
||||||
A[domains.txt] --> B[generate-configs.sh]
|
A[domains.txt] --> B[generate-configs.sh]
|
||||||
B -->|кэш .cache/api| B
|
B -->|кэш .cache/api| B
|
||||||
B -->|MISS / EXPIRED| C[Chromium headless API]
|
B -->|MISS / EXPIRED| C[gekata: Chromium headless API]
|
||||||
C --> B
|
C --> B
|
||||||
B --> D[91-ipset-bbrkn.conf]
|
B --> D[90-nftset.conf]
|
||||||
B --> E[92-resolve-bbrkn.conf]
|
B --> E[92-resolve-bbrkn.conf]
|
||||||
D --> F[deploy-to-gateway.sh]
|
D --> F[deploy-to-gateway.sh]
|
||||||
E --> F
|
E --> F
|
||||||
F -->|backup + copy| G[Pi-hole dnsmasq.d/]
|
F -->|backup + copy + systemctl restart| G[host dnsmasq :5350<br/>/etc/dnsmasq.d/]
|
||||||
F -->|docker restart| H[Pi-hole container]
|
F -->|backup + copy + docker restart| H[Pi-hole :53]
|
||||||
F -->|DNS health check| H
|
F -->|health check + nftset check| H
|
||||||
H -->|rollback если упал| G
|
F -.->|rollback если упал| G
|
||||||
|
|
||||||
subgraph "Трафик клиента"
|
subgraph "Трафик клиента"
|
||||||
K[Клиент] -->|DNS запрос| H
|
K[Клиент] -->|DNS запрос| H
|
||||||
H -->|домен в ipset → mark| I[iptables]
|
H -->|"server= → 127.0.0.1#5350"| G
|
||||||
I --> J[WireGuard VPN]
|
G -->|"резолв через VPN + nftset="| N["nft set bbrkn_v4 / bbrkn_v6"]
|
||||||
|
N -->|ip daddr @bbrkn → mark| I[nft mangle]
|
||||||
|
I -->|policy routing| J[WireGuard exit<br/>10.77.1.2 / 10.77.2.2]
|
||||||
J --> L[Интернет]
|
J --> L[Интернет]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Два инстанса dnsmasq.** Pi-hole (:53) отдаёт клиентов и делегирует bbrkn-домены host-инстансу на :5350 (`92-resolve`). Инстанс :5350 резолвит их через VPN и по директиве `nftset=` (`90-nftset.conf`) кладёт полученные IP в nft-сеты `bbrkn_v4`/`bbrkn_v6`. nft-mangle маркирует пакеты к этим IP, policy-routing уводит их в WireGuard. **Выбор из двух exit-нод (DPI-обход РКН) и failover — вне bbrkn**, ими управляет `wg-ha.service` на шлюзе.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Классификация доменов
|
## Классификация доменов
|
||||||
|
|
@ -46,15 +50,26 @@ flowchart TD
|
||||||
|
|
||||||
Домены группируются по базовому домену с комментарием:
|
Домены группируются по базовому домену с комментарием:
|
||||||
|
|
||||||
|
`90-nftset.conf` (наполняет nft-сеты):
|
||||||
|
|
||||||
```
|
```
|
||||||
# example.com — 4 subdomains
|
# example.com — 4 subdomains
|
||||||
ipset=/example.com/bbrkn
|
nftset=/example.com/4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6
|
||||||
ipset=/static.example.com/bbrkn
|
nftset=/static.example.com/4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6
|
||||||
ipset=/api.example.com/bbrkn
|
nftset=/api.example.com/4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6
|
||||||
...
|
...
|
||||||
|
|
||||||
# some-service.com — 0 subdomains
|
# some-service.com — 0 subdomains
|
||||||
ipset=/some-service.com/bbrkn
|
nftset=/some-service.com/4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6
|
||||||
|
```
|
||||||
|
|
||||||
|
`92-resolve-bbrkn.conf` (делегирует те же домены host-инстансу :5350):
|
||||||
|
|
||||||
|
```
|
||||||
|
# example.com — 4 subdomains
|
||||||
|
server=/example.com/127.0.0.1#5350
|
||||||
|
server=/static.example.com/127.0.0.1#5350
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -93,10 +108,11 @@ make check # проверить синтаксис domains.txt
|
||||||
| Переменная | По умолчанию | Описание |
|
| Переменная | По умолчанию | Описание |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `DOMAINS_FILE` | `domains.txt` | Входной файл со списком доменов |
|
| `DOMAINS_FILE` | `domains.txt` | Входной файл со списком доменов |
|
||||||
| `IPSET_CONF` | `/tmp/91-ipset-bbrkn.conf` | Выходной файл правил ipset |
|
| `NFTSET_CONF` | `/tmp/90-nftset.conf` | Выходной файл директив `nftset=` |
|
||||||
| `RESOLVE_CONF` | `/tmp/92-resolve-bbrkn.conf` | Выходной файл правил server= |
|
| `NFTSET_SPEC` | `4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6` | Цель nftset: `<4\|6>#family#table#set` |
|
||||||
| `CHROME_SERVER` | `http://127.0.0.1:3000` | Адрес Chromium headless API |
|
| `RESOLVE_CONF` | `/tmp/92-resolve-bbrkn.conf` | Выходной файл директив `server=` |
|
||||||
| `DNS_SERVER` | `8.8.8.8` | DNS-сервер для резолвинга через VPN |
|
| `CHROME_SERVER` | `http://127.0.0.1:3000` | Адрес gekata (Chromium headless API) |
|
||||||
|
| `DNS_SERVER` | `127.0.0.1#5350` | Резолвер, которому pihole делегирует домены (host dnsmasq :5350) |
|
||||||
| `IGNORE_PARTS` | _(пусто)_ | Подстроки через пробел — домены с совпадением игнорируются |
|
| `IGNORE_PARTS` | _(пусто)_ | Подстроки через пробел — домены с совпадением игнорируются |
|
||||||
| `CACHE_DIR` | `.cache/api` | Директория кэша API-ответов |
|
| `CACHE_DIR` | `.cache/api` | Директория кэша API-ответов |
|
||||||
| `CACHE_TTL_DAYS` | `15` | Время жизни кэша в днях; `0` — отключить кэш |
|
| `CACHE_TTL_DAYS` | `15` | Время жизни кэша в днях; `0` — отключить кэш |
|
||||||
|
|
@ -108,12 +124,17 @@ make check # проверить синтаксис domains.txt
|
||||||
|
|
||||||
| Переменная | По умолчанию | Описание |
|
| Переменная | По умолчанию | Описание |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `TARGET_DIR` | `/opt/appdata/pihole/etc/dnsmasq.d` | Директория dnsmasq в Pi-hole |
|
| `NFTSET_CONF` | `/tmp/90-nftset.conf` | Сгенерированный файл `nftset=` |
|
||||||
| `IPSET_CONF` | `/tmp/91-ipset-bbrkn.conf` | Сгенерированный файл ipset |
|
| `NFTSET_TARGET_DIR` | `/etc/dnsmasq.d` | Директория host-инстанса dnsmasq (:5350) |
|
||||||
| `RESOLVE_CONF` | `/tmp/92-resolve-bbrkn.conf` | Сгенерированный файл server= |
|
| `RESOLVE_CONF` | `/tmp/92-resolve-bbrkn.conf` | Сгенерированный файл `server=` |
|
||||||
|
| `TARGET_DIR` | `/opt/appdata/pihole/etc/dnsmasq.d` | Директория dnsmasq в Pi-hole (для `92-resolve`) |
|
||||||
| `DOCKER_CONTAINER` | `pihole` | Имя Docker-контейнера Pi-hole |
|
| `DOCKER_CONTAINER` | `pihole` | Имя Docker-контейнера Pi-hole |
|
||||||
| `DNS_LISTEN_ADDR` | `127.0.0.1` | Адрес, на котором Pi-hole слушает DNS |
|
| `HOST_DNSMASQ_SVC` | `dnsmasq` | systemd-сервис host-инстанса dnsmasq |
|
||||||
| `DNS_CHECK_DOMAIN` | `google.com` | Домен для DNS health check после рестарта |
|
| `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 |
|
||||||
|
| `NFTSET_CHECK_DOMAIN` | `chatgpt.com` | bbrkn-домен для end-to-end проверки nftset-захвата |
|
||||||
|
| `RESOLVER_ADDR` / `RESOLVER_PORT` | `127.0.0.1` / `5350` | Адрес host-инстанса для проверки захвата |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -125,7 +146,7 @@ make check # проверить синтаксис domains.txt
|
||||||
- Если файл **старше** TTL → перезапрашивается и кэш обновляется.
|
- Если файл **старше** TTL → перезапрашивается и кэш обновляется.
|
||||||
- Если кэш **повреждён** (невалидный JSON) → перезапрашивается автоматически.
|
- Если кэш **повреждён** (невалидный JSON) → перезапрашивается автоматически.
|
||||||
|
|
||||||
На self-hosted runner workspace сохраняется между запусками, поэтому повторные CI-запуски значительно быстрее.
|
> **Внимание:** forgejo-runner на шлюзе **эфемерный** — workspace (и `.cache/api`) между запусками не сохраняется. Поэтому первый CI-прогон после сброса кэша выполняет **холодный обход всех доменов через gekata** (~30-70 с на домен, последовательно) и работает долго. Для быстрых повторных прогонов держите тёплый `.cache/api` в постоянной директории (`CACHE_DIR`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Сбросить кэш и запросить всё заново
|
# Сбросить кэш и запросить всё заново
|
||||||
|
|
@ -139,15 +160,16 @@ CACHE_TTL_DAYS=0 make generate
|
||||||
|
|
||||||
## Деплой и автооткат
|
## Деплой и автооткат
|
||||||
|
|
||||||
`deploy-to-gateway.sh` выполняет следующие шаги:
|
`deploy-to-gateway.sh` деплоит в **два инстанса dnsmasq** и выполняет шаги:
|
||||||
|
|
||||||
1. Проверяет наличие сгенерированных конфигов.
|
1. Проверяет наличие обоих сгенерированных конфигов.
|
||||||
2. Создаёт timestamped-бэкапы текущих конфигов в `TARGET_DIR`.
|
2. Создаёт timestamped-бэкапы `90-nftset.conf` (host) и `92-resolve-bbrkn.conf` (pihole).
|
||||||
3. Копирует новые конфиги в `TARGET_DIR`.
|
3. Копирует `90-nftset.conf` → `NFTSET_TARGET_DIR`, `92-resolve-bbrkn.conf` → `TARGET_DIR`.
|
||||||
4. Перезапускает контейнер Pi-hole.
|
4. Перезапускает **оба** инстанса: `systemctl restart $HOST_DNSMASQ_SVC` + `docker restart $DOCKER_CONTAINER`. Полный рестарт обязателен — dnsmasq по **SIGHUP не перечитывает** директивы `nftset=`/`server=`.
|
||||||
5. Ждёт запуска (до 30 секунд, polling через `docker inspect`).
|
5. Ждёт: host-инстанс слушает `:5350` (до 15 с) и pihole в состоянии `running` (до 30 с).
|
||||||
6. Выполняет DNS health check через `dig` / `nslookup`.
|
6. Сбрасывает nft-сеты `bbrkn_v4`/`bbrkn_v6` (устаревшие IP иначе висят до timeout 24 ч).
|
||||||
7. При ошибке на шагах 5 или 6 — **автоматически откатывается**: восстанавливает бэкапы и перезапускает контейнер со старой конфигурацией.
|
7. Health check: базовый DNS через pihole `:53` + end-to-end проверка nftset-захвата (резолв bbrkn-домена через `:5350` → сет наполнился).
|
||||||
|
8. При ошибке на шагах 5–7 — **автоматически откатывается**: восстанавливает оба бэкапа и перезапускает оба инстанса со старой конфигурацией.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -226,10 +248,11 @@ some-domain.com → api_failed_added_without_subdomains
|
||||||
| Инструмент | Где используется |
|
| Инструмент | Где используется |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `bash` 4.2+ | оба скрипта |
|
| `bash` 4.2+ | оба скрипта |
|
||||||
| `curl` | запросы к Chromium API |
|
| `curl` | запросы к gekata API |
|
||||||
| `jq` | парсинг JSON-ответов |
|
| `jq` | парсинг JSON-ответов |
|
||||||
| `docker` | перезапуск Pi-hole |
|
| `docker` | перезапуск Pi-hole |
|
||||||
| `ipset` | сброс маршрутов после деплоя |
|
| `nft` | flush nft-сетов + проверка захвата после деплоя |
|
||||||
|
| `systemctl` | рестарт host-инстанса dnsmasq |
|
||||||
| `dig` или `nslookup` | DNS health check (опционально) |
|
| `dig` или `nslookup` | DNS health check (опционально) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -4,46 +4,64 @@ set -euo pipefail
|
||||||
# ==============================
|
# ==============================
|
||||||
# Конфигурация через переменные окружения
|
# Конфигурация через переменные окружения
|
||||||
# ==============================
|
# ==============================
|
||||||
TARGET_DIR="${TARGET_DIR:-/opt/appdata/pihole/etc/dnsmasq.d}"
|
# Сгенерированные конфиги (из generate-configs.sh)
|
||||||
IPSET_CONF="${IPSET_CONF:-/tmp/91-ipset-bbrkn.conf}"
|
NFTSET_CONF="${NFTSET_CONF:-/tmp/90-nftset.conf}"
|
||||||
RESOLVE_CONF="${RESOLVE_CONF:-/tmp/92-resolve-bbrkn.conf}"
|
RESOLVE_CONF="${RESOLVE_CONF:-/tmp/92-resolve-bbrkn.conf}"
|
||||||
|
|
||||||
|
# Два таргета — два инстанса dnsmasq:
|
||||||
|
# NFTSET_CONF → host dnsmasq (:5350), грузит /etc/dnsmasq.d, наполняет nft-сеты
|
||||||
|
# RESOLVE_CONF → pihole (docker), делегирует bbrkn-домены инстансу :5350
|
||||||
|
NFTSET_TARGET_DIR="${NFTSET_TARGET_DIR:-/etc/dnsmasq.d}"
|
||||||
|
RESOLVE_TARGET_DIR="${RESOLVE_TARGET_DIR:-${TARGET_DIR:-/opt/appdata/pihole/etc/dnsmasq.d}}"
|
||||||
|
|
||||||
DOCKER_CONTAINER="${DOCKER_CONTAINER:-pihole}"
|
DOCKER_CONTAINER="${DOCKER_CONTAINER:-pihole}"
|
||||||
|
# systemd-сервис host-инстанса dnsmasq (:5350)
|
||||||
|
HOST_DNSMASQ_SVC="${HOST_DNSMASQ_SVC:-dnsmasq}"
|
||||||
|
|
||||||
# Адрес, на котором слушает Pi-hole DNS (для health check)
|
# nft-сеты для сброса после деплоя (иначе висят до timeout 24h).
|
||||||
DNS_LISTEN_ADDR="${DNS_LISTEN_ADDR:-127.0.0.1}"
|
# Формат: "family table set" — перечисляются через ';' в NFT_SETS.
|
||||||
# Домен для проверки DNS после рестарта
|
NFT_SETS="${NFT_SETS:-inet filter bbrkn_v4;inet filter bbrkn_v6}"
|
||||||
|
|
||||||
|
# --- Health check ---
|
||||||
|
DNS_LISTEN_ADDR="${DNS_LISTEN_ADDR:-127.0.0.1}" # pihole :53 (клиентский)
|
||||||
DNS_CHECK_DOMAIN="${DNS_CHECK_DOMAIN:-google.com}"
|
DNS_CHECK_DOMAIN="${DNS_CHECK_DOMAIN:-google.com}"
|
||||||
|
# Домен, который ЕСТЬ в bbrkn-сете — для end-to-end проверки nftset-захвата
|
||||||
|
NFTSET_CHECK_DOMAIN="${NFTSET_CHECK_DOMAIN:-chatgpt.com}"
|
||||||
|
RESOLVER_ADDR="${RESOLVER_ADDR:-127.0.0.1}" # host dnsmasq (:5350)
|
||||||
|
RESOLVER_PORT="${RESOLVER_PORT:-5350}"
|
||||||
|
NFT_CHECK_SET="${NFT_CHECK_SET:-inet filter bbrkn_v4}"
|
||||||
|
|
||||||
# Имена назначения (без basename — используем параметрическую подстановку)
|
# Имена/пути назначения (параметрическая подстановка, без basename)
|
||||||
ipset_name="${IPSET_CONF##*/}"
|
nftset_name="${NFTSET_CONF##*/}"
|
||||||
resolve_name="${RESOLVE_CONF##*/}"
|
resolve_name="${RESOLVE_CONF##*/}"
|
||||||
ipset_dest="$TARGET_DIR/$ipset_name"
|
nftset_dest="$NFTSET_TARGET_DIR/$nftset_name"
|
||||||
resolve_dest="$TARGET_DIR/$resolve_name"
|
resolve_dest="$RESOLVE_TARGET_DIR/$resolve_name"
|
||||||
|
|
||||||
# Пути к бэкапам (заполняются ниже, нужны для rollback)
|
# Пути к бэкапам (для rollback)
|
||||||
IPSET_BACKUP=""
|
NFTSET_BACKUP=""
|
||||||
RESOLVE_BACKUP=""
|
RESOLVE_BACKUP=""
|
||||||
|
|
||||||
echo "Deploying configuration files to $TARGET_DIR"
|
echo "Deploying nftset config → $nftset_dest (host dnsmasq :$RESOLVER_PORT)"
|
||||||
|
echo "Deploying resolve config → $resolve_dest ($DOCKER_CONTAINER)"
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# Проверка входных файлов
|
# Проверка входных файлов
|
||||||
# ==============================
|
# ==============================
|
||||||
if [ ! -f "$IPSET_CONF" ] || [ ! -f "$RESOLVE_CONF" ]; then
|
if [ ! -f "$NFTSET_CONF" ] || [ ! -f "$RESOLVE_CONF" ]; then
|
||||||
echo "Error: one or both config files not found!"
|
echo "Error: one or both generated config files not found!"
|
||||||
|
echo " NFTSET_CONF=$NFTSET_CONF RESOLVE_CONF=$RESOLVE_CONF"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# Функция отката: восстанавливает бэкапы и перезапускает контейнер
|
# Функция отката: восстанавливает бэкапы и перезапускает оба инстанса
|
||||||
# ==============================
|
# ==============================
|
||||||
rollback() {
|
rollback() {
|
||||||
echo "[ROLLBACK] Restoring previous configuration..."
|
echo "[ROLLBACK] Restoring previous configuration..."
|
||||||
local restored=false
|
local restored=false
|
||||||
if [ -n "$IPSET_BACKUP" ] && [ -f "$IPSET_BACKUP" ]; then
|
if [ -n "$NFTSET_BACKUP" ] && [ -f "$NFTSET_BACKUP" ]; then
|
||||||
cp "$IPSET_BACKUP" "$ipset_dest"
|
cp "$NFTSET_BACKUP" "$nftset_dest"
|
||||||
echo "[ROLLBACK] Restored $ipset_dest"
|
echo "[ROLLBACK] Restored $nftset_dest"
|
||||||
restored=true
|
restored=true
|
||||||
fi
|
fi
|
||||||
if [ -n "$RESOLVE_BACKUP" ] && [ -f "$RESOLVE_BACKUP" ]; then
|
if [ -n "$RESOLVE_BACKUP" ] && [ -f "$RESOLVE_BACKUP" ]; then
|
||||||
|
|
@ -52,7 +70,8 @@ rollback() {
|
||||||
restored=true
|
restored=true
|
||||||
fi
|
fi
|
||||||
if $restored; then
|
if $restored; then
|
||||||
echo "[ROLLBACK] Restarting $DOCKER_CONTAINER with previous config..."
|
echo "[ROLLBACK] Restarting host dnsmasq ($HOST_DNSMASQ_SVC) and $DOCKER_CONTAINER..."
|
||||||
|
systemctl restart "$HOST_DNSMASQ_SVC" 2>/dev/null || true
|
||||||
docker restart "$DOCKER_CONTAINER" 2>/dev/null || true
|
docker restart "$DOCKER_CONTAINER" 2>/dev/null || true
|
||||||
else
|
else
|
||||||
echo "[ROLLBACK] No backups found, cannot restore"
|
echo "[ROLLBACK] No backups found, cannot restore"
|
||||||
|
|
@ -64,10 +83,10 @@ rollback() {
|
||||||
# Бэкап существующих файлов
|
# Бэкап существующих файлов
|
||||||
# ==============================
|
# ==============================
|
||||||
ts=$(date +%Y%m%d-%H%M%S)
|
ts=$(date +%Y%m%d-%H%M%S)
|
||||||
if [ -f "$ipset_dest" ]; then
|
if [ -f "$nftset_dest" ]; then
|
||||||
IPSET_BACKUP="${ipset_dest}.backup.${ts}"
|
NFTSET_BACKUP="${nftset_dest}.backup.${ts}"
|
||||||
cp "$ipset_dest" "$IPSET_BACKUP"
|
cp "$nftset_dest" "$NFTSET_BACKUP"
|
||||||
echo "Backed up $ipset_dest → $IPSET_BACKUP"
|
echo "Backed up $nftset_dest → $NFTSET_BACKUP"
|
||||||
fi
|
fi
|
||||||
if [ -f "$resolve_dest" ]; then
|
if [ -f "$resolve_dest" ]; then
|
||||||
RESOLVE_BACKUP="${resolve_dest}.backup.${ts}"
|
RESOLVE_BACKUP="${resolve_dest}.backup.${ts}"
|
||||||
|
|
@ -78,16 +97,38 @@ fi
|
||||||
# ==============================
|
# ==============================
|
||||||
# Копирование новых файлов
|
# Копирование новых файлов
|
||||||
# ==============================
|
# ==============================
|
||||||
cp "$IPSET_CONF" "$TARGET_DIR/"
|
cp "$NFTSET_CONF" "$nftset_dest"
|
||||||
cp "$RESOLVE_CONF" "$TARGET_DIR/"
|
cp "$RESOLVE_CONF" "$resolve_dest"
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# Перезапуск контейнера
|
# Перезапуск обоих инстансов.
|
||||||
|
# ВАЖНО: dnsmasq по SIGHUP НЕ перечитывает директивы конфига (nftset=/server=),
|
||||||
|
# только hosts-файлы — новые домены подхватываются лишь полным рестартом.
|
||||||
# ==============================
|
# ==============================
|
||||||
|
echo "Restarting host dnsmasq ($HOST_DNSMASQ_SVC)..."
|
||||||
|
systemctl restart "$HOST_DNSMASQ_SVC"
|
||||||
|
|
||||||
echo "Restarting $DOCKER_CONTAINER container..."
|
echo "Restarting $DOCKER_CONTAINER container..."
|
||||||
docker restart "$DOCKER_CONTAINER"
|
docker restart "$DOCKER_CONTAINER"
|
||||||
|
|
||||||
# Ждём запуска — polling через docker inspect вместо sleep + docker ps
|
# --- Ждём host dnsmasq (:5350) ---
|
||||||
|
echo "Waiting for host dnsmasq on :$RESOLVER_PORT (up to 15s)..."
|
||||||
|
host_ready=false
|
||||||
|
for i in $(seq 1 15); do
|
||||||
|
if ss -uln 2>/dev/null | grep -q ":$RESOLVER_PORT "; then
|
||||||
|
host_ready=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if ! $host_ready; then
|
||||||
|
echo "Error: host dnsmasq not listening on :$RESOLVER_PORT after 15s"
|
||||||
|
rollback
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "host dnsmasq is listening on :$RESOLVER_PORT"
|
||||||
|
|
||||||
|
# --- Ждём pihole ---
|
||||||
echo "Waiting for $DOCKER_CONTAINER to be ready (up to 30s)..."
|
echo "Waiting for $DOCKER_CONTAINER to be ready (up to 30s)..."
|
||||||
ready=false
|
ready=false
|
||||||
for i in $(seq 1 30); do
|
for i in $(seq 1 30); do
|
||||||
|
|
@ -98,52 +139,79 @@ for i in $(seq 1 30); do
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! $ready; then
|
if ! $ready; then
|
||||||
echo "Error: $DOCKER_CONTAINER failed to start after 30s (status: $status)"
|
echo "Error: $DOCKER_CONTAINER failed to start after 30s (status: ${status:-unknown})"
|
||||||
rollback
|
rollback
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "$DOCKER_CONTAINER is running"
|
echo "$DOCKER_CONTAINER is running"
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# DNS health check
|
# Сброс nft-сетов (чистим устаревшие записи удалённых доменов; timeout 24h иначе)
|
||||||
# ==============================
|
# ==============================
|
||||||
dns_health_check() {
|
echo "Flushing nft sets..."
|
||||||
if command -v dig >/dev/null 2>&1; then
|
IFS=';' read -ra _sets <<< "$NFT_SETS"
|
||||||
dig +short +time=3 +tries=2 "@$DNS_LISTEN_ADDR" "$DNS_CHECK_DOMAIN" >/dev/null 2>&1
|
for s in "${_sets[@]}"; do
|
||||||
elif command -v nslookup >/dev/null 2>&1; then
|
[ -z "$s" ] && continue
|
||||||
nslookup "$DNS_CHECK_DOMAIN" "$DNS_LISTEN_ADDR" >/dev/null 2>&1
|
# shellcheck disable=SC2086
|
||||||
|
if nft flush set $s 2>/dev/null; then
|
||||||
|
echo " flushed set $s"
|
||||||
else
|
else
|
||||||
echo "Warning: neither 'dig' nor 'nslookup' found, skipping DNS health check"
|
echo " WARN: could not flush set $s"
|
||||||
return 0
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# Health check 1: базовый DNS через pihole :53
|
||||||
|
# ==============================
|
||||||
|
dns_query() { # <addr> <port> <domain>
|
||||||
|
local addr="$1" port="$2" dom="$3"
|
||||||
|
if command -v dig >/dev/null 2>&1; then
|
||||||
|
dig +short +time=3 +tries=2 -p "$port" "@$addr" "$dom" 2>/dev/null
|
||||||
|
elif command -v nslookup >/dev/null 2>&1; then
|
||||||
|
nslookup -port="$port" "$dom" "$addr" 2>/dev/null | awk '/^Address: /{print $2}'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Running DNS health check ($DNS_CHECK_DOMAIN via $DNS_LISTEN_ADDR, up to 15 attempts)..."
|
if ! command -v dig >/dev/null 2>&1 && ! command -v nslookup >/dev/null 2>&1; then
|
||||||
dns_ok=false
|
echo "Warning: neither 'dig' nor 'nslookup' found, skipping DNS health checks"
|
||||||
for i in $(seq 1 15); do
|
else
|
||||||
if dns_health_check; then
|
echo "DNS health check: $DNS_CHECK_DOMAIN via $DNS_LISTEN_ADDR:53 (up to 15 attempts)..."
|
||||||
|
dns_ok=false
|
||||||
|
for i in $(seq 1 15); do
|
||||||
|
if [ -n "$(dns_query "$DNS_LISTEN_ADDR" 53 "$DNS_CHECK_DOMAIN")" ]; then
|
||||||
dns_ok=true
|
dns_ok=true
|
||||||
echo "DNS health check passed (attempt $i)"
|
echo " passed (attempt $i)"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
echo " attempt $i failed, retrying in 1s..."
|
echo " attempt $i failed, retry in 1s..."
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
if ! $dns_ok; then
|
||||||
if ! $dns_ok; then
|
echo "Error: DNS health check failed — pihole not resolving on $DNS_LISTEN_ADDR:53"
|
||||||
echo "Error: DNS health check failed after 15 attempts — Pi-hole not responding on $DNS_LISTEN_ADDR"
|
|
||||||
rollback
|
rollback
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# Health check 2: end-to-end nftset-захват.
|
||||||
|
# Резолвим bbrkn-домен через :5350 и убеждаемся, что nft-сет наполнился.
|
||||||
|
# ==============================
|
||||||
|
echo "nftset capture check: $NFTSET_CHECK_DOMAIN via $RESOLVER_ADDR:$RESOLVER_PORT..."
|
||||||
|
dns_query "$RESOLVER_ADDR" "$RESOLVER_PORT" "$NFTSET_CHECK_DOMAIN" >/dev/null
|
||||||
|
sleep 1
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
set_count=$(nft list set $NFT_CHECK_SET 2>/dev/null | grep -cE '([0-9]{1,3}\.){3}[0-9]{1,3}' || echo 0)
|
||||||
|
if [ "${set_count:-0}" -gt 0 ]; then
|
||||||
|
echo " passed — set '$NFT_CHECK_SET' populated ($set_count element line(s))"
|
||||||
|
else
|
||||||
|
echo "Error: nftset '$NFT_CHECK_SET' still empty after resolving $NFTSET_CHECK_DOMAIN"
|
||||||
|
echo " host dnsmasq likely not loading $nftset_dest"
|
||||||
|
rollback
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# Очистка IPSET
|
|
||||||
# ==============================
|
|
||||||
echo "Flushing ipset bbrkn..."
|
|
||||||
ipset flush bbrkn
|
|
||||||
|
|
||||||
echo "Deployment completed successfully"
|
echo "Deployment completed successfully"
|
||||||
DOMAIN_COUNT=$(grep -c '^ipset=' "$ipset_dest" || echo "0")
|
DOMAIN_COUNT=$(grep -c '^nftset=' "$nftset_dest" || echo "0")
|
||||||
echo "Applied configuration for $DOMAIN_COUNT domains"
|
echo "Applied nftset configuration for $DOMAIN_COUNT domain directives"
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,17 @@ set -euo pipefail
|
||||||
# ==============================
|
# ==============================
|
||||||
INPUT_FILE="${DOMAINS_FILE:-domains.txt}"
|
INPUT_FILE="${DOMAINS_FILE:-domains.txt}"
|
||||||
|
|
||||||
IPSET_CONF="${IPSET_CONF:-/tmp/91-ipset-bbrkn.conf}"
|
NFTSET_CONF="${NFTSET_CONF:-/tmp/90-nftset.conf}"
|
||||||
RESOLVE_CONF="${RESOLVE_CONF:-/tmp/92-resolve-bbrkn.conf}"
|
RESOLVE_CONF="${RESOLVE_CONF:-/tmp/92-resolve-bbrkn.conf}"
|
||||||
|
|
||||||
|
# nftset-цель для dnsmasq: <4|6>#<family>#<table>#<set>[,...]
|
||||||
|
# 4# → A-записи в bbrkn_v4; 6# → AAAA в bbrkn_v6 (таблица: inet filter)
|
||||||
|
NFTSET_SPEC="${NFTSET_SPEC:-4#inet#filter#bbrkn_v4,6#inet#filter#bbrkn_v6}"
|
||||||
|
|
||||||
API_URL="${CHROME_SERVER:-http://127.0.0.1:3000}/domains?domain="
|
API_URL="${CHROME_SERVER:-http://127.0.0.1:3000}/domains?domain="
|
||||||
DNS_SERVER="${DNS_SERVER:-8.8.8.8}"
|
# Куда pihole делегирует резолвинг bbrkn-доменов: инстанс dnsmasq :5350, который
|
||||||
|
# резолвит через VPN и наполняет nft-сеты. Формат dnsmasq server=: ip[#port].
|
||||||
|
DNS_SERVER="${DNS_SERVER:-127.0.0.1#5350}"
|
||||||
|
|
||||||
# Игнорируемые части доменов (разделитель — пробел)
|
# Игнорируемые части доменов (разделитель — пробел)
|
||||||
IGNORE_PARTS="${IGNORE_PARTS:-}"
|
IGNORE_PARTS="${IGNORE_PARTS:-}"
|
||||||
|
|
@ -43,7 +49,7 @@ if ! command -v jq >/dev/null 2>&1; then err "jq is required"; exit 2; fi
|
||||||
if [ "$DEBUG" != "0" ]; then : > "$DEBUG_LOG"; dbg "Debugging enabled"; fi
|
if [ "$DEBUG" != "0" ]; then : > "$DEBUG_LOG"; dbg "Debugging enabled"; fi
|
||||||
|
|
||||||
log "Starting generate-configs.sh"
|
log "Starting generate-configs.sh"
|
||||||
dbg "ENV: INPUT_FILE=$INPUT_FILE IPSET_CONF=$IPSET_CONF RESOLVE_CONF=$RESOLVE_CONF API_URL=$API_URL DNS_SERVER=$DNS_SERVER IGNORE_PARTS='$IGNORE_PARTS' DRY_RUN=$DRY_RUN CACHE_DIR=$CACHE_DIR CACHE_TTL_DAYS=$CACHE_TTL_DAYS STRICT_MODE=$STRICT_MODE"
|
dbg "ENV: INPUT_FILE=$INPUT_FILE NFTSET_CONF=$NFTSET_CONF NFTSET_SPEC=$NFTSET_SPEC RESOLVE_CONF=$RESOLVE_CONF API_URL=$API_URL DNS_SERVER=$DNS_SERVER IGNORE_PARTS='$IGNORE_PARTS' DRY_RUN=$DRY_RUN CACHE_DIR=$CACHE_DIR CACHE_TTL_DAYS=$CACHE_TTL_DAYS STRICT_MODE=$STRICT_MODE"
|
||||||
|
|
||||||
if [ ! -f "$INPUT_FILE" ]; then
|
if [ ! -f "$INPUT_FILE" ]; then
|
||||||
err "Input file not found: $INPUT_FILE"
|
err "Input file not found: $INPUT_FILE"
|
||||||
|
|
@ -313,9 +319,9 @@ if ! $DRY_RUN; then
|
||||||
{
|
{
|
||||||
for d in "${ORDERED_DOMS[@]}"; do
|
for d in "${ORDERED_DOMS[@]}"; do
|
||||||
[[ -n "${OUTPUT_COMMENT[$d]:-}" ]] && printf '%s\n' "${OUTPUT_COMMENT[$d]}"
|
[[ -n "${OUTPUT_COMMENT[$d]:-}" ]] && printf '%s\n' "${OUTPUT_COMMENT[$d]}"
|
||||||
printf 'ipset=/%s/bbrkn\n' "$d"
|
printf 'nftset=/%s/%s\n' "$d" "$NFTSET_SPEC"
|
||||||
done
|
done
|
||||||
} > "$IPSET_CONF"
|
} > "$NFTSET_CONF"
|
||||||
{
|
{
|
||||||
for d in "${ORDERED_DOMS[@]}"; do
|
for d in "${ORDERED_DOMS[@]}"; do
|
||||||
[[ -n "${OUTPUT_COMMENT[$d]:-}" ]] && printf '%s\n' "${OUTPUT_COMMENT[$d]}"
|
[[ -n "${OUTPUT_COMMENT[$d]:-}" ]] && printf '%s\n' "${OUTPUT_COMMENT[$d]}"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue