diff --git a/.github/auto-comment.yml b/.github/auto-comment.yml
index 1e7b389e..520b3ce3 100644
--- a/.github/auto-comment.yml
+++ b/.github/auto-comment.yml
@@ -4,17 +4,23 @@ issuesOpened: >
如果有 bug, 请先更新到最新版试试:
- ```sh
+ ```
acme.sh --upgrade
```
please also provide the log with `--debug 2`.
+ 同时请提供调试输出 `--debug 2`
+
see: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
+
+ Without `--debug 2` log, your issue will NEVER get replied.
+
+ 没有调试输出, 你的 issue 不会得到任何解答.
pullRequestOpened: >
- First, never send a PR to `master` branch, it will never be accepted. Please send to the `dev` branch instead.
+ First, NEVER send a PR to `master` branch, it will NEVER be accepted. Please send to the `dev` branch instead.
If this is a PR to support new DNS API or new notification API, please read this guide first:
https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
@@ -23,6 +29,12 @@ pullRequestOpened: >
Then add your usage here:
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
-
+
+ Or some other wiki pages:
+
+ https://github.com/acmesh-official/acme.sh/wiki/deployhooks
+
+ https://github.com/acmesh-official/acme.sh/wiki/notify
+
diff --git a/acme.sh b/acme.sh
index f672710d..57f45905 100755
--- a/acme.sh
+++ b/acme.sh
@@ -138,6 +138,8 @@ _NOTIFY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/notify"
_SUDO_WIKI="https://github.com/acmesh-official/acme.sh/wiki/sudo"
+_REVOKE_WIKI="https://github.com/acmesh-official/acme.sh/wiki/revokecert"
+
_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
@@ -3512,6 +3514,8 @@ updateaccount() {
if [ "$ACME_VERSION" = "2" ]; then
if [ "$ACCOUNT_EMAIL" ]; then
updjson='{"contact": ["mailto:'$ACCOUNT_EMAIL'"]}'
+ else
+ updjson='{"contact": []}'
fi
else
# ACMEv1: Updates happen the same way a registration is done.
@@ -5454,6 +5458,7 @@ uninstallcronjob() {
}
+#domain isECC revokeReason
revoke() {
Le_Domain="$1"
if [ -z "$Le_Domain" ]; then
@@ -5462,7 +5467,10 @@ revoke() {
fi
_isEcc="$2"
-
+ _reason="$3"
+ if [ -z "$_reason" ]; then
+ _reason="0"
+ fi
_initpath "$Le_Domain" "$_isEcc"
if [ ! -f "$DOMAIN_CONF" ]; then
_err "$Le_Domain is not a issued domain, skip."
@@ -5484,7 +5492,7 @@ revoke() {
_initAPI
if [ "$ACME_VERSION" = "2" ]; then
- data="{\"certificate\": \"$cert\"}"
+ data="{\"certificate\": \"$cert\",\"reason\":$_reason}"
else
data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
fi
@@ -6293,6 +6301,7 @@ Parameters:
0: Bulk mode. Send all the domain's notifications in one message(mail).
1: Cert mode. Send a message for every single cert.
--notify-hook [hookname] Set the notify hook
+ --revoke-reason [0-10] The reason for '--revoke' command. See: $_REVOKE_WIKI
"
}
@@ -6468,6 +6477,7 @@ _process() {
_notify_hook=""
_notify_level=""
_notify_mode=""
+ _revoke_reason=""
while [ ${#} -gt 0 ]; do
case "${1}" in
@@ -6940,6 +6950,14 @@ _process() {
_notify_mode="$_nmode"
shift
;;
+ --revoke-reason)
+ _revoke_reason="$2"
+ if _startswith "$_revoke_reason" "-"; then
+ _err "'$_revoke_reason' is not a integer for '$1'"
+ return 1
+ fi
+ shift
+ ;;
*)
_err "Unknown parameter : $1"
return 1
@@ -7027,7 +7045,7 @@ _process() {
renewAll "$_stopRenewOnError"
;;
revoke)
- revoke "$_domain" "$_ecc"
+ revoke "$_domain" "$_ecc" "$_revoke_reason"
;;
remove)
remove "$_domain" "$_ecc"
diff --git a/deploy/ssh.sh b/deploy/ssh.sh
index 9cb0af9e..d71637a1 100644
--- a/deploy/ssh.sh
+++ b/deploy/ssh.sh
@@ -12,7 +12,7 @@
# Only a username is required. All others are optional.
#
# The following examples are for QNAP NAS running QTS 4.2
-# export DEPLOY_SSH_CMD="" # defaults to ssh
+# export DEPLOY_SSH_CMD="" # defaults to "ssh -T"
# export DEPLOY_SSH_USER="admin" # required
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
@@ -20,7 +20,9 @@
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
# export DEPLOY_SSH_FULLCHAIN=""
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
-# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes
+# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value
+# export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy
+# export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
#
######## Public functions #####################
@@ -31,10 +33,10 @@ ssh_deploy() {
_ccert="$3"
_cca="$4"
_cfullchain="$5"
+ _err_code=0
_cmdstr=""
- _homedir='~'
- _backupprefix="$_homedir/.acme_ssh_deploy/$_cdomain-backup"
- _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
+ _backupprefix=""
+ _backupdir=""
if [ -f "$DOMAIN_CONF" ]; then
# shellcheck disable=SC1090
@@ -71,18 +73,62 @@ ssh_deploy() {
Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD"
_savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd"
elif [ -z "$Le_Deploy_ssh_cmd" ]; then
- Le_Deploy_ssh_cmd="ssh"
+ Le_Deploy_ssh_cmd="ssh -T"
fi
- # BACKUP is optional. If not provided then default to yes
+ # BACKUP is optional. If not provided then default to previously saved value or yes.
if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then
Le_Deploy_ssh_backup="no"
- elif [ -z "$Le_Deploy_ssh_backup" ]; then
+ elif [ -z "$Le_Deploy_ssh_backup" ] || [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
Le_Deploy_ssh_backup="yes"
fi
_savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup"
+ # BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
+ if [ -n "$DEPLOY_SSH_BACKUP_PATH" ]; then
+ Le_Deploy_ssh_backup_path="$DEPLOY_SSH_BACKUP_PATH"
+ elif [ -z "$Le_Deploy_ssh_backup_path" ]; then
+ Le_Deploy_ssh_backup_path=".acme_ssh_deploy"
+ fi
+ _savedomainconf Le_Deploy_ssh_backup_path "$Le_Deploy_ssh_backup_path"
+
+ # MULTI_CALL is optional. If not provided then default to previously saved
+ # value (which may be undefined... equivalent to "no").
+ if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
+ Le_Deploy_ssh_multi_call="yes"
+ _savedomainconf Le_Deploy_ssh_multi_call "$Le_Deploy_ssh_multi_call"
+ elif [ "$DEPLOY_SSH_MULTI_CALL" = "no" ]; then
+ Le_Deploy_ssh_multi_call=""
+ _cleardomainconf Le_Deploy_ssh_multi_call
+ fi
+
_info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server"
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ _info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
+ else
+ _info "Required commands batched and sent in single call to remote host"
+ fi
+
+ if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
+ _backupprefix="$Le_Deploy_ssh_backup_path/$_cdomain-backup"
+ _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
+ # run cleanup on the backup directory, erase all older
+ # than 180 days (15552000 seconds).
+ _cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
+do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
+then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
+ # Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
+ # Create our backup directory for overwritten cert files.
+ _cmdstr="mkdir -p $_backupdir; $_cmdstr"
+ _info "Backup of old certificate files will be placed in remote directory $_backupdir"
+ _info "Backup directories erased after 180 days."
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
+ _cmdstr=""
+ fi
+ fi
# KEYFILE is optional.
# If provided then private key will be copied to provided filename.
@@ -98,6 +144,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;"
_info "will copy private key to remote file $Le_Deploy_ssh_keyfile"
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
+ _cmdstr=""
+ fi
fi
# CERTFILE is optional.
@@ -118,6 +170,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;"
_info "will copy certificate to remote file $Le_Deploy_ssh_certfile"
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
+ _cmdstr=""
+ fi
fi
# CAFILE is optional.
@@ -139,6 +197,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;"
_info "will copy CA file to remote file $Le_Deploy_ssh_cafile"
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
+ _cmdstr=""
+ fi
fi
# FULLCHAIN is optional.
@@ -161,6 +225,12 @@ ssh_deploy() {
# copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;"
_info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain"
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
+ _cmdstr=""
+ fi
fi
# REMOTE_CMD is optional.
@@ -172,34 +242,36 @@ ssh_deploy() {
if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then
_cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;"
_info "Will execute remote command $Le_Deploy_ssh_remote_cmd"
+ if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
+ _cmdstr=""
+ fi
fi
- if [ -z "$_cmdstr" ]; then
- _err "No remote commands to excute. Failed to deploy certificates to remote server"
- return 1
- elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
- # run cleanup on the backup directory, erase all older
- # than 180 days (15552000 seconds).
- _cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
-do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
-then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
- # Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
- # Create our backup directory for overwritten cert files.
- _cmdstr="mkdir -p $_backupdir; $_cmdstr"
- _info "Backup of old certificate files will be placed in remote directory $_backupdir"
- _info "Backup directories erased after 180 days."
+ # if commands not all sent in multiple calls then all commands sent in a single SSH call now...
+ if [ -n "$_cmdstr" ]; then
+ if ! _ssh_remote_cmd "$_cmdstr"; then
+ return $_err_code
+ fi
fi
+ return 0
+}
- _secure_debug "Remote commands to execute: " "$_cmdstr"
+#cmd
+_ssh_remote_cmd() {
+ _cmd="$1"
+ _secure_debug "Remote commands to execute: $_cmd"
_info "Submitting sequence of commands to remote server by ssh"
# quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029
- $Le_Deploy_ssh_cmd -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmdstr'"
- _ret="$?"
+ $Le_Deploy_ssh_cmd "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmd'"
+ _err_code="$?"
- if [ "$_ret" != "0" ]; then
- _err "Error code $_ret returned from $Le_Deploy_ssh_cmd"
+ if [ "$_err_code" != "0" ]; then
+ _err "Error code $_err_code returned from ssh"
fi
- return $_ret
+ return $_err_code
}
diff --git a/dnsapi/dns_arvan.sh b/dnsapi/dns_arvan.sh
new file mode 100644
index 00000000..edeb56ca
--- /dev/null
+++ b/dnsapi/dns_arvan.sh
@@ -0,0 +1,163 @@
+#!/usr/bin/env sh
+
+#Arvan_Token="xxxx"
+
+ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
+
+#Author: Ehsan Aliakbar
+#Report Bugs here: https://github.com/Neilpang/acme.sh
+#
+######## Public functions #####################
+
+#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_arvan_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using Arvan"
+
+ Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
+
+ if [ -z "$Arvan_Token" ]; then
+ _err "You didn't specify \"Arvan_Token\" token yet."
+ _err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
+ return 1
+ fi
+ #save the api token to the account conf file.
+ _saveaccountconf_mutable Arvan_Token "$Arvan_Token"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _info "Adding record"
+ if _arvan_rest POST "$_domain/dns-records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":{\"text\":\"$txtvalue\"},\"ttl\":120}"; then
+ if _contains "$response" "$txtvalue"; then
+ _info "Added, OK"
+ return 0
+ elif _contains "$response" "Record Data is Duplicated"; then
+ _info "Already exists, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_arvan_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using Arvan"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ shorted_txtvalue=$(printf "%s" "$txtvalue" | cut -d "-" -d "_" -f1)
+ _arvan_rest GET "${_domain}/dns-records?search=$shorted_txtvalue"
+
+ if ! printf "%s" "$response" | grep \"current_page\":1 >/dev/null; then
+ _err "Error on Arvan Api"
+ _err "Please create a github issue with debbug log"
+ return 1
+ fi
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"total\":[^,]*" | cut -d : -f 2)
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _arvan_rest "DELETE" "${_domain}/dns-records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _debug "$response"
+ _contains "$response" 'dns record deleted'
+ fi
+}
+
+#################### Private functions below ##################################
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+ i=1
+ 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 ! _arvan_rest GET "?search=$h"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"domain\":\"$h\"" || _contains "$response" '"total":1'; then
+ _domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
+ if [ "$_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
+}
+
+_arvan_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ token_trimmed=$(echo "$Arvan_Token" | tr -d '"')
+
+ export _H1="Authorization: $token_trimmed"
+
+ if [ "$mtd" = "DELETE" ]; then
+ #DELETE Request shouldn't have Content-Type
+ _debug data "$data"
+ response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
+ elif [ "$mtd" = "POST" ]; then
+ export _H2="Content-Type: application/json"
+ _debug data "$data"
+ response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
+ else
+ response="$(_get "$ARVAN_API_URL/$ep$data")"
+ fi
+}
diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh
index 53781d0d..ecc4f174 100644
--- a/dnsapi/dns_ddnss.sh
+++ b/dnsapi/dns_ddnss.sh
@@ -119,7 +119,7 @@ _ddnss_rest() {
# DDNSS uses GET to update domain info
if [ "$method" = "GET" ]; then
- response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | _tail_n 1)"
+ response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | tr -s "\n" | _tail_n 1)"
else
_err "Unsupported method"
return 1
diff --git a/dnsapi/dns_easydns.sh b/dnsapi/dns_easydns.sh
index ca8faab2..f466f1e2 100644
--- a/dnsapi/dns_easydns.sh
+++ b/dnsapi/dns_easydns.sh
@@ -4,8 +4,7 @@
#
# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
#
-# Please note: # API is currently beta and subject to constant change
-# http://sandbox.rest.easydns.net:3000/
+# API Documentation: https://sandbox.rest.easydns.net:3001/
#
# Author: wurzelpanzer [wurzelpanzer@maximolider.net]
# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2647
@@ -25,7 +24,7 @@ dns_easydns_add() {
EASYDNS_Key="${EASYDNS_Key:-$(_readaccountconf_mutable EASYDNS_Key)}"
if [ -z "$EASYDNS_Token" ] || [ -z "$EASYDNS_Key" ]; then
- _err "You didn't specify an easydns.net token or api key. Please sign up at http://docs.sandbox.rest.easydns.net/beta_signup.php"
+ _err "You didn't specify an easydns.net token or api key. Signup at https://cp.easydns.com/manage/security/api/signup.php"
return 1
else
_saveaccountconf_mutable EASYDNS_Token "$EASYDNS_Token"
diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh
index caa4d2c4..5829e00e 100755
--- a/dnsapi/dns_he.sh
+++ b/dnsapi/dns_he.sh
@@ -24,7 +24,7 @@ dns_he_add() {
if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then
HE_Username=
HE_Password=
- _err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password envoronment variables."
+ _err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password environment variables."
return 1
fi
_saveaccountconf_mutable HE_Username "$HE_Username"
diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh
index 7c08d72e..50b4b10c 100755
--- a/dnsapi/dns_inwx.sh
+++ b/dnsapi/dns_inwx.sh
@@ -34,6 +34,10 @@ dns_inwx_add() {
_saveaccountconf_mutable INWX_Password "$INWX_Password"
_saveaccountconf_mutable INWX_Shared_Secret "$INWX_Shared_Secret"
+ if ! _inwx_login; then
+ return 1
+ fi
+
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@@ -64,6 +68,10 @@ dns_inwx_rm() {
return 1
fi
+ if ! _inwx_login; then
+ return 1
+ fi
+
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@@ -123,8 +131,42 @@ dns_inwx_rm() {
#################### Private functions below ##################################
+_inwx_check_cookie() {
+ INWX_Cookie="${INWX_Cookie:-$(_readaccountconf_mutable INWX_Cookie)}"
+ if [ -z "$INWX_Cookie" ]; then
+ _debug "No cached cookie found"
+ return 1
+ fi
+ _H1="$INWX_Cookie"
+ export _H1
+
+ xml_content=$(printf '
+
+ account.info
+ ')
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if _contains "$response" "code1000"; then
+ _debug "Cached cookie still valid"
+ return 0
+ fi
+
+ _debug "Cached cookie no longer valid"
+ _H1=""
+ export _H1
+ INWX_Cookie=""
+ _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
+ return 1
+}
+
_inwx_login() {
+ if _inwx_check_cookie; then
+ _debug "Already logged in"
+ return 0
+ fi
+
xml_content=$(printf '
account.login
@@ -148,17 +190,25 @@ _inwx_login() {
- ' $INWX_User $INWX_Password)
+ ' "$INWX_User" "$INWX_Password")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
- _H1=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+
+ INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+ _H1=$INWX_Cookie
export _H1
+ export INWX_Cookie
+ _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
+
+ if ! _contains "$response" "code1000"; then
+ _err "INWX API: Authentication error (username/password correct?)"
+ return 1
+ fi
#https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
- if _contains "$response" "code1000" \
- && _contains "$response" "tfaGOOGLE-AUTH"; then
+ if _contains "$response" "tfaGOOGLE-AUTH"; then
if [ -z "$INWX_Shared_Secret" ]; then
- _err "Mobile TAN detected."
+ _err "INWX API: Mobile TAN detected."
_err "Please define a shared secret."
return 1
fi
@@ -191,6 +241,11 @@ _inwx_login() {
' "$tan")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! _contains "$response" "code1000"; then
+ _err "INWX API: Mobile TAN not correct."
+ return 1
+ fi
fi
}
@@ -203,8 +258,6 @@ _get_root() {
i=2
p=1
- _inwx_login
-
xml_content='
nameserver.list
diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh
index 0fdc3d5e..96ef5969 100644
--- a/dnsapi/dns_one.sh
+++ b/dnsapi/dns_one.sh
@@ -5,8 +5,11 @@
# Author: github: @diseq
# Created: 2019-02-17
# Fixed by: @der-berni
-# Modified: 2019-05-31
-#
+# Modified: 2020-04-07
+#
+# Use ONECOM_KeepCnameProxy to keep the CNAME DNS record
+# export ONECOM_KeepCnameProxy="1"
+#
# export ONECOM_User="username"
# export ONECOM_Password="password"
#
@@ -30,32 +33,45 @@ dns_one_add() {
return 1
fi
- mysubdomain=$_sub_domain
- mydomain=$_domain
- _debug mysubdomain "$mysubdomain"
- _debug mydomain "$mydomain"
+ subdomain="${_sub_domain}"
+ maindomain=${_domain}
- # get entries
- response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")"
- _debug response "$response"
+ useProxy=0
+ if [ "${_sub_domain}" = "_acme-challenge" ]; then
+ subdomain="proxy${_sub_domain}"
+ useProxy=1
+ fi
- # Update the IP address for domain entry
- postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}"
- _debug postdata "$postdata"
- response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records" "" "POST" "application/json")"
- response="$(echo "$response" | _normalizeJson)"
- _debug response "$response"
+ _debug subdomain "$subdomain"
+ _debug maindomain "$maindomain"
+
+ if [ $useProxy -eq 1 ]; then
+ #Check if the CNAME exists
+ _dns_one_getrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
+ if [ -z "$id" ]; then
+ _info "$(__red "Add CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
+ _dns_one_addrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
- id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p")
+ _info "Not valid yet, let's wait 1 hour to take effect."
+ _sleep 3600
+ fi
+ fi
+ #Check if the TXT exists
+ _dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
+ if [ -n "$id" ]; then
+ _info "$(__green "Txt record with the same value found. Skip adding.")"
+ return 0
+ fi
+
+ _dns_one_addrecord "TXT" "$subdomain" "$txtvalue"
if [ -z "$id" ]; then
- _err "Add txt record error."
+ _err "Add TXT record error."
return 1
else
- _info "Added, OK ($id)"
+ _info "$(__green "Added, OK ($id)")"
return 0
fi
-
}
dns_one_rm() {
@@ -73,36 +89,45 @@ dns_one_rm() {
return 1
fi
- mysubdomain=$_sub_domain
- mydomain=$_domain
- _debug mysubdomain "$mysubdomain"
- _debug mydomain "$mydomain"
+ subdomain="${_sub_domain}"
+ maindomain=${_domain}
- # get entries
- response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")"
- response="$(echo "$response" | _normalizeJson)"
- _debug response "$response"
+ useProxy=0
+ if [ "${_sub_domain}" = "_acme-challenge" ]; then
+ subdomain="proxy${_sub_domain}"
+ useProxy=1
+ fi
- id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ _debug subdomain "$subdomain"
+ _debug maindomain "$maindomain"
+ if [ $useProxy -eq 1 ]; then
+ if [ "$ONECOM_KeepCnameProxy" = "1" ]; then
+ _info "$(__red "Keeping CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
+ else
+ #Check if the CNAME exists
+ _dns_one_getrecord "CNAME" "$_sub_domain" "$subdomain.$maindomain"
+ if [ -n "$id" ]; then
+ _info "$(__red "Removing CNAME Proxy record: '$(__green "\"$_sub_domain\" => \"$subdomain.$maindomain\"")'")"
+ _dns_one_delrecord "$id"
+ fi
+ fi
+ fi
+ #Check if the TXT exists
+ _dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
if [ -z "$id" ]; then
_err "Txt record not found."
return 1
fi
# delete entry
- response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records/$id" "" "DELETE" "application/json")"
- response="$(echo "$response" | _normalizeJson)"
- _debug response "$response"
-
- if [ "$response" = '{"result":null,"metadata":null}' ]; then
- _info "Removed, OK"
+ if _dns_one_delrecord "$id"; then
+ _info "$(__green Removed, OK)"
return 0
else
_err "Removing txt record error."
return 1
fi
-
}
#_acme-challenge.www.domain.com
@@ -138,6 +163,8 @@ _get_root() {
_dns_one_login() {
# get credentials
+ ONECOM_KeepCnameProxy="${ONECOM_KeepCnameProxy:-$(_readaccountconf_mutable ONECOM_KeepCnameProxy)}"
+ ONECOM_KeepCnameProxy="${ONECOM_KeepCnameProxy:-0}"
ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}"
ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}"
if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then
@@ -149,6 +176,7 @@ _dns_one_login() {
fi
#save the api key and email to the account conf file.
+ _saveaccountconf_mutable ONECOM_KeepCnameProxy "$ONECOM_KeepCnameProxy"
_saveaccountconf_mutable ONECOM_User "$ONECOM_User"
_saveaccountconf_mutable ONECOM_Password "$ONECOM_Password"
@@ -177,3 +205,75 @@ _dns_one_login() {
return 0
}
+
+_dns_one_getrecord() {
+ type="$1"
+ name="$2"
+ value="$3"
+ if [ -z "$type" ]; then
+ type="TXT"
+ fi
+ if [ -z "$name" ]; then
+ _err "Record name is empty."
+ return 1
+ fi
+
+ response="$(_get "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug response "$response"
+
+ if [ -z "${value}" ]; then
+ id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"[^\"]*\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ response=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"[^\"]*\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"\([^\"]*\)\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ else
+ id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"${value}\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ fi
+ if [ -z "$id" ]; then
+ return 1
+ fi
+ return 0
+}
+
+_dns_one_addrecord() {
+ type="$1"
+ name="$2"
+ value="$3"
+ if [ -z "$type" ]; then
+ type="TXT"
+ fi
+ if [ -z "$name" ]; then
+ _err "Record name is empty."
+ return 1
+ fi
+
+ postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"${type}\",\"prefix\":\"${name}\",\"content\":\"${value}\"}}"
+ _debug postdata "$postdata"
+ response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records" "" "POST" "application/json")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug response "$response"
+
+ id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$subdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p")
+
+ if [ -z "$id" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+_dns_one_delrecord() {
+ id="$1"
+ if [ -z "$id" ]; then
+ return 1
+ fi
+
+ response="$(_post "" "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records/$id" "" "DELETE" "application/json")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug response "$response"
+
+ if [ "$response" = '{"result":null,"metadata":null}' ]; then
+ return 0
+ else
+ return 1
+ fi
+}
diff --git a/dnsapi/dns_opnsense.sh b/dnsapi/dns_opnsense.sh
index b2a3746f..ec7d2277 100755
--- a/dnsapi/dns_opnsense.sh
+++ b/dnsapi/dns_opnsense.sh
@@ -150,7 +150,7 @@ _get_root() {
return 1
fi
_debug h "$h"
- id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":\"[^\"]*\",\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
+ id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":\"[^\"]*\",\"allownotifyslave\":{\"\":{[^}]*}},\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
diff --git a/notify/mail.sh b/notify/mail.sh
index ec9aa0de..54b2a6d4 100644
--- a/notify/mail.sh
+++ b/notify/mail.sh
@@ -6,6 +6,7 @@
#MAIL_FROM="yyyy@gmail.com"
#MAIL_TO="yyyy@gmail.com"
#MAIL_NOVALIDATE=""
+#MAIL_MSMTP_ACCOUNT=""
mail_send() {
_subject="$1"
@@ -76,18 +77,17 @@ mail_send() {
}
_mail_bin() {
- if [ -n "$MAIL_BIN" ]; then
- _MAIL_BIN="$MAIL_BIN"
- elif _exists "sendmail"; then
- _MAIL_BIN="sendmail"
- elif _exists "ssmtp"; then
- _MAIL_BIN="ssmtp"
- elif _exists "mutt"; then
- _MAIL_BIN="mutt"
- elif _exists "mail"; then
- _MAIL_BIN="mail"
- else
- _err "Please install sendmail, ssmtp, mutt or mail first."
+ _MAIL_BIN=""
+
+ for b in "$MAIL_BIN" sendmail ssmtp mutt mail msmtp; do
+ if _exists "$b"; then
+ _MAIL_BIN="$b"
+ break
+ fi
+ done
+
+ if [ -z "$_MAIL_BIN" ]; then
+ _err "Please install sendmail, ssmtp, mutt, mail or msmtp first."
return 1
fi
@@ -95,30 +95,35 @@ _mail_bin() {
}
_mail_cmnd() {
+ _MAIL_ARGS=""
+
case $(basename "$_MAIL_BIN") in
sendmail)
if [ -n "$MAIL_FROM" ]; then
- echo "'$_MAIL_BIN' -f '$MAIL_FROM' '$MAIL_TO'"
- else
- echo "'$_MAIL_BIN' '$MAIL_TO'"
+ _MAIL_ARGS="-f '$MAIL_FROM'"
fi
;;
- ssmtp)
- echo "'$_MAIL_BIN' '$MAIL_TO'"
- ;;
mutt | mail)
- echo "'$_MAIL_BIN' -s '$_subject' '$MAIL_TO'"
+ _MAIL_ARGS="-s '$_subject'"
;;
- *)
- _err "Command $MAIL_BIN is not supported, use sendmail, ssmtp, mutt or mail."
- return 1
+ msmtp)
+ if [ -n "$MAIL_FROM" ]; then
+ _MAIL_ARGS="-f '$MAIL_FROM'"
+ fi
+
+ if [ -n "$MAIL_MSMTP_ACCOUNT" ]; then
+ _MAIL_ARGS="$_MAIL_ARGS -a '$MAIL_MSMTP_ACCOUNT'"
+ fi
;;
+ *) ;;
esac
+
+ echo "'$_MAIL_BIN' $_MAIL_ARGS '$MAIL_TO'"
}
_mail_body() {
case $(basename "$_MAIL_BIN") in
- sendmail | ssmtp)
+ sendmail | ssmtp | msmtp)
if [ -n "$MAIL_FROM" ]; then
echo "From: $MAIL_FROM"
fi