Every network call fails on a host with no system CA store
tan sdk list --online, and by extension the whole sdk install path, fails
with CERTIFICATE_VERIFY_FAILED on any host that has no OS trust store —
which is the normal state of a minimal container (ubuntu:24.04,
debian:*-slim, most CI base images ship no ca-certificates).
This is not the --onedir change in #349. Reproduced identically on the
published v0.5.0-rc4 --onefile asset, so it has shipped in every RC.
Measured
docker run --rm ubuntu:24.04, tan installed from the release tarball, nothing
else touched:
system CA bundle (/etc/ssl/certs/ca-certificates.crt) : ABSENT
tan's own bundled certifi (_internal/certifi/cacert.pem) : PRESENT
$ tan sdk list --online --format json ; echo exit=$?
{"command":"sdk","ok":false,"exitCode":1,...,"issues":[{"code":"sdk.fetch-failed",
"severity":"error","message":"<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)>..."}]}
exit=1
Same container, published rc4 --onefile binary: byte-identical failure
(_ssl.c:1010).
So tan ships a CA bundle and does not use it.
Why the #304 fallback does not fire
python/tan/net.py:
try:
import truststore
return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
except Exception:
import certifi
return ssl.create_default_context(cafile=certifi.where())
Its own docstring states the intent: certifi is "used only if truststore
itself is unavailable or fails to construct a context."
That is the gap. On Linux with an empty OS store, truststore.SSLContext(...)
constructs perfectly well — it defers to the platform verifier, and the
platform verifier simply has no anchors. The failure happens later, at
verify time, inside urlopen. Nothing in this try can observe that, so
the certifi floor is never reached.
The preference for truststore is correct and should stay — #304's reasoning
holds: a host behind a real TLS-intercepting proxy needs the OS store, and
certifi alone would trade one failure for another. The bug is that "the floor"
is not actually underneath anything.
Impact
Anyone running tan in a container or on a minimal distro: sdk list --online,
sdk install, and every other HTTPS path fail. The error text is good — it
already names the three candidate causes — but the behaviour is wrong, and the
one cause it lists last ("this tan build's own CA trust failed to load") is the
actual one.
The e2e suite never caught it because every host it has ever run on had a
system CA store. A container is the first genuinely clean host tan has been
tested on.
Fix direction
Give truststore a real floor rather than an alternative: ensure the returned
context can verify public CAs from the bundled certifi even when the OS store
is empty, without losing the OS store when it is populated. Verify the fix
in the container, not on a host that has /etc/ssl/certs.
Found while testing tan the way a customer would — a pristine ubuntu:24.04,
nothing pre-installed.
Every network call fails on a host with no system CA store
tan sdk list --online, and by extension the wholesdk installpath, failswith
CERTIFICATE_VERIFY_FAILEDon any host that has no OS trust store —which is the normal state of a minimal container (
ubuntu:24.04,debian:*-slim, most CI base images ship noca-certificates).This is not the
--onedirchange in #349. Reproduced identically on thepublished
v0.5.0-rc4--onefileasset, so it has shipped in every RC.Measured
docker run --rm ubuntu:24.04, tan installed from the release tarball, nothingelse touched:
Same container, published rc4
--onefilebinary: byte-identical failure(
_ssl.c:1010).So tan ships a CA bundle and does not use it.
Why the #304 fallback does not fire
python/tan/net.py:Its own docstring states the intent: certifi is "used only if
truststoreitself is unavailable or fails to construct a context."
That is the gap. On Linux with an empty OS store,
truststore.SSLContext(...)constructs perfectly well — it defers to the platform verifier, and the
platform verifier simply has no anchors. The failure happens later, at
verify time, inside
urlopen. Nothing in thistrycan observe that, sothe certifi floor is never reached.
The preference for
truststoreis correct and should stay — #304's reasoningholds: a host behind a real TLS-intercepting proxy needs the OS store, and
certifi alone would trade one failure for another. The bug is that "the floor"
is not actually underneath anything.
Impact
Anyone running tan in a container or on a minimal distro:
sdk list --online,sdk install, and every other HTTPS path fail. The error text is good — italready names the three candidate causes — but the behaviour is wrong, and the
one cause it lists last ("this tan build's own CA trust failed to load") is the
actual one.
The e2e suite never caught it because every host it has ever run on had a
system CA store. A container is the first genuinely clean host tan has been
tested on.
Fix direction
Give
truststorea real floor rather than an alternative: ensure the returnedcontext can verify public CAs from the bundled
certifieven when the OS storeis empty, without losing the OS store when it is populated. Verify the fix
in the container, not on a host that has
/etc/ssl/certs.Found while testing tan the way a customer would — a pristine
ubuntu:24.04,nothing pre-installed.