网络红军 發表於 2022-1-13 14:56:00

使用Certbot实现阿里云泛域名证书的自动续期

<p>不得不说,支持泛域名证书的certbot真的太香了!</p>
<p>很久之前就利用certbot给网站开通了泛域名证书(利用certbot-auto生成证书 ),唯一麻烦是每隔90天就得手动执行续期。</p>
<p>主要障碍就是利用阿里云的DNS解析接口自动完成域名校验,趁着最近有时间好好研究了一下,最终效果非常固的,再也不用担心证书过期了。</p>
<p>&nbsp;</p>
<p><span style="font-size: 24px"><strong>涉及的一些资源或文档:</strong></span></p>
<p><span class="ant-page-header-heading-title" title="云解析 - OpenAPI 概览" data-spm-anchor-id="api-workbench.API Document.0.i1.517b1e0fA94mFO">1、云解析 - OpenAPI 概览</span>:https://next.api.aliyun.com/document/Alidns/2015-01-09/overview</p>
<p>2、certbot-auth-alidns:https://github.com/zphiliam/certbot-auth-alidns</p>
<p>&nbsp;</p>
<p><strong><span style="font-size: 24px">主要步骤:</span></strong></p>
<p><span style="font-size: 18px"><strong>1、获取阿里云AccessKey</strong></span></p>
<p style="padding-left: 40px">包括id和secret,成对儿使用,登录阿里云控制台,可以使用主账号的AccessKey,推荐利用RAM创建子账号的AccessKey,更安全。</p>
<p><span style="font-size: 18px"><strong>2、安装阿里云SDK(python环境)</strong></span></p>
<pre class="language-bash"><code>pip3 install aliyun-python-sdk-core
pip3 install aliyun-python-sdk-cms</code></pre>
<p style="padding-left: 40px">阿里云SDK要求python3.0以上,所以上面使用pip3安装。</p>
<p style="padding-left: 40px">第一个sdk是阿里云的核心库,第二个是RAM库</p>
<p><span style="font-size: 18px"><strong>3、准备脚本</strong></span></p>
<p style="padding-left: 40px">1)脚本存放目录:/mnt/runtime/certbot-auth-alidns</p>
<p style="padding-left: 40px">&nbsp;</p>
<p style="padding-left: 40px">2)安装certbot-auto入口文件</p>
<pre class="language-bash"><code>wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto</code></pre>
<p style="padding-left: 40px">如果之前安装过certbot-auto,直接复制过来也可以。</p>
<p style="padding-left: 40px">&nbsp;</p>
<p style="padding-left: 40px">3)创建AccessKey存放文件:config.py(将其中的id和secret换成实际值)</p>
<pre class="language-python"><code>#!/usr/bin/env python
# coding:utf-8

# 阿里云控制台 api 访问账户
ACCESS_KEY_ID = 'ACCESS_KEY_ID'
ACCESS_KEY_SECRET = 'ACCESS_KEY_SECRET'</code></pre>
<p style="padding-left: 40px">&nbsp;</p>
<p style="padding-left: 40px">4)创建域名校验逻辑文件:alidns.py</p>
<pre class="language-python"><code>#!/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('&lt;accessKeyId&gt;', '&lt;accessSecret&gt;', '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&amp;api=AddDomainRecord&amp;params={}&amp;tab=DEMO&amp;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&amp;api=DescribeDomainRecords&amp;params={}&amp;tab=DEMO&amp;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&amp;api=DeleteDomainRecord&amp;params={}&amp;tab=DEMO&amp;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"
          "{} --&gt; {}".format(acme_challenge + "." + domain, validation))

    # 添加新记录
    dns.add_domain_record(acme_challenge, validation)
</code></pre>
<p style="padding-left: 40px">&nbsp;</p>
<p style="padding-left: 40px">5)创建校验脚本文件:auth.sh</p>
<pre class="language-bash"><code>#!/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"
</code></pre>
<p style="padding-left: 40px">我本地的python版本是3.6,所以命令名称是python3.6,要根据实际情况进行修改。</p>
<p style="padding-left: 40px">为脚本添加可执行权限:</p>
<pre class="language-bash"><code>chmod +x auth.sh</code></pre>
<p style="padding-left: 40px">&nbsp;</p>
<p style="padding-left: 40px">6)创建自动续期脚本:renew.sh</p>
<pre class="language-bash"><code>#!/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"</code></pre>
<p style="padding-left: 40px">为脚本添加可执行权限:</p>
<pre class="language-bash"><code>chmod +x renew.sh</code></pre>
<p>&nbsp;</p>
<p><span style="font-size: 18px"><strong>4、手动创建新证书</strong></span></p>
<pre class="language-bash"><code>./certbot-auto certonly-d *.domain.cn--manual --preferred-challenges dns--manual-auth-hook /mnt/runtime/certbot-auth-alidns/auth.sh</code></pre>
<p>注:在使用certbot命令时,可以附加 <span style="color: rgba(35, 111, 161, 1)"><strong>--dry-run</strong></span> 参数,避免操作次数限制,等测试无误后,再进行真实操作。</p>
<p>&nbsp;</p>
<p><span style="font-size: 18px"><strong>5、手动续期所有证书(命令)</strong></span></p>
<pre class="language-bash"><code>./certbot-auto renew --manual --preferred-challenges dns --manual-auth-hook /mnt/runtime/certbot-auth-alidns/auth.sh --deploy-hook "nginx -s reload"</code></pre>
<p>&nbsp;</p>
<p><span style="font-size: 18px"><strong>6、手动续期所有证书(引用脚本)</strong></span></p>
<pre class="language-bash"><code>./renew.sh</code></pre>
<p>&nbsp;</p>
<p><span style="font-size: 18px"><strong>7、创建自动执行</strong></span></p>
<pre class="language-bash"><code>crontab -e
0 2 1 * * /mnt/runtime/certbot-auth-alidns/renew.sh</code></pre>
<p style="padding-left: 40px">以上规则是每月1日凌晨2点自动执行续期操作</p>
<p>&nbsp;</p>
<p><span style="font-size: 18px"><strong>8、相关文件打包下载</strong></span></p>
<p style="padding-left: 40px">certbot-auth-alidns.zip</p>

</div>
<div id="MySignature" role="contentinfo">
    <div class="cr-container">
        <p>
                <span class="cr-lbl">版权声明:</span>
                <span class="cr-txt">本文为博主 网无忌 原创文章,欢迎转载,但请务必标注原文链接。</span>
        </p>
        <p>
                <span class="cr-lbl">本文链接:</span>
                https://www.cnblogs.com/netWild/p/15797664.html
        </p>
</div><br><br>
来源:https://www.cnblogs.com/netWild/p/15797664.html
頁: [1]
查看完整版本: 使用Certbot实现阿里云泛域名证书的自动续期