diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml index ed0426ad..5dc2d453 100644 --- a/.github/workflows/DNS.yml +++ b/.github/workflows/DNS.yml @@ -184,7 +184,7 @@ jobs: - uses: actions/checkout@v2 - name: Clone acmetest run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ - - uses: vmactions/freebsd-vm@v0.1.2 + - uses: vmactions/freebsd-vm@v0.0.7 with: envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}' prepare: pkg install -y socat curl @@ -223,7 +223,7 @@ jobs: - uses: actions/checkout@v2 - name: Clone acmetest run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ - - uses: vmactions/solaris-vm@v0.0.3 + - uses: vmactions/solaris-vm@v0.0.1 with: envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}' prepare: pkgutil -y -i socat curl diff --git a/.github/workflows/LetsEncrypt.yml b/.github/workflows/LetsEncrypt.yml index 7c398c09..8d0c4eb0 100644 --- a/.github/workflows/LetsEncrypt.yml +++ b/.github/workflows/LetsEncrypt.yml @@ -111,7 +111,7 @@ jobs: - uses: actions/checkout@v2 - name: Clone acmetest run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ - - uses: vmactions/freebsd-vm@v0.1.2 + - uses: vmactions/freebsd-vm@v0.0.7 with: envs: 'NGROK_TOKEN TEST_LOCAL' prepare: pkg install -y socat curl @@ -136,7 +136,7 @@ jobs: run: echo "TestingDomain=${{steps.ngrok.outputs.server}}" >> $GITHUB_ENV - name: Clone acmetest run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/ - - uses: vmactions/solaris-vm@v0.0.3 + - uses: vmactions/solaris-vm@v0.0.1 with: envs: 'TEST_LOCAL TestingDomain' nat: | diff --git a/acme.sh b/acme.sh index 8d422719..a1ad4195 100755 --- a/acme.sh +++ b/acme.sh @@ -562,16 +562,8 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then fi _h2b() { - if _exists xxd; then - if _contains "$(xxd --help 2>&1)" "assumes -c30"; then - if xxd -r -p -c 9999 2>/dev/null; then - return - fi - else - if xxd -r -p 2>/dev/null; then - return - fi - fi + if _exists xxd && xxd -r -p 2>/dev/null; then + return fi hex=$(cat) @@ -1132,7 +1124,7 @@ _createkey() { if _isEccKey "$length"; then _debug "Using ec name: $eccname" - if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -noout -genkey 2>/dev/null)"; then + if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null)"; then echo "$_opkey" >"$f" else _err "error ecc key name: $eccname" @@ -1140,11 +1132,7 @@ _createkey() { fi else _debug "Using RSA: $length" - __traditional="" - if _contains "$(${ACME_OPENSSL_BIN:-openssl} help genrsa 2>&1)" "-traditional"; then - __traditional="-traditional" - fi - if _opkey="$(${ACME_OPENSSL_BIN:-openssl} genrsa $__traditional "$length" 2>/dev/null)"; then + if _opkey="$(${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null)"; then echo "$_opkey" >"$f" else _err "error rsa key: $length" @@ -2133,12 +2121,6 @@ _send_signed_request() { _sleep $_sleep_retry_sec continue fi - if _contains "$_body" "The Replay Nonce is not recognized"; then - _info "The replay Nonce is not valid, let's get a new one, Sleeping $_sleep_retry_sec seconds." - _CACHED_NONCE="" - _sleep $_sleep_retry_sec - continue - fi fi return 0 done @@ -2297,13 +2279,6 @@ _clearaccountconf() { _clear_conf "$ACCOUNT_CONF_PATH" "$1" } -#key -_clearaccountconf_mutable() { - _clearaccountconf "SAVED_$1" - #remove later - _clearaccountconf "$1" -} - #_savecaconf key value _savecaconf() { _save_conf "$CA_CONF" "$1" "$2" @@ -4034,42 +4009,12 @@ _check_dns_entries() { } #file -_get_chain_issuers() { +_get_cert_issuers() { _cfile="$1" - if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -help 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 help 2>&1)" "unknown option help"; then - ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | ${ACME_OPENSSL_BIN:-openssl} pkcs7 -print_certs -text -noout | grep -i 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 + if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 help 2>&1)" "unknown option help"; then + ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | ${ACME_OPENSSL_BIN:-openssl} pkcs7 -print_certs -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 else - _cindex=1 - for _startn in $(grep -n -- "$BEGIN_CERT" "$_cfile" | cut -d : -f 1); do - _endn="$(grep -n -- "$END_CERT" "$_cfile" | cut -d : -f 1 | _head_n $_cindex | _tail_n 1)" - _debug2 "_startn" "$_startn" - _debug2 "_endn" "$_endn" - if [ "$DEBUG" ]; then - _debug2 "cert$_cindex" "$(sed -n "$_startn,${_endn}p" "$_cfile")" - fi - sed -n "$_startn,${_endn}p" "$_cfile" | ${ACME_OPENSSL_BIN:-openssl} x509 -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 | sed "s/ *\(.*\)/\1/" - _cindex=$(_math $_cindex + 1) - done - fi -} - -# -_get_chain_subjects() { - _cfile="$1" - if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -help 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 help 2>&1)" "unknown option help"; then - ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | ${ACME_OPENSSL_BIN:-openssl} pkcs7 -print_certs -text -noout | grep -i 'Subject:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 - else - _cindex=1 - for _startn in $(grep -n -- "$BEGIN_CERT" "$_cfile" | cut -d : -f 1); do - _endn="$(grep -n -- "$END_CERT" "$_cfile" | cut -d : -f 1 | _head_n $_cindex | _tail_n 1)" - _debug2 "_startn" "$_startn" - _debug2 "_endn" "$_endn" - if [ "$DEBUG" ]; then - _debug2 "cert$_cindex" "$(sed -n "$_startn,${_endn}p" "$_cfile")" - fi - sed -n "$_startn,${_endn}p" "$_cfile" | ${ACME_OPENSSL_BIN:-openssl} x509 -text -noout | grep -i 'Subject:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 | sed "s/ *\(.*\)/\1/" - _cindex=$(_math $_cindex + 1) - done + ${ACME_OPENSSL_BIN:-openssl} x509 -in $_cfile -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 fi } @@ -4077,12 +4022,14 @@ _get_chain_subjects() { _match_issuer() { _cfile="$1" _missuer="$2" - _fissuers="$(_get_chain_issuers $_cfile)" + _fissuers="$(_get_cert_issuers $_cfile)" _debug2 _fissuers "$_fissuers" - _rootissuer="$(echo "$_fissuers" | _lower_case | _tail_n 1)" - _debug2 _rootissuer "$_rootissuer" + if _contains "$_fissuers" "$_missuer"; then + return 0 + fi + _fissuers="$(echo "$_fissuers" | _lower_case)" _missuer="$(echo "$_missuer" | _lower_case)" - _contains "$_rootissuer" "$_missuer" + _contains "$_fissuers" "$_missuer" } #webroot, domain domainlist keylength @@ -4856,9 +4803,6 @@ $_authorizations_map" _split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH" if [ "$_preferred_chain" ] && [ -f "$CERT_FULLCHAIN_PATH" ]; then - if [ "$DEBUG" ]; then - _debug "default chain issuers: " "$(_get_chain_issuers "$CERT_FULLCHAIN_PATH")" - fi if ! _match_issuer "$CERT_FULLCHAIN_PATH" "$_preferred_chain"; then rels="$(echo "$responseHeaders" | tr -d ' <>' | grep -i "^link:" | grep -i 'rel="alternate"' | cut -d : -f 2- | cut -d ';' -f 1)" _debug2 "rels" "$rels" @@ -4874,22 +4818,13 @@ $_authorizations_map" _relca="$CA_CERT_PATH.alt" echo "$response" >"$_relcert" _split_cert_chain "$_relcert" "$_relfullchain" "$_relca" - if [ "$DEBUG" ]; then - _debug "rel chain issuers: " "$(_get_chain_issuers "$_relfullchain")" - fi if _match_issuer "$_relfullchain" "$_preferred_chain"; then _info "Matched issuer in: $rel" cat $_relcert >"$CERT_PATH" cat $_relfullchain >"$CERT_FULLCHAIN_PATH" cat $_relca >"$CA_CERT_PATH" - rm -f "$_relcert" - rm -f "$_relfullchain" - rm -f "$_relca" break fi - rm -f "$_relcert" - rm -f "$_relfullchain" - rm -f "$_relca" done fi fi @@ -5287,7 +5222,6 @@ signcsr() { _renew_hook="${10}" _local_addr="${11}" _challenge_alias="${12}" - _preferred_chain="${13}" _csrsubj=$(_readSubjectFromCSR "$_csrfile") if [ "$?" != "0" ]; then @@ -5334,7 +5268,7 @@ signcsr() { _info "Copy csr to: $CSR_PATH" cp "$_csrfile" "$CSR_PATH" - issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias" "$_preferred_chain" + issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias" } @@ -7431,7 +7365,7 @@ _process() { deploy "$_domain" "$_deploy_hook" "$_ecc" ;; signcsr) - signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain" + signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" ;; showcsr) showcsr "$_csr" "$_domain" diff --git a/deploy/unifi.sh b/deploy/unifi.sh index a864135e..184aa62e 100644 --- a/deploy/unifi.sh +++ b/deploy/unifi.sh @@ -1,43 +1,12 @@ #!/usr/bin/env sh -# Here is a script to deploy cert on a Unifi Controller or Cloud Key device. -# It supports: -# - self-hosted Unifi Controller -# - Unifi Cloud Key (Gen1/2/2+) -# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only) -# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359 +#Here is a script to deploy cert to unifi server. #returns 0 means success, otherwise error. -# The deploy-hook automatically detects standard Unifi installations -# for each of the supported environments. Most users should not need -# to set any of these variables, but if you are running a self-hosted -# Controller with custom locations, set these as necessary before running -# the deploy hook. (Defaults shown below.) -# -# Settings for Unifi Controller: -# Location of Java keystore or unifi.keystore.jks file: #DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" -# Keystore password (built into Unifi Controller, not a user-set password): #DEPLOY_UNIFI_KEYPASS="aircontrolenterprise" -# Command to restart Unifi Controller: #DEPLOY_UNIFI_RELOAD="service unifi restart" -# -# Settings for Unifi Cloud Key Gen1 (nginx admin pages): -# Directory where cloudkey.crt and cloudkey.key live: -#DEPLOY_UNIFI_CLOUDKEY_CERTDIR="/etc/ssl/private" -# Command to restart maintenance pages and Controller -# (same setting as above, default is updated when running on Cloud Key Gen1): -#DEPLOY_UNIFI_RELOAD="service nginx restart && service unifi restart" -# -# Settings for UnifiOS (Cloud Key Gen2): -# Directory where unifi-core.crt and unifi-core.key live: -#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/" -# Command to restart unifi-core: -#DEPLOY_UNIFI_RELOAD="systemctl restart unifi-core" -# -# At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR, -# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs. ######## Public functions ##################### @@ -55,160 +24,77 @@ unifi_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _getdeployconf DEPLOY_UNIFI_KEYSTORE - _getdeployconf DEPLOY_UNIFI_KEYPASS - _getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR - _getdeployconf DEPLOY_UNIFI_CORE_CONFIG - _getdeployconf DEPLOY_UNIFI_RELOAD + if ! _exists keytool; then + _err "keytool not found" + return 1 + fi - _debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" - _debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" - _debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" - _debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG" - _debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" + DEFAULT_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" + _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-$DEFAULT_UNIFI_KEYSTORE}" + DEFAULT_UNIFI_KEYPASS="aircontrolenterprise" + _unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-$DEFAULT_UNIFI_KEYPASS}" + DEFAULT_UNIFI_RELOAD="service unifi restart" + _reload="${DEPLOY_UNIFI_RELOAD:-$DEFAULT_UNIFI_RELOAD}" - # Space-separated list of environments detected and installed: - _services_updated="" - - # Default reload commands accumulated as we auto-detect environments: - _reload_cmd="" - - # Unifi Controller environment (self hosted or any Cloud Key) -- - # auto-detect by file /usr/lib/unifi/data/keystore: - _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}" - if [ -f "$_unifi_keystore" ]; then - _info "Installing certificate for Unifi Controller (Java keystore)" - _debug _unifi_keystore "$_unifi_keystore" - if ! _exists keytool; then - _err "keytool not found" + _debug _unifi_keystore "$_unifi_keystore" + if [ ! -f "$_unifi_keystore" ]; then + if [ -z "$DEPLOY_UNIFI_KEYSTORE" ]; then + _err "unifi keystore is not found, please define DEPLOY_UNIFI_KEYSTORE" return 1 - fi - if [ ! -w "$_unifi_keystore" ]; then - _err "The file $_unifi_keystore is not writable, please change the permission." - return 1 - fi - - _unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-aircontrolenterprise}" - - _debug "Generate import pkcs12" - _import_pkcs12="$(_mktemp)" - _toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root - # shellcheck disable=SC2181 - if [ "$?" != "0" ]; then - _err "Error generating pkcs12. Please re-run with --debug and report a bug." - return 1 - fi - - _debug "Import into keystore: $_unifi_keystore" - if keytool -importkeystore \ - -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \ - -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \ - -alias unifi -noprompt; then - _debug "Import keystore success!" - rm "$_import_pkcs12" else - _err "Error importing into Unifi Java keystore." - _err "Please re-run with --debug and report a bug." - rm "$_import_pkcs12" + _err "It seems that the specified unifi keystore is not valid, please check." return 1 fi - - if systemctl -q is-active unifi; then - _reload_cmd="${_reload_cmd:+$_reload_cmd && }service unifi restart" - fi - _services_updated="${_services_updated} unifi" - _info "Install Unifi Controller certificate success!" - elif [ "$DEPLOY_UNIFI_KEYSTORE" ]; then - _err "The specified DEPLOY_UNIFI_KEYSTORE='$DEPLOY_UNIFI_KEYSTORE' is not valid, please check." + fi + if [ ! -w "$_unifi_keystore" ]; then + _err "The file $_unifi_keystore is not writable, please change the permission." return 1 fi - # Cloud Key environment (non-UnifiOS -- nginx serves admin pages) -- - # auto-detect by file /etc/ssl/private/cloudkey.key: - _cloudkey_certdir="${DEPLOY_UNIFI_CLOUDKEY_CERTDIR:-/etc/ssl/private}" - if [ -f "${_cloudkey_certdir}/cloudkey.key" ]; then - _info "Installing certificate for Cloud Key Gen1 (nginx admin pages)" - _debug _cloudkey_certdir "$_cloudkey_certdir" - if [ ! -w "$_cloudkey_certdir" ]; then - _err "The directory $_cloudkey_certdir is not writable; please check permissions." - return 1 - fi - # Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks. - # Normally /usr/lib/unifi/data/keystore is a symlink there (so the keystore was - # updated above), but if not, we don't know how to handle this installation: - if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then - _err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'" - return 1 - fi - - cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt" - cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key" - (cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks) - - if systemctl -q is-active nginx; then - _reload_cmd="${_reload_cmd:+$_reload_cmd && }service nginx restart" - fi - _info "Install Cloud Key Gen1 certificate success!" - _services_updated="${_services_updated} nginx" - elif [ "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" ]; then - _err "The specified DEPLOY_UNIFI_CLOUDKEY_CERTDIR='$DEPLOY_UNIFI_CLOUDKEY_CERTDIR' is not valid, please check." + _info "Generate import pkcs12" + _import_pkcs12="$(_mktemp)" + _toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root + if [ "$?" != "0" ]; then + _err "Oops, error creating import pkcs12, please report bug to us." return 1 fi - # UnifiOS environment -- auto-detect by /data/unifi-core/config/unifi-core.key: - _unifi_core_config="${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}" - if [ -f "${_unifi_core_config}/unifi-core.key" ]; then - _info "Installing certificate for UnifiOS" - _debug _unifi_core_config "$_unifi_core_config" - if [ ! -w "$_unifi_core_config" ]; then - _err "The directory $_unifi_core_config is not writable; please check permissions." - return 1 - fi - - cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt" - cat "$_ckey" >"${_unifi_core_config}/unifi-core.key" - - if systemctl -q is-active unifi-core; then - _reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core" - fi - _info "Install UnifiOS certificate success!" - _services_updated="${_services_updated} unifi-core" - elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then - _err "The specified DEPLOY_UNIFI_CORE_CONFIG='$DEPLOY_UNIFI_CORE_CONFIG' is not valid, please check." + _info "Modify unifi keystore: $_unifi_keystore" + if keytool -importkeystore \ + -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \ + -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \ + -alias unifi -noprompt; then + _info "Import keystore success!" + rm "$_import_pkcs12" + else + _err "Import unifi keystore error, please report bug to us." + rm "$_import_pkcs12" return 1 fi - if [ -z "$_services_updated" ]; then - # None of the Unifi environments were auto-detected, so no deployment has occurred - # (and none of DEPLOY_UNIFI_{KEYSTORE,CLOUDKEY_CERTDIR,CORE_CONFIG} were set). - _err "Unable to detect Unifi environment in standard location." - _err "(This deploy hook must be run on the Unifi device, not a remote machine.)" - _err "For non-standard Unifi installations, set DEPLOY_UNIFI_KEYSTORE," - _err "DEPLOY_UNIFI_CLOUDKEY_CERTDIR, and/or DEPLOY_UNIFI_CORE_CONFIG as appropriate." - return 1 - fi - - _reload_cmd="${DEPLOY_UNIFI_RELOAD:-$_reload_cmd}" - if [ -z "$_reload_cmd" ]; then - _err "Certificates were installed for services:${_services_updated}," - _err "but none appear to be active. Please set DEPLOY_UNIFI_RELOAD" - _err "to a command that will restart the necessary services." - return 1 - fi - _info "Reload services (this may take some time): $_reload_cmd" - if eval "$_reload_cmd"; then + _info "Run reload: $_reload" + if eval "$_reload"; then _info "Reload success!" + if [ "$DEPLOY_UNIFI_KEYSTORE" ]; then + _savedomainconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" + else + _cleardomainconf DEPLOY_UNIFI_KEYSTORE + fi + if [ "$DEPLOY_UNIFI_KEYPASS" ]; then + _savedomainconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" + else + _cleardomainconf DEPLOY_UNIFI_KEYPASS + fi + if [ "$DEPLOY_UNIFI_RELOAD" ]; then + _savedomainconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" + else + _cleardomainconf DEPLOY_UNIFI_RELOAD + fi + return 0 else _err "Reload error" return 1 fi - - # Successful, so save all (non-default) config: - _savedeployconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" - _savedeployconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" - _savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" - _savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG" - _savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" - return 0 + } diff --git a/dnsapi/dns_arvan.sh b/dnsapi/dns_arvan.sh index 4c9217e5..ca1f56c7 100644 --- a/dnsapi/dns_arvan.sh +++ b/dnsapi/dns_arvan.sh @@ -1,9 +1,10 @@ #!/usr/bin/env sh -#Arvan_Token="Apikey xxxx" +#Arvan_Token="xxxx" ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains" -#Author: Vahid Fardi + +#Author: Ehsan Aliakbar #Report Bugs here: https://github.com/Neilpang/acme.sh # ######## Public functions ##################### @@ -37,7 +38,6 @@ dns_arvan_add() { _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 "response id is $response" _info "Added, OK" return 0 elif _contains "$response" "Record Data is Duplicated"; then @@ -49,7 +49,7 @@ dns_arvan_add() { fi fi _err "Add txt record error." - return 0 + return 1 } #Usage: fulldomain txtvalue @@ -73,21 +73,33 @@ dns_arvan_rm() { _debug _domain "$_domain" _debug "Getting txt records" - _arvan_rest GET "${_domain}/dns-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 - _record_id=$(echo "$response" | _egrep_o ".\"id\":\"[^\"]*\",\"type\":\"txt\",\"name\":\"_acme-challenge\",\"value\":{\"text\":\"$txtvalue\"}" | cut -d : -f 2 | cut -d , -f 1 | tr -d \") - if ! _arvan_rest "DELETE" "${_domain}/dns-records/${_record_id}"; then - _err "Error on Arvan Api" - return 1 + 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 - _debug "$response" - _contains "$response" 'dns record deleted' - return 0 } #################### Private functions below ################################## @@ -99,7 +111,7 @@ dns_arvan_rm() { # _domain_id=sdjkglgdfewsdfg _get_root() { domain=$1 - i=2 + i=1 p=1 while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -109,11 +121,12 @@ _get_root() { return 1 fi - if ! _arvan_rest GET "$h"; then + if ! _arvan_rest GET "?search=$h"; then return 1 fi - if _contains "$response" "\"domain\":\"$h\""; then - _domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \") + + 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 @@ -133,6 +146,7 @@ _arvan_rest() { data="$3" token_trimmed=$(echo "$Arvan_Token" | tr -d '"') + export _H1="Authorization: $token_trimmed" if [ "$mtd" = "DELETE" ]; then @@ -146,5 +160,4 @@ _arvan_rest() { else response="$(_get "$ARVAN_API_URL/$ep$data")" fi - return 0 } diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index 9b8b7a8b..033fa5aa 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -89,7 +89,7 @@ add_record() { _info "Adding record" - 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=%E9%BB%98%E8%AE%A4"; 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 diff --git a/dnsapi/dns_duckdns.sh b/dnsapi/dns_duckdns.sh index d6e1dbdc..618e12c6 100755 --- a/dnsapi/dns_duckdns.sh +++ b/dnsapi/dns_duckdns.sh @@ -12,7 +12,7 @@ DuckDNS_API="https://www.duckdns.org/update" -######## Public functions ###################### +######## Public functions ##################### #Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_duckdns_add() { @@ -112,7 +112,7 @@ _duckdns_rest() { param="$2" _debug param "$param" url="$DuckDNS_API?$param" - if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ]; then + if [ "$DEBUG" -gt 0 ]; then url="$url&verbose=true" fi _debug url "$url" @@ -121,7 +121,7 @@ _duckdns_rest() { if [ "$method" = "GET" ]; then response="$(_get "$url")" _debug2 response "$response" - if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ] && _contains "$response" "UPDATED" && _contains "$response" "OK"; then + if [ "$DEBUG" -gt 0 ] && _contains "$response" "UPDATED" && _contains "$response" "OK"; then response="OK" fi else diff --git a/dnsapi/dns_huaweicloud.sh b/dnsapi/dns_huaweicloud.sh index f7192725..74fec2a9 100644 --- a/dnsapi/dns_huaweicloud.sh +++ b/dnsapi/dns_huaweicloud.sh @@ -5,7 +5,7 @@ # HUAWEICLOUD_ProjectID iam_api="https://iam.myhuaweicloud.com" -dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work +dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" ######## Public functions ##################### @@ -29,27 +29,16 @@ dns_huaweicloud_add() { return 1 fi - unset token # Clear token token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_ProjectID}")" - if [ -z "${token}" ]; then # Check token - _err "dns_api(dns_huaweicloud): Error getting token." - return 1 - fi - _debug "Access token is: ${token}" - - unset zoneid + _debug2 "${token}" zoneid="$(_get_zoneid "${token}" "${fulldomain}")" - if [ -z "${zoneid}" ]; then - _err "dns_api(dns_huaweicloud): Error getting zone id." - return 1 - fi - _debug "Zone ID is: ${zoneid}" + _debug "${zoneid}" _debug "Adding Record" _add_record "${token}" "${fulldomain}" "${txtvalue}" ret="$?" if [ "${ret}" != "0" ]; then - _err "dns_api(dns_huaweicloud): Error adding record." + _err "dns_huaweicloud: Error adding record." return 1 fi @@ -80,21 +69,12 @@ dns_huaweicloud_rm() { return 1 fi - unset token # Clear token token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_ProjectID}")" - if [ -z "${token}" ]; then # Check token - _err "dns_api(dns_huaweicloud): Error getting token." - return 1 - fi - _debug "Access token is: ${token}" - - unset zoneid + _debug2 "${token}" zoneid="$(_get_zoneid "${token}" "${fulldomain}")" - if [ -z "${zoneid}" ]; then - _err "dns_api(dns_huaweicloud): Error getting zone id." - return 1 - fi - _debug "Zone ID is: ${zoneid}" + _debug "${zoneid}" + record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")" + _debug "Record Set ID is: ${record_id}" # Remove all records # Therotically HuaweiCloud does not allow more than one record set diff --git a/dnsapi/dns_ionos.sh b/dnsapi/dns_ionos.sh index aaf8580f..e6bd5000 100755 --- a/dnsapi/dns_ionos.sh +++ b/dnsapi/dns_ionos.sh @@ -24,9 +24,20 @@ dns_ionos_add() { return 1 fi - _body="[{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}]" + _new_record="{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}" - if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ -z "$response" ]; then + # As no POST route is supported by the API, check for existing records and include them in the PATCH request in order not delete them. + # This is required to support ACME v2 wildcard certificate creation, where two TXT records for the same domain name are created. + + _ionos_get_existing_records "$fulldomain" "$_zone_id" + + if [ "$_existing_records" ]; then + _body="[$_new_record,$_existing_records]" + else + _body="[$_new_record]" + fi + + if _ionos_rest PATCH "$IONOS_ROUTE_ZONES/$_zone_id" "$_body" && [ -z "$response" ]; then _info "TXT record has been created successfully." return 0 fi @@ -114,6 +125,17 @@ _get_root() { return 1 } +_ionos_get_existing_records() { + fulldomain=$1 + zone_id=$2 + + if _ionos_rest GET "$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then + response="$(echo "$response" | tr -d "\n")" + + _existing_records="$(printf "%s\n" "$response" | _egrep_o "\"records\":\[.*\]" | _head_n 1 | cut -d '[' -f 2 | sed 's/]//')" + fi +} + _ionos_get_record() { fulldomain=$1 zone_id=$2 @@ -146,7 +168,7 @@ _ionos_rest() { export _H2="Accept: application/json" export _H3="Content-Type: application/json" - response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")" + response="$(_post "$data" "$IONOS_API$route" "" "$method")" else export _H2="Accept: */*" diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index d15d6b0e..7ce39fa9 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -208,7 +208,7 @@ _namecheap_parse_host() { _hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2) _hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2) _hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2) - _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2 | _xml_decode) + _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2) _hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2) _hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2) @@ -405,7 +405,3 @@ _namecheap_set_tld_sld() { done } - -_xml_decode() { - sed 's/"/"/g' -} diff --git a/dnsapi/dns_pdns.sh b/dnsapi/dns_pdns.sh index 28b35492..8f07e8c4 100755 --- a/dnsapi/dns_pdns.sh +++ b/dnsapi/dns_pdns.sh @@ -175,13 +175,13 @@ _get_root() { i=1 if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then - _zones_response=$(echo "$response" | _normalizeJson) + _zones_response="$response" fi while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) - if _contains "$_zones_response" "\"name\":\"$h.\""; then + if _contains "$_zones_response" "\"name\": \"$h.\""; then _domain="$h." if [ -z "$h" ]; then _domain="=2E" diff --git a/dnsapi/dns_porkbun.sh b/dnsapi/dns_porkbun.sh deleted file mode 100644 index 18da6b2f..00000000 --- a/dnsapi/dns_porkbun.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env sh - -# -#PORKBUN_API_KEY="pk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" -#PORKBUN_SECRET_API_KEY="sk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" - -PORKBUN_Api="https://porkbun.com/api/json/v3" - -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_porkbun_add() { - fulldomain=$1 - txtvalue=$2 - - PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}" - PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}" - - if [ -z "$PORKBUN_API_KEY" ] || [ -z "$PORKBUN_SECRET_API_KEY" ]; then - PORKBUN_API_KEY='' - PORKBUN_SECRET_API_KEY='' - _err "You didn't specify a Porkbun api key and secret api key yet." - _err "You can get yours from here https://porkbun.com/account/api." - return 1 - fi - - #save the credentials to the account conf file. - _saveaccountconf_mutable PORKBUN_API_KEY "$PORKBUN_API_KEY" - _saveaccountconf_mutable PORKBUN_SECRET_API_KEY "$PORKBUN_SECRET_API_KEY" - - _debug 'First detect the root zone' - if ! _get_root "$fulldomain"; then - return 1 - fi - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so - # we can not use updating anymore. - # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - # _debug count "$count" - # if [ "$count" = "0" ]; then - _info "Adding record" - if _porkbun_rest POST "dns/create/$_domain" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if _contains "$response" '\"status\":"SUCCESS"'; then - _info "Added, OK" - return 0 - elif _contains "$response" "The record already exists"; then - _info "Already exists, OK" - return 0 - else - _err "Add txt record error. ($response)" - return 1 - fi - fi - _err "Add txt record error." - return 1 - -} - -#fulldomain txtvalue -dns_porkbun_rm() { - fulldomain=$1 - txtvalue=$2 - - PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}" - PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}" - - _debug 'First detect the root zone' - if ! _get_root "$fulldomain"; then - return 1 - fi - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ") - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Don't need to remove." - else - record_id=$(echo "$response" | tr '{' '\n' | grep "$txtvalue" | cut -d, -f1 | cut -d: -f2 | tr -d \") - _debug "record_id" "$record_id" - if [ -z "$record_id" ]; then - _err "Can not get record id to remove." - return 1 - fi - if ! _porkbun_rest POST "dns/delete/$_domain/$record_id"; then - _err "Delete record error." - return 1 - fi - echo "$response" | tr -d " " | grep '\"status\":"SUCCESS"' >/dev/null - fi - -} - -#################### Private functions below ################################## -#_acme-challenge.www.domain.com -#returns -# _sub_domain=_acme-challenge.www -# _domain=domain.com -_get_root() { - domain=$1 - i=1 - while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) - _debug h "$h" - if [ -z "$h" ]; then - return 1 - fi - - if _porkbun_rest POST "dns/retrieve/$h"; then - if _contains "$response" "\"status\":\"SUCCESS\""; then - _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" - _domain=$h - return 0 - else - _debug "Go to next level of $_domain" - fi - else - _debug "Go to next level of $_domain" - fi - i=$(_math "$i" + 1) - done - - return 1 -} - -_porkbun_rest() { - m=$1 - ep="$2" - data="$3" - _debug "$ep" - - api_key_trimmed=$(echo "$PORKBUN_API_KEY" | tr -d '"') - secret_api_key_trimmed=$(echo "$PORKBUN_SECRET_API_KEY" | tr -d '"') - - test -z "$data" && data="{" || data="$(echo $data | cut -d'}' -f1)," - data="$data\"apikey\":\"$api_key_trimmed\",\"secretapikey\":\"$secret_api_key_trimmed\"}" - - export _H1="Content-Type: application/json" - - if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$PORKBUN_Api/$ep" "" "$m")" - else - response="$(_get "$PORKBUN_Api/$ep")" - fi - - _sleep 3 # prevent rate limit - - if [ "$?" != "0" ]; then - _err "error $ep" - return 1 - fi - _debug2 response "$response" - return 0 -} diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index f70a2294..e73d85b0 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -49,42 +49,16 @@ dns_servercow_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - # check whether a txt record already exists for the subdomain - if printf -- "%s" "$response" | grep "{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\"" >/dev/null; then - _info "A txt record with the same name already exists." - # trim the string on the left - txtvalue_old=${response#*{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"} - # trim the string on the right - txtvalue_old=${txtvalue_old%%\"*} - - _debug txtvalue_old "$txtvalue_old" - - _info "Add the new txtvalue to the existing txt record." - if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":[\"$txtvalue\",\"$txtvalue_old\"],\"ttl\":20}"; then - if printf -- "%s" "$response" | grep "ok" >/dev/null; then - _info "Added additional txtvalue, OK" - return 0 - else - _err "add txt record error." - return 1 - fi + if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then + if printf -- "%s" "$response" | grep "ok" >/dev/null; then + _info "Added, OK" + return 0 + else + _err "add txt record error." + return 1 fi - _err "add txt record error." - return 1 - else - _info "There is no txt record with the name yet." - if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then - if printf -- "%s" "$response" | grep "ok" >/dev/null; then - _info "Added, OK" - return 0 - else - _err "add txt record error." - return 1 - fi - fi - _err "add txt record error." - return 1 fi + _err "add txt record error." return 1 } diff --git a/dnsapi/dns_simply.sh b/dnsapi/dns_simply.sh index e0e05017..d053dcf6 100644 --- a/dnsapi/dns_simply.sh +++ b/dnsapi/dns_simply.sh @@ -6,10 +6,8 @@ #SIMPLY_ApiKey="apikey" # #SIMPLY_Api="https://api.simply.com/1/[ACCOUNTNAME]/[APIKEY]" -SIMPLY_Api_Default="https://api.simply.com/1" -#This is used for determining success of REST call -SIMPLY_SUCCESS_CODE='"status": 200' +SIMPLY_Api_Default="https://api.simply.com/1" ######## Public functions ##################### #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" @@ -173,7 +171,7 @@ _get_root() { return 1 fi - if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then + if _contains "$response" '"code":"NOT_FOUND"'; then _debug "$h not found" else _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) @@ -198,12 +196,6 @@ _simply_add_record() { return 1 fi - if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then - _err "Call to API not sucessfull, see below message for more details" - _err "$response" - return 1 - fi - return 0 } @@ -219,12 +211,6 @@ _simply_delete_record() { return 1 fi - if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then - _err "Call to API not sucessfull, see below message for more details" - _err "$response" - return 1 - fi - return 0 } diff --git a/notify/smtp.sh b/notify/smtp.sh index 293c665e..6aa37ca3 100644 --- a/notify/smtp.sh +++ b/notify/smtp.sh @@ -2,398 +2,14 @@ # support smtp -# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3358 - -# This implementation uses either curl or Python (3 or 2.7). -# (See also the "mail" notify hook, which supports other ways to send mail.) - -# SMTP_FROM="from@example.com" # required -# SMTP_TO="to@example.com" # required -# SMTP_HOST="smtp.example.com" # required -# SMTP_PORT="25" # defaults to 25, 465 or 587 depending on SMTP_SECURE -# SMTP_SECURE="tls" # one of "none", "ssl" (implicit TLS, TLS Wrapper), "tls" (explicit TLS, STARTTLS) -# SMTP_USERNAME="" # set if SMTP server requires login -# SMTP_PASSWORD="" # set if SMTP server requires login -# SMTP_TIMEOUT="30" # seconds for SMTP operations to timeout -# SMTP_BIN="/path/to/python_or_curl" # default finds first of python3, python2.7, python, pypy3, pypy, curl on PATH - -SMTP_SECURE_DEFAULT="tls" -SMTP_TIMEOUT_DEFAULT="30" - -# subject content statuscode smtp_send() { - SMTP_SUBJECT="$1" - SMTP_CONTENT="$2" - # UNUSED: _statusCode="$3" # 0: success, 1: error 2($RENEW_SKIP): skipped + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" - # Load and validate config: - SMTP_BIN="$(_readaccountconf_mutable_default SMTP_BIN)" - if [ -n "$SMTP_BIN" ] && ! _exists "$SMTP_BIN"; then - _err "SMTP_BIN '$SMTP_BIN' does not exist." - return 1 - fi - if [ -z "$SMTP_BIN" ]; then - # Look for a command that can communicate with an SMTP server. - # (Please don't add sendmail, ssmtp, mutt, mail, or msmtp here. - # Those are already handled by the "mail" notify hook.) - for cmd in python3 python2.7 python pypy3 pypy curl; do - if _exists "$cmd"; then - SMTP_BIN="$cmd" - break - fi - done - if [ -z "$SMTP_BIN" ]; then - _err "The smtp notify-hook requires curl or Python, but can't find any." - _err 'If you have one of them, define SMTP_BIN="/path/to/curl_or_python".' - _err 'Otherwise, see if you can use the "mail" notify-hook instead.' - return 1 - fi - fi - _debug SMTP_BIN "$SMTP_BIN" - _saveaccountconf_mutable_default SMTP_BIN "$SMTP_BIN" - - SMTP_FROM="$(_readaccountconf_mutable_default SMTP_FROM)" - SMTP_FROM="$(_clean_email_header "$SMTP_FROM")" - if [ -z "$SMTP_FROM" ]; then - _err "You must define SMTP_FROM as the sender email address." - return 1 - fi - if _email_has_display_name "$SMTP_FROM"; then - _err "SMTP_FROM must be only a simple email address (sender@example.com)." - _err "Change your SMTP_FROM='$SMTP_FROM' to remove the display name." - return 1 - fi - _debug SMTP_FROM "$SMTP_FROM" - _saveaccountconf_mutable_default SMTP_FROM "$SMTP_FROM" - - SMTP_TO="$(_readaccountconf_mutable_default SMTP_TO)" - SMTP_TO="$(_clean_email_header "$SMTP_TO")" - if [ -z "$SMTP_TO" ]; then - _err "You must define SMTP_TO as the recipient email address(es)." - return 1 - fi - if _email_has_display_name "$SMTP_TO"; then - _err "SMTP_TO must be only simple email addresses (to@example.com,to2@example.com)." - _err "Change your SMTP_TO='$SMTP_TO' to remove the display name(s)." - return 1 - fi - _debug SMTP_TO "$SMTP_TO" - _saveaccountconf_mutable_default SMTP_TO "$SMTP_TO" - - SMTP_HOST="$(_readaccountconf_mutable_default SMTP_HOST)" - if [ -z "$SMTP_HOST" ]; then - _err "You must define SMTP_HOST as the SMTP server hostname." - return 1 - fi - _debug SMTP_HOST "$SMTP_HOST" - _saveaccountconf_mutable_default SMTP_HOST "$SMTP_HOST" - - SMTP_SECURE="$(_readaccountconf_mutable_default SMTP_SECURE "$SMTP_SECURE_DEFAULT")" - case "$SMTP_SECURE" in - "none") smtp_port_default="25" ;; - "ssl") smtp_port_default="465" ;; - "tls") smtp_port_default="587" ;; - *) - _err "Invalid SMTP_SECURE='$SMTP_SECURE'. It must be 'ssl', 'tls' or 'none'." - return 1 - ;; - esac - _debug SMTP_SECURE "$SMTP_SECURE" - _saveaccountconf_mutable_default SMTP_SECURE "$SMTP_SECURE" "$SMTP_SECURE_DEFAULT" - - SMTP_PORT="$(_readaccountconf_mutable_default SMTP_PORT "$smtp_port_default")" - case "$SMTP_PORT" in - *[!0-9]*) - _err "Invalid SMTP_PORT='$SMTP_PORT'. It must be a port number." - return 1 - ;; - esac - _debug SMTP_PORT "$SMTP_PORT" - _saveaccountconf_mutable_default SMTP_PORT "$SMTP_PORT" "$smtp_port_default" - - SMTP_USERNAME="$(_readaccountconf_mutable_default SMTP_USERNAME)" - _debug SMTP_USERNAME "$SMTP_USERNAME" - _saveaccountconf_mutable_default SMTP_USERNAME "$SMTP_USERNAME" - - SMTP_PASSWORD="$(_readaccountconf_mutable_default SMTP_PASSWORD)" - _secure_debug SMTP_PASSWORD "$SMTP_PASSWORD" - _saveaccountconf_mutable_default SMTP_PASSWORD "$SMTP_PASSWORD" - - SMTP_TIMEOUT="$(_readaccountconf_mutable_default SMTP_TIMEOUT "$SMTP_TIMEOUT_DEFAULT")" - _debug SMTP_TIMEOUT "$SMTP_TIMEOUT" - _saveaccountconf_mutable_default SMTP_TIMEOUT "$SMTP_TIMEOUT" "$SMTP_TIMEOUT_DEFAULT" - - SMTP_X_MAILER="$(_clean_email_header "$PROJECT_NAME $VER --notify-hook smtp")" - - # Run with --debug 2 (or above) to echo the transcript of the SMTP session. - # Careful: this may include SMTP_PASSWORD in plaintext! - if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then - SMTP_SHOW_TRANSCRIPT="True" - else - SMTP_SHOW_TRANSCRIPT="" - fi - - SMTP_SUBJECT=$(_clean_email_header "$SMTP_SUBJECT") - _debug SMTP_SUBJECT "$SMTP_SUBJECT" - _debug SMTP_CONTENT "$SMTP_CONTENT" - - # Send the message: - case "$(basename "$SMTP_BIN")" in - curl) _smtp_send=_smtp_send_curl ;; - py*) _smtp_send=_smtp_send_python ;; - *) - _err "Can't figure out how to invoke '$SMTP_BIN'." - _err "Check your SMTP_BIN setting." - return 1 - ;; - esac - - if ! smtp_output="$($_smtp_send)"; then - _err "Error sending message with $SMTP_BIN." - if [ -n "$smtp_output" ]; then - _err "$smtp_output" - fi - return 1 - fi - - return 0 -} - -# Strip CR and NL from text to prevent MIME header injection -# text -_clean_email_header() { - printf "%s" "$(echo "$1" | tr -d "\r\n")" -} - -# Simple check for display name in an email address (< > or ") -# email -_email_has_display_name() { - _email="$1" - expr "$_email" : '^.*[<>"]' >/dev/null -} - -## -## curl smtp sending -## - -# Send the message via curl using SMTP_* variables -_smtp_send_curl() { - # Build curl args in $@ - case "$SMTP_SECURE" in - none) - set -- --url "smtp://${SMTP_HOST}:${SMTP_PORT}" - ;; - ssl) - set -- --url "smtps://${SMTP_HOST}:${SMTP_PORT}" - ;; - tls) - set -- --url "smtp://${SMTP_HOST}:${SMTP_PORT}" --ssl-reqd - ;; - *) - # This will only occur if someone adds a new SMTP_SECURE option above - # without updating this code for it. - _err "Unhandled SMTP_SECURE='$SMTP_SECURE' in _smtp_send_curl" - _err "Please re-run with --debug and report a bug." - return 1 - ;; - esac - - set -- "$@" \ - --upload-file - \ - --mail-from "$SMTP_FROM" \ - --max-time "$SMTP_TIMEOUT" - - # Burst comma-separated $SMTP_TO into individual --mail-rcpt args. - _to="${SMTP_TO}," - while [ -n "$_to" ]; do - _rcpt="${_to%%,*}" - _to="${_to#*,}" - set -- "$@" --mail-rcpt "$_rcpt" - done - - _smtp_login="${SMTP_USERNAME}:${SMTP_PASSWORD}" - if [ "$_smtp_login" != ":" ]; then - set -- "$@" --user "$_smtp_login" - fi - - if [ "$SMTP_SHOW_TRANSCRIPT" = "True" ]; then - set -- "$@" --verbose - else - set -- "$@" --silent --show-error - fi - - raw_message="$(_smtp_raw_message)" - - _debug2 "curl command:" "$SMTP_BIN" "$*" - _debug2 "raw_message:\n$raw_message" - - echo "$raw_message" | "$SMTP_BIN" "$@" -} - -# Output an RFC-822 / RFC-5322 email message using SMTP_* variables. -# (This assumes variables have already been cleaned for use in email headers.) -_smtp_raw_message() { - echo "From: $SMTP_FROM" - echo "To: $SMTP_TO" - echo "Subject: $(_mime_encoded_word "$SMTP_SUBJECT")" - echo "Date: $(_rfc2822_date)" - echo "Content-Type: text/plain; charset=utf-8" - echo "X-Mailer: $SMTP_X_MAILER" - echo - echo "$SMTP_CONTENT" -} - -# Convert text to RFC-2047 MIME "encoded word" format if it contains non-ASCII chars -# text -_mime_encoded_word() { - _text="$1" - # (regex character ranges like [a-z] can be locale-dependent; enumerate ASCII chars to avoid that) - _ascii='] $`"'"[!#%&'()*+,./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ~^_abcdefghijklmnopqrstuvwxyz{|}~-" - if expr "$_text" : "^.*[^$_ascii]" >/dev/null; then - # At least one non-ASCII char; convert entire thing to encoded word - printf "%s" "=?UTF-8?B?$(printf "%s" "$_text" | _base64)?=" - else - # Just printable ASCII, no conversion needed - printf "%s" "$_text" - fi -} - -# Output current date in RFC-2822 Section 3.3 format as required in email headers -# (e.g., "Mon, 15 Feb 2021 14:22:01 -0800") -_rfc2822_date() { - # Notes: - # - this is deliberately not UTC, because it "SHOULD express local time" per spec - # - the spec requires weekday and month in the C locale (English), not localized - # - this date format specifier has been tested on Linux, Mac, Solaris and FreeBSD - _old_lc_time="$LC_TIME" - LC_TIME=C - date +'%a, %-d %b %Y %H:%M:%S %z' - LC_TIME="$_old_lc_time" -} - -## -## Python smtp sending -## - -# Send the message via Python using SMTP_* variables -_smtp_send_python() { - _debug "Python version" "$("$SMTP_BIN" --version 2>&1)" - - # language=Python - "$SMTP_BIN" <