route-proxy: an empty ACME_CA_BUNDLE means the system trust store

Piece A of ADR 0056 (selectable issuer). A provider that serves an empty root —
public-acme, whose root already ships in the OS trust store — leaves route-proxy's
CA bundle file existing but empty, because the mesh writes it unconditionally from
${bound:acme-ca:root}. Read that as "trust the system roots", the same as an unset
bundle, instead of failing with "holds no certificate this can trust". A bundle
that holds bytes but no parseable certificate is still refused.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
2026-09-10 00:22:06 +02:00
parent 232862315c
commit 98f5d34610
+15 -7
View File
@@ -203,13 +203,21 @@ func run() error {
if err != nil { if err != nil {
return fmt.Errorf("ACME_CA_BUNDLE names %s and it cannot be read: %w", bundle, err) return fmt.Errorf("ACME_CA_BUNDLE names %s and it cannot be read: %w", bundle, err)
} }
pool := x509.NewCertPool() // An empty bundle means the issuer's root is already in the system trust store — a public
if !pool.AppendCertsFromPEM(pem) { // authority whose root ships with the OS, pointed at by a provider that serves an empty
return fmt.Errorf("%s holds no certificate this can trust", bundle) // root (novox/hq ADR 0056). The mesh always writes the bundle file, so it exists and holds
} // nothing; that is the signal to fall back to the system roots, the same as if nothing had
client.HTTPClient = &http.Client{ // named a bundle at all. A file that holds bytes but no certificate is still a
Timeout: 30 * time.Second, // misconfiguration and is refused, because there the operator meant to trust something.
Transport: &http.Transport{TLSClientConfig: &tls.Config{RootCAs: pool}}, if strings.TrimSpace(string(pem)) != "" {
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(pem) {
return fmt.Errorf("%s holds no certificate this can trust", bundle)
}
client.HTTPClient = &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{TLSClientConfig: &tls.Config{RootCAs: pool}},
}
} }
} }
manager := &autocert.Manager{ manager := &autocert.Manager{