Преглед на файлове

大大更新,操作更简单,如果运行有问题,欢迎@我,或者使用老版本(v2018-12 tag)

ywdblog преди 6 години
родител
ревизия
f20385e95e
променени са 7 файла, в които са добавени 269 реда и са изтрити 111 реда
  1. 223 0
      :
  2. 41 25
      README.md
  3. 5 5
      au.sh
  4. 0 14
      python-version/1.py
  5. 0 16
      python-version/alydns.py
  6. 0 28
      python-version/au.sh
  7. 0 23
      python-version/qcloud-au.sh

+ 223 - 0
:

@@ -0,0 +1,223 @@
+# coding:utf-8
+
+import base64
+import urllib
+import hmac
+import pytz
+import datetime
+import random
+import string
+import json
+import sys
+
+pv = "python2"
+#python2
+if sys.version_info[0] < 3:
+    from urllib import quote
+    from urllib import urlencode
+    import hashlib
+else:
+    from urllib.parse import quote
+    from urllib.parse import urlencode 
+    from urllib import request
+    pv = "python3"
+
+class AliDns:
+    def __init__(self, access_key_id, access_key_secret, domain_name):
+        self.access_key_id = access_key_id
+        self.access_key_secret = access_key_secret
+        self.domain_name = domain_name
+
+    @staticmethod
+    def getDomain(domain):
+        domain_parts = domain.split('.')
+        if len(domain_parts) > 2:
+            rootdomain='.'.join(domain_parts[-(2 if domain_parts[-1] in {"co.jp","com.tw","net","com","com.cn","org","cn","gov","net.cn","io","top","me","int","edu","link"} else 3):])
+            selfdomain=domain.split(rootdomain)[0]
+            return (selfdomain[0:len(selfdomain)-1],rootdomain)
+        return ("",domain)
+
+    @staticmethod
+    def generate_random_str(length=14):
+        """
+        生成一个指定长度(默认14位)的随机数值,其中
+        string.digits = "0123456789'
+        """
+        str_list = [random.choice(string.digits) for i in range(length)]
+        random_str = ''.join(str_list)
+        return random_str
+
+    @staticmethod
+    def percent_encode(str):
+        res = quote(str.encode('utf-8'), '')
+        res = res.replace('+', '%20')
+        res = res.replace('*', '%2A')
+        res = res.replace('%7E', '~')
+        return res
+
+    @staticmethod
+    def utc_time():
+        """
+        请求的时间戳。日期格式按照ISO8601标准表示,
+        并需要使用UTC时间。格式为YYYY-MM-DDThh:mm:ssZ
+        例如,2015-01-09T12:00:00Z(为UTC时间2015年1月9日12点0分0秒)
+        :return:
+        """
+        utc_tz = pytz.timezone('UTC')
+        time = datetime.datetime.now(tz=utc_tz).strftime('%Y-%m-%dT%H:%M:%SZ')
+        return time
+
+    @staticmethod
+    def sign_string(url_param):
+        percent_encode = AliDns.percent_encode
+        sorted_url_param = sorted(url_param.items(), key=lambda x: x[0])
+        can_string = ''
+        for k, v in sorted_url_param:
+            can_string += '&' + percent_encode(k) + '=' + percent_encode(v)
+        string_to_sign = 'GET' + '&' + '%2F' + '&' + percent_encode(can_string[1:])
+        return string_to_sign
+
+    @staticmethod
+    def access_url(url):
+        if pv == "python2" :
+            f = urllib.urlopen(url)
+            result = f.read().decode('utf-8')
+            #print(result)
+            return json.loads(result)
+        else :
+            req = request.Request(url) 
+            with request.urlopen(req) as f:
+                result = f.read().decode('utf-8')
+                #print(result)
+                return json.loads(result)
+
+    def visit_url(self, action_param):
+        common_param = {
+            'Format': 'json',
+            'Version': '2015-01-09',
+            'AccessKeyId': self.access_key_id,
+            'SignatureMethod': 'HMAC-SHA1',
+            'Timestamp': AliDns.utc_time(),
+            'SignatureVersion': '1.0',
+            'SignatureNonce': AliDns.generate_random_str(),
+            'DomainName': self.domain_name,
+        }
+        url_param = dict(common_param, **action_param)
+        string_to_sign = AliDns.sign_string(url_param)
+
+        hash_bytes = self.access_key_secret + "&"
+        if pv == "python2":
+            h = hmac.new(hash_bytes, string_to_sign, digestmod=hashlib.sha1)
+        else :
+            h = hmac.new(hash_bytes.encode('utf-8'), string_to_sign.encode('utf-8'), digestmod='SHA1')
+        
+        signature = base64.encodestring(h.digest()).strip()
+        url_param.setdefault('Signature', signature)
+        url = 'https://alidns.aliyuncs.com/?' + urlencode(url_param)
+        #print(url)
+        return AliDns.access_url(url)
+
+    # 显示所有
+    def describe_domain_records(self):
+        """
+        最多只能查询此域名的 500条解析记录
+        PageNumber  当前页数,起始值为1,默认为1
+        PageSize  分页查询时设置的每页行数,最大值500,默认为20
+        :return:
+        """
+        action_param = dict(
+            Action='DescribeDomainRecords',
+            PageNumber='1',
+            PageSize='500',
+        )
+        result = self.visit_url(action_param)
+        return result
+
+    # 增加解析
+    def add_domain_record(self, type, rr, value):
+        action_param = dict(
+            Action='AddDomainRecord',
+            RR=rr,
+            Type=type,
+            Value=value,
+        )
+        result = self.visit_url(action_param)
+        return result
+
+    # 修改解析
+    def update_domain_record(self, id, type, rr, value):
+        action_param = dict(
+            Action="UpdateDomainRecord",
+            RecordId=id,
+            RR=rr,
+            Type=type,
+            Value=value,
+        )
+        result = self.visit_url(action_param)
+        return result
+
+    # 删除解析
+    def delete_domain_record(self, id):
+        action_param = dict(
+            Action="DeleteDomainRecord",
+            RecordId=id,
+        )
+        result = self.visit_url(action_param)
+        return result
+
+
+if __name__ == '__main__':
+    #filename,ACCESS_KEY_ID, ACCESS_KEY_SECRET = sys.argv
+    #domain = AliDns(ACCESS_KEY_ID, ACCESS_KEY_SECRET, 'simplehttps.com')
+    #domain.describe_domain_records()
+    #增加记录
+    #print(domain.add_domain_record("TXT", "test", "test"))
+
+   
+    # 修改解析
+    #domain.update_domain_record('4011918010876928', 'TXT', 'test2', 'text2')
+    # 删除解析记录
+    # data = domain.describe_domain_records()
+    # record_list = data["DomainRecords"]["Record"]
+    # for item in record_list:
+    #	if 'test' in item['RR']:
+    #		domain.delete_domain_record(item['RecordId'])
+
+   
+	# 第一个参数是 action,代表 (add/clean) 
+	# 第二个参数是域名 
+	# 第三个参数是主机名(第三个参数+第二个参数组合起来就是要添加的 TXT 记录)
+	# 第四个参数是 TXT 记录值
+	# 第五个参数是 APPKEY
+	# 第六个参数是 APPTOKEN
+    #sys.exit(0)
+    print(sys.argv)
+    file_name, cmd ,certbot_domain, acme_challenge, certbot_validation,ACCESS_KEY_ID, ACCESS_KEY_SECRET = sys.argv
+   
+    certbot_domain=AliDns.getDomain(certbot_domain)
+    # print (certbot_domain)
+    if ertbot_domain[0]=="":
+            selfdomain =  acme_challenge
+    else:
+            selfdomain = acme_challenge + "." + certbot_domain[0]
+
+    print(selfdomain)    
+
+    domain = AliDns(ACCESS_KEY_ID, ACCESS_KEY_SECRET, certbot_domain[1])
+  
+    if cmd == "add":
+        result = (domain.add_domain_record("TXT",selfdomain, certbot_validation))
+        
+	if "Code" in result:
+		print ("aly dns 域名增加失败-"+str(result["Code"]) + ":" + str(result["Message"]))
+    elif cmd == "clean":
+        data = domain.describe_domain_records()
+        print (data)  
+	record_list = data["DomainRecords"]["Record"]
+	sys.exit(0)
+        if record_list:
+            for item in record_list:
+                if (item['RR'] == selfdomain):
+                    domain.delete_domain_record(item['RecordId'])
+
+

+ 41 - 25
README.md

@@ -19,39 +19,46 @@ $ git clone https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-a
 
 $ cd certbot-letencrypt-wildcardcertificates-alydns-au
 
-$ chmod 0777 au.sh autxy.sh augodaddy.sh python-version/au.sh
+$ chmod 0777 au.sh 
 ```
 
 2:配置
 
-目前该工具支持四种运行环境和场景
+(1)DNS API 密钥
 
-- au.sh:操作阿里云 DNS hook shell(PHP 环境)。
-- autxy.sh:操作腾讯云 DNS hook shell(PHP 环境)。
-- python-version/au.py:操作阿里云 DNS hook shell(兼容**Python 2/3**),感谢 @Duke-Wu 的 PR。
-- augodaddy.sh:操作 GoDaddy DNS hook shell(PHP 环境),感谢 wlx_1990(微信号)的 PR。【2019-01-11】
+这个 API 密钥什么意思呢?由于需要通过 API 操作阿里云 DNS 或腾讯云 DNS 的记录,所以需要去域名服务商哪儿获取 API 密钥。,然后配置在 au.sh 文件中
 
-这四种运行环境和场景什么意思呢?就是可根据自己服务器环境和域名服务商选择任意一个 hook shell(操作的时候任选其一即可)。
+- ALY_KEY 和 ALY_TOKEN:阿里云 [API key 和 Secrec 官方申请文档](https://help.aliyun.com/knowledge_detail/38738.html)。
+- TXY_KEY 和 TXY_TOKEN:腾讯云 [API 密钥官方申请文档](https://console.cloud.tencent.com/cam/capi)。
 
-DNS API 密钥
+(2)目前该工具支持四种运行环境和场景,通过 hook 文件和参数来调用
 
-- alydns.php,修改 accessKeyId、accessSecrec 变量,阿里云 [API key 和 Secrec 官方申请文档](https://help.aliyun.com/knowledge_detail/38738.html)。
-- txydns.php,修改 txyaccessKeyId、txyaccessSecrec 变量,腾讯云 [API 密钥官方申请文档](https://console.cloud.tencent.com/cam/capi)。
-- python-version/alydns.py,修改 ACCESS_KEY_ID、ACCESS_KEY_SECRET,阿里云 [API key 和 Secrec 官方申请文档](https://help.aliyun.com/knowledge_detail/38738.html)。
-- godaddydns.php,修改 accessKeyId、accessSecrec 变量,GoDaddy [API 密钥官方申请文档](https://developer.godaddy.com/keys)。
+- PHP
+	- au.sh php aly add/clean:PHP 表示选择PHP命令行,操作阿里云DNS,增加/清空指定 DNS TXT 记录。
+	- au.sh php txy add/clean:PHP 表示选择PHP命令行,操作腾讯云DNS,增加/清空指定 DNS TXT 记录。
+- Python
+	- au.sh python aly add/clean:PHP 表示选择PHP命令行,操作阿里云DNS,增加/清空指定 DNS TXT 记录。
+	- au.sh python txy add/clean:PHP 表示选择PHP命令行,操作腾讯云DNS,增加/清空指定 DNS TXT 记录。
 
-这个 API 密钥什么意思呢?由于需要通过 API 操作阿里云 DNS 或腾讯云 DNS 的记录,所以需要去域名服务商哪儿获取 API 密钥。
+这四种运行环境和场景什么意思呢?就是可根据自己服务器环境和域名服务商选择任意一个 hook shell(包含相应参数)
 
 3:申请证书
 
-**特别说明:** --manual-auth-hook 指定的 hook 文件四个任选其一(au.sh、autxy.sh、augodaddy.sh、python-version/au.sh),其他操作完全相同
+**特别说明:** --manual-auth-hook 根据运行环境(语言,DNS产商,add/clean)指定 hook 文件和操作参数
 
+测试是否有错误:
+
+```
+$ ./certbot-auto certonly  -d *.example.com --manual --preferred-challenges dns --dry-run  --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" 
 ```
-# 测试是否有错误
-$ ./certbot-auto certonly  -d *.example.com --manual --preferred-challenges dns --dry-run  --manual-auth-hook /脚本目录/au.sh(autxy.sh 或 python-version/au.sh,下面统一以 au.sh 介绍)
 
+**如果你要选择Python环境**,可以将 --manual-auth-hook 和 --manual-cleanup-hook 的输入修改为 "/脚本目录/au.sh python aly clean"
+
+确认无误后,实际运行(去除 --dry-run 参数):
+
+``` 
 # 实际申请
-$ ./certbot-auto certonly  -d *.example.com --manual --preferred-challenges dns  --manual-auth-hook /脚本目录/au.sh    
+$ ./certbot-auto certonly  -d *.example.com --manual --preferred-challenges dns --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean"   
 ```
 
 参数解释(可以不用关心):
@@ -62,19 +69,22 @@ $ ./certbot-auto certonly  -d *.example.com --manual --preferred-challenges dns
 - --dry-run:在实际申请/更新证书前进行测试,强烈推荐
 - -d:表示需要为那个域名申请证书,可以有多个。
 - --manual-auth-hook:在执行命令的时候调用一个 hook 文件
+- --manual-cleanup-hook:清除 DNS 添加的 TXT 值
 
 如果你想为多个域名申请通配符证书(合并在一张证书中,也叫做 **SAN 通配符证书**),直接输入多个 -d 参数即可,比如:
 
 ```
-$ ./certbot-auto certonly  -d *.example.com -d *.example.org -d www.example.cn  --manual --preferred-challenges dns  --dry-run --manual-auth-hook /脚本目录/au.sh
+$ ./certbot-auto certonly  -d *.example.com -d *.example.org -d www.example.cn  --manual --preferred-challenges dns  --dry-run --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" 
 ```
 
 ### 续期证书
 
+**注意,根据自己的环境和DNS厂商选择 hook 和参数**
+
 1:对机器上所有证书 renew
 
 ```
-$ ./certbot-auto renew  --manual --preferred-challenges dns  --manual-auth-hook /脚本目录/au.sh   
+$ ./certbot-auto renew  --manual --preferred-challenges dns --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean"  
 ```
 
 2:对某一张证书进行续期
@@ -92,7 +102,7 @@ $ ./certbot-auto certificates
 记住证书名,比如 simplehttps.com,然后运行下列命令 renew:
 
 ```
-$ ./certbot-auto renew --cert-name simplehttps.com  --manual-auth-hook /脚本目录/au.sh 
+$ ./certbot-auto renew --cert-name simplehttps.com  --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" 
 ```
 
 ### 加入 crontab 
@@ -101,25 +111,31 @@ $ ./certbot-auto renew --cert-name simplehttps.com  --manual-auth-hook /脚本
 
 ```
 #证书有效期<30天才会renew,所以crontab可以配置为1天或1周
-1 1 */1 * * root certbot-auto renew --manual --preferred-challenges dns  --manual-auth-hook /脚本目录/au.sh 
+1 1 */1 * * root certbot-auto renew --manual --preferred-challenges dns  --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" 
 ```
 
 如果是certbot 机器和运行web服务(比如 nginx,apache)的机器是同一台,那么成功renew证书后,可以启动对应的web 服务器,运行下列crontab :
 
 ```
 # 注意只有成功renew证书,才会重新启动nginx
-1 1 */1 * * root certbot-auto renew --manual --preferred-challenges dns -deploy-hook  "service nginx restart"  --manual-auth-hook /脚本目录/au.sh 
+1 1 */1 * * root certbot-auto renew --manual --preferred-challenges dns -deploy-hook  "service nginx restart" --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" 
 ```
 
 **注意:只有单机建议这样运行,如果要将证书同步到多台web服务器,需要有别的方案**
 
 ### ROADMAP
 
-1: 关于申请 SAN 证书
+1: ~~关于申请 SAN 证书~~
+
+~~如果你想为 example.com,*.example.com 生成一张证书,目前会有Bug,可以查看下面的 [issues]( https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-alydns-au/issues/21) 临时解决。~~
+
+2:~~代码优化~~
 
-如果你想为 example.com,*.example.com 生成一张证书,目前会有Bug,可以查看下面的 [issues]( https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-alydns-au/issues/21) 临时解决。
+- ~~入口文件修改为一个 au.sh,API Key 统一写在 au.sh 文件~~
+- ~~增加调试log,可查看 /var/log/certd.log~~
+- ~~修复 python 版本的 Bug~~
 
-2:rsync 证书
+3:rsync 证书
 
 本工具只是生成或renew证书,一旦成功后,需要将证书同步到其他服务器上(大型应用肯定有多台机器,比如nginx,apache,haproxy),应用场景不一样,所以很难有统一的方案,后面可以考虑写个 github 仓库解决下。
 

+ 5 - 5
au.sh

@@ -1,16 +1,16 @@
 #!/bin/bash
 
-#ywdblog@gmail.com 欢迎关注我的书
+#ywdblog@gmail.com 欢迎关注我的书《深入浅出HTTPS:从原理到实战》
 
 #填写腾讯云的AccessKey ID及AccessKey Secret
 #如何申请见https://help.aliyun.com/knowledge_detail/38738.html
-ALY_KEY="LTAIkLV6coSSKklZ"
-ALY_TOKEN="YEGDVHQV4oBC6AGQM9BWaHStUtNE5M"
+ALY_KEY=""
+ALY_TOKEN=""
 
 #填写腾讯云的SecretId及SecretKey
 #如何申请见https://console.cloud.tencent.com/cam/capi
-TXY_KEY="AKIDwlPr7DUpLgpZBb4tlT0MWUHtIVXOJwxm"
-TXY_TOKEN="mMkxzoTxOirrfJlFYfbS7g7792jEi5GG"
+TXY_KEY=""
+TXY_TOKEN=""
 
 #GoDaddy的SecretId及SecretKey
 #如何申请见https://developer.godaddy.com/getstarted

+ 0 - 14
python-version/1.py

@@ -1,14 +0,0 @@
-import sys 
-
-def getDomain(domain):
-    
-    domain_parts = domain.split('.')
-    if len(domain_parts) > 2:
-        rootdomain='.'.join(domain_parts[-(2 if domain_parts[-1] in {"co.jp","com.tw","net","com","com.cn","org","cn","gov","net.cn","io","top","me","int","edu","link"} else 3):])
-	selfdomain=domain.split(rootdomain)[0]
-	return (selfdomain[0:len(selfdomain)-1],rootdomain)
-    return ("",domain)
-
-
-print (getDomain(sys.argv[1]))
-

+ 0 - 16
python-version/alydns.py

@@ -184,7 +184,6 @@ if __name__ == '__main__':
     #		domain.delete_domain_record(item['RecordId'])
 
    
-<<<<<<< HEAD
 	# 第一个参数是 action,代表 (add/clean) 
 	# 第二个参数是域名 
 	# 第三个参数是主机名(第三个参数+第二个参数组合起来就是要添加的 TXT 记录)
@@ -226,18 +225,3 @@ if __name__ == '__main__':
 
 print ("域名 API 调用结束")
 
-=======
-    #print(sys.argv)
-    file_name, cmd, certbot_domain, acme_challenge, certbot_validation = sys.argv
-
-    domain = AliDns(ACCESS_KEY_ID, ACCESS_KEY_SECRET, certbot_domain)
-    if cmd == "add":
-        domain.add_domain_record("TXT", acme_challenge, certbot_validation)
-    elif cmd == "delete":
-        data = domain.describe_domain_records()
-        record_list = data["DomainRecords"]["Record"]
-        if record_list:
-            for item in record_list:
-                if (item['RR'] == acme_challenge and item['Value'] == certbot_validation):
-                    domain.delete_domain_record(item['RecordId'])
->>>>>>> 修改python版本的脚本,自动添加和删除DNS记录,解决同时多个子域名的问题

+ 0 - 28
python-version/au.sh

@@ -1,28 +0,0 @@
-#!/bin/bash
-
-
-path=$(cd `dirname $0`; pwd)
-cmd=$1
-
-echo $path"/alydns.py"
-
-# 调用 python 脚本,自动设置 DNS TXT 记录。
-# 第一个参数:命令 add 和 delete
-# 第二个参数:需要为那个域名设置 DNS 记录
-# 第三个参数: 需要为具体那个 RR 设置
-# 第四个参数: letsencrypt 动态传递的 RR 值
-
-echo $cmd $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION
-
-if [[ -n "$cmd" ]]; then
-    # 根据自己机器的python环境选择python版本
-    python $path"/alydns.py" $cmd $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION >"/var/log/certdebug.log"
-
-    if [[ "$cmd" == "add" ]]; then
-        # DNS TXT 记录刷新时间
-        /bin/sleep 10
-    fi
-fi
-
-echo "END"
-###

+ 0 - 23
python-version/qcloud-au.sh

@@ -1,23 +0,0 @@
-#!/bin/bash
-
-path=$(cd `dirname $0`; pwd)
-qcloud="${path}/qcloud-dns.py"
-option=$1
-
-# 调用 Python 脚本,自动设置 DNS TXT 记录。
-# 第一个参数:命令 add 或 delete
-# 第二个参数:需要为那个域名设置 DNS 记录
-# 第三个参数: 需要为具体那个 RR 设置
-# 第四个参数: letsencrypt 动态传递的 RR 值
-
-echo $qcloud $option $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION
-
-if [[ -n "$option" ]]; then
-    # 根据自己机器的环境选择 Python 版本
-    python3 $qcloud $option $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION > "/var/log/certdebug.log"
-
-    if [[ "$option" == "add" ]]; then
-        # DNS TXT 记录刷新时间
-        /bin/sleep 10
-    fi
-fi