Browse Source

Merge branch 'dev' into master

pull/2081/head^2
Sebastiaan Hoogeveen 6 years ago
committed by GitHub
parent
commit
78c92642e4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      Dockerfile
  2. 13
      README.md
  3. 328
      acme.sh
  4. 18
      deploy/README.md
  5. 59
      deploy/mydevil.sh
  6. 2
      deploy/qiniu.sh
  7. 115
      dnsapi/README.md
  8. 50
      dnsapi/dns_hostingde.sh
  9. 97
      dnsapi/dns_mydevil.sh
  10. 45
      dnsapi/dns_namecheap.sh
  11. 4
      dnsapi/dns_nsone.sh
  12. 211
      dnsapi/dns_nw.sh
  13. 217
      dnsapi/dns_online.sh
  14. 207
      dnsapi/dns_rackspace.sh

3
Dockerfile

@ -1,4 +1,4 @@
FROM alpine:3.6
FROM alpine:3.9
RUN apk update -f \ RUN apk update -f \
&& apk --no-cache add -f \ && apk --no-cache add -f \
@ -7,6 +7,7 @@ RUN apk update -f \
bind-tools \ bind-tools \
curl \ curl \
socat \ socat \
tzdata \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
ENV LE_CONFIG_HOME /acme.sh ENV LE_CONFIG_HOME /acme.sh

13
README.md

@ -74,6 +74,7 @@ https://github.com/Neilpang/acmetest
- Letsencrypt.org CA(default) - Letsencrypt.org CA(default)
- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA) - [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA)
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
# Supported modes # Supported modes
@ -253,7 +254,7 @@ Just set string "apache" as the second argument and it will force use of apache
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
``` ```
**This apache mode is only to issue the cert, it will not change your apache config files.
**This apache mode is only to issue the cert, it will not change your apache config files.
You will need to configure your website config files to use the cert by yourself. You will need to configure your website config files to use the cert by yourself.
We don't want to mess your apache server, don't worry.** We don't want to mess your apache server, don't worry.**
@ -277,7 +278,7 @@ So, the config is not changed.
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
``` ```
**This nginx mode is only to issue the cert, it will not change your nginx config files.
**This nginx mode is only to issue the cert, it will not change your nginx config files.
You will need to configure your website config files to use the cert by yourself. You will need to configure your website config files to use the cert by yourself.
We don't want to mess your nginx server, don't worry.** We don't want to mess your nginx server, don't worry.**
@ -351,6 +352,12 @@ You don't have to do anything manually!
1. PointDNS API (https://pointhq.com/) 1. PointDNS API (https://pointhq.com/)
1. Active24.cz API (https://www.active24.cz/) 1. Active24.cz API (https://www.active24.cz/)
1. do.de API (https://www.do.de/) 1. do.de API (https://www.do.de/)
1. Nexcess API (https://www.nexcess.net)
1. Thermo.io API (https://www.thermo.io)
1. Futurehosting API (https://www.futurehosting.com)
1. Rackspace Cloud DNS (https://www.rackspace.com)
1. Online.net API (https://online.net/)
1. MyDevil.net (https://www.mydevil.net/)
1. NederHost API (https://www.nederhost.nl/) 1. NederHost API (https://www.nederhost.nl/)
And: And:
@ -529,5 +536,5 @@ Please Star and Fork me.
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/)
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)

328
acme.sh

@ -139,6 +139,7 @@ __red() {
} }
_printargs() { _printargs() {
_exitstatus="$?"
if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
printf -- "%s" "[$(date)] " printf -- "%s" "[$(date)] "
fi fi
@ -148,6 +149,8 @@ _printargs() {
printf -- "%s" "$1='$2'" printf -- "%s" "$1='$2'"
fi fi
printf "\n" printf "\n"
# return the saved exit status
return "$_exitstatus"
} }
_dlg_versions() { _dlg_versions() {
@ -183,6 +186,7 @@ _dlg_versions() {
#class #class
_syslog() { _syslog() {
_exitstatus="$?"
if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then
return return
fi fi
@ -196,6 +200,7 @@ _syslog() {
fi fi
fi fi
$__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1 $__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1
return "$_exitstatus"
} }
_log() { _log() {
@ -1188,7 +1193,7 @@ _ss() {
if _exists "netstat"; then if _exists "netstat"; then
_debug "Using: netstat" _debug "Using: netstat"
if netstat -h 2>&1 | grep "\-p proto" >/dev/null; then
if netstat -help 2>&1 | grep "\-p proto" >/dev/null; then
#for windows version netstat tool #for windows version netstat tool
netstat -an -p tcp | grep "LISTENING" | grep ":$_port " netstat -an -p tcp | grep "LISTENING" | grep ":$_port "
else else
@ -1822,23 +1827,29 @@ _send_signed_request() {
nonceurl="$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE"
if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then
_headers="$(cat "$HTTP_HEADER")" _headers="$(cat "$HTTP_HEADER")"
_debug2 _headers "$_headers"
_CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
fi fi
fi fi
if [ -z "$_headers" ]; then
if [ -z "$_CACHED_NONCE" ]; then
_debug2 "Get nonce with GET. ACME_DIRECTORY" "$ACME_DIRECTORY" _debug2 "Get nonce with GET. ACME_DIRECTORY" "$ACME_DIRECTORY"
nonceurl="$ACME_DIRECTORY" nonceurl="$ACME_DIRECTORY"
_headers="$(_get "$nonceurl" "onlyheader")" _headers="$(_get "$nonceurl" "onlyheader")"
_debug2 _headers "$_headers"
_CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
fi fi
if [ -z "$_CACHED_NONCE" ] && [ "$ACME_NEW_NONCE" ]; then
_debug2 "Get nonce with GET. ACME_NEW_NONCE" "$ACME_NEW_NONCE"
nonceurl="$ACME_NEW_NONCE"
_headers="$(_get "$nonceurl" "onlyheader")"
_debug2 _headers "$_headers"
_CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
fi
_debug2 _CACHED_NONCE "$_CACHED_NONCE"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "Can not connect to $nonceurl to get nonce." _err "Can not connect to $nonceurl to get nonce."
return 1 return 1
fi fi
_debug2 _headers "$_headers"
_CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
_debug2 _CACHED_NONCE "$_CACHED_NONCE"
else else
_debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE" _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
fi fi
@ -1882,29 +1893,34 @@ _send_signed_request() {
_err "Can not post to $url" _err "Can not post to $url"
return 1 return 1
fi fi
_debug2 original "$response"
response="$(echo "$response" | _normalizeJson)"
responseHeaders="$(cat "$HTTP_HEADER")" responseHeaders="$(cat "$HTTP_HEADER")"
_debug2 responseHeaders "$responseHeaders" _debug2 responseHeaders "$responseHeaders"
_debug2 response "$response"
code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
_debug code "$code" _debug code "$code"
_CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
_body="$response"
if [ "$needbase64" ]; then
_body="$(echo "$_body" | _dbase64 | tr -d '\0')"
_debug3 _body "$_body"
_debug2 original "$response"
if echo "$responseHeaders" | grep -i "Content-Type: application/json" >/dev/null 2>&1; then
response="$(echo "$response" | _normalizeJson)"
fi fi
_debug2 response "$response"
if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then
_info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds."
_CACHED_NONCE=""
_sleep $_sleep_retry_sec
continue
_CACHED_NONCE="$(echo "$responseHeaders" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
if ! _startswith "$code" "2"; then
_body="$response"
if [ "$needbase64" ]; then
_body="$(echo "$_body" | _dbase64 multiline)"
_debug3 _body "$_body"
fi
if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then
_info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds."
_CACHED_NONCE=""
_sleep $_sleep_retry_sec
continue
fi
fi fi
break break
done done
@ -2050,6 +2066,7 @@ _clearcaconf() {
_startserver() { _startserver() {
content="$1" content="$1"
ncaddr="$2" ncaddr="$2"
_debug "content" "$content"
_debug "ncaddr" "$ncaddr" _debug "ncaddr" "$ncaddr"
_debug "startserver: $$" _debug "startserver: $$"
@ -2076,8 +2093,14 @@ _startserver() {
SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}"
fi fi
_content_len="$(printf "%s" "$content" | wc -c)"
_debug _content_len "$_content_len"
_debug "_NC" "$_NC $SOCAT_OPTIONS" _debug "_NC" "$_NC $SOCAT_OPTIONS"
$_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" &
$_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; \
echo 'HTTP/1.0 200 OK'; \
echo 'Content-Length\: $_content_len'; \
echo ''; \
printf '$content';" &
serverproc="$!" serverproc="$!"
} }
@ -2919,42 +2942,38 @@ _clearup() {
_clearupdns() { _clearupdns() {
_debug "_clearupdns" _debug "_clearupdns"
_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 "dns_entries" "$dns_entries"
if [ -z "$dns_entries" ]; then
_debug "skip dns." _debug "skip dns."
return return
fi fi
_info "Removing DNS records." _info "Removing DNS records."
ventries=$(echo "$vlist" | tr ',' ' ')
_alias_index=1
for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_debug "$d is already verified, skip $vtype."
_alias_index="$(_math "$_alias_index" + 1)"
continue
fi
if [ "$vtype" != "$VTYPE_DNS" ]; then
_debug "Skip $d for $vtype"
continue
for entry in $dns_entries; do
d=$(_getfield "$entry" 1)
txtdomain=$(_getfield "$entry" 2)
aliasDomain=$(_getfield "$entry" 3)
txt=$(_getfield "$entry" 5)
d_api=$(_getfield "$entry" 6)
_debug "d" "$d"
_debug "txtdomain" "$txtdomain"
_debug "aliasDomain" "$aliasDomain"
_debug "txt" "$txt"
_debug "d_api" "$d_api"
if [ "$d_api" = "$txt" ]; then
d_api=""
fi fi
d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
_debug d_api "$d_api"
if [ -z "$d_api" ]; then if [ -z "$d_api" ]; then
_info "Not Found domain api file: $d_api" _info "Not Found domain api file: $d_api"
continue continue
fi fi
if [ "$aliasDomain" ]; then
txtdomain="$aliasDomain"
fi
( (
if ! . "$d_api"; then if ! . "$d_api"; then
_err "Load file $d_api error. Please check your api file and try again." _err "Load file $d_api error. Please check your api file and try again."
@ -2967,24 +2986,6 @@ _clearupdns() {
return 1 return 1
fi fi
_dns_root_d="$d"
if _startswith "$_dns_root_d" "*."; then
_dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
fi
_d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
_alias_index="$(_math "$_alias_index" + 1)"
_debug "_d_alias" "$_d_alias"
if [ "$_d_alias" ]; then
if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then
txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")"
else
txtdomain="_acme-challenge.$_d_alias"
fi
else
txtdomain="_acme-challenge.$_dns_root_d"
fi
if ! $rmcommand "$txtdomain" "$txt"; then if ! $rmcommand "$txtdomain" "$txt"; then
_err "Error removing txt for domain:$txtdomain" _err "Error removing txt for domain:$txtdomain"
return 1 return 1
@ -3074,6 +3075,7 @@ _on_before_issue() {
_info "Standalone mode." _info "Standalone mode."
if [ -z "$Le_HTTPPort" ]; then if [ -z "$Le_HTTPPort" ]; then
Le_HTTPPort=80 Le_HTTPPort=80
_cleardomainconf "Le_HTTPPort"
else else
_savedomainconf "Le_HTTPPort" "$Le_HTTPPort" _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
fi fi
@ -3281,7 +3283,7 @@ _regAccount() {
fi fi
_debug2 responseHeaders "$responseHeaders" _debug2 responseHeaders "$responseHeaders"
_accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
_accUri="$(echo "$responseHeaders" | grep -i "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
_debug "_accUri" "$_accUri" _debug "_accUri" "$_accUri"
if [ -z "$_accUri" ]; then if [ -z "$_accUri" ]; then
_err "Can not find account id url." _err "Can not find account id url."
@ -3447,12 +3449,119 @@ __trigger_validation() {
_t_vtype="$3" _t_vtype="$3"
_debug2 _t_vtype "$_t_vtype" _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" "{}"
else else
_send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}" _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}"
fi fi
} }
#endpoint domain type
_ns_lookup() {
_ns_ep="$1"
_ns_domain="$2"
_ns_type="$3"
_debug2 "_ns_ep" "$_ns_ep"
_debug2 "_ns_domain" "$_ns_domain"
_debug2 "_ns_type" "$_ns_type"
response="$(_H1="accept: application/dns-json" _get "$_ns_ep?name=$_ns_domain&type=$_ns_type")"
_ret=$?
_debug2 "response" "$response"
if [ "$_ret" != "0" ]; then
return $_ret
fi
_answers="$(echo "$response" | tr '{}' '<>' | _egrep_o '"Answer":\[[^]]*]' | tr '<>' '\n\n')"
_debug2 "_answers" "$_answers"
echo "$_answers"
}
#domain, type
_ns_lookup_cf() {
_cf_ld="$1"
_cf_ld_type="$2"
_cf_ep="https://cloudflare-dns.com/dns-query"
_ns_lookup "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
}
#domain, type
_ns_purge_cf() {
_cf_d="$1"
_cf_d_type="$2"
_debug "Cloudflare purge $_cf_d_type record for domain $_cf_d"
_cf_purl="https://1.1.1.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type"
response="$(_post "" "$_cf_purl")"
_debug2 response "$response"
}
#txtdomain, alias, txt
__check_txt() {
_c_txtdomain="$1"
_c_aliasdomain="$2"
_c_txt="$3"
_debug "_c_txtdomain" "$_c_txtdomain"
_debug "_c_aliasdomain" "$_c_aliasdomain"
_debug "_c_txt" "$_c_txt"
_answers="$(_ns_lookup_cf "$_c_aliasdomain" TXT)"
_contains "$_answers" "$_c_txt"
}
#txtdomain
__purge_txt() {
_p_txtdomain="$1"
_debug _p_txtdomain "$_p_txtdomain"
_ns_purge_cf "$_p_txtdomain" "TXT"
}
#wait and check each dns entries
_check_dns_entries() {
_success_txt=","
_end_time="$(_time)"
_end_time="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes.
while [ "$(_time)" -le "$_end_time" ]; do
_left=""
for entry in $dns_entries; do
d=$(_getfield "$entry" 1)
txtdomain=$(_getfield "$entry" 2)
aliasDomain=$(_getfield "$entry" 3)
txt=$(_getfield "$entry" 5)
d_api=$(_getfield "$entry" 6)
_debug "d" "$d"
_debug "txtdomain" "$txtdomain"
_debug "aliasDomain" "$aliasDomain"
_debug "txt" "$txt"
_debug "d_api" "$d_api"
_info "Checking $d for $aliasDomain"
if _contains "$_success_txt" ",$txt,"; then
_info "Already success, continue next one."
continue
fi
if __check_txt "$txtdomain" "$aliasDomain" "$txt"; then
_info "Domain $d '$aliasDomain' success."
_success_txt="$_success_txt,$txt,"
continue
fi
_left=1
_info "Not valid yet, let's wait 10 seconds and check next one."
_sleep 10
__purge_txt "$txtdomain"
if [ "$txtdomain" != "$aliasDomain" ]; then
__purge_txt "$aliasDomain"
fi
done
if [ "$_left" ]; then
_info "Let's wait 10 seconds and check again".
_sleep 10
else
_info "All success, let's return"
break
fi
done
}
#webroot, domain domainlist keylength #webroot, domain domainlist keylength
issue() { issue() {
if [ -z "$2" ]; then if [ -z "$2" ]; then
@ -3776,6 +3885,7 @@ $_authorizations_map"
done done
_debug vlist "$vlist" _debug vlist "$vlist"
#add entry #add entry
dns_entries=""
dnsadded="" dnsadded=""
ventries=$(echo "$vlist" | tr "$dvsep" ' ') ventries=$(echo "$vlist" | tr "$dvsep" ' ')
_alias_index=1 _alias_index=1
@ -3806,8 +3916,10 @@ $_authorizations_map"
else else
txtdomain="_acme-challenge.$_d_alias" txtdomain="_acme-challenge.$_d_alias"
fi fi
dns_entries="${dns_entries}${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot"
else else
txtdomain="_acme-challenge.$_dns_root_d" txtdomain="_acme-challenge.$_dns_root_d"
dns_entries="${dns_entries}${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot"
fi fi
_debug txtdomain "$txtdomain" _debug txtdomain "$txtdomain"
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
@ -3816,7 +3928,9 @@ $_authorizations_map"
d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")"
_debug d_api "$d_api" _debug d_api "$d_api"
dns_entries="$dns_entries$dvsep$txt${dvsep}$d_api
"
_debug2 "$dns_entries"
if [ "$d_api" ]; then if [ "$d_api" ]; then
_info "Found domain api file: $d_api" _info "Found domain api file: $d_api"
else else
@ -3870,15 +3984,21 @@ $_authorizations_map"
fi fi
if [ "$dnsadded" = '1' ]; then
if [ "$dns_entries" ]; then
if [ -z "$Le_DNSSleep" ]; then if [ -z "$Le_DNSSleep" ]; then
Le_DNSSleep="$DEFAULT_DNS_SLEEP"
_info "Let's check each dns records now. Sleep 20 seconds first."
_sleep 20
if ! _check_dns_entries; then
_err "check dns error."
_on_issue_err "$_post_hook"
_clearup
return 1
fi
else else
_savedomainconf "Le_DNSSleep" "$Le_DNSSleep" _savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
_info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect"
_sleep "$Le_DNSSleep"
fi fi
_info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect"
_sleep "$Le_DNSSleep"
fi fi
NGINX_RESTORE_VLIST="" NGINX_RESTORE_VLIST=""
@ -4099,28 +4219,74 @@ $_authorizations_map"
der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
if [ "$ACME_VERSION" = "2" ]; then if [ "$ACME_VERSION" = "2" ]; then
_info "Lets finalize the order, Le_OrderFinalize: $Le_OrderFinalize"
if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then
_err "Sign failed." _err "Sign failed."
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
if [ "$code" != "200" ]; then if [ "$code" != "200" ]; then
_err "Sign failed, code is not 200."
_err "Sign failed, finalize code is not 200."
_err "$response" _err "$response"
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
if [ -z "$Le_LinkOrder" ]; then
_err "Sign error, can not get order link location header"
_err "responseHeaders" "$responseHeaders"
_on_issue_err "$_post_hook"
return 1
fi
_savedomainconf "Le_LinkOrder" "$Le_LinkOrder"
_link_cert_retry=0
_MAX_CERT_RETRY=5
while [ -z "$Le_LinkCert" ] && [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do
if _contains "$response" "\"status\":\"valid\""; then
_debug "Order status is valid."
Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
_debug Le_LinkCert "$Le_LinkCert"
if [ -z "$Le_LinkCert" ]; then
_err "Sign error, can not find Le_LinkCert"
_err "$response"
_on_issue_err "$_post_hook"
return 1
fi
break
elif _contains "$response" "\"processing\""; then
_info "Order status is processing, lets sleep and retry."
_sleep 2
else
_err "Sign error, wrong status"
_err "$response"
_on_issue_err "$_post_hook"
return 1
fi
if ! _send_signed_request "$Le_LinkOrder"; then
_err "Sign failed, can not post to Le_LinkOrder cert:$Le_LinkOrder."
_err "$response"
_on_issue_err "$_post_hook"
return 1
fi
_link_cert_retry="$(_math $_link_cert_retry + 1)"
done
_tempSignedResponse="$response"
if ! _send_signed_request "$Le_LinkCert" "" "needbase64"; then
if [ -z "$Le_LinkCert" ]; then
_err "Sign failed, can not get Le_LinkCert, retry time limit."
_err "$response"
_on_issue_err "$_post_hook"
return 1
fi
_info "Download cert, Le_LinkCert: $Le_LinkCert"
if ! _send_signed_request "$Le_LinkCert"; 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"
echo "$response" >"$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"
@ -4131,7 +4297,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"
@ -4720,7 +4886,7 @@ _installcert() {
export CERT_KEY_PATH export CERT_KEY_PATH
export CA_CERT_PATH export CA_CERT_PATH
export CERT_FULLCHAIN_PATH export CERT_FULLCHAIN_PATH
export Le_Domain
export Le_Domain="$_main_domain"
cd "$DOMAIN_PATH" && eval "$_reload_cmd" cd "$DOMAIN_PATH" && eval "$_reload_cmd"
); then ); then
_info "$(__green "Reload success")" _info "$(__green "Reload success")"

18
deploy/README.md

@ -349,10 +349,10 @@ $ export QINIU_SK="bar"
$ acme.sh --deploy -d example.com --deploy-hook qiniu $ acme.sh --deploy -d example.com --deploy-hook qiniu
``` ```
假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名:
假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名(请注意泛域名前的点)
```sh ```sh
$ export QINIU_CDN_DOMAIN="cdn.example.com"
$ export QINIU_CDN_DOMAIN=".cdn.example.com"
$ acme.sh --deploy -d example.com --deploy-hook qiniu $ acme.sh --deploy -d example.com --deploy-hook qiniu
``` ```
@ -375,9 +375,19 @@ $ acme.sh --deploy -d example.com --deploy-hook qiniu
(Optional), If you are using wildcard certificate, (Optional), If you are using wildcard certificate,
you may need export `QINIU_CDN_DOMAIN` to specify which domain you may need export `QINIU_CDN_DOMAIN` to specify which domain
you want to update:
you want to update (please note the leading dot):
```sh ```sh
$ export QINIU_CDN_DOMAIN="cdn.example.com"
$ export QINIU_CDN_DOMAIN=".cdn.example.com"
$ acme.sh --deploy -d example.com --deploy-hook qiniu $ acme.sh --deploy -d example.com --deploy-hook qiniu
``` ```
## 14. Deploy your cert on MyDevil.net
Once you have acme.sh installed and certificate issued (see info in [DNS API](../dnsapi/README.md#61-use-mydevilnet)), you can install it by following command:
```sh
acme.sh --deploy --deploy-hook mydevil -d example.com
```
That will remove old certificate and install new one.

59
deploy/mydevil.sh

@ -0,0 +1,59 @@
#!/usr/bin/env sh
# MyDevil.net API (2019-02-03)
#
# MyDevil.net already supports automatic Let's Encrypt certificates,
# except for wildcard domains.
#
# This script depends on `devil` command that MyDevil.net provides,
# which means that it works only on server side.
#
# Author: Marcin Konicki <https://ahwayakchih.neoni.net>
#
######## Public functions #####################
# Usage: mydevil_deploy domain keyfile certfile cafile fullchain
mydevil_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
ip=""
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if ! _exists "devil"; then
_err "Could not find 'devil' command."
return 1
fi
ip=$(mydevil_get_ip "$_cdomain")
if [ -z "$ip" ]; then
_err "Could not find IP for domain $_cdomain."
return 1
fi
# Delete old certificate first
_info "Removing old certificate for $_cdomain at $ip"
devil ssl www del "$ip" "$_cdomain"
# Add new certificate
_info "Adding new certificate for $_cdomain at $ip"
devil ssl www add "$ip" "$_cfullchain" "$_ckey" "$_cdomain" || return 1
return 0
}
#################### Private functions below ##################################
# Usage: ip=$(mydevil_get_ip domain.com)
# echo $ip
mydevil_get_ip() {
devil dns list "$1" | cut -w -s -f 3,7 | grep "^A$(printf '\t')" | cut -w -s -f 2 || return 1
return 0
}

2
deploy/qiniu.sh

@ -87,6 +87,6 @@ qiniu_deploy() {
} }
_make_access_token() { _make_access_token() {
_token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64)"
_token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64 | tr -- '+/' '-_')"
echo "$QINIU_AK:$_token" echo "$QINIU_AK:$_token"
} }

115
dnsapi/README.md

@ -1,6 +1,6 @@
# How to use DNS API # How to use DNS API
If your dns provider doesn't provide api access, you can use our dns alias mode:
If your dns provider doesn't provide api access, you can use our dns alias mode:
https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode
@ -891,7 +891,7 @@ acme.sh --issue --dns dns_loopia -d example.com -d *.example.com
The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 45. Use ACME DNS API ## 45. Use ACME DNS API
ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely.
ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely.
https://github.com/joohoi/acme-dns https://github.com/joohoi/acme-dns
``` ```
@ -1011,7 +1011,6 @@ acme.sh --issue --dns dns_netcup -d example.com -d www.example.com
``` ```
The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 52. Use GratisDNS.dk ## 52. Use GratisDNS.dk
GratisDNS.dk (https://gratisdns.dk/) does not provide an API to update DNS records (other than IPv4 and IPv6 GratisDNS.dk (https://gratisdns.dk/) does not provide an API to update DNS records (other than IPv4 and IPv6
@ -1172,7 +1171,115 @@ 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. The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 61. Use NederHost API
## 61. Use Nexcess API
First, you'll need to login to the [Nexcess.net Client Portal](https://portal.nexcess.net) and [generate a new API token](https://portal.nexcess.net/api-token).
Once you have a token, set it in your systems environment:
```
export NW_API_TOKEN="YOUR_TOKEN_HERE"
export NW_API_ENDPOINT="https://portal.nexcess.net"
```
Finally, we'll issue the certificate: (Nexcess DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`)
```
acme.sh --issue --dns dns_nw -d example.com --dnssleep 900
```
The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 62. Use Thermo.io API
First, you'll need to login to the [Thermo.io Client Portal](https://core.thermo.io) and [generate a new API token](https://core.thermo.io/api-token).
Once you have a token, set it in your systems environment:
```
export NW_API_TOKEN="YOUR_TOKEN_HERE"
export NW_API_ENDPOINT="https://core.thermo.io"
```
Finally, we'll issue the certificate: (Thermo DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`)
```
acme.sh --issue --dns dns_nw -d example.com --dnssleep 900
```
The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 63. Use Futurehosting API
First, you'll need to login to the [Futurehosting Client Portal](https://my.futurehosting.com) and [generate a new API token](https://my.futurehosting.com/api-token).
Once you have a token, set it in your systems environment:
```
export NW_API_TOKEN="YOUR_TOKEN_HERE"
export NW_API_ENDPOINT="https://my.futurehosting.com"
```
Finally, we'll issue the certificate: (Futurehosting DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`)
```
acme.sh --issue --dns dns_nw -d example.com --dnssleep 900
```
The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 64. Use Rackspace API
Set username and API key, which is available under "My Profile & Settings"
```
export RACKSPACE_Username='username'
export RACKSPACE_Apikey='xxx'
```
Now, let's issue a cert:
```
acme.sh --issue --dns dns_rackspace -d example.com -d www.example.com
```
## 65. Use Online API
First, you'll need to retrive your API key, which is available under https://console.online.net/en/api/access
```
export ONLINE_API_KEY='xxx'
```
To issue a cert run:
```
acme.sh --issue --dns dns_online -d example.com -d www.example.com
```
`ONLINE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 66. Use MyDevil.net
Make sure that you can execute own binaries:
```sh
devil binexec on
```
Install acme.sh, or simply `git clone` it into some directory on your MyDevil host account (in which case you should link to it from your `~/bin` directory).
If you're not using private IP and depend on default IP provided by host, you may want to edit `crontab` too, and make sure that `acme.sh --cron` is run also after reboot (you can find out how to do that on their wiki pages).
To issue a new certificate, run:
```sh
acme.sh --issue --dns dns_mydevil -d example.com -d *.example.com
```
After certificate is ready, you can install it with [deploy command](../deploy/README.md#14-deploy-your-cert-on-mydevilnet).
## 67. Use NederHost API
Create an API token in Mijn NederHost. Create an API token in Mijn NederHost.

50
dnsapi/dns_hostingde.sh

@ -13,6 +13,7 @@ dns_hostingde_add() {
txtvalue="${2}" txtvalue="${2}"
_debug "Calling: _hostingde_addRecord() '${fulldomain}' '${txtvalue}'" _debug "Calling: _hostingde_addRecord() '${fulldomain}' '${txtvalue}'"
_hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_addRecord _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_addRecord
return $?
} }
dns_hostingde_rm() { dns_hostingde_rm() {
@ -20,6 +21,7 @@ dns_hostingde_rm() {
txtvalue="${2}" txtvalue="${2}"
_debug "Calling: _hostingde_removeRecord() '${fulldomain}' '${txtvalue}'" _debug "Calling: _hostingde_removeRecord() '${fulldomain}' '${txtvalue}'"
_hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_removeRecord _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_removeRecord
return $?
} }
#################### own Private functions below ################################## #################### own Private functions below ##################################
@ -38,6 +40,18 @@ _hostingde_apiKey() {
_saveaccountconf_mutable HOSTINGDE_ENDPOINT "$HOSTINGDE_ENDPOINT" _saveaccountconf_mutable HOSTINGDE_ENDPOINT "$HOSTINGDE_ENDPOINT"
} }
_hostingde_parse() {
find="${1}"
if [ "${2}" ]; then
notfind="${2}"
fi
if [ "${notfind}" ]; then
_egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d ' '
else
_egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d ' '
fi
}
_hostingde_getZoneConfig() { _hostingde_getZoneConfig() {
_info "Getting ZoneConfig" _info "Getting ZoneConfig"
curZone="${fulldomain#*.}" curZone="${fulldomain#*.}"
@ -59,18 +73,18 @@ _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}'"
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
zoneConfigId=$(echo "${curResult}" | _hostingde_parse "id")
zoneConfigName=$(echo "${curResult}" | _hostingde_parse "name")
zoneConfigType=$(echo "${curResult}" | _hostingde_parse "type" "FindZoneConfigsResult")
zoneConfigExpire=$(echo "${curResult}" | _hostingde_parse "expire")
zoneConfigNegativeTtl=$(echo "${curResult}" | _hostingde_parse "negativeTtl")
zoneConfigRefresh=$(echo "${curResult}" | _hostingde_parse "refresh")
zoneConfigRetry=$(echo "${curResult}" | _hostingde_parse "retry")
zoneConfigTtl=$(echo "${curResult}" | _hostingde_parse "ttl")
zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId")
zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress")
zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode")
if [ "${zoneConfigType}" != "\"NATIVE\"" ]; then
_err "Zone is not native" _err "Zone is not native"
returnCode=1 returnCode=1
break break
@ -89,11 +103,11 @@ _hostingde_getZoneConfig() {
_hostingde_getZoneStatus() { _hostingde_getZoneStatus() {
_debug "Checking Zone status" _debug "Checking Zone status"
curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":\"${zoneConfigId}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}"
curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":${zoneConfigId}},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind")" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind")"
_debug "Calling zonesFind '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind'" _debug "Calling zonesFind '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind'"
_debug "Result of zonesFind '$curResult'" _debug "Result of zonesFind '$curResult'"
zoneStatus=$(echo "${curResult}" | grep -v success | _egrep_o '"status":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneStatus=$(echo "${curResult}" | _hostingde_parse "status" "success")
_debug "zoneStatus '${zoneStatus}'" _debug "zoneStatus '${zoneStatus}'"
return 0 return 0
} }
@ -102,12 +116,12 @@ _hostingde_addRecord() {
_info "Adding record to zone" _info "Adding record to zone"
_hostingde_getZoneStatus _hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'" _debug "Result of zoneStatus: '${zoneStatus}'"
while [ "${zoneStatus}" != "active" ]; do
while [ "${zoneStatus}" != "\"active\"" ]; do
_sleep 5 _sleep 5
_hostingde_getZoneStatus _hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'" _debug "Result of zoneStatus: '${zoneStatus}'"
done 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}]}"
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'"
@ -126,12 +140,12 @@ _hostingde_removeRecord() {
_info "Removing record from zone" _info "Removing record from zone"
_hostingde_getZoneStatus _hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'" _debug "Result of zoneStatus: '$zoneStatus'"
while [ "$zoneStatus" != "active" ]; do
while [ "$zoneStatus" != "\"active\"" ]; do
_sleep 5 _sleep 5
_hostingde_getZoneStatus _hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'" _debug "Result of zoneStatus: '$zoneStatus'"
done 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}\\\"\"}]}"
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'"

97
dnsapi/dns_mydevil.sh

@ -0,0 +1,97 @@
#!/usr/bin/env sh
# MyDevil.net API (2019-02-03)
#
# MyDevil.net already supports automatic Let's Encrypt certificates,
# except for wildcard domains.
#
# This script depends on `devil` command that MyDevil.net provides,
# which means that it works only on server side.
#
# Author: Marcin Konicki <https://ahwayakchih.neoni.net>
#
######## Public functions #####################
#Usage: dns_mydevil_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_mydevil_add() {
fulldomain=$1
txtvalue=$2
domain=""
if ! _exists "devil"; then
_err "Could not find 'devil' command."
return 1
fi
_info "Using mydevil"
domain=$(mydevil_get_domain "$fulldomain")
if [ -z "$domain" ]; then
_err "Invalid domain name: could not find root domain of $fulldomain."
return 1
fi
# No need to check if record name exists, `devil` always adds new record.
# In worst case scenario, we end up with multiple identical records.
_info "Adding $fulldomain record for domain $domain"
if devil dns add "$domain" "$fulldomain" TXT "$txtvalue"; then
_info "Successfully added TXT record, ready for validation."
return 0
else
_err "Unable to add DNS record."
return 1
fi
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_mydevil_rm() {
fulldomain=$1
txtvalue=$2
domain=""
if ! _exists "devil"; then
_err "Could not find 'devil' command."
return 1
fi
_info "Using mydevil"
domain=$(mydevil_get_domain "$fulldomain")
if [ -z "$domain" ]; then
_err "Invalid domain name: could not find root domain of $fulldomain."
return 1
fi
# catch one or more numbers
num='[0-9][0-9]*'
# catch one or more whitespace
w=$(printf '[\t ][\t ]*')
# catch anything, except newline
any='.*'
# filter to make sure we do not delete other records
validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$"
for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do
_info "Removing record $id from domain $domain"
devil dns del "$domain" "$id" || _err "Could not remove DNS record."
done
}
#################### Private functions below ##################################
# Usage: domain=$(mydevil_get_domain "_acme-challenge.www.domain.com" || _err "Invalid domain name")
# echo $domain
mydevil_get_domain() {
fulldomain=$1
domain=""
for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do
if _endswith "$fulldomain" "$domain"; then
printf -- "%s" "$domain"
return 0
fi
done
return 1
}

45
dnsapi/dns_namecheap.sh

@ -76,6 +76,22 @@ dns_namecheap_rm() {
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
# _domain=domain.com # _domain=domain.com
_get_root() { _get_root() {
fulldomain=$1
if ! _get_root_by_getList "$fulldomain"; then
_debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api."
# The above "getList" api will only return hosts *owned* by the calling user. However, if the calling
# user is not the owner, but still has administrative rights, we must query the getHosts api directly.
# See this comment and the official namecheap response: http://disq.us/p/1q6v9x9
if ! _get_root_by_getHosts "$fulldomain"; then
return 1
fi
fi
return 0
}
_get_root_by_getList() {
domain=$1 domain=$1
if ! _namecheap_post "namecheap.domains.getList"; then if ! _namecheap_post "namecheap.domains.getList"; then
@ -94,6 +110,10 @@ _get_root() {
#not valid #not valid
return 1 return 1
fi fi
if ! _contains "$h" "\\."; then
#not valid
return 1
fi
if ! _contains "$response" "$h"; then if ! _contains "$response" "$h"; then
_debug "$h not found" _debug "$h not found"
@ -108,6 +128,31 @@ _get_root() {
return 1 return 1
} }
_get_root_by_getHosts() {
i=100
p=99
while [ $p -ne 0 ]; do
h=$(printf "%s" "$1" | cut -d . -f $i-100)
if [ -n "$h" ]; then
if _contains "$h" "\\."; then
_debug h "$h"
if _namecheap_set_tld_sld "$h"; then
_sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p)
_domain="$h"
return 0
else
_debug "$h not found"
fi
fi
fi
i="$p"
p=$(_math "$p" - 1)
done
return 1
}
_namecheap_set_publicip() { _namecheap_set_publicip() {
if [ -z "$NAMECHEAP_SOURCEIP" ]; then if [ -z "$NAMECHEAP_SOURCEIP" ]; then

4
dnsapi/dns_nsone.sh

@ -46,7 +46,7 @@ dns_nsone_add() {
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; then
if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\",\"ttl\":0}"; then
if _contains "$response" "$fulldomain"; then if _contains "$response" "$fulldomain"; then
_info "Added" _info "Added"
#todo: check if the record takes effect #todo: check if the record takes effect
@ -62,7 +62,7 @@ dns_nsone_add() {
prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1) prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1)
_debug "prev_txt" "$prev_txt" _debug "prev_txt" "$prev_txt"
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}"
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\",\"ttl\":0}"
if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then
_info "Updated!" _info "Updated!"
#todo: check if the record takes effect #todo: check if the record takes effect

211
dnsapi/dns_nw.sh

@ -0,0 +1,211 @@
#!/usr/bin/env sh
########################################################################
# NocWorx script for acme.sh
#
# Handles DNS Updates for the Following vendors:
# - Nexcess.net
# - Thermo.io
# - Futurehosting.com
#
# Environment variables:
#
# - NW_API_TOKEN (Your API Token)
# - NW_API_ENDPOINT (One of the following listed below)
#
# Endpoints:
# - https://portal.nexcess.net (default)
# - https://core.thermo.io
# - https://my.futurehosting.com
#
# Note: If you do not have an API token, one can be generated at one
# of the following URLs:
# - https://portal.nexcess.net/api-token
# - https://core.thermo.io/api-token
# - https://my.futurehosting.com/api-token
#
# Author: Frank Laszlo <flaszlo@nexcess.net>
NW_API_VERSION="0"
# dns_nw_add() - Add TXT record
# Usage: dns_nw_add _acme-challenge.subdomain.domain.com "XyZ123..."
dns_nw_add() {
host="${1}"
txtvalue="${2}"
_debug host "${host}"
_debug txtvalue "${txtvalue}"
if ! _check_nw_api_creds; then
return 1
fi
_info "Using NocWorx (${NW_API_ENDPOINT})"
_debug "Calling: dns_nw_add() '${host}' '${txtvalue}'"
_debug "Detecting root zone"
if ! _get_root "${host}"; then
_err "Zone for domain does not exist."
return 1
fi
_debug _zone_id "${_zone_id}"
_debug _sub_domain "${_sub_domain}"
_debug _domain "${_domain}"
_post_data="{\"zone_id\": \"${_zone_id}\", \"type\": \"TXT\", \"host\": \"${host}\", \"target\": \"${txtvalue}\", \"ttl\": \"300\"}"
if _rest POST "dns-record" "${_post_data}" && [ -n "${response}" ]; then
_record_id=$(printf "%s\n" "${response}" | _egrep_o "\"record_id\": *[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
_debug _record_id "${_record_id}"
if [ -z "$_record_id" ]; then
_err "Error adding the TXT record."
return 1
fi
_info "TXT record successfully added."
return 0
fi
return 1
}
# dns_nw_rm() - Remove TXT record
# Usage: dns_nw_rm _acme-challenge.subdomain.domain.com "XyZ123..."
dns_nw_rm() {
host="${1}"
txtvalue="${2}"
_debug host "${host}"
_debug txtvalue "${txtvalue}"
if ! _check_nw_api_creds; then
return 1
fi
_info "Using NocWorx (${NW_API_ENDPOINT})"
_debug "Calling: dns_nw_rm() '${host}'"
_debug "Detecting root zone"
if ! _get_root "${host}"; then
_err "Zone for domain does not exist."
return 1
fi
_debug _zone_id "${_zone_id}"
_debug _sub_domain "${_sub_domain}"
_debug _domain "${_domain}"
_parameters="?zone_id=${_zone_id}"
if _rest GET "dns-record" "${_parameters}" && [ -n "${response}" ]; then
response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"record_id":/|"record_id":/g' | sed 's/|/&{/g' | tr "|" "\n")"
_debug response "${response}"
record="$(echo "${response}" | _egrep_o "{.*\"host\": *\"${_sub_domain}\", *\"target\": *\"${txtvalue}\".*}")"
_debug record "${record}"
if [ "${record}" ]; then
_record_id=$(printf "%s\n" "${record}" | _egrep_o "\"record_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "${_record_id}" ]; then
_debug _record_id "${_record_id}"
_rest DELETE "dns-record/${_record_id}"
_info "TXT record successfully deleted."
return 0
fi
return 1
fi
return 0
fi
return 1
}
_check_nw_api_creds() {
NW_API_TOKEN="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}"
NW_API_ENDPOINT="${NW_API_ENDPOINT:-$(_readaccountconf_mutable NW_API_ENDPOINT)}"
if [ -z "${NW_API_ENDPOINT}" ]; then
NW_API_ENDPOINT="https://portal.nexcess.net"
fi
if [ -z "${NW_API_TOKEN}" ]; then
_err "You have not defined your NW_API_TOKEN."
_err "Please create your token and try again."
_err "If you need to generate a new token, please visit one of the following URLs:"
_err " - https://portal.nexcess.net/api-token"
_err " - https://core.thermo.io/api-token"
_err " - https://my.futurehosting.com/api-token"
return 1
fi
_saveaccountconf_mutable NW_API_TOKEN "${NW_API_TOKEN}"
_saveaccountconf_mutable NW_API_ENDPOINT "${NW_API_ENDPOINT}"
}
_get_root() {
domain="${1}"
i=2
p=1
if _rest GET "dns-zone"; then
response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"zone_id":/|"zone_id":/g' | sed 's/|/&{/g' | tr "|" "\n")"
_debug response "${response}"
while true; do
h=$(printf "%s" "${domain}" | cut -d . -f $i-100)
_debug h "${h}"
if [ -z "${h}" ]; then
#not valid
return 1
fi
hostedzone="$(echo "${response}" | _egrep_o "{.*\"domain\": *\"${h}\".*}")"
if [ "${hostedzone}" ]; then
_zone_id=$(printf "%s\n" "${hostedzone}" | _egrep_o "\"zone_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "${_zone_id}" ]; then
_sub_domain=$(printf "%s" "${domain}" | cut -d . -f 1-${p})
_domain="${h}"
return 0
fi
return 1
fi
p=$i
i=$(_math "${i}" + 1)
done
fi
return 1
}
_rest() {
method="${1}"
ep="/${2}"
data="${3}"
_debug method "${method}"
_debug ep "${ep}"
export _H1="Accept: application/json"
export _H2="Content-Type: application/json"
export _H3="Api-Version: ${NW_API_VERSION}"
export _H4="User-Agent: NW-ACME-CLIENT"
export _H5="Authorization: Bearer ${NW_API_TOKEN}"
if [ "${method}" != "GET" ]; then
_debug data "${data}"
response="$(_post "${data}" "${NW_API_ENDPOINT}${ep}" "" "${method}")"
else
response="$(_get "${NW_API_ENDPOINT}${ep}${data}")"
fi
if [ "${?}" != "0" ]; then
_err "error ${ep}"
return 1
fi
_debug2 response "${response}"
return 0
}

217
dnsapi/dns_online.sh

@ -0,0 +1,217 @@
#!/usr/bin/env sh
# Online API
# https://console.online.net/en/api/
#
# Requires Online API key set in ONLINE_API_KEY
######## Public functions #####################
ONLINE_API="https://api.online.net/api/v1"
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_online_add() {
fulldomain=$1
txtvalue=$2
if ! _online_check_config; then
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 _real_dns_version "$_real_dns_version"
_info "Creating temporary zone version"
_online_create_temporary_zone_version
_info "Enabling temporary zone version"
_online_enable_zone "$_temporary_dns_version"
_info "Adding record"
_online_create_TXT_record "$_real_dns_version" "$_sub_domain" "$txtvalue"
_info "Disabling temporary version"
_online_enable_zone "$_real_dns_version"
_info "Destroying temporary version"
_online_destroy_zone "$_temporary_dns_version"
_info "Record added."
return 0
}
#fulldomain
dns_online_rm() {
fulldomain=$1
txtvalue=$2
if ! _online_check_config; then
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 _real_dns_version "$_real_dns_version"
_debug "Getting txt records"
if ! _online_rest GET "domain/$_domain/version/active"; then
return 1
fi
rid=$(echo "$response" | _egrep_o "\"id\":[0-9]+,\"name\":\"$_sub_domain\",\"data\":\"\\\u0022$txtvalue\\\u0022\"" | cut -d ':' -f 2 | cut -d ',' -f 1)
_debug rid "$rid"
if [ -z "$rid" ]; then
return 1
fi
_info "Creating temporary zone version"
_online_create_temporary_zone_version
_info "Enabling temporary zone version"
_online_enable_zone "$_temporary_dns_version"
_info "Removing DNS record"
_online_rest DELETE "domain/$_domain/version/$_real_dns_version/zone/$rid"
_info "Disabling temporary version"
_online_enable_zone "$_real_dns_version"
_info "Destroying temporary version"
_online_destroy_zone "$_temporary_dns_version"
return 0
}
#################### Private functions below ##################################
_online_check_config() {
ONLINE_API_KEY="${ONLINE_API_KEY:-$(_readaccountconf_mutable ONLINE_API_KEY)}"
if [ -z "$ONLINE_API_KEY" ]; then
_err "No API key specified for Online API."
_err "Create your key and export it as ONLINE_API_KEY"
return 1
fi
if ! _online_rest GET "domain/"; then
_err "Invalid API key specified for Online API."
return 1
fi
_saveaccountconf_mutable ONLINE_API_KEY "$ONLINE_API_KEY"
return 0
}
#_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)
if [ -z "$h" ]; then
#not valid
return 1
fi
_online_rest GET "domain/$h/version/active"
if ! _contains "$response" "Domain not found" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
_real_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Unable to retrive DNS zone matching this domain"
return 1
}
# this function create a temporary zone version
# as online.net does not allow updating an active version
_online_create_temporary_zone_version() {
_online_rest POST "domain/$_domain/version" "name=acme.sh"
if [ "$?" != "0" ]; then
return 1
fi
_temporary_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
# Creating a dummy record in this temporary version, because online.net doesn't accept enabling an empty version
_online_create_TXT_record "$_temporary_dns_version" "dummy.acme.sh" "dummy"
return 0
}
_online_destroy_zone() {
version_id=$1
_online_rest DELETE "domain/$_domain/version/$version_id"
if [ "$?" != "0" ]; then
return 1
fi
return 0
}
_online_enable_zone() {
version_id=$1
_online_rest PATCH "domain/$_domain/version/$version_id/enable"
if [ "$?" != "0" ]; then
return 1
fi
return 0
}
_online_create_TXT_record() {
version=$1
txt_name=$2
txt_value=$3
_online_rest POST "domain/$_domain/version/$version/zone" "type=TXT&name=$txt_name&data=%22$txt_value%22&ttl=60&priority=0"
# Note : the normal, expected response SHOULD be "Unknown method".
# this happens because the API HTTP response contains a Location: header, that redirect
# to an unknown online.net endpoint.
if [ "$?" != "0" ] || _contains "$response" "Unknown method" || _contains "$response" "\$ref"; then
return 0
else
_err "error $response"
return 1
fi
}
_online_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_online_url="$ONLINE_API/$ep"
_debug2 _online_url "$_online_url"
export _H1="Authorization: Bearer $ONLINE_API_KEY"
export _H2="X-Pretty-JSON: 1"
if [ "$data" ] || [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$_online_url" "" "$m")"
else
response="$(_get "$_online_url")"
fi
if [ "$?" != "0" ] || _contains "$response" "invalid_grant" || _contains "$response" "Method not allowed"; then
_err "error $response"
return 1
fi
_debug2 response "$response"
return 0
}

207
dnsapi/dns_rackspace.sh

@ -0,0 +1,207 @@
#!/usr/bin/env sh
#
#
#RACKSPACE_Username=""
#
#RACKSPACE_Apikey=""
RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0"
# 20190213 - The name & id fields swapped in the API response; fix sed
# 20190101 - Duplicating file for new pull request to dev branch
# Original - tcocca:rackspace_dnsapi https://github.com/Neilpang/acme.sh/pull/1297
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_rackspace_add() {
fulldomain="$1"
_debug fulldomain="$fulldomain"
txtvalue="$2"
_debug txtvalue="$txtvalue"
_rackspace_check_auth || return 1
_rackspace_check_rootzone || return 1
_info "Creating TXT record."
if ! _rackspace_rest POST "$RACKSPACE_Tenant/domains/$_domain_id/records" "{\"records\":[{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":300}]}"; then
return 1
fi
_debug2 response "$response"
if ! _contains "$response" "$txtvalue" >/dev/null; then
_err "Could not add TXT record."
return 1
fi
return 0
}
#fulldomain txtvalue
dns_rackspace_rm() {
fulldomain=$1
_debug fulldomain="$fulldomain"
txtvalue=$2
_debug txtvalue="$txtvalue"
_rackspace_check_auth || return 1
_rackspace_check_rootzone || return 1
_info "Checking for TXT record."
if ! _get_recordid "$_domain_id" "$fulldomain" "$txtvalue"; then
_err "Could not get TXT record id."
return 1
fi
if [ "$_dns_record_id" = "" ]; then
_err "TXT record not found."
return 1
fi
_info "Removing TXT record."
if ! _delete_txt_record "$_domain_id" "$_dns_record_id"; then
_err "Could not remove TXT record $_dns_record_id."
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root_zone() {
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 ! _rackspace_rest GET "$RACKSPACE_Tenant/domains"; then
return 1
fi
_debug2 response "$response"
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
# Response looks like:
# {"ttl":300,"accountId":12345,"id":1111111,"name":"example.com","emailAddress": ...<and so on>
_domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\([^,]*\),\"name\":\"$h\",.*/\1/p")
_debug2 domain_id "$_domain_id"
if [ -n "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_get_recordid() {
domainid="$1"
fulldomain="$2"
txtvalue="$3"
if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains/$domainid/records?name=$fulldomain&type=TXT"; then
return 1
fi
_debug response "$response"
if ! _contains "$response" "$txtvalue"; then
_dns_record_id=0
return 0
fi
_dns_record_id=$(echo "$response" | tr '{' "\n" | grep "\"data\":\"$txtvalue\"" | sed -n 's/^.*"id":"\([^"]*\)".*/\1/p')
_debug _dns_record_id "$_dns_record_id"
return 0
}
_delete_txt_record() {
domainid="$1"
_dns_record_id="$2"
if ! _rackspace_rest DELETE "$RACKSPACE_Tenant/domains/$domainid/records?id=$_dns_record_id"; then
return 1
fi
_debug response "$response"
if ! _contains "$response" "RUNNING"; then
return 1
fi
return 0
}
_rackspace_rest() {
m="$1"
ep="$2"
data="$3"
_debug ep "$ep"
export _H1="Accept: application/json"
export _H2="X-Auth-Token: $RACKSPACE_Token"
export _H3="X-Project-Id: $RACKSPACE_Tenant"
export _H4="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$RACKSPACE_Endpoint/$ep" "" "$m")"
retcode=$?
else
_info "Getting $RACKSPACE_Endpoint/$ep"
response="$(_get "$RACKSPACE_Endpoint/$ep")"
retcode=$?
fi
if [ "$retcode" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_rackspace_authorization() {
export _H1="Content-Type: application/json"
data="{\"auth\":{\"RAX-KSKEY:apiKeyCredentials\":{\"username\":\"$RACKSPACE_Username\",\"apiKey\":\"$RACKSPACE_Apikey\"}}}"
_debug data "$data"
response="$(_post "$data" "https://identity.api.rackspacecloud.com/v2.0/tokens" "" "POST")"
retcode=$?
_debug2 response "$response"
if [ "$retcode" != "0" ]; then
_err "Authentication failed."
return 1
fi
if _contains "$response" "token"; then
RACKSPACE_Token="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)",".*/\1/p')"
RACKSPACE_Tenant="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)"}.*/\1/p')"
_debug RACKSPACE_Token "$RACKSPACE_Token"
_debug RACKSPACE_Tenant "$RACKSPACE_Tenant"
fi
return 0
}
_rackspace_check_auth() {
# retrieve the rackspace creds
RACKSPACE_Username="${RACKSPACE_Username:-$(_readaccountconf_mutable RACKSPACE_Username)}"
RACKSPACE_Apikey="${RACKSPACE_Apikey:-$(_readaccountconf_mutable RACKSPACE_Apikey)}"
# check their vals for null
if [ -z "$RACKSPACE_Username" ] || [ -z "$RACKSPACE_Apikey" ]; then
RACKSPACE_Username=""
RACKSPACE_Apikey=""
_err "You didn't specify a Rackspace username and api key."
_err "Please set those values and try again."
return 1
fi
# save the username and api key to the account conf file.
_saveaccountconf_mutable RACKSPACE_Username "$RACKSPACE_Username"
_saveaccountconf_mutable RACKSPACE_Apikey "$RACKSPACE_Apikey"
if [ -z "$RACKSPACE_Token" ]; then
_info "Getting authorization token."
if ! _rackspace_authorization; then
_err "Can not get token."
fi
fi
}
_rackspace_check_rootzone() {
_debug "First detect the root zone"
if ! _get_root_zone "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
}
Loading…
Cancel
Save