diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index 1166d489..0b56a073 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -54,9 +54,8 @@ func (*Suite) TestConfigLoading(c *check.C) { // Test that config file was interpreted correctly c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080") c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080") - c.Assert(viper.GetStringSlice("derp.paths")[0], check.Equals, "derp-example.yaml") c.Assert(viper.GetString("db_type"), check.Equals, "sqlite3") - c.Assert(viper.GetString("db_path"), check.Equals, "db.sqlite") + c.Assert(viper.GetString("db_path"), check.Equals, "/var/lib/headscale/db.sqlite") c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "") c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http") c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01") diff --git a/config-example.yaml b/config-example.yaml index 29b2f3ff..3301669d 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,39 +1,65 @@ --- +# headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order: +# +# - `/etc/headscale` +# - `~/.headscale` +# - current working directory + # The url clients will connect to. -# Typically this will be a domain. +# Typically this will be a domain like: +# +# https://myheadscale.example.com:443 +# server_url: http://127.0.0.1:8080 # Address to listen to / bind to on the server +# listen_addr: 0.0.0.0:8080 -# Private key file which will be +# Private key used encrypt the traffic between headscale +# and Tailscale clients. +# The private key file which will be # autogenerated if it's missing -private_key_path: private.key +private_key_path: /var/lib/headscale/private.key +# DERP is a relay system that Tailscale uses when a direct +# connection cannot be established. +# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp +# +# headscale needs a list of DERP servers that can be presented +# to the clients. derp: # List of externally available DERP maps encoded in JSON urls: - https://controlplane.tailscale.com/derpmap/default # Locally available DERP map files encoded in YAML - paths: - - derp-example.yaml + # + # This option is mostly interesting for people hosting + # their own DERP servers: + # https://tailscale.com/kb/1118/custom-derp-servers/ + # + # paths: + # - /etc/headscale/derp-example.yaml + paths: [] # If enabled, a worker will be set up to periodically # refresh the given sources and update the derpmap # will be set up. auto_update_enabled: true - # How often should we check for updates? + # How often should we check for DERP updates? update_frequency: 24h -# Disables the automatic check for updates on startup +# Disables the automatic check for headscale updates on startup disable_check_updates: false + +# Time before an inactive ephemeral node is deleted? ephemeral_node_inactivity_timeout: 30m # SQLite config db_type: sqlite3 -db_path: db.sqlite +db_path: /var/lib/headscale/db.sqlite # # Postgres config # db_type: postgres @@ -43,35 +69,87 @@ db_path: db.sqlite # db_user: foo # db_pass: bar +### TLS configuration +# +## Let's encrypt / ACME +# +# headscale supports automatically requesting and setting up +# TLS for a domain with Let's Encrypt. +# +# URL to ACME directory acme_url: https://acme-v02.api.letsencrypt.org/directory + +# Email to register with ACME provider acme_email: "" +# Domain name to request a TLS certificate for: tls_letsencrypt_hostname: "" -tls_letsencrypt_listen: ":http" -tls_letsencrypt_cache_dir: ".cache" -tls_letsencrypt_challenge_type: HTTP-01 +# Path to store certificates and metadata needed by +# letsencrypt +tls_letsencrypt_cache_dir: /var/lib/headscale/cache + +# Type of ACME challenge to use, currently supported types: +# HTTP-01 or TLS_ALPN-01 +# See [docs/tls.md](docs/tls.md) for more information +tls_letsencrypt_challenge_type: HTTP-01 +# When HTTP-01 challenge is chosen, letsencrypt must set up a +# verification endpoint, and it will be listning on: +# :http = port 80 +tls_letsencrypt_listen: ":http" + +## Use already defined certificates: tls_cert_path: "" tls_key_path: "" log_level: info # Path to a file containg ACL policies. +# Recommended path: /etc/headscale/acl.hujson acl_policy_path: "" +## DNS +# +# headscale supports Tailscale's DNS configuration and MagicDNS. +# Please have a look to their KB to better understand the concepts: +# +# - https://tailscale.com/kb/1054/dns/ +# - https://tailscale.com/kb/1081/magicdns/ +# - https://tailscale.com/blog/2021-09-private-dns-with-magicdns/ +# dns_config: - # Upstream DNS servers + # List of DNS servers to expose to clients. nameservers: - 1.1.1.1 + + # Split DNS (see https://tailscale.com/kb/1054/dns/), + # list of search domains and the DNS to query for each one. + # + # restricted_nameservers: + # foo.bar.com: + # - 1.1.1.1 + # darp.headscale.net: + # - 1.1.1.1 + # - 8.8.8.8 + + # Search domains to inject. domains: [] + # Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). + # Only works if there is at least a nameserver defined. magic_dns: true + + # Defines the base domain to create the hostnames for MagicDNS. + # `base_domain` must be a FQDNs, without the trailing dot. + # The FQDN of the hosts will be + # `hostname.namespace.base_domain` (e.g., _myhost.mynamespace.example.com_). base_domain: example.com # Unix socket used for the CLI to connect without authentication # Note: for local development, you probably want to change this to: # unix_socket: ./headscale.sock unix_socket: /var/run/headscale.sock +# # headscale supports experimental OpenID connect support, # it is still being tested and might have some bugs, please # help us test it. diff --git a/docs/Configuration.md b/docs/Configuration.md deleted file mode 100644 index 55ab56e5..00000000 --- a/docs/Configuration.md +++ /dev/null @@ -1,74 +0,0 @@ -# Configuration reference - -Headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order: - -- `/etc/headscale` -- `~/.headscale` -- current working directory - -```yaml -server_url: http://headscale.mydomain.net -listen_addr: 0.0.0.0:8080 -ip_prefix: 100.64.0.0/10 -disable_check_updates: false -``` - -`server_url` is the external URL via which Headscale is reachable. `listen_addr` is the IP address and port the Headscale program should listen on. `ip_prefix` is the IP prefix (range) in which IP addresses for nodes will be allocated (default 100.64.0.0/10, e.g., 192.168.4.0/24, 10.0.0.0/8). `disable_check_updates` disables the automatic check for updates. - -```yaml -log_level: debug -``` - -`log_level` can be used to set the Log level for Headscale, it defaults to `debug`, and the available levels are: `trace`, `debug`, `info`, `warn` and `error`. - -```yaml -derp_map_path: derp.yaml -``` - -`derp_map_path` is the path to the [DERP](https://pkg.go.dev/tailscale.com/derp) map file. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. - -```yaml -ephemeral_node_inactivity_timeout: "30m" -``` - -`ephemeral_node_inactivity_timeout` is the timeout after which inactive ephemeral node records will be deleted from the database. The default is 30 minutes. This value must be higher than 65 seconds (the keepalive timeout for the HTTP long poll is 60 seconds, plus a few seconds to avoid race conditions). - -PostgresSQL - -```yaml -db_host: localhost -db_port: 5432 -db_name: headscale -db_user: foo -db_pass: bar -``` - -SQLite - -```yaml -db_type: sqlite3 -db_path: db.sqlite -``` - -The fields starting with `db_` are used for the DB connection information. - -### TLS configuration - -Please check [`TLS.md`](TLS.md). - -### DNS configuration - -Please refer to [`DNS.md`](DNS.md). - -### Policy ACLs - -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 namespaces (which are the equivalent to user/logins in Tailscale.com). - -Please check https://tailscale.com/kb/1018/acls/, and `./tests/acls/` in this repo for working examples. - -### Apple devices - -An endpoint with information on how to connect your Apple devices (currently macOS only) is available at `/apple` on your running instance. diff --git a/docs/DNS.md b/docs/DNS.md deleted file mode 100644 index 53106526..00000000 --- a/docs/DNS.md +++ /dev/null @@ -1,37 +0,0 @@ -# DNS in headscale - -headscale supports Tailscale's DNS configuration and MagicDNS. Please have a look to their KB to better understand what this means: - -- https://tailscale.com/kb/1054/dns/ -- https://tailscale.com/kb/1081/magicdns/ -- https://tailscale.com/blog/2021-09-private-dns-with-magicdns/ - -Long story short, you can define the DNS servers you want to use in your tailnets, activate MagicDNS (so you don't have to remember the IP addresses of your nodes), define search domains, as well as predefined hosts. headscale will inject that settings into your nodes. - -## Configuration reference - -The setup is done via the `config.yaml` file, under the `dns_config` key. - -```yaml -server_url: http://127.0.0.1:8001 -listen_addr: 0.0.0.0:8001 -dns_config: - nameservers: - - 1.1.1.1 - - 8.8.8.8 - restricted_nameservers: - foo.bar.com: - - 1.1.1.1 - darp.headscale.net: - - 1.1.1.1 - - 8.8.8.8 - domains: [] - magic_dns: true - base_domain: example.com -``` - -- `nameservers`: The list of DNS servers to use. -- `domains`: Search domains to inject. -- `magic_dns`: Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). Only works if there is at least a nameserver defined. -- `base_domain`: Defines the base domain to create the hostnames for MagicDNS. `base_domain` must be a FQDNs, without the trailing dot. The FQDN of the hosts will be `hostname.namespace.base_domain` (e.g., _myhost.mynamespace.example.com_). -- `restricted_nameservers`: Split DNS (see https://tailscale.com/kb/1054/dns/), list of search domains and the DNS to query for each one. diff --git a/docs/README.md b/docs/README.md index 111b3bc8..f42d67de 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,42 @@ -# Official headscale documentation +# headscale documentation -- [Configuration](Configuration.md) -- [Running](Running.md) -- [DNS](DNS.md) -- [TLS](TLS.md) -- [Glossary](Glossary.md) +This page contains the official and community contributed documentation for `headscale`. + +If you are having trouble with following the documentation or get unexpected results, +please ask on [Discord](https://discord.gg/XcQxk2VHjx) instead of opening an Issue. + +## Official documentation + +### How-to + +- [Running headscale on Linux](running-headscale-linux.md) + +### References + +- [Configuration](../config-example.yaml) +- [Glossary](glossary.md) +- [TLS](tls.md) + +## Community documentation + +Community documentation is not actively maintained by the headscale authors and is +written by community members. It is _not_ verified by `headscale` developers. + +**It might be outdated and it might miss necessary steps**. + +- [Running headscale in a container](running-headscale-container.md) + +## Misc + +### Policy ACLs + +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 namespaces (which are the equivalent to user/logins in Tailscale.com). + +Please check https://tailscale.com/kb/1018/acls/, and `./tests/acls/` in this repo for working examples. + +### Apple devices + +An endpoint with information on how to connect your Apple devices (currently macOS only) is available at `/apple` on your running instance. diff --git a/docs/Running.md b/docs/Running.md deleted file mode 100644 index 9613e160..00000000 --- a/docs/Running.md +++ /dev/null @@ -1,193 +0,0 @@ -# Running headscale - -**Note:** The docs are currently out of date with the new 0.12.x releases, we are working on this. The _main_ change is that `headscale` must be running before using the CLI. - -## Server configuration - -1. Download the headscale binary https://github.com/juanfont/headscale/releases, and place it somewhere in your $PATH or use the docker container - - ```shell - docker pull headscale/headscale:x.x.x - ``` - - - -2. When running headscale in a docker container, prepare a directory to hold all configuration - - ```shell - mkdir config - ``` - -3. Get yourself a DB - - a) Get a Postgres DB running in Docker: - - ```shell - docker run --name headscale \ - -e POSTGRES_DB=headscale \ - -e POSTGRES_USER=foo \ - -e POSTGRES_PASSWORD=bar \ - -p 5432:5432 \ - -d postgres - ``` - - or b) Prepare a SQLite DB file: - - ```shell - touch config/db.sqlite - ``` - -4. Create a headscale configuration, and a DERP map file. Refer to [tailscale sample](https://raw.githubusercontent.com/tailscale/tailscale/main/net/dnsfallback/dns-fallback-servers.json) for more guidance. - - ```shell - cp config.yaml.[sqlite|postgres].example config/config.yaml - - cp derp-example.yaml config/derp.yaml - ``` - -5. Create a namespace - - ```shell - headscale namespaces create myfirstnamespace - ``` - - or Docker: - - ```shell - docker run \ - -v $(pwd)/config:/etc/headscale/ \ - -p 127.0.0.1:8080:8080 \ - headscale/headscale:x.x.x \ - headscale namespaces create myfirstnamespace - ``` - - or if your server is already running in Docker: - - ```shell - docker exec \ - headscale namespaces create myfirstnamespace - ``` - -6. Run the server - - ```shell - headscale serve - ``` - - or Docker: - - ```shell - docker run \ - -v $(pwd)/config:/etc/headscale/ \ - -p 127.0.0.1:8080:8080 \ - headscale/headscale:x.x.x \ - headscale serve - ``` - -## Nodes configuration - -If you used tailscale.com before in your nodes, make sure you clear the tailscaled data folder - -```shell -systemctl stop tailscaled -rm -fr /var/lib/tailscale -systemctl start tailscaled -``` - -### Adding node based on MACHINEKEY - -1. Add your first machine - - ```shell - tailscale up --login-server YOUR_HEADSCALE_URL - ``` - -2. Navigate to the URL returned by `tailscale up`, where you'll find your machine key. - -3. In the server, register your machine to a namespace with the CLI - - ```shell - headscale -n myfirstnamespace nodes register -k YOURMACHINEKEY - ``` - - or Docker: - - ```shell - docker run \ - -v $(pwd)/config:/etc/headscale/ \ - headscale/headscale:x.x.x \ - headscale -n myfirstnamespace nodes register -k YOURMACHINEKEY - ``` - - or if your server is already running in Docker: - - ```shell - docker exec \ - headscale -n myfirstnamespace nodes register -k YOURMACHINEKEY - ``` - -### Alternative: adding node with AUTHKEY - -1. Create an authkey - - ```shell - headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - - or Docker: - - ```shell - docker run \ - -v $(pwd)/config:/etc/headscale/ \ - headscale/headscale:x.x.x \ - headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - - or if your server is already running in Docker: - - ```shell - docker exec \ - headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - -2. Use the authkey on your node to register it: - - ```shell - tailscale up --login-server YOUR_HEADSCALE_URL --authkey YOURAUTHKEY - ``` - -If you create an authkey with the `--ephemeral` flag, that key will create ephemeral nodes. This implies that `--reusable` is true. - -Please bear in mind that all headscale commands support adding `-o json` or `-o json-line` to get nicely JSON-formatted output. - -## Debugging headscale running in Docker - -The `headscale/headscale` Docker container is based on a "distroless" image that does not contain a shell or any other debug tools. If you need to debug your application running in the Docker container, you can use the `-debug` variant, for example `headscale/headscale:x.x.x-debug`. - -### Running the debug Docker container - -To run the debug Docker container, use the exact same commands as above, but replace `headscale/headscale:x.x.x` with `headscale/headscale:x.x.x-debug` (`x.x.x` is the version of headscale). The two containers are compatible with each other, so you can alternate between them. - -### Executing commands in the debug container - -The default command in the debug container is to run `headscale`, which is located at `/bin/headscale` inside the container. - -Additionally, the debug container includes a minimalist Busybox shell. - -To launch a shell in the container, use: - -``` -docker run -it headscale/headscale:x.x.x-debug sh -``` - -You can also execute commands directly, such as `ls /bin` in this example: - -``` -docker run headscale/headscale:x.x.x-debug ls /bin -``` - -Using `docker exec` allows you to run commands in an existing container. diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 00000000..f9e85ff3 --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,5 @@ +# Examples + +This directory contains examples on how to run `headscale` on different platforms. + +All examples are provided by the community and they are not verified by the `headscale` authors. diff --git a/k8s/.gitignore b/docs/examples/kustomize/.gitignore similarity index 100% rename from k8s/.gitignore rename to docs/examples/kustomize/.gitignore diff --git a/k8s/README.md b/docs/examples/kustomize/README.md similarity index 95% rename from k8s/README.md rename to docs/examples/kustomize/README.md index 78e9ef2c..cc57f147 100644 --- a/k8s/README.md +++ b/docs/examples/kustomize/README.md @@ -1,5 +1,7 @@ # Deploying headscale on Kubernetes +**Note:** This is contributed by the community and not verified by the headscale authors. + This directory contains [Kustomize](https://kustomize.io) templates that deploy headscale in various configurations. @@ -66,7 +68,7 @@ tasks like creating namespaces, authkeys, etc. headscale is an open source implementation of the Tailscale control server -https://gitlab.com/juanfont/headscale +https://github.com/juanfont/headscale Usage: headscale [command] diff --git a/k8s/base/configmap.yaml b/docs/examples/kustomize/base/configmap.yaml similarity index 100% rename from k8s/base/configmap.yaml rename to docs/examples/kustomize/base/configmap.yaml diff --git a/k8s/base/ingress.yaml b/docs/examples/kustomize/base/ingress.yaml similarity index 100% rename from k8s/base/ingress.yaml rename to docs/examples/kustomize/base/ingress.yaml diff --git a/k8s/base/kustomization.yaml b/docs/examples/kustomize/base/kustomization.yaml similarity index 100% rename from k8s/base/kustomization.yaml rename to docs/examples/kustomize/base/kustomization.yaml diff --git a/k8s/base/service.yaml b/docs/examples/kustomize/base/service.yaml similarity index 100% rename from k8s/base/service.yaml rename to docs/examples/kustomize/base/service.yaml diff --git a/k8s/headscale.bash b/docs/examples/kustomize/headscale.bash similarity index 100% rename from k8s/headscale.bash rename to docs/examples/kustomize/headscale.bash diff --git a/k8s/init.bash b/docs/examples/kustomize/init.bash similarity index 100% rename from k8s/init.bash rename to docs/examples/kustomize/init.bash diff --git a/k8s/install-cert-manager.bash b/docs/examples/kustomize/install-cert-manager.bash similarity index 100% rename from k8s/install-cert-manager.bash rename to docs/examples/kustomize/install-cert-manager.bash diff --git a/k8s/postgres/deployment.yaml b/docs/examples/kustomize/postgres/deployment.yaml similarity index 100% rename from k8s/postgres/deployment.yaml rename to docs/examples/kustomize/postgres/deployment.yaml diff --git a/k8s/postgres/kustomization.yaml b/docs/examples/kustomize/postgres/kustomization.yaml similarity index 100% rename from k8s/postgres/kustomization.yaml rename to docs/examples/kustomize/postgres/kustomization.yaml diff --git a/k8s/postgres/postgres-service.yaml b/docs/examples/kustomize/postgres/postgres-service.yaml similarity index 100% rename from k8s/postgres/postgres-service.yaml rename to docs/examples/kustomize/postgres/postgres-service.yaml diff --git a/k8s/postgres/postgres-statefulset.yaml b/docs/examples/kustomize/postgres/postgres-statefulset.yaml similarity index 100% rename from k8s/postgres/postgres-statefulset.yaml rename to docs/examples/kustomize/postgres/postgres-statefulset.yaml diff --git a/k8s/production-tls/ingress-patch.yaml b/docs/examples/kustomize/production-tls/ingress-patch.yaml similarity index 100% rename from k8s/production-tls/ingress-patch.yaml rename to docs/examples/kustomize/production-tls/ingress-patch.yaml diff --git a/k8s/production-tls/kustomization.yaml b/docs/examples/kustomize/production-tls/kustomization.yaml similarity index 100% rename from k8s/production-tls/kustomization.yaml rename to docs/examples/kustomize/production-tls/kustomization.yaml diff --git a/k8s/production-tls/production-issuer.yaml b/docs/examples/kustomize/production-tls/production-issuer.yaml similarity index 100% rename from k8s/production-tls/production-issuer.yaml rename to docs/examples/kustomize/production-tls/production-issuer.yaml diff --git a/k8s/sqlite/kustomization.yaml b/docs/examples/kustomize/sqlite/kustomization.yaml similarity index 100% rename from k8s/sqlite/kustomization.yaml rename to docs/examples/kustomize/sqlite/kustomization.yaml diff --git a/k8s/sqlite/statefulset.yaml b/docs/examples/kustomize/sqlite/statefulset.yaml similarity index 100% rename from k8s/sqlite/statefulset.yaml rename to docs/examples/kustomize/sqlite/statefulset.yaml diff --git a/k8s/staging-tls/ingress-patch.yaml b/docs/examples/kustomize/staging-tls/ingress-patch.yaml similarity index 100% rename from k8s/staging-tls/ingress-patch.yaml rename to docs/examples/kustomize/staging-tls/ingress-patch.yaml diff --git a/k8s/staging-tls/kustomization.yaml b/docs/examples/kustomize/staging-tls/kustomization.yaml similarity index 100% rename from k8s/staging-tls/kustomization.yaml rename to docs/examples/kustomize/staging-tls/kustomization.yaml diff --git a/k8s/staging-tls/staging-issuer.yaml b/docs/examples/kustomize/staging-tls/staging-issuer.yaml similarity index 100% rename from k8s/staging-tls/staging-issuer.yaml rename to docs/examples/kustomize/staging-tls/staging-issuer.yaml diff --git a/docs/Glossary.md b/docs/glossary.md similarity index 100% rename from docs/Glossary.md rename to docs/glossary.md diff --git a/docs/running-headscale-container.md b/docs/running-headscale-container.md new file mode 100644 index 00000000..9c658bf7 --- /dev/null +++ b/docs/running-headscale-container.md @@ -0,0 +1,133 @@ +# Running headscale in a container + +**Note:** the container documentation is maintained by the _community_ and there is no guarentee +it is up to date, or working. + +## Goal + +This documentation has the goal of showing a user how-to set up and run `headscale` in a container. +[Docker](https://www.docker.com) is used as the reference container implementation, but there is no reason that it should +not work with alternatives like [Podman](https://podman.io). + +## Configure and run `headscale` + +1. Prepare a direction to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: + +```shell +mkdir config +``` + +2. Create an empty SQlite datebase: + +```shell +touch config/db.sqlite +``` + +3. Create a `headscale` configuration: + +```shell +touch config/config.yaml +``` + +It is **strongly recommended** to copy the [example configuration](../config.yaml) from the [headscale repository](../) + +4. Start the headscale server: + +```shell +docker run \ + --name headscale \ + --detach \ + --rm \ + --volume $(pwd)/config:/etc/headscale/ \ + --publish 127.0.0.1:8080:8080 \ + headscale/headscale: \ + headscale serve + +``` + +This command will mount `config/` under `/etc/headscale`, forward port 8080 out of the container so the +`headscale` instance becomes available and then detach so headscale runs in the background. + +5. Verify `headscale` is running: + +Follow the container logs: + +```shell +docker logs --follow headscale +``` + +Verify running containers: + +```shell +docker ps +``` + +Verify `headscale` is available: + +```shell +curl http://127.0.0.1:8080/metrics +``` + +6. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)): + +```shell +docker exec headscale -- headscale namespaces create myfirstnamespace +``` + +### Register a machine (normal login) + +On a client machine, execute the `tailscale` login command: + +```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 headscale -- \ + headscale --namespace myfirstnamespace nodes register --key +``` + +### Register machine using a pre authenticated key + +Generate a key using the command line: + +```shell +docker exec headscale -- \ + headscale --namespace myfirstnamespace preauthkeys create --reusable --expiration 24h +``` + +This will return a pre-authenticated key that can be used to connect a node to `headscale` during the `tailscale` command: + +```shell +tailscale up --login-server --authkey +``` + +## Debugging headscale running in Docker + +The `headscale/headscale` Docker container is based on a "distroless" image that does not contain a shell or any other debug tools. If you need to debug your application running in the Docker container, you can use the `-debug` variant, for example `headscale/headscale:x.x.x-debug`. + +### Running the debug Docker container + +To run the debug Docker container, use the exact same commands as above, but replace `headscale/headscale:x.x.x` with `headscale/headscale:x.x.x-debug` (`x.x.x` is the version of headscale). The two containers are compatible with each other, so you can alternate between them. + +### Executing commands in the debug container + +The default command in the debug container is to run `headscale`, which is located at `/bin/headscale` inside the container. + +Additionally, the debug container includes a minimalist Busybox shell. + +To launch a shell in the container, use: + +``` +docker run -it headscale/headscale:x.x.x-debug sh +``` + +You can also execute commands directly, such as `ls /bin` in this example: + +``` +docker run headscale/headscale:x.x.x-debug ls /bin +``` + +Using `docker exec` allows you to run commands in an existing container. diff --git a/docs/running-headscale-linux.md b/docs/running-headscale-linux.md new file mode 100644 index 00000000..6b3d0ecb --- /dev/null +++ b/docs/running-headscale-linux.md @@ -0,0 +1,172 @@ +# Running headscale on Linux + +## Goal + +This documentation has the goal of showing a user how-to set up and run `headscale` on Linux. +In additional to the "get up and running section", there is an optional [SystemD section](#running-headscale-in-the-background-with-systemd) +describing how to make `headscale` run properly in a server environment. + +## Configure and run `headscale` + +1. Download the latest [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases): + +```shell +wget --output-document=/usr/local/bin/headscale \ + https://github.com/juanfont/headscale/releases/download/v/headscale__linux_ +``` + +2. Make `headscale` executable: + +```shell +chmod +x /usr/local/bin/headscale +``` + +3. Prepare a direction to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: + +```shell +# Directory for configuration + +mkdir -p /etc/headscale + +# Directory for Database, and other variable data (like certificates) +mkdir -p /var/lib/headscale +``` + +4. Create an empty SQlite datebase: + +```shell +touch /var/lib/headscale/db.sqlite +``` + +5. Create a `headscale` configuration: + +```shell +touch /etc/headscale/config.yaml +``` + +It is **strongly recommended** to copy and modifiy the [example configuration](../config.yaml) +from the [headscale repository](../) + +6. Start the headscale server: + +```shell + headscale serve +``` + +This command will start `headscale` in the current terminal session. + +--- + +To continue the tutorial, open a new terminal and let it run in the background. +Alternatively use terminal emulators like [tmux](https://github.com/tmux/tmux) or [screen](https://www.gnu.org/software/screen/). + +To run `headscale` in the background, please follow the steps in the [SystemD section](#running-headscale-in-the-background-with-systemd) before continuing. + +7. Verify `headscale` is running: + +Verify `headscale` is available: + +```shell +curl http://127.0.0.1:8080/metrics +``` + +8. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)): + +```shell +headscale namespaces create myfirstnamespace +``` + +### Register a machine (normal login) + +On a client machine, execute the `tailscale` login command: + +```shell +tailscale up --login-server YOUR_HEADSCALE_URL +``` + +Register the machine: + +```shell +headscale --namespace myfirstnamespace nodes register --key +``` + +### Register machine using a pre authenticated key + +Generate a key using the command line: + +```shell +headscale --namespace myfirstnamespace preauthkeys create --reusable --expiration 24h +``` + +This will return a pre-authenticated key that can be used to connect a node to `headscale` during the `tailscale` command: + +```shell +tailscale up --login-server --authkey +``` + +## Running `headscale` in the background with SystemD + +In this section it will be demonstrated how to run `headscale` as a service in the background with [SystemD](https://www.freedesktop.org/wiki/Software/systemd/). +This should work on most modern Linux distributions. + +1. Create a SystemD service configuration at `/etc/systemd/system/headscale.service` containing: + +```systemd +[Unit] +Description=headscale controller +After=syslog.target +After=network.target + +[Service] +Type=simple +User=headscale +Group=headscale +ExecStart=/usr/local/bin/headscale serve +Restart=always +RestartSec=5 + +# Optional security enhancements +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/var/lib/headscale /var/run/headscale +AmbientCapabilities=CAP_NET_BIND_SERVICE +RuntimeDirectory=headscale + +[Install] +WantedBy=multi-user.target +``` + +2. In `/etc/headscale/config.yaml`, override the default `headscale` unix socket with a SystemD friendly path: + +```yaml +unix_socket: /var/run/headscale/headscale.sock +``` + +3. Reload SystemD to load the new configuration file: + +```shell +systemctl daemon-reload +``` + +4. Enable and start the new `headscale` service: + +```shell +systemctl enable headscale +systemctl start headscale +``` + +5. Verify the headscale service: + +```shell +systemctl status headscale +``` + +Verify `headscale` is available: + +```shell +curl http://127.0.0.1:8080/metrics +``` + +`headscale` will now run in the background and start at boot. diff --git a/docs/TLS.md b/docs/tls.md similarity index 95% rename from docs/TLS.md rename to docs/tls.md index 47de1cd7..557cdf01 100644 --- a/docs/TLS.md +++ b/docs/tls.md @@ -1,5 +1,9 @@ # Running the service via TLS (optional) +## Let's Encrypt / ACME + +To get a certificate automatically via [Let's Encrypt](https://letsencrypt.org/), set `tls_letsencrypt_hostname` to the desired certificate hostname. This name must resolve to the IP address(es) headscale is reachable on (i.e., it must correspond to the `server_url` configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in `tls_letsencrypt_cache_dir`. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. The certificate will automatically be renewed as needed. + ```yaml tls_letsencrypt_hostname: "" tls_letsencrypt_listen: ":http" @@ -7,21 +11,21 @@ tls_letsencrypt_cache_dir: ".cache" tls_letsencrypt_challenge_type: HTTP-01 ``` -To get a certificate automatically via [Let's Encrypt](https://letsencrypt.org/), set `tls_letsencrypt_hostname` to the desired certificate hostname. This name must resolve to the IP address(es) headscale is reachable on (i.e., it must correspond to the `server_url` configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in `tls_letsencrypt_cache_dir`. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. The certificate will automatically be renewed as needed. - -```yaml -tls_cert_path: "" -tls_key_path: "" -``` - -headscale can also be configured to expose its web service via TLS. To configure the certificate and key file manually, set the `tls_cert_path` and `tls_cert_path` configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. - -## Challenge type HTTP-01 +### Challenge type HTTP-01 The default challenge type `HTTP-01` requires that headscale is reachable on port 80 for the Let's Encrypt automated validation, in addition to whatever port is configured in `listen_addr`. By default, headscale listens on port 80 on all local IPs for Let's Encrypt automated validation. If you need to change the ip and/or port used by headscale for the Let's Encrypt validation process, set `tls_letsencrypt_listen` to the appropriate value. This can be handy if you are running headscale as a non-root user (or can't run `setcap`). Keep in mind, however, that Let's Encrypt will _only_ connect to port 80 for the validation callback, so if you change `tls_letsencrypt_listen` you will also need to configure something else (e.g. a firewall rule) to forward the traffic from port 80 to the ip:port combination specified in `tls_letsencrypt_listen`. -## Challenge type TLS-ALPN-01 +### Challenge type TLS-ALPN-01 Alternatively, `tls_letsencrypt_challenge_type` can be set to `TLS-ALPN-01`. In this configuration, headscale listens on the ip:port combination defined in `listen_addr`. Let's Encrypt will _only_ connect to port 443 for the validation callback, so if `listen_addr` is not set to port 443, something else (e.g. a firewall rule) will be required to forward the traffic from port 443 to the ip:port combination specified in `listen_addr`. + +## Bring your own certificate + +headscale can also be configured to expose its web service via TLS. To configure the certificate and key file manually, set the `tls_cert_path` and `tls_cert_path` configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. + +```yaml +tls_cert_path: "" +tls_key_path: "" +```