fix: the certificate follows CERTBOT_DOMAIN, on every pass
A pass branched on whether /etc/letsencrypt/live/<first-name> existed:
present, it ran `certbot renew`; absent, it handed over to the creation
script. Only the second path was ever told the names to ask for, so
after the first issuance CERTBOT_DOMAIN stopped reaching certbot
altogether -- renew takes the names off the certificate it already holds.
Editing the variable did nothing, silently, and the README documented the
manual issuance needed to work around it.
The branch is gone. Every pass now runs certonly with the names spelled
out, and certbot decides what that means:
same names, not due -> "Certificate not yet due for renewal; no
action taken", no connection opened, nothing
spent against the rate limits
name added or removed -> reissued with exactly the requested set
due for renewal -> renewed
--keep-until-expiring is what makes the first line true, and --cert-name
(already passed) is what keeps a changed list updating the existing
lineage instead of starting a second one beside it. No --expand is
needed to add a name once the lineage is named.
All three decisions were checked against the live stands: the no-op one
with a real run on testnet2, the other two as dry runs on testnet1 and
testnet2.
With the branch removed, 1-renew-cert.sh is a pass-through and the
numbering finally matches the order things run in -- 0-create-cert.sh
used to execute after 1-renew-cert.sh. So: 1-renew-cert.sh deleted,
0-create-cert.sh renamed to 1-ensure-cert.sh, which is what it now does.
Claude-Session: https://claude.ai/code/session_01BvgYcYPWd1KGABLKViVPSk
This commit is contained in:
@@ -11,16 +11,15 @@ This repository covers the docker deployment only.
|
||||
# Usage
|
||||
|
||||
The container is not a one-shot command. Its default command is a loop that runs
|
||||
the renewal script, sleeps 12 hours, and repeats — so it obtains the certificate
|
||||
on its first start and keeps it fresh from then on.
|
||||
a pass, sleeps 12 hours, and repeats — so it obtains the certificate on its
|
||||
first start and keeps it fresh from then on.
|
||||
|
||||
Each pass does four things, one script apiece:
|
||||
Each pass does three things, one script apiece:
|
||||
|
||||
|Script|What it does|
|
||||
|:--|:--|
|
||||
|`entrypoint.sh`|The loop itself, and PID 1 of the container. Runs a pass, sleeps 12 hours, repeats; a failed pass is reported and retried rather than ending the loop|
|
||||
|`1-renew-cert.sh`|The start of a pass. Renews an existing certificate, or hands over to `0-create-cert.sh` when there is none yet|
|
||||
|`0-create-cert.sh`|First run: writes a self-signed placeholder so HAProxy can bind 443, waits for HAProxy, then requests the real certificate|
|
||||
|`1-ensure-cert.sh`|The pass itself. On the first run writes a self-signed placeholder so HAProxy can bind 443; then waits for HAProxy and asks certbot for a certificate carrying exactly the names in `CERTBOT_DOMAIN` — issuing it, reissuing it when that list changed, or leaving it alone|
|
||||
|`2-concatenate-cert.sh`|Joins `fullchain.pem` and `privkey.pem` into the single `site.pem` HAProxy expects|
|
||||
|`3-update-haproxy-cert.sh`|Installs `site.pem` into the *running* HAProxy over its runtime API — no restart, no dropped connections|
|
||||
|
||||
@@ -117,7 +116,7 @@ Container images are configured using parameters passed at runtime.
|
||||
starts failing, nothing tells you until the certificate expires. Monitor the
|
||||
certificate externally, or set the variable.
|
||||
- **The first certificate is self-signed, and browsers will say so.** HAProxy
|
||||
cannot start without `site.pem`, so `0-create-cert.sh` writes a placeholder
|
||||
cannot start without `site.pem`, so `1-ensure-cert.sh` writes a placeholder
|
||||
before doing anything else. It is replaced as soon as the real certificate is
|
||||
issued — but if issuance fails, the placeholder is what the site keeps
|
||||
serving, with no error anywhere but the container log.
|
||||
@@ -136,9 +135,10 @@ Container images are configured using parameters passed at runtime.
|
||||
on the volume is correct either way, so a refusal means the *running* HAProxy
|
||||
is still on the previous certificate until it restarts. The message says so.
|
||||
- **Renewal is pushed on every pass, not on renewal.** The script concatenates
|
||||
and re-installs whether or not `certbot renew` actually did anything, twice a
|
||||
day. Harmless, but it means the "certificate updated" path is exercised
|
||||
constantly and a genuine renewal looks like every other pass. Certbot's own
|
||||
and re-installs whether or not `certbot certonly` actually did anything,
|
||||
twice a day. Harmless, but it means the "certificate updated" path is
|
||||
exercised constantly and a genuine renewal looks like every other pass.
|
||||
Certbot's own
|
||||
`--deploy-hook` is the mechanism built for this.
|
||||
- **`site.pem` is always replaced by an atomic rename**, on both the placeholder
|
||||
and the renewal path. HAProxy reads that file at start-up, and a plain
|
||||
@@ -162,11 +162,14 @@ Container images are configured using parameters passed at runtime.
|
||||
by cron, means a new certbot release reaches the registry — and through
|
||||
Watchtower, production — without anyone triggering a build. Pin a version tag
|
||||
for reproducible builds.
|
||||
- **Adding a domain to `CERTBOT_DOMAIN` does not reissue by itself.** The loop
|
||||
calls `certbot renew`, which takes the names from the certificate it already
|
||||
holds and never reads `CERTBOT_DOMAIN`. On a machine that has one, issue the
|
||||
new set once by hand and the loop keeps it from then on:
|
||||
`certbot certonly --standalone -n --agree-tos --http-01-port=380 --cert-name <first-domain> --expand -d <the,new,list>`.
|
||||
- **`CERTBOT_DOMAIN` is read on every pass, and the certificate follows it.**
|
||||
The pass runs `certbot certonly --cert-name <first-name> --keep-until-expiring
|
||||
-d "$CERTBOT_DOMAIN"`, so an added name is on the certificate within 12 hours
|
||||
and no manual issuance is needed. It follows the other way too, which is the
|
||||
sharp edge: **removing** a name reissues without it, and the site loses TLS
|
||||
under that name on the next pass. Changing the *first* name is a third case —
|
||||
it names the lineage under `/etc/letsencrypt/live`, so a new certificate is
|
||||
started beside the old one and the old one stops being renewed.
|
||||
- **Let's Encrypt enforces rate limits.** Repeated failed issuance against the
|
||||
same domain counts against them; test changes against
|
||||
`--server https://acme-staging-v02.api.letsencrypt.org/directory` before
|
||||
|
||||
Reference in New Issue
Block a user