ACL syntax

Tailscale access control rules are expressed as a single “human JSON” (HuJSON) tailnet policy file. HuJSON is a superset of JSON that allows comments, making the tailnet policy file easy to maintain while staying human readable.

The tailnet policy file has several top-level sections relating to ACLs, which we explore in detail below:

  • acls, the access rules themselves.
  • groups, collections of users, to avoid repeating yourself in access rules.
  • hosts, human readable shorthands for IP addresses.
  • postures, assertions based on device posture attributes.
  • tagOwners, to define which users can assign tags to a device.
  • autoApprovers, to define which users can advertise routes or exit nodes without further approval.
  • ssh, to define which users can establish a Tailscale SSH connection on which devices, and as which SSH users.
  • nodeAttrs, to define which devices can use certain features.
  • tests, which let you check the behavior of ACLs and avoid accidentally breaking an important permission.

The tailnet policy file also contains network-wide policy settings unrelated to access control: derpMap, disableIPv4, and randomizeClientPort.

Access rules (acls)

The acls section of the tailnet policy file is a list of access rules for your network. Each rule is a HuJSON object that grants access from a set of sources to a set of destinations.

Access rules can make use of groups to grant access to many users at once, and make use of tags to assign role accounts to nodes. Putting these together, you can build powerful role-based access control (RBAC) policies.

All rules eventually boil down to allowing traffic from a particular source IP address to a destination IP address and port. While you can write rules that reference IP addresses directly, it’s more common to use higher-level specifiers like users, groups, and tags, which Tailscale automatically translates to the right low-level rules.

Each access rule looks like this:

{
  "action": "accept",
  "src": [ list-of-sources... ],
  "proto": "protocol", // optional
  "dst": [ list-of-destinations... ],
}
Previously we used users and ports, instead of src and dst, respectively. While both sets of terms are currently supported, we are using src and dst going forward.
action

Tailscale access rules are default deny, so the only possible action is accept, to allow traffic that would otherwise be denied.

src

The src field specifies a list of sources to which the rule applies. Each element in the list can be one of the following:

Type Example Meaning
Any * No restriction on the source
User shreya@example.com All devices currently signed in as the given user
Group group:example Same as listing every user in the group explicitly
Tailscale IP 100.101.102.103 Only the device that owns the given Tailscale IP. IPv6 addresses must follow the format [1:2:3::4]:80.
Subnet CIDR Range 192.168.1.0/24 Any IP within the given subnet
Host my-host Looks up the Tailscale IP or CIDR in the hosts section
Tag tag:production All devices currently tagged with the given tag
Autogroup autogroup:member Devices of users, destinations, or usernames with the same properties or roles

An optional srcPostures field can be used to further restrict src devices to the ones matching a set of device posture conditions.

proto

The proto field is optional, and specifies the protocol to which the rule applies. (If no protocol is specified, then the access rule applies to all TCP and UDP traffic.)

A proto field can only be included in an access rule for Tailscale version v1.18.2 or later. Older clients will fail closed, blocking traffic for access rules with protocols, unless it is otherwise allowed.

If traffic is allowed for a given pair of IPs, then ICMP will also be allowed.

proto can be specified as either an IANA IP protocol number 1-255 (for example, "16") or one of the following named aliases (for example, "sctp"):

Protocol proto IANA protocol number
Internet Group Management (IGMP) igmp 2
IPv4 encapsulation ipv4, ip-in-ip 4
Transmission Control (TCP) tcp 6
Exterior Gateway Protocol (EGP) egp 8
Any private interior gateway igp 9
User Datagram (UDP) udp 17
Generic Routing Encapsulation (GRE) gre 47
Encap Security Payload (ESP) esp 50
Authentication Header (AH) ah 51
Stream Control Transmission Protocol (SCTP) sctp 132

Only TCP, UDP, and SCTP traffic support specifying ports. For all other protocols, the port specified needs to be *.

dst

The dst field specifies a list of destination devices and ports to which the rule applies. Each element in the list is of the form host:ports. host is one of the following:

Type Example Meaning
Any * No restriction on the destination
User shreya@example.com Any device currently signed in as the given user
Group group:example Same as listing every user in the group explicitly
Tailscale IP 100.101.102.103 Only the device that owns the given Tailscale IP
Hosts my-host Looks up the Tailscale IP in the hosts section
Subnet CIDR Range 192.168.1.0/24 Any IP within the given subnet
Tags tag:production Any device currently tagged with the given tag
Internet access via an exit node autogroup:internet Access to the internet through exit nodes
Own devices autogroup:self Access to devices where the same user is authenticated on both the src and the dst. This does not include devices the user has ACL tags.
Tailnet devices autogroup:member Access to devices on the tailnet where the user is a direct member (not a shared user) of the tailnet
Admin devices autogroup:admin Access to devices where the user is an Admin
Network admin devices autogroup:network-admin Access to devices where the user is a Network admin
IT admin devices autogroup:it-admin Access to devices where the user is an IT admin
Billing admin devices autogroup:billing-admin Access to devices where the user is a Billing admin
Auditor devices autogroup:auditor Access to devices where the user is an Auditor
Owner devices autogroup:owner Access to devices where the user is the tailnet Owner

ports is one of the following:

Type Example
Any *
Single 22
Multiple 80,443
Range 1000-2000

Subnet routers and exit nodes

ACLs can only allow connections, not reject them. ACLs don’t limit discovery of routes. So, you can restrict access to a node separately from access to a subnet that node routes to, if it’s a subnet router; or public IP, if it’s an exit node.

To restrict access to a subnet, ensure that no ACL allows access to those routes. You can enforce this with a test, which will fail if any rule accidentally allows access, like so:

"tests": [
   {
     "src": "not-allowed@example.com",
     "accept": ["100.101.102.103:22"], // allow access to the tailscale IP
     "deny": ["192.168.0.7:22"], // does not allow access to the subnet
   }
],

To restrict access to all exit nodes, only grant access to autogroup:internet to those who you wish to use exit nodes. You can enforce this with a test, which will fail if any rule accidentally allows access to a public IP, like so:

"tests": [
   {
     "src": "not-allowed@example.com",
     "accept": ["100.101.102.103:22"], // allow access to the tailscale IP
     "deny": ["8.8.8.8:22"], // does not allow access to a public IP
   }
],

Right now, there is no way to restrict use of specific exit nodes using ACLs. Subscribe to this GitHub issue for updates.

Taildrop precedence

Taildrop permits you to share files between devices that you are logged in to, even if ACLs are used to restrict access to the devices.

Referencing users

Users can be specified in both the source (src) and destination (dst) fields of an access rule. Users must be referenced using the full email address they use to sign into Tailscale.

If you sign into your network with GitHub, use the form username@github. For example, the GitHub user alice should be written as alice@github.

If you’ve invited a user to your Tailscale network or shared a device with someone outside your Tailscale network, you can use their Tailscale login email in ACLs to further restrict what they can access on the shared device.

Referencing multiple users

Access rules for sets of users can be defined using groups. This allows you to define role-based access controls, so that users in different roles have different access rules.

Groups in ACLs can be:

  • Autogroups, to reference all entities with the same property
  • Defined in the groups section of the tailnet policy file, as a specific list of users
  • Provisioned in the identity provider and synced through user & group provisioning

Autogroups

Autogroups are available for all plans.

An autogroup is a special group that automatically includes users, destinations, or usernames with the same properties, which you can then use in access rules.

Where allowed Autogroup Meaning Availability by plan
As a dst autogroup:internet Used to allow access for any user through any exit node in your tailnet. You cannot restrict access to only some exit nodes. Available on all plans
autogroup:self Used to allow access for any user that is authenticated as the same user as the source. Does not apply to tags.
As a src or dst, tagOwner, or autoApprover autogroup:owner Used to allow access for the tailnet Owner. Available on all plans
autogroup:admin Used to allow access for any user who has the role of Admin.
autogroup:member Used to allow access for any user who is a direct member (including all invited users) of the tailnet. Users from shared nodes are not included.
autogroup:tagged Used to allow access for any node which is tagged.
autogroup:auditor Used to allow access for any user who has the role of Auditor. Available on the Personal, Premium, and Enterprise plans
autogroup:billing-admin Used to allow access for any user who has the role of Billing admin.
autogroup:it-admin Used to allow access for any user who has the role of IT admin.
autogroup:network-admin Used to allow access for any user who has the role of Network admin.
As a src autogroup:shared Used to allow access for any user who accepted a sharing invitation to your network. This lets you write rules without knowing sharee email addresses in advance. Available on all plans
As an SSH user autogroup:nonroot Used to allow Tailscale SSH access to any user that is not root. Available on the Personal, Premium, and Enterprise plans
autogroup:self only applies to user-owned nodes, not also tagged nodes. You cannot use autogroup:self with autogroup:tagged.
Previously we used autogroup:members instead of autogroup:member. While both are currently supported, we are using autogroup:member going forward. You cannot use both autogroup:member and autogroup:members in the same tailnet policy file.

Here’s an example ssh rule that allows all users to have Tailscale SSH access to their own devices, as non-root:

"ssh": [
  {
    // All users can SSH to their own devices, as non-root
    "action": "accept",
    "src": ["autogroup:member"],
    "dst": ["autogroup:self"],
    "users": ["autogroup:nonroot"]
  },
]

Important note about autogroup:nonroot

In the default ACL, the ssh rule uses autogroup:self for the dst field and autogroup:nonroot in the users field. If you change the dst field from autogroup:self to some other destination, such as an ACL tag, also consider replacing autogroup:nonroot in the users field. If you don't remove autogroup:nonroot from the users field, then anyone permitted by the src setting will be able to SSH in as any nonroot user on the dst device.

Groups (groups)

The groups section lets you define a shorthand for a group of users, which you can then use in access rules instead of listing users out explicitly. Any change you make to the membership of a group is automatically propagated to all the rules that reference that group.

A groups definition looks like this:

"groups": {
  "group:engineering": [
    "dave@example.com",
    "laura@example.com",
  ],
  "group:sales": [
    "brad@example.com",
    "alice@example.com",
  ],
},

Every group name must start with the prefix group:. Each member of a group is specified by their full email address, as explained in the users section above. To avoid the risk of obfuscating group membership, groups cannot contain other groups.

You can add or remove a user’s group membership by editing the tailnet policy file, as shown in the example groups definition above, and directly from the Users page of the admin console.

Editing a user’s group membership from the Users page

You need to be an Owner, Admin, or Network admin to edit a user’s group membership from the Users page, same as for editing ACLs in general.

  1. Open the Users page in the admin console.
  2. Find the user by name.
  3. Click the ellipsis icon menu and then click Edit group membership.
  4. In the Edit group membership dialog:
    1. To add a group, click Add to a group and select the group to add.
    2. To remove a group, click the X next to the group to delete.
  5. When you are done editing the groups for the user, click Save.

Provisioned groups

You can create groups in your identity provider and sync them for use in Tailscale’s ACLs with user & group provisioning.

You can use the same human-readable group names you have in your identity provider to refer to groups in your tailnet policy file, for example, an access rule that manages access for the “security-team” group would look like:

{
  "acls": [
    {
      "action": "accept",
      "src": ["group:security-team@example.com"],
      "dst": ["tag:logging:*"]
    },
  ],
  "tagOwners": {
    "tag:logging": ["group:security-team@example.com"]
  },
}

You can edit only groups that are defined in ACLs. That is, while you can use groups that are synced from a System for Cross-domain Identity Management (SCIM) integration or tailnet autogroups, you cannot edit them.

Referencing multiple devices

Access rules for sets of devices can be defined in multiple ways. Tags allow you to define role-based access controls, so that different services have different access rules. Hosts allow you to define controls based on a reference to an IP address.

Devices can be referenced in ACLs using:

  • Tags, to reference non-user devices which need to be accessed together. For example, all servers in a particular data center, or all applications belonging to the DevOps team.
  • Hosts, to reference IP ranges both on and beyond the tailnet.

Tags should be used to reference applications or servers. Hosts should be used to address applications with fixed IP addresses, and which you may not be able to modify.

Tags

ACL tags are available for all plans.

Tags are a substitute for a human identity on a device. That is, rather than a device being authenticated as a particular user, the device’s identity on the Tailscale network is the set of tags assigned to it. This is the combination of all of its tags (not the intersection).

The tag must be defined in the tagOwners section of the tailnet policy file before it can be used. To tag a device, authenticate as the tag on the device.

Hosts (hosts)

Hosts are available for all plans.

The hosts section lets you define a human-friendly name for an IP address or CIDR range, to make access rules more readable.

A hosts definition looks like this:

"hosts": {
  "example-host-1": "100.100.100.100",
  "example-network-1": "100.100.101.100/24",
},

The human-friendly name cannot include the character @.

Postures (postures)

Postures are available for all plans.

The postures section of the tailnet policy file defines a set of assertions that a device must meet as part of a specific access rule, as part of device posture management.

A postures definition looks like this:

"postures": {
    "posture:latestMac": [
        "node:os IN ['macos', 'linux']",
        "node:tsReleaseTrack == 'stable'",
        "node:tsVersion >= '1.40'",
    ],
},

Each posture must start with the prefix posture: followed by a name, and lists a set of posture attributes and their allowed values, given as a list of strings.

See device posture management for more information.

Tag Owners (tagOwners)

ACL tags are available for all plans.

The tagOwners section of the tailnet policy file defines the tags that can be applied to devices, and the list of users who are allowed to assign each tag.

A tagOwners definition looks like this:

"tagOwners": {
  "tag:webserver": [
    "group:engineering",
  ],
  "tag:secure-server": [
    "group:security-admins",
    "president@example.com",
  ],
  "tag:corp": [
    "autogroup:member",
  ],
}

Every tag name must start with the prefix tag:. A tag owner can be a user’s full login email address (as defined in the users section above), a group name, an autogroup, or another tag.

A shorthand notation, [], is available for autogroup:admin. That is, the following are equivalent:

"tag:monitoring": [
  "autogroup:admin",
],
"tag:monitoring": [],

autogroup:admin and autogroup:network-admin can assign any tags, so [] implicitly means that only autogroup:admin and autogroup:network-admin are allowed.

Auto Approvers (autoApprovers)

Auto Approvers are available for all plans.
Auto Approvers are currently in beta.

The autoApprovers section of the tailnet policy file defines the list of users who can perform certain actions without requiring further approval from the admin console. This is because some actions in Tailscale require double opt-in: they need to both enabled in the Tailscale admin console by a tailnet admin, and enabled on the device running Tailscale. This includes advertising a specified set of routes as a subnet router or advertising an exit node. For routes, this also permits the auto approvers to advertise a subnet of the specified routes.

An Owner, Admin, or Network admin can still disable the route or exit node from the admin console. To avoid the same route being advertised and auto-approved again, consider modifying autoApprovers.

If the device is re-authenticated by a different user who cannot advertise the route or exit node, or the user who advertised it is suspended or deleted, the route or exit node is no longer advertised. To avoid this, consider making an ACL tag an auto approver.

An autoApprovers definition looks like this:

"autoApprovers": {
  "routes": {
    "10.0.0.0/24": ["group:engineering", "alice@example.com", "tag:foo"],
  },
  "exitNode": ["tag:bar"],
}

The auto approver of a route or exit node can be a user’s full login email address (as defined in the users section above), a group name, an autogroup or a tag.

Tailscale SSH (ssh)

Tailscale SSH is available for the Personal, Premium, and Enterprise plans.
Tailscale SSH is currently in beta.

The ssh section of the tailnet policy file defines the lists of users and devices that can use Tailscale SSH, and as which SSH users. For a connection to be permitted, the tailnet policy file must contain rules permitting both network access and SSH access:

  1. An access rule to allow connections from the source to the destination on port 22.
  2. An SSH access rule to allow connections from the source to the destination and the given SSH users. This is used for Tailscale SSH, to distribute keys for authenticating SSH connections.

An ssh definition looks like this:

{
  "action": "check", // "accept" or "check"
  "src": [ list-of-sources... ],
  "dst": [ list-of-destinations... ],
  "users": [ list-of-ssh-users... ],
  "checkPeriod": "20h", // optional, only for check actions. default 12h
},
action

The action field specifies whether to accept the connection or to perform additional checks on it.

src

The src field is the source where a connection originates from. This can be a user, group, autogroup:member, or autogroup:tagged.

dst

The dst field is the destination where the connection goes. This can be a user, tag, or autogroup. Note that unlike ACLs, a port cannot be specified. Only port 22 is allowed, and does not need to be specified as it is used by default.

users

The users field is the set of allowed usernames on the host. An SSH rule can also specify autogroup:nonroot to allow any user that is not root. If no user is specified, Tailscale will use the local host’s user. That is, if you are logged in as alice locally, then SSH to another device, Tailscale SSH will try to log in as user alice. Like other SSH clients, Tailscale will only use user accounts that already exist on the host, not create new accounts.

Optionally, for check mode only, the checkPeriod field is the time period for which to allow a connection before requiring a check. This can be specified in minutes or hours, with a minimum of one minute and a maximum of 168 hours (one week). If not specified, this is 12 hours. You may also specify "always" to require check mode on every connection. Choosing to always require check mode may cause unexpected behavior from automation tools that open many SSH connections in a short time span, like Ansible.

SSH access rules are evaluated considering the most restrictive policies first:

  • Check policies
  • Accept policies

For example, if you have an access rule allowing the user alice@example.com to access a resource with an accept rule, and a rule allowing group:devops which alice@example.com belongs to, to access a resource with a check rule, then the check rule applies.

New tailnets or existing tailnets that have not modified their ACLs have a default SSH policy allowing users to access their own devices using check mode.

The only kinds of connections that are allowed are:

  • From a user to their own devices, as any user including root.
  • From a user to a tagged device, as any user including root.
  • From a tagged device to another tagged device, for any tags. Note that an SSH access rule from a tagged device cannot be in check mode.
  • From a user to a device that has been shared with them, as long as the destination host has Tailscale configured with SSH and the destination’s ACL allows the user to connect over SSH.

That is, the broadest policy allowed would be:

{
  "acls": [
    {
      "action": "accept",
      "src": ["*"],
      "dst": ["*:*"]
    }
  ],
  "ssh": [
    {
      "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["autogroup:self"],
      "users": ["root", "autogroup:nonroot"]
    },
    {
      "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["tag:prod"],
      "users": ["root", "autogroup:nonroot"]
    },
    {
      "action": "accept",
      "src": ["tag:logging"],
      "dst": ["tag:prod"],
      "users": ["root", "autogroup:nonroot"]
    },
  ]
}

To allow a user to only SSH to their own devices, as non-root:

{
  "acls": [
    {
      "action": "accept",
      "src": ["*"],
      "dst": ["*:*"]
    }
  ],
  "ssh": [
    {
      "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["autogroup:self"],
      "users": ["autogroup:nonroot"]
    }
  ]
}

To allow group:sre to access devices in the production environment tagged tag:prod:


{
  "groups": {
    "group:sre": ["alice@example.com", "bob@example.com"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:sre"],
      "dst": ["tag:prod:*"]
    },
  ],
  "ssh": [
    {
      "action": "accept",
      "src": ["group:sre"],
      "dst": ["tag:prod"],
      "users": ["ubuntu", "root"],
    },
  ]
  "tagOwners": {
    // users in group:sre can apply the tag tag:prod
    "tag:prod": ["group:sre"]
  }
}

Node attributes (nodeAttrs)

Node attributes are currently in beta.

The nodeAttrs section of the tailnet policy file defines additional attributes that apply to certain devices in your tailnet. You can use this to set different NextDNS configurations for different devices in your tailnet.

A nodeAttrs definition looks like this:

"nodeAttrs": [
    {
        "target": ["my-kid@my-home.com", "tag:server"],
        "attr": [
            "nextdns:abc123",
            "nextdns:no-device-info",
        ],
    },
],
target

The target field specifies which nodes the attributes apply to. This can be a tag (tag:server), user (alice@example.com), group (group:kids), or *.

attr

The attr field specifies which attributes apply to those nodes. For this example, the attributes are:

  • nextdns:abc123, for a NextDNS configuration abc123. If this is used, the attribute overrides the global NextDNS configuration.
  • nextdns:no-device-info, to disable sending device metadata to NextDNS.

This example allows members of the tailnet to use Tailscale Funnel on their nodes:

"nodeAttrs": [
    {
         "target": ["autogroup:members"],
         "attr":   ["funnel"],
     },
],

Tests (tests)

ACL tests are available for all plans.

The tests section lets you write assertions about your access rules, which are checked whenever the tailnet policy file is changed. Failing assertions cause the new tailnet policy file to be rejected, with an error detailing which test assertion wasn’t met.

Just like writing tests for software, ACL tests let you ensure that an important permission isn’t accidentally revoked later on, or that a critical system in your network isn’t exposed more than expected.

A tests definition looks like this:

"tests": [
  {
    "src": "dave@example.com",
    "srcPostureAttrs": {
      "node:os": "windows",
    },
    "proto": "tcp",
    "accept": ["example-host-1:22", "vega:80"],
    "deny": ["1.2.3.4:443"],
  },
],
src

The src field specifies the user identity being tested, which can be a user’s email address, a group, an ACL tag, or a host that maps to an IP address. The test case will be run from the perspective of a device signed in as that user, with that group membership, with that tag, or as that host, respectively.

srcPostureAttrs

The srcPostureAttrs field specifies Device Posture attributes as key-value pairs to be used when evaluating posture conditions in your access rules. This field is only needed if your access rules contain device posture conditions.

proto

The optional proto field specifies the IP protocol for accept and deny rules, similar to the proto field in ACL rules. When omitted, the test checks for either TCP or UDP access.

accept and deny destinations

The accept and deny fields specify destinations that the access rules should accept or deny, respectively.

Previously we used allow, instead of accept, in ACL tests. While both terms are currently supported, we are using accept going forward.

Each destination in the list is of the form host:port, where port is a single numeric port and host is one of the following:

Type Example Meaning
Tailscale IP 100.101.102.103 The Tailscale IP of the device. IPv6 addresses must follow the format [1:2:3::4]:80.
Host my-host Looks up the Tailscale IP in the hosts section
User shreya@example.com The Tailscale IP of a device currently signed in as the given user
Group group:security@example.com The Tailscale IP of a device currently signed in as a representative member of the given group
Tag tag:production The Tailscale IP of a device currently tagged with the given tag

Sources in src and destinations in accept and deny must refer to specific entities and do not support * wildcards. For example, an accept destination cannot be tags:*.

Network policy options

Network policy options are available for all plans.

In addition to access rules, the tailnet policy file includes a few network-wide policy settings for specialized purposes. Most networks should never need to specify these.

derpMap

The derpMap section lets you add custom DERP servers to your network, which your devices will use as needed to relay traffic. You can also use this section to disable the use of Tailscale-provided DERP servers, for example to meet corporate compliance requirements. The derpMap section is detailed in the article on running custom DERP servers.

disableIPv4

The disableIPv4 field, if set to true, stops assigning Tailscale IPv4 addresses to your devices. All devices in your network will receive exclusively IPv6 Tailscale addresses, and devices that do not support IPv6 (for example, systems that have IPv6 disabled in the operating system) will be unreachable. This option is intended for users who have a pre-existing conflicting use of the 100.64.0.0/10 carrier-grade NAT address range.

OneCGNATRoute

The OneCGNATRoute field controls the routes that Tailscale clients will generate.

Tailscale clients can have either:

  • One large 100.64/10 route to avoid churn in the routing table as nodes go online and offline. (The churn is disruptive to Chromium-based browsers on macOS.)
  • Fine-grained /32 routes.

The possible values for OneCGNATRoute are:

  • An empty string or not provided: Use default heuristics for each platform.
    • On macOS (for Tailscale v1.28 or later), Tailscale will prefer to add one large 100.64/10 route. Tailscale will not do this if there are other interfaces that also route IP addresses in that range.
    • On other platforms, Tailscale will add fine-grained /32 routes for each node.
  • "mac-always": macOS clients will always add one 100.64/10 route.
  • "mac-never": macOS clients will always add fine-grained /32 routes.
randomizeClientPort

The randomizeClientPort field, if set to true, makes devices prefer a random port for WireGuard traffic over the default static port 41641. This option is intended as a workaround for some buggy firewall devices, and should only be enabled after consulting with Tailscale (contact support).