diff --git a/04-ISSUES/129-nothing-makes-a-machine-trust-the-meshs-authority/00-report.md b/04-ISSUES/129-nothing-makes-a-machine-trust-the-meshs-authority/00-report.md new file mode 100644 index 0000000..47ce7a4 --- /dev/null +++ b/04-ISSUES/129-nothing-makes-a-machine-trust-the-meshs-authority/00-report.md @@ -0,0 +1,62 @@ +--- +status: open +opened: 2026-09-26 +located-in: [mesh-controller, mesh-catalog step-ca] +--- + +# 129 — nothing makes a machine trust the mesh's own certificate authority + +## What was observed + +On an enrolled, adopted workstation — on the private network, resolving the mesh's names +through the mesh's resolver — every HTTPS name the mesh serves internally fails verification: + +``` +curl https://git..internal/ +curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20) +``` + +The route proxy presents a certificate issued by the mesh's internal authority (step-ca, the +`internal-acme-ca` provision). The machine's trust store holds the **predecessor's** authority +and a developer tool's local root, and nothing of the mesh's. No module installs the mesh's +root, and no fact carries it: step-ca's only consumers are proxies, which obtain certificates +over ACME and never need the root on the machine they run on. + +[Issue 048](../048-nothing-makes-a-machine-trust-the-mesh-registry/00-report.md) found the same +shape for the mesh's registry and resolved it by treating the private network as the transport +security ([ADR 0082](../../02-DECISIONS/0082-the-registry-is-reached-by-name-and-trusted-by-the-overlay.md)): +the runtime pulls in the clear, over the tunnel. That answer does not carry over. A browser, git +over HTTPS, a package manager and every TLS client a person or a module uses verify the +certificate chain, and there is no "insecure registries" for them — nor should there be. + +Consequences today, all silent until someone tries: + +- a person on a workstation cannot open any internal HTTPS name without a warning; +- git over HTTPS to the mesh's forge fails, so the working clone URL is ssh-only; +- a module on a non-hub machine that calls another module's internal HTTPS name fails + verification unless its image happens to carry the root; +- the predecessor's authority cannot be retired from any machine while anything there still + speaks TLS to a mesh name, because it is the only authority those machines trust. + +## What would have prevented it + +- A **mesh fact carrying the internal authority's root** (public material; the controller or + the step-ca module is its source), written onto every machine on the private network — the + same reasoning that has the private network write the registry trust and the names: being on + the network is what makes a machine one that speaks to the mesh's names. +- A resource that puts it where the machine's TLS clients look — on Arch, + `/etc/ca-certificates/trust-source/anchors/` — and **refreshes the extracted bundles** + (`update-ca-trust`). The refresh is the open design question: it is a command, and the link + may not carry an action ([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)). A declared + one-shot unit, or a host primitive for "trust this anchor", are the obvious candidates. +- Removal symmetric to arrival: undeclared, the anchor goes and the bundles are refreshed again, + so a machine leaving the mesh stops trusting it. + +## Evidence to carry into diagnosis + +- `step-ca` module: provides `acme-ca` / `internal-acme-ca`, listens on 9000 for proxies; no + resource writes its root anywhere but its own state directory. +- The private network's generator writes `/etc/hosts` and the registry trust, and nothing + about certificates. +- On the workstation, the trust anchors present are the predecessor's authority and a local + development root; `trust list` shows no entry for the mesh.