MAATRIX / Блог / Tailscale + Headscale: свой control-сервер на Ubuntu 24.04

Tailscale + Headscale: свой control-сервер на Ubuntu 24.04

MAATRIX

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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →