Diagnose TLS failures

Diagnose a hostname mismatch

Compare the requested name with certificate SAN entries and repair the identity instead of disabling checks.

A trusted certificate can still be wrong for the hostname you asked for. Chain verification and name verification are two separate checks. The first asks “did a trusted CA sign this?”. The second asks “is this certificate for the name I typed?”. A certificate for app.lab.test is perfectly valid, for app.lab.test. Ask for any other name and the identity check must fail.

Let’s force a mismatch against the lab server. Our certificate covers app.lab.test and localhost, so connect by IP address instead:

curl --cacert lab-ca.crt https://127.0.0.1:8443/
# curl: (60) SSL: no alternative certificate subject name matches target ipv4 address '127.0.0.1'

Read the error word by word. curl trusted the chain, because --cacert handled that. It failed only on the name comparison. The wording changes a bit between curl versions and TLS backends, but the shape is always the same: the name you requested, and the statement that no name in the certificate matched it.

Now let’s see which names the leaf covers. We can pull it from the live server and decode the SAN in one pipe:

openssl s_client -connect 127.0.0.1:8443 </dev/null 2>/dev/null | openssl x509 -noout -ext subjectAltName
# X509v3 Subject Alternative Name:
#     DNS:app.lab.test, DNS:localhost

Diagnosis is now a comparison. Requested: 127.0.0.1. Offered: app.lab.test and localhost. No overlap, so the failure is correct.

From here you have exactly two honest fixes. Fix the hostname you request: connect with a name the certificate covers, like https://localhost:8443/ in our lab, which returns secure lab. Or fix the certificate: reissue it with a SAN that includes the name clients really use. In production the second one is usually right. Clients use the name they use, and the certificate has to match reality.

In real systems this error usually means one of three things. A URL pointing at an internal IP. A certificate listing www.flaviocopes.com but not flaviocopes.com. Or a service moved to a new name with the old certificate still deployed.

Don’t solve a name mismatch with --insecure. It doesn’t skip just this one error. It accepts any certificate from anyone, which is exactly the attack that names in certificates exist to stop. If you find yourself typing -k in a script, stop and fix the name instead.

Lesson completed