# bbrkn — DNS Bypass Config Generator Автоматизирует генерацию конфигураций **Pi-hole / dnsmasq** для маршрутизации трафика заблокированных доменов через VPN-туннель (WireGuard). ## Как это работает ```mermaid flowchart TD A[domains.txt] --> B[generate-configs.sh] B -->|кэш .cache/api| B B -->|MISS / EXPIRED| C[Chromium headless API] C --> B B --> D[91-ipset-bbrkn.conf] B --> E[92-resolve-bbrkn.conf] D --> F[deploy-to-gateway.sh] E --> F F -->|backup + copy| G[Pi-hole dnsmasq.d/] F -->|docker restart| H[Pi-hole container] F -->|DNS health check| H H -->|rollback если упал| G subgraph "Трафик клиента" K[Клиент] -->|DNS запрос| H H -->|домен в ipset → mark| I[iptables] I --> J[WireGuard VPN] J --> L[Интернет] end ``` --- ## Классификация доменов Для каждого домена из `domains.txt` скрипт обращается к Chromium headless API и классифицирует результат: | Результат API | Действие | |---|---| | JSON с `relatedDomains` | **Сайт** — добавляется домен + все связанные субдомены | | JSON с `error: ERR_NAME_NOT_RESOLVED` или `Timeout` | **Мёртвый домен** — пропускается, в конфиг не попадает | | JSON с любой другой ошибкой (`ERR_CERT_*`, `ERR_CONNECTION_REFUSED` и т.д.) | **Сервис** — добавляется только сам домен без субдоменов (не HTTP-сервис: SMTP, VPN, игровой сервер и т.п.) | | API недоступен (не 200 / не JSON после всех попыток) | **Fallback** — домен всё равно добавляется без субдоменов; фиксируется в разделе отчёта `API FAILURES` | > Любой домен из `domains.txt` гарантированно попадает в финальный конфиг, даже если API недоступен. ### Формат выходных файлов Домены группируются по базовому домену с комментарием: ``` # example.com — 4 subdomains ipset=/example.com/bbrkn ipset=/static.example.com/bbrkn ipset=/api.example.com/bbrkn ... # some-service.com — 0 subdomains ipset=/some-service.com/bbrkn ``` --- ## Структура проекта ``` bbrkn/ ├── domains.txt # список доменов для обхода ├── Makefile # управление сборкой и деплоем ├── scripts/ │ ├── generate-configs.sh # генерация dnsmasq-конфигов │ └── deploy-to-gateway.sh # деплой в Pi-hole + rollback └── .forgejo/workflows/deploy.yaml # CI/CD на self-hosted runner ``` --- ## Makefile ```bash make all # generate + deploy (полный цикл) make generate # только сгенерировать конфиги make deploy # только задеплоить в Pi-hole make clean # удалить сгенерированные конфиги из /tmp make cache-clean # очистить кэш API-ответов (.cache/api/) make check # проверить синтаксис domains.txt ``` --- ## Переменные окружения ### generate-configs.sh | Переменная | По умолчанию | Описание | |---|---|---| | `DOMAINS_FILE` | `domains.txt` | Входной файл со списком доменов | | `IPSET_CONF` | `/tmp/91-ipset-bbrkn.conf` | Выходной файл правил ipset | | `RESOLVE_CONF` | `/tmp/92-resolve-bbrkn.conf` | Выходной файл правил server= | | `CHROME_SERVER` | `http://127.0.0.1:3000` | Адрес Chromium headless API | | `DNS_SERVER` | `8.8.8.8` | DNS-сервер для резолвинга через VPN | | `IGNORE_PARTS` | _(пусто)_ | Подстроки через пробел — домены с совпадением игнорируются | | `CACHE_DIR` | `.cache/api` | Директория кэша API-ответов | | `CACHE_TTL_DAYS` | `15` | Время жизни кэша в днях; `0` — отключить кэш | | `STRICT_MODE` | `0` | `1` — завершать с кодом 1 при любых ошибках API | | `DEBUG` | `0` | `1` — включить подробный лог | | `DEBUG_LOG` | `/tmp/generate-configs.debug.log` | Путь к файлу отладочного лога | ### deploy-to-gateway.sh | Переменная | По умолчанию | Описание | |---|---|---| | `TARGET_DIR` | `/opt/appdata/pihole/etc/dnsmasq.d` | Директория dnsmasq в Pi-hole | | `IPSET_CONF` | `/tmp/91-ipset-bbrkn.conf` | Сгенерированный файл ipset | | `RESOLVE_CONF` | `/tmp/92-resolve-bbrkn.conf` | Сгенерированный файл server= | | `DOCKER_CONTAINER` | `pihole` | Имя Docker-контейнера Pi-hole | | `DNS_LISTEN_ADDR` | `127.0.0.1` | Адрес, на котором Pi-hole слушает DNS | | `DNS_CHECK_DOMAIN` | `google.com` | Домен для DNS health check после рестарта | --- ## Кэш API-ответов При каждом запуске ответы Chromium API сохраняются в `$CACHE_DIR/.json`. При повторном запуске: - Если файл кэша **свежее** `CACHE_TTL_DAYS` дней → ответ берётся из кэша, Chromium не запускается. - Если файл **старше** TTL → перезапрашивается и кэш обновляется. - Если кэш **повреждён** (невалидный JSON) → перезапрашивается автоматически. На self-hosted runner workspace сохраняется между запусками, поэтому повторные CI-запуски значительно быстрее. ```bash # Сбросить кэш и запросить всё заново make cache-clean && make generate # Отключить кэш для одного запуска CACHE_TTL_DAYS=0 make generate ``` --- ## Деплой и автооткат `deploy-to-gateway.sh` выполняет следующие шаги: 1. Проверяет наличие сгенерированных конфигов. 2. Создаёт timestamped-бэкапы текущих конфигов в `TARGET_DIR`. 3. Копирует новые конфиги в `TARGET_DIR`. 4. Перезапускает контейнер Pi-hole. 5. Ждёт запуска (до 30 секунд, polling через `docker inspect`). 6. Выполняет DNS health check через `dig` / `nslookup`. 7. При ошибке на шагах 5 или 6 — **автоматически откатывается**: восстанавливает бэкапы и перезапускает контейнер со старой конфигурацией. --- ## Запуск вручную ```bash # Полный цикл make all # Только генерация (без деплоя) make generate # Dry-run — проверить что будет сгенерировано, ничего не записывать ./scripts/generate-configs.sh --dry-run # Отладочный режим DEBUG=1 make generate # Строгий режим — завершить с ошибкой если часть доменов не опросилась STRICT_MODE=1 make generate ``` --- ## CI/CD Workflow запускается при push в `main`, а также **вручную из UI** (`workflow_dispatch`). ``` push → main └─ make clean └─ make all ├─ generate-configs.sh (генерация с кэшем) └─ deploy-to-gateway.sh (деплой + rollback) └─ upload artifacts (конфиги + debug-лог, даже при падении) ``` Self-hosted runner работает непосредственно на шлюзе, поэтому деплой происходит локально без SSH. --- ## Пример отчёта ``` ===== DEBUG REPORT ===== Input file: domains.txt Raw input lines: 170 Processed lines: 170 Normalized OK: 165 Normalized skipped: 5 Ignored (IGNORE_PARTS): 12 Cache hits: 148 (TTL: 15d, dir: .cache/api) Cache expired/refetched: 3 API success (sites): 11 API error/failed: 3 Related domains added: 87 Final unique domains: 256 ---- VALID BASE SITES ---- example.com another-site.org ... ---- API FAILURES (added without subdomains) ---- some-domain.com → api_failed_added_without_subdomains ... ===== END DEBUG REPORT ===== ``` --- ## Зависимости | Инструмент | Где используется | |---|---| | `bash` 4.2+ | оба скрипта | | `curl` | запросы к Chromium API | | `jq` | парсинг JSON-ответов | | `docker` | перезапуск Pi-hole | | `ipset` | сброс маршрутов после деплоя | | `dig` или `nslookup` | DNS health check (опционально) | --- ## Лицензия MIT