Skip to content

# Routes

Headscale supports route advertising and can be used to manage subnet routers and exit nodes for a tailnet.

- Subnet routers may be used to connect an existing network such as a virtual private cloud or an on-premise network with your tailnet. Use a subnet router to access devices where Tailscale can't be installed or to gradually rollout Tailscale.
- Exit nodes can be used to route all Internet traffic for another Tailscale node. Use it to securely access the Internet on an untrusted Wi-Fi or to access online services that expect traffic from a specific IP address.

## Subnet router

The setup of a subnet router requires double opt-in, once from a subnet router and once on the control server to allow its use within the tailnet. Optionally, use `autoApprovers` to automatically approve routes from a subnet router.

### Setup a subnet router

#### Configure a node as subnet router

Register a node and advertise the routes it should handle as comma separated list:

    $ sudo tailscale up --login-server <YOUR_HEADSCALE_URL> --advertise-routes=10.0.0.0/8,192.168.0.0/24

If the node is already registered, it can advertise new routes or update previously announced routes with:

    $ sudo tailscale set --advertise-routes=10.0.0.0/8,192.168.0.0/24

Finally, enable IP forwarding to route traffic.

#### Enable the subnet router on the control server

The routes of a tailnet can be displayed with the `headscale nodes list-routes` command. A subnet router with the hostname `myrouter` announced the IPv4 networks `10.0.0.0/8` and `192.168.0.0/24`. Those need to be approved before they can be used.

    $ headscale nodes list-routes
    ID | Hostname | Approved | Available      | Serving (Primary)
    1  | myrouter |          | 10.0.0.0/8     |
       |          |          | 192.168.0.0/24 |

Approve all desired routes of a subnet router by specifying them as comma separated list:

    $ headscale nodes approve-routes --identifier 1 --routes 10.0.0.0/8,192.168.0.0/24
    Node updated

The node `myrouter` can now route the IPv4 networks `10.0.0.0/8` and `192.168.0.0/24` for the tailnet.

    $ headscale nodes list-routes
    ID | Hostname | Approved       | Available      | Serving (Primary)
    1  | myrouter | 10.0.0.0/8     | 10.0.0.0/8     | 10.0.0.0/8
       |          | 192.168.0.0/24 | 192.168.0.0/24 | 192.168.0.0/24

#### Use the subnet router

To accept routes advertised by a subnet router on a node:

    $ sudo tailscale set --accept-routes

Please refer to the official Tailscale documentation for how to use a subnet router on different operating systems.

### Restrict the use of a subnet router with a policy

The routes announced by subnet routers are available to the nodes in a tailnet. By default, without a policy enabled, all nodes can accept and use such routes. Configure a policy to explicitly manage who can use routes.

The policy snippet below defines three hosts, a subnet router `router`, a regular node `node` and `service.example.net` as internal service that can be reached via a route on the subnet router `router`. It allows the node `node` to access `service.example.net` on port 80 and 443 which is reachable via the subnet router. Access to the subnet router itself is denied.

Access the routes of a subnet router without the subnet router itself

    {
      "hosts": {
        // the router is not referenced but announces 192.168.0.0/24
        "router": "100.64.0.1/32",
        "node": "100.64.0.2/32",
        "service.example.net": "192.168.0.1/32"
      },
      "grants": [
        {
          "src": ["node"],
          "dst": ["service.example.net"],
          "ip": ["80,443"]
        }
      ]
    }

### Automatically approve routes of a subnet router

The initial setup of a subnet router usually requires manual approval of their announced routes on the control server before they can be used by a node in a tailnet. Headscale supports the `autoApprovers` section in a policy to automate the approval of routes served with a subnet router.

The policy snippet below defines the tag `tag:router` owned by the user `alice`. This tag is used for `routes` in the `autoApprovers` section. The IPv4 route `192.168.0.0/24` is automatically approved once announced by a subnet router that advertises the tag `tag:router`.

Subnet routers tagged with tag:router are automatically approved

    {
      "tagOwners": {
        "tag:router": ["alice@"]
      },
      "autoApprovers": {
        "routes": {
          "192.168.0.0/24": ["tag:router"]
        }
      },
      "grants": [
        // more rules
      ]
    }

Advertise the route `192.168.0.0/24` from a subnet router that also advertises the tag `tag:router` when joining the tailnet:

    $ sudo tailscale up --login-server <YOUR_HEADSCALE_URL> --advertise-tags tag:router --advertise-routes 192.168.0.0/24

Please see the official Tailscale documentation for more information on auto approvers.

## Exit node

The setup of an exit node requires double opt-in, once from an exit node and once on the control server to allow its use within the tailnet. Optionally, use `autoApprovers` to automatically approve an exit node.

### Setup an exit node

#### Configure a node as exit node

Register a node and make it advertise itself as an exit node:

    $ sudo tailscale up --login-server <YOUR_HEADSCALE_URL> --advertise-exit-node

If the node is already registered, it can advertise exit capabilities like this:

    $ sudo tailscale set --advertise-exit-node

Finally, enable IP forwarding to route traffic.

#### Enable the exit node on the control server

The routes of a tailnet can be displayed with the `headscale nodes list-routes` command. An exit node can be recognized by its announced routes: `0.0.0.0/0` for IPv4 and `::/0` for IPv6. The exit node with the hostname `myexit` is already available, but needs to be approved:

    $ headscale nodes list-routes
    ID | Hostname | Approved | Available | Serving (Primary)
    1  | myexit   |          | 0.0.0.0/0 |
       |          |          | ::/0      |

For exit nodes, it is sufficient to approve either the IPv4 or IPv6 route. The other will be approved automatically.

    $ headscale nodes approve-routes --identifier 1 --routes 0.0.0.0/0
    Node updated

The node `myexit` is now approved as exit node for the tailnet:

    $ headscale nodes list-routes
    ID | Hostname | Approved  | Available | Serving (Primary)
    1  | myexit   | 0.0.0.0/0 | 0.0.0.0/0 | 0.0.0.0/0
       |          | ::/0      | ::/0      | ::/0

#### Use the exit node

The exit node can now be used on a node with:

    $ sudo tailscale set --exit-node myexit

Please refer to the official Tailscale documentation for how to use an exit node on different operating systems.

### Restrict the use of an exit node with a policy

An exit node is offered to all nodes in a tailnet. By default, without a policy enabled, all nodes in a tailnet can select and use an exit node. Configure `autogroup:internet` in a policy rule to restrict who can use any of the available exit nodes.

Example use of autogroup:internet

    {
      "grants": [
        {
          "src": ["..."],
          "dst": ["autogroup:internet"],
          "ip": ["*"]
        }
      ]
    }

### Restrict access to exit nodes per user or group

A user can use any of the available exit nodes with `autogroup:internet`. Alternatively, the policy snippet below assigns each user a specific exit node while hiding all other exit nodes. The user `alice` can only use an exit node tagged with `tag:exit1` while user `bob` can only use an exit node tagged with `tag:exit2`.

Assign each user a dedicated exit node

    {
      "tagOwners": {
        "tag:exit1": ["alice@"],
        "tag:exit2": ["bob@"]
      },
      "grants": [
        {
          "src": ["alice@"],
          "dst": ["autogroup:internet"],
          "via": ["tag:exit1"],
          "ip": ["*"]
        },
        {
          "src": ["bob@"],
          "dst": ["autogroup:internet"],
          "via": ["tag:exit2"],
          "ip": ["*"]
        }
      ]
    }

### Automatically approve an exit node with auto approvers

The initial setup of an exit node usually requires manual approval on the control server before it can be used by a node in a tailnet. Headscale supports the `autoApprovers` section in a policy to automate the approval of a new exit node as soon as it joins the tailnet.

The policy snippet below defines the tag `tag:exit` owned by the user `alice`. This tag is used for the `exitNode` entry in the `autoApprovers` section. A new exit node that advertises the tag `tag:exit` is automatically approved:

Exit nodes tagged with tag:exit are automatically approved

    {
      "tagOwners": {
        "tag:exit": ["alice@"]
      },
      "autoApprovers": {
        "exitNode": ["tag:exit"]
      },
      "grants": [
        // more rules
      ]
    }

Advertise a node as exit node and also advertise the tag `tag:exit` when joining the tailnet:

    $ sudo tailscale up --login-server <YOUR_HEADSCALE_URL> --advertise-tags tag:exit --advertise-exit-node

Please see the official Tailscale documentation for more information on auto approvers.

## High availability

Headscale supports high availability routing. Multiple subnet routers with overlapping routes or multiple exit nodes can be used to provide high availability for users. If one router node goes offline, another one can serve the same routes to clients. Please see the official Tailscale documentation on high availability for details.

This feature is enabled by default when at least two nodes advertise the same prefix. See the configuration options `node.routes.ha` in the configuration file for details.

## Troubleshooting

### Enable IP forwarding

A subnet router or exit node is routing traffic on behalf of other nodes and thus requires IP forwarding. Check the official Tailscale documentation for how to enable IP forwarding.