diff --git a/CHANGELOG.md b/CHANGELOG.md index 18878d8f..bfac6365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ this, the CLI and API has been simplified to reflect the changes; ```console $ headscale nodes list-routes -ID | Hostname | Approved | Available | Serving +ID | Hostname | Approved | Available | Serving (Primary) 1 | ts-head-ruqsg8 | | 0.0.0.0/0, ::/0 | 2 | ts-unstable-fq7ob4 | | 0.0.0.0/0, ::/0 | @@ -24,7 +24,7 @@ $ headscale nodes approve-routes --identifier 1 --routes 0.0.0.0/0,::/0 Node updated $ headscale nodes list-routes -ID | Hostname | Approved | Available | Serving +ID | Hostname | Approved | Available | Serving (Primary) 1 | ts-head-ruqsg8 | 0.0.0.0/0, ::/0 | 0.0.0.0/0, ::/0 | 0.0.0.0/0, ::/0 2 | ts-unstable-fq7ob4 | | 0.0.0.0/0, ::/0 | ``` @@ -106,6 +106,8 @@ working in v1 and not tested might be broken in v2 (and vice versa). [#2503](https://github.com/juanfont/headscale/pull/2503) - Restore support for "Override local DNS" [#2438](https://github.com/juanfont/headscale/pull/2438) +- Add documentation for routes + [#2496](https://github.com/juanfont/headscale/pull/2496) ## 0.25.1 (2025-02-25) diff --git a/docs/about/features.md b/docs/about/features.md index 6775beca..3f5370bc 100644 --- a/docs/about/features.md +++ b/docs/about/features.md @@ -14,7 +14,9 @@ provides on overview of headscale's feature and compatibility with the Tailscale - [x] [search domains](https://tailscale.com/kb/1054/dns#search-domains) - [x] [Extra DNS records (headscale only)](../ref/dns.md#setting-extra-dns-records) - [x] [Taildrop (File Sharing)](https://tailscale.com/kb/1106/taildrop) -- [x] Routing advertising (including exit nodes) +- [x] Routes + - [x] [Subnet routers](https://tailscale.com/kb/1019/subnets) + - [x] [Exit nodes](https://tailscale.com/kb/1103/exit-nodes) - [x] Dual stack (IPv4 and IPv6) - [x] Ephemeral nodes - [x] Embedded [DERP server](https://tailscale.com/kb/1232/derp-servers) diff --git a/docs/ref/exit-node.md b/docs/ref/exit-node.md deleted file mode 100644 index 5f9ba6a7..00000000 --- a/docs/ref/exit-node.md +++ /dev/null @@ -1,45 +0,0 @@ -# Exit Nodes - -## On the node - -Register the node and make it advertise itself as an exit node: - -```console -$ sudo tailscale up --login-server https://headscale.example.com --advertise-exit-node -``` - -If the node is already registered, it can advertise exit capabilities like this: - -```console -$ sudo tailscale set --advertise-exit-node -``` - -To use a node as an exit node, IP forwarding must be enabled on the node. Check the official [Tailscale documentation](https://tailscale.com/kb/1019/subnets/?tab=linux#enable-ip-forwarding) for how to enable IP forwarding. - -## On the control server - -```console -$ headscale nodes list-routes -ID | Hostname | Approved | Available | Serving -1 | ts-head-ruqsg8 | | 0.0.0.0/0, ::/0 | -2 | ts-unstable-fq7ob4 | | 0.0.0.0/0, ::/0 | - -# Note that for exit nodes, it is sufficient to approve either the IPv4 or IPv6 route. The other will be added automatically. -$ headscale nodes approve-routes --identifier 1 --routes 0.0.0.0/0 -Node updated - -$ headscale nodes list-routes -ID | Hostname | Approved | Available | Serving -1 | ts-head-ruqsg8 | 0.0.0.0/0, ::/0 | 0.0.0.0/0, ::/0 | 0.0.0.0/0, ::/0 -2 | ts-unstable-fq7ob4 | | 0.0.0.0/0, ::/0 | -``` - -## On the client - -The exit node can now be used with: - -```console -$ sudo tailscale set --exit-node phobos -``` - -Check the official [Tailscale documentation](https://tailscale.com/kb/1103/exit-nodes#use-the-exit-node) for how to do it on your device. diff --git a/docs/ref/routes.md b/docs/ref/routes.md new file mode 100644 index 00000000..21740f7e --- /dev/null +++ b/docs/ref/routes.md @@ -0,0 +1,287 @@ +# Routes +Headscale supports route advertising and can be used to manage [subnet routers](https://tailscale.com/kb/1019/subnets) +and [exit nodes](https://tailscale.com/kb/1103/exit-nodes) for a tailnet. + +- [Subnet routers](#subnet-router) 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](#exit-node) 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](#automatically-approve-routes-of-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: + +```console +$ sudo tailscale up --login-server --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: + +```console +$ sudo tailscale set --advertise-routes=10.0.0.0/8,192.168.0.0/24 +``` + +Finally, [enable IP forwarding](#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. + +```console +$ 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: + +```console +$ 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. + +```console +$ headscale nodes list-routes +ID | Hostname | Approved | Available | Serving (Primary) +1 | myrouter | 10.0.0.0/8, 192.168.0.0/24 | 10.0.0.0/8, 192.168.0.0/24 | 10.0.0.0/8, 192.168.0.0/24 +``` + +#### Use the subnet router + +To accept routes advertised by a subnet router on a node: + +```console +$ sudo tailscale set --accept-routes +``` + +Please refer to the official [Tailscale +documentation](https://tailscale.com/kb/1019/subnets#use-your-subnet-routes-from-other-devices) for how to use a subnet +router on different operating systems. + +### Restrict the use of a subnet router with ACL +The routes announced by subnet routers are available to the nodes in a tailnet. By default, without an ACL enabled, all +nodes can accept and use such routes. Configure an ACL to explicitly manage who can use routes. + +The ACL 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`. The first ACL rule allows anyone to see +the subnet router `router` without allowing access to any service of the subnet router itself. The second ACL rule +allows the node `node` to access `service.example.net` on port 80 and 443 which is reachable via the subnet router. + +```json title="Access the routes of a subnet router without the subnet router itself" +{ + "hosts": { + "router": "100.64.0.1/32", + "node": "100.64.0.2/32", + "service.example.net": "192.168.0.1/32" + }, + "acls": [ + { + "action": "accept", + "src": [ + "*" + ], + "dst": [ + "router:0" + ] + }, + { + "action": "accept", + "src": [ + "node" + ], + "dst": [ + "service.example.net: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 of an ACL to automate the +approval of routes served with a subnet router. + +The ACL 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 +owned by the user `alice` and that also advertises the tag `tag:router`. + +```json title="Subnet routers owned by alice and tagged with tag:router are automatically approved" +{ + "tagOwners": { + "tag:router": [ + "alice@" + ] + }, + "autoApprovers": { + "routes": { + "192.168.0.0/24": [ + "tag:router" + ] + } + }, + "acls": [ + // 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: + +```console +$ sudo tailscale up --login-server --advertise-tags tag:router --advertise-routes 192.168.0.0/24 +``` + +Please see the [official Tailscale documentation](https://tailscale.com/kb/1337/acl-syntax#autoapprovers) 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](#automatically-approve-an-exit-node-with-auto-approvers). + +### Setup an exit node +#### Configure a node as exit node + +Register a node and make it advertise itself as an exit node: + +```console +$ sudo tailscale up --login-server --advertise-exit-node +``` + +If the node is already registered, it can advertise exit capabilities like this: + +```console +$ sudo tailscale set --advertise-exit-node +``` + +Finally, [enable IP forwarding](#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: + +```console +$ 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. + +```console +$ 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: + +```console +$ 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: + +```console +$ sudo tailscale set --exit-node myexit +``` + +Please refer to the official [Tailscale documentation](https://tailscale.com/kb/1103/exit-nodes#use-the-exit-node) for +how to use an exit node on different operating systems. + +### Restrict the use of an exit node with ACL +An exit node is offered to all nodes in a tailnet. By default, without an ACL enabled, all nodes in a tailnet can select +and use an exit node. Configure `autogroup:internet` in an ACL rule to restrict who can use *any* of the available exit +nodes. + +```json title="Example use of autogroup:internet" +{ + "acls": [ + { + "action": "accept", + "src": [ + "..." + ], + "dst": [ + "autogroup:internet:*" + ] + } + ] +} +``` + +### 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 of an ACL to automate the approval of a new exit node as +soon as it joins the tailnet. + +The ACL snippet below defines the tag `tag:exit` owned by the user `alice`. This tag is used for `exitNode` in the +`autoApprovers` section. A new exit node which is owned by the user `alice` and that also advertises the tag `tag:exit` +is automatically approved: + +```json title="Exit nodes owned by alice and tagged with tag:exit are automatically approved" +{ + "tagOwners": { + "tag:exit": [ + "alice@" + ] + }, + "autoApprovers": { + "exitNode": [ + "tag:exit" + ] + }, + "acls": [ + // more rules + ] +} +``` + +Advertise a node as exit node and also advertise the tag `tag:exit` when joining the tailnet: + +```console +$ sudo tailscale up --login-server --advertise-tags tag:exit --advertise-exit-node +``` + +Please see the [official Tailscale documentation](https://tailscale.com/kb/1337/acl-syntax#autoapprovers) for more +information on auto approvers. + +## High availability + +Headscale has limited support for 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](https://tailscale.com/kb/1115/high-availability#subnet-router-high-availability) for details. + +!!! bug + + In certain situations it might take up to 16 minutes for Headscale to detect a node as offline. A failover node + might not be selected fast enough, if such a node is used as subnet router or exit node causing service + interruptions for clients. See [issue 2129](https://github.com/juanfont/headscale/issues/2129) for more information. + +## 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](https://tailscale.com/kb/1019/subnets/?tab=linux#enable-ip-forwarding) for how to +enable IP forwarding. diff --git a/hscontrol/policy/v1/acls_types.go b/hscontrol/policy/v1/acls_types.go index 8c4584c7..c44d8df7 100644 --- a/hscontrol/policy/v1/acls_types.go +++ b/hscontrol/policy/v1/acls_types.go @@ -43,7 +43,7 @@ type ACLTest struct { Deny []string `json:"deny,omitempty"` } -// AutoApprovers specify which users (users?), groups or tags have their advertised routes +// AutoApprovers specify which users, groups or tags have their advertised routes // or exit node status automatically enabled. type AutoApprovers struct { Routes map[string][]string `json:"routes"` diff --git a/mkdocs.yml b/mkdocs.yml index d68cc6dc..dec10d34 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,7 +79,8 @@ plugins: android-client.md: usage/connect/android.md apple-client.md: usage/connect/apple.md dns-records.md: ref/dns.md - exit-node.md: ref/exit-node.md + exit-node.md: ref/routes.md + ref/exit-node.md: ref/routes.md faq.md: about/faq.md iOS-client.md: usage/connect/apple.md#ios oidc.md: ref/oidc.md @@ -179,7 +180,7 @@ nav: - Reference: - Configuration: ref/configuration.md - OIDC authentication: ref/oidc.md - - Exit node: ref/exit-node.md + - Routes: ref/routes.md - TLS: ref/tls.md - ACLs: ref/acls.md - DNS: ref/dns.md