# Conflicts:
#	README.md
#	dnsapi/README.md
This commit is contained in:
Ne-Lexa 2019-02-08 12:31:01 +03:00
commit 412f85b665
19 changed files with 988 additions and 242 deletions

View File

@ -70,11 +70,16 @@ For all build statuses, check our [weekly build project](https://github.com/Neil
https://github.com/Neilpang/acmetest https://github.com/Neilpang/acmetest
# Supported CA
- Letsencrypt.org CA(default)
- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA)
# Supported modes # Supported modes
- Webroot mode - Webroot mode
- Standalone mode - Standalone mode
- Standalone tls-alpn mode
- Apache mode - Apache mode
- Nginx mode - Nginx mode
- DNS mode - DNS mode
@ -221,8 +226,20 @@ acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 5. Use Standalone ssl server to issue cert
# 5. Use Apache mode **(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
```bash
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com
```
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 6. Use Apache mode
**(requires you to be root/sudoer, since it is required to interact with Apache server)** **(requires you to be root/sudoer, since it is required to interact with Apache server)**
@ -242,7 +259,7 @@ We don't want to mess your apache server, don't worry.**
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 6. Use Nginx mode # 7. Use Nginx mode
**(requires you to be root/sudoer, since it is required to interact with Nginx server)** **(requires you to be root/sudoer, since it is required to interact with Nginx server)**
@ -266,7 +283,7 @@ We don't want to mess your nginx server, don't worry.**
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 7. Automatic DNS API integration # 8. Automatic DNS API integration
If your DNS provider supports API access, we can use that API to automatically issue the certs. If your DNS provider supports API access, we can use that API to automatically issue the certs.
@ -331,7 +348,10 @@ You don't have to do anything manually!
1. hosting.de (https://www.hosting.de) 1. hosting.de (https://www.hosting.de)
1. Neodigit.net API (https://www.neodigit.net) 1. Neodigit.net API (https://www.neodigit.net)
1. Exoscale.com API (https://www.exoscale.com/) 1. Exoscale.com API (https://www.exoscale.com/)
1. Internet.bs API (https://internetbs.net/) 1. PointDNS API (https://pointhq.com/)
1. Active24.cz API (https://www.active24.cz/)
1. do.de API (https://www.do.de/)
1. internetbs.net API (https://internetbs.net/)
And: And:
@ -345,7 +365,7 @@ If your DNS provider is not on the supported list above, you can write your own
For more details: [How to use DNS API](dnsapi) For more details: [How to use DNS API](dnsapi)
# 8. Use DNS manual mode: # 9. Use DNS manual mode:
See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first.
@ -381,7 +401,7 @@ Ok, it's done.
**Please use dns api mode instead.** **Please use dns api mode instead.**
# 9. Issue ECC certificates # 10. Issue ECC certificates
`Let's Encrypt` can now issue **ECDSA** certificates. `Let's Encrypt` can now issue **ECDSA** certificates.
@ -413,7 +433,7 @@ Valid values are:
# 10. Issue Wildcard certificates # 11. Issue Wildcard certificates
It's simple, just give a wildcard domain as the `-d` parameter. It's simple, just give a wildcard domain as the `-d` parameter.
@ -423,7 +443,7 @@ acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf
# 11. How to renew the certs # 12. How to renew the certs
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
@ -440,7 +460,7 @@ acme.sh --renew -d example.com --force --ecc
``` ```
# 12. How to stop cert renewal # 13. How to stop cert renewal
To stop renewal of a cert, you can execute the following to remove the cert from the renewal list: To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
@ -453,7 +473,7 @@ The cert/key file is not removed from the disk.
You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself.
# 13. How to upgrade `acme.sh` # 14. How to upgrade `acme.sh`
acme.sh is in constant development, so it's strongly recommended to use the latest code. acme.sh is in constant development, so it's strongly recommended to use the latest code.
@ -478,25 +498,25 @@ acme.sh --upgrade --auto-upgrade 0
``` ```
# 14. Issue a cert from an existing CSR # 15. Issue a cert from an existing CSR
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
# 15. Under the Hood # 16. Under the Hood
Speak ACME language using shell, directly to "Let's Encrypt". Speak ACME language using shell, directly to "Let's Encrypt".
TODO: TODO:
# 16. Acknowledgments # 17. Acknowledgments
1. Acme-tiny: https://github.com/diafygi/acme-tiny 1. Acme-tiny: https://github.com/diafygi/acme-tiny
2. ACME protocol: https://github.com/ietf-wg-acme/acme 2. ACME protocol: https://github.com/ietf-wg-acme/acme
# 17. License & Others # 18. License & Others
License is GPLv3 License is GPLv3
@ -505,7 +525,7 @@ Please Star and Fork me.
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
# 18. Donate # 19. Donate
Your donation makes **acme.sh** better: Your donation makes **acme.sh** better:
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)

107
acme.sh
View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.8.0 VER=2.8.1
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
@ -35,19 +35,16 @@ _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org"
VTYPE_HTTP="http-01" VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01" VTYPE_DNS="dns-01"
VTYPE_TLS="tls-sni-01"
VTYPE_TLS2="tls-sni-02"
VTYPE_ALPN="tls-alpn-01" VTYPE_ALPN="tls-alpn-01"
LOCAL_ANY_ADDRESS="0.0.0.0" LOCAL_ANY_ADDRESS="0.0.0.0"
MAX_RENEW=60 DEFAULT_RENEW=60
DEFAULT_DNS_SLEEP=120 DEFAULT_DNS_SLEEP=120
NO_VALUE="no" NO_VALUE="no"
W_TLS="tls"
W_DNS="dns" W_DNS="dns"
W_ALPN="alpn" W_ALPN="alpn"
DNS_ALIAS_PREFIX="=" DNS_ALIAS_PREFIX="="
@ -1090,7 +1087,7 @@ _createcsr() {
fi fi
if [ "$acmeValidationv1" ]; then if [ "$acmeValidationv1" ]; then
printf "\n1.3.6.1.5.5.7.1.30.1=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}" printf "\n1.3.6.1.5.5.7.1.31=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}"
fi fi
_csr_cn="$(_idn "$domain")" _csr_cn="$(_idn "$domain")"
@ -1875,11 +1872,7 @@ _send_signed_request() {
sig="$(printf "%s" "$_sig_t" | _url_replace)" sig="$(printf "%s" "$_sig_t" | _url_replace)"
_debug3 sig "$sig" _debug3 sig "$sig"
if [ "$ACME_VERSION" = "2" ]; then body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
else
body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
fi
_debug3 body "$body" _debug3 body "$body"
response="$(_post "$body" "$url" "$needbase64" "POST" "$__request_conent_type")" response="$(_post "$body" "$url" "$needbase64" "POST" "$__request_conent_type")"
@ -2926,7 +2919,10 @@ _clearup() {
_clearupdns() { _clearupdns() {
_debug "_clearupdns" _debug "_clearupdns"
if [ "$dnsadded" != 1 ] || [ -z "$vlist" ]; then _debug "dnsadded" "$dnsadded"
_debug "vlist" "$vlist"
#dnsadded is "0" or "1" means dns-01 method was used for at least one domain
if [ -z "$dnsadded" ] || [ -z "$vlist" ]; then
_debug "skip dns." _debug "skip dns."
return return
fi fi
@ -3082,8 +3078,8 @@ _on_before_issue() {
_savedomainconf "Le_HTTPPort" "$Le_HTTPPort" _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
fi fi
_checkport="$Le_HTTPPort" _checkport="$Le_HTTPPort"
elif [ "$_currentRoot" = "$W_TLS" ] || [ "$_currentRoot" = "$W_ALPN" ]; then elif [ "$_currentRoot" = "$W_ALPN" ]; then
_info "Standalone tls/alpn mode." _info "Standalone alpn mode."
if [ -z "$Le_TLSPort" ]; then if [ -z "$Le_TLSPort" ]; then
Le_TLSPort=443 Le_TLSPort=443
else else
@ -3443,15 +3439,17 @@ __get_domain_new_authz() {
#uri keyAuthorization #uri keyAuthorization
__trigger_validation() { __trigger_validation() {
_debug2 "tigger domain validation." _debug2 "Trigger domain validation."
_t_url="$1" _t_url="$1"
_debug2 _t_url "$_t_url" _debug2 _t_url "$_t_url"
_t_key_authz="$2" _t_key_authz="$2"
_debug2 _t_key_authz "$_t_key_authz" _debug2 _t_key_authz "$_t_key_authz"
_t_vtype="$3"
_debug2 _t_vtype "$_t_vtype"
if [ "$ACME_VERSION" = "2" ]; then if [ "$ACME_VERSION" = "2" ]; then
_send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}"
else else
_send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}"
fi fi
} }
@ -3654,7 +3652,7 @@ issue() {
_authorizations_map="" _authorizations_map=""
for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
_debug2 "_authz_url" "$_authz_url" _debug2 "_authz_url" "$_authz_url"
if ! response="$(_get "$_authz_url")"; then if ! _send_signed_request "$_authz_url"; then
_err "get to authz error." _err "get to authz error."
_err "_authorizations_seg" "$_authorizations_seg" _err "_authorizations_seg" "$_authorizations_seg"
_err "_authz_url" "$_authz_url" _err "_authz_url" "$_authz_url"
@ -3701,14 +3699,6 @@ $_authorizations_map"
vtype="$VTYPE_DNS" vtype="$VTYPE_DNS"
fi fi
if [ "$_currentRoot" = "$W_TLS" ]; then
if [ "$ACME_VERSION" = "2" ]; then
vtype="$VTYPE_TLS2"
else
vtype="$VTYPE_TLS"
fi
fi
if [ "$_currentRoot" = "$W_ALPN" ]; then if [ "$_currentRoot" = "$W_ALPN" ]; then
vtype="$VTYPE_ALPN" vtype="$VTYPE_ALPN"
fi fi
@ -3861,8 +3851,8 @@ $_authorizations_map"
) )
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_clearup
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook" "$vlist"
_clearup
return 1 return 1
fi fi
dnsadded='1' dnsadded='1'
@ -3873,8 +3863,8 @@ $_authorizations_map"
_savedomainconf "Le_Vlist" "$vlist" _savedomainconf "Le_Vlist" "$vlist"
_debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit." _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
_err "Please add the TXT records to the domains, and re-run with --renew." _err "Please add the TXT records to the domains, and re-run with --renew."
_clearup
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
_clearup
return 1 return 1
fi fi
@ -3908,7 +3898,7 @@ $_authorizations_map"
continue continue
fi fi
_info "Verifying:$d" _info "Verifying: $d"
_debug "d" "$d" _debug "d" "$d"
_debug "keyauthorization" "$keyauthorization" _debug "keyauthorization" "$keyauthorization"
_debug "uri" "$uri" _debug "uri" "$uri"
@ -3992,40 +3982,6 @@ $_authorizations_map"
fi fi
fi fi
elif [ "$vtype" = "$VTYPE_TLS" ]; then
#create A
#_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
#_debug2 _hash_A "$_hash_A"
#_x="$(echo $_hash_A | cut -c 1-32)"
#_debug2 _x "$_x"
#_y="$(echo $_hash_A | cut -c 33-64)"
#_debug2 _y "$_y"
#_SAN_A="$_x.$_y.token.acme.invalid"
#_debug2 _SAN_A "$_SAN_A"
#create B
_hash_B="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
_debug2 _hash_B "$_hash_B"
_x="$(echo "$_hash_B" | cut -c 1-32)"
_debug2 _x "$_x"
_y="$(echo "$_hash_B" | cut -c 33-64)"
_debug2 _y "$_y"
#_SAN_B="$_x.$_y.ka.acme.invalid"
_SAN_B="$_x.$_y.acme.invalid"
_debug2 _SAN_B "$_SAN_B"
_ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math "$_ncIndex" + 1)"
if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then
_err "Start tls server error."
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
return 1
fi
elif [ "$vtype" = "$VTYPE_ALPN" ]; then elif [ "$vtype" = "$VTYPE_ALPN" ]; then
acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")" acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
_debug acmevalidationv1 "$acmevalidationv1" _debug acmevalidationv1 "$acmevalidationv1"
@ -4038,7 +3994,7 @@ $_authorizations_map"
fi fi
fi fi
if ! __trigger_validation "$uri" "$keyauthorization"; then if ! __trigger_validation "$uri" "$keyauthorization" "$vtype"; then
_err "$d:Can not get challenge: $response" _err "$d:Can not get challenge: $response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup _clearup
@ -4047,7 +4003,7 @@ $_authorizations_map"
fi fi
if [ "$code" ] && [ "$code" != '202' ]; then if [ "$code" ] && [ "$code" != '202' ]; then
if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then if [ "$code" = '200' ]; then
_debug "trigger validation code: $code" _debug "trigger validation code: $code"
else else
_err "$d:Challenge error: $response" _err "$d:Challenge error: $response"
@ -4076,7 +4032,11 @@ $_authorizations_map"
_debug "sleep 2 secs to verify" _debug "sleep 2 secs to verify"
sleep 2 sleep 2
_debug "checking" _debug "checking"
response="$(_get "$uri")" if [ "$ACME_VERSION" = "2" ]; then
_send_signed_request "$uri"
else
response="$(_get "$uri")"
fi
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "$d:Verify error:$response" _err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
@ -4152,13 +4112,16 @@ $_authorizations_map"
fi fi
Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
if ! _get "$Le_LinkCert" >"$CERT_PATH"; then _tempSignedResponse="$response"
if ! _send_signed_request "$Le_LinkCert" "" "needbase64"; then
_err "Sign failed, can not download cert:$Le_LinkCert." _err "Sign failed, can not download cert:$Le_LinkCert."
_err "$response" _err "$response"
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
echo "$response" | _dbase64 "multiline" >"$CERT_PATH"
if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then
_debug "Found cert chain" _debug "Found cert chain"
cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH" cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH"
@ -4168,6 +4131,7 @@ $_authorizations_map"
_end_n="$(_math $_end_n + 1)" _end_n="$(_math $_end_n + 1)"
sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH" sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH"
fi fi
response="$_tempSignedResponse"
else else
if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
_err "Sign failed. $response" _err "Sign failed. $response"
@ -4238,7 +4202,8 @@ $_authorizations_map"
while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
_debug _link_issuer_retry "$_link_issuer_retry" _debug _link_issuer_retry "$_link_issuer_retry"
if [ "$ACME_VERSION" = "2" ]; then if [ "$ACME_VERSION" = "2" ]; then
if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then if _send_signed_request "$Le_LinkIssuer"; then
echo "$response" >"$CA_CERT_PATH"
break break
fi fi
else else
@ -4274,8 +4239,8 @@ $_authorizations_map"
Le_CertCreateTimeStr=$(date -u) Le_CertCreateTimeStr=$(date -u)
_savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr" _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ]; then if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then
Le_RenewalDays="$MAX_RENEW" Le_RenewalDays="$DEFAULT_RENEW"
else else
_savedomainconf "Le_RenewalDays" "$Le_RenewalDays" _savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
fi fi
@ -4964,7 +4929,7 @@ _deactivate() {
authzUri="$_authorizations_seg" authzUri="$_authorizations_seg"
_debug2 "authzUri" "$authzUri" _debug2 "authzUri" "$authzUri"
if ! response="$(_get "$authzUri")"; then if ! _send_signed_request "$authzUri"; then
_err "get to authz error." _err "get to authz error."
_err "_authorizations_seg" "$_authorizations_seg" _err "_authorizations_seg" "$_authorizations_seg"
_err "authzUri" "$authzUri" _err "authzUri" "$authzUri"
@ -5527,7 +5492,7 @@ Parameters:
--useragent Specifies the user agent string. it will be saved for future use too. --useragent Specifies the user agent string. it will be saved for future use too.
--accountemail Specifies the account email, only valid for the '--install' and '--update-account' command. --accountemail Specifies the account email, only valid for the '--install' and '--update-account' command.
--accountkey Specifies the account key path, only valid for the '--install' command. --accountkey Specifies the account key path, only valid for the '--install' command.
--days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days. --days Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days.
--httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer. --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
--tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer. --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
--local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses. --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.

View File

@ -332,3 +332,52 @@ variable to anything (ex: "1") before running `acme.sh`:
```sh ```sh
export FABIO="1" export FABIO="1"
``` ```
## 13. Deploy your certificate to Qiniu.com
使用 acme.sh 部署到七牛之前,需要确保部署的域名已打开 HTTPS 功能,您可以访问[融合 CDN - 域名管理](https://portal.qiniu.com/cdn/domain) 设置。
另外还需要先导出 AK/SK 环境变量,您可以访问[密钥管理](https://portal.qiniu.com/user/key) 获得。
```sh
$ export QINIU_AK="foo"
$ export QINIU_SK="bar"
```
完成准备工作之后,您就可以通过下面的命令开始部署 SSL 证书到七牛上:
```sh
$ acme.sh --deploy -d example.com --deploy-hook qiniu
```
假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名:
```sh
$ export QINIU_CDN_DOMAIN="cdn.example.com"
$ acme.sh --deploy -d example.com --deploy-hook qiniu
```
### English version
You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key
before deploying your certificate, and please ensure you have enabled HTTPS for
your domain name. You can enable it in https://portal.qiniu.com/cdn/domain.
```sh
$ export QINIU_AK="foo"
$ export QINIU_SK="bar"
```
then you can deploy certificate by following command:
```sh
$ acme.sh --deploy -d example.com --deploy-hook qiniu
```
(Optional), If you are using wildcard certificate,
you may need export `QINIU_CDN_DOMAIN` to specify which domain
you want to update:
```sh
$ export QINIU_CDN_DOMAIN="cdn.example.com"
$ acme.sh --deploy -d example.com --deploy-hook qiniu
```

92
deploy/qiniu.sh Normal file
View File

@ -0,0 +1,92 @@
#!/usr/bin/env sh
# Script to create certificate to qiniu.com
#
# This deployment required following variables
# export QINIU_AK="QINIUACCESSKEY"
# export QINIU_SK="QINIUSECRETKEY"
# export QINIU_CDN_DOMAIN="cdn.example.com"
QINIU_API_BASE="https://api.qiniu.com"
qiniu_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if [ -z "$QINIU_AK" ]; then
_err "QINIU_AK is not defined."
return 1
else
_savedomainconf QINIU_AK "$QINIU_AK"
fi
if [ -z "$QINIU_SK" ]; then
_err "QINIU_SK is not defined."
return 1
else
_savedomainconf QINIU_SK "$QINIU_SK"
fi
if [ "$QINIU_CDN_DOMAIN" ]; then
_savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN"
else
QINIU_CDN_DOMAIN="$_cdomain"
fi
## upload certificate
string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
sslcert_path="/sslcert"
sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$QINIU_CDN_DOMAIN\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}"
sslcert_access_token="$(_make_access_token "$sslcert_path")"
_debug sslcert_access_token "$sslcert_access_token"
export _H1="Authorization: QBox $sslcert_access_token"
sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline")
if ! _contains "$sslcert_response" "certID"; then
_err "Error in creating certificate:"
_err "$sslcert_response"
return 1
fi
_debug sslcert_response "$sslcert_response"
_info "Certificate successfully uploaded, updating domain $_cdomain"
## extract certId
_certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\": *\"[^\"]*\"" | cut -d : -f 2)"
_debug certId "$_certId"
## update domain ssl config
update_path="/domain/$QINIU_CDN_DOMAIN/httpsconf"
update_body="{\"certid\":$_certId,\"forceHttps\":false}"
update_access_token="$(_make_access_token "$update_path")"
_debug update_access_token "$update_access_token"
export _H1="Authorization: QBox $update_access_token"
update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline")
if _contains "$update_response" "error"; then
_err "Error in updating domain httpsconf:"
_err "$update_response"
return 1
fi
_debug update_response "$update_response"
_info "Certificate successfully deployed"
return 0
}
_make_access_token() {
_token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64)"
echo "$QINIU_AK:$_token"
}

View File

@ -1122,21 +1122,67 @@ export EXOSCALE_SECRET_KEY='xxx'
Now, let's issue a cert: Now, let's issue a cert:
``` ```
acme.sh --issue --dns dns_netcup -d example.com -d www.example.com acme.sh --issue --dns dns_exoscale -d example.com -d www.example.com
``` ```
The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 58. Use Internet.bs ## 58. Using PointHQ API to issue certs
First you need to create/obtain API credentials on your Internet.bs (https://internetbs.net) account. Go to the "Get my API Key" section in the "My Domains" section. Log into [PointHQ account management](https://app.pointhq.com/profile) and copy the API key from the page there.
```export PointHQ_Key="apikeystringgoeshere"
exportPointHQ_Email="accountemail@yourdomain.com"
```
You can then issue certs by using:
```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com
```
## 59. Use Active24 API
Create an API token in the Active24 account section, documentation on https://faq.active24.com/cz/790131-REST-API-rozhran%C3%AD.
Set your API token:
```
export ACTIVE24_Token='xxx'
```
Now, let's issue a cert, set `dnssleep` for propagation new DNS record:
```
acme.sh --issue --dns dns_active24 -d example.com -d www.example.com --dnssleep 1000
```
The `ACTIVE24_Token` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 60. Use do.de API
Create an API token in your do.de account.
Set your API token:
```
export DO_LETOKEN='FmD408PdqT1E269gUK57'
```
To issue a certificate run:
```
acme.sh --issue --dns dns_doapi -d example.com -d *.example.com
```
The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 61. Use internetbs.net API
Create an API token in your internetbs.net account.
Set your API token:
``` ```
export INTERNETBS_API_KEY="..." export INTERNETBS_API_KEY="..."
export INTERNETBS_API_PASSWORD="..." export INTERNETBS_API_PASSWORD="..."
``` ```
Ok, let's issue a cert now: To issue a certificate run:
``` ```
acme.sh --issue --dns dns_internetbs -d example.com -d www.example.com acme.sh --issue --dns dns_internetbs -d example.com -d www.example.com
``` ```

141
dnsapi/dns_active24.sh Executable file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env sh
#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
ACTIVE24_Api="https://api.active24.com"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_active24_add() {
fulldomain=$1
txtvalue=$2
_active24_init
_info "Adding txt record"
if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
if _contains "$response" "errors"; then
_err "Add txt record error."
return 1
else
_info "Added, OK"
return 0
fi
fi
_err "Add txt record error."
return 1
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_active24_rm() {
fulldomain=$1
txtvalue=$2
_active24_init
_debug "Getting txt records"
_active24_rest GET "dns/$_domain/records/v1"
if _contains "$response" "errors"; then
_err "Error"
return 1
fi
hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-)
for hash_id in $hash_ids; do
_debug "Removing hash_id" "$hash_id"
if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
if _contains "$response" "errors"; then
_err "Unable to remove txt record."
return 1
else
_info "Removed txt record."
return 0
fi
fi
done
_err "No txt records found."
return 1
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
if ! _active24_rest GET "dns/domains/v1"; then
return 1
fi
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug "h" "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "\"$h\"" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_active24_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Authorization: Bearer $ACTIVE24_Token"
if [ "$m" != "GET" ]; then
_debug "data" "$data"
response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
else
response="$(_get "$ACTIVE24_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_active24_init() {
ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}"
if [ -z "$ACTIVE24_Token" ]; then
ACTIVE24_Token=""
_err "You didn't specify a Active24 api token yet."
_err "Please create the token and try again."
return 1
fi
_saveaccountconf_mutable ACTIVE24_Token "ACTIVE24_Token"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
}

View File

@ -19,8 +19,8 @@ dns_cf_add() {
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
CF_Email="" CF_Email=""
_err "You didn't specify a cloudflare api key and email yet." _err "You didn't specify a Cloudflare api key and email yet."
_err "Please create the key and try again." _err "You can get yours from here https://dash.cloudflare.com/profile."
return 1 return 1
fi fi
@ -34,9 +34,6 @@ dns_cf_add() {
_saveaccountconf_mutable CF_Key "$CF_Key" _saveaccountconf_mutable CF_Key "$CF_Key"
_saveaccountconf_mutable CF_Email "$CF_Email" _saveaccountconf_mutable CF_Email "$CF_Email"
_DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '@.' '__')"
_cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
@ -100,21 +97,16 @@ dns_cf_rm() {
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
CF_Email="" CF_Email=""
_err "You didn't specify a cloudflare api key and email yet." _err "You didn't specify a Cloudflare api key and email yet."
_err "Please create the key and try again." _err "You can get yours from here https://dash.cloudflare.com/profile."
return 1 return 1
fi fi
_DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '@.' '__')"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_"
_err "invalid domain" _err "invalid domain"
return 1 return 1
fi fi
_cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_"
_debug _domain_id "$_domain_id" _debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
@ -154,21 +146,6 @@ dns_cf_rm() {
# _domain=domain.com # _domain=domain.com
# _domain_id=sdjkglgdfewsdfg # _domain_id=sdjkglgdfewsdfg
_get_root() { _get_root() {
_cf_zones="$(_readdomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_")"
_debug2 "_cf_zones" "$_cf_zones"
if [ -z "$_cf_zones" ]; then
_debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ is none, so get it."
if ! _cf_rest GET "zones"; then
return 1
fi
_cf_zones="$response"
_savedomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" "$(echo "$_cf_zones" | _base64)"
else
_debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ found"
_cf_zones="$(echo "$_cf_zones" | _dbase64)"
fi
domain=$1 domain=$1
i=2 i=2
p=1 p=1
@ -180,8 +157,12 @@ _get_root() {
return 1 return 1
fi fi
if _contains "$_cf_zones" "\"name\":\"$h\"" >/dev/null; then if ! _cf_rest GET "zones?name=$h"; then
_domain_id=$(echo "$_cf_zones" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "^\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain=$h

View File

@ -104,48 +104,60 @@ dns_dgon_rm() {
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}} ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
GURL="https://api.digitalocean.com/v2/domains/$_domain/records" GURL="https://api.digitalocean.com/v2/domains/$_domain/records"
## while we dont have a record ID we keep going ## Get all the matching records
while [ -z "$record" ]; do while true; do
## 1) get the URL ## 1) get the URL
## the create request - get ## the create request - get
## args: URL, [onlyheader, timeout] ## args: URL, [onlyheader, timeout]
domain_list="$(_get "$GURL")" domain_list="$(_get "$GURL")"
## 2) find record
## check for what we are looing for: "type":"A","name":"$_sub_domain" ## check response
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" if [ "$?" != "0" ]; then
## 3) check record and get next page _err "error in domain_list response: $domain_list"
if [ -z "$record" ]; then return 1
## find the next page if we dont have a match
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
if [ -z "$nextpage" ]; then
_err "no record and no nextpage in digital ocean DNS removal"
return 1
fi
_debug2 nextpage "$nextpage"
GURL="$nextpage"
fi fi
## we break out of the loop when we have a record _debug2 domain_list "$domain_list"
## 2) find records
## check for what we are looking for: "type":"A","name":"$_sub_domain"
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
if [ ! -z "$record" ]; then
## we found records
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_ids "$rec_ids"
if [ ! -z "$rec_ids" ]; then
echo "$rec_ids" | while IFS= read -r rec_id; do
## delete the record
## delete URL for removing the one we dont want
DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id"
## the create request - delete
## args: BODY, URL, [need64, httpmethod]
response="$(_post "" "$DURL" "" "DELETE")"
## check response (sort of)
if [ "$?" != "0" ]; then
_err "error in remove response: $response"
return 1
fi
_debug2 response "$response"
done
fi
fi
## 3) find the next page
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
if [ -z "$nextpage" ]; then
break
fi
_debug2 nextpage "$nextpage"
GURL="$nextpage"
done done
## we found the record
rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_id "$rec_id"
## delete the record
## delete URL for removing the one we dont want
DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id"
## the create request - delete
## args: BODY, URL, [need64, httpmethod]
response="$(_post "" "$DURL" "" "DELETE")"
## check response (sort of)
if [ "$?" != "0" ]; then
_err "error in remove response: $response"
return 1
fi
_debug2 response "$response"
## finished correctly ## finished correctly
return 0 return 0
} }
@ -178,44 +190,57 @@ _get_base_domain() {
export _H2="Authorization: Bearer $DO_API_KEY" export _H2="Authorization: Bearer $DO_API_KEY"
_debug DO_API_KEY "$DO_API_KEY" _debug DO_API_KEY "$DO_API_KEY"
## get URL for the list of domains ## get URL for the list of domains
## havent seen this request paginated, tested with 18 domains (more requires manual requests with DO) ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
DOMURL="https://api.digitalocean.com/v2/domains" DOMURL="https://api.digitalocean.com/v2/domains"
## get the domain list (DO gives basically a full XFER!) ## while we dont have a matching domain we keep going
domain_list="$(_get "$DOMURL")" while [ -z "$found" ]; do
## get the domain list (current page)
domain_list="$(_get "$DOMURL")"
## check response ## check response
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "error in domain_list response: $domain_list" _err "error in domain_list response: $domain_list"
return 1
fi
_debug2 domain_list "$domain_list"
## for each shortening of our $fulldomain, check if it exists in the $domain_list
## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
i=2
while [ $i -gt 0 ]; do
## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
## check we got something back from our cut (or are we at the end)
if [ -z "$_domain" ]; then
## we got to the end of the domain - invalid domain
_err "domain not found in DigitalOcean account"
return 1 return 1
fi fi
## we got part of a domain back - grep it out _debug2 domain_list "$domain_list"
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
## check if it exists ## for each shortening of our $fulldomain, check if it exists in the $domain_list
if [ ! -z "$found" ]; then ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
## exists - exit loop returning the parts i=2
sub_point=$(_math $i - 1) while [ $i -gt 0 ]; do
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point") ## get next longest domain
_debug _domain "$_domain" _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
_debug _sub_domain "$_sub_domain" ## check we got something back from our cut (or are we at the end)
return 0 if [ -z "$_domain" ]; then
break
fi
## we got part of a domain back - grep it out
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
## check if it exists
if [ ! -z "$found" ]; then
## exists - exit loop returning the parts
sub_point=$(_math $i - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
_debug _domain "$_domain"
_debug _sub_domain "$_sub_domain"
return 0
fi
## increment cut point $i
i=$(_math $i + 1)
done
if [ -z "$found" ]; then
## find the next page if we dont have a match
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
if [ -z "$nextpage" ]; then
_err "no record and no nextpage in digital ocean DNS removal"
return 1
fi
_debug2 nextpage "$nextpage"
DOMURL="$nextpage"
fi fi
## increment cut point $i
i=$(_math $i + 1)
done done
## we went through the entire domain zone list and dint find one that matched ## we went through the entire domain zone list and dint find one that matched

View File

@ -152,7 +152,7 @@ _get_records() {
sub_domain=$3 sub_domain=$3
_debug "fetching txt records" _debug "fetching txt records"
_dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100" _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=5000&sort=id:desc"
if ! _contains "$response" "\"id\":"; then if ! _contains "$response" "\"id\":"; then
_err "failed to retrieve records" _err "failed to retrieve records"

59
dnsapi/dns_doapi.sh Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env sh
# Official Let's Encrypt API for do.de / Domain-Offensive
#
# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers
# This API is also available to private customers/individuals
#
# Provide the required LetsEncrypt token like this:
# DO_LETOKEN="FmD408PdqT1E269gUK57"
DO_API="https://www.do.de/api/letsencrypt"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_doapi_add() {
fulldomain=$1
txtvalue=$2
DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}"
if [ -z "$DO_LETOKEN" ]; then
DO_LETOKEN=""
_err "You didn't configure a do.de API token yet."
_err "Please set DO_LETOKEN and try again."
return 1
fi
_saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN"
_info "Adding TXT record to ${fulldomain}"
response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&value=${txtvalue}")"
if _contains "${response}" 'success'; then
return 0
fi
_err "Could not create resource record, check logs"
_err "${response}"
return 1
}
dns_doapi_rm() {
fulldomain=$1
DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}"
if [ -z "$DO_LETOKEN" ]; then
DO_LETOKEN=""
_err "You didn't configure a do.de API token yet."
_err "Please set DO_LETOKEN and try again."
return 1
fi
_saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN"
_info "Deleting resource record $fulldomain"
response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&action=delete")"
if _contains "${response}" 'success'; then
return 0
fi
_err "Could not delete resource record, check logs"
_err "${response}"
return 1
}

View File

@ -63,7 +63,7 @@ dns_dp_rm() {
return 0 return 0
fi fi
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \") record_id=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
_debug record_id "$record_id" _debug record_id "$record_id"
if [ -z "$record_id" ]; then if [ -z "$record_id" ]; then
_err "Can not get record id." _err "Can not get record id."

View File

@ -10,7 +10,7 @@
Dynu_Token="" Dynu_Token=""
# #
#Endpoint #Endpoint
Dynu_EndPoint="https://api.dynu.com/v1" Dynu_EndPoint="https://api.dynu.com/v2"
# #
#Author: Dynu Systems, Inc. #Author: Dynu Systems, Inc.
#Report Bugs here: https://github.com/shar0119/acme.sh #Report Bugs here: https://github.com/shar0119/acme.sh
@ -51,11 +51,11 @@ dns_dynu_add() {
_debug _domain_name "$_domain_name" _debug _domain_name "$_domain_name"
_info "Creating TXT record." _info "Creating TXT record."
if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then if ! _dynu_rest POST "dns/$dnsId/record" "{\"domainId\":\"$dnsId\",\"nodeName\":\"$_node\",\"recordType\":\"TXT\",\"textData\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then
return 1 return 1
fi fi
if ! _contains "$response" "text_data"; then if ! _contains "$response" "200"; then
_err "Could not add TXT record." _err "Could not add TXT record."
return 1 return 1
fi fi
@ -132,11 +132,12 @@ _get_root() {
return 1 return 1
fi fi
if ! _dynu_rest GET "dns/get/$h"; then if ! _dynu_rest GET "dns/getroot/$h"; then
return 1 return 1
fi fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
_domain_name=$h _domain_name=$h
_node=$(printf "%s" "$domain" | cut -d . -f 1-$p) _node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
return 0 return 0
@ -152,7 +153,7 @@ _get_recordid() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then if ! _dynu_rest GET "dns/$dnsId/record"; then
return 1 return 1
fi fi
@ -161,19 +162,18 @@ _get_recordid() {
return 0 return 0
fi fi
_dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | _egrep_o ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) _dns_record_id=$(printf "%s" "$response" | sed -e 's/[^{]*\({[^}]*}\)[^{]*/\1\n/g' | grep "\"textData\":\"$txtvalue\"" | sed -e 's/.*"id":\([^,]*\).*/\1/')
return 0 return 0
} }
_delete_txt_record() { _delete_txt_record() {
_dns_record_id=$1 _dns_record_id=$1
if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then if ! _dynu_rest DELETE "dns/$dnsId/record/$_dns_record_id"; then
return 1 return 1
fi fi
if ! _contains "$response" "true"; then if ! _contains "$response" "200"; then
return 1 return 1
fi fi
@ -189,7 +189,7 @@ _dynu_rest() {
export _H1="Authorization: Bearer $Dynu_Token" export _H1="Authorization: Bearer $Dynu_Token"
export _H2="Content-Type: application/json" export _H2="Content-Type: application/json"
if [ "$data" ]; then if [ "$data" ] || [ "$m" = "DELETE" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")"
else else
@ -216,8 +216,8 @@ _dynu_authentication() {
_err "Authentication failed." _err "Authentication failed."
return 1 return 1
fi fi
if _contains "$response" "accessToken"; then if _contains "$response" "access_token"; then
Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2) Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
fi fi
if _contains "$Dynu_Token" "null"; then if _contains "$Dynu_Token" "null"; then
Dynu_Token="" Dynu_Token=""

60
dnsapi/dns_gandi_livedns.sh Executable file → Normal file
View File

@ -7,6 +7,7 @@
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
# #
#Author: Frédéric Crozat <fcrozat@suse.com> #Author: Frédéric Crozat <fcrozat@suse.com>
# Dominik Röttsches <drott@google.com>
#Report Bugs here: https://github.com/fcrozat/acme.sh #Report Bugs here: https://github.com/fcrozat/acme.sh
# #
######## Public functions ##################### ######## Public functions #####################
@ -36,9 +37,7 @@ dns_gandi_livedns_add() {
_debug domain "$_domain" _debug domain "$_domain"
_debug sub_domain "$_sub_domain" _debug sub_domain "$_sub_domain"
_gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \ _dns_gandi_append_record "$_domain" "$_sub_domain" "$txtvalue"
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Add $(__green "success")"
} }
#Usage: fulldomain txtvalue #Usage: fulldomain txtvalue
@ -56,9 +55,23 @@ dns_gandi_livedns_rm() {
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
_debug domain "$_domain" _debug domain "$_domain"
_debug sub_domain "$_sub_domain" _debug sub_domain "$_sub_domain"
_debug txtvalue "$txtvalue"
_gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" "" if ! _dns_gandi_existing_rrset_values "$_domain" "$_sub_domain"; then
return 1
fi
_new_rrset_values=$(echo "$_rrset_values" | sed "s/...$txtvalue...//g")
# Cleanup dangling commata.
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, ,/ ,/g")
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, *\]/\]/g")
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/\[ *,/\[/g")
_debug "New rrset_values" "$_new_rrset_values"
_gandi_livedns_rest PUT \
"domains/$_domain/records/$_sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" \
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Removing record $(__green "success")"
} }
#################### Private functions below ################################## #################### Private functions below ##################################
@ -98,6 +111,45 @@ _get_root() {
return 1 return 1
} }
_dns_gandi_append_record() {
domain=$1
sub_domain=$2
txtvalue=$3
if _dns_gandi_existing_rrset_values "$domain" "$sub_domain"; then
_debug "Appending new value"
_rrset_values=$(echo "$_rrset_values" | sed "s/\"]/\",\"$txtvalue\"]/")
else
_debug "Creating new record" "$_rrset_values"
_rrset_values="[\"$txtvalue\"]"
fi
_debug new_rrset_values "$_rrset_values"
_gandi_livedns_rest PUT "domains/$_domain/records/$sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_rrset_values}" \
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Adding record $(__green "success")"
}
_dns_gandi_existing_rrset_values() {
domain=$1
sub_domain=$2
if ! _gandi_livedns_rest GET "domains/$domain/records/$sub_domain"; then
return 1
fi
if ! _contains "$response" '"rrset_type": "TXT"'; then
_debug "Does not have a _acme-challenge TXT record yet."
return 1
fi
if _contains "$response" '"rrset_values": \[\]'; then
_debug "Empty rrset_values for TXT record, no previous TXT record."
return 1
fi
_debug "Already has TXT record."
_rrset_values=$(echo "$response" | _egrep_o 'rrset_values.*\[.*\]' \
| _egrep_o '\[".*\"]')
return 0
}
_gandi_livedns_rest() { _gandi_livedns_rest() {
m=$1 m=$1
ep="$2" ep="$2"

View File

@ -59,9 +59,22 @@ _hostingde_getZoneConfig() {
if _contains "${curResult}" '"totalEntries": 1'; then if _contains "${curResult}" '"totalEntries": 1'; then
_info "Retrieved zone data." _info "Retrieved zone data."
_debug "Zone data: '${curResult}'" _debug "Zone data: '${curResult}'"
# read ZoneConfigId for later update
zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigName=$(echo "${curResult}" | _egrep_o '"name":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigType=$(echo "${curResult}" | grep -v "FindZoneConfigsResult" | _egrep_o '"type":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigExpire=$(echo "${curResult}" | _egrep_o '"expire":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigNegativeTtl=$(echo "${curResult}" | _egrep_o '"negativeTtl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigRefresh=$(echo "${curResult}" | _egrep_o '"refresh":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigRetry=$(echo "${curResult}" | _egrep_o '"retry":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigTtl=$(echo "${curResult}" | _egrep_o '"ttl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigDnsServerGroupId=$(echo "${curResult}" | _egrep_o '"dnsServerGroupId":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigEmailAddress=$(echo "${curResult}" | _egrep_o '"emailAddress":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigDnsSecMode=$(echo "${curResult}" | _egrep_o '"dnsSecMode":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ "${zoneConfigType}" != "NATIVE" ]; then
_err "Zone is not native"
returnCode=1
break
fi
_debug "zoneConfigId '${zoneConfigId}'" _debug "zoneConfigId '${zoneConfigId}'"
returnCode=0 returnCode=0
break break
@ -74,9 +87,27 @@ _hostingde_getZoneConfig() {
return $returnCode return $returnCode
} }
_hostingde_getZoneStatus() {
_debug "Checking Zone status"
curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":\"${zoneConfigId}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind")"
_debug "Calling zonesFind '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind'"
_debug "Result of zonesFind '$curResult'"
zoneStatus=$(echo "${curResult}" | grep -v success | _egrep_o '"status":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "zoneStatus '${zoneStatus}'"
return 0
}
_hostingde_addRecord() { _hostingde_addRecord() {
_info "Adding record to zone" _info "Adding record to zone"
curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" _hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'"
while [ "${zoneStatus}" != "active" ]; do
_sleep 5
_hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'"
done
curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\",\"name\":\"${zoneConfigName}\",\"type\":\"${zoneConfigType}\",\"dnsServerGroupId\":\"${zoneConfigDnsServerGroupId}\",\"dnsSecMode\":\"${zoneConfigDnsSecMode}\",\"emailAddress\":\"${zoneConfigEmailAddress}\",\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
_debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
_debug "Result of zoneUpdate: '$curResult'" _debug "Result of zoneUpdate: '$curResult'"
@ -93,7 +124,14 @@ _hostingde_addRecord() {
_hostingde_removeRecord() { _hostingde_removeRecord() {
_info "Removing record from zone" _info "Removing record from zone"
curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" _hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'"
while [ "$zoneStatus" != "active" ]; do
_sleep 5
_hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'"
done
curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\",\"name\":\"${zoneConfigName}\",\"type\":\"${zoneConfigType}\",\"dnsServerGroupId\":\"${zoneConfigDnsServerGroupId}\",\"dnsSecMode\":\"${zoneConfigDnsSecMode}\",\"emailAddress\":\"${zoneConfigEmailAddress}\",\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
_debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
_debug "Result of zoneUpdate: '$curResult'" _debug "Result of zoneUpdate: '$curResult'"

View File

@ -158,7 +158,8 @@ _inwx_login() {
export _H1 export _H1
#https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71 #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
if _contains "$response" "tfa"; then if _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>" \
&& _contains "$response" "<member><name>tfa</name><value><string>GOOGLE-AUTH</string></value></member>"; then
if [ -z "$INWX_Shared_Secret" ]; then if [ -z "$INWX_Shared_Secret" ]; then
_err "Mobile TAN detected." _err "Mobile TAN detected."
_err "Please define a shared secret." _err "Please define a shared secret."

View File

@ -8,7 +8,7 @@ LINODE_V4_API_URL="https://api.linode.com/v4/domains"
######## Public functions ##################### ######## Public functions #####################
#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_linode_add() { dns_linode_v4_add() {
fulldomain="${1}" fulldomain="${1}"
txtvalue="${2}" txtvalue="${2}"
@ -51,7 +51,7 @@ dns_linode_add() {
} }
#Usage: dns_linode_rm _acme-challenge.www.domain.com #Usage: dns_linode_rm _acme-challenge.www.domain.com
dns_linode_rm() { dns_linode_v4_rm() {
fulldomain="${1}" fulldomain="${1}"
if ! _Linode_API; then if ! _Linode_API; then

View File

@ -38,8 +38,8 @@ dns_loopia_add() {
_info "Adding record" _info "Adding record"
_loopia_add_record "$_domain" "$_sub_domain" _loopia_add_sub_domain "$_domain" "$_sub_domain"
_loopia_update_record "$_domain" "$_sub_domain" "$txtvalue" _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"
} }
@ -96,6 +96,37 @@ dns_loopia_rm() {
#################### Private functions below ################################## #################### Private functions below ##################################
_loopia_get_records() {
domain=$1
sub_domain=$2
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>getZoneRecords</methodName>
<params>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if ! _contains "$response" "<array>"; then
_err "Error"
return 1
fi
return 0
}
_get_root() { _get_root() {
domain=$1 domain=$1
_debug "get root" _debug "get root"
@ -137,14 +168,14 @@ _get_root() {
} }
_loopia_update_record() { _loopia_add_record() {
domain=$1 domain=$1
sub_domain=$2 sub_domain=$2
txtval=$3 txtval=$3
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall> <methodCall>
<methodName>updateZoneRecord</methodName> <methodName>addZoneRecord</methodName>
<params> <params>
<param> <param>
<value><string>%s</string></value> <value><string>%s</string></value>
@ -176,10 +207,6 @@ _loopia_update_record() {
<name>rdata</name> <name>rdata</name>
<value><string>%s</string></value> <value><string>%s</string></value>
</member> </member>
<member>
<name>record_id</name>
<value><int>0</int></value>
</member>
</struct> </struct>
</param> </param>
</params> </params>
@ -194,10 +221,42 @@ _loopia_update_record() {
return 0 return 0
} }
_loopia_add_record() { _sub_domain_exists() {
domain=$1 domain=$1
sub_domain=$2 sub_domain=$2
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>getSubdomains</methodName>
<params>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if _contains "$response" "$sub_domain"; then
return 0
fi
return 1
}
_loopia_add_sub_domain() {
domain=$1
sub_domain=$2
if _sub_domain_exists "$domain" "$sub_domain"; then
return 0
fi
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall> <methodCall>
<methodName>addSubdomain</methodName> <methodName>addSubdomain</methodName>

View File

@ -3,16 +3,15 @@
# Namecheap API # Namecheap API
# https://www.namecheap.com/support/api/intro.aspx # https://www.namecheap.com/support/api/intro.aspx
# #
# Requires Namecheap API key set in NAMECHEAP_API_KEY, NAMECHEAP_SOURCEIP and NAMECHEAP_USERNAME set as environment variable # Requires Namecheap API key set in
#NAMECHEAP_API_KEY,
#NAMECHEAP_USERNAME,
#NAMECHEAP_SOURCEIP
# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. # Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
######## Public functions ##################### ######## Public functions #####################
if [ "$STAGE" -eq 1 ]; then NAMECHEAP_API="https://api.namecheap.com/xml.response"
NAMECHEAP_API="https://api.sandbox.namecheap.com/xml.response"
else
NAMECHEAP_API="https://api.namecheap.com/xml.response"
fi
#Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_namecheap_add() { dns_namecheap_add() {
@ -144,7 +143,7 @@ _namecheap_set_publicip() {
_namecheap_post() { _namecheap_post() {
command=$1 command=$1
data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}" data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}"
_debug2 "_namecheap_post data" "$data"
response="$(_post "$data" "$NAMECHEAP_API" "" "POST")" response="$(_post "$data" "$NAMECHEAP_API" "" "POST")"
_debug2 response "$response" _debug2 response "$response"
@ -161,12 +160,12 @@ _namecheap_parse_host() {
_host=$1 _host=$1
_debug _host "$_host" _debug _host "$_host"
_hostid=$(echo "$_host" | _egrep_o '\sHostId="[^"]*' | cut -d '"' -f 2) _hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2)
_hostname=$(echo "$_host" | _egrep_o '\sName="[^"]*' | cut -d '"' -f 2) _hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2)
_hosttype=$(echo "$_host" | _egrep_o '\sType="[^"]*' | cut -d '"' -f 2) _hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2)
_hostaddress=$(echo "$_host" | _egrep_o '\sAddress="[^"]*' | cut -d '"' -f 2) _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2)
_hostmxpref=$(echo "$_host" | _egrep_o '\sMXPref="[^"]*' | cut -d '"' -f 2) _hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2)
_hostttl=$(echo "$_host" | _egrep_o '\sTTL="[^"]*' | cut -d '"' -f 2) _hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2)
_debug hostid "$_hostid" _debug hostid "$_hostid"
_debug hostname "$_hostname" _debug hostname "$_hostname"
@ -199,9 +198,12 @@ _namecheap_check_config() {
_set_namecheap_TXT() { _set_namecheap_TXT() {
subdomain=$2 subdomain=$2
txt=$3 txt=$3
tld=$(echo "$1" | cut -d '.' -f 2)
sld=$(echo "$1" | cut -d '.' -f 1) if ! _namecheap_set_tld_sld "$1"; then
request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" return 1
fi
request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
if ! _namecheap_post "$request"; then if ! _namecheap_post "$request"; then
_err "$error" _err "$error"
@ -221,6 +223,12 @@ _set_namecheap_TXT() {
while read -r host; do while read -r host; do
if _contains "$host" "<host"; then if _contains "$host" "<host"; then
_namecheap_parse_host "$host" _namecheap_parse_host "$host"
_debug2 _hostname "_hostname"
_debug2 _hosttype "_hosttype"
_debug2 _hostaddress "_hostaddress"
_debug2 _hostmxpref "_hostmxpref"
_hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)"
_debug2 "encoded _hostaddress" "_hostaddress"
_namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl" _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
fi fi
done <<EOT done <<EOT
@ -231,7 +239,7 @@ EOT
_debug hostrequestfinal "$_hostrequest" _debug hostrequestfinal "$_hostrequest"
request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}" request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}"
if ! _namecheap_post "$request"; then if ! _namecheap_post "$request"; then
_err "$error" _err "$error"
@ -244,9 +252,12 @@ EOT
_del_namecheap_TXT() { _del_namecheap_TXT() {
subdomain=$2 subdomain=$2
txt=$3 txt=$3
tld=$(echo "$1" | cut -d '.' -f 2)
sld=$(echo "$1" | cut -d '.' -f 1) if ! _namecheap_set_tld_sld "$1"; then
request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" return 1
fi
request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
if ! _namecheap_post "$request"; then if ! _namecheap_post "$request"; then
_err "$error" _err "$error"
@ -272,6 +283,7 @@ _del_namecheap_TXT() {
_debug "TXT entry found" _debug "TXT entry found"
found=1 found=1
else else
_hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)"
_namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl" _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
fi fi
fi fi
@ -286,7 +298,7 @@ EOT
_debug hostrequestfinal "$_hostrequest" _debug hostrequestfinal "$_hostrequest"
request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}" request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}"
if ! _namecheap_post "$request"; then if ! _namecheap_post "$request"; then
_err "$error" _err "$error"
@ -306,3 +318,45 @@ _namecheap_add_host() {
_hostindex=$(_math "$_hostindex" + 1) _hostindex=$(_math "$_hostindex" + 1)
_hostrequest=$(printf '%s&HostName%d=%s&RecordType%d=%s&Address%d=%s&MXPref%d=%d&TTL%d=%d' "$_hostrequest" "$_hostindex" "$1" "$_hostindex" "$2" "$_hostindex" "$3" "$_hostindex" "$4" "$_hostindex" "$5") _hostrequest=$(printf '%s&HostName%d=%s&RecordType%d=%s&Address%d=%s&MXPref%d=%d&TTL%d=%d' "$_hostrequest" "$_hostindex" "$1" "$_hostindex" "$2" "$_hostindex" "$3" "$_hostindex" "$4" "$_hostindex" "$5")
} }
_namecheap_set_tld_sld() {
domain=$1
_tld=""
_sld=""
i=2
while true; do
_tld=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug tld "$_tld"
if [ -z "$_tld" ]; then
_debug "invalid tld"
return 1
fi
j=$(_math "$i" - 1)
_sld=$(printf "%s" "$domain" | cut -d . -f 1-"$j")
_debug sld "$_sld"
if [ -z "$_sld" ]; then
_debug "invalid sld"
return 1
fi
request="namecheap.domains.dns.getHosts&SLD=$_sld&TLD=$_tld"
if ! _namecheap_post "$request"; then
_debug "sld($_sld)/tld($_tld) not found"
else
_debug "sld($_sld)/tld($_tld) found"
return 0
fi
i=$(_math "$i" + 1)
done
}

164
dnsapi/dns_pointhq.sh Normal file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env sh
#
#PointHQ_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#PointHQ_Email="xxxx@sss.com"
PointHQ_Api="https://api.pointhq.com"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_pointhq_add() {
fulldomain=$1
txtvalue=$2
PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}"
PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}"
if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then
PointHQ_Key=""
PointHQ_Email=""
_err "You didn't specify a PointHQ API key and email yet."
_err "Please create the key and try again."
return 1
fi
if ! _contains "$PointHQ_Email" "@"; then
_err "It seems that the PointHQ_Email=$PointHQ_Email is not a valid email address."
_err "Please check and retry."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable PointHQ_Key "$PointHQ_Key"
_saveaccountconf_mutable PointHQ_Email "$PointHQ_Email"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
if _pointhq_rest POST "zones/$_domain/records" "{\"zone_record\": {\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":3600}}"; then
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_pointhq_rm() {
fulldomain=$1
txtvalue=$2
PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}"
PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}"
if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then
PointHQ_Key=""
PointHQ_Email=""
_err "You didn't specify a PointHQ API key and email yet."
_err "Please create the key and try again."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_pointhq_rest GET "zones/${_domain}/records?record_type=TXT&name=$_sub_domain"
if ! printf "%s" "$response" | grep "^\[" >/dev/null; then
_err "Error"
return 1
fi
if [ "$response" = "[]" ]; then
_info "No records to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | tr -d \" | head -n 1)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _pointhq_rest DELETE "zones/$_domain/records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" '"status":"OK"'
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _pointhq_rest GET "zones"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_pointhq_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_pointhq_auth=$(printf "%s:%s" "$PointHQ_Email" "$PointHQ_Key" | _base64)
export _H1="Authorization: Basic $_pointhq_auth"
export _H2="Content-Type: application/json"
export _H3="Accept: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$PointHQ_Api/$ep" "" "$m")"
else
response="$(_get "$PointHQ_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}