mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-05-18 11:53:56 +08:00
support dns-persist-01
This commit is contained in:
68
README.md
68
README.md
@@ -146,6 +146,7 @@
|
||||
| 🌐 DNS mode | Use DNS TXT records |
|
||||
| 🔗 [DNS alias mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode) | Use DNS alias for verification |
|
||||
| 📡 [Stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode) | Stateless verification |
|
||||
| 📌 DNS persist mode | Persistent DNS TXT record ([draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/)) |
|
||||
|
||||
---
|
||||
|
||||
@@ -396,7 +397,50 @@ acme.sh --renew -d example.com
|
||||
|
||||
---
|
||||
|
||||
### 🔟 Issue Certificates of Different Key Types (ECC or RSA)
|
||||
### 🔟 Use DNS Persist Mode
|
||||
|
||||
📚 Spec: [draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/)
|
||||
|
||||
DNS persist mode lets you place a **single, long‑lived `_validation-persist` TXT record** in your zone and reuse it for every subsequent issuance and renewal. There is no per-issuance challenge token, so renewals require **no DNS edits** — useful when DNS API access is not available but you still want unattended renewals.
|
||||
|
||||
#### 🪄 Step 1: Print the TXT record value
|
||||
|
||||
```bash
|
||||
acme.sh --make-dns-persist-value -d example.com [--server letsencrypt] [--dns-persist-wildcard] [--dns-persist-ca-name "sectigo.com"]
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--server <ca>` | Pick the CA (default is your configured default). The account is registered automatically if you have not used this CA before. |
|
||||
| `--dns-persist-wildcard` | Adds `policy=wildcard` to the record so it also authorizes wildcard / subdomain certs. |
|
||||
| `--dns-persist-ca-name <name>` | Use a specific CA identity domain (e.g. `sectigo.com`). If omitted, identities are read from the ACME directory's `caaIdentities` field and one record per identity is printed — you only need to add **any one** of them. |
|
||||
|
||||
You should get an output like:
|
||||
|
||||
```sh
|
||||
TXT domain: _validation-persist.example.com
|
||||
TXT value: "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123456789"
|
||||
```
|
||||
|
||||
#### ✍️ Step 2: Add the TXT record to your DNS
|
||||
|
||||
Add the printed `TXT domain` / `TXT value` pair as a TXT record at your DNS provider, then wait for it to propagate.
|
||||
|
||||
#### 📜 Step 3: Issue the certificate
|
||||
|
||||
```bash
|
||||
acme.sh --issue -d example.com --dns-persist
|
||||
```
|
||||
|
||||
✅ **Done!** No challenge token is provisioned during issuance — the CA reads the persistent TXT record directly.
|
||||
|
||||
> 🔄 Renewals just work: `acme.sh --renew -d example.com` (or the cron job) reuses the same TXT record automatically — no further DNS edits needed.
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣1️⃣ Issue Certificates of Different Key Types (ECC or RSA)
|
||||
|
||||
Just set the `keylength` to a valid, supported value.
|
||||
|
||||
@@ -427,7 +471,7 @@ acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com -
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣1️⃣ Issue Wildcard Certificates
|
||||
### 1️⃣2️⃣ Issue Wildcard Certificates
|
||||
|
||||
It's simple! Just give a wildcard domain as the `-d` parameter:
|
||||
|
||||
@@ -439,7 +483,7 @@ acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣2️⃣ How to Renew Certificates
|
||||
### 1️⃣3️⃣ How to Renew Certificates
|
||||
|
||||
> 🔄 No need to renew manually! All certs will be renewed automatically every **30** days.
|
||||
|
||||
@@ -457,7 +501,7 @@ acme.sh --renew -d example.com --force --ecc
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣3️⃣ How to Stop Certificate Renewal
|
||||
### 1️⃣4️⃣ How to Stop Certificate Renewal
|
||||
|
||||
To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
|
||||
|
||||
@@ -471,7 +515,7 @@ The cert/key file is not removed from the disk.
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣4️⃣ How to Upgrade acme.sh
|
||||
### 1️⃣5️⃣ How to Upgrade acme.sh
|
||||
|
||||
> 🚀 acme.sh is in constant development — it's strongly recommended to use the latest code.
|
||||
|
||||
@@ -495,25 +539,25 @@ acme.sh --upgrade --auto-upgrade 0
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣5️⃣ Issue a Certificate from an Existing CSR
|
||||
### 1️⃣6️⃣ Issue a Certificate from an Existing CSR
|
||||
|
||||
📚 https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣6️⃣ Send Notifications in Cronjob
|
||||
### 1️⃣7️⃣ Send Notifications in Cronjob
|
||||
|
||||
📚 https://github.com/acmesh-official/acme.sh/wiki/notify
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣7️⃣ Under the Hood
|
||||
### 1️⃣8️⃣ Under the Hood
|
||||
|
||||
> 🔧 Speak ACME language using shell, directly to "Let's Encrypt".
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣8️⃣ Acknowledgments
|
||||
### 1️⃣9️⃣ Acknowledgments
|
||||
|
||||
| Project | Link |
|
||||
|---------|------|
|
||||
@@ -555,7 +599,7 @@ Support this project with your organization. Your logo will show up here with a
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣9️⃣ License & Others
|
||||
### 2️⃣0️⃣ License & Others
|
||||
|
||||
📄 **License:** GPLv3
|
||||
|
||||
@@ -565,7 +609,7 @@ Support this project with your organization. Your logo will show up here with a
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣0️⃣ Donate
|
||||
### 2️⃣1️⃣ Donate
|
||||
|
||||
> 💝 Your donation makes **acme.sh** better!
|
||||
|
||||
@@ -577,7 +621,7 @@ Support this project with your organization. Your logo will show up here with a
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣1️⃣ About This Repository
|
||||
### 2️⃣2️⃣ About This Repository
|
||||
|
||||
> [!NOTE]
|
||||
> This repository is officially maintained by <strong>ZeroSSL</strong> as part of our commitment to providing secure and reliable SSL/TLS solutions. We welcome contributions and feedback from the community!
|
||||
|
||||
155
acme.sh
155
acme.sh
@@ -59,6 +59,7 @@ DEFAULT_OPENSSL_BIN="openssl"
|
||||
VTYPE_HTTP="http-01"
|
||||
VTYPE_DNS="dns-01"
|
||||
VTYPE_ALPN="tls-alpn-01"
|
||||
VTYPE_DNS_PERSIST="dns-persist-01"
|
||||
|
||||
ID_TYPE_DNS="dns"
|
||||
ID_TYPE_IP="ip"
|
||||
@@ -71,6 +72,7 @@ NO_VALUE="no"
|
||||
|
||||
W_DNS="dns"
|
||||
W_ALPN="alpn"
|
||||
W_DNS_PERSIST="dns_persist"
|
||||
DNS_ALIAS_PREFIX="="
|
||||
|
||||
MODE_STATELESS="stateless"
|
||||
@@ -4028,6 +4030,85 @@ deactivateaccount() {
|
||||
fi
|
||||
}
|
||||
|
||||
#domain wildcard ca_name
|
||||
#Print the TXT record(s) the user must add to enable persistent DNS validation
|
||||
#per draft-ietf-acme-dns-persist-01.
|
||||
makednspersistvalue() {
|
||||
_mdpv_domain="$1"
|
||||
_mdpv_wildcard="$2"
|
||||
_mdpv_ca_name="$3"
|
||||
|
||||
if [ -z "$_mdpv_domain" ]; then
|
||||
_err "Please specify a domain with -d."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_initpath
|
||||
|
||||
_accUri="$(_readcaconf ACCOUNT_URL)"
|
||||
if [ -z "$_accUri" ]; then
|
||||
_info "No account is registered for $ACME_DIRECTORY yet, registering one now..."
|
||||
if ! _regAccount "$DEFAULT_ACCOUNT_KEY_LENGTH"; then
|
||||
_err "Cannot register account."
|
||||
return 1
|
||||
fi
|
||||
_accUri="$(_readcaconf ACCOUNT_URL)"
|
||||
fi
|
||||
|
||||
if [ -z "$_accUri" ]; then
|
||||
_err "Cannot determine the ACME account URL."
|
||||
return 1
|
||||
fi
|
||||
_debug "Account URL" "$_accUri"
|
||||
|
||||
_txt_name="_validation-persist.$_mdpv_domain"
|
||||
|
||||
_txt_suffix="; accounturi=$_accUri"
|
||||
if [ "$_mdpv_wildcard" = "1" ]; then
|
||||
_txt_suffix="$_txt_suffix; policy=wildcard"
|
||||
fi
|
||||
|
||||
if [ -n "$_mdpv_ca_name" ]; then
|
||||
_info ""
|
||||
_info "Add the following DNS TXT record to enable persistent DNS validation:"
|
||||
_info ""
|
||||
_info "$(printf 'TXT domain: %s' "$(__green "$_txt_name")")"
|
||||
_info "$(printf 'TXT value: %s' "$(__green "\"$_mdpv_ca_name$_txt_suffix\"")")"
|
||||
_info ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
_info "Fetching ACME directory: $ACME_DIRECTORY"
|
||||
_dir_resp="$(_get "$ACME_DIRECTORY" "" 30)"
|
||||
if [ "$?" != "0" ] || [ -z "$_dir_resp" ]; then
|
||||
_err "Cannot fetch ACME directory: $ACME_DIRECTORY"
|
||||
return 1
|
||||
fi
|
||||
_dir_resp="$(echo "$_dir_resp" | _json_decode)"
|
||||
_debug2 _dir_resp "$_dir_resp"
|
||||
|
||||
_caa_array="$(echo "$_dir_resp" | tr -d ' \r\n\t' | _egrep_o '"caaIdentities":\[[^]]*\]')"
|
||||
_debug2 _caa_array "$_caa_array"
|
||||
_caaids="$(echo "$_caa_array" | sed 's/.*\[//' | sed 's/\].*//' | tr ',' '\n' | tr -d '"')"
|
||||
_debug2 _caaids "$_caaids"
|
||||
|
||||
if [ -z "$_caaids" ]; then
|
||||
_err "The directory does not include 'caaIdentities'. Please specify --dns-persist-ca-name explicitly."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info ""
|
||||
_info "Add ANY ONE of the following DNS TXT records to enable persistent DNS validation."
|
||||
_info "(You only need to add one; pick whichever issuer identity you prefer.)"
|
||||
for _id in $_caaids; do
|
||||
[ -z "$_id" ] && continue
|
||||
_info ""
|
||||
_info "$(printf 'TXT domain: %s' "$(__green "$_txt_name")")"
|
||||
_info "$(printf 'TXT value : %s' "$(__green "\"$_id$_txt_suffix\"")")"
|
||||
done
|
||||
_info ""
|
||||
}
|
||||
|
||||
# domain folder file
|
||||
_findHook() {
|
||||
_hookdomain="$1"
|
||||
@@ -4806,7 +4887,9 @@ $_authorizations_map"
|
||||
|
||||
vtype="$VTYPE_HTTP"
|
||||
#todo, v2 wildcard force to use dns
|
||||
if _startswith "$_currentRoot" "$W_DNS"; then
|
||||
if [ "$_currentRoot" = "$W_DNS_PERSIST" ]; then
|
||||
vtype="$VTYPE_DNS_PERSIST"
|
||||
elif _startswith "$_currentRoot" "$W_DNS"; then
|
||||
vtype="$VTYPE_DNS"
|
||||
fi
|
||||
|
||||
@@ -4864,18 +4947,7 @@ $_authorizations_map"
|
||||
fi
|
||||
|
||||
if [ -z "$keyauthorization" ]; then
|
||||
token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
|
||||
_debug token "$token"
|
||||
|
||||
if [ -z "$token" ]; then
|
||||
_err "Cannot get domain token $entry"
|
||||
_clearup
|
||||
_on_issue_err "$_post_hook"
|
||||
return 1
|
||||
fi
|
||||
|
||||
uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
|
||||
|
||||
_debug uri "$uri"
|
||||
|
||||
if [ -z "$uri" ]; then
|
||||
@@ -4884,8 +4956,26 @@ $_authorizations_map"
|
||||
_on_issue_err "$_post_hook"
|
||||
return 1
|
||||
fi
|
||||
keyauthorization="$token.$thumbprint"
|
||||
_debug keyauthorization "$keyauthorization"
|
||||
|
||||
if [ "$vtype" = "$VTYPE_DNS_PERSIST" ]; then
|
||||
# dns-persist-01 challenges have no token; the TXT record is
|
||||
# provisioned out-of-band. Use a non-empty placeholder so the
|
||||
# downstream code does not treat this entry as already verified.
|
||||
keyauthorization="$VTYPE_DNS_PERSIST"
|
||||
_debug keyauthorization "$keyauthorization"
|
||||
else
|
||||
token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
|
||||
_debug token "$token"
|
||||
|
||||
if [ -z "$token" ]; then
|
||||
_err "Cannot get domain token $entry"
|
||||
_clearup
|
||||
_on_issue_err "$_post_hook"
|
||||
return 1
|
||||
fi
|
||||
keyauthorization="$token.$thumbprint"
|
||||
_debug keyauthorization "$keyauthorization"
|
||||
fi
|
||||
fi
|
||||
|
||||
dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot$sep$_authz_url"
|
||||
@@ -7158,6 +7248,8 @@ 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 <domain>.
|
||||
--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.
|
||||
@@ -7205,6 +7297,10 @@ Parameters:
|
||||
--dns [dns_hook] Use dns manual mode or dns api. Defaults to manual mode when argument is omitted.
|
||||
See: $_DNS_API_WIKI
|
||||
|
||||
--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.
|
||||
|
||||
--dnssleep <seconds> 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, $PROJECT_NAME polls dns status by DOH automatically.
|
||||
-k, --keylength <bits> Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
|
||||
@@ -7215,6 +7311,14 @@ Parameters:
|
||||
--eab-kid <eab_key_id> Key Identifier for External Account Binding.
|
||||
--eab-hmac-key <eab_hmac_key> HMAC key for External Account Binding.
|
||||
|
||||
--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 (draft-ietf-acme-dns-persist-01).
|
||||
--dns-persist-ca-name <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, the identities are read from the ACME directory's
|
||||
'caaIdentities' field and one record is printed per identity.
|
||||
|
||||
|
||||
These parameters are to install the cert to nginx/Apache or any other server after issue/renew a cert:
|
||||
|
||||
@@ -7585,6 +7689,8 @@ _process() {
|
||||
_valid_to=""
|
||||
_certificate_profile=""
|
||||
_extended_key_usage=""
|
||||
_dns_persist_wildcard=""
|
||||
_dns_persist_ca_name=""
|
||||
while [ ${#} -gt 0 ]; do
|
||||
case "${1}" in
|
||||
|
||||
@@ -7679,6 +7785,16 @@ _process() {
|
||||
--deactivate-account)
|
||||
_CMD="deactivateaccount"
|
||||
;;
|
||||
--make-dns-persist-value | --makednspersistvalue)
|
||||
_CMD="makednspersistvalue"
|
||||
;;
|
||||
--dns-persist-wildcard | --dnspersistwildcard)
|
||||
_dns_persist_wildcard="1"
|
||||
;;
|
||||
--dns-persist-ca-name | --dnspersistcaname)
|
||||
_dns_persist_ca_name="$2"
|
||||
shift
|
||||
;;
|
||||
--set-notify)
|
||||
_CMD="setnotify"
|
||||
;;
|
||||
@@ -7822,6 +7938,14 @@ _process() {
|
||||
_webroot="$_webroot,$wvalue"
|
||||
fi
|
||||
;;
|
||||
--dns-persist)
|
||||
wvalue="$W_DNS_PERSIST"
|
||||
if [ -z "$_webroot" ]; then
|
||||
_webroot="$wvalue"
|
||||
else
|
||||
_webroot="$_webroot,$wvalue"
|
||||
fi
|
||||
;;
|
||||
--dnssleep)
|
||||
_dnssleep="$2"
|
||||
Le_DNSSleep="$_dnssleep"
|
||||
@@ -8238,6 +8362,9 @@ _process() {
|
||||
deactivateaccount)
|
||||
deactivateaccount
|
||||
;;
|
||||
makednspersistvalue)
|
||||
makednspersistvalue "$_domain" "$_dns_persist_wildcard" "$_dns_persist_ca_name"
|
||||
;;
|
||||
list)
|
||||
list "$_listraw" "$_domain"
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user