acmesh-official / acmesh-official/acme.sh

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

オープン
#7,105 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Shell
スター
47.6k
フォーク
5.7k
平均マージ
6日 5時間
マージ済み PR(30日)
15

説明

## 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.

コントリビューションガイド

コントリビューションガイドを開く

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
bash, shell
領域
cli, security
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
45/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。