Merge remote-tracking branch 'origin/main'

This commit is contained in:
Ryan Malloy 2026-05-21 17:58:02 -06:00
commit 2c8640f822
1496 changed files with 12110903 additions and 24774 deletions

View file

@ -1,3 +1,3 @@
{%
include-markdown "../../CONTRIBUTING.md"
include-markdown "../../CONTRIBUTING.md"
%}

View file

@ -24,9 +24,12 @@ We are more than happy to exchange emails, or to have dedicated calls before a P
## When/Why is Feature X going to be implemented?
We don't know. We might be working on it. If you're interested in contributing, please post a feature request about it.
We use [GitHub Milestones to plan for upcoming Headscale releases](https://github.com/juanfont/headscale/milestones).
Have a look at [our current plan](https://github.com/juanfont/headscale/milestones) to get an idea when a specific
feature is about to be implemented. The release plan is subject to change at any time.
Please be aware that there are a number of reasons why we might not accept specific contributions:
If you're interested in contributing, please post a feature request about it. Please be aware that there are a number of
reasons why we might not accept specific contributions:
- It is not possible to implement the feature in a way that makes sense in a self-hosted environment.
- Given that we are reverse-engineering Tailscale to satisfy our own curiosity, we might be interested in implementing the feature ourselves.
@ -47,8 +50,8 @@ we have a "docker-issues" channel where you can ask for Docker-specific help to
## What is the recommended update path? Can I skip multiple versions while updating?
Please follow the steps outlined in the [upgrade guide](../setup/upgrade.md) to update your existing Headscale
installation. Its best to update from one stable version to the next (e.g. 0.24.0 → 0.25.1 → 0.26.1) in case
you are multiple releases behind. You should always pick the latest available patch release.
installation. Its required to update from one stable version to the next (e.g. 0.26.0 → 0.27.1 → 0.28.0) without
skipping minor versions in between. You should always pick the latest available patch release.
Be sure to check the [changelog](https://github.com/juanfont/headscale/blob/main/CHANGELOG.md) for version specific
upgrade instructions and breaking changes.
@ -70,12 +73,12 @@ of Headscale:
1. An environment with 1000 servers
- they rarely "move" (change their endpoints)
- new nodes are added rarely
- they rarely "move" (change their endpoints)
- new nodes are added rarely
2. An environment with 80 laptops/phones (end user devices)
1. An environment with 80 laptops/phones (end user devices)
- nodes move often, e.g. switching from home to office
- nodes move often, e.g. switching from home to office
Headscale calculates a map of all nodes that need to talk to each other,
creating this "world map" requires a lot of CPU time. When an event that
@ -131,7 +134,7 @@ help to the community.
Running headscale on a machine that is also in the tailnet can cause problems with subnet routers, traffic relay nodes, and MagicDNS. It might work, but it is not supported.
## Why do two nodes see each other in their status, even if an ACL allows traffic only in one direction?
## Why do two nodes see each other in their status, even if a policy rule allows traffic only in one direction?
A frequent use case is to allow traffic only from one node to another, but not the other way around. For example, the
workstation of an administrator should be able to connect to all nodes but the nodes themselves shouldn't be able to
@ -139,7 +142,72 @@ connect back to the administrator's node. Why do all nodes see the administrator
`tailscale status`?
This is essentially how Tailscale works. If traffic is allowed to flow in one direction, then both nodes see each other
in their output of `tailscale status`. Traffic is still filtered according to the ACL, with the exception of `tailscale
ping` which is always allowed in either direction.
in their output of `tailscale status`. Traffic is still filtered according to the policy, with the exception of
`tailscale ping` which is always allowed in either direction.
See also <https://tailscale.com/kb/1087/device-visibility>.
See also <https://tailscale.com/docs/concepts/device-visibility>.
## My policy is stored in the database and Headscale refuses to start due to an invalid policy. How can I recover?
Headscale checks if the policy is valid during startup and refuses to start if it detects an error. The error message
indicates which part of the policy is invalid. Follow these steps to fix your policy:
- Dump the policy to a file: `headscale policy get --bypass-grpc-and-access-database-directly > policy.json`
- Edit and fixup `policy.json`. Use the command `headscale policy check --file policy.json` to validate the policy.
- Load the modified policy: `headscale policy set --bypass-grpc-and-access-database-directly --file policy.json`
- Start Headscale as usual.
!!! warning "Full server configuration required"
The above commands to get/set the policy require a complete server configuration file including database settings. A
minimal config to [control Headscale via remote CLI](../ref/api.md#grpc) is not sufficient. You may use
`headscale -c /path/to/config.yaml` to specify the path to an alternative configuration file.
## How can I migrate back to the recommended IP prefixes?
Tailscale only supports the IP prefixes `100.64.0.0/10` and `fd7a:115c:a1e0::/48` or smaller subnets thereof. The
following steps can be used to migrate from unsupported IP prefixes back to the supported and recommended ones.
!!! warning "Backup and test in a demo environment required"
The commands below update the IP addresses of all nodes in your tailnet and this might have a severe impact in your
specific environment. At a minimum:
- [Create a backup of your database](../setup/upgrade.md#backup)
- Test the commands below in a representive demo environment. This allows to catch subsequent connectivity errors
early and see how the tailnet behaves in your specific environment.
- Stop Headscale
- Restore the default prefixes in the [configuration file](../ref/configuration.md):
```yaml
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
```
- Update the `nodes.ipv4` and `nodes.ipv6` columns in the database and assign each node a unique IPv4 and IPv6 address.
The following SQL statement assigns IP addresses based on the node ID:
```sql
UPDATE nodes
SET ipv4=concat('100.64.', id/256, '.', id%256),
ipv6=concat('fd7a:115c:a1e0::', format('%x', id));
```
- Update the [policy](../ref/policy.md) to reflect the IP address changes (if any)
- Start Headscale
Nodes should reconnect within a few seconds and pickup their newly assigned IP addresses.
## How can I avoid to send logs to Tailscale Inc?
A Tailscale client [collects logs about its operation and connection attempts with other
clients](https://tailscale.com/docs/features/logging#client-logs) and sends them to a central log service operated by
Tailscale Inc.
Headscale, by default, instructs clients to disable log submission to the central log service. This configuration is
applied by a client once it successfully connected with Headscale. See the configuration option `logtail.enabled` in the
[configuration file](../ref/configuration.md) for details.
Alternatively, logging can also be disabled on the client side. This is independent of Headscale and opting out of
client logging disables log submission early during client startup. The configuration is operating system specific and
is usually achieved by setting the environment variable `TS_NO_LOGS_NO_SUPPORT=true` or by passing the flag
`--no-logs-no-support` to `tailscaled`. See <https://tailscale.com/docs/features/logging#opt-out-of-client-logging> for
details.

View file

@ -5,33 +5,41 @@ to provide self-hosters and hobbyists with an open-source server they can use fo
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] [Node registration](../ref/registration.md)
- [x] [Web authentication](../ref/registration.md#web-authentication)
- [x] [Pre authenticated key](../ref/registration.md#pre-authenticated-key)
- [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] [MagicDNS](https://tailscale.com/docs/features/magicdns)
- [x] [Global and restricted nameservers (split DNS)](https://tailscale.com/docs/reference/dns-in-tailscale#nameservers)
- [x] [search domains](https://tailscale.com/docs/reference/dns-in-tailscale#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] File sharing
- [x] [Taildrive](https://tailscale.com/docs/features/taildrive)
- [x] [Taildrop](https://tailscale.com/docs/features/taildrop)
- [x] [Tags](../ref/tags.md)
- [x] [Routes](../ref/routes.md)
- [x] [Subnet routers](../ref/routes.md#subnet-router)
- [x] [Exit nodes](../ref/routes.md#exit-node)
- [x] [Route filtering with Via](https://tailscale.com/docs/features/access-control/grants/grants-via)
- [x] Dual stack (IPv4 and IPv6)
- [x] Ephemeral nodes
- [x] Embedded [DERP server](../ref/derp.md)
- [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] Some [Autogroups](https://tailscale.com/kb/1396/targets#autogroups), currently: `autogroup:internet`,
`autogroup:nonroot`, `autogroup:member`, `autogroup:tagged`
- [x] [Auto approvers](https://tailscale.com/kb/1337/acl-syntax#auto-approvers) for [subnet
- [x] [Peer relays](https://tailscale.com/docs/features/peer-relay)
- [x] [Policy](../ref/policy.md) ([GitHub label "policy"](https://github.com/juanfont/headscale/labels/policy%20%F0%9F%93%9D))
- [x] ACLs
- [x] Grants
- [x] Some [Autogroups](../ref/policy.md#autogroups)
- [x] [Auto approvers](https://tailscale.com/docs/reference/syntax/policy-file#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)
- [x] [Tailscale SSH](https://tailscale.com/kb/1193/tailscale-ssh)
* [x] [Node registration using Single-Sign-On (OpenID Connect)](../ref/oidc.md) ([GitHub label "OIDC"](https://github.com/juanfont/headscale/labels/OIDC))
- [x] [Tailscale SSH](https://tailscale.com/docs/features/tailscale-ssh)
- [x] [Node attributes](../ref/policy.md#node-attributes)
- [x] [Tests](https://tailscale.com/docs/reference/syntax/policy-file#tests) and
[sshTests](https://tailscale.com/docs/reference/syntax/policy-file#ssh-tests)
- [x] [Node registration using Single-Sign-On (OpenID Connect)](../ref/oidc.md) ([GitHub label "OIDC"](https://github.com/juanfont/headscale/labels/OIDC))
- [x] Basic registration
- [x] Update user profile from identity provider
- [ ] OIDC groups cannot be used in ACLs
- [ ] [Funnel](https://tailscale.com/kb/1223/funnel) ([#1040](https://github.com/juanfont/headscale/issues/1040))
- [ ] [Serve](https://tailscale.com/kb/1312/serve) ([#1234](https://github.com/juanfont/headscale/issues/1921))
- [ ] [Network flow logs](https://tailscale.com/kb/1219/network-flow-logs) ([#1687](https://github.com/juanfont/headscale/issues/1687))
- [ ] [Funnel](https://tailscale.com/docs/features/tailscale-funnel) ([#1040](https://github.com/juanfont/headscale/issues/1040))
- [ ] [Serve](https://tailscale.com/docs/features/tailscale-serve) ([#1234](https://github.com/juanfont/headscale/issues/1921))
- [ ] [Network flow logs](https://tailscale.com/docs/features/logging/network-flow-logs) ([#1687](https://github.com/juanfont/headscale/issues/1687))

BIN
docs/assets/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 1280 640"><circle cx="141.023" cy="338.36" r="117.472" style="fill:#f8b5cb" transform="matrix(.997276 0 0 1.00556 10.0024 -14.823)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 -3.15847 0)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 -3.15847 115.914)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 148.43 115.914)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 148.851 0)"/><circle cx="805.557" cy="336.915" r="118.199" style="fill:#8d8d8d" transform="matrix(.99196 0 0 1 3.36978 -10.2458)"/><circle cx="805.557" cy="336.915" r="118.199" style="fill:#8d8d8d" transform="matrix(.99196 0 0 1 255.633 -10.2458)"/><path d="M680.282 124.808h-68.093v390.325h68.081v-28.23H640V153.228h40.282v-28.42Z" style="fill:#303030"/><path d="M680.282 124.808h-68.093v390.325h68.081v-28.23H640V153.228h40.282v-28.42Z" style="fill:#303030" transform="matrix(-1 0 0 1 1857.19 0)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 1280 640"><circle cx="141.023" cy="338.36" r="117.472" style="fill:#f8b5cb" transform="matrix(.997276 0 0 1.00556 10.0024 -14.823)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 -3.15847 0)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 -3.15847 115.914)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 148.43 115.914)"/><circle cx="352.014" cy="268.302" r="33.095" style="fill:#a2a2a2" transform="matrix(1.01749 0 0 1 148.851 0)"/><circle cx="805.557" cy="336.915" r="118.199" style="fill:#8d8d8d" transform="matrix(.99196 0 0 1 3.36978 -10.2458)"/><circle cx="805.557" cy="336.915" r="118.199" style="fill:#8d8d8d" transform="matrix(.99196 0 0 1 255.633 -10.2458)"/><path d="M680.282 124.808h-68.093v390.325h68.081v-28.23H640V153.228h40.282v-28.42Z" style="fill:#303030"/><path d="M680.282 124.808h-68.093v390.325h68.081v-28.23H640V153.228h40.282v-28.42Z" style="fill:#303030" transform="matrix(-1 0 0 1 1857.19 0)"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View file

@ -8,7 +8,7 @@ hide:
Headscale is an open source, self-hosted implementation of the Tailscale control server.
This page contains the documentation for the latest version of headscale. Please also check our [FAQ](./about/faq.md).
This page contains the documentation for the latest version of headscale. Please also check our [FAQ](about/faq.md).
Join our [Discord server](https://discord.gg/c84AZQhmpx) for a chat and community support.

View file

@ -1,206 +0,0 @@
Headscale implements the same policy ACLs as Tailscale.com, adapted to the self-hosted environment.
For instance, instead of referring to users when defining groups you must
use users (which are the equivalent to user/logins in Tailscale.com).
Please check https://tailscale.com/kb/1018/acls/ for further information.
When using ACL's the User borders are no longer applied. All machines
whichever the User have the ability to communicate with other hosts as
long as the ACL's permits this exchange.
## ACL Setup
To enable and configure ACLs in Headscale, you need to specify the path to your ACL policy file in the `policy.path` key in `config.yaml`.
Your ACL policy file must be formatted using [huJSON](https://github.com/tailscale/hujson).
Info on how these policies are written can be found
[here](https://tailscale.com/kb/1018/acls/).
Please reload or restart Headscale after updating the ACL file. Headscale may be reloaded either via its systemd service
(`sudo systemctl reload headscale`) or by sending a SIGHUP signal (`sudo kill -HUP $(pidof headscale)`) to the main
process. Headscale logs the result of ACL policy processing after each reload.
## Simple Examples
- [**Allow All**](https://tailscale.com/kb/1192/acl-samples#allow-all-default-acl): If you define an ACL file but completely omit the `"acls"` field from its content, Headscale will default to an "allow all" policy. This means all devices connected to your tailnet will be able to communicate freely with each other.
```json
{}
```
- [**Deny All**](https://tailscale.com/kb/1192/acl-samples#deny-all): To prevent all communication within your tailnet, you can include an empty array for the `"acls"` field in your policy file.
```json
{
"acls": []
}
```
## Complex Example
Let's build a more complex example use case for a small business (It may be the place where
ACL's are the most useful).
We have a small company with a boss, an admin, two developers and an intern.
The boss should have access to all servers but not to the user's hosts. Admin
should also have access to all hosts except that their permissions should be
limited to maintaining the hosts (for example purposes). The developers can do
anything they want on dev hosts but only watch on productions hosts. Intern
can only interact with the development servers.
There's an additional server that acts as a router, connecting the VPN users
to an internal network `10.20.0.0/16`. Developers must have access to those
internal resources.
Each user have at least a device connected to the network and we have some
servers.
- database.prod
- database.dev
- app-server1.prod
- app-server1.dev
- billing.internal
- router.internal
![ACL implementation example](../images/headscale-acl-network.png)
When [registering the servers](../usage/getting-started.md#register-a-node) we
will need to add the flag `--advertise-tags=tag:<tag1>,tag:<tag2>`, and the user
that is registering the server should be allowed to do it. Since anyone can add
tags to a server they can register, the check of the tags is done on headscale
server and only valid tags are applied. A tag is valid if the user that is
registering it is allowed to do it.
Here are the ACL's to implement the same permissions as above:
```json title="acl.json"
{
// groups are collections of users having a common scope. A user can be in multiple groups
// groups cannot be composed of groups
"groups": {
"group:boss": ["boss@"],
"group:dev": ["dev1@", "dev2@"],
"group:admin": ["admin1@"],
"group:intern": ["intern1@"]
},
// tagOwners in tailscale is an association between a TAG and the people allowed to set this TAG on a server.
// This is documented [here](https://tailscale.com/kb/1068/acl-tags#defining-a-tag)
// and explained [here](https://tailscale.com/blog/rbac-like-it-was-meant-to-be/)
"tagOwners": {
// the administrators can add servers in production
"tag:prod-databases": ["group:admin"],
"tag:prod-app-servers": ["group:admin"],
// the boss can tag any server as internal
"tag:internal": ["group:boss"],
// dev can add servers for dev purposes as well as admins
"tag:dev-databases": ["group:admin", "group:dev"],
"tag:dev-app-servers": ["group:admin", "group:dev"]
// interns cannot add servers
},
// hosts should be defined using its IP addresses and a subnet mask.
// to define a single host, use a /32 mask. You cannot use DNS entries here,
// as they're prone to be hijacked by replacing their IP addresses.
// see https://github.com/tailscale/tailscale/issues/3800 for more information.
"hosts": {
"postgresql.internal": "10.20.0.2/32",
"webservers.internal": "10.20.10.1/29"
},
"acls": [
// boss have access to all servers
{
"action": "accept",
"src": ["group:boss"],
"dst": [
"tag:prod-databases:*",
"tag:prod-app-servers:*",
"tag:internal:*",
"tag:dev-databases:*",
"tag:dev-app-servers:*"
]
},
// admin have only access to administrative ports of the servers, in tcp/22
{
"action": "accept",
"src": ["group:admin"],
"proto": "tcp",
"dst": [
"tag:prod-databases:22",
"tag:prod-app-servers:22",
"tag:internal:22",
"tag:dev-databases:22",
"tag:dev-app-servers:22"
]
},
// we also allow admin to ping the servers
{
"action": "accept",
"src": ["group:admin"],
"proto": "icmp",
"dst": [
"tag:prod-databases:*",
"tag:prod-app-servers:*",
"tag:internal:*",
"tag:dev-databases:*",
"tag:dev-app-servers:*"
]
},
// developers have access to databases servers and application servers on all ports
// they can only view the applications servers in prod and have no access to databases servers in production
{
"action": "accept",
"src": ["group:dev"],
"dst": [
"tag:dev-databases:*",
"tag:dev-app-servers:*",
"tag:prod-app-servers:80,443"
]
},
// developers have access to the internal network through the router.
// the internal network is composed of HTTPS endpoints and Postgresql
// database servers.
{
"action": "accept",
"src": ["group:dev"],
"dst": ["10.20.0.0/16:443,5432"]
},
// servers should be able to talk to database in tcp/5432. Database should not be able to initiate connections to
// applications servers
{
"action": "accept",
"src": ["tag:dev-app-servers"],
"proto": "tcp",
"dst": ["tag:dev-databases:5432"]
},
{
"action": "accept",
"src": ["tag:prod-app-servers"],
"dst": ["tag:prod-databases:5432"]
},
// interns have access to dev-app-servers only in reading mode
{
"action": "accept",
"src": ["group:intern"],
"dst": ["tag:dev-app-servers:80,443"]
},
// We still have to allow internal users communications since nothing guarantees that each user have
// their own users.
{ "action": "accept", "src": ["boss@"], "dst": ["boss@:*"] },
{ "action": "accept", "src": ["dev1@"], "dst": ["dev1@:*"] },
{ "action": "accept", "src": ["dev2@"], "dst": ["dev2@:*"] },
{ "action": "accept", "src": ["admin1@"], "dst": ["admin1@:*"] },
{ "action": "accept", "src": ["intern1@"], "dst": ["intern1@:*"] }
]
}
```

129
docs/ref/api.md Normal file
View file

@ -0,0 +1,129 @@
# API
Headscale provides a [HTTP REST API](#rest-api) and a [gRPC interface](#grpc) which may be used to integrate a [web
interface](integration/web-ui.md), [remote control Headscale](#setup-remote-control) or provide a base for custom
integration and tooling.
Both interfaces require a valid API key before use. To create an API key, log into your Headscale server and generate
one with the default expiration of 90 days:
```shell
headscale apikeys create
```
Copy the output of the command and save it for later. Please note that you can not retrieve an API key again. If the API
key is lost, expire the old one, and create a new one.
To list the API keys currently associated with the server:
```shell
headscale apikeys list
```
and to expire an API key:
```shell
headscale apikeys expire --prefix <PREFIX>
```
## REST API
- API endpoint: `/api/v1`, e.g. `https://headscale.example.com/api/v1`
- Documentation: `/swagger`, e.g. `https://headscale.example.com/swagger`
- Headscale Version: `/version`, e.g. `https://headscale.example.com/version`
- Authenticate using HTTP Bearer authentication by sending the [API key](#api) with the HTTP `Authorization: Bearer <API_KEY>` header.
Start by [creating an API key](#api) and test it with the examples below. Read the API documentation provided by your
Headscale server at `/swagger` for details.
=== "Get details for all users"
```console
curl -H "Authorization: Bearer <API_KEY>" \
https://headscale.example.com/api/v1/user
```
=== "Get details for user 'bob'"
```console
curl -H "Authorization: Bearer <API_KEY>" \
https://headscale.example.com/api/v1/user?name=bob
```
=== "Register a node"
```console
curl -H "Authorization: Bearer <API_KEY>" \
--json '{"user": "<USER>", "authId": "AUTH_ID>"}' \
https://headscale.example.com/api/v1/auth/register
```
## gRPC
The gRPC interface can be used to control a Headscale instance from a remote machine with the `headscale` binary.
### Prerequisite
- A workstation to run `headscale` (any supported platform, e.g. Linux).
- A Headscale server with gRPC enabled.
- Connections to the gRPC port (default: `50443`) are allowed.
- Remote access requires an encrypted connection via TLS.
- An [API key](#api) to authenticate with the Headscale server.
### Setup remote control
1. Download the [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases). Make
sure to use the same version as on the server.
1. Put the binary somewhere in your `PATH`, e.g. `/usr/local/bin/headscale`
1. Make `headscale` executable: `chmod +x /usr/local/bin/headscale`
1. [Create an API key](#api) on the Headscale server.
1. Provide the connection parameters for the remote Headscale server either via a minimal YAML configuration file or
via environment variables:
=== "Minimal YAML configuration file"
```yaml title="config.yaml"
cli:
address: <HEADSCALE_ADDRESS>:<PORT>
api_key: <API_KEY>
```
=== "Environment variables"
```shell
export HEADSCALE_CLI_ADDRESS="<HEADSCALE_ADDRESS>:<PORT>"
export HEADSCALE_CLI_API_KEY="<API_KEY>"
```
This instructs the `headscale` binary to connect to a remote instance at `<HEADSCALE_ADDRESS>:<PORT>`, instead of
connecting to the local instance.
1. Test the connection by listing all nodes:
```shell
headscale nodes list
```
You should now be able to see a list of your nodes from your workstation, and you can
now control the Headscale server from your workstation.
### Behind a proxy
It's possible to run the gRPC remote endpoint behind a reverse proxy, like Nginx, and have it run on the _same_ port as Headscale.
While this is _not a supported_ feature, an example on how this can be set up on
[NixOS is shown here](https://github.com/kradalby/dotfiles/blob/4489cdbb19cddfbfae82cd70448a38fde5a76711/machines/headscale.oracldn/headscale.nix#L61-L91).
### Troubleshooting
- Make sure you have the _same_ Headscale version on your server and workstation.
- Ensure that connections to the gRPC port are allowed.
- Verify that your TLS certificate is valid and trusted.
- If you don't have access to a trusted certificate (e.g. from Let's Encrypt), either:
- Add your self-signed certificate to the trust store of your OS _or_
- Disable certificate verification by either setting `cli.insecure: true` in the configuration file or by setting
`HEADSCALE_CLI_INSECURE=1` via an environment variable. We do **not** recommend to disable certificate validation.

View file

@ -17,8 +17,8 @@
=== "View on GitHub"
* Development version: <https://github.com/juanfont/headscale/blob/main/config-example.yaml>
* Version {{ headscale.version }}: <https://github.com/juanfont/headscale/blob/v{{ headscale.version }}/config-example.yaml>
- Development version: <https://github.com/juanfont/headscale/blob/main/config-example.yaml>
- Version {{ headscale.version }}: https://github.com/juanfont/headscale/blob/v{{ headscale.version }}/config-example.yaml
=== "Download with `wget`"

View file

@ -3,16 +3,16 @@
Headscale and Tailscale provide debug and introspection capabilities that can be helpful when things don't work as
expected. This page explains some debugging techniques to help pinpoint problems.
Please also have a look at [Tailscale's Troubleshooting guide](https://tailscale.com/kb/1023/troubleshooting). It offers
a many tips and suggestions to troubleshoot common issues.
Please also have a look at [Tailscale's Troubleshooting guide](https://tailscale.com/docs/reference/troubleshooting). It
offers a many tips and suggestions to troubleshoot common issues.
## Tailscale
The Tailscale client itself offers many commands to introspect its state as well as the state of the network:
- [Check local network conditions](https://tailscale.com/kb/1080/cli#netcheck): `tailscale netcheck`
- [Get the client status](https://tailscale.com/kb/1080/cli#status): `tailscale status --json`
- [Get DNS status](https://tailscale.com/kb/1080/cli#dns): `tailscale dns status --all`
- [Check local network conditions](https://tailscale.com/docs/reference/tailscale-cli#netcheck): `tailscale netcheck`
- [Get the client status](https://tailscale.com/docs/reference/tailscale-cli#status): `tailscale status --json`
- [Get DNS status](https://tailscale.com/docs/reference/tailscale-cli#dns): `tailscale dns status --all`
- Client logs: `tailscale debug daemon-logs`
- Client netmap: `tailscale debug netmap`
- Test DERP connection: `tailscale debug derp headscale`
@ -53,17 +53,20 @@ Headscale provides a metrics and debug endpoint. It allows to introspect differe
- Information about the Go runtime, memory usage and statistics
- Connected nodes and pending registrations
- Active ACLs, filters and SSH policy
- Active policy, filters and SSH policy
- Current DERPMap
- Prometheus metrics
!!! warning "Keep the metrics and debug endpoint private"
The listen address and port can be configured with the `metrics_listen_addr` variable in the [configuration
file](./configuration.md). By default it listens on localhost, port 9090.
file](configuration.md). By default it listens on localhost, port 9090.
Keep the metrics and debug endpoint private to your internal network and don't expose it to the Internet.
The metrics and debug interface can be disabled completely by setting `metrics_listen_addr: null` in the
[configuration file](configuration.md).
Query metrics via <http://localhost:9090/metrics> and get an overview of available debug information via
<http://localhost:9090/debug/>. Metrics may be queried from outside localhost but the debug interface is subject to
additional protection despite listening on all interfaces.

View file

@ -1,13 +1,13 @@
# DERP
A [DERP (Designated Encrypted Relay for Packets) server](https://tailscale.com/kb/1232/derp-servers) is mainly used to
relay traffic between two nodes in case a direct connection can't be established. Headscale provides an embedded DERP
server to ensure seamless connectivity between nodes.
A [DERP (Designated Encrypted Relay for Packets) server](https://tailscale.com/docs/reference/derp-servers) is mainly
used to relay traffic between two nodes in case a direct connection can't be established. Headscale provides an embedded
DERP server to ensure seamless connectivity between nodes.
## Configuration
DERP related settings are configured within the `derp` section of the [configuration file](./configuration.md). The
following sections only use a few of the available settings, check the [example configuration](./configuration.md) for
DERP related settings are configured within the `derp` section of the [configuration file](configuration.md). The
following sections only use a few of the available settings, check the [example configuration](configuration.md) for
all available configuration options.
### Enable embedded DERP
@ -31,8 +31,8 @@ traversal. [Check DERP server connectivity](#check-derp-server-connectivity) to
### Remove Tailscale's DERP servers
Once enabled, Headscale's embedded DERP is added to the list of free-to-use [DERP
servers](https://tailscale.com/kb/1232/derp-servers) offered by Tailscale Inc. To only use Headscale's embedded DERP
server, disable the loading of the default DERP map:
servers](https://tailscale.com/docs/reference/derp-servers) offered by Tailscale Inc. To only use Headscale's embedded
DERP server, disable the loading of the default DERP map:
```yaml title="config.yaml" hl_lines="6"
derp:
@ -59,14 +59,14 @@ maps fetched via URL or to offer your own, custom DERP servers to nodes.
=== "Remove specific DERP regions"
The free-to-use [DERP servers](https://tailscale.com/kb/1232/derp-servers) are organized into regions via a region
ID. You can explicitly disable a specific region by setting its region ID to `null`. The following sample
The free-to-use [DERP servers](https://tailscale.com/docs/reference/derp-servers) are organized into regions via a
region ID. You can explicitly disable a specific region by setting its region ID to `null`. The following sample
`derp.yaml` disables the New York DERP region (which has the region ID 1):
```yaml title="derp.yaml"
regions:
1: null
```
```yaml title="derp.yaml"
regions:
1: null
```
Use the following configuration to serve the default DERP map (excluding New York) to nodes:
@ -163,13 +163,12 @@ Any Tailscale client may be used to introspect the DERP map and to check for con
- Check connectivity with the embedded DERP[^1]:`tailscale debug derp headscale`
Additional DERP related metrics and information is available via the [metrics and debug
endpoint](./debug.md#metrics-and-debug-endpoint).
[^1]:
This assumes that the default region code of the [configuration file](./configuration.md) is used.
endpoint](debug.md#metrics-and-debug-endpoint).
## Limitations
- The embedded DERP server can't be used for Tailscale's captive portal checks as it doesn't support the `/generate_204`
endpoint via HTTP on port tcp/80.
- There are no speed or throughput optimisations, the main purpose is to assist in node connectivity.
[^1]: This assumes that the default region code of the [configuration file](configuration.md) is used.

View file

@ -1,19 +1,19 @@
# DNS
Headscale supports [most DNS features](../about/features.md) from Tailscale. DNS related settings can be configured
within `dns` section of the [configuration file](./configuration.md).
within the `dns` section of the [configuration file](configuration.md).
## Setting extra DNS records
Headscale allows to set extra DNS records which are made available via
[MagicDNS](https://tailscale.com/kb/1081/magicdns). Extra DNS records can be configured either via static entries in the
[configuration file](./configuration.md) or from a JSON file that Headscale continuously watches for changes:
[MagicDNS](https://tailscale.com/docs/features/magicdns). Extra DNS records can be configured either via static entries
in the [configuration file](configuration.md) or from a JSON file that Headscale continuously watches for changes:
- Use the `dns.extra_records` option in the [configuration file](./configuration.md) for entries that are static and
- Use the `dns.extra_records` option in the [configuration file](configuration.md) for entries that are static and
don't change while Headscale is running. Those entries are processed when Headscale is starting up and changes to the
configuration require a restart of Headscale.
- For dynamic DNS records that may be added, updated or removed while Headscale is running or DNS records that are
generated by scripts the option `dns.extra_records_path` in the [configuration file](./configuration.md) is useful.
generated by scripts the option `dns.extra_records_path` in the [configuration file](configuration.md) is useful.
Set it to the absolute path of the JSON file containing DNS records and Headscale processes this file as it detects
changes.
@ -25,7 +25,7 @@ hostname and port combination "http://hostname-in-magic-dns.myvpn.example.com:30
Currently, [only A and AAAA records are processed by Tailscale](https://github.com/tailscale/tailscale/blob/v1.86.5/ipn/ipnlocal/node_backend.go#L662).
1. Configure extra DNS records using one of the available configuration options:
1. Configure extra DNS records using one of the available configuration options:
=== "Static entries, via `dns.extra_records`"
@ -66,12 +66,12 @@ hostname and port combination "http://hostname-in-magic-dns.myvpn.example.com:30
!!! tip "Good to know"
* The `dns.extra_records_path` option in the [configuration file](./configuration.md) needs to reference the
- The `dns.extra_records_path` option in the [configuration file](configuration.md) needs to reference the
JSON file containing extra DNS records.
* Be sure to "sort keys" and produce a stable output in case you generate the JSON file with a script.
- Be sure to "sort keys" and produce a stable output in case you generate the JSON file with a script.
Headscale uses a checksum to detect changes to the file and a stable output avoids unnecessary processing.
1. Verify that DNS records are properly set using the DNS querying tool of your choice:
1. Verify that DNS records are properly set using the DNS querying tool of your choice:
=== "Query with dig"
@ -87,7 +87,7 @@ hostname and port combination "http://hostname-in-magic-dns.myvpn.example.com:30
100.64.0.3
```
1. Optional: Setup the reverse proxy
1. Optional: Setup the reverse proxy
The motivating example here was to be able to access internal monitoring services on the same host without
specifying a port, depicted as NGINX configuration snippet:

View file

@ -31,6 +31,30 @@ tls_cert_path: ""
tls_key_path: ""
```
### Trusted proxies
Headscale ignores `True-Client-IP`, `X-Real-IP` and `X-Forwarded-For`
unless the request's TCP peer matches `trusted_proxies`. Set this to
the CIDR(s) your reverse proxy connects from so the real client IP
appears in access logs:
```yaml title="config.yaml"
trusted_proxies:
- 127.0.0.1/32
- ::1/128
```
The reverse proxy must also strip any client-supplied
`True-Client-IP` / `X-Real-IP` / `X-Forwarded-For` on inbound requests
and set its own values. nginx's `$proxy_add_x_forwarded_for` only
appends to whatever the client sent — pair it with
`proxy_set_header X-Real-IP $remote_addr;` and clear the inbound XFF
yourself if your nginx version does not do so.
Leaving `trusted_proxies` empty when there is no proxy in front is
safe: the headers are dropped from every request and the access log
shows the directly-connecting TCP peer.
## nginx
The following example configuration can be used in your nginx setup, substituting values as necessary. `<IP:PORT>` should be the IP address and port where headscale is running. In most cases, this will be `http://localhost:8080`.

View file

@ -7,10 +7,16 @@
This page collects third-party tools, client libraries, and scripts related to headscale.
| Name | Repository Link | Description |
| --------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------- |
| tailscale-manager | [Github](https://github.com/singlestore-labs/tailscale-manager) | Dynamically manage Tailscale route advertisements |
| headscalebacktosqlite | [Github](https://github.com/bigbozza/headscalebacktosqlite) | Migrate headscale from PostgreSQL back to SQLite |
| headscale-pf | [Github](https://github.com/YouSysAdmin/headscale-pf) | Populates user groups based on user groups in Jumpcloud or Authentik |
| headscale-client-go | [Github](https://github.com/hibare/headscale-client-go) | A Go client implementation for the Headscale HTTP API. |
| headscale-zabbix | [Github](https://github.com/dblanque/headscale-zabbix) | A Zabbix Monitoring Template for the Headscale Service. |
- [headscale-operator](https://github.com/infradohq/headscale-operator) - Headscale Kubernetes Operator
- [tailscale-manager](https://github.com/singlestore-labs/tailscale-manager) - Dynamically manage Tailscale route
advertisements
- [headscalebacktosqlite](https://github.com/bigbozza/headscalebacktosqlite) - Migrate headscale from PostgreSQL back to
SQLite
- [headscale-pf](https://github.com/YouSysAdmin/headscale-pf) - Populates user groups based on user groups in Jumpcloud
or Authentik
- [headscale-client-go](https://github.com/hibare/headscale-client-go) - A Go client implementation for the Headscale
HTTP API.
- [headscale-zabbix](https://github.com/dblanque/headscale-zabbix) - A Zabbix Monitoring Template for the Headscale
Service.
- [tailscale-exporter](https://github.com/adinhodovic/tailscale-exporter) - A Prometheus exporter for Headscale that
provides network-level metrics using the Headscale API.

View file

@ -7,14 +7,21 @@
Headscale doesn't provide a built-in web interface but users may pick one from the available options.
| Name | Repository Link | Description |
| ---------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| headscale-ui | [Github](https://github.com/gurucomputing/headscale-ui) | A web frontend for the headscale Tailscale-compatible coordination server |
| HeadscaleUi | [GitHub](https://github.com/simcu/headscale-ui) | A static headscale admin ui, no backend environment required |
| Headplane | [GitHub](https://github.com/tale/headplane) | An advanced Tailscale inspired frontend for headscale |
| headscale-admin | [Github](https://github.com/GoodiesHQ/headscale-admin) | Headscale-Admin is meant to be a simple, modern web interface for headscale |
| ouroboros | [Github](https://github.com/yellowsink/ouroboros) | Ouroboros is designed for users to manage their own devices, rather than for admins |
| unraid-headscale-admin | [Github](https://github.com/ich777/unraid-headscale-admin) | A simple headscale admin UI for Unraid, it offers Local (`docker exec`) and API Mode |
| headscale-console | [Github](https://github.com/rickli-cloud/headscale-console) | WebAssembly-based client supporting SSH, VNC and RDP with optional self-service capabilities |
- [headscale-ui](https://github.com/gurucomputing/headscale-ui) - A web frontend for the headscale Tailscale-compatible
coordination server
- [HeadscaleUi](https://github.com/simcu/headscale-ui) - A static headscale admin ui, no backend environment required
- [Headplane](https://github.com/tale/headplane) - An advanced Tailscale inspired frontend for headscale
- [headscale-admin](https://github.com/GoodiesHQ/headscale-admin) - Headscale-Admin is meant to be a simple, modern web
interface for headscale
- [ouroboros](https://github.com/yellowsink/ouroboros) - Ouroboros is designed for users to manage their own devices,
rather than for admins
- [unraid-headscale-admin](https://github.com/ich777/unraid-headscale-admin) - A simple headscale admin UI for Unraid,
it offers Local (`docker exec`) and API Mode
- [headscale-console](https://github.com/rickli-cloud/headscale-console) - WebAssembly-based client supporting SSH, VNC
and RDP with optional self-service capabilities
- [headscale-piying](https://github.com/wszgrcy/headscale-piying) - headscale web ui, support visual ACL configuration
- [HeadControl](https://github.com/ahmadzip/HeadControl) - Minimal Headscale admin dashboard, built with Go and HTMX
- [Headscale Manager](https://github.com/hkdone/headscalemanager) - Headscale UI for Android
- [Headscale UI](https://github.com/MunMunMiao/headscale-ui) - Headscale UI online and Self-hosting
You can ask for support on our [Discord server](https://discord.gg/c84AZQhmpx) in the "web-interfaces" channel.

View file

@ -40,9 +40,9 @@ A basic configuration connects Headscale to an identity provider and typically r
=== "Identity provider"
* Create a new confidential client (`Client ID`, `Client secret`)
* Add Headscale's OIDC callback URL as valid redirect URL: `https://headscale.example.com/oidc/callback`
* Configure additional parameters to improve user experience such as: name, description, logo, …
- Create a new confidential client (`Client ID`, `Client secret`)
- Add Headscale's OIDC callback URL as valid redirect URL: `https://headscale.example.com/oidc/callback`
- Configure additional parameters to improve user experience such as: name, description, logo, …
### Enable PKCE (recommended)
@ -63,8 +63,8 @@ recommended and needs to be configured for Headscale and the identity provider a
=== "Identity provider"
* Enable PKCE for the headscale client
* Set the PKCE challenge method to "S256"
- Enable PKCE for the headscale client
- Set the PKCE challenge method to "S256"
### Authorize users with filters
@ -75,10 +75,11 @@ are configured, a user needs to pass all of them.
=== "Allowed domains"
* Check the email domain of each authenticating user against the list of allowed domains and only authorize users
- Check the email domain of each authenticating user against the list of allowed domains and only authorize users
whose email domain matches `example.com`.
* Access allowed: `alice@example.com`
* Access denied: `bob@example.net`
- A verified email address is required [unless email verification is disabled](#control-email-verification).
- Access allowed: `alice@example.com`
- Access denied: `bob@example.net`
```yaml hl_lines="5-6"
oidc:
@ -91,10 +92,11 @@ are configured, a user needs to pass all of them.
=== "Allowed users/emails"
* Check the email address of each authenticating user against the list of allowed email addresses and only authorize
- Check the email address of each authenticating user against the list of allowed email addresses and only authorize
users whose email is part of the `allowed_users` list.
* Access allowed: `alice@example.com`, `bob@example.net`
* Access denied: `mallory@example.net`
- A verified email address is required [unless email verification is disabled](#control-email-verification).
- Access allowed: `alice@example.com`, `bob@example.net`
- Access denied: `mallory@example.net`
```yaml hl_lines="5-7"
oidc:
@ -108,10 +110,10 @@ are configured, a user needs to pass all of them.
=== "Allowed groups"
* Use the OIDC `groups` claim of each authenticating user to get their group membership and only authorize users
- Use the OIDC `groups` claim of each authenticating user to get their group membership and only authorize users
which are members in at least one of the referenced groups.
* Access allowed: users in the `headscale_users` group
* Access denied: users without groups, users with other groups
- Access allowed: users in the `headscale_users` group
- Access denied: users without groups, users with other groups
```yaml hl_lines="5-7"
oidc:
@ -123,19 +125,32 @@ are configured, a user needs to pass all of them.
- "headscale_users"
```
### Control email verification
Headscale uses the `email` claim from the identity provider to synchronize the email address to its user profile. By
default, a user's email address is only synchronized when the identity provider reports the email address as verified
via the `email_verified: true` claim.
Unverified emails may be allowed in case an identity provider does not send the `email_verified` claim or email
verification is not required. In that case, a user's email address is always synchronized to the user profile.
```yaml hl_lines="5"
oidc:
issuer: "https://sso.example.com"
client_id: "headscale"
client_secret: "generated-secret"
email_verified_required: false
```
### Customize node expiration
The node expiration is the amount of time a node is authenticated with OpenID Connect until it expires and needs to
reauthenticate. The default node expiration is 180 days. This can either be customized or set to the expiration from the
Access Token.
reauthenticate. The default node expiration can be configured via the top-level `node.expiry` setting.
=== "Customize node expiration"
```yaml hl_lines="5"
oidc:
issuer: "https://sso.example.com"
client_id: "headscale"
client_secret: "generated-secret"
```yaml hl_lines="2"
node:
expiry: 30d # Use 0 to disable node expiration
```
@ -144,7 +159,6 @@ Access Token.
Please keep in mind that the Access Token is typically a short-lived token that expires within a few minutes. You
will have to configure token expiration in your identity provider to avoid frequent re-authentication.
```yaml hl_lines="5"
oidc:
issuer: "https://sso.example.com"
@ -156,6 +170,7 @@ Access Token.
!!! tip "Expire a node and force re-authentication"
A node can be expired immediately via:
```console
headscale node expire -i <NODE_ID>
```
@ -166,13 +181,16 @@ You may refer to users in the Headscale policy via:
- Email address
- Username
- Provider identifier (only available in the database or from your identity provider)
- Provider identifier (this value is currently only available from the [API](api.md), database or directly from your
identity provider)
!!! note "A user identifier in the policy must contain a single `@`"
The Headscale policy requires a single `@` to reference a user. If the username or provider identifier doesn't
already contain a single `@`, it needs to be appended at the end. For example: the username `ssmith` has to be
written as `ssmith@` to be correctly identified as user within the policy.
already contain a single `@`, it needs to be appended at the end. For example: the Headscale username `ssmith` has
to be written as `ssmith@` to be correctly identified as user within the policy.
Ensure that the Headscale username itself does not end with `@`.
!!! warning "Email address or username might be updated by users"
@ -181,6 +199,34 @@ You may refer to users in the Headscale policy via:
consequences for Headscale where a policy might no longer work or a user might obtain more access by hijacking an
existing username or email address.
!!! tip "Howto use the provider identifier in the policy"
The provider identifier uniquely identifies an OIDC user and a well-behaving identity provider guarantees that this
value never changes for a particular user. It is usually an opaque and long string and its value is currently only
available from the [API](api.md), database or directly from your identity provider).
Use the [API](api.md) with the `/api/v1/user` endpoint to fetch the provider identifier (`providerId`). The value
(be sure to append an `@` in case the provider identifier doesn't already contain an `@` somewhere) can be used
directly to reference a user in the policy. To improve readability of the policy, one may use the `groups` section
as an alias:
```json
{
"groups": {
"group:alice": [
"https://sso.example.com/oauth2/openid/59ac9125-c31b-46c5-814e-06242908cf57@"
]
},
"grants": [
{
"src": ["group:alice"],
"dst": ["*"],
"ip": ["*"]
}
]
}
```
## Supported OIDC claims
Headscale uses [the standard OIDC claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) to
@ -189,7 +235,7 @@ endpoint.
| Headscale profile | OIDC claim | Notes / examples |
| ------------------- | -------------------- | ------------------------------------------------------------------------------------------------- |
| email address | `email` | Only used when `email_verified: true` |
| email address | `email` | Only verified emails are synchronized, unless `email_verified_required: false` is configured |
| display name | `name` | eg: `Sam Smith` |
| username | `preferred_username` | Depends on identity provider, eg: `ssmith`, `ssmith@idp.example.com`, `\\example.com\ssmith` |
| profile picture | `picture` | URL to a profile picture or avatar |
@ -251,13 +297,11 @@ For detailed Headplane OIDC configuration, see the [Headplane documentation](htt
- Support for OpenID Connect aims to be generic and vendor independent. It offers only limited support for quirks of
specific identity providers.
- OIDC groups cannot be directly used in Headscale ACLs (use external integrations for role-based access control).
- OIDC groups cannot be used in policy rules directly (use external integrations for role-based access control).
- The username provided by the identity provider needs to adhere to this pattern:
- The username must be at least two characters long.
- It must only contain letters, digits, hyphens, dots, underscores, and up to a single `@`.
- The username must start with a letter.
- A user's email address is only synchronized to the local user profile when the identity provider marks the email
address as verified (`email_verified: true`).
Please see the [GitHub label "OIDC"](https://github.com/juanfont/headscale/labels/OIDC) for OIDC related issues.
@ -284,14 +328,15 @@ Authelia is fully supported by Headscale.
### Authentik
- Authentik is fully supported by Headscale.
- [Headscale does not JSON Web Encryption](https://github.com/juanfont/headscale/issues/2446). Leave the field
- [Headscale does not support JSON Web Encryption](https://github.com/juanfont/headscale/issues/2446). Leave the field
`Encryption Key` in the providers section unset.
- See Authentik's [Integrate with Headscale](https://integrations.goauthentik.io/networking/headscale/)
### Google OAuth
!!! warning "No username due to missing preferred_username"
!!! warning "No username due to missing preferred_username claim"
Google OAuth does not send the `preferred_username` claim when the scope `profile` is requested. The username in
Google OAuth does not send the `preferred_username` claim when the `profile` scope is requested. The username in
Headscale will be blank/not set.
In order to integrate Headscale with Google, you'll need to have a [Google Cloud
@ -307,22 +352,30 @@ Console.
#### Steps
1. Go to [Google Console](https://console.cloud.google.com) and login or create an account if you don't have one.
2. Create a project (if you don't already have one).
3. On the left hand menu, go to `APIs and services` -> `Credentials`
4. Click `Create Credentials` -> `OAuth client ID`
5. Under `Application Type`, choose `Web Application`
6. For `Name`, enter whatever you like
7. Under `Authorised redirect URIs`, add Headscale's OIDC callback URL: `https://headscale.example.com/oidc/callback`
8. Click `Save` at the bottom of the form
9. Take note of the `Client ID` and `Client secret`, you can also download it for reference if you need it.
10. [Configure Headscale following the "Basic configuration" steps](#basic-configuration). The issuer URL for Google
OAuth is: `https://accounts.google.com`.
1. Create a project (if you don't already have one).
1. On the left hand menu, go to `APIs and services` -> `Credentials`
1. Click `Create Credentials` -> `OAuth client ID`
1. Under `Application Type`, choose `Web Application`
1. For `Name`, enter whatever you like
1. Under `Authorised redirect URIs`, add Headscale's OIDC callback URL: `https://headscale.example.com/oidc/callback`
1. Click `Save` at the bottom of the form
1. Take note of the `Client ID` and `Client secret`, you can also download it for reference if you need it.
1. [Configure Headscale following the "Basic configuration" steps](#basic-configuration). The issuer URL for Google
OAuth is: `https://accounts.google.com`.
### Kanidm
- Kanidm is fully supported by Headscale.
- Groups for the [allowed groups filter](#authorize-users-with-filters) need to be specified with their full SPN, for
example: `headscale_users@sso.example.com`.
- Kanidm sends the full SPN (`alice@sso.example.com`) as `preferred_username` by default. Headscale stores this value as
username which might be confusing as the username and email fields now contain values that look like an email address.
[Kanidm can be configured to send the short username as `preferred_username` attribute
instead](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#short-names):
```console
kanidm system oauth2 prefer-short-username <client name>
```
Once configured, the short username in Headscale will be `alice` and can be referred to as `alice@` in the policy.
### Keycloak
@ -356,5 +409,19 @@ Entra ID is: `https://login.microsoftonline.com/<tenant-UUID>/v2.0`. The followi
- `domain_hint: example.com` to use your own domain
- `prompt: select_account` to force an account picker during login
Groups for the [allowed groups filter](#authorize-users-with-filters) need to be specified with their group ID instead
When using Microsoft Entra ID together with the [allowed groups filter](#authorize-users-with-filters), configure the
Headscale OIDC scope without the `groups` claim, for example:
```yaml
oidc:
scope: ["openid", "profile", "email"]
```
Groups for the [allowed groups filter](#authorize-users-with-filters) need to be specified with their group ID(UUID) instead
of the group name.
## Switching OIDC providers
Headscale only supports a single OIDC provider in its configuration, but it does store the provider identifier of each user. When switching providers, this might lead to issues with existing users: all user details (name, email, groups) might be identical with the new provider, but the identifier will differ. Headscale will be unable to create a new user as the name and email will already be in use for the existing users.
At this time, you will need to manually update the `provider_identifier` column in the `users` table for each user with the appropriate value for the new provider. The identifier is built from the `iss` and `sub` claims of the OIDC ID token, for example `https://id.example.com/12340987`.

249
docs/ref/policy.md Normal file
View file

@ -0,0 +1,249 @@
# Policy
Headscale implements a large portion of Tailscale's [policy
features](https://tailscale.com/docs/features/tailnet-policy-file), most notably access control based on
[ACLs](https://tailscale.com/docs/features/access-control/acls) and
[Grants](https://tailscale.com/docs/features/access-control/grants) or [Tailscale
SSH](https://tailscale.com/docs/features/tailscale-ssh). See [limitations](#limitations) to learn about missing features
and notable implementation differences between Headscale and Tailscale.
Headscale uses the same [huJSON](https://github.com/tailscale/hujson) based file format as Tailscale. By default, no
policy is loaded which means that Headscale allows all traffic between nodes. To start using a policy file[^1], specify
its path in the `policy.path` key in the [configuration file](configuration.md).
Headscale needs to be reloaded to pick up changes to the policy file. Either reload Headscale via its systemd service
(`sudo systemctl reload headscale`) or by sending a SIGHUP signal (`sudo kill -HUP $(pidof headscale)`) to the main
process. Headscale logs the result of policy processing after each reload.
Please have a look at Tailscale's policy related documentation to learn more:
- [Tailscale policy file](https://tailscale.com/docs/features/tailnet-policy-file): A description of supported sections
within the policy file along with links to syntax references for each section.
- [ACLs](https://tailscale.com/docs/features/access-control/acls): How to configure access control using ACLs.
- [Grants](https://tailscale.com/docs/features/access-control/grants): Introduction to Grants with links to [syntax
reference](https://tailscale.com/docs/reference/syntax/grants),
[examples](https://tailscale.com/docs/reference/examples/grants) and a [migration guide from ACLs to
Grants](https://tailscale.com/docs/reference/migrate-acls-grants).
## Getting started
Headscale supports both [ACLs](https://tailscale.com/docs/features/access-control/acls) and
[Grants](https://tailscale.com/docs/features/access-control/grants) to write an access control policy. We recommend the
use of Grants since ACLs are considered legacy and will not receive new features by Tailscale.
### Allow All
If you define a policy file but completely omit the `"acls"` or `"grants"` section, Headscale will default to an [allow
all](https://tailscale.com/docs/reference/examples/acls#allow-all-default-acl) policy. This means all devices connected
to your tailnet will be able to communicate freely with each other.
```json title="policy.json"
{}
```
### Deny All
To [prevent all communication within your tailnet](https://tailscale.com/docs/reference/examples/acls#deny-all), you can
include an empty array for the `"grants"` section in your policy file.
```json title="policy.json"
{
"grants": []
}
```
### More examples
- See our documentation on [subnet routers](routes.md#subnet-router) and [exit nodes](routes.md#exit-node) to learn how
to restrict their use or how to automatically approve them.
- The Tailscale documentation provides a large collection of configuration examples:
- [ACL examples](https://tailscale.com/docs/reference/examples/acls)
- [Grants examples](https://tailscale.com/docs/reference/examples/grants)
- [SSH configuration](https://tailscale.com/docs/features/tailscale-ssh#configure-tailscale-ssh)
- [Define a tag](https://tailscale.com/docs/features/tags#define-a-tag)
______________________________________________________________________
## Limitations
- [Device postures](https://tailscale.com/docs/features/device-posture) and the related sections such as `postures` or
`srcPosture` aren't supported.
- [IP sets](https://tailscale.com/docs/features/tailnet-policy-file/ip-sets) aren't supported.
- A subset of [Autogroups](#autogroups) are available.
## Autogroups
Headscale supports several [Autogroups](https://tailscale.com/docs/reference/targets-and-selectors#autogroups) that
automatically include users, destinations, or devices with specific properties. Autogroups provide a convenient way to
write policy rules without manually listing individual users or devices.
### [`autogroup:internet`](https://tailscale.com/docs/reference/targets-and-selectors#autogroupinternet)
Allows access to the internet through [exit nodes](routes.md#exit-node). Can only be used in policy destinations.
```json title="policy.json"
{
"grants": [
{
"src": ["alice@"],
"dst": ["autogroup:internet"],
"ip": ["*"]
}
]
}
```
### [`autogroup:member`](https://tailscale.com/docs/reference/targets-and-selectors#autogrouprole)
Includes all [personal (untagged) devices](registration.md/#identity-model).
```json title="policy.json"
{
"grants": [
{
"src": ["autogroup:member"],
"dst": ["tag:prod-app-servers"],
"ip": ["80,443"]
}
]
}
```
### [`autogroup:tagged`](https://tailscale.com/docs/reference/targets-and-selectors#autogrouptagged)
Includes all devices that [have at least one tag](registration.md/#identity-model).
```json title="policy.json"
{
"grants": [
{
"src": ["autogroup:tagged"],
"dst": ["tag:monitoring"],
"ip": ["9090"]
}
]
}
```
### [`autogroup:self`](https://tailscale.com/docs/reference/targets-and-selectors#autogroupself)
Includes devices where the same user is authenticated on both the source and destination. Does not include tagged
devices. Can only be used in policy destinations.
```json title="policy.json"
{
"grants": [
{
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"ip": ["*"]
}
]
}
```
!!! warning "The current implementation of `autogroup:self` is inefficient"
Using `autogroup:self` may cause performance degradation on the Headscale coordinator server in large deployments,
as filter rules must be compiled per-node rather than globally and the current implementation is not very efficient.
If you experience performance issues, consider using more specific policy rules or limiting the use of
`autogroup:self`.
```json title="policy.json"
{
"grants": [
// The following rules allow internal users to communicate with their
// own nodes in case autogroup:self is causing performance issues.
{
"src": ["boss@"],
"dst": ["boss@"],
"ip": "*"
},
{
"src": ["dev1@"],
"dst": ["dev1@"],
"ip": "*"
},
{
"src": ["intern1@"],
"dst": ["intern1@"],
"ip": "*"
}
]
}
```
### [`autogroup:nonroot`](https://tailscale.com/docs/reference/targets-and-selectors#other-built-in-targets)
Used in Tailscale SSH rules to allow access to any user except root. Can only be used in the `users` field of SSH rules.
```json title="policy.json"
{
"ssh": [
{
"action": "accept",
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot"]
}
]
}
```
### [`autogroup:danger-all`](https://tailscale.com/docs/reference/targets-and-selectors#autogroupdanger-all)
This autogroup resolves to all IP addresses (`0.0.0.0/0` and `::/0`) which also includes all IP addresses outside the
standard Tailscale IP ranges. This autogroup can only be used as source.
## Node Attributes
[Node attributes](https://tailscale.com/docs/reference/syntax/policy-file#node-attributes) allow for device-specific
configuration and attributes. At least the following node attributes are currently supported by Headscale[^2]:
- `drive:access`, `drive:share`: [Taildrive support](https://tailscale.com/docs/features/taildrive).
- `nextdns:<profile>`, `nextdns:no-device-info`: [NextDNS integration](https://tailscale.com/docs/integrations/nextdns).
Be sure to set NextDNS as global resolver in the [configuration](configuration.md).
- `magicdns-aaaa`: Respond to AAAA queries on the local [MagicDNS](https://tailscale.com/docs/features/magicdns)
resolver at 100.100.100.100.
- `disable-ipv4`: Selectively disable IPv4 for specfic nodes. This is may be useful to workaround [CGNat
conflicts](https://tailscale.com/docs/reference/troubleshooting/network-configuration/cgnat-conflicts).
- `randomize-client-port`: Allocate a [random port for WireGuard
traffic](https://tailscale.com/docs/reference/syntax/policy-file#randomizeclientport) instead of the static default
port 41641.
- `disable-captive-portal-detection`: [Disable automatic captive portal
detection](https://tailscale.com/docs/integrations/captive-portals#disable-captive-portal-detection).
```json title="policy.json"
{
"nodeAttrs": [
{
// Enable MagicDNS AAAA records for all nodes
"target": ["*"]
"attr": ["magicdns-aaaa"]
}
]
}
```
## Network-wide policy options
The following options are applied for the entire tailnet. Consider [node attributes](#node-attributes) for a more
fine-grained configuration instead.
- `randomizeClientPort`: Allocate a [random port for WireGuard
traffic](https://tailscale.com/docs/reference/syntax/policy-file#randomizeclientport) instead of the static default
port 41641.
```json title="policy.json"
{
// Use a random WireGuard port for the entire tailnet
"randomizeClientPort": true
}
```
[^1]: Headscale also allows to store the policy in the database. This is typically only required in case a [web
interface](integration/web-ui.md) is used.
[^2]: Other key-only node attributes can be used as well. Find them in the client source code with `grep -E '^\s+NodeAttr\w+' tailcfg/tailcfg.go` or by using [GitHub code search (requires
login)](https://github.com/search?q=repo%3Atailscale%2Ftailscale%20language%3Ago%20path%3Atailcfg%2Ftailcfg.go%20symbol%3A%2FNodeAttr%5Cw%2B%2F&type=code).

144
docs/ref/registration.md Normal file
View file

@ -0,0 +1,144 @@
# Registration methods
Headscale supports multiple ways to register a node. The preferred registration method depends on the identity of a node
and your use case.
## Identity model
Tailscale's identity model distinguishes between personal and tagged nodes:
- A personal node (or user-owned node) is owned by a human and typically refers to end-user devices such as laptops,
workstations or mobile phones. End-user devices are managed by a single user.
- A tagged node (or service-based node or non-human node) provides services to the network. Common examples include web-
and database servers. Those nodes are typically managed by a team of users. Some additional restrictions apply for
tagged nodes, e.g. a tagged node is not allowed to [Tailscale SSH](https://tailscale.com/docs/features/tailscale-ssh)
into a personal node.
Headscale implements Tailscale's identity model and distinguishes between personal and tagged nodes where a personal
node is owned by a Headscale user and a tagged node is owned by a tag. Tagged devices are grouped under the special user
`tagged-devices`.
## Registration methods
There are two main ways to register new nodes, [web authentication](#web-authentication) and [registration with a pre
authenticated key](#pre-authenticated-key). Both methods can be used to register personal and tagged nodes.
### Web authentication
Web authentication is the default method to register a new node. It's interactive, where the client initiates the
registration and the Headscale administrator needs to approve the new node before it is allowed to join the network. A
node can be approved with:
- Headscale CLI (described in this documentation)
- [Headscale API](api.md)
- Or delegated to an identity provider via [OpenID Connect](oidc.md)
Web authentication relies on the presence of a Headscale user. Use the `headscale users` command to create a new
user[^1]:
```console
headscale users create <USER>
```
=== "Personal devices"
Run `tailscale up` to login your personal device:
```console
tailscale up --login-server <YOUR_HEADSCALE_URL>
```
Usually, a browser window with further instructions is opened. This page explains how to complete the registration
on your Headscale server and it also prints the Auth ID required to approve the node:
```console
headscale auth register --user <USER> --auth-id <AUTH_ID>
```
Congrations, the registration of your personal node is complete and it should be listed as "online" in the output of
`headscale nodes list`. The "User" column displays `<USER>` as the owner of the node.
=== "Tagged devices"
Your Headscale user needs to be authorized to register tagged devices. This authorization is specified in the
[`tagOwners`](https://tailscale.com/docs/reference/syntax/policy-file#tag-owners) section of the
[policy](policy.md). A simple example looks like this:
```json title="The user alice can register nodes tagged with tag:server"
{
"tagOwners": {
"tag:server": ["alice@"]
},
// more rules
}
```
Run `tailscale up` and provide at least one tag to login a tagged device:
```console
tailscale up --login-server <YOUR_HEADSCALE_URL> --advertise-tags tag:<TAG>
```
Usually, a browser window with further instructions is opened. This page explains how to complete the registration
on your Headscale server and it also prints the Auth ID required to approve the node:
```console
headscale auth register --user <USER> --auth-id <AUTH_ID>
```
Headscale checks that `<USER>` is allowed to register a node with the specified tag(s) and then transfers ownership
of the new node to the special user `tagged-devices`. The registration of a tagged node is complete and it should be
listed as "online" in the output of `headscale nodes list`. The "User" column displays `tagged-devices` as the owner
of the node. See the "Tags" column for the list of assigned tags.
### Pre authenticated key
Registration with a pre authenticated key (or auth key) is a non-interactive way to register a new node. The Headscale
administrator creates a preauthkey upfront and this preauthkey can then be used to register a node non-interactively.
Its best suited for automation.
=== "Personal devices"
A personal node is always assigned to a Headscale user. Use the `headscale users` command to create a new user[^1]:
```console
headscale users create <USER>
```
Use the `headscale user list` command to learn its `<USER_ID>` and create a new pre authenticated key for your user:
```console
headscale preauthkeys create --user <USER_ID>
```
The above prints a pre authenticated key with the default settings (can be used once and is valid for one hour). Use
this auth key to register a node non-interactively:
```console
tailscale up --login-server <YOUR_HEADSCALE_URL> --authkey <YOUR_AUTH_KEY>
```
Congrations, the registration of your personal node is complete and it should be listed as "online" in the output of
`headscale nodes list`. The "User" column displays `<USER>` as the owner of the node.
=== "Tagged devices"
Create a new pre authenticated key and provide at least one tag:
```console
headscale preauthkeys create --tags tag:<TAG>
```
The above prints a pre authenticated key with the default settings (can be used once and is valid for one hour). Use
this auth key to register a node non-interactively. You don't need to provide the `--advertise-tags` parameter as
the tags are automatically read from the pre authenticated key:
```console
tailscale up --login-server <YOUR_HEADSCALE_URL> --authkey <YOUR_AUTH_KEY>
```
The registration of a tagged node is complete and it should be listed as "online" in the output of
`headscale nodes list`. The "User" column displays `tagged-devices` as the owner of the node. See the "Tags" column for the list of
assigned tags.
[^1]: [Ensure that the Headscale username does not end with `@`.](oidc.md#reference-a-user-in-the-policy)

View file

@ -1,105 +0,0 @@
# Controlling headscale with remote CLI
This documentation has the goal of showing a user how-to control a headscale instance
from a remote machine with the `headscale` command line binary.
## Prerequisite
- A workstation to run `headscale` (any supported platform, e.g. Linux).
- A headscale server with gRPC enabled.
- Connections to the gRPC port (default: `50443`) are allowed.
- Remote access requires an encrypted connection via TLS.
- An API key to authenticate with the headscale server.
## Create an API key
We need to create an API key to authenticate with the remote headscale server when using it from our workstation.
To create an API key, log into your headscale server and generate a key:
```shell
headscale apikeys create --expiration 90d
```
Copy the output of the command and save it for later. Please note that you can not retrieve a key again,
if the key is lost, expire the old one, and create a new key.
To list the keys currently associated with the server:
```shell
headscale apikeys list
```
and to expire a key:
```shell
headscale apikeys expire --prefix "<PREFIX>"
```
## Download and configure headscale
1. Download the [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases). Make
sure to use the same version as on the server.
1. Put the binary somewhere in your `PATH`, e.g. `/usr/local/bin/headscale`
1. Make `headscale` executable:
```shell
chmod +x /usr/local/bin/headscale
```
1. Provide the connection parameters for the remote headscale server either via a minimal YAML configuration file or via
environment variables:
=== "Minimal YAML configuration file"
```yaml title="config.yaml"
cli:
address: <HEADSCALE_ADDRESS>:<PORT>
api_key: <API_KEY_FROM_PREVIOUS_STEP>
```
=== "Environment variables"
```shell
export HEADSCALE_CLI_ADDRESS="<HEADSCALE_ADDRESS>:<PORT>"
export HEADSCALE_CLI_API_KEY="<API_KEY_FROM_PREVIOUS_STEP>"
```
!!! bug
Headscale currently requires at least an empty configuration file when environment variables are used to
specify connection details. See [issue 2193](https://github.com/juanfont/headscale/issues/2193) for more
information.
This instructs the `headscale` binary to connect to a remote instance at `<HEADSCALE_ADDRESS>:<PORT>`, instead of
connecting to the local instance.
1. Test the connection
Let us run the headscale command to verify that we can connect by listing our nodes:
```shell
headscale nodes list
```
You should now be able to see a list of your nodes from your workstation, and you can
now control the headscale server from your workstation.
## Behind a proxy
It is possible to run the gRPC remote endpoint behind a reverse proxy, like Nginx, and have it run on the _same_ port as headscale.
While this is _not a supported_ feature, an example on how this can be set up on
[NixOS is shown here](https://github.com/kradalby/dotfiles/blob/4489cdbb19cddfbfae82cd70448a38fde5a76711/machines/headscale.oracldn/headscale.nix#L61-L91).
## Troubleshooting
- Make sure you have the _same_ headscale version on your server and workstation.
- Ensure that connections to the gRPC port are allowed.
- Verify that your TLS certificate is valid and trusted.
- If you don't have access to a trusted certificate (e.g. from Let's Encrypt), either:
- Add your self-signed certificate to the trust store of your OS _or_
- Disable certificate verification by either setting `cli.insecure: true` in the configuration file or by setting
`HEADSCALE_CLI_INSECURE=1` via an environment variable. We do **not** recommend to disable certificate validation.

View file

@ -1,7 +1,8 @@
# 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.
Headscale supports route advertising and can be used to manage [subnet
routers](https://tailscale.com/docs/features/subnet-routers) and [exit
nodes](https://tailscale.com/docs/features/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
@ -42,8 +43,9 @@ 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 |
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:
@ -57,8 +59,9 @@ The node `myrouter` can now route the IPv4 networks `10.0.0.0/8` and `192.168.0.
```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
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
@ -70,32 +73,32 @@ $ 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.
documentation](https://tailscale.com/docs/features/subnet-routers#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
### 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 an ACL enabled, all
nodes can accept and use such routes. Configure an ACL to explicitly manage who can use routes.
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 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`. It allows the node `node` to access
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.
```json title="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"
// 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"
},
"acls": [
"grants": [
{
"action": "accept",
"src": ["node"],
"dst": ["service.example.net:80,443"]
"dst": ["service.example.net"],
"ip": ["80,443"]
}
]
}
@ -104,14 +107,14 @@ denied.
### 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.
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 ACL snippet below defines the tag `tag:router` owned by the user `alice`. This tag is used for `routes` in the
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
owned by the user `alice` and that also advertises the tag `tag:router`.
that advertises the tag `tag:router`.
```json title="Subnet routers owned by alice and tagged with tag:router are automatically approved"
```json title="Subnet routers tagged with tag:router are automatically approved"
{
"tagOwners": {
"tag:router": ["alice@"]
@ -121,7 +124,7 @@ owned by the user `alice` and that also advertises the tag `tag:router`.
"192.168.0.0/24": ["tag:router"]
}
},
"acls": [
"grants": [
// more rules
]
}
@ -133,8 +136,9 @@ Advertise the route `192.168.0.0/24` from a subnet router that also advertises t
$ 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](https://tailscale.com/kb/1337/acl-syntax#autoapprovers) for more
information on auto approvers.
Please see the [official Tailscale
documentation](https://tailscale.com/docs/reference/syntax/policy-file#auto-approvers) for more information on auto
approvers.
## Exit node
@ -168,8 +172,9 @@ 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 |
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.
@ -183,8 +188,9 @@ 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
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
@ -195,22 +201,51 @@ The exit node can now be used on a node with:
$ 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.
Please refer to the official [Tailscale documentation](https://tailscale.com/docs/features/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
### Restrict the use of an exit node with a policy
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.
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.
```json title="Example use of autogroup:internet"
{
"acls": [
"grants": [
{
"action": "accept",
"src": ["..."],
"dst": ["autogroup:internet:*"]
"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`.
```json title="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": ["*"]
}
]
}
@ -219,14 +254,13 @@ nodes.
### 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
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 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:
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:
```json title="Exit nodes owned by alice and tagged with tag:exit are automatically approved"
```json title="Exit nodes tagged with tag:exit are automatically approved"
{
"tagOwners": {
"tag:exit": ["alice@"]
@ -234,7 +268,7 @@ is automatically approved:
"autoApprovers": {
"exitNode": ["tag:exit"]
},
"acls": [
"grants": [
// more rules
]
}
@ -246,26 +280,23 @@ Advertise a node as exit node and also advertise the tag `tag:exit` when joining
$ sudo tailscale up --login-server <YOUR_HEADSCALE_URL> --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.
Please see the [official Tailscale documentation](https://tailscale.com/docs/reference/syntax/policy-file#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.
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](https://tailscale.com/docs/how-to/set-up-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.
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](configuration.md) 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](https://tailscale.com/kb/1019/subnets/?tab=linux#enable-ip-forwarding) for how to
official [Tailscale documentation](https://tailscale.com/docs/features/subnet-routers#enable-ip-forwarding) for how to
enable IP forwarding.

54
docs/ref/tags.md Normal file
View file

@ -0,0 +1,54 @@
# Tags
Headscale supports Tailscale tags. Please read [Tailscale's tag documentation](https://tailscale.com/docs/features/tags)
to learn how tags work and how to use them.
Tags can be applied during [node registration](registration.md):
- using the `--advertise-tags` flag, see [web authentication for tagged devices](registration.md#__tabbed_1_2)
- using a tagged pre authenticated key, see [how to create and use it](registration.md#__tabbed_2_2)
Administrators can manage tags with:
- Headscale CLI
- [Headscale API](api.md)
## Common operations
### Manage tags for a node
Run `headscale nodes list` to list the tags for a node.
Use the `headscale nodes tag` command to modify the tags for a node. At least one tag is required and multiple tags can
be provided as comma separated list. The following command sets the tags `tag:server` and `tag:prod` on node with ID 1:
```console
headscale nodes tag -i 1 -t tag:server,tag:prod
```
### Convert from personal to tagged node
Use the `headscale nodes tag` command to convert a personal (user-owned) node to a tagged node:
```console
headscale nodes tag -i <NODE_ID> -t <TAG>
```
The node is now owned by the special user `tagged-devices` and has the specified tags assigned to it.
### Convert from tagged to personal node
Tagged nodes can return to personal (user-owned) nodes by re-authenticating with:
```console
tailscale up --login-server <YOUR_HEADSCALE_URL> --advertise-tags= --force-reauth
```
Usually, a browser window with further instructions is opened. This page explains how to complete the registration on
your Headscale server and it also prints the Auth ID required to approve the node:
```console
headscale auth register --user <USER> --auth-id <AUTH_ID>
```
All previously assigned tags get removed and the node is now owned by the user specified in the above command.

View file

@ -50,7 +50,7 @@ Headscale uses [autocert](https://pkg.go.dev/golang.org/x/crypto/acme/autocert),
If you want to validate that certificate renewal completed successfully, this can be done either manually, or through external monitoring software. Two examples of doing this manually:
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`:
1. Or, check remotely from CLI using `openssl`:
```console
$ openssl s_client -servername [hostname] -connect [hostname]:443 | openssl x509 -noout -dates

View file

@ -1,6 +1,6 @@
mike~=2.1
mkdocs-include-markdown-plugin~=7.1
mkdocs-macros-plugin~=1.3
mkdocs-material[imaging]~=9.5
mkdocs-minify-plugin~=0.7
mkdocs-include-markdown-plugin~=7.2
mkdocs-macros-plugin~=1.5
mkdocs-materialx[imaging]~=10.1
mkdocs-minify-plugin~=0.8
mkdocs-redirects~=1.2

View file

@ -1,7 +1,7 @@
# Community packages
Several Linux distributions and community members provide packages for headscale. Those packages may be used instead of
the [official releases](./official.md) provided by the headscale maintainers. Such packages offer improved integration
the [official releases](official.md) provided by the headscale maintainers. Such packages offer improved integration
for their targeted operating system and usually:
- setup a dedicated local user account to run headscale
@ -10,8 +10,8 @@ for their targeted operating system and usually:
!!! warning "Community packages might be outdated"
The packages mentioned on this page might be outdated or unmaintained. Use the [official releases](./official.md) to
get the current stable version or to test pre-releases.
The packages mentioned on this page might be outdated or unmaintained. Use the [official releases](official.md) to
get the current stable version or to [test pre-releases](main.md).
[![Packaging status](https://repology.org/badge/vertical-allrepos/headscale.svg)](https://repology.org/project/headscale/versions)
@ -23,9 +23,6 @@ Arch Linux offers a package for headscale, install via:
pacman -S headscale
```
The [AUR package `headscale-git`](https://aur.archlinux.org/packages/headscale-git) can be used to build the current
development version.
## Fedora, RHEL, CentOS
A third-party repository for various RPM based distributions is available at:

View file

@ -7,9 +7,8 @@
**It might be outdated and it might miss necessary steps**.
This documentation has the goal of showing a user how-to set up and run headscale in a container. A container runtime
such as [Docker](https://www.docker.com) or [Podman](https://podman.io) is required. The container image can be found on
[Docker Hub](https://hub.docker.com/r/headscale/headscale) and [GitHub Container
A container runtime such as [Docker](https://www.docker.com) or [Podman](https://podman.io) is required. The container
image can be found on [Docker Hub](https://hub.docker.com/r/headscale/headscale) and [GitHub Container
Registry](https://github.com/juanfont/headscale/pkgs/container/headscale). The container image URLs are:
- [Docker Hub](https://hub.docker.com/r/headscale/headscale): `docker.io/headscale/headscale:<VERSION>`
@ -18,27 +17,29 @@ Registry](https://github.com/juanfont/headscale/pkgs/container/headscale). The c
## Configure and run headscale
1. Create a directory on the Docker host to store headscale's [configuration](../../ref/configuration.md) and the [SQLite](https://www.sqlite.org/) database:
1. Create a directory on the container host to store headscale's [configuration](../../ref/configuration.md) and the SQLite database:
```shell
mkdir -p ./headscale/{config,lib,run}
mkdir -p ./headscale/{config,lib}
cd ./headscale
```
1. Download the example configuration for your chosen version and save it as: `$(pwd)/config/config.yaml`. Adjust the
configuration to suit your local environment. See [Configuration](../../ref/configuration.md) for details.
1. Download the example configuration for your chosen version and save it as: `$(pwd)/config/config.yaml`. Adjust the
configuration to suit your local environment. See [Configuration](../../ref/configuration.md) for details.
1. Start headscale from within the previously created `./headscale` directory:
1. Start headscale from within the previously created `./headscale` directory:
```shell
docker run \
--name headscale \
--detach \
--volume "$(pwd)/config:/etc/headscale" \
--read-only \
--tmpfs /var/run/headscale \
--volume "$(pwd)/config:/etc/headscale:ro" \
--volume "$(pwd)/lib:/var/lib/headscale" \
--volume "$(pwd)/run:/var/run/headscale" \
--publish 127.0.0.1:8080:8080 \
--publish 127.0.0.1:9090:9090 \
--health-cmd "CMD headscale health" \
docker.io/headscale/headscale:<VERSION> \
serve
```
@ -56,19 +57,23 @@ Registry](https://github.com/juanfont/headscale/pkgs/container/headscale). The c
image: docker.io/headscale/headscale:<VERSION>
restart: unless-stopped
container_name: headscale
read_only: true
tmpfs:
- /var/run/headscale
ports:
- "127.0.0.1:8080:8080"
- "127.0.0.1:9090:9090"
volumes:
# Please set <HEADSCALE_PATH> to the absolute path
# of the previously created headscale directory.
- <HEADSCALE_PATH>/config:/etc/headscale
- <HEADSCALE_PATH>/config:/etc/headscale:ro
- <HEADSCALE_PATH>/lib:/var/lib/headscale
- <HEADSCALE_PATH>/run:/var/run/headscale
command: serve
healthcheck:
test: ["CMD", "headscale", "health"]
```
1. Verify headscale is running:
1. Verify headscale is running:
Follow the container logs:
@ -85,49 +90,14 @@ Registry](https://github.com/juanfont/headscale/pkgs/container/headscale). The c
Verify headscale is available:
```shell
curl http://127.0.0.1:9090/metrics
curl http://127.0.0.1:8080/health
```
1. Create a headscale user:
```shell
docker exec -it headscale \
headscale users create myfirstuser
```
### Register a machine (normal login)
On a client machine, execute the `tailscale up` command to login:
```shell
tailscale up --login-server YOUR_HEADSCALE_URL
```
To register a machine when running headscale in a container, take the headscale command and pass it to the container:
```shell
docker exec -it headscale \
headscale nodes register --user myfirstuser --key <YOUR_MACHINE_KEY>
```
### Register a machine using a pre authenticated key
Generate a key using the command line for the user with ID 1:
```shell
docker exec -it headscale \
headscale preauthkeys create --user 1 --reusable --expiration 24h
```
This will return a pre-authenticated key that can be used to connect a node to headscale with the `tailscale up` command:
```shell
tailscale up --login-server <YOUR_HEADSCALE_URL> --authkey <YOUR_AUTH_KEY>
```
Continue on the [getting started page](../../usage/getting-started.md) to register your first machine.
## Debugging headscale running in Docker
The Headscale container image is based on a "distroless" image that does not contain a shell or any other debug tools. If you need to debug headscale running in the Docker container, you can use the `-debug` variant, for example `docker.io/headscale/headscale:x.x.x-debug`.
The Headscale container image is based on a distroless image that does not contain a shell or any other debug tools. If you need to debug headscale running in the Docker container, you can use the `-debug` variant, for example `docker.io/headscale/headscale:x.x.x-debug`.
### Running the debug Docker container

View file

@ -0,0 +1,58 @@
# Development builds
!!! warning
Development builds are created automatically from the latest `main` branch
and are **not versioned releases**. They may contain incomplete features,
breaking changes, or bugs. Use them for testing only.
Each push to `main` produces container images and cross-compiled binaries.
Container images are multi-arch (amd64, arm64) and use the same distroless
base image as official releases.
## Container images
Images are available from both Docker Hub and GitHub Container Registry, tagged
with the short commit hash of the build (e.g. `main-abc1234`):
- Docker Hub: `docker.io/headscale/headscale:main-<sha>`
- GitHub Container Registry: `ghcr.io/juanfont/headscale:main-<sha>`
To find the latest available tag, check the
[GitHub Actions workflow](https://github.com/juanfont/headscale/actions/workflows/container-main.yml)
or the [GitHub Container Registry package page](https://github.com/juanfont/headscale/pkgs/container/headscale).
For example, to run a specific development build:
```shell
docker run \
--name headscale \
--detach \
--read-only \
--tmpfs /var/run/headscale \
--volume "$(pwd)/config:/etc/headscale:ro" \
--volume "$(pwd)/lib:/var/lib/headscale" \
--publish 127.0.0.1:8080:8080 \
--publish 127.0.0.1:9090:9090 \
--health-cmd "CMD headscale health" \
docker.io/headscale/headscale:main-<sha> \
serve
```
See [Running headscale in a container](container.md) for full container setup instructions.
## Binaries
Pre-built binaries from the latest successful build on `main` are available
via [nightly.link](https://nightly.link/juanfont/headscale/workflows/container-main/main):
| OS | Arch | Download |
| ----- | ----- | -------------------------------------------------------------------------------------------------------------------------- |
| Linux | amd64 | [headscale-linux-amd64](https://nightly.link/juanfont/headscale/workflows/container-main/main/headscale-linux-amd64.zip) |
| Linux | arm64 | [headscale-linux-arm64](https://nightly.link/juanfont/headscale/workflows/container-main/main/headscale-linux-arm64.zip) |
| macOS | amd64 | [headscale-darwin-amd64](https://nightly.link/juanfont/headscale/workflows/container-main/main/headscale-darwin-amd64.zip) |
| macOS | arm64 | [headscale-darwin-arm64](https://nightly.link/juanfont/headscale/workflows/container-main/main/headscale-darwin-arm64.zip) |
After downloading and extracting the archive, make the binary executable and follow the
[standalone binary installation](official.md#using-standalone-binaries-advanced)
instructions for setting up the service.

View file

@ -7,9 +7,9 @@ Both are available on the [GitHub releases page](https://github.com/juanfont/hea
It is recommended to use our DEB packages to install headscale on a Debian based system as those packages configure a
local user to run headscale, provide a default configuration and ship with a systemd service file. Supported
distributions are Ubuntu 22.04 or newer, Debian 11 or newer.
distributions are Ubuntu 22.04 or newer, Debian 12 or newer.
1. Download the [latest headscale package](https://github.com/juanfont/headscale/releases/latest) for your platform (`.deb` for Ubuntu and Debian).
1. Download the [latest headscale package](https://github.com/juanfont/headscale/releases/latest) for your platform (`.deb` for Ubuntu and Debian).
```shell
HEADSCALE_VERSION="" # See above URL for latest version, e.g. "X.Y.Z" (NOTE: do not add the "v" prefix!)
@ -18,56 +18,59 @@ distributions are Ubuntu 22.04 or newer, Debian 11 or newer.
"https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
```
1. Install headscale:
1. Install headscale:
```shell
sudo apt install ./headscale.deb
```
1. [Configure headscale by editing the configuration file](../../ref/configuration.md):
1. [Configure headscale by editing the configuration file](../../ref/configuration.md). An up-to date example
configuration file is also available in `/usr/share/doc/headscale/examples/config-example.yaml`:
```shell
sudo nano /etc/headscale/config.yaml
```
1. Enable and start the headscale service:
1. Restart headscale to pick up configuration changes:
```shell
sudo systemctl enable --now headscale
sudo systemctl restart headscale
```
1. Verify that headscale is running as intended:
1. Verify that headscale is running as intended:
```shell
sudo systemctl status headscale
```
Continue on the [getting started page](../../usage/getting-started.md) to register your first machine.
## Using standalone binaries (advanced)
!!! warning "Advanced"
This installation method is considered advanced as one needs to take care of the local user and the systemd
service themselves. If possible, use the [DEB packages](#using-packages-for-debianubuntu-recommended) or a
[community package](./community.md) instead.
[community package](community.md) instead.
This section describes the installation of headscale according to the [Requirements and
assumptions](../requirements.md#assumptions). Headscale is run by a dedicated local user and the service itself is
managed by systemd.
1. Download the latest [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases):
1. Download the latest [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases):
```shell
sudo wget --output-document=/usr/local/bin/headscale \
sudo wget --output-document=/usr/bin/headscale \
https://github.com/juanfont/headscale/releases/download/v<HEADSCALE VERSION>/headscale_<HEADSCALE VERSION>_linux_<ARCH>
```
1. Make `headscale` executable:
1. Make `headscale` executable:
```shell
sudo chmod +x /usr/local/bin/headscale
sudo chmod +x /usr/bin/headscale
```
1. Add a dedicated local user to run headscale:
1. Add a dedicated local user to run headscale:
```shell
sudo useradd \
@ -79,39 +82,41 @@ managed by systemd.
headscale
```
1. Download the example configuration for your chosen version and save it as: `/etc/headscale/config.yaml`. Adjust the
configuration to suit your local environment. See [Configuration](../../ref/configuration.md) for details.
1. Download the example configuration for your chosen version and save it as: `/etc/headscale/config.yaml`. Adjust the
configuration to suit your local environment. See [Configuration](../../ref/configuration.md) for details.
```shell
sudo mkdir -p /etc/headscale
sudo nano /etc/headscale/config.yaml
```
1. Copy [headscale's systemd service file](https://github.com/juanfont/headscale/blob/main/packaging/systemd/headscale.service)
to `/etc/systemd/system/headscale.service` and adjust it to suit your local setup. The following parameters likely need
to be modified: `ExecStart`, `WorkingDirectory`, `ReadWritePaths`.
1. Copy [headscale's systemd service file](https://github.com/juanfont/headscale/blob/main/packaging/systemd/headscale.service)
to `/etc/systemd/system/headscale.service` and adjust it to suit your local setup. The following parameters likely need
to be modified: `ExecStart`, `WorkingDirectory`, `ReadWritePaths`.
1. In `/etc/headscale/config.yaml`, override the default `headscale` unix socket with a path that is writable by the
`headscale` user or group:
1. In `/etc/headscale/config.yaml`, override the default `headscale` unix socket with a path that is writable by the
`headscale` user or group:
```yaml title="config.yaml"
unix_socket: /var/run/headscale/headscale.sock
```
1. Reload systemd to load the new configuration file:
1. Reload systemd to load the new configuration file:
```shell
systemctl daemon-reload
```
1. Enable and start the new headscale service:
1. Enable and start the new headscale service:
```shell
systemctl enable --now headscale
```
1. Verify that headscale is running as intended:
1. Verify that headscale is running as intended:
```shell
systemctl status headscale
```
Continue on the [getting started page](../../usage/getting-started.md) to register your first machine.

View file

@ -17,7 +17,7 @@ The ports in use vary with the intended scenario and enabled features. Some of t
- tcp/80
- Expose publicly: yes
- HTTP, used by Let's Encrypt to verify ownership via the HTTP-01 challenge.
- Only required if the built-in Let's Enrypt client with the HTTP-01 challenge is used. See [TLS](../ref/tls.md) for
- Only required if the built-in Let's Encrypt client with the HTTP-01 challenge is used. See [TLS](../ref/tls.md) for
details.
- tcp/443
- Expose publicly: yes
@ -28,7 +28,7 @@ The ports in use vary with the intended scenario and enabled features. Some of t
- STUN, required if the [embedded DERP server](../ref/derp.md) is enabled
- tcp/50443
- Expose publicly: yes
- Only required if the gRPC interface is used to [remote-control Headscale](../ref/remote-cli.md).
- Only required if the gRPC interface is used to [remote-control Headscale](../ref/api.md#grpc).
- tcp/9090
- Expose publicly: no
- [Metrics and debug endpoint](../ref/debug.md#metrics-and-debug-endpoint)
@ -40,13 +40,12 @@ The headscale documentation and the provided examples are written with a few ass
- Headscale is running as system service via a dedicated local user `headscale`.
- The [configuration](../ref/configuration.md) is loaded from `/etc/headscale/config.yaml`.
- SQLite is used as database.
- The data directory for headscale (used for private keys, ACLs, SQLite database, …) is located in `/var/lib/headscale`.
- The data directory for headscale (used for private keys, policy, SQLite database, …) is located in `/var/lib/headscale`.
- URLs and values that need to be replaced by the user are either denoted as `<VALUE_TO_CHANGE>` or use placeholder
values such as `headscale.example.com`.
Please adjust to your local environment accordingly.
[^1]:
The Tailscale client assumes HTTPS on port 443 in certain situations. Serving headscale either via HTTP or via HTTPS
on a port other than 443 is possible but sticking with HTTPS on port 443 is strongly recommended for production
setups. See [issue 2164](https://github.com/juanfont/headscale/issues/2164) for more information.
[^1]: The Tailscale client assumes HTTPS on port 443 in certain situations. Serving headscale either via HTTP or via
HTTPS on a port other than 443 is possible but sticking with HTTPS on port 443 is strongly recommended for
production setups. See [issue 2164](https://github.com/juanfont/headscale/issues/2164) for more information.

View file

@ -1,10 +1,50 @@
# Upgrade an existing installation
Update an existing headscale installation to a new version:
!!! tip "Required update path"
Its required to update from one stable version to the next (e.g. 0.26.0 → 0.27.1 → 0.28.0) without skipping minor
versions in between. You should always pick the latest available patch release.
Update an existing Headscale installation to a new version:
- Read the announcement on the [GitHub releases](https://github.com/juanfont/headscale/releases) page for the new
version. It lists the changes of the release along with possible breaking changes.
- **Create a backup of your database.**
- Update headscale to the new version, preferably by following the same installation method.
version. It lists the changes of the release along with possible breaking changes and version-specific upgrade
instructions.
- Stop Headscale
- **[Create a backup of your installation](#backup)**
- Update Headscale to the new version, preferably by following the same installation method.
- Compare and update the [configuration](../ref/configuration.md) file.
- Restart headscale.
- Start Headscale
## Backup
Headscale applies database migrations during upgrades and we highly recommend to create a backup of your database before
upgrading. A full backup of Headscale depends on your individual setup, but below are some typical setup scenarios.
=== "Standard installation"
A installation that follows our [official releases](install/official.md) setup guide uses the following paths:
- [Configuration file](../ref/configuration.md): `/etc/headscale/config.yaml`
- Data directory: `/var/lib/headscale`
- SQLite as database: `/var/lib/headscale/db.sqlite`
```console
TIMESTAMP=$(date +%Y%m%d%H%M%S)
cp -aR /etc/headscale /etc/headscale.backup-$TIMESTAMP
cp -aR /var/lib/headscale /var/lib/headscale.backup-$TIMESTAMP
```
=== "Container"
A installation that follows our [container](install/container.md) setup guide uses a single source volume directory
that contains the configuration file, data directory and the SQLite database.
```console
cp -aR /path/to/headscale /path/to/headscale.backup-$(date +%Y%m%d%H%M%S)
```
=== "PostgreSQL"
Please follow PostgreSQL's [Backup and Restore](https://www.postgresql.org/docs/current/backup.html) documentation
to create a backup of your PostgreSQL database.

View file

@ -6,7 +6,7 @@ This documentation has the goal of showing how a user can use the official Andro
Install the official Tailscale Android client from the [Google Play Store](https://play.google.com/store/apps/details?id=com.tailscale.ipn) or [F-Droid](https://f-droid.org/packages/com.tailscale.ipn/).
## Connect via normal, interactive login
## Connect via web authentication
- Open the app and select the settings menu in the upper-right corner
- Tap on `Accounts`
@ -15,7 +15,7 @@ Install the official Tailscale Android client from the [Google Play Store](https
- The client connects automatically as soon as the node registration is complete on headscale. Until then, nothing is
visible in the server logs.
## Connect using a preauthkey
## Connect using a pre authenticated key
- Open the app and select the settings menu in the upper-right corner
- Tap on `Accounts`
@ -24,5 +24,5 @@ Install the official Tailscale Android client from the [Google Play Store](https
- Open the settings menu in the upper-right corner
- Tap on `Accounts`
- In the kebab menu icon (three dots) in the upper-right corner select `Use an auth key`
- Enter your [preauthkey generated from headscale](../getting-started.md#using-a-preauthkey)
- Enter your [preauthkey generated from headscale](../../ref/registration.md#pre-authenticated-key)
- If needed, tap `Log in` on the main screen. You should now be connected to your headscale.

View file

@ -25,7 +25,8 @@ Install the official Tailscale iOS client from the [App Store](https://apps.appl
### Installation
Choose one of the available [Tailscale clients for macOS](https://tailscale.com/kb/1065/macos-variants) and install it.
Choose one of the available [Tailscale clients for macOS](https://tailscale.com/docs/concepts/macos-variants) and
install it.
### Configuring the headscale URL

View file

@ -33,7 +33,8 @@ all the time, please enable "Unattended mode":
- Enable `Run unattended`
- Confirm the "Unattended mode" message
See also [Keep Tailscale running when I'm not logged in to my computer](https://tailscale.com/kb/1088/run-unattended)
See also [Keep Tailscale running when I'm not logged in to my
computer](https://tailscale.com/docs/how-to/run-unattended).
### Failing node registration
@ -54,6 +55,6 @@ This typically means that the registry keys above was not set appropriately.
To reset and try again, it is important to do the following:
1. Shut down the Tailscale service (or the client running in the tray)
2. Delete Tailscale Application data folder, located at `C:\Users\<USERNAME>\AppData\Local\Tailscale` and try to connect again.
3. Ensure the Windows node is deleted from headscale (to ensure fresh setup)
4. Start Tailscale on the Windows machine and retry the login.
1. Delete Tailscale Application data folder, located at `C:\Users\<USERNAME>\AppData\Local\Tailscale` and try to connect again.
1. Ensure the Windows node is deleted from headscale (to ensure fresh setup)
1. Start Tailscale on the Windows machine and retry the login.

View file

@ -5,13 +5,13 @@ This page helps you get started with headscale and provides a few usage examples
!!! note "Prerequisites"
* Headscale is installed and running as system service. Read the [setup section](../setup/requirements.md) for
- Headscale is installed and running as system service. Read the [setup section](../setup/requirements.md) for
installation instructions.
* The configuration file exists and is adjusted to suit your environment, see
- The configuration file exists and is adjusted to suit your environment, see
[Configuration](../ref/configuration.md) for details.
* Headscale is reachable from the Internet. Verify this by opening client specific setup instructions in your
browser, e.g. https://headscale.example.com/windows
* The Tailscale client is installed, see [Client and operating system support](../about/clients.md) for more
- Headscale is reachable from the Internet. Verify this by visiting the health endpoint:
https://headscale.example.com/health
- The Tailscale client is installed, see [Client and operating system support](../about/clients.md) for more
information.
## Getting help
@ -41,12 +41,28 @@ options, run:
headscale <COMMAND> --help
```
!!! note "Manage headscale from another local user"
By default only the user `headscale` or `root` will have the necessary permissions to access the unix socket
(`/var/run/headscale/headscale.sock`) that is used to communicate with the service. In order to be able to
communicate with the headscale service you have to make sure the unix socket is accessible by the user that runs
the commands. In general you can achieve this by any of the following methods:
- using `sudo`
- run the commands as user `headscale`
- add your user to the `headscale` group
To verify you can run the following command using your preferred method:
```shell
headscale users list
```
## Manage headscale users
In headscale, a node (also known as machine or device) is always assigned to a
headscale user. Such a headscale user may have many nodes assigned to them and
can be managed with the `headscale users` command. Invoke the built-in help for
more information: `headscale users --help`.
In headscale, a node (also known as machine or device) is [typically assigned to a headscale
user](../ref/registration.md#identity-model). Such a headscale user[^1] may have many nodes assigned to them and can be
managed with the `headscale users` command. Invoke the built-in help for more information: `headscale users --help`.
### Create a headscale user
@ -80,11 +96,12 @@ more information: `headscale users --help`.
## Register a node
One has to register a node first to use headscale as coordination with Tailscale. The following examples work for the
Tailscale client on Linux/BSD operating systems. Alternatively, follow the instructions to connect
[Android](connect/android.md), [Apple](connect/apple.md) or [Windows](connect/windows.md) devices.
One has to [register a node](../ref/registration.md) first to use headscale as coordination server with Tailscale. The
following examples work for the Tailscale client on Linux/BSD operating systems. Alternatively, follow the instructions
to connect [Android](connect/android.md), [Apple](connect/apple.md) or [Windows](connect/windows.md) devices. Read
[registration methods](../ref/registration.md) for an overview of available registration methods.
### Normal, interactive login
### [Web authentication](../ref/registration.md#web-authentication)
On a client machine, run the `tailscale up` command and provide the FQDN of your headscale instance as argument:
@ -92,27 +109,26 @@ On a client machine, run the `tailscale up` command and provide the FQDN of your
tailscale up --login-server <YOUR_HEADSCALE_URL>
```
Usually, a browser window with further instructions is opened and contains the value for `<YOUR_MACHINE_KEY>`. Approve
and register the node on your headscale server:
Usually, a browser window with further instructions is opened. This page explains how to complete the registration on
your headscale server and it also prints the Auth ID required to approve the node:
=== "Native"
```shell
headscale nodes register --user <USER> --key <YOUR_MACHINE_KEY>
headscale auth register --user <USER> --auth-id <AUTH_ID>
```
=== "Container"
```shell
docker exec -it headscale \
headscale nodes register --user <USER> --key <YOUR_MACHINE_KEY>
headscale auth register --user <USER> --auth-id <AUTH_ID>
```
### Using a preauthkey
### [Pre authenticated key](../ref/registration.md#pre-authenticated-key)
It is also possible to generate a preauthkey and register a node non-interactively. First, generate a preauthkey on the
headscale instance. By default, the key is valid for one hour and can only be used once (see `headscale preauthkeys
--help` for other options):
headscale instance. By default, the key is valid for one hour and can only be used once (see `headscale preauthkeys --help` for other options):
=== "Native"
@ -133,3 +149,5 @@ The command returns the preauthkey on success which is used to connect a node to
```shell
tailscale up --login-server <YOUR_HEADSCALE_URL> --authkey <YOUR_AUTH_KEY>
```
[^1]: [Ensure that the Headscale username does not end with `@`.](../ref/oidc.md#reference-a-user-in-the-policy)