Refresh docs for Grants

- Mention policy as generic term that covers ACLs or Grants
- Refresh routes policy examples
- Remove Headscale specific exit node separation. Use via instead.

Fixes: #3087
This commit is contained in:
Florian Preinstorfer 2026-04-19 12:59:20 +02:00 committed by nblock
parent 1a64d950fd
commit 109bfc404c
6 changed files with 49 additions and 52 deletions

View File

@ -134,7 +134,7 @@ help to the community.
Running headscale on a machine that is also in the tailnet can cause problems with subnet routers, traffic relay nodes, and MagicDNS. It might work, but it is not supported. Running headscale on a machine that is also in the tailnet can cause problems with subnet routers, traffic relay nodes, and MagicDNS. It might work, but it is not supported.
## Why do two nodes see each other in their status, even if an ACL allows traffic only in one direction? ## Why do two nodes see each other in their status, even if a policy rule allows traffic only in one direction?
A frequent use case is to allow traffic only from one node to another, but not the other way around. For example, the A frequent use case is to allow traffic only from one node to another, but not the other way around. For example, the
workstation of an administrator should be able to connect to all nodes but the nodes themselves shouldn't be able to workstation of an administrator should be able to connect to all nodes but the nodes themselves shouldn't be able to
@ -142,7 +142,7 @@ connect back to the administrator's node. Why do all nodes see the administrator
`tailscale status`? `tailscale status`?
This is essentially how Tailscale works. If traffic is allowed to flow in one direction, then both nodes see each other This is essentially how Tailscale works. If traffic is allowed to flow in one direction, then both nodes see each other
in their output of `tailscale status`. Traffic is still filtered according to the ACL, with the exception of in their output of `tailscale status`. Traffic is still filtered according to the policy, with the exception of
`tailscale ping` which is always allowed in either direction. `tailscale ping` which is always allowed in either direction.
See also <https://tailscale.com/docs/concepts/device-visibility>. See also <https://tailscale.com/docs/concepts/device-visibility>.

View File

@ -21,8 +21,9 @@ provides on overview of Headscale's feature and compatibility with the Tailscale
- [x] Dual stack (IPv4 and IPv6) - [x] Dual stack (IPv4 and IPv6)
- [x] Ephemeral nodes - [x] Ephemeral nodes
- [x] Embedded [DERP server](../ref/derp.md) - [x] Embedded [DERP server](../ref/derp.md)
- [x] Access control lists ([GitHub label "policy"](https://github.com/juanfont/headscale/labels/policy%20%F0%9F%93%9D)) - [x] Policy ([GitHub label "policy"](https://github.com/juanfont/headscale/labels/policy%20%F0%9F%93%9D))
- [x] ACL management via API - [x] ACLs
- [x] Grants
- [x] Some [Autogroups](../ref/policy.md#autogroups) - [x] Some [Autogroups](../ref/policy.md#autogroups)
- [x] [Auto approvers](https://tailscale.com/docs/reference/syntax/policy-file#auto-approvers) for [subnet - [x] [Auto approvers](https://tailscale.com/docs/reference/syntax/policy-file#auto-approvers) for [subnet
routers](../ref/routes.md#automatically-approve-routes-of-a-subnet-router) and [exit routers](../ref/routes.md#automatically-approve-routes-of-a-subnet-router) and [exit

View File

@ -53,7 +53,7 @@ Headscale provides a metrics and debug endpoint. It allows to introspect differe
- Information about the Go runtime, memory usage and statistics - Information about the Go runtime, memory usage and statistics
- Connected nodes and pending registrations - Connected nodes and pending registrations
- Active ACLs, filters and SSH policy - Active policy, filters and SSH policy
- Current DERPMap - Current DERPMap
- Prometheus metrics - Prometheus metrics

View File

@ -214,14 +214,14 @@ You may refer to users in the Headscale policy via:
{ {
"groups": { "groups": {
"group:alice": [ "group:alice": [
"https://soo.example.com/oauth2/openid/59ac9125-c31b-46c5-814e-06242908cf57@" "https://sso.example.com/oauth2/openid/59ac9125-c31b-46c5-814e-06242908cf57@"
] ]
}, },
"acls": [ "grants": [
{ {
"action": "accept",
"src": ["group:alice"], "src": ["group:alice"],
"dst": ["*:*"] "dst": ["*"],
"ip": ["*"]
} }
] ]
} }
@ -246,7 +246,7 @@ endpoint.
- Support for OpenID Connect aims to be generic and vendor independent. It offers only limited support for quirks of - Support for OpenID Connect aims to be generic and vendor independent. It offers only limited support for quirks of
specific identity providers. specific identity providers.
- OIDC groups cannot be used in ACLs. - OIDC groups cannot be used in policy rules.
- The username provided by the identity provider needs to adhere to this pattern: - The username provided by the identity provider needs to adhere to this pattern:
- The username must be at least two characters long. - The username must be at least two characters long.
- It must only contain letters, digits, hyphens, dots, underscores, and up to a single `@`. - It must only contain letters, digits, hyphens, dots, underscores, and up to a single `@`.

View File

@ -76,29 +76,29 @@ Please refer to the official [Tailscale
documentation](https://tailscale.com/docs/features/subnet-routers#use-your-subnet-routes-from-other-devices) for how to documentation](https://tailscale.com/docs/features/subnet-routers#use-your-subnet-routes-from-other-devices) for how to
use a subnet router on different operating systems. use a subnet router on different operating systems.
### Restrict the use of a subnet router with ACL ### Restrict the use of a subnet router with a policy
The routes announced by subnet routers are available to the nodes in a tailnet. By default, without an ACL enabled, all The routes announced by subnet routers are available to the nodes in a tailnet. By default, without a policy enabled,
nodes can accept and use such routes. Configure an ACL to explicitly manage who can use routes. all nodes can accept and use such routes. Configure a policy to explicitly manage who can use routes.
The ACL snippet below defines three hosts, a subnet router `router`, a regular node `node` and `service.example.net` as The policy snippet below defines three hosts, a subnet router `router`, a regular node `node` and `service.example.net`
internal service that can be reached via a route on the subnet router `router`. It allows the node `node` to access as internal service that can be reached via a route on the subnet router `router`. It allows the node `node` to access
`service.example.net` on port 80 and 443 which is reachable via the subnet router. Access to the subnet router itself is `service.example.net` on port 80 and 443 which is reachable via the subnet router. Access to the subnet router itself is
denied. denied.
```json title="Access the routes of a subnet router without the subnet router itself" ```json title="Access the routes of a subnet router without the subnet router itself"
{ {
"hosts": { "hosts": {
// the router is not referenced but announces 192.168.0.0/24" // the router is not referenced but announces 192.168.0.0/24
"router": "100.64.0.1/32", "router": "100.64.0.1/32",
"node": "100.64.0.2/32", "node": "100.64.0.2/32",
"service.example.net": "192.168.0.1/32" "service.example.net": "192.168.0.1/32"
}, },
"acls": [ "grants": [
{ {
"action": "accept",
"src": ["node"], "src": ["node"],
"dst": ["service.example.net:80,443"] "dst": ["service.example.net"],
"ip": ["80,443"]
} }
] ]
} }
@ -107,10 +107,10 @@ denied.
### Automatically approve routes of a subnet router ### Automatically approve routes of a subnet router
The initial setup of a subnet router usually requires manual approval of their announced routes on the control server The initial setup of a subnet router usually requires manual approval of their announced routes on the control server
before they can be used by a node in a tailnet. Headscale supports the `autoApprovers` section of an ACL to automate the before they can be used by a node in a tailnet. Headscale supports the `autoApprovers` section in a policy to automate
approval of routes served with a subnet router. the approval of routes served with a subnet router.
The ACL snippet below defines the tag `tag:router` owned by the user `alice`. This tag is used for `routes` in the The policy snippet below defines the tag `tag:router` owned by the user `alice`. This tag is used for `routes` in the
`autoApprovers` section. The IPv4 route `192.168.0.0/24` is automatically approved once announced by a subnet router `autoApprovers` section. The IPv4 route `192.168.0.0/24` is automatically approved once announced by a subnet router
that advertises the tag `tag:router`. that advertises the tag `tag:router`.
@ -124,7 +124,7 @@ that advertises the tag `tag:router`.
"192.168.0.0/24": ["tag:router"] "192.168.0.0/24": ["tag:router"]
} }
}, },
"acls": [ "grants": [
// more rules // more rules
] ]
} }
@ -204,19 +204,19 @@ $ sudo tailscale set --exit-node myexit
Please refer to the official [Tailscale documentation](https://tailscale.com/docs/features/exit-nodes#use-the-exit-node) Please refer to the official [Tailscale documentation](https://tailscale.com/docs/features/exit-nodes#use-the-exit-node)
for how to use an exit node on different operating systems. for how to use an exit node on different operating systems.
### Restrict the use of an exit node with ACL ### Restrict the use of an exit node with a policy
An exit node is offered to all nodes in a tailnet. By default, without an ACL enabled, all nodes in a tailnet can select An exit node is offered to all nodes in a tailnet. By default, without a policy enabled, all nodes in a tailnet can
and use an exit node. Configure `autogroup:internet` in an ACL rule to restrict who can use _any_ of the available exit select and use an exit node. Configure `autogroup:internet` in a policy rule to restrict who can use _any_ of the
nodes. available exit nodes.
```json title="Example use of autogroup:internet" ```json title="Example use of autogroup:internet"
{ {
"acls": [ "grants": [
{ {
"action": "accept",
"src": ["..."], "src": ["..."],
"dst": ["autogroup:internet:*"] "dst": ["autogroup:internet"],
"ip": ["*"]
} }
] ]
} }
@ -224,45 +224,41 @@ nodes.
### Restrict access to exit nodes per user or group ### Restrict access to exit nodes per user or group
A user can use _any_ of the available exit nodes with `autogroup:internet`. Alternatively, the ACL snippet below assigns A user can use _any_ of the available exit nodes with `autogroup:internet`. Alternatively, the policy snippet below
each user a specific exit node while hiding all other exit nodes. The user `alice` can only use exit node `exit1` while assigns each user a specific exit node while hiding all other exit nodes. The user `alice` can only use an exit node
user `bob` can only use exit node `exit2`. tagged with `tag:exit1` while user `bob` can only use an exit node tagged with `tag:exit2`.
```json title="Assign each user a dedicated exit node" ```json title="Assign each user a dedicated exit node"
{ {
"hosts": { "tagOwners": {
"exit1": "100.64.0.1/32", "tag:exit1": ["alice@"],
"exit2": "100.64.0.2/32" "tag:exit2": ["bob@"]
}, },
"acls": [ "grants": [
{ {
"action": "accept",
"src": ["alice@"], "src": ["alice@"],
"dst": ["exit1:*"] "dst": ["autogroup:internet"],
"via": ["tag:exit1"],
"ip": ["*"]
}, },
{ {
"action": "accept",
"src": ["bob@"], "src": ["bob@"],
"dst": ["exit2:*"] "dst": ["autogroup:internet"],
"via": ["tag:exit2"],
"ip": ["*"]
} }
] ]
} }
``` ```
!!! warning
- The above implementation is Headscale specific and will likely be removed once [support for
`via`](https://github.com/juanfont/headscale/issues/2409) is available.
- Beware that a user can also connect to any port of the exit node itself.
### Automatically approve an exit node with auto approvers ### Automatically approve an exit node with auto approvers
The initial setup of an exit node usually requires manual approval on the control server before it can be used by a node The initial setup of an exit node usually requires manual approval on the control server before it can be used by a node
in a tailnet. Headscale supports the `autoApprovers` section of an ACL to automate the approval of a new exit node as in a tailnet. Headscale supports the `autoApprovers` section in a policy to automate the approval of a new exit node as
soon as it joins the tailnet. soon as it joins the tailnet.
The ACL snippet below defines the tag `tag:exit` owned by the user `alice`. This tag is used for `exitNode` in the The policy snippet below defines the tag `tag:exit` owned by the user `alice`. This tag is used for the `exitNode` entry
`autoApprovers` section. A new exit node that advertises the tag `tag:exit` is automatically approved: in the `autoApprovers` section. A new exit node that advertises the tag `tag:exit` is automatically approved:
```json title="Exit nodes tagged with tag:exit are automatically approved" ```json title="Exit nodes tagged with tag:exit are automatically approved"
{ {
@ -272,7 +268,7 @@ The ACL snippet below defines the tag `tag:exit` owned by the user `alice`. This
"autoApprovers": { "autoApprovers": {
"exitNode": ["tag:exit"] "exitNode": ["tag:exit"]
}, },
"acls": [ "grants": [
// more rules // more rules
] ]
} }

View File

@ -40,7 +40,7 @@ The headscale documentation and the provided examples are written with a few ass
- Headscale is running as system service via a dedicated local user `headscale`. - Headscale is running as system service via a dedicated local user `headscale`.
- The [configuration](../ref/configuration.md) is loaded from `/etc/headscale/config.yaml`. - The [configuration](../ref/configuration.md) is loaded from `/etc/headscale/config.yaml`.
- SQLite is used as database. - SQLite is used as database.
- The data directory for headscale (used for private keys, ACLs, SQLite database, …) is located in `/var/lib/headscale`. - The data directory for headscale (used for private keys, policy, SQLite database, …) is located in `/var/lib/headscale`.
- URLs and values that need to be replaced by the user are either denoted as `<VALUE_TO_CHANGE>` or use placeholder - URLs and values that need to be replaced by the user are either denoted as `<VALUE_TO_CHANGE>` or use placeholder
values such as `headscale.example.com`. values such as `headscale.example.com`.