Навести порядок в доступе между серверами обычно начинают с белых списков IP и открытых портов, а заканчивают путаной таблицей, в которой уже никто не помнит, зачем третья строка сверху. Tailscale предлагает другой подход: каждая машина получает постоянный адрес в зашифрованной mesh-сети поверх WireGuard, соединения открываются напрямую между узлами, а доступ регулируется декларативными ACL между машинами, тегами и людьми. В статье разбираем реальную настройку: установка на серверы, tag-based ACL, headless-авторизация для CI/CD и повседневные практики эксплуатации.

Установка и регистрация первых узлов

Tailscale ставится одной командой: curl -fsSL https://tailscale.com/install.sh | sh, либо пакетом из официального репозитория дистрибутива. После установки потребуется регистрация узла: tailscale up выплюнет ссылку, по которой администратор с любого устройства авторизует машину в координационном сервисе tailnet. Для серверов без человека за браузером используют auth key: в админке, в разделе Keys, выпускается reusable или ephemeral ключ с тегами, и регистрация выглядит так: tailscale up --authkey=tskey-auth-... --advertise-tags=tag:server.

Каждый узел получает адрес из диапазона 100.64.0.0/10 и имя узла внутри tailnet. С этого момента сервер app-01 доступен для других узлов как 100.x.y.z либо по короткому имени app-01 с включённым MagicDNS. Соединения, где это физически возможно, идут напрямую между машинами благодаря NAT traversal; там, где прямой путь не находится, трафик временно идёт через ретрансляторы DERP, и это тоже зашифровано с ключами узлов.

Проверка состояния проста. tailscale status показывает всех пиров, их адреса и вид соединения (direct или relay). tailscale ping 100.x.y.z делает специальный протокольный пинг и показывает, установился ли прямой путь. tailscale netcheck выводит отчёт о сетевом положении узла: какой NAT, какой ближайший DERP, есть ли IPv6. Этих трёх команд достаточно для подавляющего большинства диагностики.

ACL, кому и куда можно ходить

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

{
"tagOwners": {
"tag:server": ["Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript."],
"tag:ci": ["Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript."]
},
"acls": [
{"action": "accept", "src": ["tag:ci"], "dst": ["tag:server:22"]},
{"action": "accept", "src": ["autogroup:admin"], "dst": ["tag:server:*"]},
{"action": "accept", "src": ["tag:server"], "dst": ["tag:server:5432,6379"]}
]
}

Читается это так: узлам с тегом ci разрешён SSH на серверы; администраторам разрешено всё на серверах; самим серверам разрешены только порты PostgreSQL и Redis друг с другом. Ключевая идея: приложение на сервере видит обращения по внутреннему адресу tailnet, и даже если порт БД технически доступен по сети 100.64.0.0/10, ACL ограничивает, откуда к нему реально стучатся. Хостовой firewall при этом не отменяется: Tailscale дополняет, а не заменяет ufw и security groups.

Расширенная возможность - группы пользователей: в поле src вместо списка почт пишется имя группы вроде group:devops. Группы поддерживаются на платном плане или через identity provider с собственной группировкой. Для маленькой команды достаточно autogroup:admin и тегов.

CI/CD, доступ из пайплайнов без открытого SSH

Задача выглядит знакомо: CI-раннеру нужно зайти на серверы для деплоя, но открывать SSH наружу не хочется. Решение через Tailscale выглядит так: в CI вставляется действие tailscale/github-action, которое поднимает tailscaled с ephemeral-учётными данными, вешает на узел тег tag:ci, и после этого пайплайн просто работает по SSH на адреса tailnet. После завершения job ephemeral-узел сам исчезает из tailnet - не остаётся мусорных записей.

Типовой кусок GitHub Actions:

- uses: tailscale/github-action@v3
with:
oauth-client-id: секрет-из-репозитория
oauth-secret: секрет-из-репозитория
tags: tag:ci

- name: Deploy
run: |
ssh deploy@app-01 "cd /srv/app && docker compose pull && docker compose up -d"

Здесь вместо auth key используется OAuth client с ограниченными правами, что удобнее для автоматизации: client id и secret хранятся в секретах репозитория, а тег tag:ci уже соотнесён ACL-правилом с разрешённым доступом. Для self-hosted систем (GitLab, Woodpecker, Jenkins) аналогичный результат достигается shell-шагом: запускается tailscaled в фоне, затем tailscale up --authkey=$TS_KEY --advertise-tags=tag:ci.

Дополнительная польза: тот же тег tag:ci может иметь доступ к хранилищу артефактов, базам на staging и прочим ресурсам без публичных endpoint. Пайплайн перестаёт требовать проброса портов, а аудит кто-что-делал виден в логах tailnet, где у каждого соединения фиксируется узел-источник и узел-приёмник.

Полезные возможности, subnet router, exit node, funnel

Три функции закрывают большинство вторичных сценариев. Subnet router позволяет узлу объявить маршрут в локальную сеть облака: tailscale up --advertise-routes=10.10.0.0/16 на выделенной машине включает доступ к внутренним адресам провайдера с других узлов tailnet без установки клиента на каждый из них. Сервер остаётся за пределами tailnet, но доступ к нему появляется через роутер.

Exit node превращает узел в выходную точку всего трафика клиента: пользователь ноутбука в гостиничной сети выбирает домашний сервер как exit node, и весь трафик выходит через дом. Параметр --advertise-exit-node на сервере и выбор узла на клиенте - всё. Funnel, наоборот, публикует локальный HTTP-сервис в интернет без портов: tailscale funnel 8080 поднимает внешнее имя в домене tailnet с TLS, и это удобно для быстрого показа кому-нибудь локального сервиса.

На долгой дистанции важны две практики: регулярно смотреть раздел Machines в админке и удалять мёртвые узлы (иначе через год там археология), а также проверять сроки жизни auth keys и перевыпускать их до истечения, если они используются в автоматике. И по классике: контроль состояния через tailscale status в cron или через официальный API помогает заметить узел, который отвалился от сети, раньше пользователей.

Ограничения и вывод

Есть и границы. Бесплатный план ограничен по числу пользователей и узлов; координационный сервис это внешняя зависимость, и для особо требовательных сценариев существует self-hosted альтернатива headscale, которая воспроизводит координационный сервер собственными силами за цену самостоятельной поддержки. NAT traversal в сильно ограниченных сетях иногда сводится к DERP-релеям с заметной задержкой - это редкий случай, но начинать диагностику медленного соединения стоит именно с tailscale ping, чтобы увидеть вид пути.

Итоговая картина: между серверами Tailscale заменяет путаные правила firewall на читаемые ACL, CI/CD получает доступ без открытия SSH наружу, а администрирование сводится к работе с тегами и политикой. Это тот случай, когда инструмент действительно упрощает жизнь, а не добавляет ещё одну систему для обслуживания.

Эксплуатационные мелочи, которые экономят время

Несколько привычек делают жизнь с mesh-сетью скучной, в хорошем смысле. Имена узлов стоит стандартизировать сразу: роль-площадка-номер вроде db-fsn1-01, чтобы tailscale status читался без расшифровки. Key expiry на серверах стоит отключать выборочно: для инфраструктурных машин ключ живёт долго, для CI-узлов используются ephemeral, которые удаляются сами, и это снимает рутину чистки. Логи tailscaled журналируются стандартно через journalctl -u tailscaled, и при диагностике first move это посмотреть туда, а уже потом в netcheck.

Полезно и заранее договориться, кто владеет админкой: tailnet привязан к аккаунту, и уход человека с правами администратора без передачи владения - классическая боль маленьких команд. Заводится минимум два администратора, а пароли и recovery-коды хранятся в командном менеджере секретов. Это скучная административная гигиена, но она превращает инфраструктуру из личной игрушки в рабочий инструмент команды, что для связи между серверами важнее любых тонких настроек протокола.
## Проверка ACL до того, как правила уедут в бой

Ошибка в ACL способна отрезать доступ сразу группе машин, поэтому правила тестируют не на боевых серверах, а на паре пробных узлов. Рабочий цикл: поднимается тестовый узел с нужным тегом, с него делается попытка соединения, и смотрят, пустило или нет. Утилита tailscale ping это хорошо, но конкретно проверка порта делается через nc: nc -vz 100.64.1.21 5432 должен либо соединиться, либо быстро отвалиться по tcp-reset, а подвисание означает блокировку где-то на пути.

В админке есть удобная функция dry run для ACL: политику можно проверить на синтаксис и на заданные пары источник-цель до применения. Этим стоит пользоваться всегда. Полезна и привычка хранить acl.json в git: каждое изменение политики идёт через ревью, а в админку заливается копия из репозитория. Тогда через полгода можно ответить на вопрос "кто и зачем дал tag:ci доступ к production базе", заглянув в историю коммитов.

Доступ людей, ноутбуки и мобильные устройства

Серверы это только половина картины; вторая половина это люди. Клиенты Tailscale существуют под Linux, macOS, Windows, Android и iOS, установка занимает минуту. Пользователь ноутбука после входа появляется в tailnet под своим именем, и ACL различает его именно как личность, а не как адрес. Это избавляет от анекдотических схем вида "пошли в офис, там белый IP, оттуда уже SSH".

Для общих устройств админки есть shared nodes: узел можно выдать человеку вне tailnet как гостевой доступ к одной машине, без видимости остальной сети. Сценарий: внешнему консультанту нужен доступ к одному staging-серверу - вместо заведения аккаунта во всей инфраструктуре делается share конкретного узла, и ACL сужают его права до одного адреса и одного порта. Когда работа закончилась, доступ отзывается удалением share.

Важно не склеивать людей и машины в одну модель прав. Машины описываются тегами (тег заменяет владельца-пользователя: узел с тегом не привязан к почте сотрудника и переживает ротации персонала). Люди входят с личным логином через identity provider и получают права через группы или autogroup. Такая развязка - одно из тех решений, ценность которого понимают только при увольнении сотрудника: серверные узлы не отваливаются, а доступ человека исчезает с блокировкой его учётки.

Headscale как запасной выход и когда туда смотреть

Headscale это открытая реализация координационного сервера, совместимая с клиентами Tailscale. Смотреть в его сторону стоит в трёх случаях: жёсткие требования по локализации данных, нежелание зависеть от чужого облака в принципе, и специфика закрытых контуров без доступа в интернет. Цена решения - самостоятельная эксплуатация координатора: обновления, бэкапы базы узлов, высокая доступность, если tailnet критичен для работы.

Переезд туда-обратно технически возможен, но это полноценная миграция: ключи узлов генерируются заново, ACL переносится вручную с поправками, потому что часть autogroup объявлена иначе. Поэтому выбор делают один раз и осознанно. Для подавляющего большинства команд встроенный координатор Tailscale с его SLA и исчерпывающей прозрачностью по соединениям - разумный компромисс, а headscale остаётся планом Б, знание о котором приятно иметь в закромах.

Собранная по этим правилам mesh-сеть живёт тихо: серверы общаются по коротким именам, CI деплоит по SSH без открытых портов, доступ людей прозрачен и отзываем, а политика доступа читается как документ, а не выглядит кабалистическим набором адресов и портов. Именно так выглядит инфраструктура, к которой приятно возвращаться через год.