Tailscale + Headscale: свой control-сервер на Ubuntu 24.04
Tailscale удобен, пока вас устраивает, что весь координационный трафик и метаданные сети проходят через чужой облачный control-plane. Как только появляется требование держать инфраструктуру под своим контролем — комплаенс, корпоративная политика или просто желание не зависеть от чужого сервиса — на сцену выходит Headscale: открытая реализация control-сервера Tailscale, которую можно поднять на своей VPS. Ниже — рабочий процесс развёртывания на Ubuntu 24.04: от установки до ACL-политик.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как устроена связка Tailscale и Headscale
Tailscale-клиент не передаёт трафик между узлами напрямую через какой-то центральный сервер — он строит mesh-сеть на WireGuard, где узлы по возможности соединяются point-to-point (peer-to-peer через NAT traversal), а если это не удаётся — трафик идёт через relay-серверы DERP (Designated Encrypted Relay for Packets). Отдельно от передачи данных существует control-plane — сервис, который:
- хранит реестр узлов сети (кто есть кто, какие у них ключи WireGuard);
- раздаёт клиентам актуальную карту сети (кто с кем может соединяться);
- выдаёт и ротирует ключи, обрабатывает регистрацию новых устройств;
- применяет ACL-политики — кому что можно.
По умолчанию control-plane — это облако login.tailscale.com, управляемое Tailscale Inc. Headscale реализует тот же протокол control-сервера с открытым кодом, поэтому официальный клиент tailscale можно направить на свой сервер флагом --login-server, и он не заметит разницы: команды tailscale up, tailscale status, MagicDNS, ACL — всё работает так же, только все данные остаются у вас.
Важный нюанс: Headscale — это только control-plane. Сам трафик между узлами, как и в обычном Tailscale, идёт напрямую или через DERP-релеи. Headscale по умолчанию использует публичную DERP-карту Tailscale (это нормально и не противоречит идее «свой control-сервер» — приватность метаданных и списка узлов вы уже получаете), но при желании можно поднять и собственный DERP-релей.
Подготовка сервера и DNS
Headscale — лёгкий сервис (это одиночный Go-бинарник плюс SQLite или PostgreSQL), поэтому подойдёт скромная VPS: 1 vCPU и 1 ГБ RAM с запасом хватит на сотни узлов, если это не крупная корпоративная сеть на тысячи устройств. Понадобится:
- сервер на Ubuntu 24.04 с публичным IP;
- домен или поддомен, например
hs.example.com, с A-записью на IP сервера; - открытые порты: 443/tcp (HTTPS для control-plane и, при необходимости, DERP через тот же порт), опционально 3478/udp, если поднимаете собственный STUN/DERP.
Проверьте DNS перед установкой:
dig +short hs.example.com
Если запись ещё не резолвится — подождите распространения перед выпуском TLS-сертификата, иначе Let's Encrypt откажет в выдаче.
Обновите систему и откройте порт в файрволе:
apt update && apt upgrade -y
ufw allow 443/tcp
ufw allow OpenSSH
ufw enable
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверУстановка Headscale
Headscale распространяется как .deb-пакет на странице релизов GitHub. Возьмите актуальную версию — не хардкодьте номер, а посмотрите тег последнего релиза:
HS_VERSION=$(curl -s https://api.github.com/repos/juanfont/headscale/releases/latest | grep '"tag_name"' | cut -d '"' -f4)
echo $HS_VERSION
wget "https://github.com/juanfont/headscale/releases/download/${HS_VERSION}/headscale_${HS_VERSION#v}_linux_amd64.deb"
dpkg -i "headscale_${HS_VERSION#v}_linux_amd64.deb"
Пакет ставит бинарник, systemd-юнит headscale.service и каталог конфигурации /etc/headscale/. Базовый конфиг лежит в /etc/headscale/config.yaml — правим ключевые поля:
server_url: https://hs.example.com
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
noise:
private_key_path: /var/lib/headscale/noise_private.key
dns:
magic_dns: true
base_domain: ts.example.com
Обратите внимание: listen_addr смотрит на 127.0.0.1 — сам Headscale работает по HTTP на локальном порту, а наружу его отдаёт reverse-proxy с TLS. Для небольшой сети (до нескольких сотен узлов) SQLite вполне достаточно; PostgreSQL имеет смысл при большем масштабе или когда нужна репликация состояния.
base_domain для MagicDNS должен отличаться от домена самого control-сервера (обычно это отдельный поддомен вроде ts.example.com) — так каждый узел получит имя вида laptop.ts.example.com.
Создайте каталог для данных и запустите сервис:
mkdir -p /var/lib/headscale
systemctl enable --now headscale
systemctl status headscale
Если сервис не стартует — почти всегда причина в правах на /var/lib/headscale или синтаксической ошибке в YAML (проверьте через headscale configtest перед запуском).
Reverse proxy и TLS
Headscale слушает только на loopback, поэтому наружу его нужно отдать через nginx или Caddy с валидным сертификатом — клиент Tailscale не подключится к control-серверу без TLS. Caddy проще: он сам выпускает и обновляет сертификат Let's Encrypt.
apt install -y caddy
/etc/caddy/Caddyfile:
hs.example.com {
reverse_proxy 127.0.0.1:8080
}
systemctl reload caddy
Если вместо Caddy используете nginx — не забудьте про заголовки для gRPC/HTTP2, которые использует Headscale, и про proxy_http_version 1.1 с Upgrade/Connection для вебсокетов:
server {
listen 443 ssl http2;
server_name hs.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
TLS-сертификат для nginx можно получить через certbot — этот процесс подробно разобран в статье про Caddy с авто-SSL на Ubuntu 24.04, если решите сравнить оба варианта.
После настройки прокси проверьте, что сервер отвечает:
curl -I https://hs.example.com/health
Ожидаемый ответ — 200 OK.
Подключение клиентов и управление узлами
Headscale работает через понятие «пользователей» (namespace для группировки узлов) и preauth-ключей для регистрации без интерактивного подтверждения.
Создайте пользователя:
headscale users create ivan
Сгенерируйте preauth-ключ (можно сделать одноразовым или многоразовым, с ограничением по времени):
headscale preauthkeys create --user ivan --expiration 24h --reusable
Команда выведет ключ вида nodekey:... — используйте его на клиенте.
На стороне клиента (Linux, macOS, Windows, iOS, Android — везде одинаковый флаг) устанавливаете обычный официальный клиент Tailscale и указываете свой control-сервер вместо облачного:
tailscale up --login-server https://hs.example.com --authkey <preauth-key>
Без preauth-ключа регистрация тоже работает, но потребует ручного подтверждения на сервере:
tailscale up --login-server https://hs.example.com
# клиент выведет ссылку headscale.example.com/register/<id>
headscale nodes register --user ivan --key <id-из-ссылки>
Список подключённых узлов и их статус:
headscale nodes list
Здесь видно IP из диапазона 100.64.0.0/10 (стандартная адресация Tailscale/CGNAT-диапазон), имя узла, последний онлайн и теги, если они назначены. Удалить узел из сети:
headscale nodes delete -i <node-id>
Установка самого клиента tailscale на Ubuntu, если ещё не сделали, — через официальный репозиторий (curl -fsSL https://tailscale.com/install.sh | sh); базовая настройка WireGuard-туннелей на сервере отдельно разобрана в статье про WireGuard на Ubuntu 24.04, если хотите сравнить подходы «руками» и через Tailscale/Headscale.
ACL-политики: кто с кем может соединяться
По умолчанию все узлы одного пользователя видят друг друга, а между разными пользователями связи нет. Для более тонкой сегментации (например, «сервер бэкапов доступен только с ноутбуков админов, а не со всех устройств») используются ACL — файл в формате HuJSON или YAML.
Путь к файлу указывается в config.yaml:
policy:
path: /etc/headscale/acl.yaml
Пример политики с тегами и группами:
groups:
group:admins:
- ivan
- petr
tagOwners:
tag:servers:
- group:admins
acls:
- action: accept
src:
- group:admins
dst:
- tag:servers:22,443
- action: accept
src:
- "100.64.0.5"
dst:
- "100.64.0.10:5432"
Первое правило: все узлы группы admins могут подключаться к любому узлу с тегом servers по портам 22 и 443. Второе — точечный доступ конкретного узла к конкретному порту PostgreSQL на другом узле. Тег на клиенте назначается при регистрации:
tailscale up --login-server https://hs.example.com --authkey <key> --advertise-tags=tag:servers
но реально применить тег может только владелец, указанный в tagOwners — это защищает от того, что скомпрометированный клиент сам себе назначит привилегированный тег.
После правки ACL перезагружать весь Headscale не обязательно — файл политики читается заново при следующем обращении клиента, но для проверки синтаксиса удобно прогнать:
headscale policy check
Если работаете с несколькими сетевыми периметрами (например, отдельно продакшн и стейджинг), ACL можно комбинировать с обычным UFW на самих узлах — Headscale управляет тем, кто в принципе видит узел в mesh-сети, а локальный файрвол — что именно разрешено на нём самом; базовые принципы настройки описаны в статье про UFW на Ubuntu 24.04.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Headscale — это полноценная замена облаку Tailscale?
По функциональности control-plane — да, включая MagicDNS, ACL, preauth-ключи, теги. Но это open-source проект, поддерживаемый сообществом, а не самой Tailscale Inc., поэтому часть новых фич официального облака (например, свежие возможности SaaS-консоли) может появляться в Headscale с задержкой или не появляться вовсе.
Обязательно ли поднимать свой DERP-релей?
Нет. Headscale по умолчанию использует публичную DERP-карту Tailscale, и трафик между узлами всё равно шифруется WireGuard end-to-end — DERP видит только зашифрованные пакеты. Свой DERP имеет смысл, если хотите контролировать даже путь релея (например, по требованиям к локализации трафика) или снизить задержку за счёт релея ближе к вашим серверам.
Что будет с сетью, если Headscale-сервер уйдёт в оффлайн?
Уже установленные WireGuard-туннели между узлами продолжат работать — Headscale не находится в пути трафика. Пострадает только регистрация новых узлов и обновление ACL/карты сети до восстановления сервера.
Можно ли мигрировать с облачного Tailscale на свой Headscale без переустановки клиентов?
Да, на клиенте достаточно выполнить tailscale down, затем tailscale up --login-server https://hs.example.com --force-reauth с новым preauth-ключом — переустанавливать сам пакет tailscale не требуется.
Нужен ли отдельный сервер под Headscale или можно на том же, где крутятся другие сервисы?
Можно на том же — сервис лёгкий. Единственное ограничение — порт 443 должен быть свободен под reverse-proxy Headscale, либо разносите сервисы по поддоменам на одном общем nginx/Caddy.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →