计划给自己的所有域名和子域名配置一个通用 HTTPS证书,包含以下 DNS Name:
DNS Name=*.annhe.net
DNS Name=*.tecbbs.com
DNS Name=annhe.net
DNS Name=tecbbs.com
新申请证书
使用 certbot-auto 申请通配符证书的方法如下
./certbot-auto --server https://acme-v02.api.letsencrypt.org/directory -d "*.tecbbs.com,tecbbs.com,*.annhe.net,annhe.net" --manual --preferred-challenges dns-01 certonly
像这样通配符域名和根域名都申请证书的情况,需要添加两条 TXT 记录
# dig _acme-challenge.annhe.net TXT +short
"KFUyjyiRzP80s6QCL_zrEeyzyhrXrtLicsmUpN9lifQ"
"FGq8ljI24pHy173X3PCQrO3L_B17c9R1fqfTqzAfGvE"
手工更新证书
使用如下命令,但是需要手工修改 TXT 记录。
certbot-auto renew
自动更新证书
certbot-auto 支持 hook,可以在 auth hook 里验证 DNS 记录,调用脚本如下:
#!/bin/bash
DIR=$(cd `dirname $0`;pwd)
$DIR/certbot-auto renew --manual-auth-hook $DIR/dnspod.sh --post-hook "/etc/init.d/nginx reload"
auth hook 脚本基于参考资料1 修改,期望支持多 TXT 记录的情况,暂未验证,稀里糊涂就更新成功了,只能等 3 个月后再看有没有 bug 了(2020.8.13已成功验证)。
#!/bin/bash
#
# Create: certbot certonly --manual --preferred-challenges dns-01 --email mail@domain.com -d laravel.run -d *.laravel.run --server https://acme-v02.api.letsencrypt.org/directory --manual-auth-hook /path/to/certbot-auth-dnspod.sh
# Renew: certbot renew --manual-auth-hook /path/to/certbot-auth-dnspod.sh
#
# https://www.dnspod.cn/console/user/security
API_TOKEN="id,token"
USER_AGENT="AnDNS/1.0.0 (admin@annhe.net)"
DOMAIN=$(expr match "$CERTBOT_DOMAIN" '.*\.\(.*\..*\)')
[ -z "$DOMAIN" ] && DOMAIN="$CERTBOT_DOMAIN"
if [ -z "$API_TOKEN" ]; then
[ -f /etc/dnspod_token_$DOMAIN ] && API_TOKEN=$(cat /etc/dnspod_token_$DOMAIN)
fi
if [ -z "$API_TOKEN" ]; then
[ -f /etc/dnspod_token ] && API_TOKEN=$(cat /etc/dnspod_token)
fi
if [ -z "$API_TOKEN" ]; then
API_TOKEN="$DNSPOD_TOKEN"
fi
PARAMS="login_token=$API_TOKEN&format=json"
echo "\
CERTBOT_DOMAIN: $CERTBOT_DOMAIN
DOMAIN: $DOMAIN
VALIDATION: $CERTBOT_VALIDATION"
#echo "PARAMS: $PARAMS"
if [ -f /tmp/CERTBOT_$CERTBOT_DOMAIN/VALIDATION ]; then
VALIDATION_PRE=$(cat /tmp/CERTBOT_$CERTBOT_DOMAIN/VALIDATION)
if [ "$CERTBOT_VALIDATION" = "$VALIDATION_PRE" ]; then
echo "Same Validation: $CERTBOT_VALIDATION"
exit
fi
fi
RECORDS=$(curl -s -X POST "https://dnsapi.cn/Record.List" \
-H "User-Agent: $USER_AGENT" \
-d "$PARAMS&domain=$CERTBOT_DOMAIN&keyword=_acme-challenge" | jq .records[].id |tr -d '"' |tr '\n' ' ')
echo "\
RECORDS: $RECORDS"
sleep 3
function SelectRecord() {
# param 1: records
TMPFILE=/tmp/CERTBOT_WILDCARD_${CERTBOT_DOMAIN}_RECORD_ID
IDS="$1"
if [ -f $TMPFILE ];then
PRE_ID=`cat $TMPFILE`
fi
SELECTED=`echo $IDS |awk '{print $1}'`
if [ "$PRE_ID"x == "$SELECTED"x ];then
SELECTED=`echo $IDS |awk '{print $NF}'`
fi
echo $SELECTED > $TMPFILE
echo $SELECTED
}
if [ -n "$RECORDS" ]; then
# 通配符域名,两个 TXT记录的情况(*.domain.com, domain.com)
RECORD_ID=`SelectRecord "$RECORDS"`
RECORD_ID=$(curl -s -X POST "https://dnsapi.cn/Record.Modify" \
-H "User-Agent: $USER_AGENT" \
-d "$PARAMS&domain=$CERTBOT_DOMAIN&sub_domain=_acme-challenge&record_type=TXT&value=$CERTBOT_VALIDATION&record_line=默认&record_id=$RECORD_ID" |jq .records[0].id |tr -d '"')
else
RECORD_ID=$(curl -s -X POST "https://dnsapi.cn/Record.Create" \
-H "User-Agent: $USER_AGENT" \
-d "$PARAMS&domain=$CERTBOT_DOMAIN&sub_domain=_acme-challenge&record_type=TXT&value=$CERTBOT_VALIDATION&record_line=默认" |jq .records[0].id |tr -d '"')
fi
echo "\
RECORD_ID: $RECORD_ID"
# Save info for cleanup
if [ ! -d /tmp/CERTBOT_$CERTBOT_DOMAIN ]; then
mkdir -m 0700 /tmp/CERTBOT_$CERTBOT_DOMAIN
fi
echo $DOMAIN > /tmp/CERTBOT_$CERTBOT_DOMAIN/DOMAIN
echo $RECORD_ID > /tmp/CERTBOT_$CERTBOT_DOMAIN/RECORD_ID
echo $CERTBOT_VALIDATION > /tmp/CERTBOT_$CERTBOT_DOMAIN/VALIDATION
# Sleep to make sure the change has time to propagate over to DNS
sleep 25
参考资料
1. https://www.wzs.cc/blog/free-ssl
2. certbot-auto: https://github.com/certbot/certbot/blob/master/certbot-auto
3. certbot-auto: https://certbot.eff.org/docs/install.html#certbot-auto
获取 `ocsp.int-x3.letsencrypt.org` 正确的解析地址,加到 `/etc/hosts` 中
```
dig @8.8.8.8 ocsp.int-x3.letsencrypt.org
```