From 7bb354117be2b0838a45c23dc0e9e85c068ee63f Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Tue, 19 Oct 2021 20:59:33 +0200 Subject: [PATCH 01/20] Move README documentation to doc/ --- README.md | 215 +----------------------------------------- docs/Configuration.md | 97 +++++++++++++++++++ docs/Running.md | 126 +++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 213 deletions(-) create mode 100644 docs/Configuration.md create mode 100644 docs/Running.md diff --git a/README.md b/README.md index b948a40f..c86955b2 100644 --- a/README.md +++ b/README.md @@ -47,219 +47,6 @@ Headscale implements this coordination server. Suggestions/PRs welcomed! -## Running it - -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. (Optional, you can also use SQLite) Get yourself a PostgreSQL DB running - - ```shell - docker run --name headscale -e POSTGRES_DB=headscale -e \ - POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -p 5432:5432 -d postgres - ``` - -3. Set some stuff up (headscale Wireguard keys & the config.json file) - - ```shell - wg genkey > private.key - wg pubkey < private.key > public.key # not needed - - # Postgres - cp config.json.postgres.example config.json - # or - # SQLite - cp config.json.sqlite.example config.json - ``` - -4. Create a namespace (a namespace is a 'tailnet', a group of Tailscale nodes that can talk to each other) - - ```shell - headscale namespaces create myfirstnamespace - ``` - - or docker: - - the db.sqlite mount is only needed if you use sqlite - - ```shell - touch db.sqlite - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -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 create myfirstnamespace - ``` - -5. Run the server - - ```shell - headscale serve - ``` - - or docker: - - the db.sqlite mount is only needed if you use sqlite - - ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale serve - ``` - -6. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder - - ```shell - systemctl stop tailscaled - rm -fr /var/lib/tailscale - systemctl start tailscaled - ``` - -7. Add your first machine - - ```shell - tailscale up --login-server YOUR_HEADSCALE_URL - ``` - -8. Navigate to the URL you will get with `tailscale up`, where you'll find your machine key. - -9. In the server, register your machine to a namespace with the CLI - ```shell - headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - or docker: - ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml headscale/headscale:x.x.x headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - or if your server is already running in docker: - ```shell - docker exec headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - -Alternatively, you can use Auth Keys to register your machines: - -1. Create an authkey - - ```shell - headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h - ``` - - or docker: - - ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v$(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite 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 from your machine 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 the commands from headscale support adding `-o json` or `-o json-line` to get a nicely JSON-formatted output. - -## Configuration reference - -Headscale's configuration file is named `config.json` or `config.yaml`. Headscale will look for it in `/etc/headscale`, `~/.headscale` and finally the directory from where the Headscale binary is executed. - -``` - "server_url": "http://192.168.1.12:8080", - "listen_addr": "0.0.0.0:8080", - "ip_prefix": "100.64.0.0/10" -``` - -`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) - -``` - "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`. - -``` - "private_key_path": "private.key", -``` - -`private_key_path` is the path to the Wireguard private key. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. - -``` - "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. - -``` - "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). - -``` - "db_host": "localhost", - "db_port": 5432, - "db_name": "headscale", - "db_user": "foo", - "db_pass": "bar", -``` - -The fields starting with `db_` are used for the PostgreSQL connection information. - -### Running the service via TLS (optional) - -``` - "tls_cert_path": "" - "tls_key_path": "" -``` - -Headscale can 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. - -``` - "tls_letsencrypt_hostname": "", - "tls_letsencrypt_listen": ":http", - "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. - -#### 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 - -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`. - -### 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. ## Disclaimer @@ -271,3 +58,5 @@ An endpoint with information on how to connect your Apple devices (currently mac - https://tailscale.com/blog/how-tailscale-works/ - https://tailscale.com/blog/tailscale-key-management/ - https://tailscale.com/blog/an-unlikely-database-migration/ + + diff --git a/docs/Configuration.md b/docs/Configuration.md new file mode 100644 index 00000000..56791a9a --- /dev/null +++ b/docs/Configuration.md @@ -0,0 +1,97 @@ +# Configuration reference + +Headscale's configuration file is named `config.json` or `config.yaml`. Headscale will look for it in `/etc/headscale`, `~/.headscale` and finally the directory from where the Headscale binary is executed. + +```yaml +server_url: http://192.168.1.12:8080 +listen_addr: 0.0.0.0:8080 +ip_prefix: 100.64.0.0/10 +``` + +`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) + +```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 +private_key_path: private.key +``` + +`private_key_path` is the path to the Wireguard private key. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. + +```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). + +```yaml +db_host: localhost +db_port: 5432 +db_name: headscale +db_user: foo +db_pass: bar +``` + +The fields starting with `db_` are used for the PostgreSQL connection information. + +### Running the service via TLS (optional) + +```yaml +tls_cert_path: '' +tls_key_path: '' +``` + +Headscale can 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_letsencrypt_hostname: '' +tls_letsencrypt_listen: ":http" +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. + +#### 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 + +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`. + +### 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. + +## Disclaimer + +1. We have nothing to do with Tailscale, or Tailscale Inc. +2. The purpose of writing this was to learn how Tailscale works. + +## More on Tailscale + +- https://tailscale.com/blog/how-tailscale-works/ +- https://tailscale.com/blog/tailscale-key-management/ +- https://tailscale.com/blog/an-unlikely-database-migration/ diff --git a/docs/Running.md b/docs/Running.md new file mode 100644 index 00000000..6f165edd --- /dev/null +++ b/docs/Running.md @@ -0,0 +1,126 @@ +# Running headscale + +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. (Optional, you can also use SQLite) Get yourself a PostgreSQL DB running + + ```shell + docker run --name headscale -e POSTGRES_DB=headscale -e \ + POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -p 5432:5432 -d postgres + ``` + +3. Set some stuff up (headscale Wireguard keys & the config.json file) + + ```shell + wg genkey > private.key + wg pubkey < private.key > public.key # not needed + + # Postgres + cp config.json.postgres.example config.json + # or + # SQLite + cp config.json.sqlite.example config.json + ``` + +4. Create a namespace (a namespace is a 'tailnet', a group of Tailscale nodes that can talk to each other) + + ```shell + headscale namespaces create myfirstnamespace + ``` + + or docker: + + the db.sqlite mount is only needed if you use sqlite + + ```shell + touch db.sqlite + docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -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 create myfirstnamespace + ``` + +5. Run the server + + ```shell + headscale serve + ``` + + or docker: + + the db.sqlite mount is only needed if you use sqlite + + ```shell + docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale serve + ``` + +6. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder + + ```shell + systemctl stop tailscaled + rm -fr /var/lib/tailscale + systemctl start tailscaled + ``` + +7. Add your first machine + + ```shell + tailscale up --login-server YOUR_HEADSCALE_URL + ``` + +8. Navigate to the URL you will get with `tailscale up`, where you'll find your machine key. + +9. In the server, register your machine to a namespace with the CLI + ```shell + headscale -n myfirstnamespace nodes register YOURMACHINEKEY + ``` + or docker: + ```shell + docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml headscale/headscale:x.x.x headscale -n myfirstnamespace nodes register YOURMACHINEKEY + ``` + or if your server is already running in docker: + ```shell + docker exec headscale -n myfirstnamespace nodes register YOURMACHINEKEY + ``` + +Alternatively, you can use Auth Keys to register your machines: + +1. Create an authkey + + ```shell + headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h + ``` + + or docker: + + ```shell + docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v$(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite 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 from your machine 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 the commands from headscale support adding `-o json` or `-o json-line` to get a nicely JSON-formatted output. \ No newline at end of file From 2236cc8bf7c3d8a69f27e96295f998fee746b0b8 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Tue, 19 Oct 2021 21:01:36 +0200 Subject: [PATCH 02/20] Improve wording here and there --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c86955b2..06d09d74 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Headscale implements this coordination server. - [x] Base functionality (nodes can communicate with each other) - [x] Node registration through the web flow - [x] Network changes are relayed to the nodes -- [x] Namespace support (~equivalent to multi-user in Tailscale.com) +- [x] Multiple namespaces support (~tailnets in Tailscale.com naming) - [x] Routing (advertise & accept, including exit nodes) - [x] Node registration via pre-auth keys (including reusable keys, and ephemeral node support) - [x] JSON-formatted output @@ -29,7 +29,7 @@ Headscale implements this coordination server. - [x] Taildrop (File Sharing) - [x] Support for alternative IP ranges in the tailnets (default Tailscale's 100.64.0.0/10) - [x] DNS (passing DNS servers to nodes) -- [x] Share nodes between ~~users~~ namespaces +- [x] Share nodes between namespaces - [x] MagicDNS (see `docs/`) ## Client OS support From 995dcfc6aebe520208cfd0f04679803ed17efa05 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Tue, 19 Oct 2021 21:02:45 +0200 Subject: [PATCH 03/20] Reference doc/ --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 06d09d74..83b162a5 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,11 @@ Headscale implements this coordination server. Suggestions/PRs welcomed! +## Running headscale + +Please have a look at the documentation under (`docs/`). + + ## Disclaimer 1. We have nothing to do with Tailscale, or Tailscale Inc. From 0318af5a332b32a6787cee08051ebd1920f2bdac Mon Sep 17 00:00:00 2001 From: Juan Font Date: Tue, 19 Oct 2021 23:22:56 +0200 Subject: [PATCH 04/20] Apply suggestions from code review Co-authored-by: Kristoffer Dalby --- README.md | 2 +- docs/Configuration.md | 8 ++++++-- docs/Running.md | 35 +++++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 83b162a5..94c4b241 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Suggestions/PRs welcomed! ## Running headscale -Please have a look at the documentation under (`docs/`). +Please have a look at the documentation under [`docs/`](docs/). ## Disclaimer diff --git a/docs/Configuration.md b/docs/Configuration.md index 56791a9a..6074f2b4 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,9 +1,13 @@ # Configuration reference -Headscale's configuration file is named `config.json` or `config.yaml`. Headscale will look for it in `/etc/headscale`, `~/.headscale` and finally the directory from where the Headscale binary is executed. +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://192.168.1.12:8080 +server_url: http://headscale.mydomain.net listen_addr: 0.0.0.0:8080 ip_prefix: 100.64.0.0/10 ``` diff --git a/docs/Running.md b/docs/Running.md index 6f165edd..888ddbfd 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -19,11 +19,10 @@ POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -p 5432:5432 -d postgres ``` -3. Set some stuff up (headscale Wireguard keys & the config.json file) +3. Create a WireGuard Private key and headscale configuration ```shell wg genkey > private.key - wg pubkey < private.key > public.key # not needed # Postgres cp config.json.postgres.example config.json @@ -44,7 +43,14 @@ ```shell touch db.sqlite - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale namespaces create myfirstnamespace + docker run \ + -v $(pwd)/private.key:/private.key \ + -v $(pwd)/config.json:/config.json \ + -v $(pwd)/derp.yaml:/derp.yaml \ + -v $(pwd)/db.sqlite:/db.sqlite \ + -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: @@ -64,7 +70,13 @@ the db.sqlite mount is only needed if you use sqlite ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale serve + docker run \ + -v $(pwd)/private.key:/private.key \ + -v $(pwd)/config.json:/config.json \ + -v $(pwd)/derp.yaml:/derp.yaml \ + -v $(pwd)/db.sqlite:/db.sqlite \ + -p 127.0.0.1:8080:8080 \ + headscale/headscale:x.x.x headscale serve ``` 6. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder @@ -89,7 +101,12 @@ ``` or docker: ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml headscale/headscale:x.x.x headscale -n myfirstnamespace nodes register YOURMACHINEKEY + docker run \ + -v $(pwd)/private.key:/private.key \ + -v $(pwd)/config.json:/config.json \ + -v $(pwd)/derp.yaml:/derp.yaml \ + headscale/headscale:x.x.x \ + headscale -n myfirstnamespace nodes register YOURMACHINEKEY ``` or if your server is already running in docker: ```shell @@ -107,7 +124,13 @@ Alternatively, you can use Auth Keys to register your machines: or docker: ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v$(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite headscale/headscale:x.x.x headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h + docker run \ + -v $(pwd)/private.key:/private.key \ + -v $(pwd)/config.json:/config.json \ + -v$(pwd)/derp.yaml:/derp.yaml \ + -v $(pwd)/db.sqlite:/db.sqlite \ + headscale/headscale:x.x.x \ + headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h ``` or if your server is already running in docker: From 06706aab9a408f8780aefee2c43f3d5d0814ff24 Mon Sep 17 00:00:00 2001 From: Juan Font Date: Tue, 19 Oct 2021 23:41:08 +0200 Subject: [PATCH 05/20] Update docs/Running.md Co-authored-by: Kristoffer Dalby --- docs/Running.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Running.md b/docs/Running.md index 888ddbfd..6445f678 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -31,7 +31,7 @@ cp config.json.sqlite.example config.json ``` -4. Create a namespace (a namespace is a 'tailnet', a group of Tailscale nodes that can talk to each other) +4. Create a namespace ```shell headscale namespaces create myfirstnamespace From 7b40e99aec28ef0a06457712f6f758a27a9c5665 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Tue, 19 Oct 2021 23:45:20 +0200 Subject: [PATCH 06/20] Added notes on SQLite --- docs/Configuration.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 6074f2b4..a9286778 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -38,6 +38,7 @@ 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 @@ -46,7 +47,13 @@ db_user: foo db_pass: bar ``` -The fields starting with `db_` are used for the PostgreSQL connection information. +SQLite +```yaml +db_type: sqlite3 +db_path: db.sqlite +``` + +The fields starting with `db_` are used for the DB connection information. ### Running the service via TLS (optional) From d1e8ac7ba5f1a8f748f87465bb66be61af48b5f4 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Wed, 20 Oct 2021 00:07:05 +0200 Subject: [PATCH 07/20] Moved TLS config to another file --- docs/Configuration.md | 37 ++++--------------------------------- docs/TLS.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 docs/TLS.md diff --git a/docs/Configuration.md b/docs/Configuration.md index a9286778..a293d358 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -55,33 +55,15 @@ db_path: db.sqlite The fields starting with `db_` are used for the DB connection information. -### Running the service via TLS (optional) +### TLS configuration -```yaml -tls_cert_path: '' -tls_key_path: '' -``` +Please check [`TLS.md`](TLS.md). -Headscale can 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_letsencrypt_hostname: '' -tls_letsencrypt_listen: ":http" -tls_letsencrypt_cache_dir: ".cache" -tls_letsencrypt_challenge_type: HTTP-01 -``` +### DNS configuration -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. +Please refer to [`DNS.md`](DNS.md). -#### 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 - -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`. ### Policy ACLs @@ -95,14 +77,3 @@ Please check https://tailscale.com/kb/1018/acls/, and `./tests/acls/` in this re ### Apple devices An endpoint with information on how to connect your Apple devices (currently macOS only) is available at `/apple` on your running instance. - -## Disclaimer - -1. We have nothing to do with Tailscale, or Tailscale Inc. -2. The purpose of writing this was to learn how Tailscale works. - -## More on Tailscale - -- https://tailscale.com/blog/how-tailscale-works/ -- https://tailscale.com/blog/tailscale-key-management/ -- https://tailscale.com/blog/an-unlikely-database-migration/ diff --git a/docs/TLS.md b/docs/TLS.md new file mode 100644 index 00000000..c4a7dfad --- /dev/null +++ b/docs/TLS.md @@ -0,0 +1,31 @@ + +# Running the service via TLS (optional) + +```yaml +tls_letsencrypt_hostname: '' +tls_letsencrypt_listen: ":http" +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 + +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 + +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`. + From 86ecc2a234a9f44331b1f88c15656335af313fe5 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Wed, 20 Oct 2021 00:17:08 +0200 Subject: [PATCH 08/20] Switch to YAML config --- config.json.postgres.example | 30 ------------------------------ config.json.sqlite.example | 26 -------------------------- config.yaml.example | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 56 deletions(-) delete mode 100644 config.json.postgres.example delete mode 100644 config.json.sqlite.example create mode 100644 config.yaml.example diff --git a/config.json.postgres.example b/config.json.postgres.example deleted file mode 100644 index 9b6f737f..00000000 --- a/config.json.postgres.example +++ /dev/null @@ -1,30 +0,0 @@ -{ - "server_url": "http://127.0.0.1:8080", - "listen_addr": "0.0.0.0:8080", - "private_key_path": "private.key", - "derp_map_path": "derp.yaml", - "ephemeral_node_inactivity_timeout": "30m", - "db_type": "postgres", - "db_host": "localhost", - "db_port": 5432, - "db_name": "headscale", - "db_user": "foo", - "db_pass": "bar", - "acme_url": "https://acme-v02.api.letsencrypt.org/directory", - "acme_email": "", - "tls_letsencrypt_hostname": "", - "tls_letsencrypt_listen": ":http", - "tls_letsencrypt_cache_dir": ".cache", - "tls_letsencrypt_challenge_type": "HTTP-01", - "tls_cert_path": "", - "tls_key_path": "", - "acl_policy_path": "", - "dns_config": { - "nameservers": [ - "1.1.1.1" - ], - "domains": [], - "magic_dns": true, - "base_domain": "example.com" - } -} diff --git a/config.json.sqlite.example b/config.json.sqlite.example deleted file mode 100644 index 74e15902..00000000 --- a/config.json.sqlite.example +++ /dev/null @@ -1,26 +0,0 @@ -{ - "server_url": "http://127.0.0.1:8080", - "listen_addr": "0.0.0.0:8080", - "private_key_path": "private.key", - "derp_map_path": "derp.yaml", - "ephemeral_node_inactivity_timeout": "30m", - "db_type": "sqlite3", - "db_path": "db.sqlite", - "acme_url": "https://acme-v02.api.letsencrypt.org/directory", - "acme_email": "", - "tls_letsencrypt_hostname": "", - "tls_letsencrypt_listen": ":http", - "tls_letsencrypt_cache_dir": ".cache", - "tls_letsencrypt_challenge_type": "HTTP-01", - "tls_cert_path": "", - "tls_key_path": "", - "acl_policy_path": "", - "dns_config": { - "nameservers": [ - "1.1.1.1" - ], - "domains": [], - "magic_dns": true, - "base_domain": "example.com" - } -} diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 00000000..6a089085 --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,34 @@ +--- +server_url: http://127.0.0.1:8080 +listen_addr: 0.0.0.0:8080 +private_key_path: private.key +derp_map_path: derp.yaml +ephemeral_node_inactivity_timeout: 30m + +# Postgres config +db_type: postgres +db_host: localhost +db_port: 5432 +db_name: headscale +db_user: foo +db_pass: bar + +# SQLite config (uncomment it if you want to use SQLite) +# db_type: sqlite3 +# db_path: db.sqlite + +acme_url: https://acme-v02.api.letsencrypt.org/directory +acme_email: '' +tls_letsencrypt_hostname: '' +tls_letsencrypt_listen: ":http" +tls_letsencrypt_cache_dir: ".cache" +tls_letsencrypt_challenge_type: HTTP-01 +tls_cert_path: '' +tls_key_path: '' +acl_policy_path: '' +dns_config: + nameservers: + - 1.1.1.1 + domains: [] + magic_dns: true + base_domain: example.com From 31344128a012b722b763baabf4bf8ea88282c882 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Wed, 20 Oct 2021 00:17:47 +0200 Subject: [PATCH 09/20] Switch json for yaml in README --- docs/Running.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/Running.md b/docs/Running.md index 6445f678..1df66685 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -24,11 +24,7 @@ ```shell wg genkey > private.key - # Postgres - cp config.json.postgres.example config.json - # or - # SQLite - cp config.json.sqlite.example config.json + cp config.yaml.example config.yaml ``` 4. Create a namespace From 6b0f5da11361f134e32e8442675e8f3f8e923ac5 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Wed, 20 Oct 2021 23:27:59 +0200 Subject: [PATCH 10/20] Separate config examples for sqlite and postgres for the time being --- cmd/headscale/headscale_test.go | 6 ++--- ...ml.example => config.yaml.postgres.example | 4 --- config.yaml.sqlite.example | 26 +++++++++++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) rename config.yaml.example => config.yaml.postgres.example (86%) create mode 100644 config.yaml.sqlite.example diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index bddea94c..0c3add69 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -41,7 +41,7 @@ func (*Suite) TestPostgresConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config.json.postgres.example"), filepath.Join(tmpDir, "config.json")) + err = os.Symlink(filepath.Clean(path+"/../../config.yaml.postgres.example"), filepath.Join(tmpDir, "config.yaml")) if err != nil { c.Fatal(err) } @@ -74,7 +74,7 @@ func (*Suite) TestSqliteConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config.json.sqlite.example"), filepath.Join(tmpDir, "config.json")) + err = os.Symlink(filepath.Clean(path+"/../../config.yaml.sqlite.example"), filepath.Join(tmpDir, "config.yaml")) if err != nil { c.Fatal(err) } @@ -108,7 +108,7 @@ func (*Suite) TestDNSConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config.json.sqlite.example"), filepath.Join(tmpDir, "config.json")) + err = os.Symlink(filepath.Clean(path+"/../../config.yaml.sqlite.example"), filepath.Join(tmpDir, "config.yaml")) if err != nil { c.Fatal(err) } diff --git a/config.yaml.example b/config.yaml.postgres.example similarity index 86% rename from config.yaml.example rename to config.yaml.postgres.example index 6a089085..569b42a9 100644 --- a/config.yaml.example +++ b/config.yaml.postgres.example @@ -13,10 +13,6 @@ db_name: headscale db_user: foo db_pass: bar -# SQLite config (uncomment it if you want to use SQLite) -# db_type: sqlite3 -# db_path: db.sqlite - acme_url: https://acme-v02.api.letsencrypt.org/directory acme_email: '' tls_letsencrypt_hostname: '' diff --git a/config.yaml.sqlite.example b/config.yaml.sqlite.example new file mode 100644 index 00000000..158b1e5b --- /dev/null +++ b/config.yaml.sqlite.example @@ -0,0 +1,26 @@ +--- +server_url: http://127.0.0.1:8080 +listen_addr: 0.0.0.0:8080 +private_key_path: private.key +derp_map_path: derp.yaml +ephemeral_node_inactivity_timeout: 30m + +# SQLite config (uncomment it if you want to use SQLite) +db_type: sqlite3 +db_path: db.sqlite + +acme_url: https://acme-v02.api.letsencrypt.org/directory +acme_email: '' +tls_letsencrypt_hostname: '' +tls_letsencrypt_listen: ":http" +tls_letsencrypt_cache_dir: ".cache" +tls_letsencrypt_challenge_type: HTTP-01 +tls_cert_path: '' +tls_key_path: '' +acl_policy_path: '' +dns_config: + nameservers: + - 1.1.1.1 + domains: [] + magic_dns: true + base_domain: example.com From 4c2f84b211fb5a87b7f56532777a60a45e9ef2e7 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:33:58 +0200 Subject: [PATCH 11/20] Add contributors Action --- .github/workflows/contributors.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/contributors.yml diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml new file mode 100644 index 00000000..7bf15498 --- /dev/null +++ b/.github/workflows/contributors.yml @@ -0,0 +1,25 @@ +name: Contributors + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + add-contributors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: BobAnkh/add-contributors@master + with: + CONTRIBUTOR: '### Contributors' + COLUMN_PER_ROW: '6' + ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}} + IMG_WIDTH: '100' + FONT_SIZE: '14' + PATH: '/README.md' + COMMIT_MESSAGE: 'docs(README): update contributors' + AVATAR_SHAPE: 'round' \ No newline at end of file From 16a90e799c9cdc787be77e0524dc63a62e28948e Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:36:26 +0200 Subject: [PATCH 12/20] Contributors should be working --- .github/workflows/contributors.yml | 2 +- README.md | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index 7bf15498..cef2c2e7 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v2 - uses: BobAnkh/add-contributors@master with: - CONTRIBUTOR: '### Contributors' + CONTRIBUTOR: '## Contributors' COLUMN_PER_ROW: '6' ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}} IMG_WIDTH: '100' diff --git a/README.md b/README.md index 94c4b241..ed65b1e3 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,7 @@ Please have a look at the documentation under [`docs/`](docs/). 1. We have nothing to do with Tailscale, or Tailscale Inc. 2. The purpose of writing this was to learn how Tailscale works. -## More on Tailscale -- https://tailscale.com/blog/how-tailscale-works/ -- https://tailscale.com/blog/tailscale-key-management/ -- https://tailscale.com/blog/an-unlikely-database-migration/ +## Contributors From bb1f17f5af97bff16d23e6d54a3a12356dbab656 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:46:19 +0200 Subject: [PATCH 13/20] Added glossary --- docs/Glossary.md | 3 +++ docs/Running.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/Glossary.md diff --git a/docs/Glossary.md b/docs/Glossary.md new file mode 100644 index 00000000..17a39840 --- /dev/null +++ b/docs/Glossary.md @@ -0,0 +1,3 @@ +# Glossary + +- Namespace: Collection of Taiscale nodes that can see each other. In Tailscale.com is called Tailnet. \ No newline at end of file diff --git a/docs/Running.md b/docs/Running.md index 1df66685..784d2226 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -1,6 +1,6 @@ # Running headscale -1. Download the Headscale binary https://github.com/juanfont/headscale/releases, and place it somewhere in your PATH or use the docker container +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 From a0bfad6d6e997cd51ff2dddc83cd8929206eca84 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:48:29 +0200 Subject: [PATCH 14/20] Headscale is not capitalized --- README.md | 4 ++-- docs/DNS.md | 6 +++--- docs/TLS.md | 10 +++++----- k8s/README.md | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ed65b1e3..19742d8e 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Everything in Tailscale is Open Source, except the GUI clients for proprietary O The control server works as an exchange point of Wireguard public keys for the nodes in the Tailscale network. It also assigns the IP addresses of the clients, creates the boundaries between each user, enables sharing machines between users, and exposes the advertised routes of your nodes. -Headscale implements this coordination server. +headscale implements this coordination server. ## Status - [x] Base functionality (nodes can communicate with each other) - [x] Node registration through the web flow - [x] Network changes are relayed to the nodes -- [x] Multiple namespaces support (~tailnets in Tailscale.com naming) +- [x] Namespaces support (~tailnets in Tailscale.com naming) - [x] Routing (advertise & accept, including exit nodes) - [x] Node registration via pre-auth keys (including reusable keys, and ephemeral node support) - [x] JSON-formatted output diff --git a/docs/DNS.md b/docs/DNS.md index 10f99b79..184d9039 100644 --- a/docs/DNS.md +++ b/docs/DNS.md @@ -1,12 +1,12 @@ -# DNS in Headscale +# DNS in headscale -Headscale supports Tailscale's DNS configuration and MagicDNS. Please have a look to their KB to better understand what this means: +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. +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 diff --git a/docs/TLS.md b/docs/TLS.md index c4a7dfad..0133d5c6 100644 --- a/docs/TLS.md +++ b/docs/TLS.md @@ -8,7 +8,7 @@ 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. +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 @@ -16,16 +16,16 @@ 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. +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 -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. +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`. +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 -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`. +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`. diff --git a/k8s/README.md b/k8s/README.md index 2f187abb..45574b48 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -1,7 +1,7 @@ -# Deploying Headscale on Kubernetes +# Deploying headscale on Kubernetes This directory contains [Kustomize](https://kustomize.io) templates that deploy -Headscale in various configurations. +headscale in various configurations. These templates currently support Rancher k3s. Other clusters may require adaptation, especially around volume claims and ingress. @@ -72,10 +72,10 @@ Usage: Available Commands: help Help about any command - namespace Manage the namespaces of Headscale - node Manage the nodes of Headscale - preauthkey Handle the preauthkeys in Headscale - routes Manage the routes of Headscale + namespace Manage the namespaces of headscale + node Manage the nodes of headscale + preauthkey Handle the preauthkeys in headscale + routes Manage the routes of headscale serve Launches the headscale server version Print the version. From 4be0b3f556f8fa66f07da437fcdb7c4dbd026e45 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:54:29 +0200 Subject: [PATCH 15/20] Mention disable check updates in the doc --- docs/Running.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Running.md b/docs/Running.md index 784d2226..310bda2c 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -1,6 +1,6 @@ # Running headscale -1. Download the headscale binary https://github.com/juanfont/headscale/releases, and place it somewhere in your PATH or use the docker container +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 @@ -19,7 +19,7 @@ POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -p 5432:5432 -d postgres ``` -3. Create a WireGuard Private key and headscale configuration +3. Create a WireGuard private key and headscale configuration ```shell wg genkey > private.key @@ -75,7 +75,7 @@ headscale/headscale:x.x.x headscale serve ``` -6. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder +6. If you used tailscale.com before in your nodes, make sure you clear the tailscaled data folder ```shell systemctl stop tailscaled @@ -142,4 +142,4 @@ Alternatively, you can use Auth Keys to register your machines: 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 the commands from headscale support adding `-o json` or `-o json-line` to get a nicely JSON-formatted output. \ No newline at end of file +Please bear in mind that all headscale commands support adding `-o json` or `-o json-line` to get nicely JSON-formatted output. \ No newline at end of file From e9ffd366dd84844608b13d1c8a09b9687efca9d2 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:54:41 +0200 Subject: [PATCH 16/20] Improvements here and there --- docs/Configuration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index a293d358..0d22ace4 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -10,9 +10,10 @@ Headscale will look for a configuration file named `config.yaml` (or `config.jso 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) +`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 From 636943c7154c4bf9e9ad03dc2af709e9c6622c1a Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:57:18 +0200 Subject: [PATCH 17/20] Improved docker cmd --- docs/Running.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/Running.md b/docs/Running.md index 310bda2c..c5e0b12f 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -15,8 +15,12 @@ 2. (Optional, you can also use SQLite) Get yourself a PostgreSQL DB running ```shell - docker run --name headscale -e POSTGRES_DB=headscale -e \ - POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -p 5432:5432 -d postgres + docker run --name headscale \ + -e POSTGRES_DB=headscale + -e POSTGRES_USER=foo \ + -e POSTGRES_PASSWORD=bar \ + -p 5432:5432 \ + -d postgres ``` 3. Create a WireGuard private key and headscale configuration From b93aa723cbc5c895b4e5973012cfbbf324e4b4f1 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 20:58:30 +0200 Subject: [PATCH 18/20] Run contributors on merge to master --- .github/workflows/contributors.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index cef2c2e7..7350148a 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -4,9 +4,6 @@ on: push: branches: - main - pull_request: - branches: - - main jobs: add-contributors: From 561c15bbe856fe8de4cfb91609b9f0a3ed4fc960 Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 21:01:52 +0200 Subject: [PATCH 19/20] Prettier --- docs/Configuration.md | 4 ++-- docs/DNS.md | 19 +++++++++---------- docs/Glossary.md | 2 +- docs/Running.md | 4 ++-- docs/TLS.md | 10 +++------- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 0d22ace4..fa766428 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -40,6 +40,7 @@ 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 @@ -49,6 +50,7 @@ db_pass: bar ``` SQLite + ```yaml db_type: sqlite3 db_path: db.sqlite @@ -60,12 +62,10 @@ The fields starting with `db_` are used for the DB connection information. 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. diff --git a/docs/DNS.md b/docs/DNS.md index 184d9039..e51feaf6 100644 --- a/docs/DNS.md +++ b/docs/DNS.md @@ -8,10 +8,9 @@ headscale supports Tailscale's DNS configuration and MagicDNS. Please have a loo 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. +The setup is done via the `config.yaml` file, under the `dns_config` key. ```yaml server_url: http://127.0.0.1:8001 @@ -19,21 +18,21 @@ listen_addr: 0.0.0.0:8001 private_key_path: private.key 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 + 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. +- `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. \ No newline at end of file +- `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/Glossary.md b/docs/Glossary.md index 17a39840..74ecfb89 100644 --- a/docs/Glossary.md +++ b/docs/Glossary.md @@ -1,3 +1,3 @@ # Glossary -- Namespace: Collection of Taiscale nodes that can see each other. In Tailscale.com is called Tailnet. \ No newline at end of file +- Namespace: Collection of Taiscale nodes that can see each other. In Tailscale.com is called Tailnet. diff --git a/docs/Running.md b/docs/Running.md index c5e0b12f..08373653 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -16,7 +16,7 @@ ```shell docker run --name headscale \ - -e POSTGRES_DB=headscale + -e POSTGRES_DB=headscale -e POSTGRES_USER=foo \ -e POSTGRES_PASSWORD=bar \ -p 5432:5432 \ @@ -146,4 +146,4 @@ Alternatively, you can use Auth Keys to register your machines: 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. \ No newline at end of file +Please bear in mind that all headscale commands support adding `-o json` or `-o json-line` to get nicely JSON-formatted output. diff --git a/docs/TLS.md b/docs/TLS.md index 0133d5c6..47de1cd7 100644 --- a/docs/TLS.md +++ b/docs/TLS.md @@ -1,8 +1,7 @@ - # Running the service via TLS (optional) ```yaml -tls_letsencrypt_hostname: '' +tls_letsencrypt_hostname: "" tls_letsencrypt_listen: ":http" tls_letsencrypt_cache_dir: ".cache" tls_letsencrypt_challenge_type: HTTP-01 @@ -10,15 +9,13 @@ 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: '' +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 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. @@ -28,4 +25,3 @@ If you need to change the ip and/or port used by headscale for the Let's Encrypt ## 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`. - From 672d8474b943aa4979844c442be3b790dcfbc04a Mon Sep 17 00:00:00 2001 From: Juan Font Alonso Date: Thu, 21 Oct 2021 21:18:50 +0200 Subject: [PATCH 20/20] PRettier on the yamls --- .github/workflows/build.yml | 2 +- .github/workflows/contributors.yml | 22 +++++++++++----------- .github/workflows/release.yml | 28 ++++++++++------------------ .goreleaser.yml | 15 +++++++-------- 4 files changed, 29 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90c48e92..f1773af9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,4 +33,4 @@ jobs: - uses: actions/upload-artifact@v2 with: name: headscale-linux - path: headscale \ No newline at end of file + path: headscale diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index 7350148a..248cf3f7 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -9,14 +9,14 @@ jobs: add-contributors: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: BobAnkh/add-contributors@master - with: - CONTRIBUTOR: '## Contributors' - COLUMN_PER_ROW: '6' - ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}} - IMG_WIDTH: '100' - FONT_SIZE: '14' - PATH: '/README.md' - COMMIT_MESSAGE: 'docs(README): update contributors' - AVATAR_SHAPE: 'round' \ No newline at end of file + - uses: actions/checkout@v2 + - uses: BobAnkh/add-contributors@master + with: + CONTRIBUTOR: "## Contributors" + COLUMN_PER_ROW: "6" + ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}} + IMG_WIDTH: "100" + FONT_SIZE: "14" + PATH: "/README.md" + COMMIT_MESSAGE: "docs(README): update contributors" + AVATAR_SHAPE: "round" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a3df6a0..c0605f10 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,20 +4,18 @@ name: release on: push: tags: - - "*" # triggers only if push new tag version + - "*" # triggers only if push new tag version workflow_dispatch: jobs: goreleaser: - runs-on: ubuntu-18.04 # due to CGO we need to user an older version + runs-on: ubuntu-18.04 # due to CGO we need to user an older version steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - - - name: Set up Go + - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.16 @@ -26,8 +24,7 @@ jobs: run: | sudo apt update sudo apt install -y gcc-aarch64-linux-gnu - - - name: Run GoReleaser + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: distribution: goreleaser @@ -39,13 +36,11 @@ jobs: docker-release: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - - - name: Docker meta + - name: Docker meta id: meta uses: docker/metadata-action@v3 with: @@ -58,21 +53,18 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=sha - - - name: Login to DockerHub + - name: Login to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GHCR + - name: Login to GHCR uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push + - name: Build and push id: docker_build uses: docker/build-push-action@v2 with: diff --git a/.goreleaser.yml b/.goreleaser.yml index ad4fea70..d20aed6b 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -6,7 +6,7 @@ before: builds: - id: darwin-amd64 main: ./cmd/headscale/headscale.go - mod_timestamp: '{{ .CommitTimestamp }}' + mod_timestamp: "{{ .CommitTimestamp }}" goos: - darwin goarch: @@ -23,7 +23,7 @@ builds: - id: linux-armhf main: ./cmd/headscale/headscale.go - mod_timestamp: '{{ .CommitTimestamp }}' + mod_timestamp: "{{ .CommitTimestamp }}" goos: - linux goarch: @@ -42,7 +42,6 @@ builds: ldflags: - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} - - id: linux-amd64 env: - CGO_ENABLED=1 @@ -51,7 +50,7 @@ builds: goarch: - amd64 main: ./cmd/headscale/headscale.go - mod_timestamp: '{{ .CommitTimestamp }}' + mod_timestamp: "{{ .CommitTimestamp }}" ldflags: - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} @@ -64,7 +63,7 @@ builds: - CGO_ENABLED=1 - CC=aarch64-linux-gnu-gcc main: ./cmd/headscale/headscale.go - mod_timestamp: '{{ .CommitTimestamp }}' + mod_timestamp: "{{ .CommitTimestamp }}" ldflags: - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} @@ -79,12 +78,12 @@ archives: format: binary checksum: - name_template: 'checksums.txt' + name_template: "checksums.txt" snapshot: name_template: "{{ .Tag }}-next" changelog: sort: asc filters: exclude: - - '^docs:' - - '^test:' + - "^docs:" + - "^test:"