No description
  • C# 54%
  • Vue 32.3%
  • TypeScript 5.5%
  • Shell 1.8%
  • Go 1.7%
  • Other 4.7%
Find a file
DSvinka 73ef6fa835
All checks were successful
build-push / build (./deploy/anubis-fork, deploy/anubis-fork/Dockerfile, anubis) (push) Successful in 34s
build-push / build (./deploy/caddy, deploy/caddy/Dockerfile, caddy) (push) Successful in 22s
build-push / build (., src/CaviCode.Dns.Api/Dockerfile, api) (push) Successful in 1m4s
build-push / build (./deploy/powerdns, deploy/powerdns/Dockerfile, powerdns) (push) Successful in 32s
build-push / build (./src/CaviCode.Dns.Web, src/CaviCode.Dns.Web/Dockerfile, web) (push) Successful in 1m9s
Фиксы WSS/HTTPS на стороне внешнего сервиса
2026-05-10 15:35:08 +03:00
.config Первая версия 2026-04-25 12:13:00 +03:00
.forgejo/workflows Чиним чиним CI 2026-04-26 16:15:58 +03:00
deploy Фиксы 2026-05-10 14:17:20 +03:00
docs Добавлена поддержка токенов и внешних сервисов 2026-05-02 22:04:40 +03:00
scripts Фиксим да брендируем 2026-04-25 23:10:56 +03:00
src Фиксы WSS/HTTPS на стороне внешнего сервиса 2026-05-10 15:35:08 +03:00
tests Фиксы WSS/HTTPS на стороне внешнего сервиса 2026-05-10 15:35:08 +03:00
.dockerignore Первая версия 2026-04-25 12:13:00 +03:00
.env.example Чиним логи 2026-04-27 23:54:55 +03:00
.gitignore Фикс Infrared рестарта 2026-05-10 02:31:45 +03:00
CaviCode.Dns.sln Первая версия 2026-04-25 12:13:00 +03:00
Changelog.md Фиксы WSS/HTTPS на стороне внешнего сервиса 2026-05-10 15:35:08 +03:00
docker-compose.override.example.yml Дополнительная конфигурация с Shared сетью 2026-05-03 03:44:06 +03:00
docker-compose.yml Фиксы 2026-05-10 14:17:20 +03:00
README.md Токены для изменения DNS 2026-05-10 04:04:53 +03:00

CaviCode-DNS

Self-hosted панель управления инфраструктурой: DNS, reverse-proxy, Minecraft-прокси и защита от ботов. Замена Cloudflare для одного сервера с одним публичным IP.

UI на Vue 3 закрывает CRUD для DNS-зон, RRset'ов, Reverse Proxy, Minecraft-маршрутов, статистику Anubis, бэкапы / Audit Log и получает live-обновления через SignalR-канал /hubs/live (см. src/CaviCode.Dns.Web/README.md).

Короткий обзор проекта, функций и технологий: docs/project-overview.md.

Брендовые ассеты (палитры, маскоты, логотип, шрифты) скопированы в src/CaviCode.Dns.Web/src/assets/branding, а публичные страницы Caddy и Anubis держат свои runtime-копии в deploy/.


Архитектура

                                      ┌─────────────┐
   53/UDP, 53/TCP   ── publicly ──▶  │   PowerDNS  │ ◀── HTTP API 8081
                                      │  (gpgsql)   │     (Headscale only)
                                      └─────┬───────┘
                                            ▲
                                            │ REST + X-API-Key
                                      ┌─────┴───────┐
   80, 443  ────── publicly ────▶    │    Caddy    │ ◀── admin 2019
                                      │  +powerdns  │     (Headscale only)
                                      │   ACME      │
                                      └─────┬───────┘
                                            ▲
                                            │ admin-API (JSON)
                                      ┌─────┴───────┐
                                      │   api       │ ◀── 5000
                                      │ (ASP.NET 9) │     (Headscale only)
                                      └─────┬───────┘
   25565/TCP  ──── publicly ────▶    ┌─────────────┐
                                      │   Infrared  │ ← proxies/*.yml (rw vol,
                                      └─────┬───────┘   пишется backend'ом)
                                            │
                                      ┌─────┴───────┐
                                      │   Anubis    │ ◀── REST 8924
                                      │  (форк)     │     (metrics 8923, REST 8924)
                                      └─────┬───────┘
                                            │
                                      ┌─────┴───────┐
                                      │ PostgreSQL  │  схемы pdns + panel
                                      └─────────────┘

Управляющие порты (PowerDNS API 8081, Caddy admin 2019, api 5000, Anubis metrics 8923, Anubis REST 8924) биндятся только на интерфейсе Headscale-сети (HEADSCALE_INTERFACE_IP). Снаружи доступны только 53, 80, 443, 25565.

Карта портов

Полный список портов, которые открывает стек:

Публичные (на всех интерфейсах, доступны из Интернета)

Порт Сервис За что отвечает
53/UDP PowerDNS Authoritative DNS. Сюда стучатся резолверы со всего мира за записями наших зон
53/TCP PowerDNS Тот же DNS, но TCP — для ответов >512 байт и AXFR-передач
80/TCP Caddy HTTP. Редирект на 443 + landing-страница для припаркованных доменов
443/TCP Caddy HTTPS. Все настроенные через панель сайты + UI самой панели
443/UDP Caddy HTTP/3 (QUIC). Альтернатива TCP/443 для современных браузеров
25565/TCP Infrared Minecraft-прокси. Единая точка входа для всех MC-серверов, маршрутизация по hostname

Приватные (биндятся только на ${HEADSCALE_INTERFACE_IP})

Доступны только из Headscale-оверлейной сети. Снаружи закрыты — nmap с публичного IP их не увидит.

Порт Сервис За что отвечает
8081/TCP PowerDNS HTTP API CRUD зон/записей. Используется backend'ом панели. Защищён X-API-Key (PDNS_API_KEY)
2019/TCP Caddy admin API Динамическая конфигурация прокси-сайтов. Используется backend'ом панели. Без auth — защита по сети
8923/TCP Anubis metrics Prometheus-метрики upstream-anubis (PoW counters, latency). Сейчас не используются панелью, но видны для grafana
8924/TCP Anubis REST/API gateway Side-car cavicode-api: /api/health, /api/stats, /api/reload-acl + Caddy forward_auth gateway
5000/TCP api (ASP.NET Core) REST API панели + SignalR-хаб /hubs/live. Снаружи доступен только через Caddy reverse-proxy
8080/TCP web (nginx + SPA) Vue 3 фронт. Снаружи попадает через Caddy. Доступен напрямую внутри сети для отладки

Внутренние (только Docker-сеть internal, не expose'ятся даже на хост)

Порты, которыми сервисы общаются между собой через internal Docker network. На хост не выставлены — обратиться к ним можно только из других контейнеров стека.

Порт Сервис За что отвечает
5432/TCP postgres PostgreSQL. Две схемы: pdns (для PowerDNS) и panel (для backend'а)
8080/TCP anubis Internal auth-check upstream внутри anubis-контейнера. Caddy ходит в sidecar anubis:8924, а sidecar проксирует unknown IP на 127.0.0.1:8080

Что нужно открыть на firewall'е публичного сервера

inbound: 53/udp, 53/tcp, 80/tcp, 443/tcp, 443/udp, 25565/tcp
outbound: всё (нужно для PowerDNS NOTIFY и Caddy ACME-DNS-01)

Headscale-интерфейс firewall'ом обычно не ограничивают — он уже изолирован сетевой топологией оверлея.

Live-обновления

UI получает события доменных изменений через SignalR-хаб /hubs/live (WebSocket-соединение проксируется через Caddy → api:5000). События:

Событие Когда срабатывает
ZoneUpdated создание / удаление зоны, upsert/delete RRset, restore-бэкап
ProxyUpdated CRUD прокси-сайта
CertRenewed завершён cert-renewal через Caddy admin API
McStatusChanged CRUD MC-маршрута, обновлён статус через ping
AnubisAclChanged добавлена/удалена запись в whitelist/blacklist
AnubisChallengeCompleted агрегированный апдейт статистики (с тротлингом)
BackupCompleted создан manual или scheduled бэкап (success/failure)

Контракты в src/CaviCode.Dns.Contracts/Live/LiveEvents.cs; клиент в src/CaviCode.Dns.Web/src/api/liveHub.ts. Доставка best-effort — ошибки публикации логируются и не откатывают бизнес-операцию.

Health-check

GET /api/v1/health параллельно проверяет 5 зависимостей: database, powerdns, caddy, anubis, infrared. Возвращает 200 OK при всех healthy, 503 при любой деградации. Эндпоинт требует обычную panel session или external integration token со scope health:read.

External Integration Tokens

В Settings → External integration tokens администратор создаёт scoped bearer tokens для внешних сервисов, например CaviCode-Tunnels. Plain token показывается только один раз при create/rotate; в БД хранится hash и prefix.

Token может иметь IP/CIDR allowlist, expiry или infinite lifetime, scopes и per-token rate limit. Для CaviCode-Tunnels обычно нужны scopes:

  • health:read
  • proxy:read
  • proxy:write
  • minecraft:read
  • minecraft:write
  • dns:read
  • dns:create

External token допускается только к ограниченной поверхности:

  • GET /api/v1/health
  • GET/POST/PUT /api/v1/proxy/sites... при соответствующем proxy scope
  • GET/POST/PUT /api/v1/mc/routes... при соответствующем Minecraft scope
  • GET/POST/PUT/DELETE /api/v1/zones... при соответствующем DNS scope

Users, settings, audit, backups, auth и destructive route operations вроде proxy/MC delete/reload/renew остаются доступны только обычной panel session.

Security tests cover token creation without plain persistence, expiry, revoke, rotate invalidation, IP allowlist, rate limiting and scope-denied permission flows used by CaviCode-Tunnels.

Темизация и язык

  • Темы панели: DemonesEngineer и SleepyPrincess из src/CaviCode.Dns.Web/src/assets/branding/themes/. Выбор сохраняется в localStorage с ключом cavicode-dns.theme и переключается в TopBar или в Settings → Внешний вид.
  • Публичные темы: в Settings → Публичные страницы админ выбирает отдельную тему для Anubis challenge/error UI и для страниц Caddy о парковке/служебных ошибках. Настройка хранится в panel_settings (public_pages.theme), а API генерирует CSS/JSON в deploy/public-theme/.
  • Языки: ru (по умолчанию) и en. Стандартные страницы (setup, login, 2FA, TopBar) полностью локализованы; остальные views пока на русском, переводы добавляются по мере правок. Auto-detect через navigator.language при первом заходе. Переключатель в TopBar (dropdown «RU/EN») и в шаге 1 setup-wizard'а.

Тема панели и язык сохраняются в localStorage per-browser; публичные темы сохраняются в БД и применяются ко всему стеку.

CI/CD

Сборка и публикация образов в Forgejo Container Registry автоматизирована через Forgejo Actions (.forgejo/workflows/build-push.yml):

  • Триггеры: push git-тега v* (релиз) или ручной workflow_dispatch.
  • Matrix-сборка пяти образов параллельно: powerdns, caddy, anubis, api, web. Тегаются как :<version> + :latest + :buildcache (для registry-cache следующих сборок).
  • На проде: docker compose pull && docker compose up -d тянет готовые образы, не пересобирает.

Подробнее — docs/deployment-registry.md.


Быстрый старт

1. Предварительные требования

  • Docker 24+ и Docker Compose v2.
  • Headscale-сеть настроена и активна (для управляющих API).
  • Первый администратор создаётся через UI-wizard на /setup при первом заходе — никаких секретов в .env не нужно.
  • Зарегистрированный домен с прописанными NS и glue records (см. docs/deployment.md).
  • Статический белый IP.

2. Подготовка окружения

# Клонировать репозиторий
git clone <repo-url> CaviCode-DNS
cd CaviCode-DNS

# Заполнить .env (пароли, API-ключи, домен)
cp .env.example .env
$EDITOR .env

# Сгенерировать секреты:
#   PDNS_API_KEY            — openssl rand -hex 32
#   POSTGRES_*_PASSWORD     — openssl rand -base64 24

# Первый admin создаётся через UI-wizard при первом открытии браузера
# на /setup — никаких пароль-в-plain в .env держать не нужно.

# Опционально: подкрутить пути / отладочные настройки
cp docker-compose.override.example.yml docker-compose.override.yml
$EDITOR docker-compose.override.yml

3. Скачать схему PowerDNS

PowerDNS-схема не лежит в репо — берём из upstream. У PowerDNS нет плавающей ветки auth-4.9.x, есть только конкретные теги (auth-4.9.0auth-4.9.14); подставляем последний из 4.9-серии, который совпадает с версией образа powerdns/pdns-auth-49:

curl -fsSL \
    https://raw.githubusercontent.com/PowerDNS/pdns/auth-4.9.14/modules/gpgsqlbackend/schema.pgsql.sql \
    -o deploy/postgres/pdns-schema.sql

# проверка — должно быть несколько KB SQL'я, не пустой файл и не HTML с 404
ls -lh deploy/postgres/pdns-schema.sql
head -3 deploy/postgres/pdns-schema.sql

Свежие теги: git ls-remote --tags https://github.com/PowerDNS/pdns 'auth-4.9.*'.

4. Поднять стек

# Сначала БД — она инициализируется первой и подтягивает схемы.
docker compose up -d postgres

# Дождаться, пока postgres станет healthy.
docker compose ps postgres

# Поднимаем остальное (PowerDNS, Caddy, Infrared).
docker compose up -d

Альтернативно — собрать образы у себя на dev-машине и тянуть из своего OCI-реестра (например, Forgejo Container Registry на git.dsvinka.ru). Тогда на проде нужны только docker-compose.yml + .env + docker compose pull. См. docs/deployment-registry.md.

5. Первый вход в админ-панель

После старта панель доступна только из Headscale-сети на http://${HEADSCALE_INTERFACE_IP}:8080/. Снаружи — никак, и это сознательный дефолт: админка не должна торчать в публичный интернет без явного намерения оператора.

Если временно нет Headscale-клиента — пробрось ssh-туннель:

ssh -L 8080:${HEADSCALE_INTERFACE_IP}:8080 user@server
# затем в браузере → http://localhost:8080/

Setup wizard

При первом заходе фронтенд видит, что в БД нет ни одного пользователя, и редиректит на /setup — мастер первичной настройки на 5 шагов:

  1. Внешний вид — выбор темы (DemonesEngineer / SleepyPrincess) и языка (ru / en). Применяется мгновенно, дальнейшие шаги уже на выбранном языке.
  2. Создание администратора — email, имя, пароль (≥10 символов). После submit cookie выдаётся сразу, дальнейшие шаги уже под этим аккаунтом.
  3. Двухфакторная аутентификация (опционально) — QR для Google Authenticator / Authy / 1Password / Яндекс.Ключ + 10 recovery-кодов. Можно пропустить и включить позже в Settings.
  4. Первая DNS-зона (опционально) — имя зоны (example.com) и NS-записи (заполняются автоматом из имени зоны).
  5. Публичный URL панели (опционально) — hostname (panel.example.com) и публичный IP сервера (определяется через whoami-сервис, можно изменить вручную). Создаётся сайт в Caddy с автоматическим TLS; если на шаге 4 создавалась зона и hostname в неё попадает — добавится A-запись, иначе wizard покажет warning, что DNS-запись надо завести вручную (или панель открывается через домен из стороннего DNS).

После завершения — переход в Dashboard.

⚠️ Wizard виден, только пока БД пуста. После создания первого администратора endpoint /api/v1/setup/admin возвращает 409 и редирект происходит на /login. Пройти повторно не получится — это by design.

(Опционально) Публичный домен для панели

Если хочется заходить в админку из обычного браузера — нужно добавить сайт panel.example.com → http://web:8080 в Caddy. Делается либо через UI самой панели после первого входа (раздел Reverse Proxy → добавить сайт с upstream http://web:8080), либо одной curl-командой из Headscale-сети:

curl -X POST http://${HEADSCALE_INTERFACE_IP}:2019/config/apps/http/servers/panel/routes \
    -H "Content-Type: application/json" \
    -d '{
      "match": [{"host": ["panel.example.com"]}],
      "handle": [{
        "handler": "subroute",
        "routes": [{
          "handle": [{
            "handler": "reverse_proxy",
            "upstreams": [{"dial": "web:8080"}]
          }]
        }]
      }]
    }'

Перед этим в PowerDNS должна быть запись panel.example.com IN A <PUBLIC_IP> — её можно добавить через UI панели после первого входа изнутри.

⚠️ Если открываешь панель в публичный интернет — обязательно включи 2FA (шаг 2 wizard'а), и используй длинный пароль (≥16 символов) для админа. Брутфорс поймает fail2ban (см. FAIL2BAN_* в .env.example), но первый барьер — длинный пароль.

6. Проверка инфраструктуры

См. подробный чек-лист в docs/deployment.md. Краткий smoke-test:

# DNS отвечает на запросы
dig @127.0.0.1 example.com SOA

# Caddy admin API отвечает (только из Headscale-сети)
curl http://${HEADSCALE_INTERFACE_IP}:2019/config/

# PowerDNS API отвечает
curl -H "X-API-Key: ${PDNS_API_KEY}" \
    http://${HEADSCALE_INTERFACE_IP}:8081/api/v1/servers/localhost/zones

# Infrared слушает MC-клиента
nc -zv 127.0.0.1 25565

# Health-check панели — все 5 проверок healthy
curl http://${HEADSCALE_INTERFACE_IP}:5000/api/v1/health | jq

# UI панели — изнутри Headscale-сети
curl -I http://${HEADSCALE_INTERFACE_IP}:8080/

7. Локальная разработка фронтенда (опционально)

cd src/CaviCode.Dns.Web
npm install
npm run dev   # http://localhost:5173, проксирует /api → http://localhost:5000

Подробнее — в src/CaviCode.Dns.Web/README.md.


Структура проекта

CaviCode-DNS/
├── .forgejo/workflows/                — Forgejo Actions: matrix-сборка 5 образов
├── docker-compose.yml                 — инфраструктура + api
├── docker-compose.override.example.yml
├── .env.example                       — переменные окружения
├── CaviCode.Dns.sln                   — solution с 5 src + 3 test проектами
├── CLAUDE.md                          — заметки для Claude Code (архитектура + gotchas)
├── deploy/
│   ├── postgres/                      — init.sh + pdns-schema-init compose-сервис
│   ├── powerdns/                      — Dockerfile + pdns.conf.template
│   ├── caddy/                         — Dockerfile + Caddyfile.bootstrap + landing/
│   ├── public-theme/                  — CSS/JSON выбранных публичных тем
│   ├── infrared/
│   │   ├── config.yml
│   │   └── proxies/                   — YAML-маршруты, пишутся панелью (rw bind-mount)
│   └── anubis-fork/                   — overlay-форк Anubis + cavicode-api sidecar
├── src/
│   ├── CaviCode.Dns.Domain/           — сущности и enum'ы (POCO)
│   ├── CaviCode.Dns.Contracts/        — DTO для REST + SignalR-события
│   ├── CaviCode.Dns.Application/      — бизнес-логика (Auth, Setup, Dns, Proxy, ...)
│   ├── CaviCode.Dns.Infrastructure/   — EF Core + HTTP-клиенты PowerDNS / Caddy / Anubis
│   ├── CaviCode.Dns.Api/              — ASP.NET Core endpoints + cookie auth + 2FA + SignalR
│   └── CaviCode.Dns.Web/              — Vue 3 SPA + i18n + брендовые ассеты
├── tests/
│   ├── CaviCode.Dns.Application.Tests/   — ~73 теста (services, fail2ban, totp, setup)
│   ├── CaviCode.Dns.Infrastructure.Tests/ — ~21 (HTTP-клиенты, infrared)
│   └── CaviCode.Dns.Api.Tests/          — SignalR publisher
├── docs/
│   ├── project-overview.md            — краткое описание проекта и технологий
│   ├── deployment.md                  — пошаговая установка + smoke-test
│   └── deployment-registry.md         — CI/CD через Forgejo Actions
└── scripts/
    ├── backup-zones.sh                — ручной экспорт зон в tar.gz
    ├── restore-zone.sh                — ручной импорт zonefile
    └── build-and-push.sh              — ручная сборка/публикация (альтернатива CI)

Лицензия и происхождение

Форк Anubis (deploy/anubis-fork/) использует upstream-проект TecharoHQ/anubis под MIT-лицензией. Подробности модификаций — в deploy/anubis-fork/CHANGELOG.md.