Merge pull request #7119 from acmesh-official/dev
Some checks failed
Apache / Apache (push) Has been cancelled
DNS / CheckToken (push) Has been cancelled
DragonFlyBSD / DragonFlyBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
FreeBSD / FreeBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
FreeBSD / FreeBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
GhostBSD / GhostBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
GhostBSD / GhostBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Haiku / Haiku (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Haiku / Haiku (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Linux / Linux (almalinux:latest) (push) Has been cancelled
Linux / Linux (alpine:latest) (push) Has been cancelled
Linux / Linux (archlinux:latest) (push) Has been cancelled
Linux / Linux (debian:latest) (push) Has been cancelled
Linux / Linux (fedora:latest) (push) Has been cancelled
Linux / Linux (gentoo/stage3) (push) Has been cancelled
Linux / Linux (kalilinux/kali) (push) Has been cancelled
Linux / Linux (opensuse/leap:latest) (push) Has been cancelled
Linux / Linux (oraclelinux:8) (push) Has been cancelled
Linux / Linux (ubuntu:latest) (push) Has been cancelled
MacOS / MacOS (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
MidnightBSD / MidnightBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
NetBSD / NetBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Nginx / Nginx (push) Has been cancelled
Omnios / Omnios (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Omnios / Omnios (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenBSD / OpenBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenBSD / OpenBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenIndiana / OpenIndiana (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenIndiana / OpenIndiana (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
PebbleStrict / PebbleStrict (push) Has been cancelled
PebbleStrict / PebbleStrict_IPCert (push) Has been cancelled
Solaris / Solaris (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Solaris / Solaris (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Tribblix / Tribblix (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Tribblix / Tribblix (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (Smallstep Intermediate CA, Smallstep Intermediate CA, , 1, https://localhost:9000/acme/acme/directory, ) (push) Has been cancelled
Ubuntu / Ubuntu (Smallstep Intermediate CA, Smallstep Intermediate CA, , 1, https://localhost:9000/acme/acme/directory, 1, , 172.17.0.1) (push) Has been cancelled
Ubuntu / Ubuntu (ZeroSSL RSA DV SSL CA 2, ZeroSSL ECC DV SSL CA 2, githubtest@acme.sh, ZeroSSL.com, ) (push) Has been cancelled
Windows / Windows (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Build DockerHub / CheckToken (push) Has been cancelled
Shellcheck / ShellCheck (push) Has been cancelled
Shellcheck / shfmt (push) Has been cancelled
DNS / Fail (push) Has been cancelled
DNS / Docker (push) Has been cancelled
DNS / MacOS (push) Has been cancelled
DNS / Windows (push) Has been cancelled
DNS / FreeBSD (push) Has been cancelled
DNS / GhostBSD (push) Has been cancelled
DNS / OpenBSD (push) Has been cancelled
DNS / NetBSD (push) Has been cancelled
DNS / DragonFlyBSD (push) Has been cancelled
DNS / MidnightBSD (push) Has been cancelled
DNS / Solaris (push) Has been cancelled
DNS / Omnios (push) Has been cancelled
DNS / OpenIndiana (push) Has been cancelled
DNS / Tribblix (push) Has been cancelled
DNS / Haiku (push) Has been cancelled
Build DockerHub / build (push) Has been cancelled

sync
This commit is contained in:
neil 2026-07-12 10:11:32 +08:00 committed by GitHub
commit 3e64b5e6f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 480 additions and 42 deletions

View file

@ -61,7 +61,7 @@ jobs:
// Handled by the Blacklist / Revert Command workflows.
return;
}
if (issue.title.startsWith("Report bugs to")) {
if (/^report\s+(bugs?|issues?)\b/i.test(issue.title)) {
// Tracking issue for a third-party dns/deploy/notify api:
// no upgrade boilerplate; assign it to the opener and label it.
await github.rest.issues.addAssignees({

View file

@ -81,8 +81,8 @@ if [ \"\$1\" = \"daemon\" ]; then \n \
echo \"\$LE_CONFIG_HOME/crontab not found, generating one\" \n \
time=\$(date -u \"+%s\") \n \
random_minute=\$((\$time % 60)) \n \
random_hour=\$((\$time / 60 % 24)) \n \
echo \"\$random_minute \$random_hour * * * \\\"\$LE_WORKING_DIR\\\"/acme.sh --cron --home \\\"\$LE_WORKING_DIR\\\" --config-home \\\"\$LE_CONFIG_HOME\\\"\" > \"\$LE_CONFIG_HOME\"/crontab \n \
random_hour=\$((\$time / 60 % 6)) \n \
echo \"\$random_minute \$random_hour,\$((\$random_hour + 6)),\$((\$random_hour + 12)),\$((\$random_hour + 18)) * * * \\\"\$LE_WORKING_DIR\\\"/acme.sh --cron --home \\\"\$LE_WORKING_DIR\\\" --config-home \\\"\$LE_CONFIG_HOME\\\"\" > \"\$LE_CONFIG_HOME\"/crontab \n \
fi \n \
echo \"Running Supercronic using crontab at \$LE_CONFIG_HOME/crontab\" \n \
exec -- /usr/bin/supercronic \"\$LE_CONFIG_HOME/crontab\" \n \

31
acme.sh
View file

@ -1443,13 +1443,13 @@ _readSubjectAltNamesFromCSR() {
_debug _dnsAltnames "$_dnsAltnames"
# escape the wildcard '*' so it is not taken as a regex operator by grep/sed below
_excapedAlgnames="$(echo "$_dnsAltnames" | tr '*' '#')"
_debug _excapedAlgnames "$_excapedAlgnames"
_escapedAltnames="$(echo "$_dnsAltnames" | tr '*' '#')"
_debug _escapedAltnames "$_escapedAltnames"
_escapedSubject="$(echo "$_csrsubj" | tr '*' '#')"
_debug _escapedSubject "$_escapedSubject"
if _contains "$_excapedAlgnames," "DNS:$_escapedSubject,"; then
if _contains "$_escapedAltnames," "DNS:$_escapedSubject,"; then
_debug "AltNames contains subject"
_dnsAltnames="$(echo "$_excapedAlgnames," | sed "s/DNS:$_escapedSubject,//g" | tr '#' '*' | sed "s/,\$//g")"
_dnsAltnames="$(echo "$_escapedAltnames," | sed "s/DNS:$_escapedSubject,//g" | tr '#' '*' | sed "s/,\$//g")"
_debug _dnsAltnames "$_dnsAltnames"
else
_debug "AltNames doesn't contain subject"
@ -2594,6 +2594,13 @@ _savedeployconf() {
_cleardomainconf "$1"
}
#key
_cleardeployconf() {
_cleardomainconf "SAVED_$1"
#remove later
_cleardomainconf "$1"
}
#key
_getdeployconf() {
_rac_key="$1"
@ -5343,6 +5350,8 @@ $_authorizations_map"
fi
_d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
test "$_d_alias" = "$NO_VALUE" && _d_alias=""
# strip the trailing dot of a fully-qualified alias domain
_d_alias="${_d_alias%.}"
_alias_index="$(_math "$_alias_index" + 1)"
_debug "_d_alias" "$_d_alias"
if [ "$_d_alias" ]; then
@ -7411,9 +7420,9 @@ _precheck() {
fi
if ! _exists "socat" && ! _exists "python" && ! _exists "python2" && ! _exists "python3"; then
_err "It is recommended to install socat or python first."
_err "We use socat or python for the standalone server, which is used for standalone mode."
_err "If you don't want to use standalone mode, you may ignore this warning."
_info "It is recommended to install socat or python first."
_info "We use socat or python for the standalone server, which is used for standalone mode."
_info "If you don't want to use standalone mode, you may ignore this warning."
fi
return 0
@ -7756,6 +7765,14 @@ _send_notify() {
continue
fi
if ! (
# The dns/deploy hooks export _H1.._H5 in the main process, so the
# values are inherited here. Clear them: a stale Authorization header
# from another service must not leak into the notify request.
export _H1=""
export _H2=""
export _H3=""
export _H4=""
export _H5=""
if ! . "$_n_hook_file"; then
_err "Error loading file $_n_hook_file. Please check your API file and try again."
return 1

View file

@ -210,7 +210,7 @@ _clear_envs() {
echo "$env_pairs" | while IFS='=' read -r _key _value; do
_debug3 "Deleting key" "$_key"
_cleardomainconf "SAVED_$_key"
_cleardeployconf "$_key"
unset -v "$_key"
done
}

View file

@ -25,7 +25,8 @@
# export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
# export DEPLOY_SSH_USE_SCP="" yes or no, default to no
# export DEPLOY_SSH_SCP_CMD="" defaults to "scp -q"
#
# export DEPLOY_SSH_REMOTE_SHELL="" # defaults to sh -c
# export DEPLOY_SSH_REMOTE_CMD_QUOTE="" # yes or no, defaults to yes
######## Public functions #####################
#domain keyfile certfile cafile fullchain
@ -71,6 +72,24 @@ ssh_deploy() {
fi
_savedeployconf DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
# REMOTE_SHELL is optional. If not provided then use sh
_migratedeployconf Le_Deploy_ssh_remote_shell DEPLOY_SSH_REMOTE_SHELL
_getdeployconf DEPLOY_SSH_REMOTE_SHELL
_debug2 DEPLOY_SSH_REMOTE_SHELL "$DEPLOY_SSH_REMOTE_SHELL"
if [ -z "$DEPLOY_SSH_REMOTE_SHELL" ]; then
DEPLOY_SSH_REMOTE_SHELL="sh -c"
fi
_savedeployconf DEPLOY_SSH_REMOTE_SHELL "$DEPLOY_SSH_REMOTE_SHELL"
# REMOTE_CMD_QUOTE is optional. If not provided then yes
_migratedeployconf Le_Deploy_ssh_remote_cmd_quote DEPLOY_SSH_REMOTE_CMD_QUOTE
_getdeployconf DEPLOY_SSH_REMOTE_CMD_QUOTE
_debug2 DEPLOY_SSH_REMOTE_CMD_QUOTE "$DEPLOY_SSH_REMOTE_CMD_QUOTE"
if [ -z "$DEPLOY_SSH_REMOTE_CMD_QUOTE" ]; then
DEPLOY_SSH_REMOTE_CMD_QUOTE="yes"
fi
_savedeployconf DEPLOY_SSH_REMOTE_CMD_QUOTE "$DEPLOY_SSH_REMOTE_CMD_QUOTE"
# BACKUP is optional. If not provided then default to previously saved value or yes.
_migratedeployconf Le_Deploy_ssh_backup DEPLOY_SSH_BACKUP
_getdeployconf DEPLOY_SSH_BACKUP
@ -434,9 +453,13 @@ _ssh_remote_cmd() {
_secure_debug "Remote commands to execute: $_cmd"
_info "Submitting sequence of commands to remote server by $_ssh_cmd"
# quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029
$_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
if [ "$DEPLOY_SSH_REMOTE_CMD_QUOTE" = "yes" ]; then
# quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029
$_ssh_cmd "$DEPLOY_SSH_USER@$_host" "$DEPLOY_SSH_REMOTE_SHELL" "'$_cmd'"
else
$_ssh_cmd "$DEPLOY_SSH_USER@$_host" "$DEPLOY_SSH_REMOTE_SHELL" "$_cmd"
fi
_err_code="$?"
if [ "$_err_code" != "0" ]; then

View file

@ -72,7 +72,7 @@ synology_dsm_deploy() {
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
if ! _exists synouser || ! _exists synogroup || ! _exists synosetkeyvalue; then
_err "Missing required tools to creat temp admin user, please set SYNO_USERNAME and SYNO_PASSWORD instead."
_err "Missing required tools to create temp admin user, please set SYNO_USERNAME and SYNO_PASSWORD instead."
_err "Notice: temp admin user authorization method only supports local deployment on DSM."
return 1
fi
@ -234,7 +234,7 @@ synology_dsm_deploy() {
fi
fi
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -Eo '[0-9]+')
_debug2 error_code "$error_code"
# Account has 2FA-OTP enabled, since error 403 reported.
# https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf
@ -269,7 +269,7 @@ synology_dsm_deploy() {
_secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
fi
fi
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -Eo '[0-9]+')
_debug2 error_code "$error_code"
fi
@ -336,7 +336,7 @@ synology_dsm_deploy() {
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
_debug2 id "$id"
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -Eo '[0-9]+')
_debug2 error_code "$error_code"
if [ -n "$error_code" ]; then
if [ "$error_code" -eq 105 ]; then
@ -424,11 +424,6 @@ _temp_admin_cleanup() {
fi
}
#_cleardeployconf key
_cleardeployconf() {
_cleardomainconf "SAVED_$1"
}
# key
_check2cleardeployconfexp() {
_key="$1"

View file

@ -135,7 +135,7 @@ _dns_cloudns_init_check() {
_dns_cloudns_http_api_call "dns/login.json" ""
if ! _contains "$response" "\"status\":\"Success\""; then
_err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials."
_err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Server response: $response"
return 1
fi

View file

@ -38,7 +38,7 @@ dns_cpanel_add() {
fi
# adding entry
_info "Adding the entry"
stripped_fulldomain=$(echo "$fulldomain" | sed "s/.$_domain//")
stripped_fulldomain="${fulldomain%."$_domain"}"
_debug "Adding $stripped_fulldomain to $_domain zone"
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&domain=$_domain&name=$stripped_fulldomain&type=TXT&txtdata=$txtvalue&ttl=1"
if _successful_update; then return 0; fi
@ -128,13 +128,27 @@ _get_root() {
_err "Primary domain list not found!"
return 1
fi
for _domain in $_domains; do
_debug "Checking if $fulldomain ends with $_domain"
if (_endswith "$fulldomain" "$_domain"); then
_debug "Root domain: $_domain"
return 0
fi
# Pick the LONGEST matching zone, dot-anchored: with both domain.tld and
# sub.domain.tld zones on the account, cPanel stores the record in the
# most specific zone, so add and rm must both resolve to that one.
_domain=""
for d in $_domains; do
_debug "Checking if $fulldomain ends with $d"
# case with quoted patterns gives an exact literal suffix match;
# _endswith treats the needle as a regex, so its dots would let
# xdomain.tld wrongly match zone domain.tld
case "$fulldomain" in
"$d" | *".$d")
if [ "${#d}" -gt "${#_domain}" ]; then
_domain="$d"
fi
;;
esac
done
if [ -n "$_domain" ]; then
_debug "Root domain: $_domain"
return 0
fi
return 1
}

181
dnsapi/dns_creoline.sh Normal file
View file

@ -0,0 +1,181 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_creoline_info='creoline
Site: https://www.creoline.com/de
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_creoline
Help: https://help.creoline.com
Options:
creolineApiToken
creolineApiSecret
Issues: github.com/acmesh-official/acme.sh/issues/7103
'
creolineApi="https://api.creoline.com/v1"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPB8"
dns_creoline_add() {
fulldomain=$1
txtvalue=$2
creolineApiToken="${creolineApiToken:-$(_readaccountconf_mutable creolineApiToken)}"
creolineApiSecret="${creolineApiSecret:-$(_readaccountconf_mutable creolineApiSecret)}"
if [ -z "$creolineApiToken" ] || [ -z "$creolineApiSecret" ]; then
_err "Error required creoline API Token or creoline API Secret not specified."
_err "Please set it with the Command 'export creolineApiToken=<YourToken>' and 'export creolineApiSecret=<YourSecret>'."
return 1
else
_saveaccountconf_mutable creolineApiToken "$creolineApiToken"
_saveaccountconf_mutable creolineApiSecret "$creolineApiSecret"
fi
_debug "Detecting the root dns zone."
if ! _get_root "$fulldomain"; then
_err "Error on detecting the root dns zone."
return 1
fi
_info "Adding record"
if _creoline_rest POST "dns/zone/$_domain/record" "{\"type\":\"TXT\",\"host\":\"$_sub_domain\",\"record\":\"$txtvalue\",\"ttl\":\"60\"}"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_creoline_rm() {
fulldomain=$1
txtvalue=$2
creolineApiToken="${creolineApiToken:-$(_readaccountconf_mutable creolineApiToken)}"
creolineApiSecret="${creolineApiSecret:-$(_readaccountconf_mutable creolineApiSecret)}"
_debug "Detecting the root dns zone."
if ! _get_root "$fulldomain"; then
_err "Error on detecting the root dns zone."
return 1
fi
_info "Getting earlier created txt record."
if ! _creoline_rest GET "dns/zone/$_domain/record/type/TXT/record/$txtvalue"; then
if _contains "$response" "errors" || _contains "$response" "message"; then
_err "Error on getting earlier created txt record."
return 1
fi
_err "Error on getting earlier created txt record."
return 1
fi
record_id=$(echo "$response" | _egrep_o "\"id\"[[:space:]]*:[[:space:]]*[0-9]+" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Error on deleting earlier created txt record. No record id found in response."
return 1
fi
_info "Deleting earlier created txt record."
if ! _creoline_rest DELETE "dns/zone/$_domain/record/$record_id"; then
if _contains "$response" "errors" || _contains "$response" "message"; then
_err "Error on deleting earlier created txt record."
return 1
fi
_err "Error on deleting earlier created txt record."
return 1
fi
_info "Deleted, OK"
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
if ! _creoline_rest GET "dns/zone/root/$domain"; then
return 1
fi
_sub_domain=$(echo "$response" | _egrep_o "\"subDomain\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug _sub_domain "$_sub_domain"
_domain=$(echo "$response" | _egrep_o "\"domain\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug _domain "$_domain"
if [ -z "$_domain" ] || [ -z "$_sub_domain" ]; then
return 1
fi
}
_creoline_rest() {
method=$1
uri="$2"
data="$3"
timestamp=$(_time)
canonical_request="${timestamp}.${creolineApi}/${uri}"
signature_hash=$(printf "%s" "$canonical_request" | _hmac sha256 "$(printf "%s" "$creolineApiSecret" | _hex_dump | tr -d " ")" hex)
_debug method "$method"
_debug uri "$uri"
_debug data "$data"
_debug2 timestamp "$timestamp"
_debug2 canonical_request "$canonical_request"
_debug2 signature_hash "$signature_hash"
token_trimmed=$(echo "$creolineApiToken" | tr -d '"')
hmac_trimmed=$(echo "$signature_hash" | tr -d '"')
export _H1="Content-Type: application/json"
if [ "$token_trimmed" ]; then
export _H2="X-Api-Token: $token_trimmed"
fi
if [ "$hmac_trimmed" ]; then
export _H3="X-Creoline-Api-Signature: $hmac_trimmed"
fi
if [ "$timestamp" ]; then
export _H4="X-Creoline-Api-Timestamp: $timestamp"
fi
if [ "$method" != "GET" ]; then
response="$(_post "$data" "$creolineApi/$uri" "" "$method")"
else
response="$(_get "$creolineApi/$uri")"
fi
if [ "$?" != "0" ]; then
_err "error $uri"
return 1
fi
_debug response "$response"
if _contains "$response" "errors"; then
error=$(echo "$response" | _egrep_o "\"errors\":[[]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | tr -d "[")
_err "Error: $error"
_err "URI:$uri"
return 1
elif _contains "$response" "message"; then
message=$(echo "$response" | _egrep_o "\"message\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \")
_err "Error: $message"
_err "URI:$uri"
return 1
fi
return 0
}

View file

@ -305,7 +305,7 @@ _freedns_domain_id() {
fi
domain_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's/<tr>/@<tr>/g' | tr '@' '\n' |
grep "<td>$search_domain</td>\|<td>$search_domain(.*)</td>" |
grep -E "<td>$search_domain</td>|<td>$search_domain\(.*\)</td>" |
sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' |
cut -d = -f 2)"
# The above beauty extracts domain ID from the html page...

196
dnsapi/dns_hostinger.sh Executable file
View file

@ -0,0 +1,196 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_hostinger_info='Hostinger
Site: Hostinger.com
Domains: hostinger.nl
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_hostinger
Options:
HOSTINGER_Token API Key
Issues: https://github.com/acmesh-official/acme.sh/issues/6831
Author: Sasha Reid <github@sasha.hackl.es>
'
HOSTINGER_Api="https://developers.hostinger.com/api/dns/v1/zones"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_hostinger_add() {
fulldomain=$1
txtvalue=$2
HOSTINGER_Token="${HOSTINGER_Token:-$(_readaccountconf_mutable HOSTINGER_Token)}"
if [ -z "$HOSTINGER_Token" ]; then
HOSTINGER_Token=""
_err "You didn't specify a Hostinger API Key yet."
_err "Please read the documentation for the Hostinger API authentication at https://developers.hostinger.com/#description/authentication"
return 1
fi
_saveaccountconf_mutable HOSTINGER_Token "$HOSTINGER_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"
_hostinger_rest GET "${_domain}"
if [ -z "$response" ]; then
_err "Error"
return 1
fi
# 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 _hostinger_rest PUT "$_domain" "{\"zone\":[{\"name\": \"$_sub_domain\",\"records\": [{\"content\":\"$txtvalue\"}],\"type\":\"TXT\",\"ttl\":\"120\"}],\"overwrite\":false}"; then
if _contains "$response" "Request accepted"; then
_info "Added, OK"
return 0
elif _contains "$response" "DNS resource record is not valid or conflicts with another resource record" ||
_contains "$response" 'DNS:4008'; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_hostinger_rm() {
fulldomain=$1
txtvalue=$2
HOSTINGER_Token="${HOSTINGER_Token:-$(_readaccountconf_mutable HOSTINGER_Token)}"
if [ -z "$HOSTINGER_Token" ]; then
HOSTINGER_Token=""
_err "You didn't specify a Hostinger API Key yet."
_err "Please read the documentation for the Hostinger API authentication at https://developers.hostinger.com/#description/authentication"
return 1
fi
_saveaccountconf_mutable HOSTINGER_Token "$HOSTINGER_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"
_hostinger_rest GET "${_domain}"
if [ -z "$response" ]; then
_err "Error"
return 1
fi
if _contains "$response" "\"name\":\"$_sub_domain\""; then
# Match the record, and make certain it is a TXT record for the domain not another type. Then remove our target record from the list
remaining_records=$(echo "$response" | _normalizeJson | _egrep_o '{"name":"'"$_sub_domain"'","records":\[[^]]+\],"ttl":[0-9]+,"type":"TXT"\}' | _egrep_o "\[.*\]" | sed -E 's#\{"content":"\\"'"$txtvalue"'\\"","is_disabled":false\},?##g')
if [ "$remaining_records" != "[]" ]; then
remaining_json=$(echo "$remaining_records" | _egrep_o '"content":"\\"[^}]+\\""' | sed -E 's/^(.*)$/{\1},/g' | tr -d '\n' | sed 's/,$//')
# We need to set the remaining records back to Hostinger, as we can't partially delete
_info "Removing $txtvalue from $_sub_domain by setting records to ${remaining_json}"
if _hostinger_rest PUT "$_domain" "{\"zone\":[{\"name\": \"$_sub_domain\",\"records\": [${remaining_json}],\"type\":\"TXT\",\"ttl\":\"120\"}],\"overwrite\":true}"; then
if _contains "$response" "Request accepted"; then
_info "Updated remaining records, OK"
return 0
elif _contains "$response" "DNS resource record is not valid or conflicts with another resource record" ||
_contains "$response" 'DNS:4008'; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
# Otherwise delete the TXT record that matches the subdomain
else
if ! _hostinger_rest DELETE "$_domain" "{\"filters\":[{\"name\":\"$_sub_domain\",\"type\":\"TXT\"}]}"; then
_err "Delete record error."
return 1
fi
fi
echo "$response" | grep "Request accepted" >/dev/null
else
_info "Don't need to remove."
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
_hostinger_rest GET "$h"
if _contains "$response" "records"; then
if [ "$response" = "[]" ]; then
_debug "Valid subdomains are not the root"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain=$h
return 0
fi
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_hostinger_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
token_trimmed=$(echo "$HOSTINGER_Token" | tr -d '"')
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $token_trimmed"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$HOSTINGER_Api/$ep" "" "$m")"
else
response="$(_get "$HOSTINGER_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View file

@ -136,7 +136,7 @@ _ISPC_getZoneInfo() {
curResult="$(_post "${curData}" "${ISPC_Api}?client_get_id")"
_debug "Calling _ISPC_ClientGetID: '${curData}' '${ISPC_Api}?client_get_id'"
_debug "Result of _ISPC_ClientGetID: '$curResult'"
client_id=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2 | tr -d '{}')
client_id=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d '[' -f 1 | tr -d '{}')
_debug "Client ID: '${client_id}'"
case "${client_id}" in
'' | *[!0-9]*)

View file

@ -140,7 +140,7 @@ _me_rest() {
data="$3"
_debug "$ep"
cdate=$(LANG=C date -u +"%a, %d %b %Y %T %Z")
cdate=$(LC_ALL=C date -u +"%a, %d %b %Y %T %Z")
hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex)
export _H1="x-dnsme-apiKey: $ME_Key"

View file

@ -15,7 +15,7 @@ Namecom_API="https://api.name.com/v4"
#Usage: dns_namecom_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_namecom_add() {
fulldomain=$1
fulldomain=$(_idn "$1")
txtvalue=$2
Namecom_Username="${Namecom_Username:-$(_readaccountconf_mutable Namecom_Username)}"
@ -68,7 +68,7 @@ dns_namecom_add() {
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_namecom_rm() {
fulldomain=$1
fulldomain=$(_idn "$1")
txtvalue=$2
Namecom_Username="${Namecom_Username:-$(_readaccountconf_mutable Namecom_Username)}"

View file

@ -98,7 +98,7 @@ dns_njalla_rm() {
echo "$records" | while read -r record; do
record_name=$(echo "$record" | _egrep_o "\"name\":\s?\"[^\"]*\"" | cut -d : -f 2 | tr -d " " | tr -d \")
record_content=$(echo "$record" | _egrep_o "\"content\":\s?\"[^\"]*\"" | cut -d : -f 2 | tr -d " " | tr -d \")
record_id=$(echo "$record" | _egrep_o "\"id\":\s?[0-9]+" | cut -d : -f 2 | tr -d " " | tr -d \")
record_id=$(echo "$record" | _egrep_o "\"id\":\s?\"?[^\",}]*" | cut -d : -f 2 | tr -d " " | tr -d \")
if [ "$_sub_domain" = "$record_name" ]; then
if [ "$txtvalue" = "$record_content" ]; then
_debug "record_id" "$record_id"

View file

@ -50,6 +50,9 @@ dns_pdns_add() {
PDNS_Ttl="$DEFAULT_PDNS_TTL"
fi
# Ensure PDNS_Url has no trailing slash ('/')
PDNS_Url="${PDNS_Url%/}"
#save the api addr and key to the account conf file.
_saveaccountconf_mutable PDNS_Url "$PDNS_Url"
_saveaccountconf_mutable PDNS_ServerId "$PDNS_ServerId"

View file

@ -151,8 +151,8 @@ dns_pleskxml_rm() {
# Extracting the id of the TXT record for the full domain (NOT case-sensitive) and corresponding value
recid="$(
_value "$reclist" |
grep -i "<host>${fulldomain}.</host>" |
grep "<value>${txtvalue}</value>" |
grep -Fi "<host>${fulldomain}.</host>" |
grep -F "<value>${txtvalue}</value>" |
sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/'
)"
@ -419,7 +419,7 @@ _pleskxml_get_root_domain() {
_debug "Checking if '$root_domain_name' is managed by the Plesk server..."
root_domain_id="$(_value "$output" | grep "<name>$root_domain_name</name>" | _head_n 1 | sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/')"
root_domain_id="$(_value "$output" | grep -F "<name>$root_domain_name</name>" | _head_n 1 | sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/')"
if [ -n "$root_domain_id" ]; then
# Found a match

View file

@ -7,6 +7,7 @@ Options:
SELFHOSTDNS_USERNAME Username
SELFHOSTDNS_PASSWORD Password
SELFHOSTDNS_MAP Subdomain name
SELFHOSTDNS_UPDATE_URL API url. Optional. Default "https://account.selfhost.de/cgi-bin/api.pl"
Issues: github.com/acmesh-official/acme.sh/issues/4291
Author: Marvin Edeler
'
@ -18,9 +19,11 @@ dns_selfhost_add() {
_debug fulldomain "$fulldomain"
_debug txtvalue "$txt"
SELFHOSTDNS_UPDATE_URL="https://account.selfhost.de/cgi-bin/api.pl"
DEFAULT_SELFHOSTDNS_UPDATE_URL="https://account.selfhost.de/cgi-bin/api.pl"
# Get values, but don't save until we successfully validated
SELFHOSTDNS_UPDATE_URL="${SELFHOSTDNS_UPDATE_URL:-$(_readaccountconf_mutable SELFHOSTDNS_UPDATE_URL)}"
SELFHOSTDNS_UPDATE_URL="${SELFHOSTDNS_UPDATE_URL:-$DEFAULT_SELFHOSTDNS_UPDATE_URL}"
SELFHOSTDNS_USERNAME="${SELFHOSTDNS_USERNAME:-$(_readaccountconf_mutable SELFHOSTDNS_USERNAME)}"
SELFHOSTDNS_PASSWORD="${SELFHOSTDNS_PASSWORD:-$(_readaccountconf_mutable SELFHOSTDNS_PASSWORD)}"
# These values are domain dependent, so read them from there
@ -84,6 +87,11 @@ dns_selfhost_add() {
fi
fi
# Save api url if different from default
if [ "$DEFAULT_SELFHOSTDNS_UPDATE_URL" != "$SELFHOSTDNS_UPDATE_URL" ]; then
_saveaccountconf_mutable SELFHOSTDNS_UPDATE_URL "$SELFHOSTDNS_UPDATE_URL"
fi
# Now that we know the values are good, save them
_saveaccountconf_mutable SELFHOSTDNS_USERNAME "$SELFHOSTDNS_USERNAME"
_saveaccountconf_mutable SELFHOSTDNS_PASSWORD "$SELFHOSTDNS_PASSWORD"

View file

@ -200,6 +200,7 @@ _smtp_send_curl() {
set -- "$@" \
--upload-file - \
--crlf \
--mail-from "$SMTP_FROM" \
--max-time "$SMTP_TIMEOUT"