docs: solidify DOCKER_SOCK and ROOT_API_KEY usage

This commit is contained in:
Aarnav Tale 2024-05-22 12:43:42 -04:00
parent 694b22f205
commit c7e59b137c
No known key found for this signature in database
7 changed files with 68 additions and 23 deletions

View File

@ -1,4 +1,4 @@
API_KEY=abcdefghijklmnopqrstuvwxyz ROOT_API_KEY=abcdefghijklmnopqrstuvwxyz
COOKIE_SECRET=abcdefghijklmnopqrstuvwxyz COOKIE_SECRET=abcdefghijklmnopqrstuvwxyz
DISABLE_API_KEY_LOGIN=true DISABLE_API_KEY_LOGIN=true
HEADSCALE_CONTAINER=headscale HEADSCALE_CONTAINER=headscale

View File

@ -25,7 +25,8 @@ export interface HeadplaneContext {
} }
docker?: { docker?: {
sock: string url: string
sock: boolean
container: string container: string
} }
@ -163,19 +164,48 @@ async function checkAcl(config?: HeadscaleConfig) {
} }
async function checkDocker() { async function checkDocker() {
const path = process.env.DOCKER_SOCK ?? '/var/run/docker.sock' const path = process.env.DOCKER_SOCK ?? 'unix:///var/run/docker.sock'
let url: URL | undefined
try { try {
await access(path, constants.R_OK) url = new URL(path)
} catch { } catch {
return return
} }
// The API is available as an HTTP endpoint
if (url.protocol === 'tcp:') {
url.protocol = 'http:'
}
// Check if the socket is accessible
if (url.protocol === 'unix:') {
try {
await access(path, constants.R_OK)
} catch {
return
}
}
if (url.protocol === 'http:') {
try {
await fetch(new URL('/v1.30/version', url).href)
} catch {
return
}
}
if (url.protocol !== 'http:' && url.protocol !== 'unix:') {
return
}
if (!process.env.HEADSCALE_CONTAINER) { if (!process.env.HEADSCALE_CONTAINER) {
return return
} }
return { return {
sock: path, url: url.href,
sock: url.protocol === 'unix:',
container: process.env.HEADSCALE_CONTAINER, container: process.env.HEADSCALE_CONTAINER,
} }
} }

View File

@ -1,5 +1,3 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-constant-condition */
import { setTimeout } from 'node:timers/promises' import { setTimeout } from 'node:timers/promises'
import { Client } from 'undici' import { Client } from 'undici'
@ -13,9 +11,12 @@ export async function sighupHeadscale() {
return return
} }
const client = new Client('http://localhost', { // Supports the DOCKER_SOCK environment variable
socketPath: context.docker.sock, const client = context.docker.sock
}) ? new Client('http://localhost', {
socketPath: context.docker.url,
})
: new Client(context.docker.url)
const response = await client.request({ const response = await client.request({
method: 'POST', method: 'POST',
@ -33,9 +34,12 @@ export async function restartHeadscale() {
return return
} }
const client = new Client('http://localhost', { // Supports the DOCKER_SOCK environment variable
socketPath: context.docker.sock, const client = context.docker.sock
}) ? new Client('http://localhost', {
socketPath: context.docker.url,
})
: new Client(context.docker.url)
const response = await client.request({ const response = await client.request({
method: 'POST', method: 'POST',
@ -48,14 +52,15 @@ export async function restartHeadscale() {
// Wait for Headscale to restart before continuing // Wait for Headscale to restart before continuing
let attempts = 0 let attempts = 0
// eslint-disable-next-line
while (true) { while (true) {
try { try {
// Acceptable blank because API_KEY is not required // Acceptable blank because ROOT_API_KEY is not required
await pull('v1/apikey', process.env.API_KEY ?? '') await pull('v1/apikey', context.oidc?.rootKey ?? '')
return return
} catch (error) { } catch (error) {
// This means the server is up but the API key is invalid // This means the server is up but the API key is invalid
// This can happen if the user only uses API_KEY via cookies // This can happen if the user only uses ROOT_API_KEY via cookies
if (error instanceof HeadscaleError && error.status === 401) { if (error instanceof HeadscaleError && error.status === 401) {
break break
} }

View File

@ -151,8 +151,7 @@ export async function finishOidc(oidc: OidcConfig, req: Request) {
const keyResponse = await post<{ apiKey: string }>( const keyResponse = await post<{ apiKey: string }>(
'v1/apikey', 'v1/apikey',
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion oidc.rootKey,
process.env.API_KEY!,
{ {
expiration: expDate, expiration: expDate,
}, },

View File

@ -30,10 +30,14 @@ The ACL file path is read from the following sources in order of priority:
The Docker integration can be used to automatically reload the configuration or ACLs when they are changed. The Docker integration can be used to automatically reload the configuration or ACLs when they are changed.
In order for this to work, you'll need to pass in the `HEADSCALE_CONTAINER` environment variable. In order for this to work, you'll need to pass in the `HEADSCALE_CONTAINER` environment variable.
You'll also need to ensure that `/var/run/docker.sock` is mounted if Headplane is running in a container.
This should be either the name or ID of the Headscale container (you can retrieve this using `docker ps`). This should be either the name or ID of the Headscale container (you can retrieve this using `docker ps`).
If the other integrations aren't setup, then Headplane will automatically disable the Docker integration. If the other integrations aren't setup, then Headplane will automatically disable the Docker integration.
By default the integration will check for `/var/run/docker.sock`, however you can override this by
setting the `DOCKER_SOCK` environment variable if you use a different configuration than the default.
When setting `DOCKER_SOCK`, you'll need to include the protocol (e.g., `unix://` or `tcp://`).
Headplane currently does not support the HTTPS protocol for the Docker socket.
## Deployment ## Deployment
Requirements: Requirements:
@ -85,7 +89,7 @@ services:
# This NEEDS to be set with OIDC, regardless of what's in the config # This NEEDS to be set with OIDC, regardless of what's in the config
# This needs to be a very long-lived (999 day) API key used to create # This needs to be a very long-lived (999 day) API key used to create
# shorter ones for OIDC and allow the OIDC functionality to work # shorter ones for OIDC and allow the OIDC functionality to work
API_KEY: 'abcdefghijklmnopqrstuvwxyz' ROOT_API_KEY: 'abcdefghijklmnopqrstuvwxyz'
``` ```
> For a breakdown of each configuration variable, please refer to the [Configuration](/docs/Configuration.md) guide. > For a breakdown of each configuration variable, please refer to the [Configuration](/docs/Configuration.md) guide.

View File

@ -31,8 +31,7 @@ services:
COOKIE_SECRET: 'abcdefghijklmnopqrstuvwxyz' COOKIE_SECRET: 'abcdefghijklmnopqrstuvwxyz'
# These are all optional! # These are all optional!
HEADSCALE_CONTAINER: 'headscale' ROOT_API_KEY: 'abcdefghijklmnopqrstuvwxyz'
API_KEY: 'abcdefghijklmnopqrstuvwxyz'
OIDC_CLIENT_ID: 'headscale' OIDC_CLIENT_ID: 'headscale'
OIDC_ISSUER: 'https://sso.example.com' OIDC_ISSUER: 'https://sso.example.com'
OIDC_CLIENT_SECRET: 'super_secret_client_secret' OIDC_CLIENT_SECRET: 'super_secret_client_secret'

View File

@ -13,6 +13,14 @@ You can configure Headplane using environment variables.
- **`PORT`**: The port to bind the server to (default: `3000`). - **`PORT`**: The port to bind the server to (default: `3000`).
- **`CONFIG_FILE`**: The path to the Headscale `config.yaml` (default: `/etc/headscale/config.yaml`). - **`CONFIG_FILE`**: The path to the Headscale `config.yaml` (default: `/etc/headscale/config.yaml`).
- **`ACL_FILE`**: The path to the ACL file (default: `/etc/headscale/acl_policy.json`, not needed if you have `acl_policy_path` in your config). - **`ACL_FILE`**: The path to the ACL file (default: `/etc/headscale/acl_policy.json`, not needed if you have `acl_policy_path` in your config).
#### Docker Integration
The Docker integration allows Headplane to manage the Headscale docker container.
You'll need to provide these variables if you want to use this feature.
Keep in mind that `DOCKER_SOCK` must start with a protocol (e.g., `unix://`).
Secure API is currently not supported.
- **`DOCKER_SOCK`**: The protocol and path to the Docker socket (default: `unix:///var/run/docker.sock`).
- **`HEADSCALE_CONTAINER`**: The name of the Headscale container (required for Docker integration). - **`HEADSCALE_CONTAINER`**: The name of the Headscale container (required for Docker integration).
### SSO/OpenID Connect ### SSO/OpenID Connect
@ -23,7 +31,7 @@ If you use the Headscale configuration integration, these are not required.
- **`OIDC_ISSUER`**: The issuer URL of your OIDC provider. - **`OIDC_ISSUER`**: The issuer URL of your OIDC provider.
- **`OIDC_CLIENT_ID`**: The client ID of your OIDC provider. - **`OIDC_CLIENT_ID`**: The client ID of your OIDC provider.
- **`OIDC_CLIENT_SECRET`**: The client secret of your OIDC provider. - **`OIDC_CLIENT_SECRET`**: The client secret of your OIDC provider.
- **`API_KEY`**: An API key used to issue new ones for sessions (keep expiry fairly long). - **`ROOT_API_KEY`**: An API key used to issue new ones for sessions (keep expiry fairly long).
- **`DISABLE_API_KEY_LOGIN`**: If you want to disable API key login, set this to `true`. - **`DISABLE_API_KEY_LOGIN`**: If you want to disable API key login, set this to `true`.
Here's what an example with Authelia would look like if you used the same client for both Headscale and Headplane. Here's what an example with Authelia would look like if you used the same client for both Headscale and Headplane.