AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Tailnet Policy

skill-magnus919-agent-skills-tailnet-policy · by magnus919

Author, test, and deploy Tailscale-compatible huJSON policy files for

No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add skill-magnus919-agent-skills-tailnet-policy

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-magnus919-agent-skills-tailnet-policy)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Tailnet Policy? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

tailnet-policy

Overview

Headscale uses Tailscale-compatible policy files written in huJSON (Human JSON — standard JSON with trailing commas and // comments). Policy files control:

  • ACLs (deprecated legacy syntax) — {action, users, ports} rules
  • Grants (modern syntax) — {src, dst, ip, proto, via} rules
  • Tags — Node identity tags (tag:dev, tag:prod)
  • TagOwners — Which users/groups can apply which tags
  • AutoApprovers — Auto-approval for subnet routers and exit nodes
  • Tailscale SSH — SSH access rules via ssh.users and ssh.action

ACLs vs Grants

| Feature | ACLs (legacy) | Grants (modern) | |---------|--------------|-----------------| | Format | {action: "accept", users: [...], ports: [...]} | {src: [...], dst: [...], ip: [...], proto: "tcp"} | | Port filtering | Embedded in ports: ["*:*"] | Separate ip field for ports | | Protocol filtering | Not supported | proto field (tcp, udp, icmp) | | Destination routing | Not supported | via field for relay/exit nodes | | Status | Deprecated by Tailscale | Current recommended syntax |

Use Grants wherever possible. The migrate-acls-to-grants.py script can convert legacy ACL files automatically.

Policy Location

The policy file path is configured in Headscale's config.yaml:

policy:
  path: /etc/headscale/policy.hujson

After modifying the policy file, reload it on the Headscale server:

# Reload via SIGHUP
kill -HUP $(pgrep headscale)

# Or use the convenience script
./skills/tailnet-policy/reload-headscale-policy.sh

Writing Policy

Allow-All (default-open)

{
  // Grants that allow all traffic
  "grants": [
    {
      "src": ["autogroup:member"],
      "dst": ["autogroup:member"],
      "ip": ["*:*"]
    }
  ],
  // Tag ownership
  "tagOwners": {
    "tag:dev": ["autogroup:admin"],
    "tag:prod": ["autogroup:admin"]
  }
}

Deny-All (default-closed)

{
  "grants": [
    // Only allow ICMP (ping) between all members
    {
      "src": ["autogroup:member"],
      "dst": ["autogroup:member"],
      "ip": ["*"],
      "proto": "icmp"
    }
  ],
  // Specific grants added per-service
  "tagOwners": {
    "tag:monitor": ["autogroup:admin"]
  }
}

Segmented (environments)

{
  "grants": [
    // Dev can reach dev
    {
      "src": ["tag:dev"],
      "dst": ["tag:dev"],
      "ip": ["*:*"]
    },
    // Prod can reach prod
    {
      "src": ["tag:prod"],
      "dst": ["tag:prod"],
      "ip": ["*:*"]
    },
    // Admin access to all
    {
      "src": ["autogroup:admin"],
      "dst": ["tag:dev", "tag:prod"],
      "ip": ["*:*"]
    }
  ],
  "tagOwners": {
    "tag:dev": ["autogroup:admin"],
    "tag:prod": ["autogroup:admin"]
  }
}

Tag-Based Patterns

Tags are node-level identifiers set via tailscale up --advertise-tags=tag:dev. They decouple policy from user identity.

{
  "tagOwners": {
    "tag:ci-runner":  ["autogroup:admin"],
    "tag:database":   ["autogroup:admin"],
    "tag:webserver":  ["autogroup:admin"],
    "tag:monitoring": ["autogroup:admin"]
  },
  "grants": [
    {
      "src": ["tag:monitoring"],
      "dst": ["tag:webserver", "tag:database"],
      "ip": ["*:*"]
    },
    {
      "src": ["tag:webserver"],
      "dst": ["tag:database"],
      "ip": ["tcp:5432"]
    }
  ]
}

Grants Syntax

Grants are the modern policy primitive:

{
  "grants": [
    {
      "src": ["tag:source", "user@example.com"],
      "dst": ["tag:destination", "100.64.0.1"],
      "ip": ["*:*"],                    // proto:port — "*:*" means all
      "proto": "tcp",                   // optional protocol filter
      "via": ["tag:exit-node"]          // optional via/routing
    }
  ]
}

Fields:

  • src — Source entities (tags, users, autogroups, IPs)
  • dst — Destination entities
  • ip — Protocol and port filter (e.g. tcp:80, udp:53, *:*, *)
  • proto — Protocol constraint (tcp, udp, icmp)
  • via — Route through a specific exit node or relay

Auto Approvers

Auto-approvers let specific users approve subnet routes and exit nodes without manual intervention:

{
  "autoApprovers": {
    "routes": {
      "10.0.0.0/8": ["autogroup:admin"],
      "172.16.0.0/12": ["alice@example.com"]
    },
    "exitNode": ["autogroup:admin"]
  }
}
  • routes: Maps CIDR ranges to lists of users who can auto-approve those routes
  • exitNode: Lists users who can advertise exit nodes

Autogroups

Autogroups are dynamic groups resolved by Headscale/Tailscale at runtime:

| Autogroup | Description | |-----------|-------------| | autogroup:member | All tailnet members | | autogroup:admin | Tailnet admins | | autogroup:tagged | All tagged nodes (any node with at least one tag) | | autogroup:internet | The public internet (used for exit node routing) |

Tailscale SSH Configuration

Tailscale SSH rules are configured via the ssh section:

{
  "ssh": [
    {
      "action": "accept",         // "accept" or "check"
      "src": ["autogroup:admin"],
      "dst": ["tag:webserver"],
      "users": ["root", "ubuntu"]
    },
    {
      "action": "check",          // "check" requires node-level SSH authorization
      "src": ["autogroup:member"],
      "dst": ["tag:dev"],
      "users": ["*"]
    }
  ]
}
  • action: "accept" (allow directly) or "check" (require node-level auth)
  • src: Source users/groups
  • dst: Destination tags/users
  • users: Which OS users can be SSH'd into

Testing Policies

Policy files include test definitions that are validated when loaded:

{
  "grants": [...],
  "tests": [
    {
      "src": "alice@example.com",
      "dst": "tag:webserver",
      "ip": ["tcp:443"],
      "action": "accept"  // expected result
    },
    {
      "src": "bob@example.com",
      "dst": "tag:database",
      "ip": ["tcp:22"],
      "action": "drop"    // expected result
    }
  ]
}

Validate tests with:

./skills/tailnet-policy/validate-policy.py --policy policy.hujson

Gotchas

  • Headscale does NOT support device posture — rules like devicePosture or device:managed are Tailscale-only
  • Headscale does NOT support IP setsipSets and ipprotocol are not supported
  • Headscale does NOT support OIDC groups in ACLs — groups from OIDC claims cannot be used in policy; use autogroup:admin and autogroup:member instead
  • Tag names must start with tag: and contain only lowercase letters, numbers, and hyphens
  • Policy reload via SIGHUP does not return errors on failure — always validate before reloading
  • ACL users field and Grant src field are NOT interchangeable — grants use src/dst, legacy ACLs use users/ports
  • Tests are not enforced at runtime — they only validate during parsing; a passing test does not guarantee runtime behavior

Trigger Conditions

This skill is automatically loaded when the user's message contains any of these keywords:

  • tailnet policy
  • headscale acl
  • hujson policy
  • tailscale grant
  • tagowners
  • autoapprovers
  • tailscale ssh policy
  • policy validation
  • migrate acls
  • /etc/headscale/policy
  • policy.hujson

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.