diff --git a/.github/auto-comment.yml b/.github/auto-comment.yml
new file mode 100644
index 00000000..520b3ce3
--- /dev/null
+++ b/.github/auto-comment.yml
@@ -0,0 +1,40 @@
+# Comment to a new issue.
+issuesOpened: >
+ If this is a bug report, please upgrade to the latest code and try again:
+
+ 如果有 bug, 请先更新到最新版试试:
+
+ ```
+ 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.
+
+ 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
+
+ Please check the guide items one by one.
+
+ 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 5cfe859f..97d71a22 100755
--- a/acme.sh
+++ b/acme.sh
@@ -48,8 +48,6 @@ LOCAL_ANY_ADDRESS="0.0.0.0"
DEFAULT_RENEW=60
-DEFAULT_DNS_SLEEP=120
-
NO_VALUE="no"
W_DNS="dns"
@@ -140,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"
@@ -848,6 +848,14 @@ _json_encode() {
echo "$_j_str" | _hex_dump | _lower_case | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n"
}
+#from: http:\/\/ to http://
+_json_decode() {
+ _j_str="$(sed 's#\\/#/#g')"
+ _debug3 "_json_decode"
+ _debug3 "_j_str" "$_j_str"
+ echo "$_j_str"
+}
+
#options file
_sed_i() {
options="$1"
@@ -3418,13 +3426,13 @@ _regAccount() {
if [ "$ACME_VERSION" = "2" ]; then
regjson='{"termsOfServiceAgreed": true}'
if [ "$ACCOUNT_EMAIL" ]; then
- regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}'
+ regjson='{"contact": ["mailto:'$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}'
fi
else
_reg_res="$ACME_NEW_ACCOUNT_RES"
regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
if [ "$ACCOUNT_EMAIL" ]; then
- regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
+ regjson='{"resource": "'$_reg_res'", "contact": ["mailto:'$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
fi
fi
@@ -3504,7 +3512,9 @@ updateaccount() {
if [ "$ACME_VERSION" = "2" ]; then
if [ "$ACCOUNT_EMAIL" ]; then
- updjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"]}'
+ updjson='{"contact": ["mailto:'$ACCOUNT_EMAIL'"]}'
+ else
+ updjson='{"contact": []}'
fi
else
# ACMEv1: Updates happen the same way a registration is done.
@@ -3517,6 +3527,7 @@ updateaccount() {
_send_signed_request "$_accUri" "$updjson"
if [ "$code" = '200' ]; then
+ echo "$response" >"$ACCOUNT_JSON_PATH"
_info "account update success for $_accUri."
else
_info "Error. The account was not updated."
@@ -4019,7 +4030,7 @@ issue() {
#for dns manual mode
_savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize"
- _authorizations_seg="$(echo "$response" | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
+ _authorizations_seg="$(echo "$response" | _json_decode | _egrep_o '"authorizations" *: *\[[^\[]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
_debug2 _authorizations_seg "$_authorizations_seg"
if [ -z "$_authorizations_seg" ]; then
_err "_authorizations_seg not found."
@@ -4540,7 +4551,7 @@ $_authorizations_map"
_savedomainconf "Le_LinkOrder" "$Le_LinkOrder"
_link_cert_retry=0
- _MAX_CERT_RETRY=5
+ _MAX_CERT_RETRY=30
while [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do
if _contains "$response" "\"status\":\"valid\""; then
_debug "Order status is valid."
@@ -5446,6 +5457,7 @@ uninstallcronjob() {
}
+#domain isECC revokeReason
revoke() {
Le_Domain="$1"
if [ -z "$Le_Domain" ]; then
@@ -5454,7 +5466,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."
@@ -5476,7 +5491,7 @@ revoke() {
_initAPI
if [ "$ACME_VERSION" = "2" ]; then
- data="{\"certificate\": \"$cert\"}"
+ data="{\"certificate\": \"$cert\",\"reason\":$_reason}"
else
data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
fi
@@ -6222,7 +6237,7 @@ Parameters:
--stateless Use stateless mode, see: $_STATELESS_WIKI
--apache Use apache mode.
--dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
- --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds.
+ --dnssleep 300 The time in seconds to wait for all the txt records to take effect in dns api mode. It's not necessary to use this by default, $PROJECT_NAME polls dns status automatically.
--keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
--accountkeylength, -ak [2048] Specifies the account key length: 2048, 3072, 4096
@@ -6285,6 +6300,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
"
}
@@ -6317,7 +6333,7 @@ _installOnline() {
if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
_info "Install success!"
_initpath
- _saveaccountconf "UPGRADE_HASH" "$(_getMasterHash)"
+ _saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
fi
cd ..
@@ -6327,19 +6343,27 @@ _installOnline() {
)
}
-_getMasterHash() {
+_getRepoHash() {
+ _hash_path=$1
+ shift
+ _hash_url="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/$_hash_path"
+ _get $_hash_url | tr -d "\r\n" | tr '{},' '\n' | grep '"sha":' | cut -d '"' -f 4
+}
+
+_getUpgradeHash() {
_b="$BRANCH"
if [ -z "$_b" ]; then
_b="master"
fi
- _hash_url="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/heads/$_b"
- _get $_hash_url | tr -d "\r\n" | tr '{},' '\n' | grep '"sha":' | cut -d '"' -f 4
+ _hash=$(_getRepoHash "heads/$_b")
+ if [ -z "$_hash" ]; then _hash=$(_getRepoHash "tags/$_b"); fi
+ echo $_hash
}
upgrade() {
if (
_initpath
- [ -z "$FORCE" ] && [ "$(_getMasterHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already uptodate!" && exit 0
+ [ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already uptodate!" && exit 0
export LE_WORKING_DIR
cd "$LE_WORKING_DIR"
_installOnline "nocron" "noprofile"
@@ -6452,6 +6476,7 @@ _process() {
_notify_hook=""
_notify_level=""
_notify_mode=""
+ _revoke_reason=""
while [ ${#} -gt 0 ]; do
case "${1}" in
@@ -6924,6 +6949,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
@@ -7011,7 +7044,7 @@ _process() {
renewAll "$_stopRenewOnError"
;;
revoke)
- revoke "$_domain" "$_ecc"
+ revoke "$_domain" "$_ecc" "$_revoke_reason"
;;
remove)
remove "$_domain" "$_ecc"
diff --git a/deploy/panos.sh b/deploy/panos.sh
index 6316784a..ef622ded 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -47,24 +47,24 @@ deployer() {
#Set Header
export _H1="Content-Type: multipart/form-data; boundary=$delim"
if [ "$type" = 'cert' ]; then
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"type\"\r\n\r\n\r\nimport"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\n\r\ncertificate"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n\r\n$_cdomain"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n\r\n$_panos_key"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\n\r\npem"
+ panos_url="${panos_url}?type=import"
+ content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\ncertificate"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
fi
if [ "$type" = 'key' ]; then
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"type\"\r\n\r\n\r\nimport"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\n\r\nprivate-key"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n\r\n$_cdomain"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n\r\n$_panos_key"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\n\r\npem"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n\r\n123456"
+ panos_url="${panos_url}?type=import"
+ content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\nprivate-key"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
fi
#Close multipart
- content="$content${nl}--$delim--${nl}"
+ content="$content${nl}--$delim--${nl}${nl}"
#Convert CRLF
content=$(printf %b "$content")
fi
diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh
index 13b09651..70669917 100644
--- a/deploy/qiniu.sh
+++ b/deploy/qiniu.sh
@@ -6,6 +6,8 @@
# export QINIU_AK="QINIUACCESSKEY"
# export QINIU_SK="QINIUSECRETKEY"
# export QINIU_CDN_DOMAIN="cdn.example.com"
+# If you have more than one domain, just
+# export QINIU_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
QINIU_API_BASE="https://api.qiniu.com"
@@ -67,21 +69,23 @@ qiniu_deploy() {
_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"
+ for domain in $QINIU_CDN_DOMAIN; do
+ update_path="/domain/$domain/httpsconf"
+ 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 $domain httpsconf:"
+ _err "$update_response"
+ return 1
+ fi
+
+ _debug update_response "$update_response"
+ _info "Domain $domain certificate has been deployed successfully"
+ done
return 0
}
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/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index 0c2b1185..5aef3b93 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -15,6 +15,7 @@
# SYNO_Scheme - defaults to http
# SYNO_Hostname - defaults to localhost
# SYNO_Port - defaults to 5000
+# SYNO_DID - device ID to skip OTP - defaults to empty
#
#returns 0 means success, otherwise error.
@@ -38,6 +39,7 @@ synology_dsm_deploy() {
_getdeployconf SYNO_Username
_getdeployconf SYNO_Password
_getdeployconf SYNO_Create
+ _getdeployconf SYNO_DID
if [ -z "$SYNO_Username" ] || [ -z "$SYNO_Password" ]; then
SYNO_Username=""
SYNO_Password=""
@@ -79,7 +81,7 @@ synology_dsm_deploy() {
# Login, get the token from JSON and session id from cookie
_info "Logging into $SYNO_Hostname:$SYNO_Port"
- response=$(_get "$_base_url/webman/login.cgi?username=$SYNO_Username&passwd=$SYNO_Password&enable_syno_token=yes")
+ response=$(_get "$_base_url/webman/login.cgi?username=$SYNO_Username&passwd=$SYNO_Password&enable_syno_token=yes&device_id=$SYNO_DID")
token=$(echo "$response" | grep "SynoToken" | sed -n 's/.*"SynoToken" *: *"\([^"]*\).*/\1/p')
_debug3 response "$response"
@@ -99,6 +101,7 @@ synology_dsm_deploy() {
# Now that we know the username and password are good, save them
_savedeployconf SYNO_Username "$SYNO_Username"
_savedeployconf SYNO_Password "$SYNO_Password"
+ _savedeployconf SYNO_DID "$SYNO_DID"
_debug token "$token"
_info "Getting certificates in Synology DSM"
diff --git a/dnsapi/dns_1984hosting.sh b/dnsapi/dns_1984hosting.sh
new file mode 100755
index 00000000..b7cb36d7
--- /dev/null
+++ b/dnsapi/dns_1984hosting.sh
@@ -0,0 +1,254 @@
+#!/usr/bin/env sh
+#This file name is "dns_1984hosting.sh"
+#So, here must be a method dns_1984hosting_add()
+#Which will be called by acme.sh to add the txt record to your api system.
+#returns 0 means success, otherwise error.
+#
+#Author: Adrian Fedoreanu
+#Report Bugs here: https://github.com/acmesh-official/acme.sh
+# or here... https://github.com/acmesh-official/acme.sh/issues/2851
+#
+######## Public functions #####################
+
+# Export 1984HOSTING username and password in following variables
+#
+# One984HOSTING_Username=username
+# One984HOSTING_Password=password
+#
+# sessionid cookie is saved in ~/.acme.sh/account.conf
+# username/password need to be set only when changed.
+
+#Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_1984hosting_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Add TXT record using 1984Hosting"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ if ! _1984hosting_login; then
+ _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _1984hosting_add_txt_record "$_domain" "$_sub_domain" "$txtvalue"
+ return $?
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_1984hosting_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Delete TXT record using 1984Hosting"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ if ! _1984hosting_login; then
+ _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _1984hosting_delete_txt_record "$_domain" "$_sub_domain"
+ return $?
+}
+
+#################### Private functions below ##################################
+
+# usage _1984hosting_add_txt_record domain subdomain value
+# returns 0 success
+_1984hosting_add_txt_record() {
+ _debug "Add TXT record $1 with value '$3'"
+ domain="$1"
+ subdomain="$2"
+ value="$(printf '%s' "$3" | _url_encode)"
+ url="https://management.1984hosting.com/domains/entry/"
+
+ postdata="entry=new"
+ postdata="$postdata&type=TXT"
+ postdata="$postdata&ttl=3600"
+ postdata="$postdata&zone=$domain"
+ postdata="$postdata&host=$subdomain"
+ postdata="$postdata&rdata=%22$value%22"
+ _debug2 postdata "$postdata"
+
+ _authpost "$postdata" "$url"
+ response="$(echo "$_response" | _normalizeJson)"
+ _debug2 response "$response"
+
+ if _contains "$response" '"haserrors": true'; then
+ _err "1984Hosting failed to add TXT record for $subdomain bad RC from _post"
+ return 1
+ elif _contains "$response" ""; then
+ _err "1984Hosting failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
+ return 1
+ elif [ "$response" = '{"auth": false, "ok": false}' ]; then
+ _err "1984Hosting failed to add TXT record for $subdomain. Invalid or expired cookie"
+ return 1
+ fi
+
+ _info "Added acme challenge TXT record for $fulldomain at 1984Hosting"
+ return 0
+}
+
+# usage _1984hosting_delete_txt_record entry_id
+# returns 0 success
+_1984hosting_delete_txt_record() {
+ _debug "Delete $fulldomain TXT record"
+ domain="$1"
+ subdomain="$2"
+ url="https://management.1984hosting.com/domains"
+
+ _htmlget "$url" "$domain"
+ _debug2 _response "$_response"
+ zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+')"
+ _debug2 zone_id "$zone_id"
+ if [ -z "$zone_id" ]; then
+ _err "Error getting zone_id for $1"
+ return 1
+ fi
+
+ _htmlget "$url/$zone_id" "$subdomain"
+ _debug2 _response "$_response"
+ entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
+ _debug2 entry_id "$entry_id"
+ if [ -z "$entry_id" ]; then
+ _err "Error getting TXT entry_id for $1"
+ return 1
+ fi
+
+ _authpost "entry=$entry_id" "$url/delentry/"
+ response="$(echo "$_response" | _normalizeJson)"
+ _debug2 response "$response"
+
+ if ! _contains "$response" '"ok": true'; then
+ _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
+ return 1
+ fi
+
+ _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting"
+ return 0
+}
+
+# usage: _1984hosting_login username password
+# returns 0 success
+_1984hosting_login() {
+ if ! _check_credentials; then return 1; fi
+
+ if _check_cookie; then
+ _debug "Already logged in"
+ return 0
+ fi
+
+ _debug "Login to 1984Hosting as user $One984HOSTING_Username"
+ username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
+ password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
+ url="https://management.1984hosting.com/accounts/checkuserauth/"
+
+ response="$(_post "username=$username&password=$password&otpkey=" "$url")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug2 response "$response"
+
+ if [ "$response" = '{"loggedin": true, "ok": true}' ]; then
+ One984HOSTING_COOKIE="$(grep '^Set-Cookie:' "$HTTP_HEADER" | _tail_n 1 | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
+ export One984HOSTING_COOKIE
+ _saveaccountconf_mutable One984HOSTING_COOKIE "$One984HOSTING_COOKIE"
+ return 0
+ fi
+ return 1
+}
+
+_check_credentials() {
+ if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
+ One984HOSTING_Username=""
+ One984HOSTING_Password=""
+ _err "You haven't specified 1984Hosting username or password yet."
+ _err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
+ return 1
+ fi
+ return 0
+}
+
+_check_cookie() {
+ One984HOSTING_COOKIE="${One984HOSTING_COOKIE:-$(_readaccountconf_mutable One984HOSTING_COOKIE)}"
+ if [ -z "$One984HOSTING_COOKIE" ]; then
+ _debug "No cached cookie found"
+ return 1
+ fi
+
+ _authget "https://management.1984hosting.com/accounts/loginstatus/"
+ response="$(echo "$_response" | _normalizeJson)"
+ if [ "$_response" = '{"ok": true}' ]; then
+ _debug "Cached cookie still valid"
+ return 0
+ fi
+ _debug "Cached cookie no longer valid"
+ One984HOSTING_COOKIE=""
+ _saveaccountconf_mutable One984HOSTING_COOKIE "$One984HOSTING_COOKIE"
+ return 1
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain="$1"
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _authget "https://management.1984hosting.com/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
+ if _contains "$_response" "serial"; 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
+}
+
+# add extra headers to request
+_authget() {
+ export _H1="Cookie: $One984HOSTING_COOKIE"
+ _response=$(_get "$1")
+}
+
+# truncate huge HTML response
+# echo: Argument list too long
+_htmlget() {
+ export _H1="Cookie: $One984HOSTING_COOKIE"
+ _response=$(_get "$1" | grep "$2" | _head_n 1)
+}
+
+# add extra headers to request
+_authpost() {
+ export _H1="Cookie: $One984HOSTING_COOKIE"
+ _response=$(_post "$1" "$2")
+}
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_aws.sh b/dnsapi/dns_aws.sh
index 0503d0f2..ea4736c4 100755
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -23,6 +23,7 @@ dns_aws_add() {
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
+ AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
_use_container_role || _use_instance_role
@@ -40,6 +41,7 @@ dns_aws_add() {
if [ -z "$_using_role" ]; then
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
+ _saveaccountconf_mutable AWS_DNS_SLOWRATE "$AWS_DNS_SLOWRATE"
fi
_debug "First detect the root zone"
@@ -77,7 +79,13 @@ dns_aws_add() {
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "TXT record updated successfully."
- _sleep 1
+ if [ -n "$AWS_DNS_SLOWRATE" ]; then
+ _info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
+ _sleep "$AWS_DNS_SLOWRATE"
+ else
+ _sleep 1
+ fi
+
return 0
fi
_sleep 1
@@ -91,6 +99,7 @@ dns_aws_rm() {
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
+ AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
_use_container_role || _use_instance_role
@@ -125,7 +134,13 @@ dns_aws_rm() {
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "TXT record deleted successfully."
- _sleep 1
+ if [ -n "$AWS_DNS_SLOWRATE" ]; then
+ _info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
+ _sleep "$AWS_DNS_SLOWRATE"
+ else
+ _sleep 1
+ fi
+
return 0
fi
_sleep 1
diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh
index 2927ab4b..43bc1428 100755
--- a/dnsapi/dns_cf.sh
+++ b/dnsapi/dns_cf.sh
@@ -94,6 +94,7 @@ dns_cf_rm() {
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
+ CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
@@ -110,7 +111,7 @@ dns_cf_rm() {
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
- _err "Error"
+ _err "Error: $response"
return 1
fi
diff --git a/dnsapi/dns_constellix.sh b/dnsapi/dns_constellix.sh
index c47ede44..42df710d 100644
--- a/dnsapi/dns_constellix.sh
+++ b/dnsapi/dns_constellix.sh
@@ -86,12 +86,12 @@ _get_root() {
return 1
fi
- if ! _constellix_rest GET "domains"; then
+ if ! _constellix_rest GET "domains/search?exact=$h"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\""; then
- _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | head -n 1 | cut -d ':' -f 2 | tr -d '}')
+ _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+" | cut -d ':' -f 2)
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
_domain="$h"
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_dp.sh b/dnsapi/dns_dp.sh
index 480c1f9a..033fa5aa 100755
--- a/dnsapi/dns_dp.sh
+++ b/dnsapi/dns_dp.sh
@@ -53,7 +53,7 @@ dns_dp_rm() {
return 1
fi
- if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
+ if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
_err "Record.Lis error."
return 1
fi
@@ -70,12 +70,12 @@ dns_dp_rm() {
return 1
fi
- if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
+ if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&record_id=$record_id"; then
_err "Record.Remove error."
return 1
fi
- _contains "$response" "Action completed successful"
+ _contains "$response" "successful"
}
@@ -89,11 +89,11 @@ add_record() {
_info "Adding record"
- if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
+ if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
return 1
fi
- _contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
+ _contains "$response" "successful" || _contains "$response" "Domain record already exists"
}
#################### Private functions below ##################################
@@ -113,11 +113,11 @@ _get_root() {
return 1
fi
- if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then
+ if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain=$h"; then
return 1
fi
- if _contains "$response" "Action completed successful"; then
+ if _contains "$response" "successful"; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
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 f4590cf8..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"
@@ -55,6 +59,7 @@ dns_inwx_rm() {
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
+ INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
INWX_User=""
INWX_Password=""
@@ -63,9 +68,9 @@ dns_inwx_rm() {
return 1
fi
- #save the api key and email to the account conf file.
- _saveaccountconf_mutable INWX_User "$INWX_User"
- _saveaccountconf_mutable INWX_Password "$INWX_Password"
+ if ! _inwx_login; then
+ return 1
+ fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -126,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
@@ -151,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
@@ -194,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
}
@@ -206,8 +258,6 @@ _get_root() {
i=2
p=1
- _inwx_login
-
xml_content='
nameserver.list
diff --git a/dnsapi/dns_joker.sh b/dnsapi/dns_joker.sh
new file mode 100644
index 00000000..5d50953e
--- /dev/null
+++ b/dnsapi/dns_joker.sh
@@ -0,0 +1,129 @@
+#!/usr/bin/env sh
+
+# Joker.com API for acme.sh
+#
+# This script adds the necessary TXT record to a domain in Joker.com.
+#
+# You must activate Dynamic DNS in Joker.com DNS configuration first.
+# Username and password below refer to Dynamic DNS authentication,
+# not your Joker.com login credentials.
+# See: https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
+#
+# NOTE: This script does not support wildcard certificates, because
+# Joker.com API does not support adding two TXT records with the same
+# subdomain. Adding the second record will overwrite the first one.
+# See: https://joker.com/faq/content/6/496/en/let_s-encrypt-support.html
+# "... this request will replace all TXT records for the specified
+# label by the provided content"
+#
+# Author: aattww (https://github.com/aattww/)
+#
+# Report bugs to https://github.com/acmesh-official/acme.sh/issues/2840
+#
+# JOKER_USERNAME="xxxx"
+# JOKER_PASSWORD="xxxx"
+
+JOKER_API="https://svc.joker.com/nic/replace"
+
+######## Public functions #####################
+
+#Usage: dns_joker_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_joker_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ JOKER_USERNAME="${JOKER_USERNAME:-$(_readaccountconf_mutable JOKER_USERNAME)}"
+ JOKER_PASSWORD="${JOKER_PASSWORD:-$(_readaccountconf_mutable JOKER_PASSWORD)}"
+
+ if [ -z "$JOKER_USERNAME" ] || [ -z "$JOKER_PASSWORD" ]; then
+ _err "No Joker.com username and password specified."
+ return 1
+ fi
+
+ _saveaccountconf_mutable JOKER_USERNAME "$JOKER_USERNAME"
+ _saveaccountconf_mutable JOKER_PASSWORD "$JOKER_PASSWORD"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _info "Adding TXT record"
+ if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT&value=$txtvalue"; then
+ if _startswith "$response" "OK"; then
+ _info "Added, OK"
+ return 0
+ fi
+ fi
+ _err "Error adding TXT record."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_joker_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ JOKER_USERNAME="${JOKER_USERNAME:-$(_readaccountconf_mutable JOKER_USERNAME)}"
+ JOKER_PASSWORD="${JOKER_PASSWORD:-$(_readaccountconf_mutable JOKER_PASSWORD)}"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _info "Removing TXT record"
+ # TXT record is removed by setting its value to empty.
+ if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT&value="; then
+ if _startswith "$response" "OK"; then
+ _info "Removed, OK"
+ return 0
+ fi
+ fi
+ _err "Error removing TXT record."
+ return 1
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+ i=1
+ while true; do
+ h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ # Try to remove a test record. With correct root domain, username and password this will return "OK: ..." regardless
+ # of record in question existing or not.
+ if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$h&label=jokerTXTUpdateTest&type=TXT&value="; then
+ if _startswith "$response" "OK"; then
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
+ _domain=$h
+ return 0
+ fi
+ fi
+
+ i=$(_math "$i" + 1)
+ done
+
+ _debug "Root domain not found"
+ return 1
+}
+
+_joker_rest() {
+ data="$1"
+ _debug data "$data"
+
+ if ! response="$(_post "$data" "$JOKER_API" "" "POST")"; then
+ _err "Error POSTing"
+ return 1
+ fi
+ _debug response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh
index db51cc7c..49007402 100644
--- a/dnsapi/dns_me.sh
+++ b/dnsapi/dns_me.sh
@@ -114,7 +114,7 @@ _get_root() {
fi
if _contains "$response" "\"name\":\"$h\""; then
- _domain_id=$(printf "%s\n" "$response" | cut -c 2- | head -c -2 | sed 's/{.*}//' | sed -r 's/^.*"id":([0-9]+).*$/\1/')
+ _domain_id=$(printf "%s\n" "$response" | sed 's/^{//; s/}$//; s/{.*}//' | sed -r 's/^.*"id":([0-9]+).*$/\1/')
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
diff --git a/dnsapi/dns_nm.sh b/dnsapi/dns_nm.sh
deleted file mode 100644
index ec0e2d88..00000000
--- a/dnsapi/dns_nm.sh
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env sh
-
-########################################################################
-# https://namemaster.de hook script for acme.sh
-#
-# Environment variables:
-#
-# - $NM_user (your namemaster.de API username)
-# - $NM_md5 (your namemaster.de API password_as_md5hash)
-#
-# Author: Thilo Gass
-# Git repo: https://github.com/ThiloGa/acme.sh
-
-#-- dns_nm_add() - Add TXT record --------------------------------------
-# Usage: dns_nm_add _acme-challenge.subdomain.domain.com "XyZ123..."
-
-dns_nm_add() {
- fulldomain=$1
- txt_value=$2
- _info "Using DNS-01 namemaster hook"
-
- NM_user="${NM_user:-$(_readaccountconf_mutable NM_user)}"
- NM_md5="${NM_md5:-$(_readaccountconf_mutable NM_md5)}"
- if [ -z "$NM_user" ] || [ -z "$NM_md5" ]; then
- NM_user=""
- NM_md5=""
- _err "No auth details provided. Please set user credentials using the \$NM_user and \$NM_md5 environment variables."
- return 1
- fi
- #save the api user and md5 password to the account conf file.
- _debug "Save user and hash"
- _saveaccountconf_mutable NM_user "$NM_user"
- _saveaccountconf_mutable NM_md5 "$NM_md5"
-
- zone="$(echo $fulldomain | _egrep_o "[^.]+.[^.]+$")"
- get="https://namemaster.de/api/api.php?User=$NM_user&Password=$NM_md5&Antwort=csv&Int=0&Typ=ACME&Zone=$zone&hostname=$fulldomain&TXT=$txt_value&Action=Auto&Lifetime=3600"
- erg="$(_get "$get")"
-
- exit_code="$?"
- if [ "$exit_code" != 0 ]; then
- _err "error Ading $zone TXT: $txt_value"
- _err "Error $exit_code"
- return 1
- fi
-
- if _contains "$erg" "Success"; then
- _info "Success, TXT Added, OK"
- else
- _err "error Adding $zone TXT: $txt_value erg: $erg"
- return 1
- fi
-
- _debug "ok Auto $zone TXT: $txt_value erg: $erg"
- return 0
-}
-
-dns_nm_rm() {
-
- fulldomain=$1
- txt_value=$2
-
- NM_user="${NM_user:-$(_readaccountconf_mutable NM_user)}"
- NM_md5="${NM_md5:-$(_readaccountconf_mutable NM_md5)}"
- if [ -z "$NM_user" ] || [ -z "$NM_md5" ]; then
- NM_user=""
- NM_md5=""
- _err "No auth details provided. Please set user credentials using the \$NM_user and \$NM_md5 environment variables."
- return 1
- fi
-
- zone="$(echo $fulldomain | _egrep_o "[^.]+.[^.]+$")"
- get="https://namemaster.de/api/api.php?User=$NM_user&Password=$NM_md5&Antwort=csv&Int=0&Typ=TXT&Zone=$zone&hostname=$fulldomain&TXT=$txt_value&Action=Delete_IN&TTL=0"
- erg="$(_get "$get")"
-
- exit_code="$?"
- if [ "$exit_code" != "0" ]; then
- _err "error Deleting $zone TXT: $txt_value"
- _err "Error $exit_code"
- return 1
- fi
-
- if _contains "$erg" "Success"; then
- _info "Success, TXT removed, OK"
- else
- _err "error Auto $zone TXT: $txt_value erg: $erg"
- return 1
- fi
-
- _debug "ok Auto $zone TXT: $txt_value erg: $erg"
- return 0
-
-}
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
deleted file mode 100755
index ae2e864e..00000000
--- a/dnsapi/dns_opnsense.sh
+++ /dev/null
@@ -1,277 +0,0 @@
-#!/usr/bin/env sh
-
-#OPNsense Bind API
-#https://docs.opnsense.org/development/api.html
-#
-#OPNs_Host="opnsense.example.com"
-#OPNs_Port="443"
-# optional, defaults to 443 if unset
-#OPNs_Key="qocfU9RSbt8vTIBcnW8bPqCrpfAHMDvj5OzadE7Str+rbjyCyk7u6yMrSCHtBXabgDDXx/dY0POUp7ZA"
-#OPNs_Token="pZEQ+3ce8dDlfBBdg3N8EpqpF5I1MhFqdxX06le6Gl8YzyQvYCfCzNaFX9O9+IOSyAs7X71fwdRiZ+Lv"
-#OPNs_Api_Insecure=0
-# optional, defaults to 0 if unset
-# Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
-
-######## Public functions #####################
-#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
-#fulldomain
-#txtvalue
-OPNs_DefaultPort=443
-OPNs_DefaultApi_Insecure=0
-
-dns_opnsense_add() {
- fulldomain=$1
- txtvalue=$2
-
- _opns_check_auth || return 1
-
- if ! set_record "$fulldomain" "$txtvalue"; then
- return 1
- fi
-
- return 0
-}
-
-#fulldomain
-dns_opnsense_rm() {
- fulldomain=$1
- txtvalue=$2
-
- _opns_check_auth || return 1
-
- if ! rm_record "$fulldomain" "$txtvalue"; then
- return 1
- fi
-
- return 0
-}
-
-set_record() {
- fulldomain=$1
- new_challenge=$2
- _info "Adding record $fulldomain with challenge: $new_challenge"
-
- _debug "Detect root zone"
- if ! _get_root "$fulldomain"; then
- _err "invalid domain"
- return 1
- fi
-
- _debug _domain "$_domain"
- _debug _host "$_host"
- _debug _domainid "$_domainid"
- _return_str=""
- _record_string=""
- _build_record_string "$_domainid" "$_host" "$new_challenge"
- _uuid=""
- if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
- # Update
- if _opns_rest "POST" "/record/setRecord/${_uuid}" "$_record_string"; then
- _return_str="$response"
- else
- return 1
- fi
-
- else
- #create
- if _opns_rest "POST" "/record/addRecord" "$_record_string"; then
- _return_str="$response"
- else
- return 1
- fi
- fi
-
- if echo "$_return_str" | _egrep_o "\"result\":\"saved\"" >/dev/null; then
- _opns_rest "POST" "/service/reconfigure" "{}"
- _debug "Record created"
- else
- _err "Error creating record $_record_string"
- return 1
- fi
-
- return 0
-}
-
-rm_record() {
- fulldomain=$1
- new_challenge="$2"
- _info "Remove record $fulldomain with challenge: $new_challenge"
-
- _debug "Detect root zone"
- if ! _get_root "$fulldomain"; then
- _err "invalid domain"
- return 1
- fi
-
- _debug _domain "$_domain"
- _debug _host "$_host"
- _debug _domainid "$_domainid"
- _uuid=""
- if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
- # Delete
- if _opns_rest "POST" "/record/delRecord/${_uuid}" "\{\}"; then
- if echo "$_return_str" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
- _opns_rest "POST" "/service/reconfigure" "{}"
- _debug "Record deleted"
- else
- _err "Error deleting record $_host from domain $fulldomain"
- return 1
- fi
- else
- _err "Error deleting record $_host from domain $fulldomain"
- return 1
- fi
- else
- _info "Record not found, nothing to remove"
- fi
-
- return 0
-}
-
-#################### Private functions below ##################################
-#_acme-challenge.www.domain.com
-#returns
-# _domainid=domid
-#_domain=domain.com
-_get_root() {
- domain=$1
- i=2
- p=1
- if _opns_rest "GET" "/domain/get"; then
- _domain_response="$response"
- else
- return 1
- fi
-
- while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
- if [ -z "$h" ]; then
- #not valid
- return 1
- fi
- _debug h "$h"
-<<<<<<< HEAD
- 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)
->>>>>>> Allow old and new API response
-
- if [ -n "$id" ]; then
- _debug id "$id"
- _host=$(printf "%s" "$domain" | cut -d . -f 1-$p)
- _domain="${h}"
- _domainid="${id}"
- return 0
- fi
- p=$i
- i=$(_math $i + 1)
- done
- _debug "$domain not found"
-
- return 1
-}
-
-_opns_rest() {
- method=$1
- ep=$2
- data=$3
- #Percent encode user and token
- key=$(echo "$OPNs_Key" | tr -d "\n\r" | _url_encode)
- token=$(echo "$OPNs_Token" | tr -d "\n\r" | _url_encode)
-
- opnsense_url="https://${key}:${token}@${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
- export _H1="Content-Type: application/json"
- _debug2 "Try to call api: https://${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
- if [ ! "$method" = "GET" ]; then
- _debug data "$data"
- export _H1="Content-Type: application/json"
- response="$(_post "$data" "$opnsense_url" "" "$method")"
- else
- export _H1=""
- response="$(_get "$opnsense_url")"
- fi
-
- if [ "$?" != "0" ]; then
- _err "error $ep"
- return 1
- fi
- _debug2 response "$response"
-
- return 0
-}
-
-_build_record_string() {
- _record_string="{\"record\":{\"enabled\":\"1\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"}}"
-}
-
-_existingchallenge() {
- if _opns_rest "GET" "/record/searchRecord"; then
- _record_response="$response"
- else
- return 1
- fi
- _uuid=""
- _uuid=$(echo "$_record_response" | _egrep_o "\"uuid\":\"[^\"]*\",\"enabled\":\"[01]\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
-
- if [ -n "$_uuid" ]; then
- _debug uuid "$_uuid"
- return 0
- fi
- _debug "${2}.$1{1} record not found"
-
- return 1
-}
-
-_opns_check_auth() {
- OPNs_Host="${OPNs_Host:-$(_readaccountconf_mutable OPNs_Host)}"
- OPNs_Port="${OPNs_Port:-$(_readaccountconf_mutable OPNs_Port)}"
- OPNs_Key="${OPNs_Key:-$(_readaccountconf_mutable OPNs_Key)}"
- OPNs_Token="${OPNs_Token:-$(_readaccountconf_mutable OPNs_Token)}"
- OPNs_Api_Insecure="${OPNs_Api_Insecure:-$(_readaccountconf_mutable OPNs_Api_Insecure)}"
-
- if [ -z "$OPNs_Host" ]; then
- _err "You don't specify OPNsense address."
- return 1
- else
- _saveaccountconf_mutable OPNs_Host "$OPNs_Host"
- fi
-
- if ! printf '%s' "$OPNs_Port" | grep '^[0-9]*$' >/dev/null; then
- _err 'OPNs_Port specified but not numeric value'
- return 1
- elif [ -z "$OPNs_Port" ]; then
- _info "OPNSense port not specified. Defaulting to using port $OPNs_DefaultPort"
- else
- _saveaccountconf_mutable OPNs_Port "$OPNs_Port"
- fi
-
- if ! printf '%s' "$OPNs_Api_Insecure" | grep '^[01]$' >/dev/null; then
- _err 'OPNs_Api_Insecure specified but not 0/1 value'
- return 1
- elif [ -n "$OPNs_Api_Insecure" ]; then
- _saveaccountconf_mutable OPNs_Api_Insecure "$OPNs_Api_Insecure"
- fi
- export HTTPS_INSECURE="${OPNs_Api_Insecure:-$OPNs_DefaultApi_Insecure}"
-
- if [ -z "$OPNs_Key" ]; then
- _err "you have not specified your OPNsense api key id."
- _err "Please set OPNs_Key and try again."
- return 1
- else
- _saveaccountconf_mutable OPNs_Key "$OPNs_Key"
- fi
-
- if [ -z "$OPNs_Token" ]; then
- _err "you have not specified your OPNsense token."
- _err "Please create OPNs_Token and try again."
- return 1
- else
- _saveaccountconf_mutable OPNs_Token "$OPNs_Token"
- fi
-
- if ! _opns_rest "GET" "/general/get"; then
- _err "Call to OPNsense API interface failed. Unable to access OPNsense API."
- return 1
- fi
- return 0
-}
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