Як запустити Tailscale subnet router на VPS
Налаштуйте subnet router Tailscale на VPS: схвалення маршрутів, IP forwarding після перезавантаження та прапорець --accept-routes, потрібний Linux.
Що робить subnet router Tailscale
Subnet router Tailscale — це машина, яка оголошує tailnet цілий діапазон приватних IP-адрес. Завдяки цьому кожен пристрій у tailnet може звертатися до адрес у цьому діапазоні, навіть якщо на цих пристроях не запущено Tailscale. Ваш tailnet — це приватна мережа Tailscale: набір пристроїв, авторизованих в одному обліковому записі або організації. Exit node — це функція, з якою його часто плутають, але вона виконує протилежне завдання. Exit node передає весь трафік пристрою через VPS, тому VPS стає маршрутом цього пристрою до публічного інтернету.
Одним реченням: subnet router робить одну приватну мережу доступною з tailnet. Exit node змінює місце виходу вашого публічного трафіку в інтернет. Якщо вам потрібен другий варіант, прочитайте як запустити Tailscale exit node на VPS. Це окремі прапорці, і один VPS може одночасно виконувати обидві функції, але вони вирішують різні завдання та виходять з ладу з різних причин.
Коли VPS потрібен як маршрутизатор підмережі
Типовий випадок — приватна мережа, яку вже надав ваш провайдер. 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 (local area network) за власним маршрутизатором чи стійка пристроїв, на яких узагалі не можна запустити Tailscale, наприклад керований комутатор або старий NAS із заблокованою прошивкою. Один Linux-комп’ютер у цій мережі стає маршрутизатором підмережі для всіх інших пристроїв у ній. У домашніх умовах таким пристроєм часто є невелика VM на гіпервізорі, який ви вже використовуєте, а порівняння вартості домашнього хоста Proxmox з орендованим VPS — це питання, яке потрібно вирішити до вибору, на якому кінці тунелю мають працювати ваші сервіси.
В обох випадках є спільна вимога. Маршрутизатор підмережі вже повинен мати доступ до діапазону, який він оголошує, через власну таблицю маршрутизації та власний firewall. 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.20Команда ip route show має показати приватний діапазон на реальному інтерфейсі, наприклад 10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5. Якщо ping не працює вже на цьому етапі, безпосередньо до маршрутизатора, жоден параметр Tailscale не допоможе. Проблема полягає в конфігурації мережі VPS або у firewall на цільовому хості. Спочатку усуньте її, оскільки всі наступні перевірки залежать від цього.
Увімкніть IP forwarding і збережіть це налаштування після перезавантаження
Linux-комп’ютер відкидає всі пакети, адресовані не йому, якщо forwarding не увімкнено. Пересилання пакетів інших комп’ютерів — основне завдання subnet router, тому цей крок є обов’язковим.
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 починає працювати одразу, але після наступного завантаження налаштування зникає. Через це subnet router може працювати тижнями, а потім перестати пересилати трафік вранці після перезавантаження через оновлення ядра. Проблему складно помітити, оскільки зовні все працює. tailscale status і далі показує вузол як підключений, адміністративна консоль і далі показує схвалений маршрут, а клієнти й далі мають встановлений маршрут. Пакети надходять до VPS, але ядро відкидає їх без запису в журнал. Запис значень у /etc/sysctl.d/99-tailscale.conf забезпечує їх відновлення після перезавантаження.
Якщо оголосити маршрути, коли forwarding ще вимкнено, tailscale up попередить про це одразу. У виводі буде рядок, подібний до Warning: IPv4 forwarding is disabled. Subnet routes and exit nodes may not work correctly.. Прочитайте вивід цієї команди, а не пропускайте його.
Рекламуйте маршрути
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 (classless inter-domain routing, у форматі 10.0.0.0/24). Якщо помилково вказати власну адресу хоста, 10.0.0.5/24, команду буде відхилено, оскільки біти після префікса не дорівнюють нулю; у повідомленні про помилку буде зазначено префікс, який, імовірно, потрібно було вказати. Щоб припинити рекламу маршрутів, задайте порожній список за допомогою sudo tailscale set --advertise-routes=.
Схвалення маршруту в адміністративній консолі
Анонс маршруту — це запит, а не зміна конфігурації. Поки адміністратор не схвалить маршрут, жоден клієнт його не отримує, і жодна адреса в цьому діапазоні недоступна. Це зроблено навмисно: машина, яка може додавати себе до таблиць маршрутизації всіх клієнтів, могла б перехоплювати трафік для будь-якого потрібного їй діапазону.
Схваліть маршрут на сторінці Machines в адміністративній консолі. VPS буде позначено бейджем підмережі. Відкрийте його рядок, знайдіть розділ підмереж, відредагуйте параметри маршруту, увімкніть потрібний маршрут і збережіть зміни.
Схвалення надається окремо для кожного префікса. Якщо сьогодні анонсувати 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 сам є другим subnet router для власної локальної мережі, --accept-routes змусить його передавати трафік до власної безпосередньо підключеної підмережі через інший маршрутизатор, а не через власний інтерфейс. На резервному маршрутизаторі в парі з високою доступністю залиште --accept-routes вимкненим і рекламуйте лише потрібні маршрути.
Режим відмови: два маршрутизатори оголошують діапазони, що перекриваються
Два subnet routers не повинні оголошувати однакові діапазони. Діапазони, що перекриваються та мають різну довжину префікса, дозволені, і Tailscale вибирає найспецифічніший маршрут. Якщо router A оголошує 10.0.0.0/24, а router B — 10.0.0.0/16, трафік до 10.0.0.20 спрямовується через A.
Проблема виникає, коли A переходить в офлайн. Tailscale не перемикається на менш специфічний маршрут. Трафік до 10.0.0.20 зупиняється, а трафік до 10.1.0.20 продовжує проходити через B. Це виглядає так, ніби половина приватної мережі недоступна. Причина полягає в тому, що один недоступний вузол утримує специфічніший префікс. Якщо потрібне перемикання на резервний маршрут, налаштуйте ширший router так, щоб він також оголошував вужчі префікси. Тоді обидва маршрутизатори охоплюватимуть однакові адреси.
Інший конфлікт виникає ближче до клієнта. Якщо ви підключені до мережі готелю через 192.168.1.0/24, а ваш subnet router оголошує 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 свідомо. Такий самий конфлікт порушує роботу звичайного WireGuard VPN, який ви налаштовуєте вручну, з тієї самої причини: специфічніший локальний маршрут має перевагу, тому трафік не потрапляє в тунель.
Режим відмови: DNS перетворює ім’я на адресу, для якої немає відповідного маршруту
Цю проблему складно діагностувати, оскільки жоден компонент не повідомляє про помилку. Ім’я успішно перетворюється на адресу. Підключення завершується тайм-аутом.
Припустімо, що db.internal.example.com через ваш приватний nameserver перетворюється на 10.0.5.20, а ви оголосили 10.0.0.0/24. Запит успішний, оскільки перетворення DNS (domain name system) і IP-маршрутизація є окремими етапами, і жоден із них не перевіряє інший. Потім пакет до 10.0.5.20 не знаходить відповідного маршруту в tailnet, тому виходить через шлюз за замовчуванням клієнта й зникає.
Дві команди дають змогу перевірити ці етапи окремо:
nslookup db.internal.example.com
ip route get 10.0.5.20Якщо запит повертає адресу, але ip route get не відповідає на dev tailscale0, ім’я налаштоване правильно, а маршрут відсутній. Оголосіть діапазон, який охоплює цю адресу: 10.0.0.0/16 або другий явний префікс. Потім схваліть новий префікс у консолі.
На самому nameserver також є схожа проблема. Якщо ви вкажете глобальний nameserver в адміністративній консолі за приватною адресою, наприклад 10.0.0.53, ця адреса має входити до схваленого маршруту. Інакше пристрої взагалі не зможуть досягти резолвера. Якщо ввімкнути опцію, яка замінює локальні DNS-сервери, і вказати резолвер, до якого ніхто не має доступу, усі пристрої в tailnet одночасно втратять перетворення імен, зокрема ті, які ще секунду тому працювали. Спочатку оголосіть і схваліть маршрут до резолвера, а потім змініть налаштування DNS. Якщо проблема з DNS усередині тунелю постійно повторюється, у матеріалі як DNS працює некоректно через WireGuard-тунель описано той самий механізм без додаткового координаційного рівня.
Source NAT і site-to-site з’єднання
За замовчуванням subnet router замінює source address кожного переспрямованого пакета на власну private address. Це SNAT (source network address translation). Він потрібен, щоб відповіді працювали без змін у приватній мережі: база даних на 10.0.0.20 відповідає VPS, маршрут до якого їй уже відомий. Недолік полягає в тому, що база даних бачить кожне tailnet-з’єднання як підключення з VPS. Тому правила firewall за source address і access logs не містять корисної інформації.
Вимкніть SNAT у Linux, якщо потрібно зберігати реальну tailnet address клієнта:
sudo tailscale set --snat-subnet-routes=falseТоді хостам у приватній мережі потрібен маршрут назад до 100.64.0.0/10 — діапазону, який Tailscale призначає пристроям. Цей маршрут має вказувати на subnet router. Без зворотного маршруту відповіді надходитимуть до default gateway і не досягатимуть клієнта. Через це з’єднання зависатиме після першого пакета. Додайте static route на gateway приватної мережі або залиште SNAT увімкненим.
Site-to-site з’єднання — це два subnet router, які одночасно виконують цю функцію. Кожен із них оголошує власну мережу та приймає маршрути до мережі іншого:
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesВиконайте відповідну команду на іншому router, указавши його власний діапазон. Діапазони мають відрізнятися. Якщо великі передавання даних зависають, а ssh і ping працюють нормально, причиною є MSS (maximum segment size) — найбільший обсяг даних, який може містити TCP-пакет. Накладні витрати tunnel роблять переспрямовані пакети завеликими для певної ділянки мережі. 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, інакше після наступного boot воно зникне.
Обслуговування для стабільної роботи
За замовчуванням ключі вузлів мають термін дії 180 days, починаючи з August 2026. Коли ключ на subnet router спливає, вузол виходить із системи, а весь анонсований діапазон стає недоступним. При цьому в конфігурації не буде жодної зміни, яка пояснює проблему. На сторінці Machines в адмінконсолі вимкніть термін дії ключа для цієї машини. Занотуйте, що ви це зробили.
Tailscale надає перевагу прямому з’єднанню між вузлами та використовує relay servers, якщо не може його встановити. Реле працюють, але додають затримку. VPS із публічною адресою — найпростіший випадок: дозвольте вхідний UDP-трафік на 41641, і більшість вузлів підключатиметься напряму. Якщо firewall керується через ufw, у матеріалі описано синтаксис правил ufw, які насправді потрібні VPS.
Правила доступу — інша складова конфігурації. У tailnet за замовчуванням кожен ваш пристрій може підключатися до будь-якого іншого, тому схвалений маршрут працює одразу. Після створення ACL policy у призначенні правила потрібно вказати приватний діапазон, оскільки 10.0.0.20 не є адресою tailnet і на нього не поширюються правила, створені для tailnet IP-адрес або тегів.
Нарешті, вирішіть, чи потрібен вам coordination server, який ви не адмініструєте. Control plane Tailscale — це hosted service. Ключі зберігаються на ваших машинах, але обліковий запис і policy file зберігаються там. До того як надати йому маршрут у вашу приватну мережу, варто визначити, що насправді можна зробити через скомпрометований control plane або викрадений логін до облікового запису. У матеріалі описано модель довіри Tailscale і межі цієї довіри. Вартість рідко є причиною відмови від цього рішення, оскільки безкоштовний план підтримує до шести користувачів із необмеженою кількістю їхніх власних пристроїв. Водночас subnet router, запущений із тегом, обліковується інакше, ніж subnet router, у якому виконано вхід від вашого імені. Після цього вартість залежить від кількості людей, а не машин, тому варто заздалегідь розрахувати скільки насправді платить родина або команда з п’яти людей після завершення дії безкоштовного плану, перш ніж додавати обліковий запис, який перевищить ліміт. Запуск Headscale, self-hosted control server для Tailscale дає змогу розмістити його на власному VPS, але потребує самостійного обслуговування. Інший спосіб розв’язати цю проблему — відмовитися також від клієнтів Tailscale. Self-hosting VPN server NetBird розміщує coordination layer і власні mesh-клієнти на одній машині під вашим керуванням. Якщо ви ще обираєте між цією моделлю та конфігурацією, написаною вручну, порівняння WireGuard і Tailscale пояснює переваги coordination layer і пов’язані з ним витрати.
FAQ
Яка різниця між subnet router і exit node?
subnet router оголошує діапазон приватних адрес, тому пристрої tailnet можуть підключатися до машин, на яких не запущено Tailscale. exit node оголошує себе маршрутом до всього інтернету, тому пристрій передає весь трафік через публічну адресу цього вузла. Один VPS може виконувати обидві ролі. Це окремі прапорці, --advertise-routes і --advertise-exit-node, і для кожного потрібне окреме схвалення в admin console.
Чому мій Linux-клієнт ігнорує оголошений маршрут subnet?
Linux-клієнти не приймають маршрути subnet без явного дозволу. Виконайте sudo tailscale set --accept-routes на клієнті. Потім перевірте за допомогою ip route show table 52, а не ip route show. Tailscale встановлює прийняті маршрути в таблицю маршрутизації 52 і використовує для них policy rules, тому в основній таблиці їх немає, через що робочий маршрут може здаватися відсутнім.
Після перезавантаження subnet перестав працювати. Що сталося?
Найімовірніше, вимкнено IP forwarding. Значення, задане через sysctl -w, не зберігається після перезавантаження, тому запишіть його в /etc/sysctl.d/99-tailscale.conf і перевірте за допомогою sysctl net.ipv4.ip_forward. Якщо forwarding увімкнено, але діапазон і далі недоступний, перевірте вузол в admin console. За замовчуванням node keys завершують дію через 180 days. Прострочений subnet router виглядає як проблема мережі, а не облікового запису.
Чи можуть два subnet router оголошувати один і той самий діапазон?
Ідентичні діапазони — ні. Діапазони, що перекриваються, але мають різну довжину префікса, допустимі; використовується найспецифічніший маршрут. Для failover потрібна обережність: коли router із більш специфічним префіксом стає недоступним, Tailscale не перемикається на ширший маршрут, тому цей трафік зупиняється. Для повноцінної standby-пари обидва router мають оголошувати однакові специфічні префікси.
Ім’я хоста визначається, але підключення завершується тайм-аутом. Чому?
DNS-резолюція і маршрутизація — окремі етапи. Ім’я може визначатися в адресу, яку не охоплює жоден схвалений маршрут. У такому разі пакет виходить через шлюз за замовчуванням клієнта. Виконайте ip route get <address> на клієнті. Якщо відповідь не містить dev tailscale0, оголосіть діапазон, який охоплює цю адресу, і схваліть новий префікс в admin console.