Медведь-талисман NordBastion стоит в тёмном северном каменном зале, положив одну лапу на резной рунический обелиск-краеугольный камень; над ним парит циановое голографическое кольцо ключей, а пять светящихся глифов-устройств — ноутбук, телефон, планшет, серверная башня и глобус — напрямую связаны друг с другом в яркую циановую mesh-сеть над головой, а от кольца к каждому устройству тянутся едва заметные пунктирные линии-ключи; позади него — серверная стойка и арочное окно, освещённое северным сиянием
Инструкция · Приватные сети·19 мин чтения · 50 мин практики

Собственный координационный сервер.
Карту — вот что стоит держать у себя.

Mesh VPN нуждается в ком-то, кто будет раздавать ключи. У Tailscale этот кто-то — компания; при обычном WireGuard это вы сами, вручную и навсегда. Headscale — третий ответ: тот же клиент с открытым исходным кодом, но он обращается к координационному серверу на машине, которую вы арендуете анонимно. Шесть шагов к tailnet, в котором карта устройств, правила доступа и резервные ретрансляторы — целиком ваши: Headscale за Caddy, письменно зафиксированные ACL, MagicDNS, указывающий на ваш собственный резолвер, маршрутизатор подсети, выходной узел и встроенный DERP. Машина стоит $3.90 в месяц. Проверено на Debian 12.

Шесть шагов
  1. 01

    Выбор

    WireGuard, Tailscale, Headscale

  2. 02

    Провизионирование

    Три имени, зафиксированных до запуска

  3. 03

    Развернуть

    Headscale, Caddy, один конфиг

  4. 04

    Регистрация

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

  5. 05

    Маршрутизация

    Подсети, выходной узел, ACL

  6. 06

    Keep

    DERP, резервные копии, обновления

Перед началом · Что такое уровень управления

Трафик не может прочитать никто. Но карту по-прежнему кто-то держит.

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

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

А вот что он действительно держит — это карту, и карта интереснее, чем принято считать. Имя каждого устройства и имя каждого пользователя. Каждый публичный ключ. Публичный адрес, с которого прямо сейчас подключается каждое устройство, — то есть живую запись о том, где физически находятся ваш ноутбук и ваш телефон. Моменты, когда каждое устройство подключается и отключается, — то есть описание вашего дня. И политику, которая решает, кто до чего может достучаться, — то есть описание вашей организации. Ничего из этого не требует злого умысла ни от кого; это просто то, для чего и существует координационный сервер.

Есть ещё одно свойство, которое важнее метаданных. Уровень управления — это то, что распространяет ключи, а значит, и то, что может добавить лишний. Координационный сервер, который скомпрометирован или принуждён к сотрудничеству, может сообщить вашим устройствам, что новая машина — легитимный участник сети, — и ваши устройства, делая именно то, для чего они созданы, начнут шифровать трафик и для неё тоже. Tailscale относится к этому достаточно серьёзно, чтобы встроить против этого механизм подписи. Самостоятельный хостинг решает ту же проблему с другой стороны: тем, кто мог бы так поступить, оказываетесь вы сами. Четыре варианта — и что на самом деле выдаёт каждый из них:

Свойство WireGuard вручную Tailscale, облачный Headscale на вашем VPS Headscale + собственный DERP
Кто может прочитать полезную нагрузкуНикто, кроме самих узловНикто, кроме самих узловНикто, кроме самих узловНикто, кроме самих узлов
Кто держит карту устройствВы — в текстовом файлеКомпания, в своей юрисдикцииВы — на своём VPSВы — на своём VPS
Кто видит, откуда подключаются устройстваТолько сами узлыКоординационный сервер — постоянноВаш сервер — постоянноВаш сервер — постоянно
Кто ретранслирует, когда прямое соединение не удаётсяНикто — соединение просто не устанавливаетсяПарк ретрансляторов провайдераПо умолчанию — всё ещё ретрансляторы провайдераВаш ретранслятор в вашем дата-центре
Добавление двадцатого устройстваДевятнадцать правок конфигурацииОдна командаОдна командаОдна команда
Выбирайте, когдаМало устройств, и ни одно не перемещаетсяВы хотите нулевую эксплуатационную нагрузку и готовы смириться с картойВам нужна карта, и вы можете обслуживать одну небольшую машинуВам нужна и карта, и резервный путь тоже

Первая колонка — не соломенное чучело. Для статичной горстки серверов WireGuard, настроенный вручную, — это действительно более грамотное инженерное решение, да ещё и на один сервис для патчинга меньше. Это руководство — о том моменте, когда таблица перестаёт быть смешной.

Шаг 01 · Выбор

Нужен ли вам уровень управления? Посчитайте устройства, которые перемещаются.

На вопрос «я хочу, чтобы мои устройства приватно связывались друг с другом» есть три честных ответа, и это руководство — только про один из них. Обычный WireGuard — самый минималистичный: модуль ядра, один конфигурационный файл на узел, никакого демона, кроме самого интерфейса, и ничего не нужно поддерживать в актуальном состоянии, кроме операционной системы. Если ваши устройства — это серверы с фиксированными адресами и их можно пересчитать по пальцам одной руки, остановитесь здесь и читайте вместо этого руководство по WireGuard. В итоге у вас будет меньше кода и короче список того, что может сломаться.

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

Headscale — третий ответ, и он более узкий, чем оба предыдущих: независимая реализация координационного сервера с открытым исходным кодом, которую запускаете вы сами, а обращается к ней тот же официальный клиент. Вы сохраняете автоматизацию и обход NAT, но берёте на себя небольшой сервис в эксплуатации, неполный набор функций и обновления, которые нужно читать, а не просто ставить. Не стоит делать вид, что это бесплатно. Взамен вы получаете ровно одну вещь — ту самую, которая важна для этого сайта: карту не держит никто, кроме вас.

Порог в пять устройств

Ручная конфигурация WireGuard растёт как квадрат размера парка устройств: четыре узла — это шесть связей, десять узлов — сорок пять, двадцать узлов — сто девяносто. Добавьте один кочующий ноутбук, чей адрес меняется при подключении к каждой новой сети, — и обслуживание перестаёт быть эпизодическим. На практике порог перехода — где-то пять устройств, либо первое устройство, которое перемещается ежедневно, либо первое правило доступа, которое уже нельзя выразить как «всем можно достучаться до всего».

Ещё одна вещь, которую стоит решить сейчас, а не потом, потому что она влияет на то, что именно вы будете устанавливать: предназначена ли эта сеть только для вас или к ней присоединятся другие люди. Tailnet для одного человека может внутри оставаться полностью открытым, и ничего страшного не случится. Tailnet, в котором есть коллега, подрядчик или член семьи, нуждается в письменных правилах доступа с первой же недели — это шаг 05 — и в том, чтобы у пользователей были раздельные учётные записи, а не один логин на всех. Решить это заранее стоит один абзац; переделывать потом — уже полдня работы.

Шаг 02 · Подготовка

Три имени, определённых до первого запуска. Изменить любое из них потом трудно.

Сначала закажите машину. В панели: Заказать → VPS → Sentinel, образ Debian 12, в любом бастионе, где вы хотите разместить карту, — Хельсинки, Стокгольм, Осло или Рейкьявик. Для открытия аккаунта не требуется адрес электронной почты, а счёт оплачивается в Monero, Bitcoin, Lightning или любом другом поддерживаемом активе. Выбирайте бастион ближе к большинству ваших устройств, если вы также планируете запустить ретранслятор из шага 06: задержка до ретранслятора — это задержка на каждом соединении, которое он несёт.

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

Определите эти три параметра до того, как что-либо зарегистрируется

Каждое устройство встраивает все три параметра в своё состояние в момент подключения, поэтому изменить один из них позже означает вручную перерегистрировать весь парк устройств. Первое: URL координационного сервера — точный публичный адрес HTTPS, на который будет обращаться каждый клиент, включая схему и имя хоста. Второе: DNS-суффикс tailnet — домен, на который будет отвечать MagicDNS внутри сети; он должен быть под вашим контролем и не должен быть родительским или дочерним доменом по отношению к имени хоста самого координационного сервера, — это самая частая причина сбоя при первом запуске. Третье: диапазоны адресов, которые раздаёт tailnet, — по умолчанию это блок carrier-grade NAT 100.64.0.0/10 для IPv4 и уникальный локальный /48 для IPv6; оставьте значения по умолчанию, если они уже не конфликтуют с сетью, которой вы пользуетесь.

Разобранный пример, который стоит взять за образец: координационный сервер отвечает на vpn.example.com, суффикс tailnet — ts.example.com, а диапазоны адресов остаются стандартными. Обратите внимание: суффикс — не потомок хоста координационного сервера, а как бы «сосед» по домену — vpn.example.com и ts.example.com используют общий регистрируемый домен, но ни один не содержит другого. Направьте на машину запись A — и AAAA, если нужна доступность по IPv6, — и дайте ей время распространиться, прежде чем начинать: обратный прокси попытается получить сертификат для этого имени сразу при первом запуске.

Затем напишите правила файрвола, пока ещё ничего не слушает порты. Координационный сервер по необходимости — публичная точка входа HTTPS: до него должно быть возможно достучаться с любого устройства, из любой сети. UDP-порт нужен, только если вы планируете запустить собственный ретранслятор на шаге 06, и открыть его сейчас проще, чем потом отлаживать его отсутствие.

ufw default deny incoming
ufw allow 47222/tcp                  # administrative SSH, moved
ufw allow 80/tcp                     # ACME HTTP-01 challenge
ufw allow 443/tcp                    # the control server, and DERP over HTTPS
ufw allow 3478/udp                   # STUN, only if you run your own relay
ufw enable
ufw status numbered

Устанавливайте Docker из официального репозитория, а не из пакета дистрибутива, — тогда Compose v2 будет на месте и актуальной версии. Затем создайте два каталога, в которых будет храниться всё, что стоит резервировать, — один для конфигурации, один для состояния, — и не поддавайтесь искушению класть туда что-то ещё.

curl -fsSL https://get.docker.com | sh
docker compose version

install -d -m 0750 /srv/headscale/config
install -d -m 0750 /srv/headscale/data
cd /srv/headscale
Шаг 03 · Установка

Два контейнера и один файл. Всегда закрепляйте версию.

Headscale и Caddy — и больше ничего. Headscale слушает обычный HTTP внутри сети Docker; Caddy терминирует TLS, получает сертификат при первом запуске и обновляет его без напоминаний. Слушатель метрик остаётся на localhost, потому что это внутренняя отладочная поверхность, а не публичная.

Одно правило прежде, чем открывать файл: закрепите образ на точной версии. Headscale — это программа с версией ниже 1.0, в которой не раз переименовывались ключи конфигурации и подкоманды CLI между минорными релизами. Плавающий тег превращает рутинный перезапуск контейнера в простой сервиса, чья единственная задача — дать вам доступ ко всему остальному.

/srv/headscale/docker-compose.yml

services:
  headscale:
    image: docker.io/headscale/headscale:0.26            # pin it; never :latest
    container_name: headscale
    command: serve
    volumes:
      - ./config:/etc/headscale
      - ./data:/var/lib/headscale
    ports:
      - "3478:3478/udp"                                  # STUN, for your own DERP
    restart: unless-stopped

  caddy:
    image: caddy:2-alpine
    container_name: caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
    restart: unless-stopped

volumes:
  caddy_data:

/srv/headscale/Caddyfile

vpn.example.com {
    reverse_proxy headscale:8080
}

Это и есть вся конфигурация прокси. Caddy говорит по HTTP/2 и без дополнительных указаний повышает уровень долгоживущих соединений, что здесь важно, поскольку каждый клиент держит одно такое соединение открытым для получения обновлений карты. Возьмите пример конфигурационного файла, который поставляется вместе с версией, которую вы закрепили, и отредактируйте его, а не пишите с нуля — файл длинный, большая часть в нём — разумные значения по умолчанию, а важные ключи — вот эти:

/srv/headscale/config/config.yaml — the lines that matter

server_url: https://vpn.example.com          # exactly what clients will dial
listen_addr: 0.0.0.0:8080                    # inside the Docker network only
metrics_listen_addr: 127.0.0.1:9090          # debug surface, keep it local

prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48

noise:
  private_key_path: /var/lib/headscale/noise_private.key

database:
  type: sqlite                               # postgres for a large fleet
  sqlite:
    path: /var/lib/headscale/db.sqlite

dns:
  magic_dns: true
  base_domain: ts.example.com                # NOT a parent or child of server_url
  nameservers:
    global:
      - 1.1.1.1                              # replaced by your own in step 05

Запустите его и затем читайте лог, а не гадайте. Если при первом запуске не удаётся получить сертификат или отклоняется DNS-суффикс, потому что он пересекается с именем хоста сервера, — сервис ясно сообщит об этом и остановится.

docker compose up -d
docker compose logs -f headscale

# From your laptop — a control server that is up answers this:
curl -s https://vpn.example.com/health

При первом запуске в /srv/headscale/data генерируется закрытый ключевой материал. Скопируйте его в надёжное место прямо сейчас, до того как зарегистрируется хотя бы одно устройство, и ещё раз — после первой регистрации: потеря этого ключа означает, что каждое устройство отвергнет сервер, которому доверяло, и единственное решение — вручную перерегистрировать их все. Шаг 06 превращает эту копию в настоящую резервную копию.

Шаг 04 · Регистрация

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

Команда headscale живёт внутри контейнера, поэтому любое административное действие — это docker exec. Сначала создайте пользователя — пользователь является владельцем устройств и той единицей, на которую ссылаются правила доступа, поэтому давайте ему имя человека или роли, а не машины.

hs() { docker exec -it headscale headscale "$@"; }

hs users create alice
hs users list                      # note the numeric id — newer releases want it

# A single-use key that expires in an hour is the right default.
hs preauthkeys create --user 1 --expiration 1h

Затем запустите официальный клиент на устройстве, указав ему ваш сервер вместо сервера по умолчанию. Это официально поддерживаемый флаг, а не обходной приём — клиент имеет открытый исходный код, а сервер входа — это просто параметр.

# On the device (Debian/Ubuntu shown; every platform has an equivalent):
curl -fsSL https://tailscale.com/install.sh | sh

sudo tailscale up \
  --login-server https://vpn.example.com \
  --auth-key <the-key-you-just-created>

tailscale status
tailscale ip -4

Без ключа предварительной аутентификации клиент вместо этого выводит URL; переход по нему показывает команду регистрации, которую нужно выполнить на сервере, — это интерактивный путь, удобный для устройства, за которым вы сидите прямо сейчас. В любом случае, не празднуйте, пока не подключили второе устройство — mesh из одного устройства ничего не доказывает. Пропингуйте его по адресу tailnet, а затем внимательно изучите вывод статуса:

hs nodes list

# On a client, the word after the peer name is the one that matters:
tailscale status
#   100.64.0.2  laptop  alice  linux  active; direct 203.0.113.9:41641
#                                              ^^^^^^ direct, not relay

tailscale netcheck                 # why it chose what it chose

direct означает, что два устройства нашли друг друга и трафик идёт напрямую, точка-точка. relay означает, что он проходит через сервер DERP — это тоже работает, но добавляет задержку и ставит на путь третью машину, причём по умолчанию она принадлежит не вам. Шаг 06 посвящён тому, как добиться нужного варианта.

Телефоны, планшеты и отдельная история с Apple

На десктопах это делается флагом командной строки. В Android нестандартный сервер задаётся прямо в приложении — в настройках, под номером версии. На платформах Apple поля внутри приложения нет вовсе: вы устанавливаете небольшой конфигурационный профиль, который задаёт URL координационного сервера ещё до входа в аккаунт, — один раз муторно, зато потом навсегда. Есть два способа сэкономить время. Проверяйте страницу документации Headscale для конкретного клиента, а не гадайте, — процесс для Apple менялся уже не раз. И сначала выйдите из приложения из собственного сервиса Tailscale: приложение, уже хранящее там сессию, часто отказывается принимать другой сервер, пока из него не выйти или не переустановить его.

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

Шаг 05 · Маршрутизация

Анонсировано — не значит одобрено. Две команды, две машины.

Tailnet, в котором каждое устройство достаёт до любого другого, уже полезен сам по себе. Три дополнения превращают его из набора туннелей в настоящую сеть, и все три объединяет одна особенность, о которую спотыкаются: клиент анонсирует возможность, а сервер должен её одобрить. Маршрут, который анонсирован, но не одобрен, просто не существует — молча, без предупреждений, — и симптом такой: вы можете пропинговать роутер, но ничего за ним не отвечает.

Маршрутизатор подсети — выводит в tailnet целую локальную сеть. Одна машина в домашней или офисной сети анонсирует диапазон адресов этой сети; после этого любое другое устройство получает доступ к принтерам, NAS и интерфейсам управления за ней — без установки чего-либо ещё там же. Сначала включите форвардинг в ядре, иначе пакеты будут останавливаться на роутере без какого-либо сообщения об ошибке.

# On the machine that sits inside the LAN:
echo 'net.ipv4.ip_forward = 1'          | sudo tee /etc/sysctl.d/99-ts.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-ts.conf
sudo sysctl -p /etc/sysctl.d/99-ts.conf

sudo tailscale up --login-server https://vpn.example.com \
  --advertise-routes=192.168.1.0/24 --accept-routes

# On the control server — approve it, or it does not exist.
# The subcommand moved between releases; check `hs nodes --help` first.
hs nodes list
hs nodes approve-routes --identifier 3 --routes 192.168.1.0/24

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

# On the VPS that will do the exiting (forwarding must be on, as above):
sudo tailscale up --login-server https://vpn.example.com --advertise-exit-node

# Approve it on the control server, then on the laptop:
tailscale set --exit-node=100.64.0.1 --exit-node-allow-lan-access
tailscale set --exit-node=                    # turn it off again

Выходной узел — это не коммерческий VPN, и не стоит убеждать себя в обратном. Его адрес принадлежит только вам и остаётся стабильным, что отлично работает против слежки в кафе с Wi-Fi, но бесполезно против долгосрочной корреляции активности на разных сайтах — этот компромисс подробно разобран в основном руководстве. Обратите также внимание, что выходной узел можно вынести на второй, более дешёвый VPS и полностью избавить координационный сервер от транзитного трафика.

Правила доступа — напишите их до того, как в сеть войдёт второй человек. Политика по умолчанию — «разрешено всё», и это правильно для tailnet из одного человека, но становится ошибкой, как только в нём появляется кто-то ещё. Политика — это небольшой документ в формате HuJSON: группы пользователей, теги для машин и список правил, которые определяют, какие источники могут достигать каких получателей на каких портах. Укажите в конфигурации путь к файлу, храните файл под системой контроля версий и перезагружайте сервис после каждой правки.

{
  "groups": {
    "group:admin":     ["alice"],
    "group:contractor":["bob"]
  },
  "tagOwners": {
    "tag:server": ["group:admin"],
    "tag:exit":   ["group:admin"]
  },
  "acls": [
    // Admins reach everything.
    { "action": "accept", "src": ["group:admin"], "dst": ["*:*"] },

    // The contractor reaches one machine, on two ports, and nothing else.
    { "action": "accept", "src": ["group:contractor"],
      "dst": ["tag:server:22,443"] }
  ],
  "autoApprovers": {
    "routes":   { "192.168.1.0/24": ["tag:server"] },
    "exitNode": ["tag:exit"]
  }
}

Об этом файле стоит сказать ещё две вещи. Язык политики принадлежит Tailscale, а Headscale реализует лишь большое и постоянно растущее подмножество этого языка, а не весь целиком, — прежде чем полагаться на экзотическое правило, проверяйте документацию для своей версии. И место, где хранится политика, тоже переезжало: в старых релизах путь к ней указывался в конфигурации верхнего уровня, в новых — она сгруппирована в отдельной секции policy с режимом, который умеет хранить правила и в базе данных. Задокументированы оба варианта; важен тот, который поставляется вместе с закреплённой у вас версией.

MagicDNS — короткие имена и собственный резолвер повсюду. Когда MagicDNS включён, каждое устройство откликается на короткое имя внутри суффикса, который вы выбрали на шаге 02, так что ssh nas работает откуда угодно без файла hosts. Вторая половина — куда интереснее: координационный сервер также рассылает каждому клиенту список нейм-серверов, а это значит, что резолвер, который вы построили в руководстве по приватному DNS-резолверу, может обслуживать ваш телефон на мобильном интернете — фильтруя, кэшируя и ничего не логируя, — при этом слушая на адресе tailnet, который вообще не достижим из интернета.

dns:
  magic_dns: true
  base_domain: ts.example.com
  override_local_dns: true          # beat whatever DHCP handed the device
  nameservers:
    global:
      - 100.64.0.5                  # your resolver, on the tailnet
Шаг 06 · Хранение

Собственный ретранслятор, собственная резервная копия. И обновления, прочитанные перед установкой.

Ретранслятор, о котором вы забыли, что им пользуетесь. Когда два устройства не могут установить прямое соединение — симметричный NAT с обеих сторон, файрвол, съедающий UDP, оператор связи, который делает что-то нестандартное, — трафик откатывается к ретранслятору DERP. Он остаётся зашифрованным сквозным образом, так что ретранслятор несёт шифротекст. Что видит ретранслятор — это кто с кем говорит, сколько и когда. «Из коробки» Headscale выдаёт вашим клиентам публичную карту ретрансляторов, а управляют этими ретрансляторами та самая компания, от которой вы уходили. Headscale поставляется со встроенным ретранслятором; включив его, вы переносите этот резервный путь в тот же североевропейский дата-центр, где находится всё остальное.

derp:
  server:
    enabled: true
    region_id: 999                  # custom regions live in the 900s
    region_code: "nb-hel"
    region_name: "Helsinki (self-hosted)"
    stun_listen_addr: "0.0.0.0:3478"
  urls:
    - https://controlplane.tailscale.com/derpmap/default   # remove for yours only
  auto_update_enabled: true
  update_frequency: 24h

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

Четыре файла, за пределами машины, зашифрованные. Резервная копия настолько мала, что её отсутствие будет попросту стыдно объяснить: база данных, закрытый ключевой материал, конфигурационный файл и файл политики. Вместе они — статистическая погрешность в любом счёте за хранение, а без ключевого материала каждое устройство в сети отвергнет сервер. Отправьте их куда-то за пределы этой машины — в идеале во вторую юрисдикцию, чему как раз посвящено руководство по резервным копиям, — и один раз, намеренно, восстановите их на тестовый VPS, прежде чем это действительно понадобится.

# Stop briefly for a consistent SQLite copy, or use .backup on a live db.
docker compose stop headscale
tar czf /tmp/headscale-$(date +%F).tar.gz -C /srv/headscale config data
docker compose start headscale

restic -r sftp:backup@backup.example.com:/srv/restic backup \
       /tmp/headscale-$(date +%F).tar.gz

Обновления — вот в чём настоящий риск. Это программа с версией ниже 1.0, и ведёт она себя соответственно: ключи конфигурации переименовывались, подкоманды CLI переезжали, а для установки нового релиза не раз требовался задокументированный перенос данных, а не просто перезапуск. Всё это не повод отказываться от неё — это повод завести чёткий порядок действий. Читайте примечания к выпуску перед тем, как скачивать новый образ. Сначала сделайте архивную копию, как описано выше. Сверяйте свой конфигурационный файл с примером, поставляемым с новой версией, — именно там переименованные ключи выдают себя. Поднимайтесь на одну минорную версию за раз, а не сразу на три. И храните где-то запись со старым тегом образа, потому что откат, который можно выполнить за тридцать секунд, — это то, что делает всё предприятие малорискованным.

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

Честная часть · Чего это не делает

Вы забрали карту себе. Но не стали невидимыми.

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

Координационный сервер — это публичная именованная точка входа. К нему обращается каждое устройство, поэтому у него есть имя хоста, и это имя хоста попадает в логи certificate transparency в момент выпуска сертификата. Оно обнаруживаемо, даже если вы никогда на него не ссылались, а его адрес — единственная константа, общая для всех ваших устройств. Используйте поддомен имени, которое вы уже публикуете, а не тот, что описывает назначение сервера.

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

Ретрансляторы по умолчанию всё ещё чужие. Пока вы не включите встроенный сервер DERP, соединение, которое не может установиться напрямую, откатывается к публичному парку ретрансляторов. Полезная нагрузка всё так же зашифрована сквозным образом, и прочитать её не может никто, — но сам факт того, что два ваших устройства общаются, и объём этого общения, проходит через инфраструктуру, которая вам не принадлежит. Это та часть цепочки доверия, которую большинство упускает из виду в первый же день.

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

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

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

Подбор конфигурации · Что заказать

Уровень управления — бесплатный. Платите вы за трафик.

Тариф Ежемесячно Ресурсы Что на нём комфортно работает
Sentinel$3.902 vCPU · 4 GB · 120 GB · 1 GbpsКоординационный сервер, ретранслятор и лёгкая нагрузка от выходного узла. Правильное начало.
Второй Sentinel$3.90Выходной узел — на отдельной машинеДержит транзитный трафик подальше от уровня управления
Ravelin$16.908 vCPU · 16 GB · 480 GB · 2.5 GbpsВыходной узел на каждый день для домохозяйства — на полной скорости канала
Bulwark$32.9016 vCPU · 32 GB · 960 GB · 5 GbpsВыходной узел для компании и загруженный ретранслятор — вместе

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

Заметки с практики · Шесть ловушек

Шесть способов всё сломать. Два из них похожи на проблему с DNS.

Ловушка 01 · Запутывающая

Суффикс tailnet пересекается с именем хоста сервера

Если выбрать суффикс MagicDNS, который приходится родительским или дочерним доменом по отношению к имени хоста самого координационного сервера, разрешение имён ломается так, что это выглядит как обычный сбой DNS. Пусть они будут на одном уровне, не вложенными друг в друга: vpn.example.com для сервера, ts.example.com для tailnet.

Ловушка 02 · Тихая

Маршрут анонсирован, но так и не одобрен

Клиент с готовностью сообщает, что анонсирует 192.168.1.0/24, а за роутером никто не отвечает, потому что сервер так и не одобрил маршрут. Никакой ошибки нигде не будет. Одобрите на сервере, а затем проверяйте список маршрутов, а не клиента.

Ловушка 03 · Медленная

Всё через ретранслятор, ничего напрямую

Сеть, которая блокирует исходящий UDP, вынуждает каждое соединение идти через ретранслятор: это работает, но медленнее и добавляет на путь третью машину. tailscale status в этом случае покажет relay вместо direct, а netcheck объяснит почему. Либо почините файрвол, либо сознательно смиритесь с лишним переходом.

Ловушка 04 · Открытая

Политика «разрешено всё» по умолчанию — и гость в tailnet

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

Ловушка 05 · Фатальная

Закрытый ключ, который так и не попал в резервную копию

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

Ловушка 06 · Предотвратимая

Плавающий тег образа у сервиса версии ниже 1.0

Ключи конфигурации и подкоманды CLI не раз переименовывались от одного минорного релиза к другому. При использовании тега :latest посторонний перезапуск подтягивает новую версию, и уровень управления для всего вашего парка устройств останавливается. Закрепите тег, прочитайте примечания к выпуску и обновляйтесь осознанно.

FAQ · Mesh VPN на собственном сервере

Вопросы, и ответы на них.

Одиннадцать вопросов, которые возникают при построении этого — начиная с двух, которые решают, стоит ли вообще это строить.

Headscale — это официальный продукт Tailscale?

Нет, и здесь стоит быть точным в формулировках. Headscale — это независимая реализация координационного сервера с открытым исходным кодом — той части Tailscale, которая распространяет публичные ключи, адреса и политику. Его не создаёт, не продаёт и не поддерживает Tailscale, у него нет коммерческого договора поддержки, и если что-то ломается, вы читаете issue-трекер, а не открываете тикет. Что он действительно умеет — говорить на том же протоколе, на котором уже говорит официальный клиент Tailscale с открытым исходным кодом, поэтому клиент работает с ним без изменений. Из этого следуют два практических вывода. Паритет функций неполон и таким и останется, потому что проект гонится за движущейся целью, которую поддерживает намного большая команда, — проверяйте по документации самого проекта, существует ли нужная функция, прежде чем строить вокруг неё план. А обновления заслуживают больше внимания, чем обычное обновление контейнера: схема конфигурации и несколько подкоманд CLI не раз переименовывались в минорных релизах, и это самая частая причина, по которой у людей ломается рабочая установка.

Может ли координационный сервер читать мой трафик?

Нет — и это как раз та часть архитектуры, которую стоит понять, а не принимать на веру. Каждое устройство генерирует собственную пару ключей WireGuard локально, и закрытая половина никогда его не покидает. То, что устройство загружает на координационный сервер, — это его публичный ключ, текущие адреса и немного метаданных; то, что оно скачивает, — то же самое для каждого другого устройства, до которого ему разрешено достучаться. Туннели затем согласовываются напрямую между устройствами, и шифрование — это обычный WireGuard между двумя ключами, которые сервер никогда не держал у себя. Так что координационный сервер не может ничего расшифровать, и то же самое верно для сервера Tailscale. А вот что он может делать — это другой, менее обсуждаемый вопрос: он держит карту. Имена устройств, имена пользователей, публичные ключи, публичный IP, с которого сейчас подключается каждое устройство, какое устройство проснулось в три часа ночи, и политику, которая определяет, кто до кого может достучаться. Он же — то, что распространяет ключи, а значит, скомпрометированный или принуждённый координационный сервер может добавить устройство в вашу сеть. Самостоятельный хостинг не решает математическую проблему — он переносит административную на машину, которой владеете вы.

Что произойдёт с моей сетью, если координационный сервер ляжет?

Гораздо меньше, чем принято думать, — и это обнадёживающая сторона такой архитектуры. Устройства кэшируют карту сети, полученную в последний раз, а уже установленные туннели WireGuard работают точка-точка — они не идут через координационный сервер и не нуждаются в нём, чтобы продолжать работать. Перезагрузка VPS в обычный рабочий день обычно проходит незаметно. Останавливаются только изменения: новое устройство не может зарегистрироваться, устройство, перешедшее в новую сеть, может дольше искать своих соседей заново, правки политики не распространяются, а любой узел, у которого истёк срок действия ключа, пока сервер лежит, не сможет его обновить. Так что правильно думать о координационном сервере как об уровне управления, а не как о пути данных: простой — это неудобство, а не разрыв связи. При этом относитесь к нему как к продакшену. Делайте резервные копии (шаг 06), и если от tailnet зависит целый отдел, вынесите координационный сервер на отдельную небольшую машину, а не делите его с чем-то, что часто перезапускается.

Headscale или обычный WireGuard — когда уровень управления действительно того стоит?

Посчитайте свои устройства и посчитайте, сколько из них перемещаются. Обычный WireGuard — превосходный ответ для небольшого статичного набора: три-четыре узла, фиксированные адреса, по одному конфигурационному файлу на каждый, и ничего не нужно запускать, кроме модуля ядра. Это меньше кода, меньше поверхности атаки и меньше обслуживания, чем что-либо в этом руководстве, и наше руководство по WireGuard разбирает это от начала до конца. Цена в том, что WireGuard намеренно не занимается распределением ключей, поэтому каждое новое устройство — это правка на всех остальных устройствах, а объём такой работы растёт как квадрат размера вашего парка, — а роуминговым клиентам в отеле на Wi-Fi или в мобильной сети нужен кто-то, кто будет постоянно сообщать всем, куда они переместились. Именно это и покупает уровень управления. На практике порог — это где-то пять устройств, или первый ноутбук, который ежедневно меняет сети, или момент, когда вам впервые понадобится правило вроде «ноутбук подрядчика достаёт до staging-сервера и ничего больше». До этого порога используйте WireGuard и наслаждайтесь тишиной.

Будет ли приложение Tailscale на моём телефоне работать с Headscale?

Да — это тот же самый официальный клиент, и указать ему другой координационный сервер — это официально поддерживаемая операция, а не взлом. Механика отличается от платформы к платформе, и именно на этом люди теряют по вечеру времени. В Linux, macOS и Windows это флаг командной строки у клиента (--login-server) или эквивалентная настройка. В Android опция доступна прямо в приложении, под номером версии на экране настроек. В iOS, iPadOS и tvOS поля внутри приложения нет: вы устанавливаете небольшой конфигурационный профиль, который задаёт URL координационного сервера ещё до входа в аккаунт, — в первый раз муторно, зато потом навсегда. В документации Headscale для этого специально есть страница по каждой платформе, и её стоит открыть, а не гадать, — процесс для Apple менялся особенно часто. Есть ещё одна оговорка, не привязанная к конкретной платформе: приложение, которое уже вошло в собственный сервис Tailscale, обычно нужно сначала разлогинить — а иногда и переустановить, — прежде чем оно примет другой сервер.

Нужно ли мне запускать собственный ретранслятор DERP?

Не для того, чтобы сеть вообще заработала, — а потому что это разница между «в основном приватно» и «стабильно приватно», так что решайте осознанно. Серверы DERP — это ретрансляторы, которые используются, когда два устройства не могут установить прямое соединение: симметричный NAT с обеих сторон, корпоративный файрвол, съедающий UDP, мобильный оператор, который делает что-то нестандартное. Ретранслируемый трафик всё равно остаётся сквозным зашифрованным WireGuard, поэтому оператор ретранслятора видит шифротекст, а не содержимое. Что он видит — это метаданные: какие два устройства общаются, сколько трафика и когда. «Из коробки» Headscale выдаёт вашим устройствам публичную карту DERP от Tailscale, а значит, эти ретрансляторы принадлежат Tailscale — вы самостоятельно разместили уровень управления, но оставили резервный путь данных у той самой компании, от которой уходили. Headscale поставляется со встроенным сервером DERP; включив его, вы получаете ретранслятор в том же североевропейском дата-центре, что и координационный сервер. Честный компромисс — это охват: Tailscale держит ретрансляторы на нескольких континентах, а вы — один, поэтому устройству на другом конце света лучше служат их ретрансляторы. Держать оба варианта, со своим в карте, — вполне разумная середина.

Можно ли заставить весь tailnet использовать мой собственный DNS-резолвер?

Да, и это одна из главных причин вообще всё это затевать. Headscale может рассылать конфигурацию DNS каждому подключившемуся устройству, так что и ваш ноутбук в кафе на Wi-Fi, и ваш телефон на мобильном интернете будут резолвить имена через резолвер, который держите вы, — с фильтрацией, с кэшем и без того, чтобы локальная сеть видела сами запросы. Направьте глобальный нейм-сервер tailnet на адрес tailnet той машины, где работает ваш собственный Unbound или AdGuard Home, и настройте переопределение так, чтобы оно побеждало всё, что выдал устройству DHCP. Важно не ошибиться в двух вещах. Резолвер должен быть достижим внутри tailnet, а не из публичного интернета, — в этом весь смысл, и заодно это значит, что вы наконец можете закрыть 53-й порт для всего мира. А MagicDNS, который даёт каждому устройству короткое имя внутри выбранного вами суффикса, требует, чтобы этот суффикс был под вашим контролем и не был родительским или дочерним доменом по отношению к имени хоста самого координационного сервера; ошибиться здесь — значит получить запутанный сбой, который выглядит как баг DNS, а на деле является ошибкой конфигурации.

Насколько большим должен быть VPS?

Только для уровня управления: нужно немного, а по факту — ещё меньше. Headscale — это один-единственный бинарник на Go, хранящий небольшую базу устройств, ключей и политик; координационный трафик — это тонкий ручеёк long-poll-соединений, по одному на устройство, которые почти всегда несут в себе почти ничего. Sentinel — 2 vCPU, 4 ГБ, 120 ГБ NVMe, $3.90 в месяц — потянет домохозяйство, небольшую компанию или несколько сотен устройств, даже не заметив нагрузки. Вопрос подбора конфигурации становится интересным только тогда, когда та же машина берёт на себя вторую роль. Встроенный ретранслятор DERP пересылает реальный трафик, поэтому ему важнее канал, чем ядра. Выходной узел пересылает весь трафик устройства, поэтому ему нужен и канал, и щедрый лимит трафика, и процессор, способный держать темп шифрования на полной скорости линии. Если планируете и то и другое, считайте конфигурацию по трафику, а координационный сервер воспринимайте как ту самую статистическую погрешность, которой он и является, — или, что лучше, вынесите выходной узел на вторую машину и оставьте уровень управления скучным.

Выходной узел на собственном VPS — это то же самое, что коммерческий VPN?

Технически похоже, а с точки зрения приватности — почти противоположно, так что стоит смотреть на вещи трезво. Выходной узел маршрутизирует весь интернет-трафик устройства через ваш VPS, поэтому сайты видят адрес VPS вместо вашего домашнего или гостиничного адреса, а локальная сеть видит один зашифрованный туннель вместо вашего трафика. В этом смысле всё совпадает с коммерческим VPN. Разница — в толпе. Адрес коммерческого VPN используют совместно тысячи не связанных друг с другом клиентов, и именно это затрудняет привязку трафика к вам конкретно; адрес вашего выходного узла используете вы, стабильно, всегда. Против слежки в сети кафе или профиля, который выстраивает провайдер, выходной узел отлично работает. Против наблюдателя, который месяцами сопоставляет активность на разных сайтах, выделенный адрес — устойчивый идентификатор, а общий — нет. Используйте выходной узел там, где он хорош, — недоверенные локальные сети, доступ к собственным сервисам, стабильный адрес для того, что его ожидает, — а для той части модели угроз, которую он не закрывает, читайте основное руководство.

Что именно нужно резервировать?

Четыре вещи, и только одна из них очевидна. База данных — по умолчанию SQLite, PostgreSQL, если вы на неё переехали, — хранит пользователей, устройства, публичные ключи, маршруты и ключи предварительной аутентификации. Закрытый ключевой материал, сгенерированный при первом запуске, — это то, о чём все забывают: потеряете его — и каждое устройство отвергнет сервер, с которым общалось целый год, а значит, придётся вручную перерегистрировать весь парк. Конфигурационный файл важен, потому что в нём закодированы три имени из шага 02, а восстановление их по памяти — как раз тот способ, которым люди случайно меняют одно из них. А файл политики, если вы храните правила доступа на диске, — это то, что меньше всего хочется переписывать заново. Все четыре хранятся в двух каталогах и вместе весят меньше, чем фотография. Зашифруйте их, отправьте на машину, которая не является этой же самой — в идеале в другую юрисдикцию, для чего и существует руководство по резервным копиям, — и один раз, намеренно, восстановите их на тестовый VPS, чтобы точно знать: файл, который у вас есть, — это тот файл, который вам нужен.

Возражает ли NordBastion, если я запущу на VPS координационный сервер и выходной узел?

Координационный сервер — это обычная, безобидная нагрузка: немного HTTPS, немного UDP, трафика почти нет, — и в политике допустимого использования нет ничего, что могло бы его касаться. Выходному узлу стоит уделить одно предложение, а не лекцию о политике: всё, что устройство отправляет через него, покидает интернет с адреса вашего VPS и под вашим аккаунтом, так что именно по действиям ваших устройств об этом адресе и будут судить. Это нормально, и именно для этого функция и создана; по той же причине выходной узел, которым пользуются незнакомые вам люди, — плохая идея, точно так же, как и открытый релей. Трафик не ограничен ни на одном тарифе, а ретранслируемый трафик — это обычный трафик, поэтому сервер DERP и выходной узел включены в тариф, а не тарифицируются отдельно. Помимо этого: аккаунту не нужно ваше имя, счёт оплачивается в Monero или Bitcoin, а машина, хранящая карту всех ваших устройств, никем не индексируется.

Получите машину

Собственный уровень управления на аккаунте, оформленном не на ваше имя. Без KYC, оплата криптовалютой.

Sentinel — 2 vCPU и 4 ГБ за $3.90 в месяц, в Хельсинки, Стокгольме, Осло или Рейкьявике. Этого достаточно для Headscale, Caddy и собственного ретранслятора DERP, да ещё с запасом для выходного узла, если хотите разместить его на той же машине. Никакой почты при регистрации, никакого документа, удостоверяющего личность, и никакой базы данных компании со списком всех ваших устройств.

Последняя проверка · 2026-10-12 · Источники · Документация Headscale (справочник по конфигурации, настройка клиентов, ACL, DERP, удалённый CLI), документация Tailscale по клиенту и выходным узлам, статья о протоколе WireGuard, документация Caddy · Периодичность · ежегодно

Приватный хостинг, построенный на Севере.