mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-10-21 13:31:18 +00:00
fix pr comment issues
This commit is contained in:
1 file changed
+134
-53
+134
-53
@@ -7,14 +7,14 @@ Options:
|
|||||||
BERGDNS_API_KEY API key, as issued in the account UI. Needs read (to find the zone and the record) and write over the challenge names.
|
BERGDNS_API_KEY API key, as issued in the account UI. Needs read (to find the zone and the record) and write over the challenge names.
|
||||||
BERGDNS_API_URL API base URL. Optional. Default "https://bergdns.at/v1".
|
BERGDNS_API_URL API base URL. Optional. Default "https://bergdns.at/v1".
|
||||||
BERGDNS_TTL TTL of the challenge record, in seconds. Optional. Default "60".
|
BERGDNS_TTL TTL of the challenge record, in seconds. Optional. Default "60".
|
||||||
BERGDNS_PROPAGATION_TIMEOUT Seconds to wait for the record to reach every secondary. Optional. Default "120". "0" does not wait.
|
BERGDNS_PROPAGATION_TIMEOUT Seconds to wait for the record to reach every secondary. Optional. Default "60". "0" does not wait.
|
||||||
Issues: github.com/acmesh-official/acme.sh/issues/7261
|
Issues: github.com/acmesh-official/acme.sh/issues/7261
|
||||||
Author: Kenny Kropp <https://github.com/kekropp>
|
Author: Kenny Kropp <https://github.com/kekropp>
|
||||||
'
|
'
|
||||||
|
|
||||||
_BERGDNS_DEFAULT_URL='https://bergdns.at/v1'
|
_BERGDNS_DEFAULT_URL='https://bergdns.at/v1'
|
||||||
_BERGDNS_DEFAULT_TTL='60'
|
_BERGDNS_DEFAULT_TTL='60'
|
||||||
_BERGDNS_DEFAULT_WAIT='120'
|
_BERGDNS_DEFAULT_WAIT='60'
|
||||||
|
|
||||||
######## Public functions ####################################################
|
######## Public functions ####################################################
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ dns_bergdns_add() {
|
|||||||
if _bergdns_rest POST "zones/$_bergdns_zone_id/rrsets" \
|
if _bergdns_rest POST "zones/$_bergdns_zone_id/rrsets" \
|
||||||
"{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":$BERGDNS_TTL,\"records\":[\"\\\"$txtvalue\\\"\"]}"; then
|
"{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":$BERGDNS_TTL,\"records\":[\"\\\"$txtvalue\\\"\"]}"; then
|
||||||
_bergdns_rrset_id=$(echo "$response" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
_bergdns_rrset_id=$(echo "$response" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||||
|
_debug _bergdns_rrset_id "$_bergdns_rrset_id"
|
||||||
_bergdns_wait "$fulldomain"
|
_bergdns_wait "$fulldomain"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -79,7 +80,11 @@ dns_bergdns_rm() {
|
|||||||
if ! _bergdns_rest DELETE \
|
if ! _bergdns_rest DELETE \
|
||||||
"zones/$_bergdns_zone_id/rrsets/$_bergdns_rrset_id/records" \
|
"zones/$_bergdns_zone_id/rrsets/$_bergdns_rrset_id/records" \
|
||||||
"{\"records\":[\"\\\"$txtvalue\\\"\"]}"; then
|
"{\"records\":[\"\\\"$txtvalue\\\"\"]}"; then
|
||||||
if [ "$_bergdns_code" = "rrset_not_found" ]; then
|
# Any flavour of not-found is a cleanup that has already happened: the
|
||||||
|
# RRset was removed by a previous run, or by the other half of a
|
||||||
|
# domain-and-wildcard pair taking the last value with it.
|
||||||
|
if [ "$_bergdns_status" = "404" ]; then
|
||||||
|
_info "bergdns: $fulldomain holds no such record any more, nothing to remove"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
_err "bergdns: could not remove the challenge record: $_bergdns_error"
|
_err "bergdns: could not remove the challenge record: $_bergdns_error"
|
||||||
@@ -111,10 +116,30 @@ _bergdns_init() {
|
|||||||
# strip trailing slashes
|
# strip trailing slashes
|
||||||
BERGDNS_API_URL=$(echo "$BERGDNS_API_URL" | sed 's#/*$##')
|
BERGDNS_API_URL=$(echo "$BERGDNS_API_URL" | sed 's#/*$##')
|
||||||
|
|
||||||
|
# The TTL is interpolated into the request body and the timeout is counted
|
||||||
|
# down in arithmetic, so a stray value from the environment or from an old
|
||||||
|
# account.conf has to be caught here rather than become malformed JSON and
|
||||||
|
# an opaque 400.
|
||||||
|
case "$BERGDNS_TTL" in
|
||||||
|
*[!0-9]* | '')
|
||||||
|
_err "bergdns: BERGDNS_TTL must be a number of seconds, not \"$BERGDNS_TTL\"."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
case "$BERGDNS_PROPAGATION_TIMEOUT" in
|
||||||
|
*[!0-9]* | '')
|
||||||
|
_err "bergdns: BERGDNS_PROPAGATION_TIMEOUT must be a number of seconds, not \"$BERGDNS_PROPAGATION_TIMEOUT\"."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
_saveaccountconf_mutable BERGDNS_API_KEY "$BERGDNS_API_KEY"
|
_saveaccountconf_mutable BERGDNS_API_KEY "$BERGDNS_API_KEY"
|
||||||
_saveaccountconf_mutable BERGDNS_API_URL "$BERGDNS_API_URL"
|
# Only what the user actually chose is written back. Persisting a default
|
||||||
_saveaccountconf_mutable BERGDNS_TTL "$BERGDNS_TTL"
|
# would pin the install to today's value, and a later change to the shipped
|
||||||
_saveaccountconf_mutable BERGDNS_PROPAGATION_TIMEOUT "$BERGDNS_PROPAGATION_TIMEOUT"
|
# one -- a move of the API base above all -- would never reach it.
|
||||||
|
[ "$BERGDNS_API_URL" = "$_BERGDNS_DEFAULT_URL" ] || _saveaccountconf_mutable BERGDNS_API_URL "$BERGDNS_API_URL"
|
||||||
|
[ "$BERGDNS_TTL" = "$_BERGDNS_DEFAULT_TTL" ] || _saveaccountconf_mutable BERGDNS_TTL "$BERGDNS_TTL"
|
||||||
|
[ "$BERGDNS_PROPAGATION_TIMEOUT" = "$_BERGDNS_DEFAULT_WAIT" ] || _saveaccountconf_mutable BERGDNS_PROPAGATION_TIMEOUT "$BERGDNS_PROPAGATION_TIMEOUT"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +148,7 @@ _bergdns_init() {
|
|||||||
# Zones are addressed by an id, not by name, so the zone list is fetched once
|
# Zones are addressed by an id, not by name, so the zone list is fetched once
|
||||||
# and the longest matching zone name wins.
|
# and the longest matching zone name wins.
|
||||||
_bergdns_find_zone() {
|
_bergdns_find_zone() {
|
||||||
_fqdn=$1
|
_bergdns_fqdn=$1
|
||||||
_bergdns_zone_id=""
|
_bergdns_zone_id=""
|
||||||
_bergdns_zone_name=""
|
_bergdns_zone_name=""
|
||||||
|
|
||||||
@@ -133,26 +158,26 @@ _bergdns_find_zone() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# one zone object per line, so id and name stay together
|
# one zone object per line, so id and name stay together
|
||||||
_zone_lines=$(echo "$response" | tr '{' '\n' | grep '"name":"')
|
_bergdns_zone_lines=$(echo "$response" | tr '{' '\n')
|
||||||
|
|
||||||
_candidate="$_fqdn"
|
_bergdns_cand="$_bergdns_fqdn"
|
||||||
while [ -n "$_candidate" ]; do
|
while [ -n "$_bergdns_cand" ]; do
|
||||||
_line=$(echo "$_zone_lines" | grep -F "\"name\":\"$_candidate\"," | _head_n 1)
|
_bergdns_line=$(echo "$_bergdns_zone_lines" | _bergdns_select "$_bergdns_cand" | _head_n 1)
|
||||||
if [ -n "$_line" ]; then
|
if [ -n "$_bergdns_line" ]; then
|
||||||
_bergdns_zone_id=$(echo "$_line" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
_bergdns_zone_id=$(echo "$_bergdns_line" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||||
_bergdns_zone_name="$_candidate"
|
_bergdns_zone_name="$_bergdns_cand"
|
||||||
_debug _bergdns_zone_id "$_bergdns_zone_id"
|
_debug _bergdns_zone_id "$_bergdns_zone_id"
|
||||||
_debug _bergdns_zone_name "$_bergdns_zone_name"
|
_debug _bergdns_zone_name "$_bergdns_zone_name"
|
||||||
[ -n "$_bergdns_zone_id" ] && return 0
|
[ -n "$_bergdns_zone_id" ] && return 0
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
case "$_candidate" in
|
case "$_bergdns_cand" in
|
||||||
*.*) _candidate=${_candidate#*.} ;;
|
*.*) _bergdns_cand=${_bergdns_cand#*.} ;;
|
||||||
*) _candidate="" ;;
|
*) _bergdns_cand="" ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
_err "bergdns: no zone in this account holds $_fqdn."
|
_err "bergdns: no zone in this account holds $_bergdns_fqdn."
|
||||||
_err "bergdns: the key must be able to read the zone as well as write the record."
|
_err "bergdns: the key must be able to read the zone as well as write the record."
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -162,7 +187,7 @@ _bergdns_find_zone() {
|
|||||||
# empty string if there is none. Records are addressed by id, so the zone's
|
# empty string if there is none. Records are addressed by id, so the zone's
|
||||||
# RRsets are listed to find it.
|
# RRsets are listed to find it.
|
||||||
_bergdns_find_rrset() {
|
_bergdns_find_rrset() {
|
||||||
_fqdn=$1
|
_bergdns_fqdn=$1
|
||||||
_bergdns_rrset_id=""
|
_bergdns_rrset_id=""
|
||||||
|
|
||||||
if ! _bergdns_rest GET "zones/$_bergdns_zone_id/rrsets"; then
|
if ! _bergdns_rest GET "zones/$_bergdns_zone_id/rrsets"; then
|
||||||
@@ -170,29 +195,62 @@ _bergdns_find_rrset() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_line=$(echo "$response" | tr '{' '\n' | grep -F "\"name\":\"$_fqdn\",\"type\":\"TXT\"," | _head_n 1)
|
_bergdns_line=$(echo "$response" | tr '{' '\n' | _bergdns_select "$_bergdns_fqdn" TXT | _head_n 1)
|
||||||
if [ -n "$_line" ]; then
|
if [ -n "$_bergdns_line" ]; then
|
||||||
_bergdns_rrset_id=$(echo "$_line" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
_bergdns_rrset_id=$(echo "$_bergdns_line" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||||
fi
|
fi
|
||||||
_debug _bergdns_rrset_id "$_bergdns_rrset_id"
|
_debug _bergdns_rrset_id "$_bergdns_rrset_id"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Usage: ... | _bergdns_select name [type]
|
||||||
|
# Reads one JSON object per line and prints those whose "name" is name and,
|
||||||
|
# when a type is given, whose "type" is that type. The two fields are matched
|
||||||
|
# one at a time, so neither the order the server writes its keys in nor
|
||||||
|
# anything sitting between them changes the answer.
|
||||||
|
#
|
||||||
|
# The comparison is a shell case, which is literal by construction: grep -F
|
||||||
|
# does not exist on Solaris, and _contains and _startswith would read the name
|
||||||
|
# as a regular expression. Each pattern anchors on the start of the object or
|
||||||
|
# on the comma before the key, so a key that merely ends in "name" cannot
|
||||||
|
# match.
|
||||||
|
_bergdns_select() {
|
||||||
|
_bergdns_sel_name=$1
|
||||||
|
_bergdns_sel_type=$2
|
||||||
|
while IFS= read -r _bergdns_sel_line || [ -n "$_bergdns_sel_line" ]; do
|
||||||
|
case "$_bergdns_sel_line" in
|
||||||
|
'"name":"'"$_bergdns_sel_name"'"'* | *',"name":"'"$_bergdns_sel_name"'"'*) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
if [ -n "$_bergdns_sel_type" ]; then
|
||||||
|
case "$_bergdns_sel_line" in
|
||||||
|
'"type":"'"$_bergdns_sel_type"'"'* | *',"type":"'"$_bergdns_sel_type"'"'*) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$_bergdns_sel_line"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Usage: _bergdns_wait _acme-challenge.www.example.com
|
# Usage: _bergdns_wait _acme-challenge.www.example.com
|
||||||
# Polls the propagation endpoint until all bergdns nameservers serve the
|
# Polls the propagation endpoint until all bergdns nameservers serve the
|
||||||
# record. A timeout is logged but does not fail the issuance.
|
# record. A timeout is logged but does not fail the issuance.
|
||||||
|
#
|
||||||
|
# This covers the zone transfer from the primary to the secondaries, which
|
||||||
|
# takes seconds; the resolver side is acme.sh's own _check_dns_entries, which
|
||||||
|
# runs after every record has been added and has a timeout of its own.
|
||||||
_bergdns_wait() {
|
_bergdns_wait() {
|
||||||
_fqdn=$1
|
_bergdns_fqdn=$1
|
||||||
if [ "$BERGDNS_PROPAGATION_TIMEOUT" = "0" ] || [ -z "$_bergdns_rrset_id" ]; then
|
if [ "$BERGDNS_PROPAGATION_TIMEOUT" = "0" ] || [ -z "$_bergdns_rrset_id" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_waited=0
|
_bergdns_waited=0
|
||||||
while [ "$_waited" -lt "$BERGDNS_PROPAGATION_TIMEOUT" ]; do
|
while [ "$_bergdns_waited" -lt "$BERGDNS_PROPAGATION_TIMEOUT" ]; do
|
||||||
if _bergdns_rest GET "zones/$_bergdns_zone_id/rrsets/$_bergdns_rrset_id/propagation"; then
|
if _bergdns_rest GET "zones/$_bergdns_zone_id/rrsets/$_bergdns_rrset_id/propagation"; then
|
||||||
case "$response" in
|
case "$response" in
|
||||||
*'"propagated":true'*)
|
*'"propagated":true'*)
|
||||||
_info "bergdns: $_fqdn is served by every secondary after ${_waited}s"
|
_info "bergdns: $_bergdns_fqdn is served by every secondary after ${_bergdns_waited}s"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -200,54 +258,77 @@ _bergdns_wait() {
|
|||||||
# propagation checks are not configured on this server
|
# propagation checks are not configured on this server
|
||||||
_info "bergdns: this deployment does not offer propagation checks; not waiting"
|
_info "bergdns: this deployment does not offer propagation checks; not waiting"
|
||||||
return 0
|
return 0
|
||||||
|
else
|
||||||
|
case "$_bergdns_status" in
|
||||||
|
429) ;; # rate limited, worth another go
|
||||||
|
4*)
|
||||||
|
# The check is refused rather than pending, and waiting will not
|
||||||
|
# change that. _check_dns_entries still has to pass, so this is not
|
||||||
|
# the place to fail the issuance.
|
||||||
|
_info "bergdns: the propagation check is unavailable ($_bergdns_error); not waiting"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
_sleep 5
|
_sleep 5
|
||||||
_waited=$((_waited + 5))
|
_bergdns_waited=$((_bergdns_waited + 5))
|
||||||
done
|
done
|
||||||
|
|
||||||
_info "bergdns: $_fqdn was not on every secondary after ${BERGDNS_PROPAGATION_TIMEOUT}s; continuing anyway"
|
_info "bergdns: $_bergdns_fqdn was not on every secondary after ${BERGDNS_PROPAGATION_TIMEOUT}s; continuing anyway"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Usage: _bergdns_rest method endpoint [body]
|
# Usage: _bergdns_rest method endpoint [body]
|
||||||
# Sets response. On failure also sets _bergdns_error and _bergdns_code.
|
# Sets response and _bergdns_status. On failure also sets _bergdns_error and,
|
||||||
|
# where the API itself answered, _bergdns_code.
|
||||||
_bergdns_rest() {
|
_bergdns_rest() {
|
||||||
_method=$1
|
_bergdns_method=$1
|
||||||
_endpoint=$2
|
_bergdns_endpoint=$2
|
||||||
_body=$3
|
_bergdns_body=$3
|
||||||
_bergdns_error=""
|
_bergdns_error=""
|
||||||
_bergdns_code=""
|
_bergdns_code=""
|
||||||
|
_bergdns_status=""
|
||||||
|
|
||||||
export _H1="Authorization: Bearer $BERGDNS_API_KEY"
|
export _H1="Authorization: Bearer $BERGDNS_API_KEY"
|
||||||
export _H2="Accept: application/json"
|
export _H2="Accept: application/json"
|
||||||
|
|
||||||
_url="$BERGDNS_API_URL/$_endpoint"
|
_bergdns_url="$BERGDNS_API_URL/$_bergdns_endpoint"
|
||||||
_debug _url "$_url"
|
_debug _bergdns_url "$_bergdns_url"
|
||||||
|
|
||||||
if [ "$_method" = "GET" ]; then
|
# drop the headers of the previous request, so that a request which never
|
||||||
response="$(_get "$_url")"
|
# reaches the server cannot be read as carrying its status
|
||||||
else
|
if [ -f "$HTTP_HEADER" ]; then
|
||||||
_debug2 _body "$_body"
|
: >"$HTTP_HEADER"
|
||||||
response="$(_post "$_body" "$_url" "" "$_method" "application/json")"
|
|
||||||
fi
|
fi
|
||||||
_ret="$?"
|
|
||||||
|
if [ "$_bergdns_method" = "GET" ]; then
|
||||||
|
response="$(_get "$_bergdns_url")"
|
||||||
|
else
|
||||||
|
_debug2 _bergdns_body "$_bergdns_body"
|
||||||
|
response="$(_post "$_bergdns_body" "$_bergdns_url" "" "$_bergdns_method" "application/json")"
|
||||||
|
fi
|
||||||
|
_bergdns_ret="$?"
|
||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
|
|
||||||
if [ "$_ret" != "0" ]; then
|
if [ "$_bergdns_ret" != "0" ]; then
|
||||||
_bergdns_error="the request to $_url could not be made"
|
_bergdns_error="the request to $_bergdns_url could not be made"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Errors are returned as RFC 9457 problem+json, which always has "status" and
|
_bergdns_status="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
|
||||||
# "code". Successful responses have neither, so the body is checked instead
|
_debug _bergdns_status "$_bergdns_status"
|
||||||
# of the HTTP status code.
|
|
||||||
case "$response" in
|
# The HTTP status decides. Errors from the API itself are RFC 9457
|
||||||
*'"status":'*'"code":"'* | *'"code":"'*'"status":'*)
|
# problem+json and carry a "detail" to show and a stable "code" to branch on,
|
||||||
_bergdns_error=$(echo "$response" | sed -n 's/.*"detail":"\([^"]*\)".*/\1/p')
|
# but a request that never gets that far -- bergdns.at answers from behind a
|
||||||
[ -n "$_bergdns_error" ] || _bergdns_error="$response"
|
# reverse proxy, whose 502 and 504 are HTML -- has neither, and reading the
|
||||||
_bergdns_code=$(echo "$response" | sed -n 's/.*"code":"\([^"]*\)".*/\1/p')
|
# body alone would take those for success.
|
||||||
return 1
|
case "$_bergdns_status" in
|
||||||
;;
|
2*) return 0 ;;
|
||||||
esac
|
esac
|
||||||
return 0
|
_bergdns_error=$(echo "$response" | _egrep_o '"detail":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||||
|
_bergdns_code=$(echo "$response" | _egrep_o '"code":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||||
|
[ -n "$_bergdns_error" ] || _bergdns_error="$_bergdns_url answered HTTP ${_bergdns_status:-(none)}"
|
||||||
|
_debug _bergdns_code "$_bergdns_code"
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||