acmesh-official / acmesh-official/acme.sh

Manual DNS renewal downloads previous cert due to stale Le_LinkOrder/Le_LinkCert reuse

Aperta
#7,105 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Shell
Stelle
47.6k
Fork
5.7k
Merge medio
6g 5h
PR unite (30g)
15

Descrizione

## Version
v3.1.2 confirmed reproducing; v3.1.4 still contains the same code paths.

## Environment
- macOS 15.5 (Darwin 25.5.0), bash
- CA: ZeroSSL (acme.zerossl.com/v2/DV90)
- Cert type: ECC wildcard (`*.example.com`)
- Renewal mode: manual DNS (`--dns --yes-I-know-dns-manual-mode-enough-go-ahead-please`)

## Reproduction
1. Issue a wildcard cert in manual DNS mode. `Le_LinkOrder` and `Le_LinkCert` get written to the domain's `.conf`.
2. ~90 days later, run `--renew ... --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please`.
3. First invocation prints the TXT challenge and exits (expected).
4. Add the TXT record; second invocation reports \"Cert success\" and writes the cert files.

**Actual result:** the written cert is byte-for-byte identical to the previous cert (same serial, fingerprint, notAfter — still expired). acme.sh only reissues on the very first `--issue`; every subsequent manual-DNS `--renew` silently re-downloads the previous cert.

## Log excerpt showing the bug
\`\`\`
Le_OrderFinalize='https://acme.example/v2/.../order/NEW-ORDER-ID/finalize'
Order status is 'processing', let's sleep and retry.
Polling order status: https://acme.example/v2/.../order/OLD-ORDER-ID <-- previous run's order
Le_LinkCert='https://acme.example/v2/.../cert/OLD-CERT-ID' <-- previous run's cert
\`\`\`

## Root cause
In \`_issue()\`:

**acme.sh:4862-4866 (v3.1.4)** — the reset is intentionally skipped for DNS manual mode (to preserve state across the two-invocation flow), but this preserves state from the *previous cert's lifetime*, not just the current run:
\`\`\`sh
elif ! _hasfield \"\$_web_roots\" \"\$W_DNS\"; then
Le_OrderFinalize=\"\"
Le_LinkOrder=\"\"
Le_LinkCert=\"\"
fi
\`\`\`

**acme.sh:5689-5691 (v3.1.4)** — after finalizing a fresh order, \`Le_LinkOrder\` is only pulled from response headers when the variable is empty. On renewal it isn't, so the subsequent poll (\`_info \"Polling order status: \$Le_LinkOrder\"\`) hits the old order, which is still \`status:valid\` and returns the old certificate URL:
\`\`\`sh
if [ -z \"\$Le_LinkOrder\" ]; then
Le_LinkOrder=\"\$(echo \"\$responseHeaders\" | grep -i '^Location.*\$' | ...)\"
fi
\`\`\`

## Workaround
Manually strip the three variables from the domain \`.conf\` before running \`--renew\`:
\`\`\`sh
sed -i '' \"/^Le_LinkOrder=/d;/^Le_LinkCert=/d;/^Le_OrderFinalize=/d\" ~/.acme.sh/DOMAIN_ecc/DOMAIN.conf
\`\`\`
Then \`--issue --force\` produces a genuinely new cert.

## Suggested fixes (any one)
1. Overwrite unconditionally after finalize: drop the \`[ -z \"\$Le_LinkOrder\" ]\` guard and always pull from response headers.
2. Reset \`Le_LinkOrder\`/\`Le_LinkCert\` at the top of the finalize step (not the top of \`_issue\`), so the manual-DNS state preservation doesn't include them.
3. Reset these three variables whenever a new order is created in this run.

Happy to submit a PR if a preferred approach is confirmed.

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

The bug is in the `_issue()` function in acme.sh, lines 4862-4866 and 5689-5691 (v3.1.4). Start by reading the manual DNS renewal flow and how state variables (Le_LinkOrder, Le_LinkCert, Le_OrderFinalize) are managed. The fix involves modifying the condition for resetting these variables or updating them after finalizing an order. Test by simulating a manual DNS renewal cycle and verifying a new certificate is issued.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
bash, shell
Ambito
cli, security
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
45/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.