From 3eb7404539702baf153a575d08c63e64c61aac79 Mon Sep 17 00:00:00 2001 From: neil Date: Fri, 1 May 2026 15:12:07 +0200 Subject: [PATCH] update wiki --- ARI.md | 86 +++++++++++++++++++++++++++++++ DNS-manual-mode.md | 5 +- DNS-persist-mode.md | 111 +++++++++++++++++++++++++++++++++++++++++ Home.md | 7 ++- How-to-issue-a-cert.md | 20 +++++++- Options-and-Params.md | 32 +++++++++++- Validity.md | 2 +- 说明.md | 46 +++++++++++++++-- 8 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 ARI.md create mode 100644 DNS-persist-mode.md diff --git a/ARI.md b/ARI.md new file mode 100644 index 0000000..08cc178 --- /dev/null +++ b/ARI.md @@ -0,0 +1,86 @@ +acme.sh implements **ACME Renewal Information** ([RFC 9773](https://www.rfc-editor.org/rfc/rfc9773.html)) automatically. **No flag, no opt-in, no configuration needed.** + +If your CA exposes a `renewalInfo` endpoint in its ACME directory (Let's Encrypt, ZeroSSL, Sectigo, SSL.com, …), acme.sh will use it. If the CA does not, acme.sh falls back to the classic 30-day fixed-interval rule — behavior is identical to before. + +## What it does + +| What | When | Why | +|------|------|-----| +| 🔍 **Polls `suggestedWindow`** | Every cron run, before deciding to skip | Lets the CA shift renewal forward in case of an incident (key compromise, mass revocation) | +| 🎯 **Picks a random renewal time** inside the window | Right after a successful issuance/renewal | Disperses renewals across the network so all clients don't hit the CA at the same instant | +| 🔗 **Sends `replaces=`** in newOrder | On `--renew` | Lets the CA correlate the new order with the certificate it supersedes (RFC 9773 §5) | +| ↩️ **Retries without `replaces`** | If the CA returns `alreadyReplaced` or an ARI validation error | Robust against edge cases (switching CAs, retired issuers, parallel renewal) | + +## When does the cert renew? + +acme.sh renews when **any one** of these is true: + +1. `--force` is given +2. The CA's **ARI `suggestedWindow.start` has passed** (so the CA is asking you to renew now) +3. The cached `Le_NextRenewTime` has passed (the classic 30-day fallback, also used when no ARI) + +This means a CA can effectively command an early renewal across the entire `acme.sh` user base by shrinking the suggested window — useful for emergency incident response. + +## Why this matters + +Short-lived certificates (Apple's 47-day proposal, Let's Encrypt's shortlived profile, etc.) are coming. With shorter lifetimes, fixed-interval renewal becomes brittle: + +- A 7-day cert with a 30-day fixed renewal interval would always be expired +- All clients renewing at the same offset would hammer the CA +- Emergency revocations need a way to push the renewal time forward + +ARI solves all three. The CA gets full control of *when* to renew; clients just follow its hints. + +## How to inspect + +The chosen next-renewal time is saved in the domain conf as `Le_NextRenewTimeStr`: + +```sh +acme.sh --info -d example.com +# Look for: Le_NextRenewTimeStr=... +``` + +To see the live ARI window the CA is currently advertising, run with `--debug 2`: + +```sh +acme.sh --renew -d example.com --debug 2 2>&1 | grep -i 'ARI suggestedWindow' +``` + +Example output: +``` +[INFO] ARI suggestedWindow: 2026-05-06T19:48:34Z to 2026-05-08T14:59:23Z +[INFO] Next renewal time picked from ARI window: 2026-05-07T16:57:49Z +``` + +## Internals + +- **certID** for ARI requests is computed per RFC 9773 §4.1 as `base64url(AKI) + "." + base64url(Serial)` of the existing certificate. +- **Window pick** is `start + (current_epoch % window_size)`. This is intentionally pseudo-random across clients (different cert issuance moments → different offsets) without needing crypto-grade randomness, and it is the same simple "use current time as entropy" pattern acme.sh already uses for cron randomization. +- **Failure handling**: any failure mode (ARI 404, network error, malformed response, CA does not support ARI) cleanly falls back to the original fixed-interval `Le_NextRenewTime` logic. ARI is a strict enhancement. + +## Relationship to other features + +- **Compatible with all validation modes**: webroot, standalone, alpn, DNS API, DNS manual, [DNS persist](https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode). ARI only changes *when* renewals happen, not *how* they validate. +- **Compatible with `--valid-to` / `--valid-from`** ([Validity](https://github.com/acmesh-official/acme.sh/wiki/Validity)): when an explicit notAfter is set, acme.sh respects the user-provided deadline and does not let ARI override it. +- **Compatible with `--cert-profile`** ([Profile selection](https://github.com/acmesh-official/acme.sh/wiki/Profile-selection)): independent. + +## CAs known to support ARI + +- ✅ Let's Encrypt +- ✅ ZeroSSL (when ACME server returns `renewalInfo`) +- Check your CA's directory — if it has a `"renewalInfo"` field, ARI is on. + +```sh +curl -s https://acme-v02.api.letsencrypt.org/directory | grep renewalInfo +``` + +## Spec reference + +Full normative reference: [RFC 9773 — Automated Certificate Management Environment (ACME) Renewal Information (ARI) Extension](https://www.rfc-editor.org/rfc/rfc9773.html) + +Key sections: +- §4.1 — certID computation +- §4.2 — `suggestedWindow` shape +- §4.3 — polling and `Retry-After` +- §5 — newOrder `replaces` field +- §7.4 — `alreadyReplaced` error code diff --git a/DNS-manual-mode.md b/DNS-manual-mode.md index a4a3c16..d4897f7 100644 --- a/DNS-manual-mode.md +++ b/DNS-manual-mode.md @@ -1,9 +1,10 @@ Warning: DNS manual mode can not renew automatically. -If your domain provider offers an DNS API, it's highly recommended to use DNS API mode instead. With the DNS API mode, you can automate the renewals. +If your domain provider offers an DNS API, it's highly recommended to use DNS API mode instead. With the DNS API mode, you can automate the renewals. +If your domain provider does **not** offer an API but you can edit your zone, **the recommended mode for unattended renewals is now [DNS persist mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode)**: you publish a single long-lived TXT record once, then renewals run automatically forever — no per-renewal DNS edit. DNS persist follows [draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/). -If your domain provider does **not** offer an API where you can add/edit TXT records of your domain, it is recommended to use [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode) instead. Or change the dns servers of your domain to anyone that support DNS api. +If neither API access nor DNS persist is available, you may also use [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode), or change the DNS servers of your domain to one that supports DNS API. DNS manual mode **should** be used for testing. If you do use it for your production server, remember to renew your certificate within 90 days. [Please, make sure you understand DNS manual mode](https://github.com/Neilpang/acme.sh/issues/1029). diff --git a/DNS-persist-mode.md b/DNS-persist-mode.md new file mode 100644 index 0000000..4855d9a --- /dev/null +++ b/DNS-persist-mode.md @@ -0,0 +1,111 @@ +DNS persist mode lets you publish a **single, long-lived TXT record once** in your zone and then issue / renew certificates **forever without further DNS edits** — including unattended cron renewals. + +It implements [draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/), an IETF draft that defines a persistent DNS authorization record at `_validation-persist.`. + +## Why use it + +DNS validation has historically forced a tradeoff: + +| Mode | Auto-renewal | Needs API access | +|------|--------------|------------------| +| [DNS API mode](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) | ✅ Yes | ✅ Required | +| [DNS manual mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-manual-mode) | ❌ No (manual every renewal) | ❌ Not needed | +| **DNS persist mode** | ✅ **Yes** | ❌ **Not needed** | + +If your DNS provider does not offer an API, but you can edit your zone once, **DNS persist is the right mode**. Renewals are fully automatic afterwards. + +## Step 1: Print the TXT record value + +```sh +acme.sh --make-dns-persist-value -d example.com +``` + +You'll get an output like: + +``` +TXT persist domain:_validation-persist.example.com +TXT persist value :"letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123456789" +``` + +The TXT value is human-readable text in the syntax of [RFC 8659 §4.2](https://www.rfc-editor.org/rfc/rfc8659.html#name-issue-property): + +``` +issuer-domain-name; accounturi=[; policy=wildcard] +``` + +If the CA's directory advertises multiple `caaIdentities` (e.g. ZeroSSL lists `sectigo.com`, `trust-provider.com`, etc.), one TXT record per identity is printed — **you only need to add ANY ONE of them**. + +### Optional flags for `--make-dns-persist-value` + +| Flag | Description | +|------|-------------| +| `--server ` | Pick the CA. The account is registered automatically if you have not used this CA before. Default is your configured default CA. | +| `--dns-persist-wildcard` | Adds `policy=wildcard` to the record so it also authorizes wildcard / subdomain certs. | +| `--dns-persist-ca-name ` | Use a specific CA identity domain (e.g. `ssl.com`). If omitted, identities are read from the ACME directory's `caaIdentities` field. | +| `--dns-persist-days ` | Adds a `persistUntil` field to the record so it self-expires N days from now. After that timestamp, the CA will refuse new validations against this record. Useful if you want a time-bounded authorization. Omit for a record with no expiry. | + +Examples: + +```sh +# Let's Encrypt +acme.sh --make-dns-persist-value -d example.com --server letsencrypt + +# Wildcard policy (also authorizes *.example.com) +acme.sh --make-dns-persist-value -d example.com --dns-persist-wildcard + +# Force a specific issuer name +acme.sh --make-dns-persist-value -d example.com --dns-persist-ca-name "ssl.com" + +# Self-expire the authorization after 365 days +acme.sh --make-dns-persist-value -d example.com --dns-persist-days 365 +``` + +## Step 2: Add the TXT record at your DNS provider + +Use whatever interface your DNS provider gives you to add the TXT record. The name is `_validation-persist.` and the value is the quoted string from the previous step. + +Wait for DNS propagation (usually a few minutes). + +## Step 3: Issue the certificate + +```sh +acme.sh --issue -d example.com --dns-persist +``` + +The CA reads your persistent TXT record directly — **no challenge token is provisioned during issuance**. This is the key difference vs. dns-01 / DNS API modes. + +## Renewal + +Cron will renew automatically — the same TXT record is reused forever. Combined with [ARI](https://github.com/acmesh-official/acme.sh/wiki/ARI), the CA also picks the renewal time for you, so the entire lifecycle is unattended. + +```sh +# Manual renewal also works: +acme.sh --renew -d example.com +``` + +## Trust model & security notes + +The persistent TXT record contains your **ACME account URI**. This means: + +- Only the holder of the corresponding ACME account key (i.e. you, on this machine) can issue certs for your domain through this record. +- Anyone reading your DNS sees your account URI. The account URI itself is not a secret — it identifies the account, but you also need the matching private key to use it. No issue. +- If you lose your ACME account key (for example, you reinstall and don't restore `~/.acme.sh/`), the TXT record becomes useless. You'll need to print and re-publish a new one with the new account. +- If you want to revoke this authorization, simply delete the TXT record. + +### Wildcard policy + +By default the record only authorizes the **exact** FQDN. Add `policy=wildcard` (via `--dns-persist-wildcard`) to additionally authorize: + +- The base domain +- `*.` wildcards +- Specific subdomains under it + +## Compatibility + +DNS persist is an IETF draft. Check that your CA implements it before relying on it. + +## Related + +- [DNS manual mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-manual-mode) — if you only want a one-off cert +- [DNS API providers](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) — if your DNS provider has an API +- [ARI auto-renewal](https://github.com/acmesh-official/acme.sh/wiki/ARI) — RFC 9773 renewal info diff --git a/Home.md b/Home.md index 2416795..7c0520c 100644 --- a/Home.md +++ b/Home.md @@ -9,6 +9,11 @@ https://wiki.acme.sh Here is the wiki page for acme.sh +## What's new + +- 📡 **[ARI auto-renewal (RFC 9773)](https://github.com/acmesh-official/acme.sh/wiki/ARI)** — acme.sh now follows the CA's `renewalInfo` endpoint automatically. No flag, no opt-in. Lets the CA shift renewals forward in case of an incident. +- 📌 **[DNS persist mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode)** — publish one TXT record once, renew forever without DNS API and without per-renewal manual edits. Implements draft-ietf-acme-dns-persist-01. + ## 1. How to install https://github.com/acmesh-official/acme.sh/wiki/How-to-install @@ -26,7 +31,7 @@ After installation, ``` /root/.acme.sh/acme.sh --issue --standalone -d ``` -After issuing, the cert will be automatically renewed every 60 days. +After issuing, the cert will be automatically renewed (every ~30 days, or earlier if the CA's [ARI](https://github.com/acmesh-official/acme.sh/wiki/ARI) endpoint advises so). 3) Install the cert to Proxmox: diff --git a/How-to-issue-a-cert.md b/How-to-issue-a-cert.md index c45951e..40773aa 100644 --- a/How-to-issue-a-cert.md +++ b/How-to-issue-a-cert.md @@ -60,7 +60,23 @@ See: https://github.com/acmesh-official/acme.sh/wiki/DNS-manual-mode See: https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode -#### 7) Apache mode: +#### 7) DNS persist mode: + +If your DNS provider does **not** offer an API but you can edit your zone once, DNS persist mode lets you publish a single long-lived TXT record and renew automatically forever — no per-renewal DNS edits. + +```sh +# Step 1: print the TXT record value +acme.sh --make-dns-persist-value -d example.com + +# Step 2: add the printed _validation-persist.example.com TXT record at your DNS provider + +# Step 3: issue +acme.sh --issue -d example.com --dns-persist +``` + +See: https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode + +#### 8) Apache mode: If your website is running apache server, acme.sh can use apache server to issue cert. And acme.sh will restore your apache conf after the cert is issued, don't worry. @@ -68,7 +84,7 @@ If your website is running apache server, acme.sh can use apache server to issue acme.sh --issue -d example.com --apache ``` -#### 8) Nginx mode: +#### 9) Nginx mode: If your website is running nginx server, acme.sh can use nginx server to issue cert. And acme.sh will restore your nginx conf after the cert is issued, don't worry. diff --git a/Options-and-Params.md b/Options-and-Params.md index 28663ab..9519a64 100644 --- a/Options-and-Params.md +++ b/Options-and-Params.md @@ -23,6 +23,9 @@ Commands: --update-account Update account info. --register-account Register account key. --deactivate-account Deactivate the account. + --make-dns-persist-value Print the DNS TXT record(s) to enable persistent DNS validation + (draft-ietf-acme-dns-persist-01). Use with -d . + See: https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode --create-account-key Create an account private key, professional use. --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job. --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically. @@ -60,6 +63,25 @@ Parameters: --dns [dns_hook] Use dns manual mode or dns api. Defaults to manual mode when argument is omitted. See: https://github.com/acmesh-official/acme.sh/wiki/dnsapi + --dns-persist Use dns-persist-01 validation (draft-ietf-acme-dns-persist-01). + Requires the persistent _validation-persist TXT record to already exist. + Use '--make-dns-persist-value' to print the value to add. + See: https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode + + --dns-persist-wildcard Used with '--make-dns-persist-value'. Adds 'policy=wildcard' to the + generated TXT record so the issuer is also authorized for wildcards + and subdomains. + + --dns-persist-ca-name Used with '--make-dns-persist-value'. Use the given CA identity domain + (e.g. 'ssl.com') as the issuer-domain-name in the TXT record. If + omitted, identities are read from the ACME directory's 'caaIdentities' + field and one record is printed per identity. + + --dns-persist-days Used with '--make-dns-persist-value'. Add a 'persistUntil' field to + the TXT record so the record self-expires N days from now (the CA + will refuse new validations against the record after that time). + If omitted, the record has no expiry. + --dnssleep The time in seconds to wait for all the txt records to propagate in dns api mode. It's not necessary to use this by default, acme.sh polls dns status by DOH automatically. -k, --keylength Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521. @@ -89,7 +111,7 @@ Parameters: --useragent Specifies the user agent string. it will be saved for future use too. -m, --email Specifies the account email, only valid for the '--install' and '--update-account' command. --accountkey Specifies the account key path, only valid for the '--install' command. - --days Specifies the days to renew the cert when using '--issue' command. The default value is 60 days. + --days Specifies the days to renew the cert when using '--issue' command. The default value is 30 days. Note: when the CA supports ARI (RFC 9773), the actual renewal time is picked from the CA's suggestedWindow and this fixed-day fallback is overridden. See the wiki page on ARI. --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer. --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer. --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses. @@ -136,4 +158,10 @@ Parameters: -``` \ No newline at end of file +``` + +## Note on automatic renewal timing + +If your CA exposes the ACME Renewal Information (ARI, RFC 9773) endpoint in its directory, acme.sh automatically uses the CA's `suggestedWindow` to schedule renewals — no flag is required. The cert is renewed when **any one** of the following becomes true: `--force` is given, the ARI window has started, or the cached `Le_NextRenewTime` has passed. + +See: https://github.com/acmesh-official/acme.sh/wiki/ARI \ No newline at end of file diff --git a/Validity.md b/Validity.md index 41c4279..7c98cff 100644 --- a/Validity.md +++ b/Validity.md @@ -47,7 +47,7 @@ Please be careful about the format, there are only `+*d` (for days) and `+*h` ( 1. `--valid-to +20d`(the cert will be renewed every 19 days). If the lifetime is longer than one day, it will renew at one day before. 2. `--valid-to +11h`(the cert will be renewed every 10 hours). If the lifttime is less than 24 hourst, it will renew at one hour before. -## Of course, if you don't use `--valid-to` parameter at all, the cert will be renewed every `60 days` as before. +## Of course, if you don't use `--valid-to` parameter at all, the cert will be renewed every `30 days` (or earlier if the CA's [ARI](https://github.com/acmesh-official/acme.sh/wiki/ARI) suggestedWindow says so). diff --git a/说明.md b/说明.md index 70f467b..caa5d27 100644 --- a/说明.md +++ b/说明.md @@ -118,7 +118,7 @@ acme.sh --set-default-ca --server letsencrypt 这需要你手动在域名上添加一条 TXT 解析记录,验证域名所有权。 -注意,如果使用手动验证,acme.sh 将无法自动更新证书,每次都需要手动添加解析来验证域名所有权。**如果有自动更新证书的需求,请使用自动验证(DNS API)。** +注意,如果使用手动验证,acme.sh 将无法自动更新证书,每次都需要手动添加解析来验证域名所有权。**如果有自动更新证书的需求,但 DNS 服务商没有 API,推荐使用下面的「DNS Persist 模式」。** ```shell acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com @@ -134,6 +134,35 @@ acme.sh --renew -d mydomain.com 注意这里现在用的是 `--renew` 参数 +### DNS Persist 模式(持久化 DNS 验证,无 API 也能自动续签) + +如果你的 DNS 服务商**没有 API**,但你能编辑一次解析记录,DNS Persist 模式让你只添加一条**长期有效的** TXT 记录,之后续签全程自动,**不再需要每次改 DNS**。 + +实现的是 IETF 草案 [draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/)。 + +```shell +# 第 1 步:打印需要添加的 TXT 记录值 +acme.sh --make-dns-persist-value -d example.com + +# 第 2 步:在你的 DNS 服务商面板里添加打印出来的 _validation-persist.example.com TXT 记录,等待解析生效 + +# 第 3 步:签发 +acme.sh --issue -d example.com --dns-persist +``` + +可选参数(用在第 1 步): + +| 参数 | 作用 | +|------|------| +| `--server ` | 指定 CA(默认是你已配置的默认 CA)。如果还没在该 CA 注册过账户,会自动注册。| +| `--dns-persist-wildcard` | 给记录加 `policy=wildcard`,同时授权通配符 / 子域证书。| +| `--dns-persist-ca-name ` | 指定特定的 CA 标识域名(例如 `ssl.com`)。省略时,从 ACME directory 的 `caaIdentities` 字段读取,每个标识各打印一条记录(任选其一添加即可)。| +| `--dns-persist-days ` | 给记录加 `persistUntil` 字段,使记录在 N 天后自动失效(CA 之后不再接受针对该记录的新验证)。省略则永不过期。| + +之后 cron 会自动续签,这条 TXT 记录会被反复复用,无需再动 DNS。 + +详见: https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode + ### 自动验证(DNS API) DNS 方式的真正强大之处在于可以使用域名解析商提供的 API 自动添加 TXT 记录,且在完成验证后删除对应的记录。 @@ -223,14 +252,25 @@ Le_RealFullChainPath=/etc/acme/example.com/chain.pem # 5. 更新证书 -目前证书每 30 天自动更新,你无需任何操作。 +证书自动更新,你无需任何操作。默认每 30 天自动更新一次,**或者更早**——如果 CA 通过 ACME Renewal Information(ARI,[RFC 9773](https://www.rfc-editor.org/rfc/rfc9773.html))告知应当提前续签时,acme.sh 会自动遵守。 -但是你也可以强制续签证书: +ARI 完全自动,**没有任何参数,无需开启**。如果 CA 的 directory 里没有 `renewalInfo` 端点,自动退回经典的 30 天规则,行为不变。 + +详见: https://github.com/acmesh-official/acme.sh/wiki/ARI + +如果想立即强制续签,可以: ```shell acme.sh --renew -d example.com --force ``` +查看下次续签时间: + +```shell +acme.sh --info -d example.com +# 看 Le_NextRenewTimeStr 字段 +``` + # 6. 关于修改 `reloadcmd` 目前修改 `reloadcmd` 没有专门的命令,可以通过重新安装证书来实现修改 `reloadcmd` 的目的。