Skip to content

ACL / Grants 入門:限制誰能連到哪裡

認識 tailnet policy file、預設全開的規則、ACL 與 grants 的差別,用 group、tag、autogroup 寫出只開放必要連線的規則,並用 tests 防止改壞

Updated View as Markdown

剛建好的 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 的建議流程:

  1. 先寫好 tests,描述想要的結果(例如家人連不到 tag:server)。
  2. 再改規則,存檔時讓 tests 幫你檢查。
  3. 改壞了可以到 Admin console 的 Configuration logs 頁面還原之前的版本。

常見問題

  • 加了限制規則,結果什麼都沒變? 多半是預設的 * 到 *:* 全開規則還在。規則是聯集,全開規則要拿掉,其他限制才有作用。
  • 改完規則後,連自己的裝置都連不到? 拿掉全開規則後,記得補上 autogroup:member 到 autogroup:self 這一條。
  • 給了 Exit Node 那台裝置的權限,還是不能經由它上網? 經由 Exit Node 上網要放行 autogroup:internet,見上面的範例。
  • 規則只放行 A 連 B,B 卻能回應? 正常。規則限制的是誰能發起連線,回應封包本來就會通。

參考資料

Navigation

Type to search…

↑↓ navigate↵ selectEsc close