feat(control-plane): production native-CA wiring for on-disk P-256 CAs (A2-prep)
Wire the prod boot to issue frp-client + device leaves from the existing on-disk P-256 CAs: NATIVE_* env vars, a file-backed KmsResolver (loads the PEM key, validates prime256v1, never logs key material), buildFileBackedNativeCas threaded into buildControlPlane via a nativeCas override. Fail-closed on partial NATIVE_*. 281 tests pass.
This commit is contained in:
@@ -58,6 +58,13 @@ export interface ControlPlaneOverrides {
|
||||
readonly caChainDer?: readonly Uint8Array[]
|
||||
/** Production mode → native-CA + renew-anchor material is fail-closed (INV9). Defaults to NODE_ENV. */
|
||||
readonly production?: boolean
|
||||
/**
|
||||
* Pre-built native-tunnel CAs (A2-prep production path). When supplied, `buildControlPlane` uses
|
||||
* these verbatim and does NOT call `buildNativeCas` — this is how `server.ts` threads the two
|
||||
* on-disk P-256 CAs (`buildFileBackedNativeCas`) in without coupling them to the intermediate
|
||||
* `kmsResolver`. Takes precedence over `nativeCaMaterial`.
|
||||
*/
|
||||
readonly nativeCas?: NativeCas
|
||||
/** Production-loaded native-tunnel CA material (per-CA KMS ref + public cert DER). */
|
||||
readonly nativeCaMaterial?: NativeCaMaterial
|
||||
/** DNS zone the native-tunnel leaves are stamped under (defaults to `terminal.yaojia.wang`). */
|
||||
@@ -185,11 +192,15 @@ export async function buildControlPlane(
|
||||
// the renew route MUST validate presented certs against non-empty anchors (renew.ts). DEV generates
|
||||
// self-signed in-process P-256 CAs so leaves chain to a real, re-parseable CA.
|
||||
const production = overrides.production ?? process.env.NODE_ENV === 'production'
|
||||
const nativeCas = await buildNativeCas(env, {
|
||||
production,
|
||||
...(overrides.kmsResolver !== undefined ? { kmsResolver: overrides.kmsResolver } : {}),
|
||||
...(overrides.nativeCaMaterial !== undefined ? { material: overrides.nativeCaMaterial } : {}),
|
||||
})
|
||||
// `server.ts` builds the two on-disk P-256 CAs itself (buildFileBackedNativeCas, with the file
|
||||
// resolver) and threads them in as `nativeCas` — decoupled from the intermediate `kmsResolver`.
|
||||
const nativeCas =
|
||||
overrides.nativeCas ??
|
||||
(await buildNativeCas(env, {
|
||||
production,
|
||||
...(overrides.kmsResolver !== undefined ? { kmsResolver: overrides.kmsResolver } : {}),
|
||||
...(overrides.nativeCaMaterial !== undefined ? { material: overrides.nativeCaMaterial } : {}),
|
||||
}))
|
||||
const nativeDnsZone = overrides.nativeDnsZone ?? DEFAULT_NATIVE_DNS_ZONE
|
||||
|
||||
// ONE DeviceStore shared across the device registry, the device signer, and the renew path so the
|
||||
|
||||
Reference in New Issue
Block a user