Why TLS Certificate Trust Isn't Universal on Linux
A breakdown of why TLS certificate trust varies across applications on Linux, covering system trust stores, private CAs, and per-runtime overrides.
On Linux, the 'system trust store' is a convention, not an enforcement mechanism. TLS validation happens entirely in userspace, inside whichever library an application links against, which is exactly why curl can trust a certificate while a Java service on the same box throws certificate verify failed.
The root cause isn't the chain-validation logic itself, which is fairly mechanical, but the fragmentation of trust sources. OpenSSL, GnuTLS, and NSS each ship different default lookup paths that distros patch toward /etc/ssl or /etc/pki/ca-trust with varying consistency. Layer on top of that: Java ships its own cacerts keystore untouched by system config, Node.js compiles Mozilla's root list directly into its binary at build time, and Python's standard library uses OpenSSL defaults while the popular requests library instead trusts certifi, a separately packaged Mozilla bundle. A single Python process can hold two different trust stores at once depending on which import path runs.
This fragmentation becomes a real operational headache for anyone running an internal CA. Adding it to the system store and running update-ca-certificates only satisfies C-linked tools; you then need keytool for Java, NODE_EXTRA_CA_CERTS for Node, REQUESTS_CA_BUNDLE for Python/requests, and repeated patching for every container image carrying its own isolated /etc/ssl. The practical takeaway for engineers: there's no single answer to 'does this machine trust my CA' — only per-runtime answers.