mirror of
https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-alydns-au.git
synced 2025-12-25 20:32:47 +00:00
feat: add python version of huaweicloud dns api
This commit is contained in:
parent
c96b4bc5c0
commit
379ba69430
41
README.md
41
README.md
|
|
@ -7,10 +7,10 @@
|
|||
不管是申请还是续期,只要是通配符证书,只能采用 dns-01 的方式校验申请者的域名,也就是说 certbot 操作者必须手动添加 DNS TXT 记录。
|
||||
|
||||
如果你编写一个 Cron (比如 1 1 */1 * * root certbot-auto renew),自动 renew 通配符证书,此时 Cron 无法自动添加 TXT 记录,这样 renew 操作就会失败,如何解决?
|
||||
|
||||
|
||||
certbot 提供了一个 hook,可以编写一个 Shell 脚本,让脚本调用 DNS 服务商的 API 接口,动态添加 TXT 记录,这样就无需人工干预了。
|
||||
|
||||
在 certbot 官方提供的插件和 hook 例子中,都没有针对国内 DNS 服务器的样例,所以我编写了这样一个工具,目前支持阿里云 DNS、腾讯云 DNS、GoDaddy(certbot 官方没有对应的插件)。
|
||||
在 certbot 官方提供的插件和 hook 例子中,都没有针对国内 DNS 服务器的样例,所以我编写了这样一个工具,目前支持阿里云 DNS、腾讯云 DNS、GoDaddy(certbot 官方没有对应的插件)。
|
||||
|
||||
### 自动申请通配符证书
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ $ git clone https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-a
|
|||
|
||||
$ cd certbot-letencrypt-wildcardcertificates-alydns-au
|
||||
|
||||
$ chmod 0777 au.sh
|
||||
$ chmod 0777 au.sh
|
||||
```
|
||||
|
||||
2:配置
|
||||
|
|
@ -32,10 +32,11 @@ $ chmod 0777 au.sh
|
|||
|
||||
(2)DNS 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)。
|
||||
- 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_TOKEN:GoDaddy [API 密钥官方申请文档](https://developer.godaddy.com/getstarted)。
|
||||
|
||||
(3)选择运行环境
|
||||
|
|
@ -49,16 +50,17 @@ $ chmod 0777 au.sh
|
|||
- Python(支持2.7和3.7,无需任何第三方库)
|
||||
- au.sh python aly add/clean:Python操作阿里云DNS,增加/清空DNS。
|
||||
- au.sh python txy add/clean:Python操作腾讯云DNS,增加/清空DNS。
|
||||
- au.sh python godaddy add/clean:Python操作GoDaddy DNS,增加/清空DNS。
|
||||
- au.sh python hwy add/clean:Python操作华为云DNS,增加/清空DNS。
|
||||
- au.sh python godaddy add/clean:Python操作GoDaddy DNS,增加/清空DNS。
|
||||
|
||||
根据自己服务器环境和域名服务商选择任意一个 hook shell(包含相应参数),具体使用见下面。
|
||||
|
||||
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。
|
||||
|
|
@ -70,12 +72,12 @@ $ ./certbot-auto certonly -d *.example.com --manual --preferred-challenges dns
|
|||
- 第三个参数是固定的(--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"
|
||||
|
||||
|
||||
确认无误后,实际运行(去除 --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 参数即可,比如:
|
||||
|
||||
```
|
||||
$ ./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
|
||||
|
||||
```
|
||||
$ ./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:对某一张证书进行续期
|
||||
|
|
@ -117,23 +119,23 @@ $ ./certbot-auto certificates
|
|||
记住证书名,比如 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 :
|
||||
|
||||
```
|
||||
#证书有效期<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服务(比如 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 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 版 @akgnah
|
||||
- 腾讯云 python 版 @akgnah
|
||||
- 华为云 python 版 @jinhucheung
|
||||
- GoDaddy PHP 版 wlx_1990 (2019-01-11)
|
||||
|
||||
### 其他
|
||||
|
||||
- 可以关注公众号(虞大胆的叽叽喳喳,yudadanwx),了解更多密码学&HTTPS协议知识。
|
||||
- 我写了一本书[《深入浅出HTTPS:从原理到实战》](https://mp.weixin.qq.com/s/80oQhzmP9BTimoReo1oMeQ)了解更多关于HTTPS方面的知识。**如果你觉得本书还可以,希望能在豆瓣做个点评,以便让更多人了解,非常感谢。豆瓣评论地址:[https://book.douban.com/subject/30250772/](https://book.douban.com/subject/30250772/)**
|
||||
|
||||
|
||||
公众号二维码:
|
||||
|
||||

|
||||
|
||||
《深入浅出HTTPS:从原理到实战》二维码:
|
||||
|
||||

|
||||

|
||||
|
|
|
|||
47
au.sh
47
au.sh
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
###### 根据自己的情况修改 Begin ##############
|
||||
|
||||
#PHP 命令行路径,如果有需要可以修改
|
||||
#PHP 命令行路径,如果有需要可以修改
|
||||
phpcmd="/usr/bin/php"
|
||||
#Python 命令行路径,如果有需要可以修改
|
||||
#Python 命令行路径,如果有需要可以修改
|
||||
pythoncmd="/usr/bin/python"
|
||||
|
||||
#填写阿里云的AccessKey ID及AccessKey Secret
|
||||
|
|
@ -19,6 +19,11 @@ ALY_TOKEN=""
|
|||
TXY_KEY=""
|
||||
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
|
||||
#如何申请见https://developer.godaddy.com/getstarted
|
||||
GODADDY_KEY=""
|
||||
|
|
@ -32,8 +37,8 @@ PATH=$(cd `dirname $0`; pwd)
|
|||
# 第一个参数:使用什么语言环境
|
||||
# 第二个参数:使用那个 DNS 的 API
|
||||
# 第三个参数:add or clean
|
||||
plang=$1 #python or php
|
||||
pdns=$2 #aly or txy
|
||||
plang=$1 #python or php
|
||||
pdns=$2 #aly, txy, hwy, godaddy
|
||||
paction=$3 #add or clean
|
||||
|
||||
#内部变量
|
||||
|
|
@ -45,43 +50,53 @@ if [[ "$paction" != "clean" ]]; then
|
|||
paction="add"
|
||||
fi
|
||||
|
||||
case $plang in
|
||||
"php")
|
||||
case $plang in
|
||||
"php")
|
||||
|
||||
cmd=$phpcmd
|
||||
if [[ "$pdns" == "aly" ]]; then
|
||||
dnsapi=$PATH"/php-version/alydns.php"
|
||||
key=$ALY_KEY
|
||||
if [[ "$pdns" == "aly" ]]; then
|
||||
dnsapi=$PATH"/php-version/alydns.php"
|
||||
key=$ALY_KEY
|
||||
token=$ALY_TOKEN
|
||||
elif [[ "$pdns" == "txy" ]] ;then
|
||||
elif [[ "$pdns" == "txy" ]]; then
|
||||
dnsapi="$PATH/php-version/txydns.php"
|
||||
key=$TXY_KEY
|
||||
token=$TXY_TOKEN
|
||||
elif [[ "$pdns" == "hwy" ]]; then
|
||||
# TODO
|
||||
dnsapi=""
|
||||
key=$HWY_KEY
|
||||
token=$HWY_TOKEN
|
||||
exit
|
||||
else
|
||||
dnsapi="$PATH/php-version/godaddydns.php"
|
||||
key=$GODADDY_KEY
|
||||
token=$GODADDY_TOKEN
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
"python")
|
||||
|
||||
|
||||
cmd=$pythoncmd
|
||||
if [[ "$pdns" == "aly" ]]; then
|
||||
if [[ "$pdns" == "aly" ]]; then
|
||||
dnsapi=$PATH"/python-version/alydns.py"
|
||||
key=$ALY_KEY
|
||||
token=$ALY_TOKEN
|
||||
elif [[ "$pdns" == "txy" ]] ;then
|
||||
elif [[ "$pdns" == "txy" ]]; then
|
||||
dnsapi=$PATH"/python-version/txydns.py"
|
||||
key=$TXY_KEY
|
||||
token=$TXY_TOKEN
|
||||
elif [[ "$pdns" == "hwy" ]]; then
|
||||
dnsapi="$PATH/python-version/hwydns.py"
|
||||
key=$HWY_KEY
|
||||
token=$HWY_TOKEN
|
||||
else
|
||||
dnsapi=$PATH"/python-version/godaddydns.py"
|
||||
key=$GODADDY_KEY
|
||||
token=$GODADDY_TOKEN
|
||||
exit
|
||||
fi
|
||||
;;
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
$cmd $dnsapi $paction $CERTBOT_DOMAIN "_acme-challenge" $CERTBOT_VALIDATION $key $token >>"/var/log/certd.log"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import random
|
|||
import string
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import os
|
||||
|
||||
pv = "python2"
|
||||
#python2
|
||||
|
|
@ -32,8 +32,8 @@ class AliDns:
|
|||
@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"
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
Loading…
Reference in New Issue