From 54ac529917fd748baac2f5246ff341460f424bb9 Mon Sep 17 00:00:00 2001 From: Niklas Rosenstein Date: Fri, 11 Oct 2024 13:55:57 +0200 Subject: [PATCH] feature: add `ENTYRYPOINT_IDLE` variable --- README.md | 89 +++++++++++++++++++++------------- headscale-fly-io/entrypoint.sh | 10 ++++ 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 81b5d20..eeb614b 100644 --- a/README.md +++ b/README.md @@ -134,39 +134,62 @@ Many Headscale configuration options can be set vie the `[env]` section in your following is a complete list of the environment variables the Headscale-on-Fly.io recognizes, including those that are expected to be set automatically. -| Variable | Default | Description | -|--------------------------------------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `AWS_ACCESS_KEY_ID` | (automatic) | Access key for the object storage for Litestream SQlite replication. Usually set automatically by Fly.io when enabling the Tigris integration. | -| `AWS_SECRET_ACCESS_KEY` | (automatic) | Secret key for the object storage. | -| `AWS_REGION` | (automatic) | | -| `AWS_ENDPOINT_URL_S3` | (automatic) | | -| `BUCKET_NAME` | (automatic) | | -| `FLY_APP_NAME` | (automatic) | Used to determine the Headscale server URL, if `HEADSCALE_SERVER_URL` is not set. | -| `HEADSCALE_SERVER_URL` | `https://${FLY_APP_NAME}.fly.dev` | URL of the Headscale server. | -| `HEADSCALE_DNS_BASE_DOMAIN` | `tailnet` | Base domain for members in the Tailnet. This **must not** be a part of the `HEADSCALE_SERVER_URL`. | -| `HEADSCALE_LOG_LEVEL` | `info` | Log level for the Headscale server. | -| `HEADSCALE_PREFIXES_V4` | `100.64.0.0/10` | Prefix for IP-v4 addresses of nodes in the Tailnet. | -| `HEADSCALE_PREFIXES_V6` | `fd7a:115c:a1e0::/48` | Prefix for IP-v6 addresses of nodes in the Tailnet. | -| `HEADSCALE_PREFIXES_ALLOCATION` | `random` | How IPs are allocated to nodes joining the Tailnet. Can be `random` or `sequential`. | -| `HEADSCALE_OIDC_ISSUER` | n/a | If set, enables OIDC configuration. Must be set to the URL of the OIDC issuer. For example, if you use Keycloak, it might look something like `https://mykeycloak.com/realms/main` | -| `HEADSCALE_OIDC_CLIENT_ID` | n/a, but required | The OIDC client ID. | -| `HEADSCALE_OIDC_CLIENT_SECRET` | n/a, but required | The OIDC client secret. **Important:** Configure this through `fly secrets set`. | -| `HEADSCALE_OIDC_SCOPES` | `openid, profile, email` | A comma-separated list of OpenID scopes. (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | -| `HEADSCALE_OIDC_ALLOWED_GROUPS` | n/a | A comma-separated list of groups to permit. Note that this requires your OIDC client to be configured with a groups claim mapping. In some cases you may need to prefix the group name with a slash (e.g. `/headscale`). (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | -| `HEADSCALE_OIDC_ALLOWED_DOMAINS` | n/a | A comma-separated list of email domains to permit. (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | -| `HEADSCALE_OIDC_ALLOWED_USERS` | n/a | A comma-separated list of users to permit. (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | -| `HEADSCALE_OIDC_STRIP_EMAIL_DOMAIN` | `true` | Whether to strip the email domain for the Headscale user names. | -| `HEADSCALE_OIDC_EXPIRY` | `180d` | The amount of time from a node is authenticated with OpenID until it expires and needs to reauthenticate. Setting the value to "0" will mean no expiry. | -| `HEADSCALE_OIDC_USE_EXPIRY_FROM_TOKEN` | `true` | Use the expiry from the token received from OpenID when the user logged in, this will typically lead to frequent need to reauthenticate and should only been enabled if you know what you are doing. If enabled, `HEADSCALE_OIDC_EXPIRY` is ignored. | -| `HEADSCALE_OIDC_ONLY_START_IF_OIDC_IS_AVAILABLE` | `true` | Fail startup if the OIDC server cannot be reached. | -| `LITESTREAM_ENABLED` | `true` | Whether to restore and replicate the SQlite database with Litestream. You likely never want to turn this option off, as you will loose your SQlite database on restarts. | -| `LITESTREAM_RETENTION` | `24h` | Configure the Litestream retention period. Retention is enforced periodically and can be changed with `LITESTREAM_RETENTION_CHECK_INTERVAL`. | -| `LITESTREAM_RETENTION_CHECK_INTERVAL` | `1h` | The interval at which retention should be applied. | -| `LITESTREAM_VALIDATION_INTERVAL` | `12h` | The interval at which Litestream does a separate restore of the database and validates the result vs. the current database. | -| `IMPORT_DATABASE` | `false` | If set to `true`, the entrypoint will check for an `import-db.sqlite` file in the S3 bucket to restore, and use that instead of `litestream restore` if it exists. Note that the file will not be removed, so you should disable this option and remove the file from the bucket once the import is complete. | -| `ENTRYPOINT_DEBUG` | n/a | If set to `true`, enables logging of executed commands in the container entrypoint and prints out the Headscale configuration before startup. Use with caution, as it might reveal secret values to stdout (and thus into Fly.io's logging infrastructure). | -| `NOISE_PRIVATE_KEY` | n/a, but required | Noise private key for Headscale. Generate with `echo privkey:$(openssl rand -hex 32)`. **Important:** Pass this value securely with `fly secrets set`. | -| `AGE_SECRET_KEY` | n/a, but required | [age] Secret key for encryption your Litestream SQLite replication. | +__System variables__ + +| Variable | Default | Description | +|-------------------------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------| +| `AWS_ACCESS_KEY_ID` | (automatic) | Access key for the object storage for Litestream SQlite replication. Usually set automatically by Fly.io when enabling the Tigris integration. | +| `AWS_SECRET_ACCESS_KEY` | (automatic) | Secret key for the object storage. | +| `AWS_REGION` | (automatic) | | +| `AWS_ENDPOINT_URL_S3` | (automatic) | | +| `BUCKET_NAME` | (automatic) | | +| `FLY_APP_NAME` | (automatic) | Used to determine the Headscale server URL, if `HEADSCALE_SERVER_URL` is not set. | + +__Security variables__ + +| Variable | Default | Description | +|---------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| `AGE_SECRET_KEY` | n/a, but required | [age] Secret key for encryption your Litestream SQLite replication. | +| `NOISE_PRIVATE_KEY` | n/a, but required | Noise private key for Headscale. Generate with `echo privkey:$(openssl rand -hex 32)`. **Important:** Pass this value securely with `fly secrets set`. | + +__Headscale configuration variables__ + +| Variable | Default | Description | +|--------------------------------------------------|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `HEADSCALE_SERVER_URL` | `https://${FLY_APP_NAME}.fly.dev` | URL of the Headscale server. | +| `HEADSCALE_DNS_BASE_DOMAIN` | `tailnet` | Base domain for members in the Tailnet. This **must not** be a part of the `HEADSCALE_SERVER_URL`. | +| `HEADSCALE_LOG_LEVEL` | `info` | Log level for the Headscale server. | +| `HEADSCALE_PREFIXES_V4` | `100.64.0.0/10` | Prefix for IP-v4 addresses of nodes in the Tailnet. | +| `HEADSCALE_PREFIXES_V6` | `fd7a:115c:a1e0::/48` | Prefix for IP-v6 addresses of nodes in the Tailnet. | +| `HEADSCALE_PREFIXES_ALLOCATION` | `random` | How IPs are allocated to nodes joining the Tailnet. Can be `random` or `sequential`. | +| `HEADSCALE_OIDC_ISSUER` | n/a | If set, enables OIDC configuration. Must be set to the URL of the OIDC issuer. For example, if you use Keycloak, it might look something like `https://mykeycloak.com/realms/main` | +| `HEADSCALE_OIDC_CLIENT_ID` | n/a, but required if oidc is enabled | The OIDC client ID. | +| `HEADSCALE_OIDC_CLIENT_SECRET` | n/a, but required if oidc is enabled | The OIDC client secret. **Important:** Configure this through `fly secrets set`. | +| `HEADSCALE_OIDC_SCOPES` | `openid, profile, email` | A comma-separated list of OpenID scopes. (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | +| `HEADSCALE_OIDC_ALLOWED_GROUPS` | n/a | A comma-separated list of groups to permit. Note that this requires your OIDC client to be configured with a groups claim mapping. In some cases you may need to prefix the group name with a slash (e.g. `/headscale`). (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | +| `HEADSCALE_OIDC_ALLOWED_DOMAINS` | n/a | A comma-separated list of email domains to permit. (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | +| `HEADSCALE_OIDC_ALLOWED_USERS` | n/a | A comma-separated list of users to permit. (The comma-separated list must be valid YAML if placed inside `[ ... ]`.) | +| `HEADSCALE_OIDC_STRIP_EMAIL_DOMAIN` | `true` | Whether to strip the email domain for the Headscale user names. | +| `HEADSCALE_OIDC_EXPIRY` | `180d` | The amount of time from a node is authenticated with OpenID until it expires and needs to reauthenticate. Setting the value to "0" will mean no expiry. | +| `HEADSCALE_OIDC_USE_EXPIRY_FROM_TOKEN` | `true` | Use the expiry from the token received from OpenID when the user logged in, this will typically lead to frequent need to reauthenticate and should only been enabled if you know what you are doing. If enabled, `HEADSCALE_OIDC_EXPIRY` is ignored. | +| `HEADSCALE_OIDC_ONLY_START_IF_OIDC_IS_AVAILABLE` | `true` | Fail startup if the OIDC server cannot be reached. | + +__Litestream configuration variables__ + +| Variable | Default | Description | +|---------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `LITESTREAM_ENABLED` | `true` | Whether to restore and replicate the SQlite database with Litestream. You likely never want to turn this option off, as you will loose your SQlite database on restarts. | +| `LITESTREAM_RETENTION` | `24h` | Configure the Litestream retention period. Retention is enforced periodically and can be changed with `LITESTREAM_RETENTION_CHECK_INTERVAL`. | +| `LITESTREAM_RETENTION_CHECK_INTERVAL` | `1h` | The interval at which retention should be applied. | +| `LITESTREAM_VALIDATION_INTERVAL` | `12h` | The interval at which Litestream does a separate restore of the database and validates the result vs. the current database. | + +__Maintenance variables__ + +| Variable | Default | Description | +|--------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ENTRYPOINT_DEBUG` | n/a | If set to `true`, enables logging of executed commands in the container entrypoint and prints out the Headscale configuration before startup. Use with caution, as it might reveal secret values to stdout (and thus into Fly.io's logging infrastructure). | +| `ENTRYPOINT_IDLE` | `false` | If set to `true`, go idle instead of starting the Headscale server. Will also go idle if an intermediate error occurs. Useful for recovering secrets when the deployment critically fails. | +| `IMPORT_DATABASE` | `false` | If set to `true`, the entrypoint will check for an `import-db.sqlite` file in the S3 bucket to restore, and use that instead of `litestream restore` if it exists. Note that the file will not be removed, so you should disable this option and remove the file from the bucket once the import is complete. | ### Migrating to Headscale on Fly.io diff --git a/headscale-fly-io/entrypoint.sh b/headscale-fly-io/entrypoint.sh index f589315..a23f699 100644 --- a/headscale-fly-io/entrypoint.sh +++ b/headscale-fly-io/entrypoint.sh @@ -34,9 +34,17 @@ assert_file_exists() { fi } +maybe_idle() { + if [ "${ENTRYPOINT_IDLE:-false}" = "true" ]; then + info "ENTRYPOINT_IDLE=true, entering idle state" + sleep infinity + fi +} + on_error() { [ $? -eq 0 ] && exit error "an unexpected error occurred." + maybe_idle } trap 'on_error' EXIT @@ -136,6 +144,8 @@ elif [ "${LITESTREAM_ENABLED:-true}" = "true" ]; then info_run litestream restore -if-db-not-exists -if-replica-exists -replica s3 "$HEADSCALE_DB_PATH" fi +maybe_idle + # Run Headscale. if [ "${LITESTREAM_ENABLED:-true}" = "true" ]; then info_run exec litestream replicate -exec "headscale serve"