- C# 76.6%
- Vue 12.6%
- TypeScript 8.1%
- CSS 2%
- Dockerfile 0.4%
- Other 0.3%
| .config | ||
| .forgejo/workflows | ||
| CaviCode-SSO-Client@fd7d9dfec5 | ||
| scripts | ||
| src | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .gitmodules | ||
| CaviCode.Tunnels.sln | ||
| Development.md | ||
| Directory.Build.props | ||
| docker-compose.shared-network.example.yml | ||
| docker-compose.sso-bundled.yml | ||
| docker-compose.sso-external.yml | ||
| docker-compose.yml | ||
| global.json | ||
| README.md | ||
CaviCode-Tunnels
CaviCode-Tunnels - это система, которую вы разворачиваете у себя, чтобы безопасно публиковать внутренние сервисы с удаленных серверов через один контролируемый шлюз.
Проект нужен, когда есть несколько машин, контейнеров или домашних/офисных серверов за NAT, а наружу нужно аккуратно отдать только выбранные сервисы: панели, API, игровые серверы, админские инструменты или другие TCP-сервисы. Вместо открытия портов на каждом узле CaviCode-Tunnels строит управляемый туннель до шлюза и публикует наружу только явно разрешенные маршруты.
Система хорошо работает рядом с CaviCode-DNS: Tunnels отвечает за WireGuard, агентов, правила доступа и внутренние адреса сервисов, а CaviCode-DNS - за DNS, Caddy, TLS, Anubis, публичные reverse-proxy и Minecraft-маршруты.
Как это работает
CaviCode-Tunnels состоит из трех основных частей:
- Controller - центральная панель и API. Здесь хранится состояние узлов, сервисов, правил доступа, журнал действий и настройки интеграций.
- Gateway - WireGuard-шлюз. Через него удаленные узлы соединяются с вашей инфраструктурой.
- Agent - небольшой сервис на удаленном узле. Он подключается к Controller, получает конфигурацию и открывает локальный проброс портов только для разрешенных сервисов.
Обычный поток выглядит так:
- Администратор поднимает CaviCode-Tunnels на сервере-шлюзе.
- В панели создается одноразовый токен подключения для нового узла.
- На удаленном сервере запускается команда установки агента.
- Агент регистрируется, получает WireGuard-конфигурацию и начинает отправлять сигнал активности.
- Администратор выбирает сервис на узле, например
127.0.0.1:8080, и публикует его через шлюз. - Шлюз открывает только нужный порт, агент пропускает только разрешенный трафик, а CaviCode-DNS при необходимости создает публичный маршрут.
Если узел отозван, его ключ доступа становится недействительным, опубликованные сервисы отключаются, а агент при отказе Controller останавливает локальные пробросы.
Что умеет
- подключать удаленные узлы через одноразовые токены;
- поднимать встроенный WireGuard-шлюз;
- публиковать выбранные TCP-сервисы через один шлюз;
- импортировать сервисы из Docker-контейнеров на агенте;
- хранить журнал действий и изменений;
- показывать состояние узлов, сервисов, политик и проверок в панели управления;
- экспортировать резервную копию и собирать диагностический архив;
- работать с CaviCode-DNS в ручном режиме или через API-токен;
- отзывать узлы и блокировать повторное использование токенов.
Развертывание через Docker Compose
Для запуска нужен Linux-сервер с Docker и Docker Compose. Шлюз должен иметь
доступ к /dev/net/tun, возможность управлять сетевыми правилами и публично
доступный UDP-порт WireGuard.
Минимальный сценарий:
cp .env.example .env
Откройте .env и обязательно поменяйте секреты:
POSTGRES_PASSWORD=change-me-tunnels
Проверьте адреса и порты:
CONTROL_BIND_IP=127.0.0.1
WEB_PORT=18080
API_PORT=5080
WIREGUARD_BIND_IP=0.0.0.0
WIREGUARD_PORT=51820
Управляющую web-панель и API обычно лучше держать на 127.0.0.1 или приватном
адресе, а наружу отдавать через CaviCode-DNS/Caddy или другой reverse proxy.
WireGuard UDP-порт, наоборот, должен быть доступен удаленным агентам.
Запуск:
docker compose up -d
После старта откройте панель:
http://127.0.0.1:18080
Дальше мастер настройки проведет через создание первого администратора, проверку окружения и настройку шлюза.
Важные переменные
Основные параметры находятся в .env.example.
POSTGRES_PASSWORD должен быть изменен перед рабочим запуском.
CONTROL_BIND_IP управляет тем, на каком адресе слушают web/API порты. Для
публичной установки безопаснее оставлять 127.0.0.1 и публиковать панель
через защищенный reverse proxy.
WIREGUARD_BIND_IP и WIREGUARD_PORT задают публичный UDP-адрес шлюза.
Этот порт должен быть доступен агентам.
WIREGUARD_INSTALL_MISSING_TOOLS=false оставлен выключенным намеренно: Docker
image уже содержит wireguard-tools, iproute2, nftables и iptables.
Работающий контейнер не должен выполнять apt-get install во время настройки.
Подключение узла
После первичной настройки:
- Откройте раздел узлов.
- Создайте токен подключения.
- Скопируйте команду установки.
- Выполните ее на удаленном Linux-сервере.
- Дождитесь появления узла в панели.
Агент будет периодически отправлять сигнал активности и получать актуальную конфигурацию. Если узел перестанет выходить на связь, это будет видно в панели.
Публикация сервиса
Сервис публикуется только явно:
- Выберите узел.
- Укажите локальный адрес сервиса, например
127.0.0.1:8080. - Назначьте внешний порт или маршрут.
- Включите сервис.
CaviCode-Tunnels применит правила на шлюзе и отправит агенту конфигурацию локального проброса. Без опубликованного сервиса трафик через туннель не разрешается.
Интеграция с CaviCode-DNS
Интеграция может работать в двух режимах.
В ручном режиме CaviCode-Tunnels показывает готовые параметры сервиса и маршрута, которые можно перенести в CaviCode-DNS самостоятельно.
В API-режиме CaviCode-Tunnels использует внешний интеграционный токен из CaviCode-DNS и создает или обновляет reverse-proxy/Minecraft маршруты через официальный API CaviCode-DNS. Если указан внешний IP для DNS, Tunnels также создает отсутствующую A/AAAA-запись для hostname сервиса, не перезаписывая уже существующую запись. Токен хранится как защищенный секрет и не возвращается обратно через API или UI.
Если CaviCode-DNS и CaviCode-Tunnels запущены как два соседних Docker Compose
проекта, подключите оба проекта к общей внешней сети cavicode-shared
через docker-compose.shared-network.example.yml или локальный override.
Тогда reverse proxy upstream для панели Tunnels должен быть
http://cavicode-tunnels-web:80, а API URL интеграции DNS из Tunnels —
http://cavicode-dns-api:5000. Не используйте 127.0.0.1 как upstream между
контейнерами: внутри контейнера это сам контейнер, а не хост.
Для опубликованных сервисов в таком Docker-сценарии Caddy тоже не должен
ходить напрямую к WireGuard IP агента вроде 10.5.5.3:18000: этот адрес
виден API-контейнеру шлюза, но не соседнему Caddy-контейнеру. Shared-network
override включает gateway-side TCP proxy и экспортирует upstream как
http://cavicode-tunnels-api:<порт>; API уже пересылает соединение дальше в
WireGuard-туннель. При PublishedServices:UpstreamMode=GatewayProxy API сам
запускает listener-ы на портах опубликованных включенных сервисов; если Caddy
получает connection refused на cavicode-tunnels-api:<порт>, значит
API-контейнер запущен без этого режима, не пересоздан после обновления или
сервис отключен. Если соединение с Caddy устанавливается, но страница долго
висит без ответа, проверяйте доступность TunnelBindIp:TunnelBindPort из
API-контейнера в WireGuard-туннеле и agent-side forwarder на узле; API-side
proxy ограничивает попытку подключения параметром
GatewayServiceProxy:ConnectTimeoutSeconds.
Домен самой панели из PublicControllerUrl, например tunnels.example.com,
нельзя использовать как публичный домен сервиса или hostname DNS-маршрута. Для
сервисов выделяйте отдельные имена вроде git.example.com; иначе CaviCode-DNS
может начать отправлять /login панели на порт опубликованного сервиса.
Эксплуатация
В панели есть раздел операций для экспорта резервной копии, пробного восстановления, диагностического архива, свежих логов агентов и отката политик.
Восстановление сейчас работает только как предварительная проверка: оно показывает, что будет создано, обновлено или удалено, но не применяет изменения к рабочему состоянию. Опасные секции вроде ссылок на секреты, настроек транспорта и WireGuard-пиров показываются отдельными предупреждениями.
Для разработки
Информация для разработки, тестов, структуры решения и локальной сборки вынесена в Development.md.