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:
Yaojia Wang
2026-07-19 07:00:48 +02:00
parent 9f7f5c0c54
commit 55d177e9ee

View File

@@ -171,7 +171,15 @@ export function headerPresentedCert(headerName: string = DEFAULT_CLIENT_CERT_HEA
const value = Array.isArray(raw) ? raw[0] : raw const value = Array.isArray(raw) ? raw[0] : raw
if (typeof value !== 'string' || value.length === 0) return null if (typeof value !== 'string' || value.length === 0) return null
try { 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 { } catch {
return null 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 * 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). * already verified it, but this route must not trust a cert the terminator would never have accepted).