mirror of
https://github.com/tale/headplane.git
synced 2026-09-04 11:15:42 +00:00
fix: validate and respect oidc validation settings
This commit is contained in:
@@ -10,6 +10,7 @@ import { parse } from 'yaml'
|
|||||||
|
|
||||||
import { IntegrationFactory, loadIntegration } from '~/integration'
|
import { IntegrationFactory, loadIntegration } from '~/integration'
|
||||||
import { HeadscaleConfig, loadConfig } from '~/utils/config/headscale'
|
import { HeadscaleConfig, loadConfig } from '~/utils/config/headscale'
|
||||||
|
import { testOidc } from '~/utils/oidc'
|
||||||
import log from '~/utils/log'
|
import log from '~/utils/log'
|
||||||
|
|
||||||
export interface HeadplaneContext {
|
export interface HeadplaneContext {
|
||||||
@@ -157,6 +158,7 @@ async function checkOidc(config?: HeadscaleConfig) {
|
|||||||
let client = process.env.OIDC_CLIENT_ID
|
let client = process.env.OIDC_CLIENT_ID
|
||||||
let secret = process.env.OIDC_CLIENT_SECRET
|
let secret = process.env.OIDC_CLIENT_SECRET
|
||||||
let method = process.env.OIDC_CLIENT_SECRET_METHOD ?? 'client_secret_basic'
|
let method = process.env.OIDC_CLIENT_SECRET_METHOD ?? 'client_secret_basic'
|
||||||
|
let skip = process.env.OIDC_SKIP_CONFIG_VALIDATION === 'true'
|
||||||
|
|
||||||
log.debug('CTXT', 'Checking OIDC environment variables')
|
log.debug('CTXT', 'Checking OIDC environment variables')
|
||||||
log.debug('CTXT', 'Issuer: %s', issuer)
|
log.debug('CTXT', 'Issuer: %s', issuer)
|
||||||
@@ -171,6 +173,14 @@ async function checkOidc(config?: HeadscaleConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (issuer && client && secret) {
|
if (issuer && client && secret) {
|
||||||
|
if (!skip) {
|
||||||
|
log.debug('CTXT', 'Validating OIDC configuration from environment variables')
|
||||||
|
testOidc(issuer, client, secret)
|
||||||
|
} else {
|
||||||
|
log.debug('CTXT', 'OIDC_SKIP_CONFIG_VALIDATION is set')
|
||||||
|
log.debug('CTXT', 'Skipping OIDC configuration validation')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
issuer,
|
issuer,
|
||||||
client,
|
client,
|
||||||
@@ -214,6 +224,15 @@ async function checkOidc(config?: HeadscaleConfig) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.oidc.only_start_if_oidc_is_available) {
|
||||||
|
log.debug('CTXT', 'Validating OIDC configuration from headscale config')
|
||||||
|
testOidc(issuer, client, secret)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.debug('CTXT', 'OIDC validation is disabled in headscale config')
|
||||||
|
log.debug('CTXT', 'Skipping OIDC configuration validation')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
issuer,
|
issuer,
|
||||||
client,
|
client,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
|
|
||||||
import { post } from '~/utils/headscale'
|
import { post } from '~/utils/headscale'
|
||||||
import { commitSession, getSession } from '~/utils/sessions'
|
import { commitSession, getSession } from '~/utils/sessions'
|
||||||
|
import log from '~/utils/log'
|
||||||
|
|
||||||
import { HeadplaneContext } from './config/headplane'
|
import { HeadplaneContext } from './config/headplane'
|
||||||
|
|
||||||
@@ -169,3 +170,30 @@ export async function finishOidc(oidc: OidcConfig, req: Request) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Runs at application startup to validate the OIDC configuration
|
||||||
|
export async function testOidc(issuer: string, client: string, secret: string) {
|
||||||
|
const oidcClient = {
|
||||||
|
client_id: client,
|
||||||
|
client_secret: secret,
|
||||||
|
token_endpoint_auth_method: 'client_secret_post',
|
||||||
|
} satisfies Client
|
||||||
|
|
||||||
|
const issuerUrl = new URL(issuer)
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.debug('OIDC', 'Checking OIDC well-known endpoint')
|
||||||
|
const response = await discoveryRequest(issuerUrl)
|
||||||
|
const processed = await processDiscoveryResponse(issuerUrl, response)
|
||||||
|
if (!processed.authorization_endpoint) {
|
||||||
|
log.debug('OIDC', 'No authorization endpoint found on the OIDC provider')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug('OIDC', 'Found auth endpoint: %s', processed.authorization_endpoint)
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
log.debug('OIDC', 'Validation failed: %s', e.message)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ If you use the Headscale configuration integration, these are not required.
|
|||||||
- **`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.
|
||||||
- **`OIDC_CLIENT_SECRET_METHOD`**: The method used to send the client secret (default: `client_secret_basic`).
|
- **`OIDC_CLIENT_SECRET_METHOD`**: The method used to send the client secret (default: `client_secret_basic`).
|
||||||
|
- **`OIDC_SKIP_CONFIG_VALIDATION`**: Skip the OIDC configuration validation (default: `false`).
|
||||||
- **`ROOT_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`.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user