feat(control-plane): real X.509 agent leaf issuance (closes enrollment↔mTLS gap)
- ca/issue.ts: real X.509 v3 Ed25519 leaf with SPIFFE URI SAN, signed by the intermediate; SAN built via relay-auth spiffeIdFor so it can't drift - ca/csr.ts: parse real PKCS#10 (agent's PEM) + async PoP verify; decodeCsrWire accepts PEM or base64(DER) - main.ts/env.ts: real issuer when CA_INTERMEDIATE_KEY_PATH present, else dev fallback - interop.test.ts: oracle proving relay-auth verifyAgentCert accepts the leaf (6 tests) - deps: @peculiar/x509, reflect-metadata
This commit is contained in:
@@ -15,6 +15,7 @@ import type { HostRegistry } from '../registry/hosts.js'
|
||||
import type { PairingIssuer } from '../pairing/issue.js'
|
||||
import type { PairingRedeemer } from '../pairing/redeem.js'
|
||||
import { RedeemError } from '../pairing/redeem.js'
|
||||
import { decodeCsrWire } from '../ca/csr.js'
|
||||
import type { Deprovisioner } from '../deprovision/deprovision.js'
|
||||
|
||||
export interface ProvisionDeps {
|
||||
@@ -30,8 +31,8 @@ const PlanSchema = z.enum(['free', 'personal', 'pro', 'team'])
|
||||
const StatusSchema = z.object({ status: z.enum(['active', 'suspended']) })
|
||||
const EnrollSchema = z.object({
|
||||
code: z.string().min(1),
|
||||
agentPubkey: z.string().min(1), // base64
|
||||
csr: z.string().min(1), // base64
|
||||
agentPubkey: z.string().min(1), // base64 (agent sends base64url; Buffer decodes both)
|
||||
csr: z.string().min(1), // PKCS#10: PEM block (agent) or base64(DER) — see decodeCsrWire
|
||||
})
|
||||
|
||||
function sendError(reply: FastifyReply, err: unknown): void {
|
||||
@@ -126,7 +127,7 @@ export function buildRouter(deps: ProvisionDeps): FastifyPluginAsync {
|
||||
const result = await deps.redeemer.redeemPairingCode({
|
||||
code: body.code,
|
||||
agentPubkey: new Uint8Array(Buffer.from(body.agentPubkey, 'base64')),
|
||||
csr: new Uint8Array(Buffer.from(body.csr, 'base64')),
|
||||
csr: decodeCsrWire(body.csr),
|
||||
})
|
||||
await reply.code(201).send({ ...result, hostContentSecret: Buffer.from(result.hostContentSecret).toString('base64') })
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user