mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-09-05 20:24:54 +08:00
sync (#7234)
* Merge pull request #7220 from moezx/dev Add AK & SK based Huawei Cloud DNS API * fix: dynv6 record parsing (#7197) * unifios: extract JSON-split helper, document RSA/ECC name collision (#7200) * Extract _uos_split_json helper, document RSA/ECC name-prefix collision Per neilpang's non-blocking review notes on #7184: the _normalizeJson + split-into-lines block was duplicated at both call sites, now shared via _uos_split_json(). Also documents (without changing behavior, since it's harmless today) that an RSA and ECC deploy of the same domain share the generated name's prefix, each removing the other's entry on cleanup -- citing haproxy.sh/lighttpd.sh's existing .rsa/.ecdsa suffix pattern as the fix if this ever needs addressing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Replace grep -F with a portable matcher, fix RSA/ECC name collision grep -F isn't on Solaris, and dropping it naively breaks matching: wildcard domains and dots collide as regex. _uos_grep_literal replaces both call sites with a case-based literal match instead. _uos_name now includes the key type, so RSA and ECC deploys of the same domain no longer share a cleanup scope. Per neilpang's review on #7200. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix echo's \n handling in _uos_grep_literal, drop unneeded Le_Keylength guard echo does not behave consistently across different environments. dash interprets literal \n in a line, splitting it. printf '%s\n' does not and matches _uos_split_json's existing pattern. printf behaves more consistently across environments and is generally preferred over echo. Le_Keylength guard was a no-op and didn't help under set -u either; _isEccKey already handles empty. Kept the shellcheck warning suppressed inline instead of assigning to a core Le_* var. Per neilpang's review on #7200. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * add trigger * minor --------- Co-authored-by: Mashiro <adadam@qq.com> Co-authored-by: MBWhitestone <25477219+MBWhitestone@users.noreply.github.com> Co-authored-by: Pablo <Pablo1@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
50
.github/workflows/dockerhub.yml
vendored
50
.github/workflows/dockerhub.yml
vendored
@@ -10,9 +10,19 @@ on:
|
||||
- '**.sh'
|
||||
- "Dockerfile"
|
||||
- '.github/workflows/dockerhub.yml'
|
||||
# Rebuild the latest release tag weekly so a pinned version tag picks up
|
||||
# Alpine package security updates (see issue 7209).
|
||||
schedule:
|
||||
- cron: '17 3 * * 1'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag to rebuild (empty = latest release)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
@@ -45,9 +55,27 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: resolve the release tag to rebuild
|
||||
id: rebuild
|
||||
if: github.event_name != 'push'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
tag="$INPUT_TAG"
|
||||
if [ -z "$tag" ]; then
|
||||
tag="$(gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq .tag_name)"
|
||||
fi
|
||||
if [ -z "$tag" ]; then
|
||||
echo "::error::cannot resolve the release tag to rebuild"
|
||||
exit 1
|
||||
fi
|
||||
echo "rebuilding release tag ${tag}"
|
||||
echo "tag=${tag}" >>"$GITHUB_OUTPUT"
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ steps.rebuild.outputs.tag }}
|
||||
persist-credentials: false
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
@@ -65,12 +93,15 @@ jobs:
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||
- name: build and push the image
|
||||
env:
|
||||
REBUILD_TAG: ${{ steps.rebuild.outputs.tag }}
|
||||
run: |
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
if [ -n "$REBUILD_TAG" ]; then
|
||||
# scheduled/manual rebuild of an existing release tag
|
||||
DOCKER_IMAGE_TAG=${REBUILD_TAG}
|
||||
elif [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
|
||||
fi
|
||||
|
||||
if [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/heads/}
|
||||
|
||||
if [[ $DOCKER_IMAGE_TAG == master ]]; then
|
||||
@@ -86,6 +117,13 @@ jobs:
|
||||
DOCKER_LABELS+=(--label "${label}")
|
||||
done <<<"${DOCKER_METADATA_OUTPUT_LABELS}"
|
||||
|
||||
if [ -n "$REBUILD_TAG" ]; then
|
||||
# the metadata action derived version/revision from the default
|
||||
# branch; a later --label wins, so point them at the rebuilt tag
|
||||
DOCKER_LABELS+=(--label "org.opencontainers.image.version=${REBUILD_TAG}")
|
||||
DOCKER_LABELS+=(--label "org.opencontainers.image.revision=$(git rev-parse HEAD)")
|
||||
fi
|
||||
|
||||
docker buildx build \
|
||||
--tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
|
||||
"${DOCKER_LABELS[@]}" \
|
||||
|
||||
@@ -173,10 +173,7 @@ haproxy_deploy() {
|
||||
# Set the suffix depending if we are creating a bundle or not
|
||||
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
|
||||
_info "Bundle creation requested"
|
||||
# Initialise $Le_Keylength if its not already set
|
||||
if [ -z "${Le_Keylength}" ]; then
|
||||
Le_Keylength=""
|
||||
fi
|
||||
# shellcheck disable=SC2154 # Le_Keylength is set by acme.sh core, not this hook
|
||||
if _isEccKey "${Le_Keylength}"; then
|
||||
_info "ECC key type detected"
|
||||
_suffix=".ecdsa"
|
||||
|
||||
@@ -121,10 +121,7 @@ lighttpd_deploy() {
|
||||
# Set the suffix depending if we are creating a bundle or not
|
||||
if [ "${Le_Deploy_lighttpd_bundle}" = "yes" ]; then
|
||||
_info "Bundle creation requested"
|
||||
# Initialise $Le_Keylength if its not already set
|
||||
if [ -z "${Le_Keylength}" ]; then
|
||||
Le_Keylength=""
|
||||
fi
|
||||
# shellcheck disable=SC2154 # Le_Keylength is set by acme.sh core, not this hook
|
||||
if _isEccKey "${Le_Keylength}"; then
|
||||
_info "ECC key type detected"
|
||||
_suffix=".ecdsa"
|
||||
|
||||
@@ -95,6 +95,36 @@ _uos_response_cookie() {
|
||||
grep <"$HTTP_HEADER" -i "^Set-Cookie: *$1=" | _tail_n 1 | _egrep_o "$1=[^;]*" | _head_n 1
|
||||
}
|
||||
|
||||
_uos_split_json() {
|
||||
# $1 = raw JSON list response
|
||||
#
|
||||
# _normalizeJson collapses the response to one line. This removes extra
|
||||
# space around colons. It also removes any CR or LF characters that the
|
||||
# server can add. However, _normalizeJson also removes the newline
|
||||
# character at the end of the line. If the line has no ending newline
|
||||
# character, some sed programs drop the last line of input. This code
|
||||
# adds the newline back before the split below, to prevent that problem.
|
||||
_uos_normalized="$(echo "$1" | _normalizeJson)"
|
||||
# A literal newline character splits the JSON into one object per line.
|
||||
# Grep can then match a single certificate entry at a time. This is not
|
||||
# the two-character "\n" sequence: GNU sed reads "\n" in the replacement
|
||||
# text as a newline character. POSIX does not define this behavior, and
|
||||
# BSD sed prints "\n" as two literal characters, not as a newline.
|
||||
printf '%s\n' "$_uos_normalized" | sed 's/},{/},\
|
||||
{/g'
|
||||
}
|
||||
|
||||
_uos_grep_literal() {
|
||||
# $1 = literal text to find, matched without a regex -- portable to
|
||||
# grep implementations with no -F flag (e.g. Solaris), and avoids "*"
|
||||
# or "." in a domain name being read as a regex metacharacter.
|
||||
while IFS= read -r _uos_line || [ -n "$_uos_line" ]; do
|
||||
case "$_uos_line" in
|
||||
*"$1"*) printf '%s\n' "$_uos_line" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
unifios_deploy() {
|
||||
_cdomain="$1"
|
||||
_ckey="$2"
|
||||
@@ -189,7 +219,20 @@ unifios_deploy() {
|
||||
# wrap (confirmed against the real UI: a long name overlaps the Expires
|
||||
# column and makes both unreadable), so keep the suffix short instead --
|
||||
# Unix epoch seconds are still unique enough for this purpose.
|
||||
_uos_name="$_cdomain $(_time)"
|
||||
#
|
||||
# This name includes the key type (rsa or ecdsa), to keep an RSA
|
||||
# deploy and an ECC deploy of the same domain from sharing this
|
||||
# prefix. Without the key type, the cleanup step for each deploy
|
||||
# removes the entry that the other deploy creates. `deploy/haproxy.sh`
|
||||
# and `deploy/lighttpd.sh` use the same `_isEccKey` check, for the same
|
||||
# reason.
|
||||
# shellcheck disable=SC2154 # Le_Keylength is set by acme.sh core, not this hook
|
||||
if _isEccKey "${Le_Keylength}"; then
|
||||
_uos_keytype="ecdsa"
|
||||
else
|
||||
_uos_keytype="rsa"
|
||||
fi
|
||||
_uos_name="$_cdomain $_uos_keytype $(_time)"
|
||||
_uos_key_json="$(_json_encode <"$_ckey")"
|
||||
_uos_cert_json="$(_json_encode <"$_cfullchain")"
|
||||
_create_body="{\"name\":\"$_uos_name\",\"key\":\"$_uos_key_json\",\"cert\":\"$_uos_cert_json\"}"
|
||||
@@ -234,21 +277,8 @@ unifios_deploy() {
|
||||
_err "Response: $_list_json"
|
||||
return 1
|
||||
fi
|
||||
# _normalizeJson collapses the response to one predictable line (no stray
|
||||
# whitespace around colons, no embedded CR/LF the server might emit) but
|
||||
# also strips the trailing newline entirely -- re-terminate before the
|
||||
# split below, since some sed implementations drop an unterminated final
|
||||
# line rather than processing it.
|
||||
_list_json="$(echo "$_list_json" | _normalizeJson)"
|
||||
# A literal embedded newline (not the two-character "\n", which GNU sed
|
||||
# treats as a newline in the replacement but POSIX doesn't define and BSD
|
||||
# sed emits literally) splits it one JSON object per line so grep can
|
||||
# match a single certificate entry at a time.
|
||||
_list_json="$(
|
||||
printf '%s\n' "$_list_json" | sed 's/},{/},\
|
||||
{/g'
|
||||
)"
|
||||
_new_id="$(echo "$_list_json" | grep -F "\"fingerprint\":\"$_uos_fingerprint\"" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
|
||||
_list_json="$(_uos_split_json "$_list_json")"
|
||||
_new_id="$(echo "$_list_json" | _uos_grep_literal "\"fingerprint\":\"$_uos_fingerprint\"" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
|
||||
if [ -z "$_new_id" ]; then
|
||||
_err "Certificate upload rejected as a duplicate (server reported USER_CERTIFICATE_DUPLICATE), but no existing entry matching this fingerprint was found."
|
||||
_err "Response: $_create_json"
|
||||
@@ -290,15 +320,11 @@ unifios_deploy() {
|
||||
if [ "$_list_code" != "200" ]; then
|
||||
_err "Failed to list certificates for cleanup (HTTP $_list_code) -- leaving old entries in place."
|
||||
else
|
||||
_list_json="$(echo "$_list_json" | _normalizeJson)"
|
||||
_list_json="$(
|
||||
printf '%s\n' "$_list_json" | sed 's/},{/},\
|
||||
{/g'
|
||||
)"
|
||||
# The pattern below matches the domain name followed by a space. If the
|
||||
# space is missing, the pattern can also match a different domain that
|
||||
# starts with the same text as this domain.
|
||||
_old_ids="$(echo "$_list_json" | grep -F "\"name\":\"$_cdomain " | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4 | grep -v "^$_new_id$")"
|
||||
_list_json="$(_uos_split_json "$_list_json")"
|
||||
# The pattern below matches the domain name and key type, followed by
|
||||
# a space. If the space is missing, the pattern can also match a
|
||||
# different domain that starts with the same text as this domain.
|
||||
_old_ids="$(echo "$_list_json" | _uos_grep_literal "\"name\":\"$_cdomain $_uos_keytype " | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4 | grep -v "^$_new_id$")"
|
||||
for _old_id in $_old_ids; do
|
||||
_info "Removing old certificate entry $_old_id..."
|
||||
_del_json="$(_post "" "$DEPLOY_UNIFIOS_HOST/api/userCertificates/$_old_id" "" "DELETE")"
|
||||
|
||||
@@ -150,6 +150,9 @@ _dns_dynv6_add_http() {
|
||||
fi
|
||||
_get_zone_name "$_zone_id"
|
||||
record=${fulldomain%%."$_zone_name"}
|
||||
if [ "$fulldomain" = "$_zone_name" ]; then
|
||||
record=""
|
||||
fi
|
||||
_set_record TXT "$record" "$txtvalue"
|
||||
if _contains "$response" "$txtvalue"; then
|
||||
_info "Successfully added record"
|
||||
@@ -168,6 +171,9 @@ _dns_dynv6_rm_http() {
|
||||
fi
|
||||
_get_zone_name "$_zone_id"
|
||||
record=${fulldomain%%."$_zone_name"}
|
||||
if [ "$fulldomain" = "$_zone_name" ]; then
|
||||
record=""
|
||||
fi
|
||||
_get_record_id "$_zone_id" "$record" "$txtvalue"
|
||||
_del_record "$_zone_id" "$_record_id"
|
||||
if [ -z "$response" ]; then
|
||||
|
||||
226
dnsapi/dns_hw.sh
Normal file
226
dnsapi/dns_hw.sh
Normal file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_hw_info='Huawei Cloud DNS
|
||||
Site: HuaweiCloud.com
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_hw
|
||||
Options:
|
||||
HW_AK Access Key
|
||||
HW_SK Secret Access Key
|
||||
HW_Region Region. E.g. "cn-north-4". Optional, defaults to "cn-north-4".
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/7221
|
||||
Author: mashirozx
|
||||
'
|
||||
|
||||
dns_hw_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if ! _hw_init; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _hw_get_zoneid "$fulldomain"; then
|
||||
return 1
|
||||
fi
|
||||
if ! _hw_get_recordset "$fulldomain" "$_hw_zoneid"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Huawei Cloud stores each TXT value with its required inner quotes.
|
||||
_hw_txt_record="\"\\\"${txtvalue}\\\"\""
|
||||
case "$_hw_records" in
|
||||
*"$txtvalue"*)
|
||||
_debug "TXT record already exists"
|
||||
;;
|
||||
*)
|
||||
if [ -z "$_hw_recordid" ]; then
|
||||
_hw_body="{\"name\":\"${fulldomain}.\",\"type\":\"TXT\",\"ttl\":300,\"records\":[${_hw_txt_record}]}"
|
||||
# Create a TXT record set: https://support.huaweicloud.com/api-dns/dns_api_64001.html
|
||||
_hw_rest "POST" "/v2/zones/${_hw_zoneid}/recordsets" "" "$_hw_body" || return 1
|
||||
else
|
||||
_hw_body="{\"name\":\"${fulldomain}.\",\"type\":\"TXT\",\"ttl\":${_hw_recordttl},\"records\":[${_hw_records},${_hw_txt_record}]}"
|
||||
# Update the existing TXT record set: https://support.huaweicloud.com/api-dns/UpdateRecordSets.html
|
||||
_hw_rest "PUT" "/v2/zones/${_hw_zoneid}/recordsets/${_hw_recordid}" "" "$_hw_body" || return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
_saveaccountconf_mutable HW_AK "$HW_AK"
|
||||
_saveaccountconf_mutable HW_SK "$HW_SK"
|
||||
if [ -n "$HW_Region" ]; then
|
||||
_saveaccountconf_mutable HW_Region "$HW_Region"
|
||||
fi
|
||||
}
|
||||
|
||||
dns_hw_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if ! _hw_init; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _hw_get_zoneid "$fulldomain" || ! _hw_get_recordset "$fulldomain" "$_hw_zoneid"; then
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$_hw_recordid" ]; then
|
||||
_debug "TXT record not found"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Keep unrelated TXT values that share this record set.
|
||||
_hw_txt_record="\"\\\"${txtvalue}\\\"\""
|
||||
case "$_hw_records" in
|
||||
*"$txtvalue"*) ;;
|
||||
*)
|
||||
_debug "TXT record value not found"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
_hw_sed_txt_record=$(echo "$_hw_txt_record" | sed 's/\\/\\\\/g')
|
||||
_hw_new_records=$(echo "$_hw_records" | sed "s/${_hw_sed_txt_record},//; s/,${_hw_sed_txt_record}//; s/${_hw_sed_txt_record}//")
|
||||
if [ -z "$_hw_new_records" ]; then
|
||||
# Delete an empty TXT record set: https://support.huaweicloud.com/api-dns/dns_api_64005.html
|
||||
_hw_rest "DELETE" "/v2/zones/${_hw_zoneid}/recordsets/${_hw_recordid}" "" "" || return 1
|
||||
else
|
||||
_hw_body="{\"name\":\"${fulldomain}.\",\"type\":\"TXT\",\"ttl\":${_hw_recordttl},\"records\":[${_hw_new_records}]}"
|
||||
# Update the record set after removing this challenge value: https://support.huaweicloud.com/api-dns/UpdateRecordSets.html
|
||||
_hw_rest "PUT" "/v2/zones/${_hw_zoneid}/recordsets/${_hw_recordid}" "" "$_hw_body" || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_hw_init() {
|
||||
# Credentials from the environment override the persisted account settings.
|
||||
HW_AK="${HW_AK:-$(_readaccountconf_mutable HW_AK)}"
|
||||
HW_SK="${HW_SK:-$(_readaccountconf_mutable HW_SK)}"
|
||||
HW_Region="${HW_Region:-$(_readaccountconf_mutable HW_Region)}"
|
||||
if [ -z "$HW_AK" ] || [ -z "$HW_SK" ]; then
|
||||
_err "You don't specify Huawei Cloud Access Key and Secret Access Key yet."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_hw_region="${HW_Region:-cn-north-4}"
|
||||
_hw_api="https://dns.${_hw_region}.myhuaweicloud.com"
|
||||
_hw_host="dns.${_hw_region}.myhuaweicloud.com"
|
||||
}
|
||||
|
||||
_hw_get_zoneid() {
|
||||
_hw_domain=$1
|
||||
_hw_index=1
|
||||
# Try successively shorter suffixes so delegated zones are supported.
|
||||
while true; do
|
||||
_hw_zone_name=$(echo "$_hw_domain" | cut -d . -f "$_hw_index"-100)
|
||||
if [ -z "$_hw_zone_name" ]; then
|
||||
_err "Could not find Huawei Cloud DNS zone for $_hw_domain"
|
||||
return 1
|
||||
fi
|
||||
_hw_query="name=$(printf "%s" "$_hw_zone_name" | _url_encode upper-hex)&search_mode=equal"
|
||||
# List public zones to find the authoritative zone: https://support.huaweicloud.com/api-dns/dns_api_62003.html
|
||||
if ! _hw_rest "GET" "/v2/zones" "$_hw_query" ""; then
|
||||
return 1
|
||||
fi
|
||||
_hw_zoneid=$(echo "$_hw_response" | _egrep_o '"id"[ ]*:[ ]*"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||
_hw_returned_name=$(echo "$_hw_response" | _egrep_o '"name"[ ]*:[ ]*"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||
if [ -n "$_hw_zoneid" ] && [ "$_hw_returned_name" = "${_hw_zone_name}." ]; then
|
||||
return 0
|
||||
fi
|
||||
_hw_index=$(_math "$_hw_index" + 1)
|
||||
done
|
||||
}
|
||||
|
||||
_hw_get_recordset() {
|
||||
_hw_domain=$1
|
||||
_hw_zone=$2
|
||||
_hw_recordid=""
|
||||
_hw_records=""
|
||||
_hw_recordttl=""
|
||||
_hw_query="name=$(printf "%s" "$_hw_domain" | _url_encode upper-hex)&search_mode=equal&type=TXT"
|
||||
# List TXT record sets to locate the existing challenge record: https://support.huaweicloud.com/api-dns/dns_api_64004.html
|
||||
if ! _hw_rest "GET" "/v2/zones/${_hw_zone}/recordsets" "$_hw_query" ""; then
|
||||
return 1
|
||||
fi
|
||||
_hw_recordid=$(echo "$_hw_response" | _egrep_o '"id"[ ]*:[ ]*"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||
_hw_returned_name=$(echo "$_hw_response" | _egrep_o '"name"[ ]*:[ ]*"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||
if [ -z "$_hw_recordid" ]; then
|
||||
return 0
|
||||
fi
|
||||
_hw_expected_name="$(_lower_case "${_hw_domain}.")"
|
||||
if [ "$(_lower_case "$_hw_returned_name")" != "$_hw_expected_name" ]; then
|
||||
_err "Huawei Cloud DNS returned an unexpected record set for $_hw_domain"
|
||||
return 1
|
||||
fi
|
||||
# A DNS record set may contain multiple TXT values for concurrent challenges.
|
||||
_hw_records=$(echo "$_hw_response" | sed 's/.*"records"[ ]*:[ ]*\[//; s/\].*//' | tr -d '\r\n')
|
||||
_hw_recordttl=$(echo "$_hw_response" | _egrep_o '"ttl"[ ]*:[ ]*[0-9]*' | _head_n 1 | cut -d : -f 2 | tr -d ' ')
|
||||
if [ -z "$_hw_recordttl" ]; then
|
||||
_err "Huawei Cloud DNS record set did not include a TTL"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_hw_sha256() {
|
||||
printf "%s" "$1" | _digest sha256 hex
|
||||
}
|
||||
|
||||
_hw_hmac() {
|
||||
_hw_key_hex=$(printf "%s" "$1" | _hex_dump | tr -d ' ')
|
||||
printf "%s" "$2" | _hmac sha256 "$_hw_key_hex" hex
|
||||
}
|
||||
|
||||
_hw_rest() {
|
||||
_hw_method=$1
|
||||
_hw_uri=$2
|
||||
_hw_query=$3
|
||||
_hw_payload=$4
|
||||
_H1=""
|
||||
_H2=""
|
||||
_H3=""
|
||||
_H4=""
|
||||
_H5=""
|
||||
_hw_date=$(_utc_date | tr -d ' :-')
|
||||
_hw_short_date=${_hw_date%??????}
|
||||
_hw_date="${_hw_short_date}T${_hw_date#????????}Z"
|
||||
# Huawei's API gateway signs a trailing slash even when the published URI has none.
|
||||
_hw_canonical_uri="${_hw_uri%/}/"
|
||||
# SDK-HMAC-SHA256 signs the exact canonical request sent to Huawei Cloud.
|
||||
_hw_payload_hash=$(_hw_sha256 "$_hw_payload")
|
||||
_hw_headers="content-type:application/json
|
||||
host:${_hw_host}
|
||||
x-sdk-date:${_hw_date}
|
||||
"
|
||||
_hw_signed_headers="content-type;host;x-sdk-date"
|
||||
_hw_canonical_request="${_hw_method}
|
||||
${_hw_canonical_uri}
|
||||
${_hw_query}
|
||||
${_hw_headers}
|
||||
${_hw_signed_headers}
|
||||
${_hw_payload_hash}"
|
||||
_hw_string_to_sign="SDK-HMAC-SHA256
|
||||
${_hw_date}
|
||||
$(_hw_sha256 "$_hw_canonical_request")"
|
||||
_hw_signature=$(_hw_hmac "$HW_SK" "$_hw_string_to_sign")
|
||||
_H1="Content-Type: application/json"
|
||||
_H2="Host: ${_hw_host}"
|
||||
_H3="X-Sdk-Date: ${_hw_date}"
|
||||
_H4="Authorization: SDK-HMAC-SHA256 Access=${HW_AK}, SignedHeaders=${_hw_signed_headers}, Signature=${_hw_signature}"
|
||||
_hw_url="${_hw_api}${_hw_uri}"
|
||||
if [ -n "$_hw_query" ]; then
|
||||
_hw_url="${_hw_url}?${_hw_query}"
|
||||
fi
|
||||
# _post sends the canonical request with each signed header exactly once.
|
||||
if [ -z "$HTTP_HEADER" ]; then
|
||||
_err "HTTP header file is not initialized"
|
||||
return 1
|
||||
fi
|
||||
: >"$HTTP_HEADER" || return 1
|
||||
if ! _hw_response=$(_post "$_hw_payload" "$_hw_url" "" "$_hw_method"); then
|
||||
_err "Huawei Cloud DNS API request failed"
|
||||
return 1
|
||||
fi
|
||||
_hw_code=$(grep '^HTTP' "$HTTP_HEADER" | _tail_n 1 | cut -d ' ' -f 2 | tr -d '\r\n')
|
||||
if ! _startswith "$_hw_code" "2"; then
|
||||
_err "Huawei Cloud DNS API error: HTTP $_hw_code"
|
||||
_debug2 response "$_hw_response"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user