Ballerina HTTP Client Rejects Self-Signed Certificates by Default: Trust Store or CA-Signed Cert?
0 reputation · 25 Nov 2025, 10:30 UTC
0 reputation · 25 Nov 2025, 10:30 UTC
Ballerina's http:Client validates TLS certificates by default, so an endpoint presenting a self-signed or otherwise untrusted certificate fails the handshake. The documented paths forward are supplying a custom trust store through the client's secure socket configuration, or having the endpoint present a CA-signed certificate. A validation-bypass mode exists but is documented as insecure and meant only for isolated testing.
Two constraints frame the choice. Exact ClientSecureSocket field names and options have varied across Swan Lake releases, so they need confirming against the installed distribution's API docs. DNS resolution is delegated to the JVM/OS resolver, and hostname verification runs inside the handshake, so a SAN mismatch surfaces as a generic SSL error that is easy to mistake for a name-resolution problem.
For a caller integrating with such an endpoint, the unresolved decision is whether to pin the self-signed certificate per client through a trust store or to move the endpoint to a CA-signed certificate.
30775 reputation · 25 Nov 2025, 10:59 UTC
Pin the self-signed certificate in a per-client trust store when you have one or two stable endpoints and control both sides. Move the endpoint to a certificate signed by an internal CA as soon as the endpoint count grows, certificates rotate on a schedule, or clients you don't control need to connect. Either way, hostname verification still applies: connecting by IP address fails unless the certificate carries that IP in its Subject Alternative Name (SAN) extension.
Confirmed: a trust store configured through the client's secure socket configuration is scoped to that client instance. It does not touch the global cacerts file and does not affect other clients in the same process.
What is version-sensitive is whether your custom trust store replaces the JVM default trust material for that client or merges with it. The safe working assumption is replacement: if the client also calls public CA-signed endpoints, include the relevant CA certificates in the same store, or verify merge behavior against the API docs of your installed Swan Lake distribution. Field names on the secure socket record have shifted across releases, so check bal doc or the distribution's API docs rather than copying an older example.
Avoid the alternative of importing the self-signed certificate into the JVM-wide trust store: it widens trust for every TLS client in that JVM, which is broader than the problem requires.
Yes. Trusting the certificate and passing hostname verification are independent checks inside the handshake. If you connect to https://10.0.0.5/... but the certificate's SAN lists only api.internal.example, the handshake fails even with the certificate pinned. The fix is one of:
Note the diagnostic trap: DNS resolution is delegated to the JVM/OS resolver and completes before TLS starts, so a SAN mismatch surfaces as a generic SSL handshake error, not a name-resolution error. Inspect the certificate before assuming DNS is broken:
openssl s_client -connect api.internal.example:443 -servername api.internal.example \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"Pinning is cheaper until rotation enters the picture. Compare the two operational loops:
| Event | Pinned self-signed | Internal CA |
|---|---|---|
| Initial setup | Export one cert, reference it in the client | Stand up CA, protect its key, distribute CA cert once |
| Certificate rotation | Redistribute the new cert to every pinning client, coordinated with the server swap | Re-issue the server cert only; clients unchanged |
| New endpoint added | New pin per client | Sign with existing CA; clients already trust it |
The crossover arrives quickly: with rotation every 90 days (common for automated issuance) and more than a handful of clients, the coordinated redistribution of pins becomes the dominant cost and the dominant outage risk — a client holding the old pin fails the moment the server swaps. An internal CA converts rotation into a server-side-only operation.
For a single caller integrating with one such endpoint, pin per client now and connect by the SAN's DNS name. If the endpoint's certificate will rotate, or more consumers are coming, invest in the internal CA instead. The documented validation-bypass mode disables TLS's security guarantees; keep it confined to isolated local testing. One detail worth confirming before you commit: whether your installed release's secure socket configuration replaces default trust material, since that determines whether mixed traffic (self-signed plus public endpoints) needs a combined store.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.