Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
2c8640f822
1496 changed files with 12110903 additions and 24774 deletions
206
docs/ref/acls.md
206
docs/ref/acls.md
|
|
@ -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
|
||||
|
||||

|
||||
|
||||
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
129
docs/ref/api.md
Normal 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.
|
||||
|
|
@ -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`"
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
153
docs/ref/oidc.md
153
docs/ref/oidc.md
|
|
@ -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
249
docs/ref/policy.md
Normal 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
144
docs/ref/registration.md
Normal 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)
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
54
docs/ref/tags.md
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue