From 8797335fbcd9775f2eb62d4599e110db06594c5c Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 29 Sep 2026 15:07:40 +0200 Subject: [PATCH] ca-trust: a machine trusts the mesh's authority because a module put its root there MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit novox/hq ADR 0147, issue 129. Every internal HTTPS name fails verification on every machine: the certificates are genuine and nothing on a machine has ever been told what issued them. The proxy's fetch answers for the proxy and for nothing else — a browser, git over HTTPS and every module calling another by an internal name read the machine's own trust store. The module requires internal-acme-ca, fetches the root over the mesh's own network (no prior trust to have; that is what this establishes), installs it among the machine's anchors and refreshes the extracted bundles. Being unassigned stops the unit, and stopping it takes the anchor away and refreshes them again. Arch's layout is named out loud: a machine that keeps anchors elsewhere fails visibly rather than writing a file nothing reads. --- modules/ca-trust/module.json | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 modules/ca-trust/module.json diff --git a/modules/ca-trust/module.json b/modules/ca-trust/module.json new file mode 100644 index 0000000..1f7baf8 --- /dev/null +++ b/modules/ca-trust/module.json @@ -0,0 +1,56 @@ +{ + "module": "ca-trust", + "version": "1", + "slug": "catrust", + "capabilities": [ + "service-manager" + ], + "requires": [ + "internal-acme-ca" + ], + "seats": [ + { + "name": "the-mesh-trust-anchor", + "scope": "node" + } + ], + "claims": [ + { + "name": "the-mesh-trust-anchor", + "scope": "node" + } + ], + "resources": [ + { + "id": "state", + "type": "directory", + "mode": "0700", + "place": "." + }, + { + "id": "anchor", + "type": "file", + "path": "${dir:state}/anchor", + "mode": "0755", + "content": "#!/bin/sh\n# The mesh's internal certificate authority, trusted by this machine.\n#\n# Written by the mesh from the ca-trust module's manifest (novox/hq ADR 0147).\n# Editing it here lasts until the next apply.\n#\n# There is no prior trust to verify the fetch against \u2014 this is the thing that\n# establishes it \u2014 so it is made over the mesh's own private network, which is\n# what authenticates it (novox/hq ADR 0098, the same reasoning that lets the\n# route proxy fetch this root for itself). What comes back is checked here: a\n# body that is not a certificate is refused now, rather than believed and then\n# failed by whatever reads the trust store next.\nset -eu\n\nROOTS='https://${bound:internal-acme-ca:at}:${bound:internal-acme-ca:port}${bound:internal-acme-ca:roots}'\nANCHORS=/etc/ca-certificates/trust-source/anchors\nANCHOR=\"$ANCHORS/mesh-internal-ca.crt\"\n\n# Arch's layout, said out loud rather than assumed: a machine that keeps its\n# anchors elsewhere fails here, visibly, instead of writing a file nothing\n# reads. That failure is the signal that this belongs in the host, where one\n# operating system's difference lives (novox/hq ADR 0147, option 2).\n[ -d \"$ANCHORS\" ] || {\n\techo \"this machine keeps no trust anchors in $ANCHORS; ca-trust is written for that layout\" >&2\n\texit 1\n}\n\ncase \"${1:-}\" in\ninstall)\n\ttmp=$(mktemp)\n\ttrap 'rm -f \"$tmp\"' EXIT\n\t# The authority may still be starting, or this machine may have come up\n\t# before it: two minutes of asking, then an honest failure.\n\tn=0\n\twhile [ \"$n\" -lt 60 ]; do\n\t\tif curl --fail --silent --show-error --insecure --max-time 10 \\\n\t\t\t--output \"$tmp\" \"$ROOTS\" &&\n\t\t\tgrep -q 'BEGIN CERTIFICATE' \"$tmp\"; then\n\t\t\tinstall -m 0644 \"$tmp\" \"$ANCHOR\"\n\t\t\tupdate-ca-trust\n\t\t\texit 0\n\t\tfi\n\t\tn=$((n + 1))\n\t\tsleep 2\n\tdone\n\techo \"the authority at $ROOTS did not serve a certificate within two minutes\" >&2\n\texit 1\n\t;;\nremove)\n\t# What stopping the unit does, and therefore what being unassigned does.\n\trm -f \"$ANCHOR\"\n\tupdate-ca-trust\n\t;;\n*)\n\techo \"usage: $(basename \"$0\") install|remove\" >&2\n\texit 2\n\t;;\nesac\n" + }, + { + "id": "unit", + "type": "file", + "path": "/etc/systemd/system/mesh-ca-trust.service", + "mode": "0644", + "content": "[Unit]\nDescription=The mesh's internal certificate authority, trusted by this machine\n# novox/hq ADR 0147. Starting this unit places the mesh's root among this\n# machine's trust anchors; stopping it takes the root away again, which is what\n# the host does when the module is no longer assigned here.\nWants=network-online.target\nAfter=network-online.target\n\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=${dir:state}/anchor install\nExecStop=${dir:state}/anchor remove\n\n[Install]\nWantedBy=multi-user.target\n" + }, + { + "id": "trust", + "type": "service", + "unit": "mesh-ca-trust.service", + "state": "running", + "boot": "enabled", + "restart-on": [ + "anchor", + "unit" + ] + } + ] +}