Chạy Tailscale subnet router trên VPS
Quảng bá mạng private vào tailnet từ VPS: duyệt route, bật IP forwarding tự tồn tại sau reboot và thêm flag --accept-routes trên Linux.
Tailscale subnet router làm gì
Tailscale subnet router là một máy quảng bá toàn bộ dải địa chỉ IP private vào tailnet, để mọi thiết bị trong tailnet có thể truy cập các địa chỉ trong dải đó dù không có thiết bị nào ở đó chạy Tailscale. Tailnet là mạng Tailscale private của bạn, gồm các thiết bị đã đăng nhập vào cùng một account hoặc organisation. Exit node là tính năng thường bị nhầm với subnet router và có nhiệm vụ ngược lại. Nó gửi toàn bộ traffic của một thiết bị ra Internet thông qua VPS, nên VPS trở thành route của thiết bị đó đến Internet public.
Mỗi khái niệm có thể tóm tắt trong một câu. Subnet router giúp một mạng private có thể truy cập được từ tailnet. Exit node thay đổi nơi traffic public của bạn đi ra Internet. Nếu bạn cần tính năng thứ hai, hãy đọc cách chạy Tailscale exit node trên VPS thay vì phần này. Đây là hai flag riêng biệt; một VPS có thể chạy cả hai cùng lúc, nhưng chúng giải quyết các vấn đề khác nhau và cũng gặp lỗi theo những cách khác nhau.
Khi VPS cần làm subnet router
Trường hợp phổ biến là một private network đã được nhà cung cấp cấp sẵn. VPS có một địa chỉ public và một interface thứ hai trong một private segment. Các server khác trong segment đó hoàn toàn không có địa chỉ public: một database tại 10.0.0.20 và một backup target tại 10.0.0.30. Cài Tailscale trên một VPS, quảng bá 10.0.0.0/24, rồi laptop của bạn có thể truy cập trực tiếp các địa chỉ private đó. Không cần thay đổi gì trên các server khác trong segment, và database vẫn không có địa chỉ public. Nếu bạn chỉ cần truy cập một web app duy nhất trên một port duy nhất trong segment đó, quảng bá toàn bộ dải mạng là quá mức cần thiết; Tailscale serve đặt HTTPS trực tiếp trên port duy nhất đó. Lập luận tương tự áp dụng cho một daemon cố ý chỉ bind vào localhost, chẳng hạn dsh chạy headless dưới systemd, khi địa chỉ tailnet trên VPS đó thay thế SSH tunnel mà bạn thường phải duy trì để truy cập UI của daemon.
Trường hợp còn lại là một network nằm phía bên kia VPS. Đó có thể là một LAN (local area network) tại nhà hoặc văn phòng, nằm sau router riêng, hoặc một rack gồm các appliance không thể chạy Tailscale, chẳng hạn managed switch hoặc NAS cũ dùng firmware bị khóa. Một máy Linux trong network đó sẽ làm subnet router cho toàn bộ thiết bị còn lại. Ở nhà, máy này thường là một VM trên hypervisor mà bạn đã vận hành sẵn. Bài toán chi phí giữa một Proxmox host tại nhà và một VPS thuê là điều cần giải quyết trước khi quyết định dịch vụ nên đặt ở đầu nào của tunnel.
Cả hai trường hợp đều có chung một yêu cầu. Subnet router phải truy cập được dải mạng mà nó quảng bá, bằng routing table và firewall của chính nó. Tailscale không tạo kết nối đó. Tailscale chuyển traffic đến router rồi giao cho kernel forward.
Cài đặt Tailscale và kiểm tra route cục bộ trước
curl -fsSL https://tailscale.com/install.sh | shScript xác định bản phân phối, thêm package repository của Tailscale, cài đặt command tailscale và daemon tailscaled, sau đó enable service. Xác nhận bằng systemctl is-active tailscaled. Kết quả phải hiển thị active.
Trước tiên, hãy xác minh VPS có thể truy cập network mà bạn dự định quảng bá.
ip route show
ping -c3 10.0.0.20ip route show phải liệt kê private range trên một interface thực, chẳng hạn như 10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5. Nếu ping thất bại tại đây, ngay trên router, thì không có flag Tailscale nào có thể khắc phục. Nguyên nhân nằm ở cấu hình network của VPS hoặc firewall trên host đích. Hãy sửa lỗi đó trước, vì mọi test tiếp theo đều phụ thuộc vào nó.
Bật IP forwarding và duy trì sau khi reboot
Máy Linux sẽ loại bỏ mọi packet không được gửi đến chính nó nếu forwarding chưa được bật. Chuyển tiếp packet của các máy khác là nhiệm vụ chính của subnet router, nên không được bỏ qua bước này.
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.confKiểm tra bằng sysctl net.ipv4.ip_forward. Lệnh này phải in ra net.ipv4.ip_forward = 1.
Nhiều người chỉ thực hiện đúng một nửa bước này. sudo sysctl -w net.ipv4.ip_forward=1 có hiệu lực ngay lập tức nhưng sẽ mất sau lần boot tiếp theo. Vì vậy, subnet router có thể chạy nhiều tuần rồi dừng vào buổi sáng sau khi reboot để nâng cấp kernel. Điểm gây nhầm lẫn là không có dấu hiệu rõ ràng cho thấy hệ thống bị lỗi. tailscale status vẫn hiển thị node đang online, admin console vẫn hiển thị route đã được phê duyệt và các client vẫn cài route đó. Packet đến VPS nhưng kernel loại bỏ chúng mà không ghi log. Ghi các giá trị vào /etc/sysctl.d/99-tailscale.conf là cách để khôi phục chúng sau khi reboot.
Nếu bạn quảng bá route trong khi forwarding vẫn tắt, tailscale up sẽ cảnh báo ngay lúc đó bằng một dòng gần giống Warning: IPv4 forwarding is disabled. Subnet routes and exit nodes may not work correctly.. Hãy đọc output của lệnh đó thay vì cuộn qua.
Quảng bá các route
sudo tailscale up --advertise-routes=10.0.0.0/24Trên VPS đã đăng nhập vào tailnet, thay đổi cấu hình tại chỗ:
sudo tailscale set --advertise-routes=10.0.0.0/24Dùng tailscale set cho mọi thay đổi sau đó. Chạy lại tailscale up với một flag duy nhất sẽ reset các flag bạn không lặp lại. CLI sẽ dừng và báo lỗi rằng khi thay đổi cấu hình theo cách này, bạn phải chỉ định tất cả các flag không mặc định. tailscale set chỉ thay đổi một cấu hình và giữ nguyên các cấu hình còn lại.
Đặt nhiều dải mạng trong một danh sách phân tách bằng dấu phẩy, không có khoảng trắng: --advertise-routes=10.0.0.0/24,192.168.50.0/24. Mỗi mục phải là một network address ở dạng CIDR (classless inter-domain routing, dạng 10.0.0.0/24). Nếu ghi nhầm địa chỉ host của chính bạn, 10.0.0.5/24, lệnh sẽ bị từ chối vì các bit sau prefix không bằng 0. Thông báo lỗi sẽ nêu prefix mà bạn có thể đã định dùng. Để dừng quảng bá, đặt danh sách rỗng bằng sudo tailscale set --advertise-routes=.
Phê duyệt route trong admin console
Quảng bá một route chỉ là một yêu cầu, không phải thay đổi đã được áp dụng. Cho đến khi admin phê duyệt, không client nào nhận được route và không tài nguyên nào trong dải đó có thể truy cập. Đây là cơ chế có chủ đích, vì một máy có thể tự thêm mình vào routing table của mọi người sẽ có thể thu thập traffic của bất kỳ dải nào nó muốn.
Phê duyệt route trên trang Machines của admin console. VPS sẽ được liệt kê cùng subnet badge. Mở dòng tương ứng, tìm phần subnets, chỉnh sửa route settings, chọn route rồi lưu.
Việc phê duyệt áp dụng cho từng prefix. Hôm nay bạn quảng bá 10.0.0.0/24 và tháng sau quảng bá 192.168.50.0/24, prefix mới sẽ xuất hiện ở trạng thái chưa được phê duyệt, còn prefix cũ vẫn hoạt động. Từ VPS, route đã được phê duyệt và route bị bỏ qua trông giống hệt nhau, vì vậy hãy kiểm tra console trước khi debug vấn đề khác.
Bạn có thể bỏ qua bước thủ công bằng block autoApprovers trong file tailnet policy:
{
"autoApprovers": {
"routes": {
"10.0.0.0/24": ["tag:subnet-router"]
}
}
}Sau đó khởi động node với tag đó, sudo tailscale up --advertise-routes=10.0.0.0/24 --advertise-tags=tag:subnet-router, và route sẽ được phê duyệt ngay khi được quảng bá. Tag này trước hết phải tồn tại trong phần tagOwners của cùng file policy. Cách này đáng thiết lập nếu bạn rebuild VPS bằng script, vì node sau khi rebuild là một node mới và các route của nó lại bắt đầu ở trạng thái chưa được phê duyệt.
Vì sao client Linux bỏ qua route nếu không có --accept-routes
Route đã được quảng bá và phê duyệt. Điện thoại và Mac của bạn truy cập được 10.0.0.20. Laptop Linux không truy cập được, nhưng admin console không cho thấy vấn đề nào.
Chấp nhận subnet route nghĩa là ghi các entry vào routing table của client. Trên Android, iOS, macOS, tvOS và Windows, Tailscale client tự làm việc này cho bạn. Trên Linux thì không, vì máy Linux thường là server hoặc router có routing table đã được cấu hình có chủ đích. Tự động chèn một /24 học được từ network có thể làm hỏng traffic mà máy đó đang xử lý. Vì vậy, trên Linux, bạn phải bật tùy chọn này trên từng client:
sudo tailscale set --accept-routesSau đó kiểm tra route đã được ghi vào đâu:
ip route show table 52
ip route get 10.0.0.20Tailscale trên Linux không đặt các route đã chấp nhận vào main routing table. Nó đặt chúng vào routing table 52 và cài policy rule. Các rule này hiển thị bằng ip rule show trong dải priority từ 5210 đến 5270 và chuyển các packet không khớp đến table đó. Vì vậy, ip route show tự nó sẽ không bao giờ liệt kê 10.0.0.0/24. Người đọc chỉ kiểm tra lệnh đó sẽ kết luận rằng --accept-routes không có tác dụng. ip route show table 52 mới là lệnh hiển thị trạng thái thực tế, và kết quả phải liệt kê dải đã quảng bá trên tailscale0.
Có một ngoại lệ cần biết. Nếu node Linux này cũng là subnet router thứ hai cho local network của chính nó, --accept-routes sẽ khiến node gửi traffic đến subnet được kết nối trực tiếp của nó qua router kia thay vì qua interface của chính nó. Trên router dự phòng trong một cặp high availability, hãy tắt --accept-routes và chỉ quảng bá route.
Chế độ lỗi: hai router quảng bá các dải địa chỉ chồng lấn
Hai subnet router không được quảng bá các dải địa chỉ giống hệt nhau. Các dải chồng lấn với độ dài prefix khác nhau vẫn được phép, và Tailscale sẽ chọn route cụ thể nhất. Khi router A quảng bá 10.0.0.0/24 còn router B quảng bá 10.0.0.0/16, lưu lượng đến 10.0.0.20 sẽ đi qua A.
Điều thường gây bất ngờ là hành vi khi A offline. Tailscale không chuyển sang route ít cụ thể hơn. Lưu lượng đến 10.0.0.20 bị dừng, còn lưu lượng đến 10.1.0.20 vẫn hoạt động qua B. Triệu chứng trông giống như một nửa private network bị down, nhưng nguyên nhân là một node offline đang giữ prefix cụ thể hơn. Nếu muốn có failover, hãy để router có dải rộng hơn cũng quảng bá các prefix hẹp hơn, để cả hai cùng bao phủ các địa chỉ đó.
Một kiểu chồng lấn khác xảy ra gần client hơn. Khi bạn đang ở trên network của khách sạn tại 192.168.1.0/24, trong khi subnet router quảng bá 192.168.1.0/24, hai route sẽ tranh quyền xử lý cùng các đích đến. Route nào thắng phụ thuộc vào platform. Trên Linux, hãy thêm một rule đứng trước rule riêng của Tailscale để các địa chỉ local sử dụng main table:
sudo ip rule add to 192.168.1.0/24 priority 2500 lookup mainRule này không persistent và sẽ mất sau lần boot tiếp theo. Cách xử lý thực sự là chọn một private range mà bạn sẽ không gặp trên các network bên ngoài. 192.168.0.0/24 và 192.168.1.0/24 là các giá trị mặc định trên hầu hết home router, vì vậy hãy chọn một dải bên trong 10.0.0.0/8 một cách có chủ đích. Xung đột tương tự cũng làm hỏng WireGuard VPN thông thường mà bạn tự cấu hình, vì cùng một lý do: route local cụ thể hơn sẽ thắng, nên lưu lượng không bao giờ đi vào tunnel.
Chế độ lỗi: DNS phân giải thành địa chỉ không có route bao phủ
Trường hợp này khó debug vì không có thành phần nào báo lỗi. Tên được phân giải thành công. Kết nối bị timeout.
Giả sử db.internal.example.com được private nameserver phân giải thành 10.0.5.20 và bạn đã quảng bá 10.0.0.0/24. Lookup vẫn thành công vì DNS (domain name system) và IP routing là hai bước riêng biệt, không bước nào kiểm tra bước còn lại. Sau đó, packet đến 10.0.5.20 không tìm thấy route phù hợp trên tailnet, nên đi qua default gateway của client rồi biến mất.
Dùng 2 lệnh để kiểm tra riêng từng phần:
nslookup db.internal.example.com
ip route get 10.0.5.20Nếu lookup trả về một địa chỉ nhưng ip route get không phản hồi với dev tailscale0, tên vẫn đúng và route đang thiếu. Hãy quảng bá một range bao phủ địa chỉ đó, dùng 10.0.0.0/16 hoặc thêm một prefix tường minh, sau đó approve prefix mới trong console.
Nameserver cũng có một lỗi tương tự. Nếu bạn đặt nameserver toàn cục trong admin console tại một private address như 10.0.0.53, địa chỉ đó phải nằm trong một route đã được approve. Nếu không, các thiết bị sẽ không thể truy cập resolver. Nếu bật tùy chọn ghi đè local DNS servers trong khi trỏ đến một resolver không thiết bị nào truy cập được, mọi thiết bị trong tailnet sẽ mất khả năng phân giải tên cùng lúc, kể cả những thiết bị vừa hoạt động ngay trước đó. Hãy quảng bá và approve route đến resolver trước, rồi mới thay đổi DNS setting. Nếu DNS bên trong tunnel là phần bạn thường xuyên phải xử lý, cách DNS bị lỗi qua WireGuard tunnel trình bày cùng cơ chế này mà không có thêm coordination layer bên trên.
Source NAT và liên kết site-to-site
Theo mặc định, subnet router viết lại địa chỉ nguồn của mọi packet được forward thành địa chỉ private của chính nó. Đây là SNAT (source network address translation). Cơ chế này giúp packet phản hồi hoạt động mà không cần thay đổi gì trên private network: database tại 10.0.0.20 trả lời VPS, vì nó đã biết cách truy cập VPS. Đổi lại, database thấy mọi kết nối từ tailnet đều đến từ VPS. Vì vậy, firewall rule theo source và access log không còn cho biết client thực sự là ai.
Tắt SNAT trên Linux khi bạn muốn giữ địa chỉ tailnet thực của client:
sudo tailscale set --snat-subnet-routes=falseKhi đó, các host trên private network cần có route quay về 100.64.0.0/10, là dải địa chỉ mà Tailscale cấp cho các device, và route này phải trỏ đến subnet router. Nếu không có route quay về, packet phản hồi sẽ đi đến default gateway và không bao giờ đến nơi. Kết nối sẽ treo ngay sau packet đầu tiên. Thêm static route trên gateway của private network, hoặc giữ SNAT.
Liên kết site-to-site là mô hình trong đó 2 subnet router thực hiện việc này đồng thời. Mỗi router quảng bá network của mình và chấp nhận network của router còn lại:
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesChạy command tương ứng trên router còn lại với dải địa chỉ của router đó. 2 dải địa chỉ phải khác nhau. Nếu các lần truyền dữ liệu lớn bị treo trong khi ssh và ping vẫn hoạt động bình thường, nguyên nhân là MSS (maximum segment size), tức kích thước lớn nhất của phần dữ liệu mà một TCP packet mang theo. Overhead của tunnel khiến packet được forward quá lớn đối với một link nào đó trên đường truyền. Clamping sẽ khắc phục vấn đề này:
sudo iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuLưu rule đó bằng iptables-persistent, nếu không rule sẽ biến mất sau lần boot tiếp theo.
Bảo trì để hệ thống hoạt động ổn định
Theo mặc định, node key hết hạn sau 180 ngày, tính từ tháng 8 năm 2026. Khi key trên subnet router hết hạn, node sẽ sign out và toàn bộ dải mạng được quảng bá trở nên không thể truy cập. Không có thay đổi cấu hình nào ở nơi khác để giải thích cho sự cố này. Hãy tắt thời hạn hết hạn của key cho máy này trên trang Machines trong admin console, rồi ghi lại rằng bạn đã thực hiện việc đó.
Tailscale ưu tiên kết nối trực tiếp giữa các peer và chuyển sang relay server khi không thiết lập được kết nối trực tiếp. Relay vẫn hoạt động, nhưng làm tăng độ trễ. VPS có public address là trường hợp đơn giản: cho phép UDP inbound trên cổng 41641 thì hầu hết peer sẽ kết nối trực tiếp. Nếu ufw đang quản lý firewall, các rule ufw mà VPS thực sự cần trình bày cú pháp tương ứng.
Access rule là phần còn lại. Với tailnet mặc định, mọi device của bạn đều có thể truy cập mọi device khác, nên route đã được phê duyệt sẽ hoạt động ngay. Sau khi viết ACL policy, phía đích của rule phải chỉ rõ private range, vì 10.0.0.20 không phải là địa chỉ tailnet và không được các rule viết theo tailnet IP hoặc tag áp dụng.
Cuối cùng, hãy quyết định bạn có muốn dùng một coordination server mà mình không tự vận hành hay không. Control plane của Tailscale là một hosted service. Key vẫn nằm trên các máy của bạn, nhưng account và policy file được lưu tại đó. Điều đáng xem xét trước khi cấp cho dịch vụ này một route vào private network là control plane bị xâm nhập hoặc thông tin đăng nhập identity bị đánh cắp thực sự có thể bị lợi dụng như thế nào; mô hình tin cậy của Tailscale chỉ ra ranh giới đó nằm ở đâu. Chi phí hiếm khi là lý do chính khiến người dùng rời bỏ dịch vụ này, vì free plan hỗ trợ tối đa sáu user với số lượng device riêng không giới hạn, dù subnet router được khởi chạy dưới một tag được tính khác với subnet router đăng nhập bằng account của bạn. Sau giới hạn đó, hóa đơn được tính theo số người thay vì số máy. Vì vậy, chi phí thực tế của một hộ gia đình hoặc team năm người sau khi free plan hết hiệu lực là điều nên tính trước khi thêm account khiến bạn vượt giới hạn. Chạy Headscale, Tailscale control server tự host sẽ giữ coordination server trên VPS của bạn, nhưng bạn phải tự bảo trì. Một cách khác để giải quyết cùng mối lo này là không dùng cả client của Tailscale. Tự host NetBird VPN server sẽ đặt coordination layer và các mesh client riêng của nó trên một máy do bạn kiểm soát. Nếu bạn vẫn đang cân nhắc giữa mô hình này và việc tự viết config, bài so sánh WireGuard và Tailscale giải thích coordination layer cung cấp gì và bạn phải trả giá cho nó như thế nào.
FAQ
Sự khác nhau giữa subnet router và exit node là gì?
Subnet router quảng bá một dải địa chỉ private, để các thiết bị trong tailnet có thể truy cập những máy không chạy Tailscale. Exit node tự quảng bá làm route đến toàn bộ Internet, nên thiết bị sẽ gửi toàn bộ traffic qua địa chỉ public của node đó. Một VPS có thể đồng thời làm cả hai. Đây là 2 flag riêng biệt, --advertise-routes và --advertise-exit-node, và mỗi flag cần được phê duyệt riêng trong admin console.
Vì sao client Linux của tôi bỏ qua subnet route đã quảng bá?
Client Linux không tự chấp nhận subnet route. Hãy chạy sudo tailscale set --accept-routes trên client. Sau đó kiểm tra bằng ip route show table 52, không phải ip route show. Tailscale cài các route đã được chấp nhận vào routing table 52 và đi đến chúng thông qua policy rule, nên main table không liệt kê các route này. Vì vậy, một route đang hoạt động có thể trông như bị thiếu.
Subnet của tôi ngừng hoạt động sau khi reboot. Đã hỏng phần nào?
Khả năng cao là IP forwarding. Giá trị được đặt bằng sysctl -w sẽ không tồn tại sau reboot, nên hãy ghi giá trị đó vào /etc/sysctl.d/99-tailscale.conf và xác nhận bằng sysctl net.ipv4.ip_forward. Nếu forwarding đang bật nhưng dải địa chỉ vẫn không thể truy cập, hãy kiểm tra node trong admin console. Node key mặc định hết hạn sau 180 ngày. Khi subnet router hết hạn, sự cố trông giống lỗi mạng hơn là lỗi tài khoản.
Hai subnet router có thể quảng bá cùng một range không?
Không thể quảng bá các range giống hệt nhau. Các range chồng lấn nhưng có prefix length khác nhau thì được, và route cụ thể nhất sẽ được ưu tiên. Failover cần được thiết kế cẩn thận: khi router giữ prefix cụ thể hơn offline, Tailscale không chuyển sang route rộng hơn, nên traffic đó sẽ dừng. Để có một cặp standby thực sự, hãy để cả hai router quảng bá cùng các prefix cụ thể.
Hostname phân giải được nhưng connection bị timeout. Vì sao?
DNS resolution và routing là 2 bước riêng biệt. Một name có thể phân giải thành địa chỉ mà không có approved route nào bao phủ, sau đó packet sẽ đi qua default gateway của client. Hãy chạy ip route get <address> trên client. Nếu kết quả không chứa dev tailscale0, hãy quảng bá một range bao phủ địa chỉ đó và phê duyệt prefix mới trong admin console.