Fold public-acme into route-proxy; drop dhcpcd and cloudflare-dns (hq ADR 0226)
public-acme ran nothing and had one consumer. The proxy now states the issuer itself, byte for byte what the binding rendered, so its account directory and every certificate stay put. dhcpcd and cloudflare-dns are assigned nowhere and nothing requires what they provide.
This commit is contained in:
@@ -23,20 +23,25 @@ it.
|
||||
| `receives.route` | `…/routes/mesh.json` | where the mesh writes every contribution; the proxy reads it as `$ROUTES` and re-reads on change |
|
||||
| `listens` | `80` and `443`, both `from: anywhere` | the one machine with a public opening; the firewall opens these for free (ADR 0045) |
|
||||
|
||||
No broker account, no own-secrets, no provisioner: the proxy neither mints a credential nor emits
|
||||
an event. It only reads the file the mesh writes. (Contrast `redis`, which mints passwords, and
|
||||
`cloudflare-dns`, which emits record events.)
|
||||
No provisioner: the proxy neither mints a credential for anybody nor answers a provision other
|
||||
than `route`. It reads the file the mesh writes, and its own broker credential is for its tools.
|
||||
|
||||
## Which issuer: the node that runs a proxy carries `public-acme`
|
||||
## Which issuer: Let's Encrypt, stated here (novox/hq ADR 0226)
|
||||
|
||||
`acme-ca` has two providers in a full mesh — `public-acme` (Let's Encrypt, a facts-only module that
|
||||
runs nothing) and `step-ca` (the mesh's own authority, which also offers it so a lab without a public
|
||||
issuer still has one). A proxy beside both resolves by co-location only once a pin names the module
|
||||
(`pin <node> acme-ca <node> public-acme`, novox/hq #258); a proxy on another machine cannot resolve
|
||||
at all until it is told. **So every node that runs a route-proxy is assigned `public-acme` too**: the
|
||||
issuer is then on the proxy's own node, design 23's first rule answers, and `internal-acme-ca` has
|
||||
one provider mesh-wide. Nothing runs for it; it is the statement "this machine's public issuer is
|
||||
Let's Encrypt", on the machine that issues.
|
||||
**The public issuer is this module's own fact, not a provision.** Until 2026-10-06 it came from a
|
||||
second module, `public-acme`, that ran nothing and offered `acme-ca` pointing at Let's Encrypt; every
|
||||
node running a proxy was assigned it too, and nothing else ever consumed it. It is retired: the proxy
|
||||
names the production directory in its own `acme.env`, and the only issuer it is still bound to is the
|
||||
mesh's own (`internal-acme-ca`, from `step-ca`).
|
||||
|
||||
**`acme.env` is byte for byte what the binding rendered.** The proxy keeps each authority's account
|
||||
and certificates in a directory under `/var/lib/route-proxy/acme` named after a digest of the
|
||||
directory URL — `https://acme-v02.api.letsencrypt.org:443/directory`, port included, as the binding
|
||||
spelled it — and of the root bundle the `trust` container copies in. Any change to either is a new
|
||||
authority to the proxy: a new account, and every routed name ordered again against Let's Encrypt's
|
||||
rate limits. So the URL keeps the `:443`, `ACME_ROOTS_PATH` stays empty, and the `trust` artifact's
|
||||
pinned image is not moved without a decision; mesh-controller's
|
||||
`TestRouteProxyKeepsItsPublicAccountDirectory` holds all three.
|
||||
|
||||
## How it ships the Go proxy
|
||||
|
||||
@@ -66,9 +71,10 @@ would leave the safe path depending on somebody remembering to opt out of it, on
|
||||
most likely to iterate. A staging certificate is trusted by no browser, so the mistake announces
|
||||
itself on the first request rather than a fortnight later at the rate limit.
|
||||
|
||||
**A node that serves real public traffic states so**, by overriding `ACME_DIRECTORY` to the
|
||||
production directory `https://acme-v02.api.letsencrypt.org/directory` for this module on that node.
|
||||
It is a node property, and the node facing the public internet is the one that opts in.
|
||||
**The module states production.** The binary's staging default stands for anything that runs it
|
||||
without the module — the lab, a developer's machine — and the module's `acme.env` names production
|
||||
for every node it is assigned to, because a node assigned the public proxy is one that serves public
|
||||
traffic.
|
||||
|
||||
| env | default | meaning |
|
||||
|---|---|---|
|
||||
@@ -76,5 +82,5 @@ It is a node property, and the node facing the public internet is the one that o
|
||||
| `LISTEN` | `:80` | HTTP, and the ACME HTTP-01 challenge |
|
||||
| `TLS_LISTEN` | `:443` | HTTPS; unset to serve plain HTTP only |
|
||||
| `ACME_CACHE` | `/acme` | where issued certificates persist; required when `TLS_LISTEN` is set, so a restart does not re-order |
|
||||
| `ACME_DIRECTORY` | Let's Encrypt **staging** | the issuer; a public-serving node overrides it to production |
|
||||
| `ACME_DIRECTORY` | Let's Encrypt **staging** in the binary; production in the module's `acme.env` | the issuer |
|
||||
| `ACME_CA_BUNDLE` | *(unset)* | a file of roots to trust for the issuer's own API — set only for a private/lab authority whose API certificate the world does not yet trust |
|
||||
|
||||
@@ -18,11 +18,9 @@
|
||||
"route": "${dir:routes-dir}/mesh.json"
|
||||
},
|
||||
"requires": [
|
||||
"acme-ca",
|
||||
"internal-acme-ca"
|
||||
],
|
||||
"binds": {
|
||||
"acme-ca": "${dir:state}/acme-ca.json",
|
||||
"internal-acme-ca": "${dir:state}/internal-acme-ca.json"
|
||||
},
|
||||
"own-secrets": {
|
||||
@@ -80,7 +78,7 @@
|
||||
"type": "file",
|
||||
"path": "${dir:state}/acme.env",
|
||||
"mode": "0600",
|
||||
"content": "ACME_DIRECTORY=https://${bound:acme-ca:at}:${bound:acme-ca:port}${bound:acme-ca:path}\nACME_ROOTS=https://${bound:acme-ca:at}:${bound:acme-ca:port}${bound:acme-ca:roots}\nACME_ROOTS_PATH=${bound:acme-ca:roots}\n"
|
||||
"content": "ACME_DIRECTORY=https://acme-v02.api.letsencrypt.org:443/directory\nACME_ROOTS=https://acme-v02.api.letsencrypt.org:443\nACME_ROOTS_PATH=\n"
|
||||
},
|
||||
{
|
||||
"id": "internal-acme-env",
|
||||
|
||||
Reference in New Issue
Block a user