不得不說,支持泛域名證書的certbot真的太香了!
很久之前就利用certbot給網站開通了泛域名證書(利用certbot-auto生成證書 ),唯一麻煩是每隔90天就得手動執行續期。
主要障礙就是利用阿里雲的DNS解析接口自動完成域名校驗,趁着最近有時間好好研究了一下,最終效果非常固的,再也不用擔心證書過期了。
涉及的一些資源或文檔:
1、雲解析 - OpenAPI 概覽:https://next.api.aliyun.com/document/Alidns/2015-01-09/overview
2、certbot-auth-alidns:https://github.com/zphiliam/certbot-auth-alidns
主要步驟:
1、獲取阿里雲AccessKey
包括id和secret,成對兒使用,登錄阿里雲控制台,可以使用主賬號的AccessKey,推薦利用RAM創建子賬號的AccessKey,更安全。
2、安裝阿里雲SDK(python環境)
pip3 install aliyun-python-sdk-core
pip3 install aliyun-python-sdk-cms
阿里雲SDK要求python3.0以上,所以上面使用pip3安裝。
第一個sdk是阿里雲的核心庫,第二個是RAM庫
3、准備腳本
1)腳本存放目錄:/mnt/runtime/certbot-auth-alidns
2)安裝certbot-auto入口文件
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
如果之前安裝過certbot-auto,直接復制過來也可以。
3)創建AccessKey存放文件:config.py(將其中的id和secret換成實際值)
#!/usr/bin/env python
# coding:utf-8
# 阿里雲控制台 api 訪問賬戶
ACCESS_KEY_ID = 'ACCESS_KEY_ID'
ACCESS_KEY_SECRET = 'ACCESS_KEY_SECRET'
4)創建域名校驗邏輯文件:alidns.py
#!/usr/bin/env python
# coding=utf-8
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
from config import *
import json
from sys import argv
# client = AcsClient('<accessKeyId>', '<accessSecret>', 'cn-hangzhou')
client = AcsClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET, 'nothing')
class AliDNS(object):
def __init__(self, domain_name=''):
self.domain_name = domain_name
def add_domain_record(self, rr, value, type='TXT'):
# https://help.aliyun.com/document_detail/29772.html?spm=a2c4g.11186623.6.647.5fce1ba8XGwW3b
# https://api.aliyun.com/?spm=a2c1g.8271268.10000.1.751edf252XRbqs#product=Alidns&api=AddDomainRecord¶ms={}&tab=DEMO&lang=PYTHON
request = CommonRequest()
request.set_accept_format('json')
request.set_domain('alidns.aliyuncs.com')
request.set_method('POST')
request.set_version('2015-01-09')
request.set_action_name('AddDomainRecord')
request.add_query_param('Type', type)
request.add_query_param('RR', rr)
request.add_query_param('DomainName', self.domain_name)
request.add_query_param('Value', value)
response = client.do_action(request)
print(response.decode('utf-8'))
def describe_domain_records(self):
# https://help.aliyun.com/document_detail/29751.html?spm=a2c4g.11186623.6.627.30e77d8cBvKO4T
# https://api.aliyun.com/?spm=a2c1g.8271268.10000.1.751edf252XRbqs#product=Alidns&api=DescribeDomainRecords¶ms={}&tab=DEMO&lang=PYTHON
request = CommonRequest()
request.set_accept_format('json')
request.set_domain('alidns.aliyuncs.com')
request.set_method('POST')
request.set_version('2015-01-09')
request.set_action_name('DescribeDomainRecords')
request.add_query_param('DomainName', self.domain_name)
request.add_query_param('PageNumber', '1')
request.add_query_param('PageSize', '500')
response = client.do_action(request)
rs = response.decode('utf-8')
# print(response.decode('utf-8'))
# print(str(response, encoding='utf-8'))
data = json.loads(rs)
return data
def delete_domain_record(self, record_id):
# https://help.aliyun.com/document_detail/29773.html?spm=a2c4g.11186623.6.648.4bc76e00EoOIru
# https://api.aliyun.com/?spm=a2c1g.8271268.10000.1.751edf252XRbqs#product=Alidns&api=DeleteDomainRecord¶ms={}&tab=DEMO&lang=PYTHON
request = CommonRequest()
request.set_accept_format('json')
request.set_domain('alidns.aliyuncs.com')
request.set_method('POST')
request.set_version('2015-01-09')
request.set_action_name('DeleteDomainRecord')
request.add_query_param('RecordId', record_id)
response = client.do_action(request)
# print(response.decode('utf-8'))
def update_domain_record(self, rid, rr, value, type='TXT'):
request = CommonRequest()
request.set_accept_format('json')
request.set_domain('alidns.aliyuncs.com')
request.set_method('POST')
request.set_version('2015-01-09')
request.set_action_name('UpdateDomainRecord')
request.add_query_param('RecordId', rid)
request.add_query_param('RR', rr)
request.add_query_param('Type', type)
request.add_query_param('Value', value)
response = client.do_action(request)
# print(str(response, encoding='utf-8'))
if __name__ == '__main__':
# import time
# domain = 'iot-c.top'
# acme_challenge = 'test.z'
# validation = str(time.time())
print(argv)
file_name, domain, acme_challenge, validation = argv
dns = AliDNS(domain)
# 列出所有解析記錄
data = dns.describe_domain_records()
# print(json.dumps(data, indent=2))
record_list = data["DomainRecords"]["Record"]
# print(len(record_list))
if record_list:
for item in record_list:
if acme_challenge == item['RR']:
# 刪除原有的記錄
dns.delete_domain_record(item['RecordId'])
print("阿里雲DNS添加 TXT 記錄:\n"
"{} --> {}".format(acme_challenge + "." + domain, validation))
# 添加新記錄
dns.add_domain_record(acme_challenge, validation)
5)創建校驗腳本文件:auth.sh
#!/usr/bin/env bash
path=$(cd `dirname $0`; pwd)
# 調用 python 腳本,自動設置 DNS TXT 記錄。
# 第一個參數:需要為那個域名設置 DNS 記錄
# 第二個參數:需要為具體那個 RR 設置
# 第三個參數: letsencrypt 動態傳遞的 value 值
echo $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION
python3.6 $path"/alidns.py" $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION
# DNS TXT 記錄刷新時間
/bin/sleep 5
echo "auth.sh end"
我本地的python版本是3.6,所以命令名稱是python3.6,要根據實際情況進行修改。
為腳本添加可執行權限:
chmod +x auth.sh
6)創建自動續期腳本:renew.sh
#!/usr/bin/env bash
# https://certbot.eff.org/docs/using.html#manual
# --pre-hook and --post-hook hooks run before and after every renewal attempt.
# If you want your hook to run only after a successful renewal,
# use --deploy-hook in a command like this.
# certbot renew --deploy-hook /path/to/deploy-hook-script
# cron 每天2點執行:
# 0 2 * * * /mnt/runtime/certbot-auth-alidns/renew.sh
path=$(cd `dirname $0`; pwd)
cd ${path}
echo ------------------
pwd
date
./certbot-auto renew --manual --preferred-challenges dns --manual-auth-hook ${path}/auth.sh --deploy-hook "nginx -s reload"
為腳本添加可執行權限:
chmod +x renew.sh
4、手動創建新證書
./certbot-auto certonly -d *.domain.cn --manual --preferred-challenges dns --manual-auth-hook /mnt/runtime/certbot-auth-alidns/auth.sh
注:在使用certbot命令時,可以附加 --dry-run 參數,避免操作次數限制,等測試無誤后,再進行真實操作。
5、手動續期所有證書(命令)
./certbot-auto renew --manual --preferred-challenges dns --manual-auth-hook /mnt/runtime/certbot-auth-alidns/auth.sh --deploy-hook "nginx -s reload"
6、手動續期所有證書(引用腳本)
./renew.sh
7、創建自動執行
crontab -e
0 2 1 * * /mnt/runtime/certbot-auth-alidns/renew.sh
以上規則是每月1日凌晨2點自動執行續期操作
8、相關文件打包下載