Actions
Bug #16894
openLet's Encrypt fullchain file is truncated when moving from /tmp/acme to /conf/acme
Status:
New
Priority:
Normal
Assignee:
-
Category:
ACME
Target version:
-
Start date:
Due date:
% Done:
0%
Estimated time:
Plus Target Version:
Affected Version:
2.8.1
Affected Plus Version:
Affected Architecture:
All
Description
ACME package 1.2. With "Write ACME certificates to /conf/acme/" option selected in General Settings, the [domain].fullchain file in /conf/acme does not contain the Root YR cert. The cert is present in the fullchain.cer file found in /tmp/acme after the domain cert is issued from Let's Encrypt. If the [domain].fullchain file is subsequently used by an application on a server where the Root YR certificate is not in its list of CA certs, application authentication can fail.
A description of the problem and the current workaround: https://forum.netgate.com/topic/200786/certificate-chain-causing-application-authentication-failure.
Updated by Dan Singletary 16 days ago
Confirming this on ACME package 1.2. Same truncation here, and I traced it to the source.
ROOT CAUSE¶
The exporter builds the chain from the leaf cert plus only its single immediate CA.
In /usr/local/pkg/acme/acme.inc, acme_write_certificate():
$ca = lookup_ca($cert['caref']); // leaf's immediate issuer ONLY
...
file_put_contents("{$prefix}/{$cert['descr']}.fullchain", $crt . $ca);
It looks up exactly one CA (the cert's caref), never walks the rest of the chain, andignores acme.sh's own fullchain.cer (which is complete). The same export is driven by
acme_write_all_certificates() from acme_command.sh (the "Write certificates to /conf/acme"
/ writecerts path). This was latent until Let's Encrypt's "Generation Y" hierarchy. The old chain was
single-level (leaf -> R10/R11 -> ISRG Root X1, with X1 in the trust store), so leaf +
immediate CA happened to be a complete chain. Gen-Y is two-level: leaf -> Let's Encrypt YR1 -> ISRG Root YR (cross-signed by ISRG Root X1) The single-CA export stops at YR1 and drops the cross-signed Root YR, so
/conf/acme/<domain>.fullchain no longer chains to a trusted root. acme.sh's
/tmp/acme/.../fullchain.cer has all three certs; the package re-truncates on export.
WORKAROUND¶
1. Set Preferred Chain = "ISRG Root X1" on the cert, so acme.sh writes the cross-signed
(3-cert) chain into fullchain.cer.
2. Add a post-issuance "Shell command" action that copies the real fullchain over the
truncated export:
cp /tmp/acme/CERTNAME/*/fullchain.cer /conf/acme/DOMAIN.fullchainThe action runs after the writecerts step (acme_command.sh: writecerts then actionlist),
so it is the last writer.
SUGGESTED FIX¶
acme_write_certificate() should export the complete chain: simplest is to copy acme.sh's
fullchain.cer (it already honors Preferred Chain), or follow each CA's parent caref up to
the root when assembling $ca, instead of a single lookup_ca($cert['caref']).
Actions