diff --git a/docs/about/features.md b/docs/about/features.md index 028f680b..62985c4c 100644 --- a/docs/about/features.md +++ b/docs/about/features.md @@ -2,27 +2,31 @@ Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. This page -provides on overview of headscale's feature and compatibility with the Tailscale control server: +provides on overview of Headscale's feature and compatibility with the Tailscale control server: - [x] Full "base" support of Tailscale's features - [x] Node registration - [x] Interactive - [x] Pre authenticated key -- [x] [DNS](https://tailscale.com/kb/1054/dns) +- [x] [DNS](../ref/dns.md) - [x] [MagicDNS](https://tailscale.com/kb/1081/magicdns) - [x] [Global and restricted nameservers (split DNS)](https://tailscale.com/kb/1054/dns#nameservers) - [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] [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](../ref/routes.md) + - [x] [Subnet routers](../ref/routes.md#subnet-router) + - [x] [Exit nodes](../ref/routes.md#exit-node) - [x] Dual stack (IPv4 and IPv6) - [x] Ephemeral nodes - [x] Embedded [DERP server](https://tailscale.com/kb/1232/derp-servers) - [x] Access control lists ([GitHub label "policy"](https://github.com/juanfont/headscale/labels/policy%20%F0%9F%93%9D)) - [x] ACL management via API - - [x] `autogroup:internet` - - [ ] `autogroup:self` - - [ ] `autogroup:member` + - [x] Some [Autogroups](https://tailscale.com/kb/1396/targets#autogroups), currently: `autogroup:internet`, + `autogroup:nonroot` + - [x] [Auto approvers](https://tailscale.com/kb/1337/acl-syntax#auto-approvers) for [subnet + routers](../ref/routes.md#automatically-approve-routes-of-a-subnet-router) and [exit + nodes](../ref/routes.md#automatically-approve-an-exit-node-with-auto-approvers) * [ ] Node registration using Single-Sign-On (OpenID Connect) ([GitHub label "OIDC"](https://github.com/juanfont/headscale/labels/OIDC)) - [x] Basic registration - [x] Update user profile from identity provider diff --git a/docs/ref/dns.md b/docs/ref/dns.md index 3777661a..01f48e0a 100644 --- a/docs/ref/dns.md +++ b/docs/ref/dns.md @@ -76,14 +76,14 @@ hostname and port combination "http://hostname-in-magic-dns.myvpn.example.com:30 === "Query with dig" - ```shell + ```console dig +short grafana.myvpn.example.com 100.64.0.3 ``` === "Query with drill" - ```shell + ```console drill -Q grafana.myvpn.example.com 100.64.0.3 ``` 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..ec91aadd --- /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 +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 +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 when announced by a new 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 +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 +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/docs/ref/tls.md b/docs/ref/tls.md index d1e91016..d377457c 100644 --- a/docs/ref/tls.md +++ b/docs/ref/tls.md @@ -52,7 +52,7 @@ If you want to validate that certificate renewal completed successfully, this ca 1. Open the URL for your headscale server in your browser of choice, and manually inspecting the expiry date of the certificate you receive. 2. Or, check remotely from CLI using `openssl`: -```bash +```console $ openssl s_client -servername [hostname] -connect [hostname]:443 | openssl x509 -noout -dates (...) notBefore=Feb 8 09:48:26 2024 GMT diff --git a/docs/setup/install/container.md b/docs/setup/install/container.md index 3963597d..2527c3be 100644 --- a/docs/setup/install/container.md +++ b/docs/setup/install/container.md @@ -140,13 +140,13 @@ Additionally, the debug container includes a minimalist Busybox shell. To launch a shell in the container, use: -``` +```shell docker run -it headscale/headscale:x.x.x-debug sh ``` You can also execute commands directly, such as `ls /ko-app` in this example: -``` +```shell docker run headscale/headscale:x.x.x-debug ls /ko-app ``` 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