Browse Source

Merge branch 'acmesh-official:master' into master

pull/5276/head
Eldar Borge 3 months ago
committed by GitHub
parent
commit
72f487d2e3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      .github/workflows/DNS.yml
  2. 15
      .github/workflows/dockerhub.yml
  3. 22
      acme.sh
  4. 88
      deploy/ali_cdn.sh
  5. 88
      deploy/ali_dcdn.sh
  6. 6
      deploy/synology_dsm.sh
  7. 64
      deploy/unifi.sh
  8. 133
      dnsapi/dns_ali.sh
  9. 184
      dnsapi/dns_alviy.sh
  10. 3
      dnsapi/dns_anx.sh
  11. 3
      dnsapi/dns_doapi.sh
  12. 10
      dnsapi/dns_dynv6.sh
  13. 2
      dnsapi/dns_gcore.sh
  14. 4
      dnsapi/dns_huaweicloud.sh
  15. 13
      dnsapi/dns_inwx.sh
  16. 2
      dnsapi/dns_ispconfig.sh
  17. 9
      dnsapi/dns_miab.sh
  18. 16
      dnsapi/dns_nsupdate.sh
  19. 4
      dnsapi/dns_openprovider.sh
  20. 406
      dnsapi/dns_timeweb.sh
  21. 121
      dnsapi/dns_yandex.sh
  22. 352
      dnsapi/dns_yandex360.sh
  23. 101
      notify/bark.sh
  24. 69
      notify/teams.sh

1
.github/workflows/DNS.yml

@ -1,5 +1,6 @@
name: DNS name: DNS
on: on:
workflow_dispatch:
push: push:
paths: paths:
- 'dnsapi/*.sh' - 'dnsapi/*.sh'

15
.github/workflows/dockerhub.yml

@ -15,6 +15,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
env:
DOCKER_IMAGE: neilpang/acme.sh
jobs: jobs:
CheckToken: CheckToken:
@ -44,6 +46,11 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v2
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5.5.1
with:
images: ${DOCKER_IMAGE}
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: login to docker hub - name: login to docker hub
@ -51,8 +58,6 @@ jobs:
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: build and push the image - name: build and push the image
run: | run: |
DOCKER_IMAGE=neilpang/acme.sh
if [[ $GITHUB_REF == refs/tags/* ]]; then if [[ $GITHUB_REF == refs/tags/* ]]; then
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/} DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
fi fi
@ -66,8 +71,14 @@ jobs:
fi fi
fi fi
DOCKER_LABELS=()
while read -r label; do
DOCKER_LABELS+=(--label "${label}")
done <<<"${DOCKER_METADATA_OUTPUT_LABELS}"
docker buildx build \ docker buildx build \
--tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \ --tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
"${DOCKER_LABELS[@]}" \
--output "type=image,push=true" \ --output "type=image,push=true" \
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \ --build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x . --platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .

22
acme.sh

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=3.0.8
VER=3.1.0
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
@ -672,8 +672,10 @@ _hex_dump() {
#0 1 2 3 4 5 6 7 8 9 - _ . ~ #0 1 2 3 4 5 6 7 8 9 - _ . ~
#30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e #30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e
#_url_encode [upper-hex] the encoded hex will be upper-case if the argument upper-hex is followed
#stdin stdout #stdin stdout
_url_encode() { _url_encode() {
_upper_hex=$1
_hex_str=$(_hex_dump) _hex_str=$(_hex_dump)
_debug3 "_url_encode" _debug3 "_url_encode"
_debug3 "_hex_str" "$_hex_str" _debug3 "_hex_str" "$_hex_str"
@ -883,6 +885,9 @@ _url_encode() {
;; ;;
#other hex #other hex
*) *)
if [ "$_upper_hex" = "upper-hex" ]; then
_hex_code=$(printf "%s" "$_hex_code" | _upper_case)
fi
printf '%%%s' "$_hex_code" printf '%%%s' "$_hex_code"
;; ;;
esac esac
@ -2361,7 +2366,7 @@ _clear_conf() {
_sdkey="$2" _sdkey="$2"
if [ "$_c_c_f" ]; then if [ "$_c_c_f" ]; then
_conf_data="$(cat "$_c_c_f")" _conf_data="$(cat "$_c_c_f")"
echo "$_conf_data" | sed "s/^$_sdkey *=.*$//" >"$_c_c_f"
echo "$_conf_data" | sed "/^$_sdkey *=.*$/d" >"$_c_c_f"
else else
_err "Config file is empty, cannot clear" _err "Config file is empty, cannot clear"
fi fi
@ -5111,6 +5116,19 @@ $_authorizations_map"
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
_retryafter=$(echo "$responseHeaders" | grep -i "^Retry-After *: *[0-9]\+ *" | cut -d : -f 2 | tr -d ' ' | tr -d '\r')
_sleep_overload_retry_sec=$_retryafter
if [ "$_sleep_overload_retry_sec" ]; then
if [ $_sleep_overload_retry_sec -le 600 ]; then
_sleep $_sleep_overload_retry_sec
else
_info "The retryafter=$_retryafter value is too large (> 600), will not retry anymore."
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
return 1
fi
fi
done done
done done

88
deploy/ali_cdn.sh

@ -0,0 +1,88 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034,SC2154
# Script to create certificate to Alibaba Cloud CDN
#
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
#
# This deployment required following variables
# export Ali_Key="ALIACCESSKEY"
# export Ali_Secret="ALISECRETKEY"
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
#
# To specify the CDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com"
# If you have multiple CDN domains using the same certificate, just
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
#
# For DCDN, see ali_dcdn deploy hook
Ali_CDN_API="https://cdn.aliyuncs.com/"
ali_cdn_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
# shellcheck source=/dev/null
if ! . "$dnsapi_ali"; then
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
return 1
fi
_prepare_ali_credentials || return 1
_getdeployconf DEPLOY_ALI_CDN_DOMAIN
if [ "$DEPLOY_ALI_CDN_DOMAIN" ]; then
_savedeployconf DEPLOY_ALI_CDN_DOMAIN "$DEPLOY_ALI_CDN_DOMAIN"
else
DEPLOY_ALI_CDN_DOMAIN="$_cdomain"
fi
# read cert and key files and urlencode both
_cert=$(_url_encode upper-hex <"$_cfullchain")
_key=$(_url_encode upper-hex <"$_ckey")
_debug2 _cert "$_cert"
_debug2 _key "$_key"
## update domain ssl config
for domain in $DEPLOY_ALI_CDN_DOMAIN; do
_set_cdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
if _ali_rest "Set CDN domain SSL certificate for $domain" "" POST; then
_info "Domain $domain certificate has been deployed successfully"
fi
done
return 0
}
# domain pub pri
_set_cdn_domain_ssl_certificate_query() {
endpoint=$Ali_CDN_API
query=''
query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=SetCdnDomainSSLCertificate'
query=$query'&CertType=upload'
query=$query'&DomainName='$1
query=$query'&Format=json'
query=$query'&SSLPri='$3
query=$query'&SSLProtocol=on'
query=$query'&SSLPub='$2
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
query=$query'&Timestamp='$(_timestamp)
query=$query'&Version=2018-05-10'
}

88
deploy/ali_dcdn.sh

@ -0,0 +1,88 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034,SC2154
# Script to create certificate to Alibaba Cloud DCDN
#
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
#
# This deployment required following variables
# export Ali_Key="ALIACCESSKEY"
# export Ali_Secret="ALISECRETKEY"
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
#
# To specify the DCDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn.example.com"
# If you have multiple CDN domains using the same certificate, just
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn1.example.com dcdn2.example.com"
#
# For regular CDN, see ali_cdn deploy hook
Ali_DCDN_API="https://dcdn.aliyuncs.com/"
ali_dcdn_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
# shellcheck source=/dev/null
if ! . "$dnsapi_ali"; then
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
return 1
fi
_prepare_ali_credentials || return 1
_getdeployconf DEPLOY_ALI_DCDN_DOMAIN
if [ "$DEPLOY_ALI_DCDN_DOMAIN" ]; then
_savedeployconf DEPLOY_ALI_DCDN_DOMAIN "$DEPLOY_ALI_DCDN_DOMAIN"
else
DEPLOY_ALI_DCDN_DOMAIN="$_cdomain"
fi
# read cert and key files and urlencode both
_cert=$(_url_encode upper-hex <"$_cfullchain")
_key=$(_url_encode upper-hex <"$_ckey")
_debug2 _cert "$_cert"
_debug2 _key "$_key"
## update domain ssl config
for domain in $DEPLOY_ALI_DCDN_DOMAIN; do
_set_dcdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
if _ali_rest "Set DCDN domain SSL certificate for $domain" "" POST; then
_info "Domain $domain certificate has been deployed successfully"
fi
done
return 0
}
# domain pub pri
_set_dcdn_domain_ssl_certificate_query() {
endpoint=$Ali_DCDN_API
query=''
query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=SetDcdnDomainSSLCertificate'
query=$query'&CertType=upload'
query=$query'&DomainName='$1
query=$query'&Format=json'
query=$query'&SSLPri='$3
query=$query'&SSLProtocol=on'
query=$query'&SSLPub='$2
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
query=$query'&Timestamp='$(_timestamp)
query=$query'&Version=2018-01-15'
}

6
deploy/synology_dsm.sh

@ -113,9 +113,9 @@ synology_dsm_deploy() {
# Default values for scheme, hostname and port # Default values for scheme, hostname and port
# Defaulting to localhost and http, because it's localhost… # Defaulting to localhost and http, because it's localhost…
[ -n "$SYNO_SCHEME" ] || SYNO_SCHEME="http"
[ -n "$SYNO_HOSTNAME" ] || SYNO_HOSTNAME="localhost"
[ -n "$SYNO_PORT" ] || SYNO_PORT="5000"
[ -n "$SYNO_SCHEME" ] || SYNO_SCHEME=http
[ -n "$SYNO_HOSTNAME" ] || SYNO_HOSTNAME=localhost
[ -n "$SYNO_PORT" ] || SYNO_PORT=5000
_savedeployconf SYNO_SCHEME "$SYNO_SCHEME" _savedeployconf SYNO_SCHEME "$SYNO_SCHEME"
_savedeployconf SYNO_HOSTNAME "$SYNO_HOSTNAME" _savedeployconf SYNO_HOSTNAME "$SYNO_HOSTNAME"
_savedeployconf SYNO_PORT "$SYNO_PORT" _savedeployconf SYNO_PORT "$SYNO_PORT"

64
deploy/unifi.sh

@ -5,6 +5,15 @@
# - self-hosted Unifi Controller # - self-hosted Unifi Controller
# - Unifi Cloud Key (Gen1/2/2+) # - Unifi Cloud Key (Gen1/2/2+)
# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only) # - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
# - Unifi Dream Machine
# This has not been tested on other "all-in-one" devices such as
# UDM Pro or Unifi Express.
#
# OS Version v2.0.0+
# Network Application version 7.0.0+
# OS version ~3.1 removed java and keytool from the UnifiOS.
# Using PKCS12 format keystore appears to work fine.
#
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359 # Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
#returns 0 means success, otherwise error. #returns 0 means success, otherwise error.
@ -74,14 +83,16 @@ unifi_deploy() {
_reload_cmd="" _reload_cmd=""
# Unifi Controller environment (self hosted or any Cloud Key) -- # Unifi Controller environment (self hosted or any Cloud Key) --
# auto-detect by file /usr/lib/unifi/data/keystore:
# auto-detect by file /usr/lib/unifi/data/keystore
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}" _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
if [ -f "$_unifi_keystore" ]; then if [ -f "$_unifi_keystore" ]; then
_info "Installing certificate for Unifi Controller (Java keystore)"
_debug _unifi_keystore "$_unifi_keystore" _debug _unifi_keystore "$_unifi_keystore"
if ! _exists keytool; then if ! _exists keytool; then
_err "keytool not found"
return 1
_do_keytool=0
_info "Installing certificate for Unifi Controller (PKCS12 keystore)."
else
_do_keytool=1
_info "Installing certificate for Unifi Controller (Java keystore)"
fi fi
if [ ! -w "$_unifi_keystore" ]; then if [ ! -w "$_unifi_keystore" ]; then
_err "The file $_unifi_keystore is not writable, please change the permission." _err "The file $_unifi_keystore is not writable, please change the permission."
@ -92,6 +103,7 @@ unifi_deploy() {
_debug "Generate import pkcs12" _debug "Generate import pkcs12"
_import_pkcs12="$(_mktemp)" _import_pkcs12="$(_mktemp)"
_debug "_toPkcs $_import_pkcs12 $_ckey $_ccert $_cca $_unifi_keypass unifi root"
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root _toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
# shellcheck disable=SC2181 # shellcheck disable=SC2181
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
@ -99,22 +111,57 @@ unifi_deploy() {
return 1 return 1
fi fi
# Save the existing keystore in case something goes wrong.
mv -f "${_unifi_keystore}" "${_unifi_keystore}"_original
_info "Previous keystore saved to ${_unifi_keystore}_original."
if [ "$_do_keytool" -eq 1 ]; then
_debug "Import into keystore: $_unifi_keystore" _debug "Import into keystore: $_unifi_keystore"
if keytool -importkeystore \ if keytool -importkeystore \
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \ -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \ -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
-alias unifi -noprompt; then -alias unifi -noprompt; then
_debug "Import keystore success!" _debug "Import keystore success!"
rm "$_import_pkcs12"
else else
_err "Error importing into Unifi Java keystore." _err "Error importing into Unifi Java keystore."
_err "Please re-run with --debug and report a bug." _err "Please re-run with --debug and report a bug."
_info "Restoring original keystore."
mv -f "${_unifi_keystore}"_original "${_unifi_keystore}"
rm "$_import_pkcs12" rm "$_import_pkcs12"
return 1 return 1
fi fi
else
_debug "Copying new keystore to $_unifi_keystore"
cp -f "$_import_pkcs12" "$_unifi_keystore"
fi
# Update unifi service for certificate cipher compatibility
if ${ACME_OPENSSL_BIN:-openssl} pkcs12 \
-in "$_import_pkcs12" \
-password pass:aircontrolenterprise \
-nokeys | ${ACME_OPENSSL_BIN:-openssl} x509 -text \
-noout | grep -i "signature" | grep -iq ecdsa >/dev/null 2>&1; then
cp -f /usr/lib/unifi/data/system.properties /usr/lib/unifi/data/system.properties_original
_info "Updating system configuration for cipher compatibility."
_info "Saved original system config to /usr/lib/unifi/data/system.properties_original"
sed -i '/unifi\.https\.ciphers/d' /usr/lib/unifi/data/system.properties
echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>/usr/lib/unifi/data/system.properties
sed -i '/unifi\.https\.sslEnabledProtocols/d' /usr/lib/unifi/data/system.properties
echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>/usr/lib/unifi/data/system.properties
_info "System configuration updated."
fi
rm "$_import_pkcs12"
# Restarting unifi-core will bring up unifi, doing it out of order results in
# a certificate error, and breaks wifiman.
# Restart if we aren't doing unifi-core, otherwise stop for later restart.
if systemctl -q is-active unifi; then if systemctl -q is-active unifi; then
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service unifi restart"
if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi"
else
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl stop unifi"
fi
fi fi
_services_updated="${_services_updated} unifi" _services_updated="${_services_updated} unifi"
_info "Install Unifi Controller certificate success!" _info "Install Unifi Controller certificate success!"
@ -165,6 +212,11 @@ unifi_deploy() {
return 1 return 1
fi fi
# Save the existing certs in case something goes wrong.
cp -f "${_unifi_core_config}"/unifi-core.crt "${_unifi_core_config}"/unifi-core_original.crt
cp -f "${_unifi_core_config}"/unifi-core.key "${_unifi_core_config}"/unifi-core_original.key
_info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt/key."
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt" cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key" cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"

133
dnsapi/dns_ali.sh

@ -9,25 +9,19 @@ Options:
Ali_Secret API Secret Ali_Secret API Secret
' '
Ali_API="https://alidns.aliyuncs.com/"
# NOTICE:
# This file is referenced by Alibaba Cloud Services deploy hooks
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
# Be careful when modifying this file, especially when making breaking changes for common functions
Ali_DNS_API="https://alidns.aliyuncs.com/"
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ali_add() { dns_ali_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
Ali_Key=""
Ali_Secret=""
_err "You don't specify aliyun api key and secret yet."
return 1
fi
#save the api key and secret to the account conf file.
_saveaccountconf_mutable Ali_Key "$Ali_Key"
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
_prepare_ali_credentials || return 1
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@ -52,7 +46,67 @@ dns_ali_rm() {
_clean _clean
} }
#################### Private functions below ##################################
#################### Alibaba Cloud common functions below ####################
_prepare_ali_credentials() {
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
Ali_Key=""
Ali_Secret=""
_err "You don't specify aliyun api key and secret yet."
return 1
fi
#save the api key and secret to the account conf file.
_saveaccountconf_mutable Ali_Key "$Ali_Key"
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
}
# act ign mtd
_ali_rest() {
act="$1"
ign="$2"
mtd="${3:-GET}"
signature=$(printf "%s" "$mtd&%2F&$(printf "%s" "$query" | _url_encode upper-hex)" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
signature=$(printf "%s" "$signature" | _url_encode upper-hex)
url="$endpoint?Signature=$signature"
if [ "$mtd" = "GET" ]; then
url="$url&$query"
response="$(_get "$url")"
else
response="$(_post "$query" "$url" "" "$mtd" "application/x-www-form-urlencoded")"
fi
_ret="$?"
_debug2 response "$response"
if [ "$_ret" != "0" ]; then
_err "Error <$act>"
return 1
fi
if [ -z "$ign" ]; then
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
if [ "$message" ]; then
_err "$message"
return 1
fi
fi
}
_ali_nonce() {
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
#Not so good...
date +"%s%N" | sed 's/%N//g'
}
_timestamp() {
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
}
#################### Private functions below ####################
_get_root() { _get_root() {
domain=$1 domain=$1
@ -83,52 +137,10 @@ _get_root() {
return 1 return 1
} }
_ali_rest() {
signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
signature=$(_ali_urlencode "$signature")
url="$Ali_API?$query&Signature=$signature"
if ! response="$(_get "$url")"; then
_err "Error <$1>"
return 1
fi
_debug2 response "$response"
if [ -z "$2" ]; then
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
if [ "$message" ]; then
_err "$message"
return 1
fi
fi
}
_ali_urlencode() {
_str="$1"
_str_len=${#_str}
_u_i=1
while [ "$_u_i" -le "$_str_len" ]; do
_str_c="$(printf "%s" "$_str" | cut -c "$_u_i")"
case $_str_c in [a-zA-Z0-9.~_-])
printf "%s" "$_str_c"
;;
*)
printf "%%%02X" "'$_str_c"
;;
esac
_u_i="$(_math "$_u_i" + 1)"
done
}
_ali_nonce() {
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
#Not so good...
date +"%s%N" | sed 's/%N//g'
}
_check_exist_query() { _check_exist_query() {
_qdomain="$1" _qdomain="$1"
_qsubdomain="$2" _qsubdomain="$2"
endpoint=$Ali_DNS_API
query='' query=''
query=$query'AccessKeyId='$Ali_Key query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DescribeDomainRecords' query=$query'&Action=DescribeDomainRecords'
@ -144,6 +156,7 @@ _check_exist_query() {
} }
_add_record_query() { _add_record_query() {
endpoint=$Ali_DNS_API
query='' query=''
query=$query'AccessKeyId='$Ali_Key query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=AddDomainRecord' query=$query'&Action=AddDomainRecord'
@ -160,6 +173,7 @@ _add_record_query() {
} }
_delete_record_query() { _delete_record_query() {
endpoint=$Ali_DNS_API
query='' query=''
query=$query'AccessKeyId='$Ali_Key query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DeleteDomainRecord' query=$query'&Action=DeleteDomainRecord'
@ -173,6 +187,7 @@ _delete_record_query() {
} }
_describe_records_query() { _describe_records_query() {
endpoint=$Ali_DNS_API
query='' query=''
query=$query'AccessKeyId='$Ali_Key query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DescribeDomainRecords' query=$query'&Action=DescribeDomainRecords'
@ -203,7 +218,3 @@ _clean() {
fi fi
} }
_timestamp() {
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
}

184
dnsapi/dns_alviy.sh

@ -0,0 +1,184 @@
#!/usr/bin/env sh
# Alviy domain api
#
# Get API key and secret from https://cloud.alviy.com/token
#
# Alviy_token="some-secret-key"
#
# Ex.: acme.sh --issue --staging --dns dns_alviy -d "*.s.example.com" -d "s.example.com"
Alviy_Api="https://cloud.alviy.com/api/v1"
######## Public functions #####################
#Usage: dns_alviy_add _acme-challenge.www.domain.com "content"
dns_alviy_add() {
fulldomain=$1
txtvalue=$2
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
if [ -z "$Alviy_token" ]; then
Alviy_token=""
_err "Please specify Alviy token."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable Alviy_token "$Alviy_token"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting existing records"
if _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
_info "This record already exists, skipping"
return 0
fi
_add_data="{\"content\":\"$txtvalue\",\"type\":\"TXT\"}"
_debug2 _add_data "$_add_data"
_info "Adding record"
if _alviy_rest POST "zone/$_domain/domain/$fulldomain/" "$_add_data"; then
_debug "Checking updated records of '${fulldomain}'"
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
_err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!"
return 1
fi
else
_err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set."
return 1
fi
_sleep 10
_info "Added TXT record '${txtvalue}' for '${fulldomain}'."
return 0
}
#fulldomain
dns_alviy_rm() {
fulldomain=$1
txtvalue=$2
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
_info "The record does not exist, skip"
return 0
fi
_add_data=""
uuid=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | tr "," "\n" | grep uuid | cut -d \" -f4)
# delete record
_debug "Delete TXT record for '${fulldomain}'"
if ! _alviy_rest DELETE "zone/$_domain/record/$uuid" "{\"confirm\":1}"; then
_err "Cannot delete empty TXT record for '$fulldomain'"
return 1
fi
_info "The record '$fulldomain'='$txtvalue' deleted"
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=3
a="init"
while [ -n "$a" ]; do
a=$(printf "%s" "$domain" | cut -d . -f $i-)
i=$((i + 1))
done
n=$((i - 3))
h=$(printf "%s" "$domain" | cut -d . -f $n-)
if [ -z "$h" ]; then
#not valid
_alviy_rest GET "zone/$domain/"
_debug "can't get host from $domain"
return 1
fi
if ! _alviy_rest GET "zone/$h/"; then
return 1
fi
if _contains "$response" '"code":"NOT_FOUND"'; then
_debug "$h not found"
else
s=$((n - 1))
_sub_domain=$(printf "%s" "$domain" | cut -d . -f -$s)
_domain="$h"
return 0
fi
return 1
}
_alviy_txt_exists() {
zone=$1
domain=$2
content_data=$3
_debug "Getting existing records"
if ! _alviy_rest GET "zone/$zone/domain/$domain/TXT/"; then
_info "The record does not exist"
return 1
fi
if ! _contains "$response" "$3"; then
_info "The record has other value"
return 1
fi
# GOOD code return - TRUE function
return 0
}
_alviy_rest() {
method=$1
path="$2"
content_data="$3"
_debug "$path"
export _H1="Authorization: Bearer $Alviy_token"
export _H2="Content-Type: application/json"
if [ "$content_data" ] || [ "$method" = "DELETE" ]; then
_debug "data ($method): " "$content_data"
response="$(_post "$content_data" "$Alviy_Api/$path" "" "$method")"
else
response="$(_get "$Alviy_Api/$path")"
fi
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$_code" = "401" ]; then
_err "It seems that your api key or secret is not correct."
return 1
fi
if [ "$_code" != "200" ]; then
_err "API call error ($method): $path Response code $_code"
fi
if [ "$?" != "0" ]; then
_err "error on rest call ($method): $path. Response:"
_err "$response"
return 1
fi
_debug2 response "$response"
return 0
}

3
dnsapi/dns_anx.sh

@ -130,8 +130,6 @@ _get_root() {
i=1 i=1
p=1 p=1
_anx_rest GET "zone.json"
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h" _debug h "$h"
@ -140,6 +138,7 @@ _get_root() {
return 1 return 1
fi fi
_anx_rest GET "zone.json/${h}"
if _contains "$response" "\"name\":\"$h\""; then if _contains "$response" "\"name\":\"$h\""; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain=$h

3
dnsapi/dns_doapi.sh

@ -2,7 +2,6 @@
# shellcheck disable=SC2034 # shellcheck disable=SC2034
dns_doapi_info='Domain-Offensive do.de dns_doapi_info='Domain-Offensive do.de
Official LetsEncrypt API for do.de / Domain-Offensive. Official LetsEncrypt API for do.de / Domain-Offensive.
This is different from the dns_do adapter, because dns_do is only usable for enterprise customers.
This API is also available to private customers/individuals. This API is also available to private customers/individuals.
Site: do.de Site: do.de
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_doapi Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_doapi
@ -11,7 +10,7 @@ Options:
Issues: github.com/acmesh-official/acme.sh/issues/2057 Issues: github.com/acmesh-official/acme.sh/issues/2057
' '
DO_API="https://www.do.de/api/letsencrypt"
DO_API="https://my.do.de/api/letsencrypt"
######## Public functions ##################### ######## Public functions #####################

10
dnsapi/dns_dynv6.sh

@ -16,8 +16,8 @@ dynv6_api="https://dynv6.com/api/v2"
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
#Usage: dns_dynv6_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_dynv6_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynv6_add() { dns_dynv6_add() {
fulldomain=$1
txtvalue=$2
fulldomain="$(echo "$1" | _lower_case)"
txtvalue="$2"
_info "Using dynv6 api" _info "Using dynv6 api"
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue" _debug txtvalue "$txtvalue"
@ -50,8 +50,8 @@ dns_dynv6_add() {
#Usage: fulldomain txtvalue #Usage: fulldomain txtvalue
#Remove the txt record after validation. #Remove the txt record after validation.
dns_dynv6_rm() { dns_dynv6_rm() {
fulldomain=$1
txtvalue=$2
fulldomain="$(echo "$1" | _lower_case)"
txtvalue="$2"
_info "Using dynv6 API" _info "Using dynv6 API"
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue" _debug txtvalue "$txtvalue"
@ -206,7 +206,7 @@ _get_zone_id() {
return 1 return 1
fi fi
zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep '"id":' | tr -d '"')"
_zone_id="${zone_id#id:}" _zone_id="${zone_id#id:}"
_debug "zone id: $_zone_id" _debug "zone id: $_zone_id"
} }

2
dnsapi/dns_gcore.sh

@ -28,7 +28,7 @@ dns_gcore_add() {
fi fi
#save the api key to the account conf file. #save the api key to the account conf file.
_saveaccountconf_mutable GCORE_Key "$GCORE_Key"
_saveaccountconf_mutable GCORE_Key "$GCORE_Key" "base64"
_debug "First detect the zone name" _debug "First detect the zone name"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then

4
dnsapi/dns_huaweicloud.sh

@ -210,7 +210,7 @@ _get_recordset_id() {
_zoneid=$3 _zoneid=$3
export _H1="X-Auth-Token: ${_token}" export _H1="X-Auth-Token: ${_token}"
response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}")
response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}&status=ACTIVE")
if _contains "${response}" '"id"'; then if _contains "${response}" '"id"'; then
_id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")" _id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")"
printf "%s" "${_id}" printf "%s" "${_id}"
@ -227,7 +227,7 @@ _add_record() {
# Get Existing Records # Get Existing Records
export _H1="X-Auth-Token: ${_token}" export _H1="X-Auth-Token: ${_token}"
response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}")
response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}&status=ACTIVE")
_debug2 "${response}" _debug2 "${response}"
_exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g') _exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g')

13
dnsapi/dns_inwx.sh

@ -163,6 +163,15 @@ _inwx_check_cookie() {
return 1 return 1
} }
_htmlEscape() {
_s="$1"
_s=$(echo "$_s" | sed "s/&/&amp;/g")
_s=$(echo "$_s" | sed "s/</\&lt;/g")
_s=$(echo "$_s" | sed "s/>/\&gt;/g")
_s=$(echo "$_s" | sed 's/"/\&quot;/g')
printf -- %s "$_s"
}
_inwx_login() { _inwx_login() {
if _inwx_check_cookie; then if _inwx_check_cookie; then
@ -170,6 +179,8 @@ _inwx_login() {
return 0 return 0
fi fi
XML_PASS=$(_htmlEscape "$INWX_Password")
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall> <methodCall>
<methodName>account.login</methodName> <methodName>account.login</methodName>
@ -193,7 +204,7 @@ _inwx_login() {
</value> </value>
</param> </param>
</params> </params>
</methodCall>' "$INWX_User" "$INWX_Password")
</methodCall>' "$INWX_User" "$XML_PASS")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" response="$(_post "$xml_content" "$INWX_Api" "" "POST")"

2
dnsapi/dns_ispconfig.sh

@ -14,6 +14,8 @@ Options:
# User must provide login data and URL to the ISPConfig installation incl. port. # User must provide login data and URL to the ISPConfig installation incl. port.
# The remote user in ISPConfig must have access to: # The remote user in ISPConfig must have access to:
# - DNS txt Functions # - DNS txt Functions
# - DNS zone functions
# - Client functions
######## Public functions ##################### ######## Public functions #####################

9
dnsapi/dns_miab.sh

@ -16,8 +16,9 @@ Author: Darven Dissek, William Gertz
#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_miab_add() { dns_miab_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
_info "Using miab challange add"
# Added "value=" and "&ttl=300" to accomodate the new TXT record format used by the MIAB/PMIAB API
txtvalue="value=$2&ttl=300"
_info "Using miab challenge add"
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue" _debug txtvalue "$txtvalue"
@ -26,7 +27,7 @@ dns_miab_add() {
return 1 return 1
fi fi
#check domain and seperate into doamin and host
#check domain and seperate into domain and host
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}" _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
return 1 return 1
@ -55,7 +56,7 @@ dns_miab_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
_info "Using miab challage delete"
_info "Using miab challenge delete"
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue" _debug txtvalue "$txtvalue"

16
dnsapi/dns_nsupdate.sh

@ -20,6 +20,7 @@ dns_nsupdate_add() {
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}" NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}" NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}" NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
_checkKeyFile || return 1 _checkKeyFile || return 1
@ -28,21 +29,25 @@ dns_nsupdate_add() {
_saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}" _saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
_saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}" _saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}"
_saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}" _saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}"
_saveaccountconf_mutable NSUPDATE_OPT "${NSUPDATE_OPT}"
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53 [ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
[ -n "${NSUPDATE_OPT}" ] || NSUPDATE_OPT=""
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\"" _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then if [ -z "${NSUPDATE_ZONE}" ]; then
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
update add ${fulldomain}. 60 in txt "${txtvalue}" update add ${fulldomain}. 60 in txt "${txtvalue}"
send send
EOF EOF
else else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}. zone ${NSUPDATE_ZONE}.
update add ${fulldomain}. 60 in txt "${txtvalue}" update add ${fulldomain}. 60 in txt "${txtvalue}"
@ -65,6 +70,7 @@ dns_nsupdate_rm() {
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}" NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}" NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}" NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
_checkKeyFile || return 1 _checkKeyFile || return 1
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
@ -73,13 +79,15 @@ dns_nsupdate_rm() {
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then if [ -z "${NSUPDATE_ZONE}" ]; then
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
update delete ${fulldomain}. txt update delete ${fulldomain}. txt
send send
EOF EOF
else else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}. zone ${NSUPDATE_ZONE}.
update delete ${fulldomain}. txt update delete ${fulldomain}. txt

4
dnsapi/dns_openprovider.sh

@ -68,7 +68,7 @@ dns_openprovider_add() {
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')" new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
fi fi
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item" _debug "not an allowed record type, skipping" "$new_item"
continue continue
fi fi
@ -152,7 +152,7 @@ dns_openprovider_rm() {
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')" new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
fi fi
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item" _debug "not an allowed record type, skipping" "$new_item"
continue continue
fi fi

406
dnsapi/dns_timeweb.sh

@ -0,0 +1,406 @@
#!/usr/bin/env sh
# acme.sh DNS API for Timeweb Cloud provider (https://timeweb.cloud).
#
# Author: https://github.com/nikolaypronchev.
#
# Prerequisites:
# Timeweb Cloud API JWT token. Obtain one from the Timeweb Cloud control panel
# ("API and Terraform" section: https://timeweb.cloud/my/api-keys). The JWT token
# must be provided to this script in one of two ways:
# 1. As the "TW_Token" variable, for example: "export TW_Token=eyJhbG...zUxMiIs";
# 2. As a "TW_Token" config entry in acme.sh account config file
# (usually located at ~/.acme.sh/account.conf by default).
TW_Api="https://api.timeweb.cloud/api/v1"
################ Public functions ################
# Adds an ACME DNS-01 challenge DNS TXT record via the Timeweb Cloud API.
#
# Param1: The ACME DNS-01 challenge FQDN.
# Param2: The value of the ACME DNS-01 challenge TXT record.
#
# Example: dns_timeweb_add "_acme-challenge.sub.domain.com" "D-52Wm...4uYM"
dns_timeweb_add() {
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_add\" started."
_timeweb_set_acme_fqdn "$1" || return 1
_timeweb_set_acme_txt "$2" || return 1
_timeweb_check_token || return 1
_timeweb_split_acme_fqdn || return 1
_timeweb_dns_txt_add || return 1
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_add\" finished."
}
# Removes a DNS TXT record via the Timeweb Cloud API.
#
# Param1: The ACME DNS-01 challenge FQDN.
# Param2: The value of the ACME DNS-01 challenge TXT record.
#
# Example: dns_timeweb_rm "_acme-challenge.sub.domain.com" "D-52Wm...4uYM"
dns_timeweb_rm() {
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_rm\" started."
_timeweb_set_acme_fqdn "$1" || return 1
_timeweb_set_acme_txt "$2" || return 1
_timeweb_check_token || return 1
_timeweb_split_acme_fqdn || return 1
_timeweb_get_dns_txt || return 1
_timeweb_dns_txt_remove || return 1
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_rm\" finished."
}
################ Private functions ################
# Checks and sets the ACME DNS-01 challenge FQDN.
#
# Param1: The ACME DNS-01 challenge FQDN.
#
# Example: _timeweb_set_acme_fqdn "_acme-challenge.sub.domain.com"
#
# Sets the "Acme_Fqdn" variable (_acme-challenge.sub.domain.com)
_timeweb_set_acme_fqdn() {
Acme_Fqdn=$1
_debug "Setting ACME DNS-01 challenge FQDN \"$Acme_Fqdn\"."
[ -z "$Acme_Fqdn" ] && {
_err "ACME DNS-01 challenge FQDN is empty."
return 1
}
return 0
}
# Checks and sets the value of the ACME DNS-01 challenge TXT record.
#
# Param1: Value of the ACME DNS-01 challenge TXT record.
#
# Example: _timeweb_set_acme_txt "D-52Wm...4uYM"
#
# Sets the "Acme_Txt" variable to the provided value (D-52Wm...4uYM)
_timeweb_set_acme_txt() {
Acme_Txt=$1
_debug "Setting the value of the ACME DNS-01 challenge TXT record to \"$Acme_Txt\"."
[ -z "$Acme_Txt" ] && {
_err "ACME DNS-01 challenge TXT record value is empty."
return 1
}
return 0
}
# Checks if the Timeweb Cloud API JWT token is present (refer to the script description).
# Adds or updates the token in the acme.sh account configuration.
_timeweb_check_token() {
_debug "Checking for the presence of the Timeweb Cloud API JWT token."
TW_Token="${TW_Token:-$(_readaccountconf_mutable TW_Token)}"
[ -z "$TW_Token" ] && {
_err "Timeweb Cloud API JWT token was not found."
return 1
}
_saveaccountconf_mutable TW_Token "$TW_Token"
}
# Divides the ACME DNS-01 challenge FQDN into its main domain and subdomain components.
_timeweb_split_acme_fqdn() {
_debug "Trying to divide \"$Acme_Fqdn\" into its main domain and subdomain components."
TW_Page_Limit=100
TW_Page_Offset=0
TW_Domains_Returned=""
while [ -z "$TW_Domains_Returned" ] || [ "$TW_Domains_Returned" -ge "$TW_Page_Limit" ]; do
_timeweb_list_domains "$TW_Page_Limit" "$TW_Page_Offset" || return 1
# Remove the 'subdomains' subarray to prevent confusion with FQDNs.
TW_Domains=$(
echo "$TW_Domains" |
sed 's/"subdomains":\[[^]]*]//g'
)
[ -z "$TW_Domains" ] && {
_err "Failed to parse the list of domains."
return 1
}
while
TW_Domain=$(
echo "$TW_Domains" |
sed -n 's/.*{[^{]*"fqdn":"\([^"]*\)"[^}]*}.*/\1/p'
)
[ -n "$TW_Domain" ] && {
_timeweb_is_main_domain "$TW_Domain" && return 0
TW_Domains=$(
echo "$TW_Domains" |
sed 's/{\([^{]*"fqdn":"'"$TW_Domain"'"[^}]*\)}//'
)
continue
}
do :; done
TW_Page_Offset=$(_math "$TW_Page_Offset" + "$TW_Page_Limit")
done
_err "Failed to divide \"$Acme_Fqdn\" into its main domain and subdomain components."
return 1
}
# Searches for a previously added DNS TXT record.
#
# Sets the "TW_Dns_Txt_Id" variable.
_timeweb_get_dns_txt() {
_debug "Trying to locate a DNS TXT record with the value \"$Acme_Txt\"."
TW_Page_Limit=100
TW_Page_Offset=0
TW_Dns_Records_Returned=""
while [ -z "$TW_Dns_Records_Returned" ] || [ "$TW_Dns_Records_Returned" -ge "$TW_Page_Limit" ]; do
_timeweb_list_dns_records "$TW_Page_Limit" "$TW_Page_Offset" || return 1
while
Dns_Record=$(
echo "$TW_Dns_Records" |
sed -n 's/.*{\([^{]*{[^{]*'"$Acme_Txt"'[^}]*}[^}]*\)}.*/\1/p'
)
[ -n "$Dns_Record" ] && {
_timeweb_is_added_txt "$Dns_Record" && return 0
TW_Dns_Records=$(
echo "$TW_Dns_Records" |
sed 's/{\([^{]*{[^{]*'"$Acme_Txt"'[^}]*}[^}]*\)}//'
)
continue
}
do :; done
TW_Page_Offset=$(_math "$TW_Page_Offset" + "$TW_Page_Limit")
done
_err "DNS TXT record was not found."
return 1
}
# Lists domains via the Timeweb Cloud API.
#
# Param 1: Limit for listed domains.
# Param 2: Offset for domains list.
#
# Sets the "TW_Domains" variable.
# Sets the "TW_Domains_Returned" variable.
_timeweb_list_domains() {
_debug "Listing domains via Timeweb Cloud API. Limit: $1, offset: $2."
export _H1="Authorization: Bearer $TW_Token"
if ! TW_Domains=$(_get "$TW_Api/domains?limit=$1&offset=$2"); then
_err "The request to the Timeweb Cloud API failed."
return 1
fi
[ -z "$TW_Domains" ] && {
_err "Empty response from the Timeweb Cloud API."
return 1
}
TW_Domains_Returned=$(
echo "$TW_Domains" |
sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/'
)
[ -z "$TW_Domains_Returned" ] && {
_err "Failed to extract the total count of domains."
return 1
}
[ "$TW_Domains_Returned" -eq "0" ] && {
_err "Domains are missing."
return 1
}
_debug "Domains returned by Timeweb Cloud API: $TW_Domains_Returned."
}
# Lists domain DNS records via the Timeweb Cloud API.
#
# Param 1: Limit for listed DNS records.
# Param 2: Offset for DNS records list.
#
# Sets the "TW_Dns_Records" variable.
# Sets the "TW_Dns_Records_Returned" variable.
_timeweb_list_dns_records() {
_debug "Listing domain DNS records via the Timeweb Cloud API. Limit: $1, offset: $2."
export _H1="Authorization: Bearer $TW_Token"
if ! TW_Dns_Records=$(_get "$TW_Api/domains/$TW_Main_Domain/dns-records?limit=$1&offset=$2"); then
_err "The request to the Timeweb Cloud API failed."
return 1
fi
[ -z "$TW_Dns_Records" ] && {
_err "Empty response from the Timeweb Cloud API."
return 1
}
TW_Dns_Records_Returned=$(
echo "$TW_Dns_Records" |
sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/'
)
[ -z "$TW_Dns_Records_Returned" ] && {
_err "Failed to extract the total count of DNS records."
return 1
}
[ "$TW_Dns_Records_Returned" -eq "0" ] && {
_err "DNS records are missing."
return 1
}
_debug "DNS records returned by Timeweb Cloud API: $TW_Dns_Records_Returned."
}
# Verifies whether the domain is the primary domain for the ACME DNS-01 challenge FQDN.
# The requirement is that the provided domain is the top-level domain
# for the ACME DNS-01 challenge FQDN.
#
# Param 1: Domain object returned by Timeweb Cloud API.
#
# Sets the "TW_Main_Domain" variable (e.g. "_acme-challenge.s1.domain.co.uk" → "domain.co.uk").
# Sets the "TW_Subdomains" variable (e.g. "_acme-challenge.s1.domain.co.uk" → "_acme-challenge.s1").
_timeweb_is_main_domain() {
_debug "Checking if \"$1\" is the main domain of the ACME DNS-01 challenge FQDN."
[ -z "$1" ] && {
_debug "Failed to extract FQDN. Skipping domain."
return 1
}
! echo ".$Acme_Fqdn" | grep -qi "\.$1$" && {
_debug "Domain does not match the ACME DNS-01 challenge FQDN. Skipping domain."
return 1
}
TW_Main_Domain=$1
TW_Subdomains=$(
echo "$Acme_Fqdn" |
sed "s/\.*.\{${#1}\}$//"
)
_debug "Matched domain. ACME DNS-01 challenge FQDN split as [$TW_Subdomains].[$TW_Main_Domain]."
return 0
}
# Verifies whether a DNS record was previously added based on the following criteria:
# - The value matches the ACME DNS-01 challenge TXT record value;
# - The record type is TXT;
# - The subdomain matches the ACME DNS-01 challenge FQDN.
#
# Param 1: DNS record object returned by Timeweb Cloud API.
#
# Sets the "TW_Dns_Txt_Id" variable.
_timeweb_is_added_txt() {
_debug "Checking if \"$1\" is a previously added DNS TXT record."
echo "$1" | grep -qv '"type":"TXT"' && {
_debug "Not a TXT record. Skipping the record."
return 1
}
if [ -n "$TW_Subdomains" ]; then
echo "$1" | grep -qvi "\"subdomain\":\"$TW_Subdomains\"" && {
_debug "Subdomains do not match. Skipping the record."
return 1
}
else
echo "$1" | grep -q '"subdomain\":"..*"' && {
_debug "Subdomains do not match. Skipping the record."
return 1
}
fi
TW_Dns_Txt_Id=$(
echo "$1" |
sed 's/.*"id":\([0-9]*\)[^0-9].*/\1/'
)
[ -z "$TW_Dns_Txt_Id" ] && {
_debug "Failed to extract the DNS record ID. Skipping the record."
return 1
}
_debug "Matching DNS TXT record ID is \"$TW_Dns_Txt_Id\"."
return 0
}
# Adds a DNS TXT record via the Timeweb Cloud API.
_timeweb_dns_txt_add() {
_debug "Adding a new DNS TXT record via the Timeweb Cloud API."
export _H1="Authorization: Bearer $TW_Token"
export _H2="Content-Type: application/json"
if ! TW_Response=$(
_post "{
\"subdomain\":\"$TW_Subdomains\",
\"type\":\"TXT\",
\"value\":\"$Acme_Txt\"
}" \
"$TW_Api/domains/$TW_Main_Domain/dns-records"
); then
_err "The request to the Timeweb Cloud API failed."
return 1
fi
[ -z "$TW_Response" ] && {
_err "An unexpected empty response was received from the Timeweb Cloud API."
return 1
}
TW_Dns_Txt_Id=$(
echo "$TW_Response" |
sed 's/.*"id":\([0-9]*\)[^0-9].*/\1/'
)
[ -z "$TW_Dns_Txt_Id" ] && {
_err "Failed to extract the DNS TXT Record ID."
return 1
}
_debug "DNS TXT record has been added. ID: \"$TW_Dns_Txt_Id\"."
}
# Removes a DNS record via the Timeweb Cloud API.
_timeweb_dns_txt_remove() {
_debug "Removing DNS record via the Timeweb Cloud API."
export _H1="Authorization: Bearer $TW_Token"
if ! TW_Response=$(
_post \
"" \
"$TW_Api/domains/$TW_Main_Domain/dns-records/$TW_Dns_Txt_Id" \
"" \
"DELETE"
); then
_err "The request to the Timeweb Cloud API failed."
return 1
fi
[ -n "$TW_Response" ] && {
_err "Received an unexpected response body from the Timeweb Cloud API."
return 1
}
_debug "DNS TXT record with ID \"$TW_Dns_Txt_Id\" has been removed."
}

121
dnsapi/dns_yandex.sh

@ -1,121 +0,0 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_yandex_info='Yandex Domains
Site: tech.Yandex.com/domain/
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_yandex
Options:
PDD_Token API Token
Issues: github.com/non7top/acme.sh/issues
Author: <non7top@gmail.com>
'
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_yandex_add() {
fulldomain="${1}"
txtvalue="${2}"
_debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'"
_PDD_credentials || return 1
_PDD_get_domain || return 1
_debug "Found suitable domain: $domain"
_PDD_get_record_ids || return 1
_debug "Record_ids: $record_ids"
if [ -n "$record_ids" ]; then
_info "All existing $subdomain records from $domain will be removed at the very end."
fi
data="domain=${domain}&type=TXT&subdomain=${subdomain}&ttl=300&content=${txtvalue}"
uri="https://pddimp.yandex.ru/api2/admin/dns/add"
result="$(_post "${data}" "${uri}" | _normalizeJson)"
_debug "Result: $result"
if ! _contains "$result" '"success":"ok"'; then
if _contains "$result" '"success":"error"' && _contains "$result" '"error":"record_exists"'; then
_info "Record already exists."
else
_err "Can't add $subdomain to $domain."
return 1
fi
fi
}
#Usage: dns_myapi_rm _acme-challenge.www.domain.com
dns_yandex_rm() {
fulldomain="${1}"
_debug "Calling: dns_yandex_rm() '${fulldomain}'"
_PDD_credentials || return 1
_PDD_get_domain "$fulldomain" || return 1
_debug "Found suitable domain: $domain"
_PDD_get_record_ids "${domain}" "${subdomain}" || return 1
_debug "Record_ids: $record_ids"
for record_id in $record_ids; do
data="domain=${domain}&record_id=${record_id}"
uri="https://pddimp.yandex.ru/api2/admin/dns/del"
result="$(_post "${data}" "${uri}" | _normalizeJson)"
_debug "Result: $result"
if ! _contains "$result" '"success":"ok"'; then
_info "Can't remove $subdomain from $domain."
fi
done
}
#################### Private functions below ##################################
_PDD_get_domain() {
subdomain_start=1
while true; do
domain_start=$(_math $subdomain_start + 1)
domain=$(echo "$fulldomain" | cut -d . -f "$domain_start"-)
subdomain=$(echo "$fulldomain" | cut -d . -f -"$subdomain_start")
_debug "Checking domain $domain"
if [ -z "$domain" ]; then
return 1
fi
uri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=$domain"
result="$(_get "${uri}" | _normalizeJson)"
_debug "Result: $result"
if _contains "$result" '"success":"ok"'; then
return 0
fi
subdomain_start=$(_math $subdomain_start + 1)
done
}
_PDD_credentials() {
if [ -z "${PDD_Token}" ]; then
PDD_Token=""
_err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx."
_err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token."
return 1
else
_saveaccountconf PDD_Token "${PDD_Token}"
fi
export _H1="PddToken: $PDD_Token"
}
_PDD_get_record_ids() {
_debug "Check existing records for $subdomain"
uri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${domain}"
result="$(_get "${uri}" | _normalizeJson)"
_debug "Result: $result"
if ! _contains "$result" '"success":"ok"'; then
return 1
fi
record_ids=$(echo "$result" | _egrep_o "{[^{]*\"subdomain\":\"${subdomain}\"[^}]*}" | sed -n -e 's#.*"record_id": \([0-9]*\).*#\1#p')
}

352
dnsapi/dns_yandex360.sh

@ -0,0 +1,352 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_yandex360_info='Yandex 360 for Business DNS API.
Yandex 360 for Business is a digital environment for effective collaboration.
Site: https://360.yandex.com/
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360
Options:
YANDEX360_CLIENT_ID OAuth 2.0 ClientID
YANDEX360_CLIENT_SECRET OAuth 2.0 Client secret
OptionsAlt:
YANDEX360_ORG_ID Organization ID. Optional.
YANDEX360_ACCESS_TOKEN OAuth 2.0 Access token. Optional.
Issues: https://github.com/acmesh-official/acme.sh/issues/5213
Author: <Als@admin.ru.net>
'
YANDEX360_API_BASE='https://api360.yandex.net/directory/v1'
YANDEX360_OAUTH_BASE='https://oauth.yandex.ru'
######## Public functions #####################
dns_yandex360_add() {
fulldomain="$(_idn "$1")"
txtvalue=$2
_info 'Using Yandex 360 DNS API'
if ! _check_variables; then
return 1
fi
if ! _get_root "$fulldomain"; then
return 1
fi
sub_domain=$(echo "$fulldomain" | sed "s/\.$root_domain$//")
_debug 'Adding Yandex 360 DNS record for subdomain' "$sub_domain"
dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns"
data='{"name":"'"$sub_domain"'","type":"TXT","ttl":60,"text":"'"$txtvalue"'"}'
response="$(_post "$data" "$dns_api_url" '' 'POST' 'application/json')"
if _contains "$response" 'recordId'; then
return 0
else
_debug 'Response' "$response"
return 1
fi
}
dns_yandex360_rm() {
fulldomain="$(_idn "$1")"
txtvalue=$2
_info 'Using Yandex 360 DNS API'
if ! _check_variables; then
return 1
fi
if ! _get_root "$fulldomain"; then
return 1
fi
_debug 'Retrieving 100 records from Yandex 360 DNS'
dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns?perPage=100"
response="$(_get "$dns_api_url" '' '')"
if ! _contains "$response" "$txtvalue"; then
_info 'DNS record not found. Nothing to remove.'
_debug 'Response' "$response"
return 1
fi
response="$(echo "$response" | _normalizeJson)"
record_id=$(
echo "$response" |
_egrep_o '\{[^}]*'"${txtvalue}"'[^}]*\}' |
_egrep_o '"recordId":[0-9]*' |
cut -d':' -f2
)
if [ -z "$record_id" ]; then
_err 'Unable to get record ID to remove'
return 1
fi
_debug 'Removing DNS record' "$record_id"
delete_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns/${record_id}"
response="$(_post '' "$delete_url" '' 'DELETE')"
if _contains "$response" '{}'; then
return 0
else
_debug 'Response' "$response"
return 1
fi
}
#################### Private functions below ##################################
_check_variables() {
YANDEX360_CLIENT_ID="${YANDEX360_CLIENT_ID:-$(_readaccountconf_mutable YANDEX360_CLIENT_ID)}"
YANDEX360_CLIENT_SECRET="${YANDEX360_CLIENT_SECRET:-$(_readaccountconf_mutable YANDEX360_CLIENT_SECRET)}"
YANDEX360_ORG_ID="${YANDEX360_ORG_ID:-$(_readaccountconf_mutable YANDEX360_ORG_ID)}"
YANDEX360_ACCESS_TOKEN="${YANDEX360_ACCESS_TOKEN:-$(_readaccountconf_mutable YANDEX360_ACCESS_TOKEN)}"
YANDEX360_REFRESH_TOKEN="${YANDEX360_REFRESH_TOKEN:-$(_readaccountconf_mutable YANDEX360_REFRESH_TOKEN)}"
if [ -n "$YANDEX360_ACCESS_TOKEN" ]; then
_info '========================================='
_info ' ATTENTION'
_info '========================================='
_info 'A manually provided Yandex 360 access token has been detected, which is not recommended.'
_info 'Please note that this token is valid for a limited time after issuance.'
_info 'It is recommended to obtain the token interactively using acme.sh for one-time setup.'
_info 'Subsequent token renewals will be handled automatically.'
_info 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360'
_info '========================================='
_saveaccountconf_mutable YANDEX360_ACCESS_TOKEN "$YANDEX360_ACCESS_TOKEN"
export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN"
elif [ -z "$YANDEX360_CLIENT_ID" ] || [ -z "$YANDEX360_CLIENT_SECRET" ]; then
_err '========================================='
_err ' ERROR'
_err '========================================='
_err 'The required environment variables YANDEX360_CLIENT_ID and YANDEX360_CLIENT_SECRET are not set.'
_err 'Alternatively, you can set YANDEX360_ACCESS_TOKEN environment variable.'
_err 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360'
_err '========================================='
return 1
else
_saveaccountconf_mutable YANDEX360_CLIENT_ID "$YANDEX360_CLIENT_ID"
_saveaccountconf_mutable YANDEX360_CLIENT_SECRET "$YANDEX360_CLIENT_SECRET"
if [ -n "$YANDEX360_REFRESH_TOKEN" ]; then
_debug 'Refresh token found. Attempting to refresh access token.'
fi
_refresh_token || _get_token || return 1
fi
if [ -z "$YANDEX360_ORG_ID" ]; then
org_response="$(_get "${YANDEX360_API_BASE}/org" '' '')"
if _contains "$org_response" '"organizations"'; then
org_response="$(echo "$org_response" | _normalizeJson)"
YANDEX360_ORG_ID=$(
echo "$org_response" |
_egrep_o '"id":[[:space:]]*[0-9]+' |
cut -d':' -f2
)
_debug 'Automatically retrieved YANDEX360_ORG_ID' "$YANDEX360_ORG_ID"
else
_err '========================================='
_err ' ERROR'
_err '========================================='
_err "Failed to retrieve YANDEX360_ORG_ID automatically."
_err 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360'
_err '========================================='
_debug 'Response' "$org_response"
return 1
fi
fi
return 0
}
_get_token() {
_info "$(__red '=========================================')"
_info "$(__red ' NOTICE')"
_info "$(__red '=========================================')"
_info "$(__red 'Before using the Yandex 360 API, you need to complete an authorization procedure.')"
_info "$(__red 'The initial access token is obtained interactively and is a one-time operation.')"
_info "$(__red 'Subsequent API requests will be handled automatically.')"
_info "$(__red '=========================================')"
_info 'Initiating device authorization flow'
device_code_url="${YANDEX360_OAUTH_BASE}/device/code"
hostname=$(uname -n)
data="client_id=$YANDEX360_CLIENT_ID&device_id=acme.sh ${hostname}&device_name=acme.sh ${hostname}"
response="$(_post "$data" "$device_code_url" '' 'POST')"
if ! _contains "$response" 'device_code'; then
_err 'Failed to get device code'
_debug 'Response' "$response"
return 1
fi
response="$(echo "$response" | _normalizeJson)"
device_code=$(
echo "$response" |
_egrep_o '"device_code":"[^"]*"' |
cut -d'"' -f4
)
_debug 'Device code' "$device_code"
user_code=$(
echo "$response" |
_egrep_o '"user_code":"[^"]*"' |
cut -d'"' -f4
)
_debug 'User code' "$user_code"
verification_url=$(
echo "$response" |
_egrep_o '"verification_url":"[^"]*"' |
cut -d'"' -f4
)
_debug 'Verification URL' "$verification_url"
interval=$(
echo "$response" |
_egrep_o '"interval":[[:space:]]*[0-9]+' |
cut -d':' -f2
)
_debug 'Polling interval' "$interval"
_info "$(__red 'Please visit '"$verification_url"' and log in as an organization administrator')"
_info "$(__red 'Once logged in, enter the code: '"$user_code"' on the page from the previous step')"
_info "$(__red 'Waiting for authorization...')"
_debug 'Polling for token'
token_url="${YANDEX360_OAUTH_BASE}/token"
while true; do
data="grant_type=device_code&code=$device_code&client_id=$YANDEX360_CLIENT_ID&client_secret=$YANDEX360_CLIENT_SECRET"
response="$(_post "$data" "$token_url" '' 'POST')"
if _contains "$response" 'access_token'; then
response="$(echo "$response" | _normalizeJson)"
YANDEX360_ACCESS_TOKEN=$(
echo "$response" |
_egrep_o '"access_token":"[^"]*"' |
cut -d'"' -f4
)
YANDEX360_REFRESH_TOKEN=$(
echo "$response" |
_egrep_o '"refresh_token":"[^"]*"' |
cut -d'"' -f4
)
_secure_debug 'Obtained access token' "$YANDEX360_ACCESS_TOKEN"
_secure_debug 'Obtained refresh token' "$YANDEX360_REFRESH_TOKEN"
_saveaccountconf_mutable YANDEX360_REFRESH_TOKEN "$YANDEX360_REFRESH_TOKEN"
export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN"
_info 'Access token obtained successfully'
return 0
elif _contains "$response" 'authorization_pending'; then
_debug 'Response' "$response"
_debug "Authorization pending. Waiting $interval seconds before next attempt."
_sleep "$interval"
else
_debug 'Response' "$response"
_err 'Failed to get access token'
return 1
fi
done
}
_refresh_token() {
token_url="${YANDEX360_OAUTH_BASE}/token"
data="grant_type=refresh_token&refresh_token=$YANDEX360_REFRESH_TOKEN&client_id=$YANDEX360_CLIENT_ID&client_secret=$YANDEX360_CLIENT_SECRET"
response="$(_post "$data" "$token_url" '' 'POST')"
if _contains "$response" 'access_token'; then
response="$(echo "$response" | _normalizeJson)"
YANDEX360_ACCESS_TOKEN=$(
echo "$response" |
_egrep_o '"access_token":"[^"]*"' |
cut -d'"' -f4
)
YANDEX360_REFRESH_TOKEN=$(
echo "$response" |
_egrep_o '"refresh_token":"[^"]*"' |
cut -d'"' -f4
)
_secure_debug 'Received access token' "$YANDEX360_ACCESS_TOKEN"
_secure_debug 'Received refresh token' "$YANDEX360_REFRESH_TOKEN"
_saveaccountconf_mutable YANDEX360_REFRESH_TOKEN "$YANDEX360_REFRESH_TOKEN"
export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN"
_info 'Access token refreshed successfully'
return 0
else
_info 'Failed to refresh token. Will attempt to obtain a new one.'
_debug 'Response' "$response"
return 1
fi
}
_get_root() {
domain="$1"
for org_id in $YANDEX360_ORG_ID; do
_debug 'Checking organization ID' "$org_id"
domains_api_url="${YANDEX360_API_BASE}/org/${org_id}/domains"
domains_response="$(_get "$domains_api_url" '' '')"
if ! _contains "$domains_response" '"domains"'; then
_debug 'No domains found for organization' "$org_id"
_debug 'Response' "$domains_response"
continue
fi
domains_response="$(echo "$domains_response" | _normalizeJson)"
domain_names=$(
echo "$domains_response" |
_egrep_o '"name":"[^"]*"' |
cut -d'"' -f4
)
for d in $domain_names; do
d="$(_idn "$d")"
_debug 'Checking domain' "$d"
if _endswith "$domain" "$d"; then
root_domain="$d"
break
fi
done
if [ -n "$root_domain" ]; then
_debug "Root domain found: $root_domain in organization $org_id"
YANDEX360_ORG_ID="$org_id"
_saveaccountconf_mutable YANDEX360_ORG_ID "$YANDEX360_ORG_ID"
return 0
fi
done
if [ -z "$root_domain" ]; then
_err "Could not find a matching root domain for $domain in any organization"
return 1
fi
}

101
notify/bark.sh

@ -1,32 +1,40 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#Support iOS Bark Notification
# Support iOS Bark Notification
#BARK_API_URL="https://api.day.app/xxxx"
#BARK_SOUND="yyyy"
#BARK_GROUP="zzzz"
# Every parameter explained: https://github.com/Finb/bark-server/blob/master/docs/API_V2.md#push
# BARK_API_URL="https://api.day.app/xxxx" (required)
# BARK_GROUP="ACME" (optional)
# BARK_SOUND="alarm" (optional)
# BARK_LEVEL="active" (optional)
# BARK_BADGE=0 (optional)
# BARK_AUTOMATICALLYCOPY="1" (optional)
# BARK_COPY="My clipboard Content" (optional)
# BARK_ICON="https://example.com/icon.png" (optional)
# BARK_ISARCHIVE="1" (optional)
# BARK_URL="https://example.com" (optional)
# subject content statusCode # subject content statusCode
bark_send() { bark_send() {
_subject="$1" _subject="$1"
_content="$2" _content="$2"
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_statusCode="$3" # 0: success, 1: error, 2: skipped
_debug "_subject" "$_subject" _debug "_subject" "$_subject"
_debug "_content" "$_content" _debug "_content" "$_content"
_debug "_statusCode" "$_statusCode" _debug "_statusCode" "$_statusCode"
_content=$(echo "$_content" | _url_encode)
_subject=$(echo "$_subject" | _url_encode)
BARK_API_URL="${BARK_API_URL:-$(_readaccountconf_mutable BARK_API_URL)}" BARK_API_URL="${BARK_API_URL:-$(_readaccountconf_mutable BARK_API_URL)}"
if [ -z "$BARK_API_URL" ]; then if [ -z "$BARK_API_URL" ]; then
BARK_API_URL=""
_err "You didn't specify a Bark API URL BARK_API_URL yet." _err "You didn't specify a Bark API URL BARK_API_URL yet."
_err "You can download Bark from App Store and get yours." _err "You can download Bark from App Store and get yours."
return 1 return 1
fi fi
_saveaccountconf_mutable BARK_API_URL "$BARK_API_URL" _saveaccountconf_mutable BARK_API_URL "$BARK_API_URL"
BARK_SOUND="${BARK_SOUND:-$(_readaccountconf_mutable BARK_SOUND)}"
_saveaccountconf_mutable BARK_SOUND "$BARK_SOUND"
BARK_GROUP="${BARK_GROUP:-$(_readaccountconf_mutable BARK_GROUP)}" BARK_GROUP="${BARK_GROUP:-$(_readaccountconf_mutable BARK_GROUP)}"
if [ -z "$BARK_GROUP" ]; then if [ -z "$BARK_GROUP" ]; then
BARK_GROUP="ACME" BARK_GROUP="ACME"
@ -35,10 +43,79 @@ bark_send() {
_saveaccountconf_mutable BARK_GROUP "$BARK_GROUP" _saveaccountconf_mutable BARK_GROUP "$BARK_GROUP"
fi fi
_content=$(echo "$_content" | _url_encode)
_subject=$(echo "$_subject" | _url_encode)
BARK_SOUND="${BARK_SOUND:-$(_readaccountconf_mutable BARK_SOUND)}"
if [ -n "$BARK_SOUND" ]; then
_saveaccountconf_mutable BARK_SOUND "$BARK_SOUND"
fi
BARK_LEVEL="${BARK_LEVEL:-$(_readaccountconf_mutable BARK_LEVEL)}"
if [ -n "$BARK_LEVEL" ]; then
_saveaccountconf_mutable BARK_LEVEL "$BARK_LEVEL"
fi
BARK_BADGE="${BARK_BADGE:-$(_readaccountconf_mutable BARK_BADGE)}"
if [ -n "$BARK_BADGE" ]; then
_saveaccountconf_mutable BARK_BADGE "$BARK_BADGE"
fi
BARK_AUTOMATICALLYCOPY="${BARK_AUTOMATICALLYCOPY:-$(_readaccountconf_mutable BARK_AUTOMATICALLYCOPY)}"
if [ -n "$BARK_AUTOMATICALLYCOPY" ]; then
_saveaccountconf_mutable BARK_AUTOMATICALLYCOPY "$BARK_AUTOMATICALLYCOPY"
fi
BARK_COPY="${BARK_COPY:-$(_readaccountconf_mutable BARK_COPY)}"
if [ -n "$BARK_COPY" ]; then
_saveaccountconf_mutable BARK_COPY "$BARK_COPY"
fi
BARK_ICON="${BARK_ICON:-$(_readaccountconf_mutable BARK_ICON)}"
if [ -n "$BARK_ICON" ]; then
_saveaccountconf_mutable BARK_ICON "$BARK_ICON"
fi
BARK_ISARCHIVE="${BARK_ISARCHIVE:-$(_readaccountconf_mutable BARK_ISARCHIVE)}"
if [ -n "$BARK_ISARCHIVE" ]; then
_saveaccountconf_mutable BARK_ISARCHIVE "$BARK_ISARCHIVE"
fi
BARK_URL="${BARK_URL:-$(_readaccountconf_mutable BARK_URL)}"
if [ -n "$BARK_URL" ]; then
_saveaccountconf_mutable BARK_URL "$BARK_URL"
fi
_params=""
if [ -n "$BARK_SOUND" ]; then
_params="$_params&sound=$BARK_SOUND"
fi
if [ -n "$BARK_GROUP" ]; then
_params="$_params&group=$BARK_GROUP"
fi
if [ -n "$BARK_LEVEL" ]; then
_params="$_params&level=$BARK_LEVEL"
fi
if [ -n "$BARK_BADGE" ]; then
_params="$_params&badge=$BARK_BADGE"
fi
if [ -n "$BARK_AUTOMATICALLYCOPY" ]; then
_params="$_params&automaticallyCopy=$BARK_AUTOMATICALLYCOPY"
fi
if [ -n "$BARK_COPY" ]; then
_params="$_params&copy=$BARK_COPY"
fi
if [ -n "$BARK_ICON" ]; then
_params="$_params&icon=$BARK_ICON"
fi
if [ -n "$BARK_ISARCHIVE" ]; then
_params="$_params&isArchive=$BARK_ISARCHIVE"
fi
if [ -n "$BARK_URL" ]; then
_params="$_params&url=$BARK_URL"
fi
_params=$(echo "$_params" | sed 's/^&//') # remove leading '&' if exists
response="$(_get "$BARK_API_URL/$_subject/$_content?sound=$BARK_SOUND&group=$BARK_GROUP")"
response="$(_get "$BARK_API_URL/$_subject/$_content?$_params")"
if [ "$?" = "0" ] && _contains "$response" "success"; then if [ "$?" = "0" ] && _contains "$response" "success"; then
_info "Bark API fired success." _info "Bark API fired success."

69
notify/teams.sh

@ -3,10 +3,6 @@
#Support Microsoft Teams webhooks #Support Microsoft Teams webhooks
#TEAMS_WEBHOOK_URL="" #TEAMS_WEBHOOK_URL=""
#TEAMS_THEME_COLOR=""
#TEAMS_SUCCESS_COLOR=""
#TEAMS_ERROR_COLOR=""
#TEAMS_SKIP_COLOR=""
teams_send() { teams_send() {
_subject="$1" _subject="$1"
@ -14,9 +10,9 @@ teams_send() {
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
_debug "_statusCode" "$_statusCode" _debug "_statusCode" "$_statusCode"
_color_success="2cbe4e" # green
_color_danger="cb2431" # red
_color_muted="586069" # gray
_color_success="Good"
_color_danger="Attention"
_color_muted="Accent"
TEAMS_WEBHOOK_URL="${TEAMS_WEBHOOK_URL:-$(_readaccountconf_mutable TEAMS_WEBHOOK_URL)}" TEAMS_WEBHOOK_URL="${TEAMS_WEBHOOK_URL:-$(_readaccountconf_mutable TEAMS_WEBHOOK_URL)}"
if [ -z "$TEAMS_WEBHOOK_URL" ]; then if [ -z "$TEAMS_WEBHOOK_URL" ]; then
@ -26,26 +22,6 @@ teams_send() {
fi fi
_saveaccountconf_mutable TEAMS_WEBHOOK_URL "$TEAMS_WEBHOOK_URL" _saveaccountconf_mutable TEAMS_WEBHOOK_URL "$TEAMS_WEBHOOK_URL"
TEAMS_THEME_COLOR="${TEAMS_THEME_COLOR:-$(_readaccountconf_mutable TEAMS_THEME_COLOR)}"
if [ -n "$TEAMS_THEME_COLOR" ]; then
_saveaccountconf_mutable TEAMS_THEME_COLOR "$TEAMS_THEME_COLOR"
fi
TEAMS_SUCCESS_COLOR="${TEAMS_SUCCESS_COLOR:-$(_readaccountconf_mutable TEAMS_SUCCESS_COLOR)}"
if [ -n "$TEAMS_SUCCESS_COLOR" ]; then
_saveaccountconf_mutable TEAMS_SUCCESS_COLOR "$TEAMS_SUCCESS_COLOR"
fi
TEAMS_ERROR_COLOR="${TEAMS_ERROR_COLOR:-$(_readaccountconf_mutable TEAMS_ERROR_COLOR)}"
if [ -n "$TEAMS_ERROR_COLOR" ]; then
_saveaccountconf_mutable TEAMS_ERROR_COLOR "$TEAMS_ERROR_COLOR"
fi
TEAMS_SKIP_COLOR="${TEAMS_SKIP_COLOR:-$(_readaccountconf_mutable TEAMS_SKIP_COLOR)}"
if [ -n "$TEAMS_SKIP_COLOR" ]; then
_saveaccountconf_mutable TEAMS_SKIP_COLOR "$TEAMS_SKIP_COLOR"
fi
export _H1="Content-Type: application/json" export _H1="Content-Type: application/json"
_subject=$(echo "$_subject" | _json_encode) _subject=$(echo "$_subject" | _json_encode)
@ -63,16 +39,35 @@ teams_send() {
;; ;;
esac esac
_color=$(echo "$_color" | tr -cd 'a-fA-F0-9')
if [ -z "$_color" ]; then
_color=$(echo "${TEAMS_THEME_COLOR:-$_color_muted}" | tr -cd 'a-fA-F0-9')
fi
_data="{\"title\": \"$_subject\","
if [ -n "$_color" ]; then
_data="$_data\"themeColor\": \"$_color\", "
fi
_data="$_data\"text\": \"$_content\"}"
_data="{
\"type\": \"message\",
\"attachments\": [
{
\"contentType\": \"application/vnd.microsoft.card.adaptive\",
\"contentUrl\": null,
\"content\": {
\"schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",
\"type\": \"AdaptiveCard\",
\"version\": \"1.2\",
\"body\": [
{
\"type\": \"TextBlock\",
\"size\": \"large\",
\"weight\": \"bolder\",
\"wrap\": true,
\"color\": \"$_color\",
\"text\": \"$_subject\"
},
{
\"type\": \"TextBlock\",
\"text\": \"$_content\",
\"wrap\": true
}
]
}
}
]
}"
if response=$(_post "$_data" "$TEAMS_WEBHOOK_URL"); then if response=$(_post "$_data" "$TEAMS_WEBHOOK_URL"); then
if ! _contains "$response" error; then if ! _contains "$response" error; then

Loading…
Cancel
Save