Настройка Tailscale subnet router на VPS
Пошаговое руководство по настройке subnet router в Tailscale на VPS. Узнайте, как включить IP forwarding, настроить постоянные маршруты и использовать флаг --accept-routes.
Что делает subnet router в Tailscale
Subnet router в Tailscale — это узел, который анонсирует диапазон частных IP-адресов в вашу tailnet, позволяя всем устройствам в сети обращаться к этим адресам, даже если на них не установлен Tailscale. Ваша tailnet — это частная сеть Tailscale, состоящая из устройств, авторизованных в одной учетной записи или организации. Exit node — это функция, которую часто путают с subnet router, хотя она выполняет противоположную задачу. Она направляет весь трафик устройства через VPS, делая этот VPS точкой выхода устройства в публичный интернет.
Каждое предложение описывает одну задачу. Subnet router делает частную сеть доступной из tailnet. Exit node меняет точку выхода вашего публичного трафика. Если вам нужна именно вторая функция, ознакомьтесь с инструкцией по запуску Tailscale exit node на VPS. Это разные флаги, и один VPS может выполнять обе функции одновременно, но они решают разные задачи и имеют разные причины для сбоев.
Когда VPS требуется в качестве subnet router
Типичный сценарий — частная сеть, предоставленная провайдером. Ваш VPS имеет публичный адрес и второй интерфейс в частном сегменте, а остальные серверы в этом сегменте не имеют публичных адресов: база данных на 10.0.0.20, целевой сервер резервного копирования на 10.0.0.30. Установите Tailscale на один VPS, анонсируйте 10.0.0.0/24, и ваш ноутбук получит прямой доступ к этим частным адресам. В самом сегменте ничего не меняется, база данных по-прежнему не имеет публичного адреса. Если из этого сегмента вам нужно только одно веб-приложение на одном порту, анонсирование всего диапазона избыточно, и Tailscale serve позволяет настроить HTTPS для этого единственного порта. Тот же подход применим к демону, который намеренно привязан только к localhost, например, dsh, работающий в фоновом режиме под управлением systemd, где адрес в tailnet на этом VPS заменяет SSH-туннель, который иначе пришлось бы держать открытым для доступа к его интерфейсу.
Другой вариант — сеть на удалённой стороне VPS. Это домашняя или офисная LAN (локальная сеть) за собственным маршрутизатором либо группа устройств в стойке, на которых вообще нельзя запустить Tailscale, например управляемый коммутатор или старый NAS с заблокированной прошивкой. Один Linux-компьютер в этой сети становится subnet router для всех остальных устройств. На домашнем маршрутизаторе перед ним не нужно настраивать перенаправление портов. В материале как Tailscale проходит через NAT без открытия порта на маршрутизаторе объясняется, почему это работает и в каких случаях соединение переключается на relay. Дома таким компьютером часто служит небольшая VM на уже используемом гипервизоре. Перед выбором стороны туннеля, на которой будут работать сервисы, нужно сопоставить стоимость этого варианта с арендой VPS в материале сравнение стоимости домашнего хоста Proxmox и арендованного VPS.
Оба случая объединяет одно требование. Subnet router уже должен иметь доступ к диапазону, который он анонсирует, используя собственную таблицу маршрутизации и собственный межсетевой экран. Tailscale не создает это соединение. Он доставляет трафик до маршрутизатора и передает его ядру для дальнейшей пересылки.
Установка Tailscale и проверка локального маршрута
curl -fsSL https://tailscale.com/install.sh | shСкрипт определяет дистрибутив, добавляет репозиторий пакетов Tailscale, устанавливает команду tailscale и демон tailscaled, а затем активирует службу. Подтвердите это с помощью systemctl is-active tailscaled, команда должна вывести active.
Прежде всего убедитесь, что VPS имеет доступ к сети, которую вы планируете анонсировать.
ip route show
ping -c3 10.0.0.20ip route show должен отобразить частный диапазон на реальном интерфейсе, например 10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5. Если ping не проходит здесь, на самом маршрутизаторе, никакой флаг Tailscale не решит проблему. Причина кроется в сетевой конфигурации VPS или межсетевом экране на целевом хосте. Исправьте это в первую очередь, так как от этого зависят все последующие тесты.
Включите IP forwarding и обеспечьте сохранение настройки после перезагрузки
Linux-машина отбрасывает любой пакет, который не адресован ей самой, если не включена пересылка (forwarding). Пересылка пакетов других машин — это основная задача маршрутизатора подсети, поэтому данный шаг обязателен.
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.confПроверьте настройку с помощью sysctl net.ipv4.ip_forward, команда должна вывести net.ipv4.ip_forward = 1.
Часто этот шаг выполняют лишь частично. Команда sudo sysctl -w net.ipv4.ip_forward=1 срабатывает немедленно, но сбрасывается после перезагрузки, поэтому маршрутизатор подсети может работать неделями и отключиться на следующее утро после перезагрузки при обновлении ядра. Сложность в том, что внешне всё выглядит исправно. tailscale status по-прежнему показывает узел в сети, консоль администратора отображает одобренный маршрут, а на клиентах маршрут установлен. Пакеты приходят на VPS, но ядро отбрасывает их без записи в лог. Запись значений в /etc/sysctl.d/99-tailscale.conf гарантирует, что настройки сохранятся после перезагрузки.
Если вы анонсируете маршруты при выключенном forwarding, tailscale up выдаст предупреждение в момент выполнения. В Linux нужная строка находится рядом с Warning: IP forwarding is disabled, subnet routing/exit nodes will not work.. Читайте вывод этой команды, а не пропускайте его.
Анонсирование маршрутов
sudo tailscale up --advertise-routes=10.0.0.0/24На VPS, который уже подключен к вашей tailnet, измените настройки на месте:
sudo tailscale set --advertise-routes=10.0.0.0/24Используйте tailscale set для любых последующих изменений. Повторный запуск tailscale up с одним флагом сбрасывает те флаги, которые вы не указали, и CLI выдаст ошибку о том, что изменение настроек таким способом требует перечисления всех нетиповых флагов. tailscale set изменяет одну настройку, не затрагивая остальные.
Несколько диапазонов указываются в одном списке через запятую без пробелов: --advertise-routes=10.0.0.0/24,192.168.50.0/24. Каждая запись должна быть сетевым адресом в нотации CIDR (бесклассовая междоменная маршрутизация, формат 10.0.0.0/24). Ошибочное указание собственного адреса хоста, 10.0.0.5/24, будет отклонено, так как биты после префикса не равны нулю, а сообщение об ошибке укажет префикс, который вы, вероятно, имели в виду. Чтобы прекратить анонсирование, установите пустой список с помощью sudo tailscale set --advertise-routes=.
Одобрение маршрута в панели администратора
Анонсирование маршрута — это лишь запрос, а не изменение конфигурации. Пока администратор не одобрит его, ни один клиент не получит этот маршрут, и доступ к диапазону адресов будет закрыт. Это сделано намеренно: машина, способная самостоятельно добавить себя в таблицу маршрутизации всех клиентов, может перехватывать трафик для любого диапазона.
Одобрите маршрут на странице Machines в панели администратора. VPS будет отображаться с меткой subnet. Разверните строку с этим узлом, найдите раздел subnets, откройте настройки маршрутов, отметьте нужный маршрут и сохраните изменения.
Одобрение выполняется для каждого префикса отдельно. Если вы анонсируете 10.0.0.0/24 сегодня, а 192.168.50.0/24 в следующем месяце, новый префикс поступит в статусе «не одобрено», в то время как старый продолжит работать. Со стороны VPS одобренный и игнорируемый маршруты выглядят одинаково, поэтому перед началом отладки проверьте состояние в консоли.
Вы можете пропустить ручной этап, используя блок autoApprovers в файле политики tailnet:
{
"autoApprovers": {
"routes": {
"10.0.0.0/24": ["tag:subnet-router"]
}
}
}Затем запустите узел с этим тегом, sudo tailscale up --advertise-routes=10.0.0.0/24 --advertise-tags=tag:subnet-router, и маршрут будет одобрен сразу после анонсирования. Тег должен быть предварительно добавлен в раздел tagOwners того же файла политики. Это удобно настроить, если вы пересобираете VPS с помощью скриптов, так как пересобранный узел считается новым, и его маршруты по умолчанию снова требуют одобрения.
Почему клиенты Linux игнорируют маршрут без --accept-routes
Маршрут анонсирован и одобрен. Ваш телефон и Mac могут связаться с 10.0.0.20. Ваш ноутбук на Linux — нет, при этом в консоли администратора нет никаких признаков проблемы.
Принятие маршрута подсети означает внесение записей в таблицу маршрутизации клиента. В Android, iOS, macOS, tvOS и Windows клиент Tailscale делает это автоматически. В Linux этого не происходит, так как машина на Linux часто является сервером или маршрутизатором, чья таблица маршрутизации была настроена намеренно. Автоматическое добавление /24, полученного из сети, может нарушить передачу трафика, который эта машина уже обрабатывает. Поэтому в Linux вы должны дать явное согласие на каждом клиенте:
sudo tailscale set --accept-routesЗатем проверьте, куда был добавлен маршрут:
ip route show table 52
ip route get 10.0.0.20Tailscale в Linux не помещает принятые маршруты в основную таблицу маршрутизации. Он помещает их в таблицу маршрутизации 52 и устанавливает правила политики, видимые через ip rule show в диапазоне приоритетов от 5210 до 5270, которые направляют несовпадающие пакеты в эту таблицу. Поэтому ip route show сам по себе никогда не покажет 10.0.0.0/24, и пользователь, который проверяет только эту команду, решит, что --accept-routes ничего не сделал. ip route show table 52 — это команда, которая показывает реальное положение дел, и она должна отображать анонсированный диапазон в tailscale0.
Стоит знать об одном исключении. Если этот узел Linux сам является вторым маршрутизатором подсети для своей локальной сети, --accept-routes заставит его отправлять трафик для своей напрямую подключенной подсети через другой маршрутизатор, а не через собственный интерфейс. На резервном маршрутизаторе в паре с высокой доступностью оставьте --accept-routes выключенным и используйте только анонсирование.
Режим сбоя: два маршрутизатора анонсируют перекрывающиеся диапазоны
Два подсетевых маршрутизатора могут анонсировать идентичные диапазоны — именно так работает пара с высокой доступностью: каждый маршрутизатор анонсирует одни и те же префиксы, и клиенты могут переключаться с одного на другой. Перекрывающиеся диапазоны с разной длиной префикса также разрешены, и Tailscale выбирает наиболее точное совпадение. Если маршрутизатор A анонсирует 10.0.0.0/24, а маршрутизатор B анонсирует 10.0.0.0/16, трафик для 10.0.0.20 направляется к A.
Неожиданным для пользователей является поведение системы при отключении A. Tailscale не переключается на менее специфичный маршрут, так как отключенный маршрутизатор не может отозвать свой анонс, и клиенты продолжают отдавать предпочтение более специфичному префиксу. Трафик к 10.0.0.20 прекращается, в то время как трафик к 10.0.1.20, который покрывается только /16 маршрутизатора B, продолжает проходить через B. Симптом выглядит как частичный отказ частной сети, а причина заключается в том, что отключенный узел удерживает более специфичный префикс. Если вам нужно отказоустойчивое решение, настройте маршрутизатор с более широким диапазоном так, чтобы он также анонсировал более узкие префиксы — тогда оба будут покрывать одни и те же адреса.
Другой тип перекрытия возникает ближе к клиенту. Если вы находитесь в сети отеля с адресацией 192.168.1.0/24, а ваш подсетевой маршрутизатор анонсирует 192.168.1.0/24, они начинают конкурировать за одни и те же пункты назначения, и результат зависит от платформы. В Linux установите правило с приоритетом выше, чем у Tailscale, чтобы локальные адреса использовали основную таблицу:
sudo ip rule add to 192.168.1.0/24 priority 2500 lookup mainЭто правило не является постоянным и исчезнет после следующей перезагрузки. Правильное решение — выбрать диапазон частных адресов, который не встретится вам в других сетях. 192.168.0.0/24 и 192.168.1.0/24 являются значениями по умолчанию для большинства домашних маршрутизаторов, поэтому выберите что-то из диапазона 10.0.0.0/8, определив его самостоятельно. Такое же столкновение нарушает работу обычного VPN на базе WireGuard, настроенного вручную, по той же причине: более специфичный локальный маршрут побеждает, поэтому трафик никогда не попадает в туннель.
Режим сбоя: DNS разрешается в адрес, для которого нет маршрута
Эту проблему сложно диагностировать, так как система не сообщает об ошибке. Имя разрешается, но соединение прерывается по таймауту.
Допустим, db.internal.example.com разрешается в 10.0.5.20 через ваш частный DNS-сервер, а вы анонсировали 10.0.0.0/24. Поиск проходит успешно, так как разрешение имен (DNS) и IP-маршрутизация — это разные этапы, и ни один из них не проверяет другой. Затем пакет для 10.0.5.20 не находит подходящего маршрута в tailnet, уходит через шлюз по умолчанию на клиенте и пропадает.
Две команды позволяют разделить эти два этапа:
nslookup db.internal.example.com
ip route get 10.0.5.20Если поиск возвращает адрес, но ip route get не отвечает dev tailscale0, значит, с именем всё в порядке, а на этом клиенте отсутствует маршрут. На клиенте с Linux сначала убедитесь, что --accept-routes включен, так как без него маршрут подсети не устанавливается, даже если одобренный префикс уже покрывает этот адрес. Если он включен, но ни один одобренный префикс не покрывает адрес, анонсируйте диапазон, который его покрывает (10.0.0.0/16 или второй явный префикс), а затем одобрите новый префикс в консоли.
Существует аналогичная ловушка на самом DNS-сервере. Если вы задаете глобальный DNS-сервер в консоли администратора с частным адресом, например 10.0.0.53, этот адрес должен находиться внутри одобренного маршрута. В противном случае устройства, не находящиеся в этой частной сети, вообще не смогут связаться с резолвером. Если включить опцию переопределения локальных DNS-серверов и при этом указать резолвер, до которого невозможно достучаться, все устройства в tailnet мгновенно потеряют возможность разрешения имен, включая те, что работали секунду назад. Сначала анонсируйте и одобрите маршрут к резолверу, а затем меняйте настройки DNS. Если DNS внутри туннеля — это то, с чем вы постоянно боретесь, в описании причин сбоя DNS в туннеле WireGuard рассматривается тот же механизм без учета уровня координации.
Source NAT и соединения site-to-site
По умолчанию subnet router перезаписывает исходный адрес каждого пересылаемого пакета на свой собственный частный адрес. Это называется SNAT (source network address translation). Данный механизм используется для того, чтобы ответы доходили до отправителя без необходимости менять настройки в частной сети: база данных на 10.0.0.20 отвечает VPS, так как знает маршрут к нему. Недостаток заключается в том, что база данных видит все соединения tailnet как исходящие от VPS, поэтому правила межсетевого экрана и журналы доступа не содержат информации об исходном клиенте.
Отключите эту функцию в Linux, если хотите сохранить реальный адрес tailnet клиента:
sudo tailscale set --snat-subnet-routes=falseПосле этого хостам в частной сети потребуется маршрут обратно к 100.64.0.0/10 — диапазону, который Tailscale назначает устройствам, — указывающий на subnet router. Без этого обратного маршрута ответы будут уходить на шлюз по умолчанию и не достигнут цели, из-за чего соединения будут зависать после первого пакета. Добавьте статический маршрут на шлюзе частной сети или оставьте SNAT включенным.
Соединение site-to-site — это два subnet router, выполняющих эту операцию одновременно: каждый анонсирует свою сеть и принимает сеть другого:
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesВыполните соответствующую команду на другом маршрутизаторе, указав его собственный диапазон. Эти два диапазона должны различаться. Если передача больших объемов данных зависает, в то время как ssh и ping работают нормально, причина кроется в MSS (maximum segment size) — максимальном размере блока данных, который может нести TCP-пакет. Из-за накладных расходов туннеля пересылаемые пакеты становятся слишком большими для некоторых промежуточных звеньев сети, и ограничение размера (clamping) решает эту проблему:
sudo iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuСохраните это правило с помощью iptables-persistent, иначе оно будет удалено при следующей загрузке.
Обслуживание для поддержания работоспособности
Срок действия ключа этого устройства зависит от способа входа в систему. Узел, запущенный с использованием тега, как в примере autoApprovers выше, по умолчанию имеет отключенное истечение срока действия ключа с момента первой пометки тегом и аутентификации, поэтому для такой настройки ничего дополнительно делать не нужно. Узел, в который выполнен вход от вашего имени, следует периоду истечения срока действия ключа tailnet, который по состоянию на октябрь 2026 года составляет 180 дней по умолчанию для новой tailnet и может быть установлен в диапазоне от 1 до 180 дней. Когда ключ на таком subnet router истекает, соединения с ним и от него прекращаются, а весь анонсированный диапазон становится недоступным, при этом никаких изменений в конфигурации, объясняющих это, не происходит. Для маршрутизатора, в который выполнен вход как в пользователя, отключите истечение срока действия ключа в меню в конце соответствующей строки на странице Machines в панели администратора, а затем зафиксируйте это действие.
Tailscale отдает предпочтение прямому соединению между узлами и переключается на свои ретрансляционные серверы, если прямое соединение установить не удается. Ретрансляторы работают, но они увеличивают задержку. VPS с публичным адресом — это простой случай: разрешите входящий трафик по UDP 41641, и большинство узлов подключатся напрямую. Если трафик до частного диапазона медленнее, чем должен позволять канал VPS, первым делом следует проверить, подключается ли ваш клиент к subnet router напрямую или через ретранслятор. Если брандмауэром управляет ufw, правила ufw, которые действительно нужны VPS, описывают необходимый синтаксис.
Правила доступа — это вторая составляющая. В стандартной tailnet каждое ваше устройство может связаться с любым другим, поэтому одобренный маршрут работает сразу. Как только вы создаете политику ACL, сторона назначения в правиле должна охватывать частный диапазон — либо путем его указания, либо с помощью подстановочного знака *, который включает одобренные маршруты подсетей, поскольку 10.0.0.20 не является адресом tailnet и не подпадает под действие правил, написанных для IP-адресов или тегов tailnet.
Наконец, решите, готовы ли вы использовать coordination server, которым не управляете. Control plane Tailscale размещается как hosted service. Ключи остаются на ваших машинах, но учётная запись и policy file хранятся там. До передачи этому серверу маршрута в private network нужно определить, что именно сможет сделать злоумышленник при компрометации control plane или краже данных для входа в учётную запись. В модели доверия Tailscale указано, где проходит эта граница. Стоимость редко становится причиной отказа от Tailscale: бесплатный план поддерживает до 6 пользователей с неограниченным числом принадлежащих им устройств, хотя subnet router, который вы запускаете с tag, учитывается иначе, чем router, подключённый под вашей учётной записью. После этого оплата зависит от числа пользователей, а не машин. Поэтому до добавления учётной записи, из-за которой будет превышен лимит, стоит выяснить, сколько на самом деле будет платить семья или команда из 5 человек после исчерпания бесплатного плана. Запуск Headscale, self-hosted control server для Tailscale позволяет разместить control plane на собственном VPS, но требует самостоятельно его обслуживать. Другой вариант при таких опасениях — отказаться и от клиентов Tailscale. Self-hosting VPN-сервера NetBird размещает coordination layer и собственные mesh-клиенты на одной машине под вашим управлением. В обоих решениях используется WireGuard, поэтому при переходе меняются те аспекты, которые рассматриваются в сравнении NetBird и Tailscale: кто управляет control plane, как задаётся правило доступа к вашему private range и как передаётся трафик, когда узлы не могут установить прямое соединение. Если вы ещё выбираете между этой моделью и конфигурацией, написанной вручную, сравнение WireGuard и Tailscale объясняет, что даёт coordination layer и какова его стоимость.
FAQ
В чем разница между subnet router и exit node?
Subnet router анонсирует диапазон частных адресов, чтобы устройства в tailnet могли достигать машин, на которых не запущен Tailscale. Exit node анонсирует себя как маршрут во весь интернет, поэтому устройство отправляет весь свой трафик через публичный адрес этого узла. Один VPS может быть и тем, и другим. Это отдельные флаги, --advertise-routes и --advertise-exit-node, и каждый из них требует отдельного одобрения в панели администратора.
Почему мой Linux-клиент игнорирует анонсированный маршрут подсети?
Linux-клиенты не принимают маршруты подсетей, если вы их об этом не попросите. Запустите sudo tailscale set --accept-routes на клиенте. Затем проверьте с помощью ip route show table 52, а не ip route show. Tailscale устанавливает принятые маршруты в таблицу маршрутизации 52 и достигает их через правила политики, поэтому основная таблица никогда их не отображает, и рабочий маршрут выглядит отсутствующим.
Моя подсеть перестала работать после перезагрузки. Что сломалось?
Скорее всего, IP forwarding. Значение, установленное с помощью sysctl -w, не сохраняется после перезагрузки, поэтому запишите его в /etc/sysctl.d/99-tailscale.conf и подтвердите с помощью sysctl net.ipv4.ip_forward. Если пересылка включена, а диапазон все еще недоступен, посмотрите на узел в панели администратора. Маршрутизатор, в который выполнен вход как пользователь, имеет ключ, который по умолчанию истекает через 180 дней, и истекший subnet router выглядит как сетевая ошибка, а не как проблема с учетной записью. У маршрутизатора, запущенного под тегом, истечение срока действия ключа по умолчанию отключено, поэтому для него ищите причину в другом месте.
Могут ли два subnet router анонсировать один и тот же диапазон?
Да. Идентичные диапазоны — это то, как работает пара высокой доступности: оба маршрутизатора анонсируют точно одни и те же префиксы. Перекрывающиеся диапазоны с разной длиной префикса также допустимы, и побеждает наиболее специфичный. Здесь требуется осторожность при отказоустойчивости: когда маршрутизатор, удерживающий более специфичный префикс, отключается, Tailscale не переключается на более широкий маршрут, поэтому трафик прекращается. Для настоящей резервной пары настройте оба маршрутизатора на анонсирование одних и тех же специфических префиксов.
Имя хоста разрешается, но соединение по таймауту обрывается. Почему?
Разрешение DNS и маршрутизация — это отдельные этапы. Имя может разрешаться в адрес, который не покрывается ни одним одобренным маршрутом, и тогда пакет уходит через шлюз по умолчанию клиента. Запустите ip route get <address> на клиенте. Если ответ не включает dev tailscale0 на Linux-клиенте, сначала убедитесь, что --accept-routes включен. Если это так, анонсируйте диапазон, который покрывает этот адрес, и одобрите новый префикс в панели администратора.