cloudflare/pingora · error
invalid ca pem
Error message
invalid ca pem
What it means
When the s2n TLS listener settings include a CA (mutual-TLS client verification), TlsSettings::build() calls builder.trust_pem(&ca.raw_pem) and .expect()s success (pingora-core/src/listeners/tls/s2n/mod.rs:85). If s2n-tls cannot parse the supplied PEM (DER bytes, truncated text, wrong block type, empty file), the expect panics with 'invalid ca pem' while the acceptor is being built, i.e. at listener startup.
Source
Thrown at pingora-core/src/listeners/tls/s2n/mod.rs:85
if let Some(alpn) = self.alpn {
builder
.set_application_protocol_preference(alpn.to_wire_protocols())
.unwrap();
}
if let (Some(cert_path), Some(key_path)) = (self.cert_path, self.key_path) {
let Ok((cert, key)) = load_certs_and_key_files(&cert_path, &key_path) else {
panic!(
"Failed to load provided certificates \"{}\" or key \"{}\".",
cert_path, key_path
)
};
builder.load_pem(&cert, &key).unwrap();
}
if let Some(ca) = self.ca {
builder.trust_pem(&ca.raw_pem).expect("invalid ca pem");
}
if !self.verify_client_hostname {
builder
.set_verify_host_callback(IgnoreVerifyHostnameCallback::new())
.unwrap();
}
let config = builder.build().unwrap();
let connection_builder = S2NConnectionBuilder {
config,
psk_config: self.psk_config.clone(),
security_policy: Some(policy.clone()),
};
Acceptor {
acceptor: TlsAcceptor::new(connection_builder),
offload: self.offload_threadpool.map(|(shards, threads_per_shard)| {View on GitHub (pinned to 0046038bd4)
Solutions
- Re-export the CA in PEM format: openssl x509 -inform der -in ca.der -out ca.pem
- Sanity-check the file parses before deploying: openssl x509 -in ca.pem -noout -text
- Ensure the PEM contains CERTIFICATE blocks only (keys/cross-signed extras can confuse parsers)
Example fix
# before: DER file or corrupted PEM passed as the s2n CA ca.pem: "MII..." # base64 DER without -----BEGIN CERTIFICATE----- # after: convert and verify openssl x509 -inform der -in ca.der -out ca.pem openssl x509 -in ca.pem -noout -text
Defensive patterns
Strategy: try-catch
Try / catch
// Turn the startup panic into a clean config error before serving traffic
use std::panic::{catch_unwind, AssertUnwindSafe};
let acceptor = catch_unwind(AssertUnwindSafe(|| tls_settings.build()))
.map_err(|_| anyhow::anyhow!("invalid CA PEM for s2n listener: cannot parse ca.raw_pem"))?; Prevention
- Validate every PEM before deploying: openssl x509 -in ca.pem -noout -text must succeed
- Never pass .der files or hand-edited PEM text; generate with openssl and keep the file untouched
- Add a startup self-test that builds the s2n acceptor (catch_unwind) so bad CA input fails the deploy, not traffic
When it happens
Trigger: Building with the s2n feature and constructing TlsSettings with a CA (client cert verification / add_ca style API) whose raw PEM bytes are not a parseable PEM certificate chain.
Common situations: Certificates exported as DER instead of PEM; copy-paste that dropped the BEGIN/END lines or inserted whitespace; pointing at a bundle with non-certificate blocks; a CI cert-generation script changing format between environments.
Related errors
- Failed to parse PEM
- Failed to parse certificate from DER format.
- Failed to parse certificate from DER format.
- Failed to build listeners
- No tls feature was specified
AI-assisted analysis of cloudflare/pingora@0046038bd4 (2026-08-16).
Data as JSON: /api/errors/752ca8e7f09b4692.
Report an issue: GitHub.