剛建好的 tailnet 預設是全開的:每台裝置都能連到其他每台裝置的任何連接埠。自己一個人用沒什麼問題,但只要加入了家人、同事、伺服器或像 Codex Cloud 這種自動化節點,就該決定「誰能連到哪裡」。這篇介紹 Tailscale 的存取控制怎麼寫。
本文依 2026 年 10 月的 Tailscale 官方文件整理。存取規則在 Tailscale 的控制平面處理,和用戶端版本無關,所有方案都能使用 ACL 與 grants。
Tailnet policy file
tailnet 的存取規則都寫在一份 tailnet policy file 裡,格式是 HuJSON:就是 JSON,但允許 // 註解和結尾多一個逗號。這份檔案可以在 Admin console 的 Access controls 頁面編輯(有視覺化編輯器和 JSON 編輯器兩種模式),也可以用 GitOps 或 API 管理。
常用的區段:
| 區段 | 用途 |
|---|---|
grants |
存取規則(建議的寫法) |
acls |
舊式存取規則,仍可使用 |
groups |
把使用者分組,例如 group:family |
tagOwners |
定義有哪些 tag、誰可以把 tag 套到裝置上 |
hosts |
替 IP 或網段取好記的名字 |
tests |
存取規則的測試,存檔時不通過就拒絕 |
ssh |
Tailscale SSH 規則 |
autoApprovers |
自動核准 Subnet Router 路由與 Exit Node 宣告 |
nodeAttrs |
替裝置加上屬性,例如 Funnel 需要的 funnel |
預設的規則
新 tailnet 的預設 policy 長這樣:
{
"acls": [{ "action": "accept", "src": ["*"], "dst": ["*:*"] }],
"ssh": [
{
"action": "check",
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"]
}
]
}第一條讓所有裝置互連;第二條讓每個人能用 Tailscale SSH 連自己的裝置(check 模式,要重新驗證身分)。
要特別注意的是空的和沒寫不一樣:policy 裡完全沒有 acls 區段時,Tailscale 會套用上面的預設全開規則;要全部拒絕,得明確寫 "acls": []。官方的 grants 範例都只寫 grants 區段,自己改寫時建議搭配下面的 tests,確認改完的效果符合預期。
規則怎麼運作
不論 ACL 或 grants,規則都有這幾個特性:
- 預設拒絕:只有被某條規則明確放行的連線才會通,沒有「拒絕」規則可寫。
- 規則取聯集:多條規則同時符合時,權限是全部加起來。寫得比較細的規則不會蓋掉寬鬆的規則,所以只要留著一條
*到*的全開規則,其他限制都等於沒有作用。 - 有方向性:允許 A 連 B,不代表 B 能主動連 A;但 A 連過去之後,B 的回應封包可以正常回來。
- 在各裝置本機執行:規則由控制平面下發,每台裝置自己過濾進來的封包。
- 不影響區網:規則只管走 Tailscale 的流量,裝置在自己區網上能連到什麼不受影響。
另外 Taildrop 可以在同一個人的裝置之間傳檔,不受存取規則限制;但打了 tag 的裝置(例如下面的 tag:nas)不能用 Taildrop 收送檔案。
ACL 與 grants
Tailscale 有兩種寫法:舊的 ACL(acls 區段)與新的 grants(grants 區段)。兩者可以同時存在同一份 policy 裡。
| ACL | Grants | |
|---|---|---|
| 寫法 | "action": "accept"、src、dst 裡帶連接埠(*:*) |
src、dst、ip(連接埠與協定獨立一個欄位) |
| 現況 | 會持續支援,但不會再加新功能 | 官方建議的寫法 |
| 額外的能力 | — | via 限定經由哪些路由裝置、app 應用層權限、srcPosture 等 |
| 視覺化編輯 | 用視覺化編輯器修改後會自動轉成 grants 語法 | 原生支援 |
同一條規則的兩種寫法:
// ACL
"acls": [
{ "action": "accept", "src": ["group:family"], "dst": ["tag:nas:443"] },
],
// Grants
"grants": [
{ "src": ["group:family"], "dst": ["tag:nas"], "ip": ["tcp:443"] },
],新寫的規則建議直接用 grants,本站其他文章的範例也都用 grants。
ip 欄位的寫法
| 寫法 | 意思 |
|---|---|
* |
所有連接埠(TCP、UDP 與 ICMP) |
443、80-443 |
指定連接埠或範圍,TCP、UDP 與 ICMP 都算 |
tcp:443、tcp:80-443 |
指定協定的連接埠或範圍 |
icmp:* |
某個協定的全部(適合 ICMP 這類沒有連接埠的協定) |
可以一次寫多個,例如 "ip": ["tcp:443", "udp:53"]。一般只想限制 TCP 服務時寫 tcp:<port> 最精準,寫成單純的 443 會連 UDP 443 一起開放。
來源與目的地的寫法
src 與 dst 用「selector」指定對象,常用的有:
| Selector | 代表 |
|---|---|
* |
所有對象 |
alice@example.com |
某個使用者(的所有裝置)。GitHub 登入的帳號寫成 alice@github |
group:family |
自己在 groups 定義的群組 |
tag:nas |
打上某個 tag 的裝置(tag 要先在 tagOwners 定義) |
autogroup:member |
tailnet 的直接成員(含受邀的使用者,不含只接受了裝置分享的人) |
autogroup:admin |
角色是 Admin 的使用者;另有 autogroup:owner 等角色 |
autogroup:tagged |
所有打了 tag 的裝置 |
autogroup:self |
只能放在 dst:「來源使用者自己的裝置」,不含打了 tag 的裝置 |
autogroup:internet |
只能放在 dst:經由 Exit Node 上網 |
autogroup:shared |
只能放在 src:接受了裝置分享的外部使用者 |
192.168.1.0/24 |
IP 或網段,例如 Subnet Router 後面的設備 |
Groups
把人分組,規則就不必一個個列帳號,換人時也只要改一個地方:
"groups": {
"group:family": ["mom@example.com", "dad@example.com"],
"group:admins": ["me@example.com"],
},群組名稱必須以 group: 開頭,成員要寫完整的登入帳號,群組不能包含其他群組。
Tags
伺服器、NAS 這類「不屬於某個人」的裝置,建議用 tag 當身分。tag 要先在 tagOwners 定義誰能套用:
"tagOwners": {
"tag:server": ["group:admins"],
"tag:nas": ["group:admins"],
},裝置打上 tag 後就不再以使用者的身分出現,規則改依 tag 判斷,autogroup:self 也不會包含它。tag 與 auth key 的完整說明見之後的文章。
實際範例
下面的範例都是「整份 policy 的一部分」,要搭配移除預設的全開規則才有效果。
每個人只能連自己的裝置
"grants": [
{
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"ip": ["*"],
},
],這是官方範例,適合多人共用一個 tailnet 但各自的裝置不互通的情況。注意它不影響裝置分享:某人把自己的裝置分享給別人時,對方仍然連得到。
家人只能用 Exit Node 上網
家人裝了 Tailscale,只想讓他們在外面時經由家裡的 Exit Node 上網,不能連到家裡其他設備:
"groups": {
"group:family": ["mom@example.com", "dad@example.com"],
},
"grants": [
// 家人:只能經由 Exit Node 上網
{
"src": ["group:family"],
"dst": ["autogroup:internet"],
"ip": ["*"],
},
// 每個人都能連自己的裝置
{
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"ip": ["*"],
},
],dst 要寫 autogroup:internet,寫成 Exit Node 那台裝置只會允許連到它,不能經由它上網。Exit Node 本身仍要另外設定與核准,詳見 Exit Node。
伺服器只開放特定連接埠
讓家人只能連 NAS 的網頁介面與檔案分享,管理者則能連所有伺服器:
"grants": [
{
"src": ["group:family"],
"dst": ["tag:nas"],
"ip": ["tcp:443", "tcp:445"],
},
{
"src": ["group:admins"],
"dst": ["tag:server", "tag:nas"],
"ip": ["*"],
},
],透過 Subnet Router 連到區網設備時,dst 改寫成 IP 或網段即可,例如 "dst": ["192.168.1.20/32"],範例見 Subnet Router。
用 tests 防止改壞
規則越寫越多,很容易一改就把重要的連線擋掉,或不小心開太大。tests 區段讓你寫下「誰應該連得到、誰應該連不到」,每次儲存 policy 時都會執行,有任何一項不通過,這次修改就會被拒絕:
"tests": [
{
"src": "mom@example.com",
"accept": ["tag:nas:443"],
"deny": ["tag:server:22", "100.101.102.103:80"],
},
{
"src": "me@example.com",
"proto": "icmp",
"accept": ["tag:server:0"],
},
],src是要模擬的身分:使用者、群組、tag 或hosts裡定義的名稱,不能用*。accept/deny的每一項是目的地:連接埠,連接埠只能寫單一數字;目的地可以是 Tailscale IP、使用者、群組、tag 或hosts名稱,不能寫網段。- 沒寫
proto時檢查的是 TCP 或 UDP;測 ICMP 要寫"proto": "icmp",連接埠填0。
在 Admin console 的 Access controls 頁面,視覺化編輯器的 Tests 分頁也可以新增測試。
改 policy 的建議流程:
- 先寫好 tests,描述想要的結果(例如家人連不到
tag:server)。 - 再改規則,存檔時讓 tests 幫你檢查。
- 改壞了可以到 Admin console 的 Configuration logs 頁面還原之前的版本。
常見問題
- 加了限制規則,結果什麼都沒變? 多半是預設的
*到*:*全開規則還在。規則是聯集,全開規則要拿掉,其他限制才有作用。 - 改完規則後,連自己的裝置都連不到? 拿掉全開規則後,記得補上
autogroup:member到autogroup:self這一條。 - 給了 Exit Node 那台裝置的權限,還是不能經由它上網? 經由 Exit Node 上網要放行
autogroup:internet,見上面的範例。 - 規則只放行 A 連 B,B 卻能回應? 正常。規則限制的是誰能發起連線,回應封包本來就會通。