Merge remote-tracking branch 'upstream/dev' into dns_nexcess_thermo_fh

This commit is contained in:
Frank Laszlo 2019-01-25 12:44:14 -05:00
commit 022ee61faf
12 changed files with 462 additions and 426 deletions

View File

@ -349,6 +349,8 @@ You don't have to do anything manually!
1. Neodigit.net API (https://www.neodigit.net) 1. Neodigit.net API (https://www.neodigit.net)
1. Exoscale.com API (https://www.exoscale.com/) 1. Exoscale.com API (https://www.exoscale.com/)
1. PointDNS API (https://pointhq.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. Nexcess API (https://www.nexcess.net)
1. Thermo.io API (https://www.thermo.io) 1. Thermo.io API (https://www.thermo.io)
1. Futurehosting API (https://www.futurehosting.com) 1. Futurehosting API (https://www.futurehosting.com)

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.8.0 VER=2.8.1
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"

View File

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

92
deploy/qiniu.sh Normal file
View File

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

View File

@ -1138,59 +1138,95 @@ You can then issue certs by using:
```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com ```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). 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: 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`) 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). 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: 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`) 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). 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: 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`) 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 # Use custom API

141
dnsapi/dns_active24.sh Executable file
View File

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

View File

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

59
dnsapi/dns_doapi.sh Executable file
View File

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

View File

@ -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 <flaszlo@nexcess.net>
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
}

View File

@ -59,9 +59,22 @@ _hostingde_getZoneConfig() {
if _contains "${curResult}" '"totalEntries": 1'; then if _contains "${curResult}" '"totalEntries": 1'; then
_info "Retrieved zone data." _info "Retrieved zone data."
_debug "Zone data: '${curResult}'" _debug "Zone data: '${curResult}'"
# read ZoneConfigId for later update
zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigName=$(echo "${curResult}" | _egrep_o '"name":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigType=$(echo "${curResult}" | grep -v "FindZoneConfigsResult" | _egrep_o '"type":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigExpire=$(echo "${curResult}" | _egrep_o '"expire":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigNegativeTtl=$(echo "${curResult}" | _egrep_o '"negativeTtl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigRefresh=$(echo "${curResult}" | _egrep_o '"refresh":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigRetry=$(echo "${curResult}" | _egrep_o '"retry":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigTtl=$(echo "${curResult}" | _egrep_o '"ttl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1)
zoneConfigDnsServerGroupId=$(echo "${curResult}" | _egrep_o '"dnsServerGroupId":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigEmailAddress=$(echo "${curResult}" | _egrep_o '"emailAddress":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
zoneConfigDnsSecMode=$(echo "${curResult}" | _egrep_o '"dnsSecMode":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ "${zoneConfigType}" != "NATIVE" ]; then
_err "Zone is not native"
returnCode=1
break
fi
_debug "zoneConfigId '${zoneConfigId}'" _debug "zoneConfigId '${zoneConfigId}'"
returnCode=0 returnCode=0
break break
@ -94,7 +107,7 @@ _hostingde_addRecord() {
_hostingde_getZoneStatus _hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'" _debug "Result of zoneStatus: '${zoneStatus}'"
done 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")" 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'"
@ -118,7 +131,7 @@ _hostingde_removeRecord() {
_hostingde_getZoneStatus _hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'" _debug "Result of zoneStatus: '$zoneStatus'"
done 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")" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
_debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
_debug "Result of zoneUpdate: '$curResult'" _debug "Result of zoneUpdate: '$curResult'"

View File

@ -1,33 +1,47 @@
#!/usr/bin/env sh #!/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: # Environment variables:
# #
# - NEXCESS_API_TOKEN (your Nexcess API Token) # - NW_API_TOKEN (Your API Token)
# Note: If you do not have an API token, one can be generated at: # - NW_API_ENDPOINT (One of the following listed below)
# https://portal.nexcess.net/api-token #
# 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> # Author: Frank Laszlo <flaszlo@nexcess.net>
NEXCESS_API_URL="https://portal.nexcess.net/" NW_API_VERSION="0"
NEXCESS_API_VERSION="0"
# dns_nexcess_add() - Add TXT record # dns_nw_add() - Add TXT record
# Usage: dns_nexcess_add _acme-challenge.subdomain.domain.com "XyZ123..." # Usage: dns_nw_add _acme-challenge.subdomain.domain.com "XyZ123..."
dns_nexcess_add() { dns_nw_add() {
host="${1}" host="${1}"
txtvalue="${2}" txtvalue="${2}"
_debug host "${host}" _debug host "${host}"
_debug txtvalue "${txtvalue}" _debug txtvalue "${txtvalue}"
if ! _check_nexcess_api_token; then if ! _check_nw_api_creds; then
return 1 return 1
fi fi
_info "Using Nexcess" _info "Using NocWorx (${NW_API_ENDPOINT})"
_debug "Calling: dns_nexcess_add() '${host}' '${txtvalue}'" _debug "Calling: dns_nw_add() '${host}' '${txtvalue}'"
_debug "Detecting root zone" _debug "Detecting root zone"
if ! _get_root "${host}"; then if ! _get_root "${host}"; then
@ -56,21 +70,21 @@ dns_nexcess_add() {
return 1 return 1
} }
# dns_nexcess_rm() - Remove TXT record # dns_nw_rm() - Remove TXT record
# Usage: dns_nexcess_rm _acme-challenge.subdomain.domain.com "XyZ123..." # Usage: dns_nw_rm _acme-challenge.subdomain.domain.com "XyZ123..."
dns_nexcess_rm() { dns_nw_rm() {
host="${1}" host="${1}"
txtvalue="${2}" txtvalue="${2}"
_debug host "${host}" _debug host "${host}"
_debug txtvalue "${txtvalue}" _debug txtvalue "${txtvalue}"
if ! _check_nexcess_api_token; then if ! _check_nw_api_creds; then
return 1 return 1
fi fi
_info "Using Nexcess" _info "Using NocWorx (${NW_API_ENDPOINT})"
_debug "Calling: dns_nexcess_rm() '${host}'" _debug "Calling: dns_nw_rm() '${host}'"
_debug "Detecting root zone" _debug "Detecting root zone"
if ! _get_root "${host}"; then if ! _get_root "${host}"; then
@ -110,19 +124,27 @@ dns_nexcess_rm() {
return 1 return 1
} }
_check_nexcess_api_token() { _check_nw_api_creds() {
if [ -z "${NEXCESS_API_TOKEN}" ]; then NW_API_TOKEN="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}"
NEXCESS_API_TOKEN="${NEXCESS_API_TOKEN:-$(_readaccountconf_mutable NEXCESS_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 "Please create your token and try again."
_err "If you need to generate a new token, please visit:" _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://portal.nexcess.net/api-token"
_err " - https://core.thermo.io/api-token"
_err " - https://my.futurehosting.com/api-token"
return 1 return 1
fi 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() { _get_root() {
@ -169,15 +191,15 @@ _rest() {
export _H1="Accept: application/json" export _H1="Accept: application/json"
export _H2="Content-Type: application/json" export _H2="Content-Type: application/json"
export _H3="Api-Version: ${NEXCESS_API_VERSION}" export _H3="Api-Version: ${NW_API_VERSION}"
export _H4="User-Agent: NEXCESS-ACME-CLIENT" export _H4="User-Agent: NW-ACME-CLIENT"
export _H5="Authorization: Bearer ${NEXCESS_API_TOKEN}" export _H5="Authorization: Bearer ${NW_API_TOKEN}"
if [ "${method}" != "GET" ]; then if [ "${method}" != "GET" ]; then
_debug data "${data}" _debug data "${data}"
response="$(_post "${data}" "${NEXCESS_API_URL}${ep}" "" "${method}")" response="$(_post "${data}" "${NW_API_ENDPOINT}${ep}" "" "${method}")"
else else
response="$(_get "${NEXCESS_API_URL}${ep}${data}")" response="$(_get "${NW_API_ENDPOINT}${ep}${data}")"
fi fi
if [ "${?}" != "0" ]; then if [ "${?}" != "0" ]; then

View File

@ -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 <flaszlo@nexcess.net>
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
}