feat: add python version of huaweicloud dns api

This commit is contained in:
jimcheung 2020-02-08 00:02:32 +08:00
parent c96b4bc5c0
commit 379ba69430
4 changed files with 288 additions and 38 deletions

View File

@ -7,10 +7,10 @@
不管是申请还是续期,只要是通配符证书,只能采用 dns-01 的方式校验申请者的域名,也就是说 certbot 操作者必须手动添加 DNS TXT 记录。 不管是申请还是续期,只要是通配符证书,只能采用 dns-01 的方式校验申请者的域名,也就是说 certbot 操作者必须手动添加 DNS TXT 记录。
如果你编写一个 Cron (比如 1 1 */1 * * root certbot-auto renew),自动 renew 通配符证书,此时 Cron 无法自动添加 TXT 记录,这样 renew 操作就会失败,如何解决? 如果你编写一个 Cron (比如 1 1 */1 * * root certbot-auto renew),自动 renew 通配符证书,此时 Cron 无法自动添加 TXT 记录,这样 renew 操作就会失败,如何解决?
certbot 提供了一个 hook可以编写一个 Shell 脚本,让脚本调用 DNS 服务商的 API 接口,动态添加 TXT 记录,这样就无需人工干预了。 certbot 提供了一个 hook可以编写一个 Shell 脚本,让脚本调用 DNS 服务商的 API 接口,动态添加 TXT 记录,这样就无需人工干预了。
在 certbot 官方提供的插件和 hook 例子中,都没有针对国内 DNS 服务器的样例,所以我编写了这样一个工具,目前支持阿里云 DNS、腾讯云 DNS、GoDaddycertbot 官方没有对应的插件)。 在 certbot 官方提供的插件和 hook 例子中,都没有针对国内 DNS 服务器的样例,所以我编写了这样一个工具,目前支持阿里云 DNS、腾讯云 DNS、GoDaddycertbot 官方没有对应的插件)。
### 自动申请通配符证书 ### 自动申请通配符证书
@ -21,7 +21,7 @@ $ git clone https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-a
$ cd certbot-letencrypt-wildcardcertificates-alydns-au $ cd certbot-letencrypt-wildcardcertificates-alydns-au
$ chmod 0777 au.sh $ chmod 0777 au.sh
``` ```
2配置 2配置
@ -32,10 +32,11 @@ $ chmod 0777 au.sh
2DNS API 密钥: 2DNS API 密钥:
这个 API 密钥什么意思呢?由于需要通过 API 操作阿里云 DNS 腾讯云 DNS 的记录,所以需要去域名服务商哪儿获取 API 密钥,然后配置在 au.sh 文件中: 这个 API 密钥什么意思呢?由于需要通过 API 操作阿里云 DNS, 腾讯云 DNS 的记录,所以需要去域名服务商哪儿获取 API 密钥,然后配置在 au.sh 文件中:
- ALY_KEY 和 ALY_TOKEN阿里云 [API key 和 Secrec 官方申请文档](https://help.aliyun.com/knowledge_detail/38738.html)。 - 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)。 - TXY_KEY 和 TXY_TOKEN腾讯云 [API 密钥官方申请文档](https://console.cloud.tencent.com/cam/capi)。
- HWY_KEY 和 HWY_TOKEN: 华为云 [API 密钥官方申请文档](https://support.huaweicloud.com/devg-apisign/api-sign-provide.html)
- GODADDY_KEY 和 GODADDY_TOKENGoDaddy [API 密钥官方申请文档](https://developer.godaddy.com/getstarted)。 - GODADDY_KEY 和 GODADDY_TOKENGoDaddy [API 密钥官方申请文档](https://developer.godaddy.com/getstarted)。
3选择运行环境 3选择运行环境
@ -49,16 +50,17 @@ $ chmod 0777 au.sh
- Python(支持2.7和3.7,无需任何第三方库) - Python(支持2.7和3.7,无需任何第三方库)
- au.sh python aly add/cleanPython操作阿里云DNS增加/清空DNS。 - au.sh python aly add/cleanPython操作阿里云DNS增加/清空DNS。
- au.sh python txy add/cleanPython操作腾讯云DNS增加/清空DNS。 - au.sh python txy add/cleanPython操作腾讯云DNS增加/清空DNS。
- au.sh python godaddy add/cleanPython操作GoDaddy DNS增加/清空DNS。 - au.sh python hwy add/cleanPython操作华为云DNS增加/清空DNS。
- au.sh python godaddy add/cleanPython操作GoDaddy DNS增加/清空DNS。
根据自己服务器环境和域名服务商选择任意一个 hook shell包含相应参数具体使用见下面。 根据自己服务器环境和域名服务商选择任意一个 hook shell包含相应参数具体使用见下面。
3申请证书 3申请证书
测试是否有错误: 测试是否有错误:
``` ```
$ ./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 php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean"
``` ```
**Debug** 操作 DNS API 可能会遇到一系列问题,比如 API token 权限不足,遇到相关问题,可以查看 /var/log/certd.log。 **Debug** 操作 DNS API 可能会遇到一系列问题,比如 API token 权限不足,遇到相关问题,可以查看 /var/log/certd.log。
@ -70,12 +72,12 @@ $ ./certbot-auto certonly -d *.example.com --manual --preferred-challenges dns
- 第三个参数是固定的(--manual-auth-hook中用add--manual-clean-hook中用clean) - 第三个参数是固定的(--manual-auth-hook中用add--manual-clean-hook中用clean)
比如你要选择Python环境可以将 --manual-auth-hook 输入修改为 "/脚本目录/au.sh python aly add"--manual-cleanup-hook 输入修改为 "/脚本目录/au.sh python aly clean" 比如你要选择Python环境可以将 --manual-auth-hook 输入修改为 "/脚本目录/au.sh python aly add"--manual-cleanup-hook 输入修改为 "/脚本目录/au.sh python aly clean"
确认无误后,实际运行(去除 --dry-run 参数): 确认无误后,实际运行(去除 --dry-run 参数):
``` ```
# 实际申请 # 实际申请
$ ./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" $ ./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"
``` ```
参数解释(可以不用关心): 参数解释(可以不用关心):
@ -91,7 +93,7 @@ $ ./certbot-auto certonly -d *.example.com --manual --preferred-challenges dns
如果你想为多个域名申请通配符证书(合并在一张证书中,也叫做 **SAN 通配符证书**),直接输入多个 -d 参数即可,比如: 如果你想为多个域名申请通配符证书(合并在一张证书中,也叫做 **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 php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" $ ./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"
``` ```
### 续期证书 ### 续期证书
@ -99,7 +101,7 @@ $ ./certbot-auto certonly -d *.example.com -d *.example.org -d www.example.cn
1对机器上所有证书 renew 1对机器上所有证书 renew
``` ```
$ ./certbot-auto renew --manual --preferred-challenges dns --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" $ ./certbot-auto renew --manual --preferred-challenges dns --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean"
``` ```
2对某一张证书进行续期 2对某一张证书进行续期
@ -117,23 +119,23 @@ $ ./certbot-auto certificates
记住证书名,比如 simplehttps.com然后运行下列命令 renew 记住证书名,比如 simplehttps.com然后运行下列命令 renew
``` ```
$ ./certbot-auto renew --cert-name simplehttps.com --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean" $ ./certbot-auto renew --cert-name simplehttps.com --manual-auth-hook "/脚本目录/au.sh php aly add" --manual-cleanup-hook "/脚本目录/au.sh php aly clean"
``` ```
### 加入 crontab ### 加入 crontab
编辑文件 /etc/crontab : 编辑文件 /etc/crontab :
``` ```
#证书有效期<30天才会renew所以crontab可以配置为1天或1周 #证书有效期<30天才会renew所以crontab可以配置为1天或1周
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" 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服务比如 nginxapache的机器是同一台那么成功renew证书后可以启动对应的web 服务器运行下列crontab : 如果是certbot 机器和运行web服务比如 nginxapache的机器是同一台那么成功renew证书后可以启动对应的web 服务器运行下列crontab :
``` ```
# 注意只有成功renew证书才会重新启动nginx # 注意只有成功renew证书才会重新启动nginx
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" 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"
``` ```
@ -142,18 +144,19 @@ $ ./certbot-auto renew --cert-name simplehttps.com --manual-auth-hook "/脚本
### 贡献 ### 贡献
- 阿里云 python 版 @Duke-Wu - 阿里云 python 版 @Duke-Wu
- 腾讯云 python 版 @akgnah - 腾讯云 python 版 @akgnah
- 华为云 python 版 @jinhucheung
- GoDaddy PHP 版 wlx_1990 2019-01-11 - GoDaddy PHP 版 wlx_1990 2019-01-11
### 其他 ### 其他
- 可以关注公众号虞大胆的叽叽喳喳yudadanwx了解更多密码学&HTTPS协议知识。 - 可以关注公众号虞大胆的叽叽喳喳yudadanwx了解更多密码学&HTTPS协议知识。
- 我写了一本书[《深入浅出HTTPS从原理到实战》](https://mp.weixin.qq.com/s/80oQhzmP9BTimoReo1oMeQ)了解更多关于HTTPS方面的知识。**如果你觉得本书还可以,希望能在豆瓣做个点评,以便让更多人了解,非常感谢。豆瓣评论地址:[https://book.douban.com/subject/30250772/](https://book.douban.com/subject/30250772/)** - 我写了一本书[《深入浅出HTTPS从原理到实战》](https://mp.weixin.qq.com/s/80oQhzmP9BTimoReo1oMeQ)了解更多关于HTTPS方面的知识。**如果你觉得本书还可以,希望能在豆瓣做个点评,以便让更多人了解,非常感谢。豆瓣评论地址:[https://book.douban.com/subject/30250772/](https://book.douban.com/subject/30250772/)**
公众号二维码: 公众号二维码:
![公众号虞大胆的叽叽喳喳yudadanwx](https://notes.newyingyong.cn/static/image/wxgzh/qrcode_258.jpg) ![公众号虞大胆的叽叽喳喳yudadanwx](https://notes.newyingyong.cn/static/image/wxgzh/qrcode_258.jpg)
《深入浅出HTTPS从原理到实战》二维码 《深入浅出HTTPS从原理到实战》二维码
![深入浅出HTTPS从原理到实战](https://notes.newyingyong.cn/static/image/httpsbook/httpsbook-small-jd.jpg) ![深入浅出HTTPS从原理到实战](https://notes.newyingyong.cn/static/image/httpsbook/httpsbook-small-jd.jpg)

47
au.sh
View File

@ -4,9 +4,9 @@
###### 根据自己的情况修改 Begin ############## ###### 根据自己的情况修改 Begin ##############
#PHP 命令行路径,如果有需要可以修改 #PHP 命令行路径,如果有需要可以修改
phpcmd="/usr/bin/php" phpcmd="/usr/bin/php"
#Python 命令行路径,如果有需要可以修改 #Python 命令行路径,如果有需要可以修改
pythoncmd="/usr/bin/python" pythoncmd="/usr/bin/python"
#填写阿里云的AccessKey ID及AccessKey Secret #填写阿里云的AccessKey ID及AccessKey Secret
@ -19,6 +19,11 @@ ALY_TOKEN=""
TXY_KEY="" TXY_KEY=""
TXY_TOKEN="" TXY_TOKEN=""
#填写华为云的 Access Key Id 及 Secret Access Key
#如何申请见https://support.huaweicloud.com/devg-apisign/api-sign-provide.html
HWY_KEY=""
HWY_TOKEN=""
#GoDaddy的SecretId及SecretKey #GoDaddy的SecretId及SecretKey
#如何申请见https://developer.godaddy.com/getstarted #如何申请见https://developer.godaddy.com/getstarted
GODADDY_KEY="" GODADDY_KEY=""
@ -32,8 +37,8 @@ PATH=$(cd `dirname $0`; pwd)
# 第一个参数:使用什么语言环境 # 第一个参数:使用什么语言环境
# 第二个参数:使用那个 DNS 的 API # 第二个参数:使用那个 DNS 的 API
# 第三个参数add or clean # 第三个参数add or clean
plang=$1 #python or php plang=$1 #python or php
pdns=$2 #aly or txy pdns=$2 #aly, txy, hwy, godaddy
paction=$3 #add or clean paction=$3 #add or clean
#内部变量 #内部变量
@ -45,43 +50,53 @@ if [[ "$paction" != "clean" ]]; then
paction="add" paction="add"
fi fi
case $plang in case $plang in
"php") "php")
cmd=$phpcmd cmd=$phpcmd
if [[ "$pdns" == "aly" ]]; then if [[ "$pdns" == "aly" ]]; then
dnsapi=$PATH"/php-version/alydns.php" dnsapi=$PATH"/php-version/alydns.php"
key=$ALY_KEY key=$ALY_KEY
token=$ALY_TOKEN token=$ALY_TOKEN
elif [[ "$pdns" == "txy" ]] ;then elif [[ "$pdns" == "txy" ]]; then
dnsapi="$PATH/php-version/txydns.php" dnsapi="$PATH/php-version/txydns.php"
key=$TXY_KEY key=$TXY_KEY
token=$TXY_TOKEN token=$TXY_TOKEN
elif [[ "$pdns" == "hwy" ]]; then
# TODO
dnsapi=""
key=$HWY_KEY
token=$HWY_TOKEN
exit
else else
dnsapi="$PATH/php-version/godaddydns.php" dnsapi="$PATH/php-version/godaddydns.php"
key=$GODADDY_KEY key=$GODADDY_KEY
token=$GODADDY_TOKEN token=$GODADDY_TOKEN
fi fi
;; ;;
"python") "python")
cmd=$pythoncmd cmd=$pythoncmd
if [[ "$pdns" == "aly" ]]; then if [[ "$pdns" == "aly" ]]; then
dnsapi=$PATH"/python-version/alydns.py" dnsapi=$PATH"/python-version/alydns.py"
key=$ALY_KEY key=$ALY_KEY
token=$ALY_TOKEN token=$ALY_TOKEN
elif [[ "$pdns" == "txy" ]] ;then elif [[ "$pdns" == "txy" ]]; then
dnsapi=$PATH"/python-version/txydns.py" dnsapi=$PATH"/python-version/txydns.py"
key=$TXY_KEY key=$TXY_KEY
token=$TXY_TOKEN token=$TXY_TOKEN
elif [[ "$pdns" == "hwy" ]]; then
dnsapi="$PATH/python-version/hwydns.py"
key=$HWY_KEY
token=$HWY_TOKEN
else else
dnsapi=$PATH"/python-version/godaddydns.py" dnsapi=$PATH"/python-version/godaddydns.py"
key=$GODADDY_KEY key=$GODADDY_KEY
token=$GODADDY_TOKEN token=$GODADDY_TOKEN
exit exit
fi fi
;; ;;
esac esac
$cmd $dnsapi $paction $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION $key $token >>"/var/log/certd.log" $cmd $dnsapi $paction $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION $key $token >>"/var/log/certd.log"

View File

@ -8,7 +8,7 @@ import random
import string import string
import json import json
import sys import sys
import os import os
pv = "python2" pv = "python2"
#python2 #python2
@ -32,8 +32,8 @@ class AliDns:
@staticmethod @staticmethod
def getDomain(domain): def getDomain(domain):
domain_parts = domain.split('.') domain_parts = domain.split('.')
if len(domain_parts) > 2: if len(domain_parts) > 2:
dirpath = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) dirpath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
domainfile = dirpath + "/domain.ini" domainfile = dirpath + "/domain.ini"

232
python-version/hwydns.py Executable file
View File

@ -0,0 +1,232 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import time
import urllib
import hashlib
import hmac
import binascii
import json
if sys.version_info < (3, 0):
import urllib2
import urllib
import urlparse
else:
import urllib.request as urllib2
import urllib.parse as urllib
class HwyDns:
__endpoint = 'dns.myhuaweicloud.com'
def __init__(self, access_key_id, secret_access_key):
self.access_key_id = access_key_id
self.secret_access_key = secret_access_key
@staticmethod
def getDomain(domain):
domain_parts = domain.split('.')
if len(domain_parts) > 2:
dirpath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
domainfile = dirpath + '/domain.ini'
domainarr = []
with open(domainfile) as f:
for line in f:
val = line.strip()
domainarr.append(val)
index = -3 if '.'.join(domain_parts[-2:]).lower() in domainarr else -2
return ('.'.join(domain_parts[:index]), '.'.join(domain_parts[index:]))
return ('', domain)
# @example hwydns.add_domain_record("example.com", "_acme-challenge", "123456", "TXT")
def add_domain_record(self, domain, rr, value, _type = 'TXT'):
zone_id = self.get_domain_zone_id(domain)
if not zone_id:
return
self.__request('POST', '/v2/zones/%s/recordsets' % (zone_id), {
'name' : '%s.%s.' % (rr, domain),
'type' : _type,
'records' : [ "\"%s\"" % (value) ]
})
# @example hwydns.delete_domain_record("example.com", "_acme-challenge", "TXT")
def delete_domain_record(self, domain, rr, _type = 'TXT'):
zone_id = self.get_domain_zone_id(domain)
recordset_id = self.get_domain_recordset_id(domain, rr, _type)
if not (zone_id and recordset_id):
return
self.__request('DELETE', '/v2/zones/%s/recordsets/%s' % (zone_id, recordset_id))
# @example hwydns.get_domain_record("example.com", "_acme-challenge", "TXT")
def get_domain_record(self, domain, rr, _type = 'TXT'):
try:
full_domain = '.'.join([rr, domain])
response = self.__request('GET', '/v2/recordsets?type=%s&name=%s' % (_type, full_domain))
content = json.loads(response)
return list(filter(lambda record: record['name'][:-1] == full_domain and record['type'] == _type, content['recordsets']))[0]
except Exception as e:
print('hwydns#get_domain_record raise: ' + str(e))
return None
# @example hwydns.get_domain("example.com")
def get_domain(self, domain):
try:
response = self.__request('GET', '/v2/zones?type=public&name=%s' % (domain))
content = json.loads(response)
return list(filter(lambda item: item['name'][:-1] == domain, content['zones']))[0]
except Exception as e:
print('hwydns#get_domain raise: ' + str(e))
return None
def get_domain_recordset_id(self, domain, rr, _type = 'TXT'):
try:
record = self.get_domain_record(domain, rr, _type)
return record['id'] if record else None
except Exception as e:
print('hwydns#get_domain_recordset_id raise: ' + str(e))
return None
def get_domain_zone_id(self, domain):
try:
record = self.get_domain(domain)
return record['id'] if record else None
except Exception as e:
print('hwydns#get_domain_zone_id raise: ' + str(e))
return None
def __request(self, method, path, payload={}):
url = 'https://%s%s?%s' % (self.__endpoint, self.__parse_path(path)[:-1], self.__parse_query_string(path))
data = json.dumps(payload).encode('utf8')
sdk_date = self.__build_sdk_date()
print('Request URL: ' + url)
print('Request Data: ' + str(data))
request = urllib2.Request(url=url, data=data)
request.get_method = lambda: method
request.add_header('Content-Type', 'application/json')
request.add_header('Host', self.__endpoint)
request.add_header('X-sdk-date', sdk_date)
request.add_header('Authorization', self.__build_authorization(request))
print('Request headers: ' + str(request.headers))
try:
f = urllib2.urlopen(request, timeout=45)
response = f.read().decode('utf-8')
print(response)
return response
except urllib2.HTTPError as e:
print('hwydns#__request raise urllib2.HTTPError: ' + str(e))
raise SystemExit(e)
def __build_sdk_date(self):
return time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
def __build_authorization(self, request):
algorithm = 'SDK-HMAC-SHA256'
canonical_request = self.__build_canonical_request(request)
canonical_request_hexencode = self.__hexencode_sha256_hash(canonical_request.encode('utf-8'))
string2sign = "%s\n%s\n%s" % (algorithm, request.get_header('X-sdk-date'), canonical_request_hexencode)
sign = self.__build_sign(string2sign)
return "%s Access=%s, SignedHeaders=%s, Signature=%s" % (algorithm, self.access_key_id, self.__parse_header_keys(request.headers), sign)
def __build_canonical_request(self, request):
return "%(method)s\n%(path)s\n%(query_string)s\n%(headers)s\n%(header_keys)s\n%(data_hexencode)s" % {
'method': request.get_method().upper(),
'path': self.__parse_path(request.get_full_url()),
'query_string': self.__parse_query_string(request.get_full_url()),
'headers': self.__parse_headers(request.headers),
'header_keys': self.__parse_header_keys(request.headers),
'data_hexencode': self.__hexencode_sha256_hash(request.data)
}
def __parse_path(self, url):
if sys.version_info < (3,0):
path = urlparse.urlsplit(url).path
else:
path = urllib.urlsplit(url).path
path = path if path else '/'
pattens = urllib.unquote(path).split('/')
tmp_paths = []
for v in pattens:
tmp_paths.append(self.__urlencode(v))
urlpath = '/'.join(tmp_paths)
if urlpath[-1] != '/':
urlpath = urlpath + '/'
return urlpath
def __parse_query_string(self, url):
if sys.version_info < (3,0):
query = urlparse.parse_qs(urlparse.urlsplit(url).query)
else:
query = urllib.parse_qs(urllib.urlsplit(url).query)
sorted_query = sorted(query.items(), key=lambda item: item[0])
sorted_query_string = ''
for (k, v) in sorted_query:
if type(v) is list:
v.sort()
for item in v:
sorted_query_string += '&' + self.__urlencode(k) + '=' + self.__urlencode(item)
else:
sorted_query_string += '&' + self.__urlencode(k) + '=' + self.__urlencode(v)
return sorted_query_string[1:]
def __parse_headers(self, headers):
format_headers = dict(((k.lower(), v.strip())) for (k, v) in headers.items())
header_string = ''
for (k, v) in sorted(format_headers.items(), key=lambda item: item[0]):
header_string += "%s:%s\n" % (k, v)
return header_string
def __parse_header_keys(self, headers):
return ';'.join(sorted(map(lambda key: key.lower(), headers.keys())))
def __build_sign(self, string2sign):
if sys.version_info < (3,0):
hm = hmac.new(self.secret_access_key, string2sign, digestmod=hashlib.sha256).digest()
else:
hm = hmac.new(self.secret_access_key.encode('utf-8'), string2sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
return binascii.hexlify(hm).decode()
def __urlencode(self, string):
return urllib.quote(str(string), safe='~')
def __hexencode_sha256_hash(self, data):
sha256 = hashlib.sha256()
sha256.update(data)
return sha256.hexdigest()
if __name__ == '__main__':
print('开始调用华为云 DNS API')
print('-'.join(sys.argv))
_, action, certbot_domain, acme_challenge, certbot_validation, api_key, api_secret = sys.argv
subdomain, main_domain = HwyDns.getDomain(certbot_domain)
if subdomain:
subdomain = acme_challenge + '.' + subdomain
else:
subdomain = acme_challenge
hwydns = HwyDns(api_key, api_secret)
if 'add' == action:
hwydns.add_domain_record(main_domain, subdomain, certbot_validation)
elif 'clean' == action:
hwydns.delete_domain_record(main_domain, subdomain)
print('结束调用华为云 DNS API')