docs/ref/policy.md
Headscale implements a large portion of Tailscale's policy features, most notably access control based on ACLs and Grants or Tailscale SSH. See limitations to learn about missing features and notable implementation differences between Headscale and Tailscale.
Headscale uses the same huJSON based file format as Tailscale. By default, no
policy is loaded which means that Headscale allows all traffic between nodes. To start using a policy file1, specify
its path in the policy.path key in the configuration file.
Headscale needs to be reloaded to pick up changes to the policy file. Either reload Headscale via its systemd service
(sudo systemctl reload headscale) or by sending a SIGHUP signal (sudo kill -HUP $(pidof headscale)) to the main
process. Headscale logs the result of policy processing after each reload.
Please have a look at Tailscale's policy related documentation to learn more:
Headscale supports both ACLs and Grants to write an access control policy. We recommend the use of Grants since ACLs are considered legacy and will not receive new features by Tailscale.
If you define a policy file but completely omit the "acls" or "grants" section, Headscale will default to an allow
all policy. This means all devices connected
to your tailnet will be able to communicate freely with each other.
{}
To prevent all communication within your tailnet, you can
include an empty array for the "grants" section in your policy file.
{
"grants": []
}
postures or
srcPosture aren't supported.Headscale supports several Autogroups that automatically include users, destinations, or devices with specific properties. Autogroups provide a convenient way to write policy rules without manually listing individual users or devices.
autogroup:internetAllows access to the internet through exit nodes. Can only be used in policy destinations.
{
"grants": [
{
"src": ["alice@"],
"dst": ["autogroup:internet"],
"ip": ["*"]
}
]
}
autogroup:memberIncludes all personal (untagged) devices.
{
"grants": [
{
"src": ["autogroup:member"],
"dst": ["tag:prod-app-servers"],
"ip": ["80,443"]
}
]
}
autogroup:taggedIncludes all devices that have at least one tag.
{
"grants": [
{
"src": ["autogroup:tagged"],
"dst": ["tag:monitoring"],
"ip": ["9090"]
}
]
}
autogroup:selfIncludes devices where the same user is authenticated on both the source and destination. Does not include tagged devices. Can only be used in policy destinations.
{
"grants": [
{
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"ip": ["*"]
}
]
}
!!! warning "The current implementation of autogroup:self is inefficient"
Using `autogroup:self` may cause performance degradation on the Headscale coordinator server in large deployments,
as filter rules must be compiled per-node rather than globally and the current implementation is not very efficient.
If you experience performance issues, consider using more specific policy rules or limiting the use of
`autogroup:self`.
```json title="policy.json"
{
"grants": [
// The following rules allow internal users to communicate with their
// own nodes in case autogroup:self is causing performance issues.
{
"src": ["boss@"],
"dst": ["boss@"],
"ip": "*"
},
{
"src": ["dev1@"],
"dst": ["dev1@"],
"ip": "*"
},
{
"src": ["intern1@"],
"dst": ["intern1@"],
"ip": "*"
}
]
}
```
autogroup:nonrootUsed in Tailscale SSH rules to allow access to any user except root. Can only be used in the users field of SSH rules.
{
"ssh": [
{
"action": "accept",
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot"]
}
]
}
autogroup:danger-allThis autogroup resolves to all IP addresses (0.0.0.0/0 and ::/0) which also includes all IP addresses outside the
standard Tailscale IP ranges. This autogroup can only be used as source.
Node attributes allow for device-specific configuration and attributes. At least the following node attributes are currently supported by Headscale2:
drive:access, drive:share: Taildrive support.nextdns:<profile>, nextdns:no-device-info: NextDNS integration.
Be sure to set NextDNS as global resolver in the configuration.magicdns-aaaa: Respond to AAAA queries on the local MagicDNS
resolver at 100.100.100.100.disable-ipv4: Selectively disable IPv4 for specfic nodes. This is may be useful to workaround CGNat
conflicts.randomize-client-port: Allocate a random port for WireGuard
traffic instead of the static default
port 41641.disable-captive-portal-detection: Disable automatic captive portal
detection.{
"nodeAttrs": [
{
// Enable MagicDNS AAAA records for all nodes
"target": ["*"]
"attr": ["magicdns-aaaa"]
}
]
}
The following options are applied for the entire tailnet. Consider node attributes for a more fine-grained configuration instead.
randomizeClientPort: Allocate a random port for WireGuard
traffic instead of the static default
port 41641.{
// Use a random WireGuard port for the entire tailnet
"randomizeClientPort": true
}
Headscale also allows to store the policy in the database. This is typically only required in case a web interface is used. ↩
Other key-only node attributes can be used as well. Find them in the client source code with grep -E '^\s+NodeAttr\w+' tailcfg/tailcfg.go or by using GitHub code search (requires
login). ↩