Tailscale ACL: тонкая настройка политик доступа
По умолчанию в Tailscale все устройства вашего тailnet видят друг друга — это удобно для теста, но опасно в проде: любой ноутбук разработчика получает прямой доступ к базе данных, а гостевой аккаунт видит внутренний CI. ACL-файл решает эту проблему: вы описываете, кто с кем может говорить и по каким портам, одним конфигом в формате HuJSON. Разберём синтаксис, группы, теги устройств и типичные схемы ограничения доступа — с рабочими примерами, которые можно адаптировать под свою сеть.
Содержание
- Формат ACL-файла: HuJSON и где он живёт
- Группы: объединяем пользователей по ролям
- Теги устройств: identity не для людей, а для серверов
- Правила acls: кто к кому и по каким портам
- Ограничение доступа между узлами: изоляция сегментов
- SSH-доступ и exit-узлы через ACL
- Тестирование политики: не ломаем прод правкой файла
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Формат ACL-файла: HuJSON и где он живёт
ACL в Tailscale — это единый файл политики на весь tailnet, а не набор правил на каждом устройстве отдельно. Редактируется он в консоли администратора (https://login.tailscale.com/admin/acls/file) либо программно через Tailscale API. Формат — HuJSON (Human JSON): обычный JSON, но с поддержкой комментариев // и висячих запятых, что сильно упрощает жизнь при поддержке большого файла.
Минимальный скелет выглядит так:
{
// Группы пользователей
"groups": {
"group:admins": ["alice@example.com", "bob@example.com"],
},
// Кто может присваивать теги устройствам
"tagOwners": {
"tag:prod": ["group:admins"],
},
// Правила доступа
"acls": [
{
"action": "accept",
"src": ["group:admins"],
"dst": ["*:*"],
},
],
}
Ключевой момент: как только вы сохраняете непустой список в acls, Tailscale перестаёт применять правило «все видят всех» и начинает жить по вашему файлу. Забудете добавить правило для какой-то группы — она потеряет доступ ко всему, включая свои же устройства (подробнее — ниже, в разделе про autogroup:self). Консоль валидирует синтаксис прямо в редакторе перед сохранением — опечатки подсвечиваются сразу, без деплоя вслепую.
Для версионирования файл удобно держать в git и заливать через API или Terraform-провайдер tailscale/tailscale — изменения политики доступа тогда проходят обычный code review.
Группы: объединяем пользователей по ролям
Группы — это именованные списки пользователей, на которые потом ссылаются правила acls. Без групп пришлось бы перечислять email каждого сотрудника в каждом правиле, а при увольнении — чистить его из десятка мест.
"groups": {
"group:admins": ["alice@example.com", "bob@example.com"],
"group:devs": ["carol@example.com", "dan@example.com"],
"group:contractors": ["freelancer@example.com"],
},
Имя группы всегда начинается с group:, символы — только строчные латинские буквы, цифры, дефис и подчёркивание. Пользователь может состоять в нескольких группах одновременно — финальный набор прав считается как объединение всех правил, которые на него ссылаются напрямую или через группу.
Отдельно стоят автогруппы — зарезервированные псевдонимы, которые Tailscale вычисляет динамически, без ручного списка:
| Автогруппа | Что означает |
|---|---|
autogroup:member | все обычные участники tailnet |
autogroup:admin | администраторы аккаунта |
autogroup:self | собственные устройства пользователя-источника |
autogroup:internet | доступ в интернет через exit node |
autogroup:nonroot | все пользователи ОС, кроме root (для SSH-правил) |
autogroup:self особенно полезен: правило "src": ["autogroup:member"], "dst": ["autogroup:self:*"] разрешает каждому видеть только свои собственные устройства (телефон, ноутбук, домашний ПК), но не устройства коллег — типовая настройка для персональных или семейных тailnet вперемешку с рабочими.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверТеги устройств: identity не для людей, а для серверов
Пользовательский аккаунт — это Иван, Мария или сервисный бот. А сервер в датацентре — не человек, у него нет email, и его identity не должна зависеть от того, кто именно его поднимал. Для этого в Tailscale есть теги (tag:) — метки, которые вешаются на узел вместо привязки к учётке пользователя.
"tagOwners": {
"tag:web": ["group:devs"],
"tag:prod-db": ["group:admins"],
"tag:ci": ["group:admins", "autogroup:member"],
},
tagOwners определяет, кто вправе выдать этот тег устройству. Присвоить тег можно двумя способами:
# при первом подключении устройства
sudo tailscale up --advertise-tags=tag:web
# или на уже подключённом узле
sudo tailscale set --advertise-tags=tag:prod-db
Если пользователь не входит в список владельцев тега — команда завершится ошибкой авторизации. После того как устройство получило тег, оно перестаёт быть «собственностью» пользователя в терминах ACL: даже если сотрудник, поднявший сервер, уволится и его аккаунт удалят, тегированный узел останется в сети и будет управляться правилами тега. Это главная причина, почему прод-серверы и CI-раннеры почти всегда стоит тегировать, а не оставлять «висящими» на личном аккаунте.
Держите теги узкими: tag:prod-db, tag:staging-web, tag:ci-runner — а не один общий tag:server на всё подряд. Чем точнее тег отражает роль узла, тем проще писать под него правила acls.
Правила acls: кто к кому и по каким портам
Собственно ограничение доступа описывается массивом acls, где каждый объект — одно правило: источник (src), назначение (dst) и действие (action, сейчас практически всегда "accept" — запрещающих правил Tailscale не поддерживает, разрешено только то, что явно перечислено).
"acls": [
// Админы — доступ ко всему
{
"action": "accept",
"src": ["group:admins"],
"dst": ["*:*"],
},
// Разработчики — только к веб-серверам, по 80/443/22
{
"action": "accept",
"src": ["group:devs"],
"dst": ["tag:web:80,443,22"],
},
// Веб-серверы могут стучаться в базу, но только на порт Postgres
{
"action": "accept",
"src": ["tag:web"],
"dst": ["tag:prod-db:5432"],
},
// CI-раннер может деплоить на прод по SSH
{
"action": "accept",
"src": ["tag:ci"],
"dst": ["tag:prod-db:22", "tag:web:22"],
},
],
Синтаксис dst — это <цель>:<порты>, где цель может быть тегом, группой, автогруппой, IP/CIDR или именем узла в tailnet. Порты задаются через запятую или диапазоном (8000-8100), * означает «любой порт». Правило tag:web → tag:prod-db:5432 — классическая трёхзвенная схема: веб-слой не имеет доступа «наружу» к базе с любого порта, только к порту приложения. Даже если на веб-сервере поднят SSH, попасть на него разработчик не сможет — правило ограничено конкретным портом.
Порядок правил в файле не имеет значения — Tailscale не работает как firewall «сверху вниз до первого совпадения», все правила суммируются. Это удобно, но требует аккуратности: легко случайно объединить два широких правила и получить более открытый доступ, чем планировалось. Проверяйте эффективные права после каждого изменения через tailscale status и tailscale ping <узел> с обеих сторон.
Ограничение доступа между узлами: изоляция сегментов
Самая частая практическая задача — не дать всем узлам видеть друг друга, а разложить их по изолированным сегментам, как в классической VLAN-схеме, только без физических свитчей. В Tailscale это делается комбинацией тегов и явного перечисления допустимых пар src → dst.
Рабочая схема для инфраструктуры с прод- и стейджинг-контурами:
"tagOwners": {
"tag:prod": ["group:admins"],
"tag:staging": ["group:admins", "group:devs"],
"tag:monitoring": ["group:admins"],
},
"acls": [
// Стейджинг и прод не видят друг друга вообще
{
"action": "accept",
"src": ["tag:staging"],
"dst": ["tag:staging:*"],
},
{
"action": "accept",
"src": ["tag:prod"],
"dst": ["tag:prod:*"],
},
// Мониторинг читает метрики с обоих контуров, но только с нужного порта
{
"action": "accept",
"src": ["tag:monitoring"],
"dst": ["tag:prod:9100", "tag:staging:9100"],
},
],
Здесь узлы внутри tag:prod свободно общаются между собой, но ни у стейджинга, ни у отдельных пользователей нет маршрута в прод-сегмент, кроме явно прописанного узла мониторинга. Это тот же принцип микросегментации, что и в статье про изоляцию пользователей друг от друга для классического VPN — здесь он реализуется декларативно, без правки iptables на каждом узле.
Для одностороннего доступа (например, бастион видит прод, но прод не видит бастион) достаточно не добавлять обратное правило — Tailscale не создаёт симметричных маршрутов автоматически.
SSH-доступ и exit-узлы через ACL
Отдельный блок ssh управляет доступом по протоколу SSH через встроенный Tailscale SSH — без отдельного демона и управления ключами вручную, аутентификация происходит через identity узла в tailnet.
"ssh": [
{
"action": "check",
"src": ["group:admins"],
"dst": ["tag:prod"],
"users": ["root", "autogroup:nonroot"],
},
{
"action": "accept",
"src": ["tag:ci"],
"dst": ["tag:staging"],
"users": ["deploy"],
},
],
"check" требует интерактивной проверки — пользователь подтверждает сессию через браузер, уместно для доступа к прод-узлам под root. "accept" пускает сразу — подходит для автоматизации вроде CI-деплоя под ограниченным пользователем deploy.
Для доступа в интернет через exit node или к домашней подсети через subnet router используется autogroup:internet и явные CIDR в dst:
{
"action": "accept",
"src": ["group:remote-team"],
"dst": ["autogroup:internet:*"],
},
Такое правило нужно, если в tailnet есть узел с --advertise-exit-node — например, VPS, через который команда выходит в интернет с общим статичным IP. Без явного правила в acls даже одобренный exit node не станет доступен пользователям — после включения кастомных ACL стоит пройтись по всем сценариям использования сети и проверить, что каждый описан правилом.
Если exit node или subnet router поднимается на арендованном сервере, а не на домашнем роутере, важен постоянный аптайм и предсказуемая сеть — иначе узел будет то появляться, то исчезать из tailnet, и завязанные на него правила acls будут работать нестабильно.
Тестирование политики: не ломаем прод правкой файла
HuJSON-файл поддерживает встроенный блок tests — набор проверок, которые Tailscale прогоняет автоматически при каждом сохранении и не даёт применить политику, если тест не проходит.
"tests": [
{
"src": "carol@example.com",
"accept": ["tag:web:443"],
"deny": ["tag:prod-db:5432"],
},
{
"src": "tag:web",
"accept": ["tag:prod-db:5432"],
"deny": ["tag:prod-db:22"],
},
],
Это надёжный способ убедиться, что рефакторинг ACL не открыл случайно лишний доступ и не закрыл нужный. Пишите тест на каждое критичное правило — особенно на границы сегментов вроде прод/стейджинг — и проверяйте его при любом изменении файла, а не только при первой настройке. Tailscale отдельно пишет все изменения в аудит-лог аккаунта — те же принципы разбора «кто и когда» описаны в статье про аудит доступов VPN, применимо и к tailnet.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Что будет, если сохранить пустой список в acls?
Ничего критичного — Tailscale трактует это как «правил нет» и оставляет поведение по умолчанию (все устройства видят друг друга). Изоляция начинает работать только с первым непустым правилом.
Можно ли писать deny-правила напрямую?
Нет, модель Tailscale ACL — allow-list: разрешено только то, что перечислено явно. Отдельного "action": "deny" не существует, «запрет» — это просто отсутствие разрешающего правила.
Тег и группа — это одно и то же, только с разным префиксом?
Нет. Группа — это список пользователей, тег — идентификатор устройства, не привязанный к аккаунту. Пользователь входит в группу, устройство получает тег — это разные сущности, хотя оба фигурируют в src/dst.
Как debug’ить, почему два узла не видят друг друга?
Начните с tailscale status на обеих сторонах — проверьте, что оба online и с ожидаемыми тегами. Затем tailscale ping <peer> покажет, идёт ли трафик. Если нет — почти наверняка дело в acls: ищите правило, где источник и назначение покрывают оба узла и нужный порт.
Нужен ли self-hosted control-сервер, чтобы пользоваться ACL?
Нет, ACL — часть обычного облачного Tailscale, доступна на бесплатном тарифе. Self-hosted вариант (Headscale) поддерживает свой, более ограниченный набор возможностей ACL — сверяйтесь с документацией конкретной версии перед миграцией.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →