Tailscale выпустила исправления для нескольких уязвимостей в Tailscale SSH и Tailscale Serve, позволявших не-root пользователю получить доступ к root. Все они закрыты в версии 1.98.9 или новее. Отдельно в статье BleepingComputer упоминаются 11 неустранённых на момент отчёта проблем (Tailworm), найденных при аудите Tailscale 1.104.2 и self-hosted headscale v0.29.5, включая критическую в SSH-сервере; версии исправления для них не указаны.
Ниже — механика каждой из закрытых проблем, как устроен путь от приёма соединения до запуска сессии и что это меняет для администраторов tailnetчастной сети устройств и пользователей, объединённых одним аккаунтом Tailscale.
Что именно изменилось
Числовой UID вместо имени. Tailscale SSH разрешал адресовать пользователя по имени или по UID, но ограничения на root в ACLсписках управления доступом — правилах, которые задают, каким пользователям и устройствам разрешено подключаться и к чему учитывали только имя. Пользователь с не-root доступом мог обратиться как 0@host и получить root в обход ACL. Теперь UID и чисто числовые имена через SSH запрещены — двусмысленность устранена.
Проксирование Unix-сокетов через Serve. Проверка путей в Serve не покрывала Unix-сокеты как цели прокси. Не-root оператор мог заставить процесс tailscaled, работающий как root, проксировать привилегированный сокет. Теперь цели-сокеты ограничены пользователем root — так же, как это уже требовалось для обработчиков файловой системы.
Симлинки при форвардинге сокетов в SSH. При проверке прав на запрошенные сокеты выполнялось только лексическое сопоставление пути с allow- и deny-списками. Симлинк на защищённый ресурс обходил ограничения. Теперь, помимо лексического сопоставления, проверяются запрошенные и настроенные пути на симлинки, и их назначения тоже должны быть авторизованы.
Как устроен путь сессии и где возникал обход
Точка входа принимает сырое соединение, создаёт объект соединения, взводит таймер login-graceограничитель времени на этап аутентификации: если вход не завершён до его истечения, соединение закрывается, регистрирует соединение в списке активных и передаёт управление обработчику. Аутентификация устроена так: если проверка подлинности Tailscale отключена, соединение сразу считается прошедшим её без дополнительных прав; иначе заполняются сведения о клиенте, оценивается политика доступа, и по результату соединение либо принимается, либо отклоняется с баннером (имя запрошенного пользователя, отказ политики, ошибка оценки). При принятии выполняется поиск локального пользователя и его групп, результаты сохраняются в соединении.
После SSH-аутентификации политика проверяется заново: если изменение политики материальное изменениеправка, затрагивающая права доступа текущей сессии, а не только оформление правил, в stderr выводится tailscale: access revoked, сессия завершается с кодом 255 и соединение отзывается, команда не запускается. Затем по запрошенной подсистеме различаются sftp, обычная сессия и всё прочее (неподдерживаемая подсистема). Далее создаётся сессия и запускается команда.
Смена привилегий происходит в инкубатореподкоманда tailscaled be-child ssh, которая запускается от того же пользователя, что и демон, и перед запуском команды меняет UID/GID на целевого пользователя. Он блокирует текущий поток ОС, разбирает аргументы, загружает пересланное окружение, проверяет взаимоисключение --sftp и --shell. Если процесс работает от root и хост не в SELinuxEnforcing, инкубатор пробует заменить процесс командой login, затем su; иначе обрабатывает в процессе. В обоих путях перед запуском команды сбрасываются привилегии: выставляются дополнительные группы, при необходимости меняются GID и UID на целевого пользователя, после чего эффективные идентификаторы сверяются с ожидаемыми и при несовпадении процесс завершается. Именно этот переход от root к uid/gid целевого пользователя — место, где обход ACL приводил к повышению привилегий.
Предыстория
Раньше Tailscale SSH поддерживал вход без ключей: сервер отвечает успехом на запрос аутентификации типа «none». Для клиентов, которые на это ломаются, применялся суффикс имени пользователя +password, включающий режим парольной аутентификации с произвольным паролем.
Запуск сессий шёл через субкоманду tailscaled be-child ssh, которая сообщает системе о новом логине для монтирования домашних каталогов и расшифровки ФС. Login-shell запускался только при работе от root и вне SELinuxEnforcing, иначе использовался запуск «в процессе». Затем инкубатор пытался заменить процесс командой login (регистрация удалённого IP, PAM-аутентификация, профиль PAM «remote»), а при неудаче — запустить su с флагами -w, -l и при необходимости -c. Специальный режим совместимости отключал login-shell для SFTP и отключал su, возвращая старое поведение.
В бюллетенях описаны и более ранние SSH-инциденты. При включённом в ACL флаге enforceRecorderтребование обязательной записи сессии: если запись не удаётся, доступ должен прекращаться пользователи могли кратковременно выполнять команды до прекращения доступа из-за сбоев записи — требовалось обновить tsrecorderкомпонент Tailscale для записи SSH-сессий и клиенты до 1.78.0 или новее. Ошибка разбора ACL трактовала "checkPeriod": "0s" как неустановленное значение и применяла 12 часов вместо повторной аутентификации при каждом подключении; исправление развёрнуто 2024-04-23, документированный способ — "checkPeriod": "always". Пересылаемые переменные окружения были видны в /proc/<pid>/cmdline другим локальным пользователям на время запуска процесса и записывались в аргументы начала сессии, которые Tailscale логирует локально и (если узел не настроен с --no-logs-no-support) в log.tailscale.com; исправлено в 1.102.1 или новее — значения теперь передаются через унаследованный файловый дескриптор и не пишутся в логи.
Почему это сделали
Исправления касаются проверки прав при запуске сессии и доступа к привилегированным ресурсам, а не валидации пользователя или окружения как таковой. В каждом случае проверка была неполной: ACL смотрели только на имя, Serve не проверял Unix-сокеты, форвардинг сокетов сопоставлял пути лексически. Сброс привилегий при запуске сессии реализован корректно — процесс меняет группы, GID и UID на целевого пользователя и проверяет результат, — но обойти ограничения можно было до того, как до него доходило дело.
Что это меняет на практике
Администраторам tailnet, использующим Tailscale SSH, нужно обновить узлы до 1.98.9 или новее. Той же версии требуют Tailscale Serve и Tailscale Servicesфункция Tailscale, позволяющая публиковать сервисы, размещённые рядом с процессами, привязанными к loopback. Отдельно для управления удалёнными узлами через веб-интерфейс нужна версия 1.98.0 или новее.
В SSH-правилах ACL поле action принимает accept или check; при check доступен необязательный период повторной аутентификации (по умолчанию 12h). Поле users задаёт список SSH-пользователей, и теперь адресовать их по UID или чисто числовым именам нельзя — это затрагивает ограничения autogroup:nonroot.
Как параметры читаются во время работы: пересылаемые клиентом переменные окружения принимаются, только если ключ не опасен и не запрещён, значение не содержит NUL, а для TERM/LANG/LC_* — значения с «/» и ведущим «-»; остальные пары отклоняются. Login-shell выбирается только при работе от root и вне SELinuxEnforcing. Запись сессии включается, если у сессии есть рекордеры или включён TS_DEBUG_LOG_SSH, а изменение списка рекордеров требует реаутентификации, поскольку состояние записи фиксируется при аутентификации.
Ограничения и открытые вопросы
Tailscale не рекомендует всем отключать Tailscale SSH. Предлагаемые меры: проверить политики доступа SSH и ограничить доступ нужными пользователями и устройствами; отключить пересылку SSH, если она не нужна, установив TS_SSH_DISABLE_FORWARDING=1 в окружении службы tailscaled и перезапустив её — это отключает пересылку, но не сам Tailscale SSH, и смягчает часть наиболее серьёзных находок; временно ужесточить политики доступа к чувствительным системам; установить исправленный клиент, когда он выйдет. BleepingComputer собственных рекомендаций не даёт, а пересказывает меры Tailscale.
Открытым остаётся полный объём находок и затронутые конфигурации — Tailscale продолжает их проверять. По другой проблеме в бюллетене указано, что журналы аудита не содержат достаточно информации для обнаружения гонки при работе с auth keys, и улучшение журналов лишь планируется. У самого Tailscale SSH есть и постоянные ограничения: подключаться можно только к устройствам в сети Tailscale (не к устройствам за subnet router без Tailscale), только к Linux и macOS с open source CLI-версией, нельзя запускать на Synology/QNAP; порт SSH жёстко предполагается равным 22, изменить его нельзя; режим check применим только к Tailscale SSH, не к обычному SSH или другим TCP-соединениям; при одновременном наличии check- и accept-правил применяется более ограничительное check-правило, и пользователь ограничивается только users из него; перезапуск tailscaled обрывает существующие сессии.