MAATRIX / Блог / Двухфакторная аутентификация в Tailscale и Netbird

Двухфакторная аутентификация в Tailscale и Netbird

MAATRIX

У Tailscale и Netbird нет отдельной формы «введите код из приложения» — потому что у них вообще нет своей базы паролей, которую нужно было бы защищать вторым фактором. Оба продукта с первого дня спроектированы вокруг делегирования всей аутентификации внешнему identity-провайдеру: Google, Microsoft, Okta, Keycloak или любому другому OIDC/SAML-источнику. Вопрос «как включить 2FA в Tailscale» на самом деле звучит как «как подключить IdP и где в нём настроить MFA» — и для mesh-VPN это устроено совсем иначе, чем для классического OpenVPN с PAM-модулем.

Обсудить статью, задать вопрос или начать новую тему

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

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

Почему у mesh-VPN нет «своего» 2FA

Классический VPN-сервер (OpenVPN, IKEv2) хранит пользователей локально или в связке с PAM/RADIUS, поэтому второй фактор пришлось прикручивать отдельно — этот путь через pam_google_authenticator.so разобран в статье про двухфакторную аутентификацию для VPN. Tailscale и Netbird решают задачу принципиально по-другому: у них нет собственной таблицы users с паролями, которую можно скомпрометировать перебором. Есть только два слоя:

  • Identity-провайдер проверяет, кто вы — логин, пароль, второй фактор, устройство. Это его работа и его ответственность полностью.
  • Control-plane (облако Tailscale, Netbird Cloud или self-hosted Headscale/Netbird) верит токену, который выдал IdP, и на основе claims из него решает, какие устройства и с какими правами появятся в сети.

Практическое следствие: включить 2FA «в Tailscale» невозможно, если не настроен внешний IdP — сначала подключается провайдер, и только потом в нём включается MFA-политика. Это не недоработка, а осознанная архитектура: сложность аутентификации (TOTP, WebAuthn-ключи, push, condition access по IP/устройству) вынесена туда, где она уже решена индустрией, вместо того чтобы каждый mesh-VPN изобретал свою.

Tailscale: вход через IdP и где реально включается MFA

В облачном Tailscale тип входа выбирается один раз при создании тейлнета — «Sign in with Google», «Sign in with Microsoft», GitHub или (на платных планах) генерик OIDC/SAML под корпоративный IdP вроде Okta или Entra ID. Никакого отдельного экрана «настройки MFA» в самом Tailscale нет — и не должно быть: как только пользователь логинится через tailscale up, браузер уводит его на страницу входа именно этого провайдера, со всей его политикой безопасности.

tailscale up --login-server https://login.tailscale.com
# откроется браузер → форма входа Google/Microsoft/Okta →
# именно там сработает MFA, если он включён у провайдера

Отсюда правило: если тейлнет привязан к Google Workspace-домену, второй фактор для всех участников включается в консоли Workspace (Security → 2-Step Verification → enforcement), а не в интерфейсе Tailscale. То же для Microsoft Entra ID — Conditional Access с требованием MFA применится ко входу в Tailscale автоматически, потому что для Entra это просто ещё одно OAuth-приложение. Для генерик OIDC/SAML (Enterprise-план) вы регистрируете Tailscale как приложение в Okta/OneLogin/Keycloak и настраиваете MFA-политику для этого приложения так же, как для любого другого корпоративного сервиса.

Дополнительный рубеж, специфичный именно для Tailscale — device approval и периодическая re-authentication: в Settings → Device management можно включить обязательное подтверждение новых устройств администратором и задать интервал, через который узел обязан заново пройти вход через IdP (по умолчанию раз в год, можно ужесточить). Это не замена MFA, а второй независимый барьер: даже если сессионный токен браузера утёк, устройство рано или поздно упрётся в повторный логин через провайдера.

Арендуйте сервер под свои задачи!

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Headscale: подключаем OIDC в config.yaml

Self-hosted реализация — Headscale — даёт то же самое, но конфигурируется руками через блок oidc в config.yaml, а не через веб-панель. Если сервер ещё не поднят, сначала пройдите установку Headscale как control-сервера для Tailscale — там описана базовая часть без OIDC, здесь — только добавка identity-провайдера поверх неё.

oidc:
  only_start_if_ready: true
  issuer: "https://accounts.google.com"
  client_id: "xxxxx.apps.googleusercontent.com"
  client_secret: "GOCSPX-xxxxxxxxxxxxxxxx"
  scope: ["openid", "profile", "email"]
  pkce:
    enabled: true
  allowed_domains:
    - "example.com"
  # либо точечный allowlist вместо целого домена:
  # allowed_users:
  #   - "admin@example.com"

issuer — это discovery-URL провайдера (у Google, Okta, Keycloak он публикует /.well-known/openid-configuration по этому адресу), client_id/client_secret выдаются при регистрации Headscale как OAuth-приложения в консоли провайдера. Redirect URI, который нужно прописать на стороне IdP, обязательно должен указывать на ваш домен с точным путём — https://hs.example.com/oidc/callback; несовпадение здесь — причина большинства первых неудачных входов.

После рестарта systemctl restart headscale клиент tailscale up --login-server https://hs.example.com вместо preauth-ключа открывает браузер со страницей входа выбранного IdP. Дальше — ровно та же логика, что и в облаке: MFA включается в самом Google/Okta/Keycloak, Headscale лишь принимает готовый id_token и проверяет allowed_domains/allowed_users, чтобы отсечь чужих пользователей провайдера, если он используется не только для VPN.

Честная оговорка: не все провайдеры одинаково хорошо заводятся с конфигом выше «из коробки» — Keycloak и Okta обычно работают без правок, а некоторые corporate-IdP с нестандартными discovery-эндпоинтами требуют явно прописывать token_endpoint/authorize_endpoint через extra_params. Проверяйте journalctl -u headscale -f во время первой попытки входа — там видна конкретная причина отказа, а не общее «login failed».

Netbird: Zitadel из коробки или внешний OIDC

В self-hosted Netbird (установка на Ubuntu 24.04) identity-провайдер — не опциональная надстройка, а обязательный компонент стека: скрипт configure.sh разворачивает его сразу, по умолчанию это Zitadel. У Zitadel MFA встроен на уровне самого провайдера — TOTP, U2F/passkey и push через собственное мобильное приложение включаются в организации Zitadel (Users → Authentication Methods или организационная политика Login Policy → Force MFA), без какого-либо участия Netbird в этом процессе.

Если в инфраструктуре уже есть Keycloak, Okta или Auth0, конфигуратор на этапе ./configure.sh даёт выбрать внешний провайдер вместо Zitadel — тогда нужно вручную создать OAuth/OIDC-приложение у этого провайдера и передать скрипту client_id/client_secret. Логика та же, что у Headscale: Netbird не хранит пароли и не проверяет коды сам, он только доверяет токену, который подписал IdP.

# на этапе конфигурации self-hosted Netbird
./configure.sh
# скрипт спросит:
#   Identity provider: [zitadel|keycloak|auth0|okta|none]
#   если не zitadel — client id / client secret / issuer вашего IdP

Практическая разница с Headscale: у Netbird identity-провайдер — это ещё и панель, через которую администратор управляет пользователями и ролями внутри Dashboard (Users, Groups, Setup Keys), а не только точка входа. Поэтому миграция с Zitadel на внешний Keycloak задним числом сложнее, чем смена OIDC-провайдера в Headscale — определитесь с провайдером до первого продуктивного запуска.

Маппинг пользователей и групп из IdP в ACL

Подключить IdP — только половина задачи; вторая половина — использовать claims из токена, чтобы не раздавать всем одинаковый доступ. Оба продукта умеют это, но с разной степенью автоматизации.

Headscale (OIDC)Netbird
Кто попадает в сетьДомен/email через allowed_domains / allowed_usersЛюбой, кто прошёл вход через настроенный IdP
Роли/группы из IdPТолько вручную через ACL-теги после регистрации узлаСинхронизация групп из claim groups в токене (если IdP их отдаёт)
Формат политики доступаYAML/HuJSON-файл политики, версионируется в gitВеб-панель Dashboard, группы устройств и Policies
Отзыв доступа при увольненииЧерез headscale users delete + ACL, вручнуюЧерез отключение пользователя в IdP — Netbird синхронизирует на следующем логине

На практике: если в Okta или Keycloak уже настроены группы (admins, contractors, devops), в Netbird эти группы можно завести автоматически при первом входе — при условии, что IdP реально отдаёт claim groups в токене (в Keycloak это отдельный mapper, включается не по умолчанию). В Headscale автоматической синхронизации ролей нет — allowed_domains/allowed_users решают только «пускать или нет», а дальнейшее разделение прав делается вручную через теги и ACL-политику внутри самого Headscale, независимо от групп в IdP.

Реаутентификация узлов как второй рубеж

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

В Tailscale это Key Expiry — по умолчанию узел обязан переавторизоваться через IdP раз в 180 дней; интервал настраивается в Settings → Device management, для чувствительных групп его разумно сократить до недель. В Headscale аналога с гибким UI нет, но истечение узла контролируется вручную:

headscale nodes list
headscale nodes expire --identifier <node-id>

В Netbird похожая настройка называется Peer login expiration и включается в Settings → Authentication — при истечении срока peer перестаёт получать обновления сети, пока пользователь заново не пройдёт вход через IdP. Смысл один во всех трёх случаях: периодическая принудительная переаутентификация не даёт устройству оставаться в сети бесконечно на старой сессии, даже если токен браузера всё ещё жив — тот же принцип, что ограничение времени жизни TOTP-кода в PAM-схеме для классического VPN, только применённый на уровне узла.

Арендуйте сервер под свои задачи!

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли включить TOTP прямо в Tailscale или Netbird, без стороннего IdP?

Нет — оба продукта не хранят пароли пользователей и не проверяют TOTP-коды сами. Единственный путь к 2FA — подключить identity-провайдера (Google, Okta, Keycloak, Zitadel) и включить MFA в нём. Для self-hosted Netbird минимальный вариант — встроенный Zitadel, где TOTP включается за пару кликов без внешних сервисов.

Что произойдёт, если IdP временно недоступен — потеряется VPN-доступ?

Уже установленные WireGuard-туннели между узлами, которые не истекли по Key Expiry / Peer login expiration, продолжат работать — IdP нужен только в момент первого входа или переавторизации по истечении срока. А вот подключить новое устройство или пройти плановую переавторизацию в этот момент не получится.

Поддерживает ли Headscale SAML, а не только OIDC?

Нет, только OIDC — ограничение самого Headscale, не связанное с клиентом Tailscale. Если корпоративный IdP отдаёт только SAML (типично для legacy ADFS), нужен либо OIDC-прокси перед Headscale, либо переход на облачный Tailscale с Enterprise-планом, где SAML поддерживается нативно.

Как быть с сотрудниками без корпоративной учётки в IdP — временные подрядчики?

В Headscale — заводить preauth-ключи с ограниченным сроком жизни в обход OIDC (headscale preauthkeys create --expiration 24h). В Netbird — Setup Keys с той же идеей, либо отдельная гостевая группа в Zitadel/Keycloak с более строгим MFA и коротким Peer login expiration.

Нужно ли отдельно защищать сам IdP вторым фактором?

Да, обязательно: административный доступ к консоли Zitadel, Keycloak или Google Workspace должен быть под своим 2FA, желательно на аппаратном ключе (WebAuthn/passkey) — компрометация админ-аккаунта IdP даёт доступ не только к VPN, а ко всем сервисам, подключённым через этот провайдер.

Обсудить статью, задать вопрос или начать новую тему

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

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