docs: solidify DOCKER_SOCK and ROOT_API_KEY usage
This commit is contained in:
parent
694b22f205
commit
c7e59b137c
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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'
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user