diff --git a/README.md b/README.md index 4ec8ffd9..65b83e71 100644 --- a/README.md +++ b/README.md @@ -349,6 +349,8 @@ You don't have to do anything manually! 1. Neodigit.net API (https://www.neodigit.net) 1. Exoscale.com API (https://www.exoscale.com/) 1. PointDNS API (https://pointhq.com/) +1. Active24.cz API (https://www.active24.cz/) +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) diff --git a/acme.sh b/acme.sh index d651c4f5..85c17f16 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.0 +VER=2.8.1 PROJECT_NAME="acme.sh" diff --git a/deploy/README.md b/deploy/README.md index cec7d773..091e9feb 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -332,3 +332,52 @@ variable to anything (ex: "1") before running `acme.sh`: ```sh 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 +``` diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh new file mode 100644 index 00000000..158b8dbf --- /dev/null +++ b/deploy/qiniu.sh @@ -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" +} diff --git a/dnsapi/README.md b/dnsapi/README.md index 82734f12..a9b78ef8 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1138,59 +1138,95 @@ You can then issue certs by using: ```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com ``` -## 59. Use Nexcess API +## 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 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 NEXCESS_API_TOKEN="YOUR_TOKEN_HERE" +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_nexcess -d example.com --dnssleep 900 +acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 ``` -The `NEXCESS_API_TOKEN will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -## 60. Use Thermo.io API +## 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 THERMO_API_TOKEN="YOUR_TOKEN_HERE" +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_thermo -d example.com --dnssleep 900 +acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 ``` -The `THERMO_API_TOKEN will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -## 61. Use Futurehosting API +## 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 FH_API_TOKEN="YOUR_TOKEN_HERE" +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_fh -d example.com --dnssleep 900 +acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 ``` -The `FH_API_TOKEN will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. # Use custom API diff --git a/dnsapi/dns_active24.sh b/dnsapi/dns_active24.sh new file mode 100755 index 00000000..90ffaf68 --- /dev/null +++ b/dnsapi/dns_active24.sh @@ -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" +} diff --git a/dnsapi/dns_dnsimple.sh b/dnsapi/dns_dnsimple.sh index 0dd3918a..d831eb2b 100644 --- a/dnsapi/dns_dnsimple.sh +++ b/dnsapi/dns_dnsimple.sh @@ -152,7 +152,7 @@ _get_records() { sub_domain=$3 _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 _err "failed to retrieve records" diff --git a/dnsapi/dns_doapi.sh b/dnsapi/dns_doapi.sh new file mode 100755 index 00000000..135f0b03 --- /dev/null +++ b/dnsapi/dns_doapi.sh @@ -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 +} diff --git a/dnsapi/dns_fh.sh b/dnsapi/dns_fh.sh deleted file mode 100644 index d346d044..00000000 --- a/dnsapi/dns_fh.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env sh -######################################################################## -# Futurehosting script for acme.sh -# -# Environment variables: -# -# - FH_API_TOKEN (your Futurehosting API Token) -# Note: If you do not have an API token, one can be generated at: -# https://my.futurehosting.com/api-token -# -# Author: Frank Laszlo - -FH_API_URL="https://my.futurehosting.com/" -FH_API_VERSION="0" - -# dns_fh_add() - Add TXT record -# Usage: dns_fh_add _acme-challenge.subdomain.domain.com "XyZ123..." -dns_fh_add() { - host="${1}" - txtvalue="${2}" - - _debug host "${host}" - _debug txtvalue "${txtvalue}" - - if ! _check_fh_api_token; then - return 1 - fi - - _info "Using Futurehosting" - _debug "Calling: dns_fh_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_fh_rm() - Remove TXT record -# Usage: dns_fh_rm _acme-challenge.subdomain.domain.com "XyZ123..." -dns_fh_rm() { - host="${1}" - txtvalue="${2}" - - _debug host "${host}" - _debug txtvalue "${txtvalue}" - - if ! _check_fh_api_token; then - return 1 - fi - - _info "Using Futurehosting" - _debug "Calling: dns_fh_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_fh_api_token() { - if [ -z "${FH_API_TOKEN}" ]; then - FH_API_TOKEN="${FH_API_TOKEN:-$(_readaccountconf_mutable FH_API_TOKEN)}" - - _err "You have not defined your FH_API_TOKEN." - _err "Please create your token and try again." - _err "If you need to generate a new token, please visit:" - _err "https://portal.fh.net/api-token" - - return 1 - fi - - _saveaccountconf_mutable FH_API_TOKEN "${FH_API_TOKEN}" -} - -_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: ${FH_API_VERSION}" - export _H4="User-Agent: FH-ACME-CLIENT" - export _H5="Authorization: Bearer ${FH_API_TOKEN}" - - if [ "${method}" != "GET" ]; then - _debug data "${data}" - response="$(_post "${data}" "${FH_API_URL}${ep}" "" "${method}")" - else - response="$(_get "${FH_API_URL}${ep}${data}")" - fi - - if [ "${?}" != "0" ]; then - _err "error ${ep}" - return 1 - fi - _debug2 response "${response}" - return 0 -} diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 74a472d2..b61acb7a 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -59,9 +59,22 @@ _hostingde_getZoneConfig() { if _contains "${curResult}" '"totalEntries": 1'; then _info "Retrieved zone data." _debug "Zone data: '${curResult}'" - - # read ZoneConfigId for later update 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}'" returnCode=0 break @@ -94,7 +107,7 @@ _hostingde_addRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"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")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" @@ -118,7 +131,7 @@ _hostingde_removeRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"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")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" diff --git a/dnsapi/dns_nexcess.sh b/dnsapi/dns_nw.sh similarity index 64% rename from dnsapi/dns_nexcess.sh rename to dnsapi/dns_nw.sh index 78c794ee..b152fa13 100644 --- a/dnsapi/dns_nexcess.sh +++ b/dnsapi/dns_nw.sh @@ -1,33 +1,47 @@ #!/usr/bin/env sh ######################################################################## -# Nexcess script for acme.sh +# NocWorx script for acme.sh +# +# Handles DNS Updates for the Following vendors: +# - Nexcess.net +# - Thermo.io +# - Futurehosting.com # # Environment variables: # -# - NEXCESS_API_TOKEN (your Nexcess API Token) -# Note: If you do not have an API token, one can be generated at: -# https://portal.nexcess.net/api-token +# - 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 -NEXCESS_API_URL="https://portal.nexcess.net/" -NEXCESS_API_VERSION="0" +NW_API_VERSION="0" -# dns_nexcess_add() - Add TXT record -# Usage: dns_nexcess_add _acme-challenge.subdomain.domain.com "XyZ123..." -dns_nexcess_add() { +# 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_nexcess_api_token; then + if ! _check_nw_api_creds; then return 1 fi - _info "Using Nexcess" - _debug "Calling: dns_nexcess_add() '${host}' '${txtvalue}'" + _info "Using NocWorx (${NW_API_ENDPOINT})" + _debug "Calling: dns_nw_add() '${host}' '${txtvalue}'" _debug "Detecting root zone" if ! _get_root "${host}"; then @@ -56,21 +70,21 @@ dns_nexcess_add() { return 1 } -# dns_nexcess_rm() - Remove TXT record -# Usage: dns_nexcess_rm _acme-challenge.subdomain.domain.com "XyZ123..." -dns_nexcess_rm() { +# 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_nexcess_api_token; then + if ! _check_nw_api_creds; then return 1 fi - _info "Using Nexcess" - _debug "Calling: dns_nexcess_rm() '${host}'" + _info "Using NocWorx (${NW_API_ENDPOINT})" + _debug "Calling: dns_nw_rm() '${host}'" _debug "Detecting root zone" if ! _get_root "${host}"; then @@ -110,19 +124,27 @@ dns_nexcess_rm() { return 1 } -_check_nexcess_api_token() { - if [ -z "${NEXCESS_API_TOKEN}" ]; then - NEXCESS_API_TOKEN="${NEXCESS_API_TOKEN:-$(_readaccountconf_mutable NEXCESS_API_TOKEN)}" +_check_nw_api_creds() { + NW_API_TOKEN="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}" + NW_API_ENDPOINT="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}" - _err "You have not defined your NEXCESS_API_TOKEN." + 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:" - _err "https://portal.nexcess.net/api-token" + _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 NEXCESS_API_TOKEN "${NEXCESS_API_TOKEN}" + _saveaccountconf_mutable NW_API_TOKEN "${NW_API_TOKEN}" + _saveaccountconf_mutable NW_API_ENDPOINT "${NW_API_ENDPOINT}" } _get_root() { @@ -169,15 +191,15 @@ _rest() { export _H1="Accept: application/json" export _H2="Content-Type: application/json" - export _H3="Api-Version: ${NEXCESS_API_VERSION}" - export _H4="User-Agent: NEXCESS-ACME-CLIENT" - export _H5="Authorization: Bearer ${NEXCESS_API_TOKEN}" + 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}" "${NEXCESS_API_URL}${ep}" "" "${method}")" + response="$(_post "${data}" "${NW_API_ENDPOINT}${ep}" "" "${method}")" else - response="$(_get "${NEXCESS_API_URL}${ep}${data}")" + response="$(_get "${NW_API_ENDPOINT}${ep}${data}")" fi if [ "${?}" != "0" ]; then diff --git a/dnsapi/dns_thermo.sh b/dnsapi/dns_thermo.sh deleted file mode 100644 index 149d13f6..00000000 --- a/dnsapi/dns_thermo.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env sh -######################################################################## -# Thermo.io script for acme.sh -# -# Environment variables: -# -# - THERMO_API_TOKEN (Your Thermo.io API Token) -# Note: If you do not have an API token, one can be generated at: -# https://core.thermo.io//api-token -# -# Author: Frank Laszlo - -THERMO_API_URL="https://core.thermo.io/" -THERMO_API_VERSION="0" - -# dns_thermo_add() - Add TXT record -# Usage: dns_thermo_add _acme-challenge.subdomain.domain.com "XyZ123..." -dns_thermo_add() { - host="${1}" - txtvalue="${2}" - - _debug host "${host}" - _debug txtvalue "${txtvalue}" - - if ! _check_thermo_api_token; then - return 1 - fi - - _info "Using Thermo.io" - _debug "Calling: dns_thermo_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_thermo_rm() - Remove TXT record -# Usage: dns_thermo_rm _acme-challenge.subdomain.domain.com "XyZ123..." -dns_thermo_rm() { - host="${1}" - txtvalue="${2}" - - _debug host "${host}" - _debug txtvalue "${txtvalue}" - - if ! _check_thermo_api_token; then - return 1 - fi - - _info "Using Thermo.io" - _debug "Calling: dns_thermo_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_thermo_api_token() { - if [ -z "${THERMO_API_TOKEN}" ]; then - THERMO_API_TOKEN="${THERMO_API_TOKEN:-$(_readaccountconf_mutable THERMO_API_TOKEN)}" - - _err "You have not defined your THERMO_API_TOKEN." - _err "Please create your token and try again." - _err "If you need to generate a new token, please visit:" - _err "https://portal.thermo.net/api-token" - - return 1 - fi - - _saveaccountconf_mutable THERMO_API_TOKEN "${THERMO_API_TOKEN}" -} - -_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: ${THERMO_API_VERSION}" - export _H4="User-Agent: THERMO-ACME-CLIENT" - export _H5="Authorization: Bearer ${THERMO_API_TOKEN}" - - if [ "${method}" != "GET" ]; then - _debug data "${data}" - response="$(_post "${data}" "${THERMO_API_URL}${ep}" "" "${method}")" - else - response="$(_get "${THERMO_API_URL}${ep}${data}")" - fi - - if [ "${?}" != "0" ]; then - _err "error ${ep}" - return 1 - fi - _debug2 response "${response}" - return 0 -}