Files
magnus919_agent-skills/tailscale/references/policy-syntax-reference.md
T
Magnus Hedemarkandfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> c0c7690724 feat(flatten): move bundle dirs to repo root
Move the 8 directories under bundles/ to the repo root via git mv and
remove the now-empty bundles/ directory. Replace the "bundles" entry in
pyproject.toml [tool.deptry] extend_exclude with the 8 moved dir names so
the moved trees stay excluded from Python dependency analysis.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-08-14 15:26:27 -04:00

172 lines
3.5 KiB
Markdown

# Tailscale Policy Syntax Reference (huJSON)
Headscale uses the same policy file format as Tailscale: huJSON (human JSON — allows
comments and trailing commas). The policy file is loaded from `policy.path` in
config.yaml and reloaded via SIGHUP.
## Structure
The policy file is a single JSON object with these sections:
```json
{
"acls": [...], // Legacy ACL rules
"grants": [...], // Modern access rules (preferred)
"tagOwners": {...}, // Who can create tagged nodes
"autoApprovers": {...}, // Auto-approve routes and exit nodes
"ssh": [...], // Tailscale SSH rules
"nodeAttrs": [...], // Node attributes
"groups": {...}, // Named groups
"hosts": {...}, // Hostname aliases
"tests": [...], // Policy test definitions
"sshTests": [...] // SSH policy tests
}
```
## Grants (Modern — Preferred)
Grants replace ACLs for new deployments:
```json
{
"grants": [
{
"src": ["alice@"],
"dst": ["tag:server"],
"ip": ["*"]
}
]
}
```
- `src`: Source users, tags, or autogroups
- `dst`: Destination users, tags, or autogroups
- `ip`: Port/protocol rules: `["*"]` (all), `["80"]`, `["80,443"]`, `["tcp:22"]`
- `app`: App connector rules
- `via`: Route filtering for cross-subnet access
## ACLs (Legacy)
```json
{
"acls": [
{"action": "accept", "src": ["alice@"], "dst": ["tag:server:*"]},
{"action": "accept", "src": ["tag:server"], "dst": ["tag:db:*"]}
]
}
```
- `action`: "accept" (allow) or no default deny
- `src`: Source users/tags
- `dst`: `<tag>:<port>` — destination and port
## TagOwners
```json
{
"tagOwners": {
"tag:server": ["alice@"],
"tag:db": ["alice@", "bob@"],
"tag:ci": ["autogroup:admin"]
}
}
```
Only users listed as tagOwners can register nodes with those tags.
## Auto Approvers
Automatically approve subnet routes and exit nodes:
```json
{
"autoApprovers": {
"routes": {
"192.168.0.0/16": ["alice@"],
"10.0.0.0/8": ["bob@"]
},
"exitNode": ["tag:server"]
}
}
```
## Autogroups
Built-in dynamic groups:
- `autogroup:internet` — Access to internet via exit nodes (dst only)
- `autogroup:member` — All personal (user-owned, untagged) devices
- `autogroup:tagged` — All tagged (service) devices
- `autogroup:admin` — Admin users
## Tailscale SSH
```json
{
"ssh": [
{
"action": "accept",
"src": ["alice@"],
"dst": ["tag:server"],
"users": ["ubuntu", "root"]
},
{
"action": "check",
"src": ["autogroup:member"],
"dst": ["autogroup:member"],
"users": ["*"]
}
]
}
```
- `action`: "accept" (no check) or "check" (requires key verification)
- `users`: Remote usernames allowed
## Node Attributes
```json
{
"nodeAttrs": [
{
"target": ["tag:gateway"],
"attr": ["allow-exit-node", "allow-subnet-routing"]
}
]
}
```
## Groups
```json
{
"groups": {
"group:engineering": ["alice@", "bob@", "carol@"],
"group:infra": ["alice@", "dave@"]
}
}
```
## Tests
```json
{
"tests": [
{
"src": "alice@",
"accept": ["tag:server:80", "tag:server:443"],
"deny": ["tag:db:22"]
}
]
}
```
Also `sshTests` for SSH rules.
## Gotchas for Headscale
- **Device posture** is NOT supported
- **IP sets** are NOT supported
- **OIDC groups** cannot be used in ACLs (though OIDC auth works for login)
- **Funnel/Serve** not supported
- **Policy reload**: SIGHUP, not automatic
- **Autogroup:admin**: Not available if no OIDC admin group configured
- Default (no policy file) = allow all traffic between nodes