Get started
Login
© 2024

Grants

Grants are currently in beta.

Tailscale’s access control methodology follows the least privilege and zero trust principles. There are two ways to define access controls for your tailnet: access control lists (ACLs) and grants. Both methods follow a deny-by-default principle and are defined in the tailnet policy file using a declarative huJSON syntax.

The grants system combines network layer and application layer capabilities into a shared syntax. As a result, it offers enhanced flexibility and fine-grained control over resource access. Each grant only requires a source and a destination. Because Tailscale takes a deny-by-default approach, each grant has an implied accept action.

{
  "grants": [
    // Grants can still define network layer access controls
    {
      "src": [<list-of-sources>], // These sources (devices or users)
      "dst": [<list-of-destinations],  // can access these destination devices
      "ip": [<list-of-ports-or-protocols>], // using the following ports or protocols
    }

    // But they can also define application layer capabilities
    {
      "src": [<list-of-sources>], // These sources (devices or users)
      "dst": [<list-of-destinations],  // can access these destination devices
      "ip": [<list-of-ports-or-protocols>], // using the following ports or protocols,
      "app": {<list-of-application-capabilities>} // and once they have access, they are granted these application permissions.
    }
  ]
}

Each entry grants the source (src) specified capabilities on the destination. You can use the grants object to grant capabilities at the network layer (ip) or the application layer (app). The Tailscale policy engine compiles all capabilities and sends them to each Tailscale client. Each client then caches the policies locally to improve performance and reduce reliance on the coordination server.

Grants and ACLs

Grants and ACLs are at feature parity for network-level permissions, including features like device posture, ACL tests, and previews. You can use both grants and ACLs simultaneously in your tailnet policy. However, the best practice is to prefer grants because they offer more power and flexibility for most users.

Reference

The grants section of your tailnet policy file is an array of objects, where each object must have a network source field (src) and a network destination field (dst). Grant objects can optionally include the ip, app, or srcPosture 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 (Optional): An array of strings that define the network-layer capabilities to grant.
  • app (Optional): A map of strings to arrays of objects that define the application-layer capabilities to grant.
  • srcPosture (Optional): An array of device posture conditions to further restrict the src.
  • via (Optional): A reference to one or more devices to route traffic through. Learn more about route filtering with Via.

src

The src field is an array of selectors that define the target network sources. A network source is the device or node that initiates a network communication or sends data packets.

You can use any of the following selectors to define the network source

SelectorDescriptionExample
*Select all sources in your tailnet and from approved subnet routes. This does not include devices outside your tailnet (unless they're on an approved subnet).
autogroup:danger-allSelect all sources, including those outside your tailnet.
group:<groupName>Select all members of a specific group.group:prod
<email>Select a specific user by their email address. For GitHub users, use username@github. For Passkey users, use username@passkey.name@example.com, username@github, username@passkey
tag:<tagName>Select all devices with a specific tag.tag:tailsql
autogroup:<role>Select all members of a specific role. Possible roles are admin, member, owner, it-admin, network-admin, billing-admin, and auditor.autogroup:admin
autogroup:taggedSelect all devices with a tag (any tag).
autogroup:sharedSelect all devices that belong to users who have accepted a sharing invitation to your tailnet.
<cidr>/<ip>Select all devices in a CIDR range.192.0.2.0/24, 192.0.2.5.
<hostAlias>Select a host by its user-defined alias. You can use this selector to grant access to a specific device or a CIDR range. You can find hostAlias definitions in the hosts section.example-host-name
ipset: <ipsetName>Select an IP set (a named group of IP address ranges).ipset: prod

dst

The dst field is an array of selectors that define the destination of the grant. A network destination refers to the device, user, or target that receives the data packets from the source.

You can use any of the following selectors to define the network destination:

SelectorDescriptionExample
*Select all sources in your tailnet and from approved subnet routes. This does not include devices outside your tailnet (unless they're on an approved subnet).
autogroup:danger-allSelect all sources, including those outside your tailnet.
group:<groupName>Select all members of a specific group.group:analytics
<email>Select a specific user by their email address. For GitHub users, use username@github. For Passkey users, use username@passkey.name@example.com, username@github, username@passkey
tag:<tagName>Select all devices with a specific tag.tag:tailsql
autogroup:<role>Select all members of a specific role. Possible roles are admin, member, owner, it-admin, network-admin, billing-admin, and auditor.autogroup:admin
autogroup:taggedSelect all devices with a tag (any tag).
autogroup:internetautogroup:internet is a special autogroup selector that lets you grant access to use an exit node to access the internet.
autogroup:selfSelect a user's devices. autogroup:self is a special autogroup selector that, when combined with a src selector of autogroup:member, lets you grant access to a user's own devices from their own devices.
<cidr>/<ip>Select all devices in a CIDR range.192.0.2.0/24, 192.0.2.5.
<hostAlias>Select a host by its user-defined alias. You can use this selector to grant access to a specific device or a CIDR range. You can find hostAlias definitions in the hosts section.example-host-name
ipset: <ipsetName>Select an IP set (a named group of IP address ranges).ipset: prod

ip

The ip field is an array of strings that grant network layer capabilities. You can use any of the following selectors to grant access to network layer capabilities:

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

You can specify the IP protocol (proto) as an IANA IP protocol number 1-255 (for example, "16") or one of the following named aliases (for example, "sctp").


Expand named aliases.
ProtocolprotoIANA protocol number
Internet Group Management (IGMP)igmp2
IPv4 encapsulationipv4, ip-in-ip4
Transmission Control (TCP)tcp6
Exterior Gateway Protocol (EGP)egp8
Any private interior gatewayigp9
User Datagram (UDP)udp17
Generic Routing Encapsulation (GRE)gre47
Encap Security Payload (ESP)esp50
Authentication Header (AH)ah51
Stream Control Transmission Protocol (SCTP)sctp132

app

The app field is an optional field that maps strings to arrays of objects that define the application layer capabilities to grant. The strings are the application layer capabilities to grant, and the objects are the parameters for those capabilities.

The specific application (such as TailSQL) defines the names of capabilities names in the format <domainName>/<capabilityName>.

  • The domainName groups capabilities into namespaces to avoid collisions with other applications.
  • The capabilityName defines the specific capability. For example, the TailSQL application defines the tailscale.com/cap/tailsql capability.

The specific application also defines the parameters for each capability. The Tailscale policy engine treats these parameters as opaque JSON objects. The policy engine compiles the parameters and sends them to the Tailscale clients but does not validate them. The Tailscale clients can then use these parameters to make authorization decisions locally.

Tailscale is not involved in creating, naming, or validating application capabilities or their parameters (unless it's a Tailscale application). It's the application developer's responsibility to document all capabilities and parameters.

srcPosture

The srcPosture field is an array of device posture conditions you can use to further restrict the network source (src). For example, you can use srcPosture to restrict access to only devices running a specific version of the Tailscale client.

via

Via introduces routing awareness to grants by letting you include a via field to specify how Tailscale can route the destination (dst) from the source (src).

You can use the via syntax to define the exit nodes, subnet routers, or app connectors a source can access when they use a specific destination. For example, you can create a grant that forces traffic through a specific exit node when it goes from the engineering team group to the GitHub app connector.

Example scenario

The following scenario demonstrates connecting an application (TailSQL) to Tailscale and fine-tuning access to TailSQL based on tags and groups.

In this example scenario, the tailnet has the following configured:

  • A prod group for the production team.
  • An analytics group for the analytics team.
  • A TailSQL application tagged with tailsql and running on port 443.
  • A Prometheus server tagged with prom.

The final example grants group:prod access to all TailSQL data sources, grants group:analytics access to the TailSQL warehouse data source, and allows Prometheus to monitor TailSQL.

{
  "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"],
    },
  ]
}
Expand for step-by-step instructions to re-create the example grants along with context and explanations.
  1. Create a grants object in the tailnet policy file that grants the production group (group:prod) access to the TailSQL interface (identified by tag:tailsql and port 443). The following example lets devices in your tailnet connect in the prod group connect to port 443 of devices tagged with tailsql.

    {
      "grants": [
        {
          "src": ["group:prod"],
          "dst": ["tag:tailsql"],
          "ip": ["443"],
        }
      ]
    }
    
  2. Allow Prometheus to monitor the TailSQL application. TailSQL already exposes a metrics endpoint reachable on port 443 at /debug/varz. The following example allows Prometheus (identified by tag:prom) access that endpoint.

    {
      "grants": [
        {
          "src": ["group:prod"],
          "dst": ["tag:tailsql"],
          "ip": ["443"],
        },
        {
          "src": ["tag:prom"],
          "dst": ["tag:tailsql"],
          "ip": ["443"],
        }
      ]
    }
    
  3. Allow users in the analytics group to connect to TailSQL devices by adding "group:analytics" to the src array.

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

    At this point, the example grants Prometheus and the prod and analytics groups access to the shared TailSQL instance. However, the Prometheus server, the prod group, and the analytics group all require access to different datasets. The prod group uses TailSQL to inspect some backups, whereas the analytics group requires access to the data warehouse.

    You can configure the grant only to let each group access what it needs (and no more) by leveraging the app field to define application-level capabilities.

  4. Fine-tune access to the TailSQL application by defining two app policies: one that lets the prod group query all data sources. And another that restricts the analytics group's access to only the warehouse data source.

    {
      "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"],
        }]
      }
    
  5. Review the grant using the LocalAPI and the tailscale whois command. In the following example, 192.0.2.5 is part of the prod group and can access all data sources at Tailscale.com/cap/tailsql.

    $ tailscale whois 192.0.2.5
    Machine:
      Name:          example.ts.net
      ID:            nXXXXXCNTRL
      Addresses:     [192.0.2.5/24]
    User:
      Name:     user@example.com
      ID:       12345
    Capabilities:
      - tailscale.com/cap/tailsql:
          [
            {
              "dataSrc": [
                "*"
              ]
            },
            {
              "dataSrc": [
                "warehouse"
              ]
            }
          ]
    

Last updated Nov 27, 2024