Commit Graph
21 Commits
Author SHA1 Message Date
bitdeals a842e0771e fix: HAProxy could not read the placeholder, so a failed issuance took the site down
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 36s
The placeholder is the whole reason a first start works at all: HAProxy
resolves `bind ... ssl crt` while parsing its configuration, so it cannot
start without site.pem, and certbot writes a self-signed one to break
that circle. It wrote it 0600 root-only, under the umask the subshell
sets for the private key it builds it from. HAProxy runs unprivileged
(uid 1001 in the bitnami image), so it could not open the file and exited
-- and because it never came up, it never created the runtime API socket
the pass waits for, and the pass hung in that wait instead of reaching
the 12-hour sleep. A node whose first issuance failed -- a typo in
CERTBOT_DOMAIN is enough -- served nothing at all on 80 or 443, retried
nothing, and said so only in two container logs.

The real certificate escapes this by accident: 2-concatenate-cert.sh
writes it with `cat >` under the default umask, so site.pem is 0644 on
every node that ever issued one. That is why nobody hit this -- it needs
a first issuance that fails.

chmod on the temporary file rather than after the rename, so the atomic
rename stays the only thing HAProxy can observe and site.pem never exists
with a mode that stops it. The intermediates keep the tight umask.

Reproduced on a disposable haproxy+certbot stack with empty volumes and
CERTBOT_DOMAIN=testnet2.bitdeals.invalid: before, HAProxy crash-looped on
"cannot open the file" while certbot waited for admin.sock; after, HAProxy
starts on the placeholder, certbot reports the rejected domain, the loop
retries in 12h, and the site answers on 80 and terminates TLS on 443 with
the self-signed certificate.

Claude-Session: https://claude.ai/code/session_01BvgYcYPWd1KGABLKViVPSk
2026-08-24 13:13:23 +00:00
bitdeals e8204bbb4d 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
2026-08-24 13:13:03 +00:00
bitdeals 36e1865e37 feat: CERTBOT_DOMAIN may name several domains
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 30s
certbot's own -d already takes a comma-separated list and puts every name
in the certificate's Subject Alternative Names, so `-d "$CERTBOT_DOMAIN"`
needed no change at all. What assumed a single domain was everything that
treated the variable as a *file name*: --cert-name, and the two scripts
that address the lineage under /etc/letsencrypt/live. Certbot names a
lineage after the first domain of the list, so all three now take
${CERTBOT_DOMAIN%%,*} instead of the whole string -- otherwise a two-domain
value asks for a lineage literally called "a.org,b.org" and the renewal
pass looks for a directory nobody made.

One certificate with several names rather than several lineages: HAProxy
binds a single site.pem, and a second lineage would have nowhere to go.

Adding a domain to CERTBOT_DOMAIN on a machine that already holds a
certificate still needs one manual issuance -- `certbot renew` reads the
names off the certificate it has and never looks at the variable. The
README says so, and gives the command.
2026-08-20 11:53:12 +00:00
bitdeals 9dfa45f901 ci: pass the registry password on stdin, fail on a failed push
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 1m1s
A password in argv is world-readable through /proc/PID/cmdline while the
command runs; docker warns about it for that reason. The two pushes are joined
with && so the step's outcome is explicit rather than resting on whatever -e
flag the runner's shell happens to carry.
2026-08-07 13:39:55 +00:00
bitdeals 55083856c1 docs: add README in English and Russian
Same structure as the bitmessage and bitcoind repositories. The notes carry
what the scripts cannot say for themselves: that the first certificate is a
self-signed placeholder and stays one if issuance fails, that a refused
installation is reported but cannot be repaired, that the scripts are sourced
rather than executed so working directory and set -e are inherited, and that a
stop during issuance can outlast docker's grace period.
2026-08-07 13:39:55 +00:00
bitdeals 45eb85fae8 refactor: move the renewal loop into a script, reset the base entrypoint
The loop lived in a shell-form ENTRYPOINT one-liner: unlintable, uncommentable,
and expanded by docker into two nested shells. It is a file now, and the loop
is CMD with the base image's `certbot` entrypoint reset — CMD is appended to
ENTRYPOINT rather than replacing it, so without the reset the loop would have
arrived as arguments to certbot. In exchange a one-off run replaces the loop
outright and needs no --entrypoint:

    docker run --rm -v letsencrypt:/etc/letsencrypt <image> certbot certificates

`wait $(jobs -p)` became a bare `wait`: the command substitution runs in a
subshell that reports the parent's jobs in bash but not in dash, and bare
`wait` waits for every background job in either. A failed pass is now reported
rather than passing silently, and the trap covers INT as well so Ctrl-C in an
interactive run works.

What this does not fix: a signal arriving while certbot is talking to Let's
Encrypt is held until that call returns, because a POSIX shell runs a trap only
after the foreground command finishes. That can outlast docker's ten-second
stop grace. Set stop_grace_period on the service if a clean stop matters.
2026-08-07 13:39:43 +00:00
bitdeals 031a92265c fix: repair issuance, verify installation, reach HAProxy over a unix socket
Setting CERTBOT_EMAIL broke certificate issuance outright. CERTBOT_OPTS was
built as "--email $CERTBOT_EMAIL" and passed quoted, so certbot received the
flag and its value as one argument and rejected it as unrecognised; `set -e`
then ended the pass, and the loop repeated the same failure every 12 hours
while the site served the self-signed placeholder. The arguments are built
with `set --` now, which keeps them two words.

Installation into the running HAProxy reported success whatever happened. The
runtime API answers a refusal in the reply text and still closes cleanly, so
socat exits 0 either way; the script sent set, commit and show in a row and
inspected none of them. It now matches the replies and stops at the first that
is not an acknowledgement, saying plainly that the running HAProxy is still on
its previous certificate. It also reaches the API over a unix socket on a
volume shared with haproxy rather than TCP 9999, so the private key it carries
no longer crosses a network in the clear.

Also:

- The self-signed placeholder wrote site.key, site.csr and site.crt to the
  working directory (/opt/certbot) and left the private key there for good.
  It is built in a subshell under umask 077 on the volume the key belongs on,
  the CSR step is gone, and `>>` is replaced by a temporary file and an atomic
  rename — HAProxy reads site.pem at start-up and must never find it partial.
- 2-concatenate-cert.sh changed into the live directory without checking, and
  on failure looked for fullchain.pem in whatever directory the caller was
  sitting in — the scripts are sourced, so that is a real possibility. It uses
  absolute paths and reports when there is nothing to concatenate.
- The wait for HAProxy watches for the runtime API socket instead of probing
  TCP 9999, which no longer exists.
- compose: /var/lib/letsencrypt is declared a VOLUME by the base image, so
  leaving it unnamed created a fresh anonymous volume on every container
  creation. It and the volumes section, which was missing entirely, are added.
  Build context is the repository root, as the Dockerfile's COPY expects.

Verified on testnet2 against a real unix socket: the happy path sends set,
commit and show; a refused set stops before commit; a refused commit and an
unreachable socket are both reported. The runtime API payload is byte-identical
to what the previous echo produced.
2026-08-07 13:39:43 +00:00
private-user 3d59a1eea1 fix
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 1m33s
2026-07-01 16:32:47 +03:00
private-user 75a59eab66 fix
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 29s
2026-06-23 12:12:38 +03:00
private-user 12f9464fd4 add entrypoint
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 2m19s
2026-03-26 14:02:14 +03:00
private-user a283109207 fix socat: Connection refused
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 2m30s
2026-03-12 16:02:41 +03:00
private-user 6540f7c80a fix act_runner build job
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 48s
2026-03-06 12:55:41 +03:00
private-user c8146bb9e1 add act_runner build job
Build docker image and push to registry.bitdeals.org / main-build-job (push) Failing after 26s
2026-02-09 11:40:40 +03:00
private-user aa341c0d62 change variable names 2025-08-13 13:54:43 +03:00
private-user 5e98593f0a add registry 2025-03-19 12:33:07 +03:00
private-user 84a470bdfe dockerfile fix 2024-11-19 12:10:04 +03:00
private-user a5533e43ec add optional e-mail 2024-11-05 16:52:21 +03:00
private-user ce357978a4 add docker-compose file 2024-11-02 16:09:29 +03:00
private-user 3c2506d2cd add --register-unsafely-without-email 2024-11-02 16:04:57 +03:00
private-user 10e8804de5 add dockerfile and scripts 2024-11-02 13:20:43 +03:00
private-user a09ac126d7 add dockerfile and scripts 2024-11-02 13:09:02 +03:00