NodeRoost

Open source · BSD-3-Clause · self-hosted

Панель управления headscale

Свой control-сервер Tailscale — и нормальная админка к нему.
Подключить машину, выдать доступ, пустить трафик через нужный узел — без правки policy.hujson и SSH на ноды.

headscale 0.29+ Docker Compose FastAPI + React агент для Linux RU / EN
panel.example.com/serversувеличить
Список серверов и устройств в NodeRoost

Зачем

Зачем панель, если headscale и так работает

Панель не заменяет headscale и не проксирует трафик. Она берёт на себя то, что иначе делается руками — в CLI и в файле политики.

ЗадачаС NodeRoostБез панели
Выдать доступправило «кто → куда → порт» мышьюправить policy.hujson руками
Изоляция личных устройствустройства не видят друг друга всегдаследить за этим самому в ACL
Подсеть за нодойкнопка на карточке сервераapprove-routes по номеру ноды в CLI
Трафик к нужному адресууказать домен — адрес панель обновит самаподобрать CIDR и следить за сменой IP
Маршрут на самой нодеагент применяет самSSH на каждую машину
Выход в интернету каждого устройства свой список шлюзовexit-node открыт всем, кто прошёл ACL
Кто сейчас онлайнистория и алерты в Telegramnodes list — только срез на сейчас
Подключить машинуключ и готовая команда под нужную ОСсоздать ключ в CLI и продиктовать команду
Где стоит серверфлаг страны по внешнему адресупомнить самому или лезть в whois
Бэкапснимок по расписанию, с проверкойкопировать sqlite-файл вручную
Ошибка в правилахоткат к предыдущей рабочей версииправить файл и применять заново

Сценарии

Одна сеть поверх любых провайдеров и NAT

Машины находят друг друга сами — в облаке, в офисе, дома, за чужим NAT. Белый адрес и проброс портов для этого не нужны, а кто с кем говорит, решаете вы.

Две локалки без белых IP

Офис и склад начинают видеть сети друг друга. На каждой стороне ставите по ноде, она объявляет свою подсеть, вы её одобряете — дальше правилами решаете, кто куда ходит. Белый адрес не нужен ни там, ни там.

Рабочий компьютер за чужим NAT

Серый адрес, роутер не ваш, попросить о пробросе порта некого. Ставите ноду на этот компьютер — и он появляется в сети. Доступ открываете только своему ноутбуку и только на нужный порт. TeamViewer и обратный туннель не нужны.

Админки и базы без открытых портов

База, IPMI, API кластера, морда роутера живут на адресах тайлнета. Наружу не открыт ни один порт, отдельный бастион не нужен. Доступ даёте на конкретный порт, а не на сервер целиком.

Подрядчику — только то, что нужно

Доступ выдаётся не на сервер целиком, а на порт: дали 5432 — подрядчик видит базу и больше ничего, ни SSH, ни админку. Выдать можно сразу на роль — группу серверов: добавили в неё ещё один, он появился у подрядчика сам. Закончил работу — удалили его устройство, и доступ исчез везде сразу.

Интернет через выбранный сервер

Помечаете сервер шлюзом — и трафик тех, кому вы его разрешили, выходит в интернет с его адреса. Переключается это в клиенте Tailscale самим человеком, но в списке у него только те шлюзы, которые вы ему дали.

Весь трафик сервера — через другой сервер

Сервер выходит в интернет через выбранный узел: его запросы приходят наружу с адреса шлюза — например, из нужной страны. Включается и выключается галкой в карточке сервера.

Обычно после такого сервер пропадает: ответы на входящие соединения уходят в туннель и не доходят обратно — рвётся SSH, перестаёт открываться сайт. Здесь этого нет: ответ уходит тем же путём, которым пришёл запрос, и сервер остаётся доступен по своему адресу.

Сервис пускает только с одного адреса

Партнёр пускает только с одного IP, а ходить к нему нужно с ноутбука. Указываете в панели: к этому адресу — через edge-1, чей IP у партнёра в списке. Запросы приходят к нему уже от edge-1, а весь остальной трафик ноутбука идёт напрямую.

Домашний NAS, камеры, Home Assistant

Ни белого IP, ни DDNS, ни проброса портов на роутере. Ставите ноду на домашний сервер и заходите к нему откуда угодно — с телефона, ноутбука, рабочего компьютера — так же, как заходили бы из дома.

Сервер ходит к серверу

Машины стоят у разных провайдеров и в разных дата-центрах, общей сети между ними нет: раннер льёт в базу, бэкап уезжает в хранилище, приложение стучится в соседний ДЦ.

Раньше ради этого открывали порт наружу и держали белый список адресов на файрволе, который ломался при каждом переезде. Теперь машины видят друг друга напрямую, а доступ — одно правило в панели.

В туннель уходит не весь трафик

Обычный VPN заворачивает в себя всё подряд. Здесь ноутбук выходит в интернет напрямую и быстро, а через сеть идёт только то, что вы указали: подсети офиса, нужные сервисы, отдельные адреса.

Сменился провайдер — ничего не сломалось

Переехали в другой дата-центр, провайдер выдал новый адрес, машину увезли из офиса домой. В тайлнете у неё прежний адрес и прежнее имя, так что правила, скрипты и закладки переписывать не нужно.

Вместо рассылки VPN-конфигов

Никаких .ovpn по почте. Человек получает одноразовый ключ и одну команду под свою систему — через минуту он в сети. Отобрать доступ — удалить устройство, а не перевыпускать конфиги всем остальным.

Закрыть SSH снаружи совсем

Пока сервер отвечает на 22-м порту, его круглосуточно перебирают чужие боты. Подключив ноду, порт на публичном интерфейсе можно закрыть насовсем — и заходить на адрес в сети, по правилу из панели.

Снаружи стучаться становится некуда, а список тех, кому вход открыт, лежит в одном месте и правится мышью.

Запасная дверь, когда основной путь закрыт

Провайдер зарезал порт, белый адрес под атакой, кто-то переписал правила на файрволе, лёг корпоративный VPN — а зайти надо сейчас. Ноде для связи достаточно исходящего доступа наружу, поэтому машина остаётся достижимой, когда снаружи к ней не подступиться.

Панель в самом соединении не участвует: даже если она недоступна, уже работающие подключения продолжают работать.

Железки, на которые ничего не поставить

IPMI, камеры, принтер, управляемый свитч, старый NAS — клиента туда не установить, а дотянуться надо. Ставите ноду на любую машину в той же сети, она отдаёт подсеть, вы её одобряете — и работаете со всем этим по внутренним адресам, как будто сидите рядом.

Доступ выдаётся по-прежнему правилом: кому и на какой порт.

Модель

Чем сервер отличается от устройства

headscale про это ничего не знает — для него все машины просто ноды.
Деление на серверы и устройства придумали мы, живёт оно в базе панели.
Разница в одном: к серверу можно открыть доступ, к устройству — нельзя.
Поэтому серверы ходят и друг к другу, а устройства друг друга не видят вовсе.

Сервер

К нему можно открыть доступ

  • Это то, к чему открывают доступ: база, гипервизор, роутер в офисе, машина со сборкой.
  • Сервер и сам бывает источником: раннер ходит в базу, бэкап — в хранилище, приложение — в соседний дата-центр.
  • Серверы можно собирать в роли и давать доступ сразу ко всей роли, а не к каждой машине по отдельности.
  • Сервер умеет пустить через себя свою локальную сеть или работать шлюзом выхода в интернет.
  • Агент ставится именно на серверы — он подхватывает маршруты, которые вы задали в панели.

Устройство

К нему доступ не открывают

  • Ноутбук, телефон, рабочий компьютер — всё, за чем сидит живой человек.
  • Устройству дают доступ к серверам. А вот дать доступ к самому устройству нельзя — такого правила не получится.
  • Устройства не видят друг друга: ноутбук коллеги не появится в списке, и вписать его адрес руками тоже не выйдет.
  • Здесь же вы разрешаете шлюзы выхода — какими из них человек сможет пользоваться.

Интерфейс

Как это выглядит

Четыре экрана, на которых происходит почти всё.

panel.example.com/nodes/12увеличить
Карточка сервера
Карточка сервера. Роли, объявленные подсети, режим шлюза выхода и список устройств, которым он разрешён, состояние агента.
panel.example.com/accessувеличить
Раздел доступов
Доступы. Правило — «кто → куда → по какому порту». Выдаётся сразу нескольким источникам; итоговый HuJSON собирает панель.
panel.example.com/routingувеличить
Раздел маршрутизации
Маршрутизация. Направления «источник → адрес → через какую ноду», с резолвом домена и автообновлением маршрута.
panel.example.com/serversувеличить
Список серверов
Серверы и устройства. Онлайн, адреса, роли, маршруты и срок ключа — одним экраном, без переключения контекста.

Как устроено

Панель управляет сетью, но трафик через неё не идёт

В headscale панель ходит по внутренней сети Docker — управляющий API наружу не выставлен.
Снаружи доступно только то, что нужно клиентам Tailscale: подключиться и получить карту сети.

УПРАВЛЕНИЕБраузерадминистраторОбратный проксиTLS · доверенные адресаNodeRoostпанель + Postgresheadscalecontrol-сервервнутренняя сеть Docker · управляющий API наружу не выставленмежду слоями ходят только ключи, карта сети, политика и настройки агентапользовательский трафик сюда не попадаетДАННЫЕнода Aнапример, ноутбукнода Bнапример, серверWireGuard — напрямую между узламиDERP включается, только если прямое соединение не установилось увеличить

Установка

Docker Compose рядом с headscale

Панель, Postgres и сам headscale поднимаются одним файлом. Обратный прокси выдаёт TLS и держит панель за списком доверенных адресов.

# 1. забрать репозиторий
git clone https://github.com/mihsergeev/NodeRoost.git
cd NodeRoost

# 2. домены, секреты, список доверенных адресов
cp .env.example .env && $EDITOR .env

# 3. поднять
docker compose up -d

# 4. подключить ноду — команду выдаёт панель
curl -fsSL https://control.example.com/agent/<токен>/setup | sh

Что понадобится

headscale
0.29+, режим политики database
хост
Docker с Compose, 1 vCPU / 1 ГБ
домены
один для панели, один для control-сервера
прокси
любой с TLS; в комплекте пример для caddy-docker-proxy
образы
ghcr.io/mihsergeev/noderoost-backend и -frontend

Безопасность

Безопасно по умолчанию

Всё перечисленное работает сразу после установки — включать или настраивать отдельно ничего не нужно.

  • Двухфакторный вход и журнал действий. Пароль и TOTP, ограничение попыток подбора и запись того, кто и что менял. С дефолтным паролем администратора панель просто не поднимется.
  • Личные устройства защищены сами по себе. Ноутбуки и телефоны ходят к серверам, но не видят друг друга. Это заложено в сборку политики, а не в галочку, которую можно забыть поставить.
  • Доступ отзывается одним действием. Удалили устройство — оно исчезло из всех правил разом. Перевыпускать конфиги и править списки не нужно.
  • Неудачная политика не оставит без доступа. Панель проверяет собранный HuJSON, применяет его и возвращает предыдущую рабочую версию, если headscale отказал.
  • Защищённый контур — в комплекте. В репозитории лежит готовый пример обратного прокси: TLS, список доверенных адресов, отдельная сеть под панель и закрытый снаружи управляющий API headscale. Разворачивается как есть.
  • Исходящие запросы под контролем. Адрес, куда панель шлёт алерты, проверяется при сохранении: во внутреннюю сеть и открытым HTTP запрос не уйдёт.
  • Сборку можно проверить. Зависимости зафиксированы с хешами, базовые образы — по дайджестам, на каждый коммит гоняются тесты и аудит зависимостей. Код открыт под BSD-3: всё написанное здесь можно прочитать.
panel.example.comувеличить
Экран входа в NodeRoost
Вход: пароль, второй фактор и ничего лишнего.