ACL grants

Tailscale is a programmable private network, which means that you can define exactly what your network looks like, which devices are on it, and what they can access. One of the key inputs to defining your network is the tailnet policy file.

At the core of the tailnet policy is the grants array. Each entry grants source the specified capabilities on the destination. The capabilities being granted can either be at the IP layer (ip) or at the application layer (app). These capabilities are compiled by the policy engine and pushed down to each Tailscale client and cached locally. This allows the Tailscale client to make decisions locally without having to query the coordination server.

The grants section is an array of objects, where each object must have a src and dst, and may have ip and app capabilities. The src and dst fields are arrays of selectors. The ip field is an array of of network level capabilities, and the app field is a map application level capabilities. We will walk through each of these fields in more detail below.

ACL grants are currently in beta.

Walkthrough

Let us walk through an example of trying to connect an application to Tailscale. In this instance, we will use TailSQL as an example. TailSQL is a web SQL playground that allows you to query your databases from the browser. It is a tool for debugging and inspecting your databases. It is also an example of an application that you would want to connect to and secure using Tailscale.

When setting it up, you need to give the production group access to the interface. To achieve that in the tailnet policy file, write something like:

{
  "grants": [{
    "src": ["group:prod"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
  }]
}

This rule allows any member of group:prod to connect to port 443 on any Tailscale device tagged with tag:tailsql. It will be compiled by the policy engine and pushed down to relevant devices to allow these connections through.

Now, let’s add some monitoring to TailSQL. It already exposes a metrics endpoint reachable on port 443 at /debug/varz, we can allow prometheus access to scrape that endpoint.

{
  "grants": [{
    "src": ["group:prod"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
  },{
    "src": ["tag:prom"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
  }]
}

Now that TailSQL is operational, it is time to give the analytics group access to TailSQL as well. To do so:

{
  "grants": [{
    "src": ["group:prod", "group:analytics"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
  },{
    "src": ["tag:prom"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
  }]
}

At this point, we have both the production group and the analytics group with access to the shared TailSQL instance. However, as these are different operational groups, they require access to different datasets. The production group would like to use TailSQL to inspect some backups, whereas the analytics group requires access to the data warehouse. If your databases contain sensitive information, you may want to allow access to the SQL playground only for users in a certain group.

In the above example, even the prometheus service has access to the instance as a side effect of giving it access to the debug endpoint.

This is where we can leverage application layer capabilities to define an authorization policy which allows the production team to query to all available data sources, and restricts the analytics team data warehouse source. Monitoring does not need access to any data sources, it only needs to scrape metrics from TailSQL’s HTTPS server.

{
  "grants": [{
    "src": ["group:prod"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
    "app": {
        "tailscale.com/cap/tailsql": [{
            "dataSrc": ["*"],
        }],
    },
  },{
    "src": ["group:analytics"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
    "app": {
        "tailscale.com/cap/tailsql": [{
            "dataSrc": ["warehouse"],
        }],
    },
  },{
    "src": ["tag:prom"],
    "dst": ["tag:tailsql"],
    "ip": ["443"],
  }]
}

Similarly to IP grants, these are compiled and pushed down to relevant clients. This will then allow all devices tagged with tag:tailsql to query their local Tailscale client and query the tailscale.com/cap/tailsql grant. You can do this by using the LocalAPI and the tailscale whois command.

$ tailscale whois 100.64.0.5
Machine:
  Name:          example.ts.net
  ID:            nXXXXXCNTRL
  Addresses:     [100.64.0.5/32 fd7a:115c:a1e0::c477/128]
User:
  Name:     user@example.com
  ID:       12345
Capabilities:
  - tailscale.com/cap/tailsql:
      [
        {
          "dataSrc": [
            "*"
          ]
        },
        {
          "dataSrc": [
            "warehouse"
          ]
        }
      ]

ACLs and grants

ACLs and Grants are at feature parity for network level permissions, including features like device posture, ACL tests, and previews. Both can be used simultaneously in your tailnet policy, although we expect grants will be more powerful and expressive for most users.

Reference

Each grant is an object with the following fields:

  • src: An array of selectors that define the source of the grant.
  • dst: An array of selectors that define the destination of the grant.
  • ip: An optional array of strings that define the IP layer capabilities being granted.
  • app: An optional map of strings to arrays of objects that define the application layer capabilities being granted.
  • srcPosture: An optional array of device posture conditions that can be used to further restrict src.
src

The src field is an array of selectors that define the source of the grant. These selectors can be any of the following:

  • *: The wildcard selector allows you to select all devices. This is useful for granting access to all devices.
  • group:<group name>: The group selector allows you to select all members of a group. This is useful for granting access to a group of users.
  • <email>: The email selector allows you to select a specific user. This is useful for granting access to a specific user. For GitHub users, this is their username@github, for Passkeys, this is their username@passkey.
  • tag:<tag name>: The tag selector allows you to select all devices with a specific tag. This is useful for granting access to a group of devices.
  • autogroup:<role>: The autogroup selector allows you to select all members of a role. This is useful for granting access to a role of users. Possible roles are admin, member, owner, it-admin, network-admin, billing-admin and auditor.
  • autogroup:tagged: The autogroup selector allows you to select all devices with a tag.
  • autogroup:shared: The autogroup selector allows you to select all devices belonging to user who have accepted a sharing invitation to your network.
  • {cidr}/{ip}: The cidr selector allows you to select all devices in a CIDR range. This is useful for granting access to a group of devices. For example, 10.0.0.0/8 or 100.64.0.5.
  • {hostAlias}: The hostAlias selector allows you to use a user-defined alias. This is useful for granting access to a specific device or a CIDR range. These are defined in the hosts section.
dst

The dst field is an array of selectors that define the destination of the grant. These selectors can be any of the following:

  • *: The wildcard selector allows you to select all devices. This is useful for granting access to all devices.
  • group:<group name>: The group selector allows you to select all members of a group. This is useful for granting access to a group of users.
  • <email>: The email selector allows you to select a specific user. This is useful for granting access to a specific user. For GitHub users, this is their username@github. For Passkey users, this is their username@passkey.
  • tag:<tag name>: The tag selector allows you to select all devices with a specific tag. This is useful for granting access to a group of devices.
  • autogroup:<role>: The autogroup selector allows you to select all members of a role. This is useful for granting access to a role of users. Possible roles are admin, member, owner, it-admin, network-admin, billing-admin and auditor.
  • autogroup:tagged: The autogroup selector allows you to select all devices with a tag. This is useful for granting access to a group of devices.
  • autogroup:internet: This is a special autogroup selector that allows you to grant access to use an exit node to access the internet.
  • autogroup:self: This is a special autogroup selector that when combined with a src selector of autogroup:member allows you to grant access to a user’s own devices from their own devices.
  • {cidr}/{ip}: The cidr selector allows you to select all devices in a CIDR range. This is useful for granting access to a group of devices. For example, 10.0.0.0/8 or 100.64.0.5.
  • {hostAlias}: The hostAlias selector allows you to use a user-defined alias. This is useful for granting access to a specific device or a CIDR range. These are defined in the hosts section.
ip

The ip field is an array of strings that define the IP layer capabilities being granted. These strings can be any of the following formats:

  • *: This allows access to all ports on the destination. This implies TCP, UDP and ICMP access.
  • <port>: This allows access to a specific port on the destination. This implies TCP, UDP and ICMP access. Ports can be specified as a single port or a range of ports. For example, 443 or 80-443.
  • <proto>:*: This allows all proto access to the destination, regardless of port. This is especially useful for protocols that do not have ports, such as ICMP. For example, icmp:* or sctp:*.
  • <proto>:<port>: This allows access to a specific port on the destination. This allows you to specify the protocol. Ports can be specified as a single port or a range of ports. For example, tcp:443 or tcp:80-443.

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
app

The app field is a map of strings to arrays of objects that define the application layer capabilities being granted. The strings are the application layer capabilities being granted, and the objects are the parameters for that capability. The names of the capabilities are defined by the application and should follow the format <domainName>/<capabilityName>. The domainName allows you to namespace your capabilities and avoid collisions with other applications, and the capabilityName allows you to define the specific capability. For example, the TailSQL application defines the tailscale.com/cap/tailsql capability.

The parameters for each capability are defined by the application and are treated as opaque JSON objects by the policy engine. The policy engine does not validate the parameters, but it does compile them and push them down to the Tailscale clients. The Tailscale clients can then use these parameters to make authorization decisions locally. The parameters are defined by the application and should be documented by the application developer defining the capability.

srcPosture

The srcPosture field is an array of device posture conditions that can be used to further restrict src. For example, you can use this to restrict access to only devices that are running a specific version of the Tailscale client.