fix(control-plane): accept escaped-PEM client cert on /renew
nginx has no njs deployed, so the mTLS terminator forwards the verified client cert via $ssl_client_escaped_cert (URL-encoded PEM). headerPresentedCert now normalizes base64-DER, PEM, and escaped-PEM alike to raw DER. 281 tests pass.
This commit is contained in:
@@ -171,7 +171,15 @@ export function headerPresentedCert(headerName: string = DEFAULT_CLIENT_CERT_HEA
|
||||
const value = Array.isArray(raw) ? raw[0] : raw
|
||||
if (typeof value !== 'string' || value.length === 0) return null
|
||||
try {
|
||||
return new Uint8Array(Buffer.from(value, 'base64'))
|
||||
// The terminator may forward the verified cert as base64 DER, OR as PEM — including nginx's
|
||||
// header-safe `$ssl_client_escaped_cert` (URL-encoded PEM). Normalize all three to raw DER:
|
||||
// URL-decode if escaped, then strip PEM armor + whitespace to recover the base64 DER body.
|
||||
let s = value.includes('%') ? safeDecodeUri(value) : value
|
||||
if (s.includes('BEGIN CERTIFICATE')) {
|
||||
s = s.replace(/-----[^-]+-----/g, '').replace(/\s+/g, '')
|
||||
}
|
||||
const der = Buffer.from(s, 'base64')
|
||||
return der.length > 0 ? new Uint8Array(der) : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
@@ -179,6 +187,15 @@ export function headerPresentedCert(headerName: string = DEFAULT_CLIENT_CERT_HEA
|
||||
}
|
||||
}
|
||||
|
||||
/** URL-decode `value`, returning it unchanged if it is not valid percent-encoding. */
|
||||
function safeDecodeUri(value: string): string {
|
||||
try {
|
||||
return decodeURIComponent(value)
|
||||
} catch {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trust-anchor verification of the presented current cert (defense in depth: the mTLS terminator has
|
||||
* already verified it, but this route must not trust a cert the terminator would never have accepted).
|
||||
|
||||
Reference in New Issue
Block a user