From ff01adaa45b451941bf077f570f4f230664a06c2 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 9 Sep 2026 23:07:54 +0200 Subject: [PATCH] Add internal ACME CA (step-ca) as a generic acme-ca provision; wire route-proxy to consume it Piece C of ADR 0056: an internal authority certifies routed names by the same path a public one would, with the proxy pointed at whichever issuer the mesh names and trusting that issuer's root. step-ca (new): provides acme-ca (mesh scope), listens 9000 from mesh, persists its CA home under uid 1000. The root CA cert reaches consumers as a served value settled from a per-mesh operator setting (no baked root); the init password and root key are sealed secrets, not literals. No route-name -> IP hosts map (that is Piece B). Upstream smallstep/step-ca pinned by Docker Hub digest. route-proxy: requires + binds acme-ca. ACME_DIRECTORY is composed on the consumer side from ${bound:acme-ca:at}:${bound:acme-ca:port}, and ACME_CA_BUNDLE is a file whose content is ${bound:acme-ca:root} -- both from the binding, replacing the hardcoded directory and the baked root PEM. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF --- modules/route-proxy/module.json | 34 +++++++++++++- modules/step-ca/module.json | 79 +++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 modules/step-ca/module.json diff --git a/modules/route-proxy/module.json b/modules/route-proxy/module.json index 1fef8c9..6bd23e7 100644 --- a/modules/route-proxy/module.json +++ b/modules/route-proxy/module.json @@ -16,6 +16,12 @@ "receives": { "route": "/var/lib/route-proxy/routes/mesh.json" }, + "requires": [ + "acme-ca" + ], + "binds": { + "acme-ca": "/var/lib/route-proxy/acme-ca.json" + }, "listens": [ { "port": 80, @@ -49,22 +55,46 @@ "path": "/var/lib/route-proxy/acme", "mode": "0700" }, + { + "id": "ca-dir", + "type": "directory", + "path": "/var/lib/route-proxy/ca", + "mode": "0755" + }, + { + "id": "ca-bundle", + "type": "file", + "path": "/var/lib/route-proxy/ca/root.crt", + "mode": "0644", + "content": "${bound:acme-ca:root}\n" + }, + { + "id": "acme-env", + "type": "file", + "path": "/var/lib/route-proxy/acme.env", + "mode": "0600", + "content": "ACME_DIRECTORY=https://${bound:acme-ca:at}:${bound:acme-ca:port}/acme/acme/directory\n" + }, { "id": "server", "type": "container", "name": "route-proxy", "image": "mesh-route-proxy@sha256:0000000000000000000000000000000000000000000000000000000000000000", "network": "host", + "env-file": [ + "/var/lib/route-proxy/acme.env" + ], "volumes": [ "/var/lib/route-proxy/routes:/routes:ro", - "/var/lib/route-proxy/acme:/acme" + "/var/lib/route-proxy/acme:/acme", + "/var/lib/route-proxy/ca:/ca:ro" ], "env": { "ROUTES": "/routes/mesh.json", "LISTEN": ":80", "TLS_LISTEN": ":443", "ACME_CACHE": "/acme", - "ACME_DIRECTORY": "https://acme-staging-v02.api.letsencrypt.org/directory" + "ACME_CA_BUNDLE": "/ca/root.crt" } } ] diff --git a/modules/step-ca/module.json b/modules/step-ca/module.json new file mode 100644 index 0000000..eb9d516 --- /dev/null +++ b/modules/step-ca/module.json @@ -0,0 +1,79 @@ +{ + "module": "step-ca", + "version": "1", + "capabilities": [ + "container-runtime" + ], + "provides": [ + { + "name": "acme-ca", + "scope": "mesh" + } + ], + "serves": { + "acme-ca": {} + }, + "listens": [ + { + "port": 9000, + "protocol": "tcp", + "from": "mesh", + "why": "the ACME directory and step-ca API; a proxy on any node reaches it here over the mesh to obtain certificates, and its single listen is what the consumer is told the port is" + } + ], + "own-secrets": { + "password": "/var/lib/mesh/step-ca/password", + "root-key": "/var/lib/mesh/step-ca/root-key" + }, + "resources": [ + { + "id": "mesh-state", + "type": "directory", + "path": "/var/lib/mesh/step-ca", + "mode": "0700" + }, + { + "id": "home", + "type": "directory", + "path": "/var/lib/step-ca", + "mode": "0700", + "owner": "1000:1000" + }, + { + "id": "config", + "type": "file", + "path": "/var/lib/mesh/step-ca/config.json", + "mode": "0644", + "content": "{}", + "merge": "json" + }, + { + "id": "init-env", + "type": "file", + "path": "/var/lib/mesh/step-ca/init.env", + "mode": "0600", + "content": "DOCKER_STEPCA_INIT_PASSWORD=${secret:password}\n" + }, + { + "id": "server", + "type": "container", + "name": "step-ca", + "image": "smallstep/step-ca@sha256:a2b17872915c193259b75a5474c398326f41bd199f0842093e52cf4182bc8270", + "network": "host", + "env-file": [ + "/var/lib/mesh/step-ca/init.env" + ], + "env": { + "DOCKER_STEPCA_INIT_NAME": "Mesh Internal CA", + "DOCKER_STEPCA_INIT_DNS_NAMES": "localhost,127.0.0.1", + "DOCKER_STEPCA_INIT_ACME": "true", + "DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT": "false" + }, + "volumes": [ + "/var/lib/step-ca:/home/step", + "/var/lib/mesh/step-ca/root-key:/run/secrets/root-key:ro", + "/var/lib/mesh/step-ca/config.json:/run/config/config.json:ro" + ] + } + ] +}