Certbot + HE.NET DDNS 签发通配符证书踩坑记录

最近用 Certbot 给域名签发通配符证书,DNS 托管在 HE.NET,用 API 自动验证。本以为很简单,结果连环踩坑,记录一下排错过程。

最初的错误脚本

这是我最开始写的 /opt/dns_auth.sh,里面集中了本次所有的坑:

#!/bin/bash
set -euo pipefail
# 【错误1】DNS-01验证根本没有 CERTBOT_TOKEN 环境变量,只有 CERTBOT_VALIDATION
KEYAUTH=$CERTBOT_TOKEN 
# 创建或更新 TXT 记录
curl -s -X POST "https://dyn.dns.he.net/nic/update" \
        # 【错误2】hostname 写死了,同时申请主域名和通配符域名时,第二次验证应该覆盖第一次的记录
        -d "hostname=_acme-challenge.test.local" -d "password=password" \
        # 【错误3】把验证码传给了 ttl 参数,根本没传 txt 参数,导致写入 DNS 的值完全错乱
        -d "ttl=${KEYAUTH}" | jq . 
        # 【错误4】HE.NET 返回纯文本,用 jq 解析会报错,且 set -e 会导致脚本在这里直接退出,后续的程序无法正常运行。

踩坑过程与状态码

第 1 次尝试:变量未定义
脚本在第一行就挂了,因为 CERTBOT_TOKEN 不存在,set -u 直接拦截。

Hook '--manual-auth-hook' for test.local reported error code 1
Hook '--manual-auth-hook' for test.local ran with error output:
 /opt/dns_auth.sh: line 7: CERTBOT_TOKEN: unbound variable
...
Detail: Incorrect TXT record "GIh97XkH-ICQBLEfubTEEmR2j8a-3OXKbl21WYP9PHE" found at _acme-challenge.test.local

第 2 次尝试:API 参数写反 + jq 报错
修改变量名后,因为把值传给了 ttl 没传 txt,同时在dns.he.net给对应的记录配置DDNS的时候在DNS 记录那里写入了ddns_token的值;同时 jq 解析纯文本报错,导致脚本中断,没有等待 DNS 生效。

真正配置ddns_token的地方是在记录名配置窗口勾选了”Enable entry for dynamic dns”之后在右边会出现一个刷新的图标

然后在弹出的窗口输入或者生成一个ddns_token,点击蓝色按钮”Generate a key”就是生成一个密钥,如果不点击也可以直接在两个黄色框里面分别输入*两次相同的内容*1作为密钥 ,然后点击绿色按钮提交即可。

**内容仅限英文和数字**

Hook '--manual-auth-hook' for test.local reported error code 1
Hook '--manual-auth-hook' for test.local ran with error output:
 jq: parse error: Invalid numeric literal at line 1, column 5
...
Detail: Incorrect TXT record "password" found at _acme-challenge.test.local
Detail: Incorrect TXT record "Acme challenge key" found at _acme-challenge.test.local

第 3 次尝试:触发限速
频繁试错,1 小时内失败 5 次,触发 Let’s Encrypt 限速机制,只能干等。

An unexpected error occurred:
too many failed authorizations (5) for "*.test.local" in the last 1h0m0s, retry after 2026-05-11 14:57:21 UTC

第 4 次尝试:成功
修正所有错误,删除 jq,使用 txt=${VALIDATION},动态拼接 hostname,加上 sleep 30

最终的正确脚本

/opt/dns_auth.sh

#!/usr/bin/env bash
set -euo pipefail
# 获取验证码
VALIDATION=$CERTBOT_VALIDATION
DOMAIN=$CERTBOT_DOMAIN
# 提取主域名:把 *.test.local 转换为 test.local
if [[ "$DOMAIN" == *.test.local ]]; then
    REAL_DOMAIN="${DOMAIN#*.}"
else
    REAL_DOMAIN=$DOMAIN
fi
# 动态拼接 hostname,避免同时签发主域名和通配符时记录互相覆盖
RECORD_NAME="_acme-challenge.${REAL_DOMAIN}"
# 正确将验证码传给 txt 参数,去掉 jq
curl -s -X POST "https://dyn.dns.he.net/nic/update" \
        -d "hostname=${RECORD_NAME}" \
        -d "password=你的DDNS密码" \
        -d "txt=${VALIDATION}"
# 等待 DNS 生效
sleep 30

签发命令:

certbot certonly --manual \
  --preferred-challenges=dns \
  --manual-auth-hook /opt/dns_auth.sh \
  -d test.local \
  -d *.test.local

几句讲解

  • DNS-01 验证只有 CERTBOT_VALIDATION,没有 CERTBOT_TOKEN
  • HE.NET 的 API 用 txt= 传验证码,别传错参数。
  • API 返回值是纯文本,别用 jq 解析,配合 set -e 会导致脚本意外中断。
  • 同时签主域名和通配符时,hostname 一定要动态拼接,否则两次验证的记录会互相覆盖。
  • 排错时先手动用 curl 测 API 是否返回 good,别直接拿 Certbot 试,容易触发限速。
  1. **内容仅限英文和数字** ↩︎

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇