Compare commits

...
8 Commits
Author SHA1 Message Date
neil 37d064f7b5 Merge pull request #7337 from acmesh-official/dev
sync
2026-10-16 16:16:13 +08:00
579c994fad Handle DNSimple API response for nonexistent subdomain (#7324)
* handle OAuth response

* revert comment removal

* handle lowercased domain value

* expand error detection logic for all non-expected responses

* shell-independent lowercasing

* revert; domain value already lowercased

* add missing double-quotes

* use zone records endpoint fallback when zone cannot be identified

* add logic missing from previous

---------

Co-authored-by: neil <github@neilpang.com>
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>
2026-10-16 16:11:30 +08:00
neil f659ba2ea2 Skip conf writes that change nothing
Since #7278 every conf write is a temp file plus a rename, and _setopt
and _clear_conf rewrote the conf even when the content stayed the same:
_savedeployconf clears a legacy key that is almost never there, and
deploy hooks re-save the same values on every run. On storage where
file creation and rename are expensive (ZFS on HDD) a multideploy run
went from about 23 seconds to over 2 minutes.

Return early when the new content equals the old one. A simulated
3-service docker multideploy drops from 96 conf writes to 32.

Fixes #7335
2026-10-15 15:03:18 +08:00
neil f5f2242d60 deploy/ssh: stop the batched remote command at the first failed step
In single-call mode (the default) all steps were joined with ";", so a
failed write did not stop the batch: the remaining files were still
overwritten, DEPLOY_SSH_REMOTE_CMD still ran on the broken file, and the
deploy reported success because only the last exit code counted.

With a POSIX remote shell, steps are now wrapped in { } and joined with
&&, matching multi-call mode. Non-POSIX remote shells (powershell -c,
cmd /c) keep the old ";" join byte for byte.

See #7328
2026-10-14 14:13:21 +08:00
AmadeusAbionandstelar7 d73d68ced4 Add Abion.com DNS API (#7311)
* Add Abion.com DNS API

* Update dns_abion.sh

* dns_abion: keep existing TXT records on add and rm

Continues acmesh-official/acme.sh#6885 by @stelar7 and addresses
the review comments there.

- add: append to the existing TXT values at the name instead of
  replacing them, so wildcard + base certificates work
- rm: remove only the matching value, keep the TTL and comments of
  the other records, and clear the name with null when it is empty
- only read the TXT records of the target name; the old parser also
  picked up TXT records from other names in the zone
- find the zone with GET zones/<name> instead of the paginated zone
  list, and stop with the API error on anything but "Zone not found"
- add Docs line and progress messages, fix shfmt

Co-authored-by: stelar7 <dudedbz@gmail.com>

* dns_abion: lowercase the domain so rm finds the record

* dns_abion: add tracking issue to info block

* dns_abion: align info block with the structural info format

---------

Co-authored-by: stelar7 <dudedbz@gmail.com>
2026-10-14 12:39:44 +08:00
Eyal Kalderon 228a140094 Add deploy hook for Redfish-compatible BMCs (#7276)
* Add `deploy/redfish.sh` and implement Redfish session log in/out

Since `acme.sh` intends to keep third-party dependencies to a minimum,
this implementation will not rely on `redfishtool`.

* Detect BMC manager and replace existing cert with ACME cert

* Reuse `_toPkcs8` defined in `acme.sh`

* Return early if unsupported key algorithms or bit lengths are used

* Improve robustness and UX a bit

* Support opt-in automatic restart of BMC

* Improve log output and inline docs some more

* Remove `test-redfish-deploy-hook.sh` test harness

The business logic has been implemented, so it should be safe to delete
this test harness now and just interact with `acme.sh` directly.

* Trim `_redfish_` prefixes from local variables

Since each deploy hook is executed within its own subshell, there's no
risk of polluting the outer `acme.sh` environment nor any other deploy
hooks that might follow.

* Save deploy configuration changes to disk

* Tweak deploy hook log messages a bit

* Add `DEPLOY_REDFISH_MANAGER` environment variable

* Add function for checking HTTP status code

* Add more helper functions, clean up log output a bit more

* Return early from non-zero `curl` exit codes

* Only upload leaf cert in `--sign-csr` mode

* Fix bug with RSA key compatibility error message

* Remove `_redfish_load_deploy_conf` helper, use consistent var naming

* Add several more helper functions

* Check whether server supports Certificate schema >=1.4.0

Redfish servers with a schema version older than this don't support
uploading private keys + full certificate chains directly. Instead, the
CSR must be generated on the server and signed with `acme.sh --sign-csr`
prior to deployment.

* Clarify environment variable naming a bit

* Don't implicitly opt into `HTTPS_INSECURE`

* Feedback: Fix lower-bound RSA key length check

* Feedback: Verify file existence with `-f` flag

* Feedback: Correctly map `TPM_ECC_CURVE` key length values

* Feedback: Replace exact HTTP 204 checks with 2xx instead

* Feedback: Use POSIX-compliant alternative to `\+`

* Feedback: Get session token from body instead of HTTP header

* Feedback: Save PKCS#8 private key directly to variable

* Feedback: Drop `jq` dependency, handle missing action info

* Fix regression in `_redfish_rest()` error reporting

* Simplify construction of expected certificate JSON string

Confirmed with a real-world test that the value of `_certificate_str`
is unchanged before and after applying this commit. See below:

diff --git a/deploy/redfish.sh b/deploy/redfish.sh
index e485fd60..3459b71a 100644
--- a/deploy/redfish.sh
+++ b/deploy/redfish.sh
@@ -131,6 +131,10 @@ redfish_deploy() {
       printf '%s\n' "${_ckey_pkcs8}"
       cat "${_cfullchain}"
     } | _json_encode)"
+
+    _new_certificate_str="$(echo "${_ckey_pkcs8}" | cat - "${_cfullchain}" | _json_encode)"
+    [ "${_certificate_str}" = "${_new_certificate_str}" ] || return 123
+
     _certificate_type='PEMchain'
   else
     _info "Private key and full certificate chain not available; we must be using a server-generated CSR."

* Include link to `redfish-generate-csr.sh` helper script in error message

* Feedback: Drop use of `paste` and `expr`

* Feedback: Remove stale `_err` invocation

* Feedback: Correctly deal with whitespace after colon

* Feedback: Handle `\n` in username and password in Dash shell

It seems that `echo` in Dash automatically converts `\n` into a line
break, causing usernames and/or passwords containing a literal
backslash-n to reach the BMC with a newline, breaking login. It
seems that `printf '%s'` doesn't have the same issue, so we use those to
format `_username` and `_password` instead of `echo`.

* Feedback: Link to acme.sh wiki instead of Gist

* Feedback: Fix `Certificate` schema version check

* Feedback: Better handle missing key algo/length/curve info

This commit changes how the deploy hook detects which cryptographic
algorithms are supported by the server if not explicitly declared in
`KeyPairAlgorithm`. Instead of blindly assuming RSA, we instead check
for the presence of `KeyBitLength` and/or `KeyCurveId` fields and try to
make an educated guess. If not enough info exists to determine key
compatibility, we fail-open instead of failing closed; the server will
yell at us anyway if `ReplaceCertificate` doesn't support something.

* Feedback: Fix character escaping of username/password

* Feedback: Correctly parse inline `KeyBitLength@Redfish.AllowableValues`

This metadatum is represented as a JSON array of allowed values, not a
numeric range with min/max. Make sure to treat the element in the list
as the maximum, and not assume the list is at most 2 elements long.

* Feedback: Add "\n" to format string for Solaris compatibility

* Feedback: Use POSIX parameter expansion instead of `rev`
2026-10-14 12:38:33 +08:00
Kevin Winter f5aeed1752 dns_cf: support IDN domains (#7322)
* dns_cf: support IDN domains (punycode record lookup, URL-encoded zone lookup)

* dns_cf: convert IDN once at the top of dns_cf_add/dns_cf_rm
2026-10-14 12:37:08 +08:00
Lee 81fd2d2126 Add Option to Control HTTP Scheme (#7318)
* Add Option to Control HTTP Scheme

Once certificates are installed renewal should always use HTTPS.
HTTP is only required if there have never been any certificates on
the device.

This also fixes an authentication issue where HTTPS is forced
by the device. Before this patch authentication failed for
devices which forced HTTPS because the POST request used
for request did not follow the HTTP to HTTPS redirect.

* Use http as Default Scheme

Using https as default for SHELLY_SCHEME breaks existing deployments
as they may not have a SHELLY_HOST which is identical to a name in
the certificate.

SHELLY_SCHEME is now a parameter as any other without the automatic
upgrade to https. This will always need to be done manually.
2026-10-13 09:43:28 +08:00
7 changed files with 787 additions and 28 deletions

No files matched your search

+19 -3
View File
@@ -2914,6 +2914,7 @@ _setopt() {
_err "Cannot read $__conf."
return 1
fi
__old_text="$__text"
#build the new content first and write it once through _write_conf:
#redirecting straight into the conf truncates it before anything is
#written, so a failing sed (#2426) or a failing write (#7247) left a
@@ -2930,6 +2931,13 @@ _setopt() {
_debug3 APP
__text="$__text${__text:+$__nl}$__opt$__sep$__val$__end"
fi
if [ -z "$__sed_err" ] && [ "$__text" = "$__old_text" ]; then
#the value is already saved: callers re-save the same values on every
#run, and each write is a temp file plus a rename, which is slow on
#some storage (#7335)
_debug3 "Unchanged, skip writing $__conf"
return 0
fi
if [ "$__sed_err" ] || ! _write_conf "$__conf" "$__text"; then
_err "Cannot save '$__opt' to $__conf."
return 1
@@ -2959,11 +2967,19 @@ _clear_conf() {
_c_c_f="$1"
_sdkey="$2"
if [ "$_c_c_f" ]; then
_conf_data="$(cat "$_c_c_f")"
_c_c_old="$(cat "$_c_c_f")"
#printf, not echo: dash's builtin echo interprets backslash escapes and
#would corrupt saved values that contain them on every rewrite
if ! _conf_data="$(printf -- "%s\n" "$_conf_data" | sed "/^$_sdkey *=.*$/d")" ||
! _write_conf "$_c_c_f" "$_conf_data"; then
if ! _conf_data="$(printf -- "%s\n" "$_c_c_old" | sed "/^$_sdkey *=.*$/d")"; then
_err "Cannot clear '$_sdkey' in $_c_c_f."
return 1
fi
#the key is not there, the usual case for the legacy keys cleared on
#every _savedeployconf: nothing to write (#7335)
if [ "$_conf_data" = "$_c_c_old" ]; then
return 0
fi
if ! _write_conf "$_c_c_f" "$_conf_data"; then
_err "Cannot clear '$_sdkey' in $_c_c_f."
return 1
fi
+492
View File
@@ -0,0 +1,492 @@
#!/usr/bin/env sh
# Script to deploy certificates to compatible BMCs (baseboard management
# controllers) using the [Redfish API](https://www.dmtf.org/standards/redfish).
#
# Environment Variables:
#
# ```sh
# DEPLOY_REDFISH_HOST="ipmi.example.com" # Required
# DEPLOY_REDFISH_USERNAME="Administrator" # Required
# DEPLOY_REDFISH_PASSWORD="superuser" # Required
# DEPLOY_REDFISH_USE_BASIC_AUTH=0 # Optional (set to "1" to enable)
# DEPLOY_REDFISH_RESTART=0 # Optional (set to "1" to enable)
# DEPLOY_REDFISH_MANAGER= # Optional (e.g. "/redfish/v1/Managers/Self")
# DEPLOY_REDFISH_CERTIFICATE= # Optional (e.g. "/redfish/v1/CertificateService/Certificates/1")
# ```
#
# Compatibility:
#
# Compatible with any IPMI/BMC solution supporting Redfish v1.6.1 or newer (or
# Redfish Schema Bundle 2018.3), as defined in the following documents:
#
# <https://www.dmtf.org/sites/default/files/standards/documents/DSP0266_1.6.1.pdf>
# <https://www.dmtf.org/sites/default/files/standards/documents/DSP2046_2018.3.pdf>
# <https://www.dmtf.org/sites/default/files/standards/documents/DSP2059_1.0.0.pdf>
#
# This script is confirmed to work on the following systems:
#
# | IPMI Solution | BMC Chipset | Firmware Family | Redfish Version |
# |------------------|----------------|------------------|-----------------|
# | ASUS ASMB12-iKVM | ASPEED AST2600 | AMI MegaRAC SP-X | 1.15.1 |
#
# However, this should also work with HPE iLO 5 v2.42 (or newer), Dell iDRAC9
# v5.0 (or newer), or any ASUS/Lenovo/Supermicro IPMI solution based on AMI
# MegaRAC SP-X v12.x (or newer).
#domain keyfile certfile cafile fullchain
redfish_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"
_getdeployconf DEPLOY_REDFISH_HOST
_getdeployconf DEPLOY_REDFISH_USERNAME
_getdeployconf DEPLOY_REDFISH_PASSWORD
_getdeployconf DEPLOY_REDFISH_USE_BASIC_AUTH
_getdeployconf DEPLOY_REDFISH_RESTART
_getdeployconf DEPLOY_REDFISH_MANAGER
_getdeployconf DEPLOY_REDFISH_CERTIFICATE
if [ -z "${DEPLOY_REDFISH_HOST}" ]; then
_err 'DEPLOY_REDFISH_HOST must be set. Please specify the IP or domain name of the Redfish server.'
return 1
fi
if [ -z "${DEPLOY_REDFISH_USERNAME}" ] || [ -z "${DEPLOY_REDFISH_PASSWORD}" ]; then
_err 'DEPLOY_REDFISH_USERNAME and DEPLOY_REDFISH_PASSWORD must be set.'
return 1
fi
DEPLOY_REDFISH_USE_BASIC_AUTH="${DEPLOY_REDFISH_USE_BASIC_AUTH:-0}"
DEPLOY_REDFISH_RESTART="${DEPLOY_REDFISH_RESTART:-0}"
_debug DEPLOY_REDFISH_HOST "${DEPLOY_REDFISH_HOST}"
_debug DEPLOY_REDFISH_USERNAME "${DEPLOY_REDFISH_USERNAME}"
_secure_debug DEPLOY_REDFISH_PASSWORD "${DEPLOY_REDFISH_PASSWORD}"
_debug DEPLOY_REDFISH_USE_BASIC_AUTH "${DEPLOY_REDFISH_USE_BASIC_AUTH}"
_redfish_log_in "$DEPLOY_REDFISH_USERNAME" "$DEPLOY_REDFISH_PASSWORD" || return 1
# Credentials are proven correct now -- save them, rather than only at the
# very end, so a later step failing doesn't discard a working login.
_savedeployconf DEPLOY_REDFISH_HOST "${DEPLOY_REDFISH_HOST}"
_savedeployconf DEPLOY_REDFISH_USERNAME "${DEPLOY_REDFISH_USERNAME}"
_savedeployconf DEPLOY_REDFISH_PASSWORD "${DEPLOY_REDFISH_PASSWORD}" 'base64'
_savedeployconf DEPLOY_REDFISH_USE_BASIC_AUTH "${DEPLOY_REDFISH_USE_BASIC_AUTH}"
if ! _redfish_get_certificate_service_endpoint; then
_err "Redfish server ${DEPLOY_REDFISH_HOST} doesn't support certificate management API."
return 1
fi
_debug DEPLOY_REDFISH_RESTART "${DEPLOY_REDFISH_RESTART}"
_debug DEPLOY_REDFISH_MANAGER "${DEPLOY_REDFISH_MANAGER}"
_debug DEPLOY_REDFISH_CERTIFICATE "${DEPLOY_REDFISH_CERTIFICATE}"
if [ -n "${DEPLOY_REDFISH_MANAGER}" ]; then
_manager_endpoint="${DEPLOY_REDFISH_MANAGER}"
else
_info 'Identifying REST endpoint of primary Redfish manager.'
_redfish_get_primary_manager_endpoint || return 1
_savedeployconf DEPLOY_REDFISH_MANAGER "${_manager_endpoint}"
fi
if [ -n "${DEPLOY_REDFISH_CERTIFICATE}" ]; then
_certificate_endpoint="${DEPLOY_REDFISH_CERTIFICATE}"
else
_info "Identifying REST endpoint of primary TLS certificate on: ${_manager_endpoint}"
_redfish_get_manager_certificate_endpoint "${_manager_endpoint}" || return 1
fi
if [ -f "${_ckey}" ] && [ -f "${_cfullchain}" ]; then
_info "Uploading private key and full certificate chain to: ${_certificate_endpoint}"
if ! _redfish_supports_full_certificate_chains; then
_err 'Redfish server does not support uploading full certificate chains!'
_err "Please call 'GenerateCSR' API on server first, pass the CSR to 'acme.sh --sign-csr --csr <key.pem>', and try again."
_err 'For more information: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#generate-csr-on-server-optional'
return 1
elif ! _redfish_supports_private_key "${_ckey}" "${_certificate_service_endpoint}"; then
_err "Redfish server does not support this type of private key."
return 1
fi
_ckey_pkcs8="$("${ACME_OPENSSL_BIN:-openssl}" pkcs8 -topk8 -nocrypt -in "${_ckey}")"
# shellcheck disable=SC2181
if [ "$?" != '0' ] || [ -z "${_ckey_pkcs8}" ]; then
_err 'Failed to convert private key to PKCS#8 format!'
return 1
fi
_certificate_str="$(echo "${_ckey_pkcs8}" | cat - "${_cfullchain}" | _json_encode)"
_certificate_type='PEMchain'
else
_info "Private key and full certificate chain not available; we must be using a server-generated CSR."
_info "Uploading only leaf certificate to: ${_certificate_endpoint}"
_certificate_str="$(_json_encode <"${_ccert}")"
_certificate_type='PEM'
fi
_body="$(printf '{"CertificateString":"%s","CertificateType":"%s","CertificateUri":{"@odata.id":"%s"}}' "${_certificate_str}" "${_certificate_type}" "${_certificate_endpoint}")"
_redfish_rest POST "${_certificate_service_endpoint}/Actions/CertificateService.ReplaceCertificate" "${_body}" || return 1
_code="$(_redfish_response_code)"
if ! _startswith "${_code}" '2'; then
_err "Failed to update Redfish server TLS certificate! (HTTP ${_code})"
_err "Response: ${_response}"
return 1
fi
_info 'Successfully updated Redfish server TLS certificate!'
_savedeployconf DEPLOY_REDFISH_CERTIFICATE "${_certificate_endpoint}"
if [ "${DEPLOY_REDFISH_RESTART}" = '1' ]; then
_info 'Attempting to restart BMC gracefully.'
_redfish_attempt_graceful_restart "${_manager_endpoint}" || return 0
_info 'Successfully sent graceful restart command to BMC. After restarting, the new TLS certificate will be active.'
else
_info 'Please wait up to 20 seconds to take effect, or restart the BMC manually.'
fi
_savedeployconf DEPLOY_REDFISH_RESTART "${DEPLOY_REDFISH_RESTART}"
return 0
}
_redfish_rest() {
_method="$1"
_endpoint="$2"
_body="${3:-}"
export _H1='OData-Version: 4.0'
if [ "${_method}" = 'GET' ]; then
_response="$(_get "https://${DEPLOY_REDFISH_HOST}${_endpoint}")"
else
_response="$(_post "${_body}" "https://${DEPLOY_REDFISH_HOST}${_endpoint}" '' "${_method}" "${_body:+application/json}")"
fi
_ret="$?"
_response="$(echo "${_response}" | _normalizeJson)"
if [ "${_ret}" != '0' ]; then
_err "Error while calling ${_method} ${_endpoint}. Deploy with --insecure if current certificate is invalid. Try deploying with --debug to troubleshoot."
fi
return "${_ret}"
}
_redfish_response_code() {
_egrep_o <"${HTTP_HEADER}" '^HTTP[^ ]* [0-9]+' | _tail_n 1 | tr -d '\r\n' | cut -d ' ' -f 2
}
_redfish_get_odata_id() {
_regex='s/{'
for key in $(echo "$1" | tr '.' '\n'); do
_regex="${_regex}.*\"${key}\":{"
done
sed -n "${_regex}[^}]*\"@odata\\.id\":[ ]*\"\\([^\"]*\\)\".*/\\1/p"
}
_redfish_get_odata_members() {
sed -n 's/.*"Members":[ ]*\[\([^]]*\)\].*/\1/p' | sed 's/,/\n/g' | sed -n 's/.*{"@odata\.id":"\([^"]*\)"}.*/\1/p'
}
_redfish_get_odata_count() {
sed -n 's/.*"Members@odata\.count":[ ]*\([0-9][0-9]*\).*/\1/p'
}
_redfish_log_in() {
_username="$1"
_password="$2"
_redfish_rest GET '/redfish/v1/' || return 1
_info "Authenticating with Redfish API: ${DEPLOY_REDFISH_HOST}"
if [ "${DEPLOY_REDFISH_USE_BASIC_AUTH}" = '1' ]; then
_access_token="$(printf '%s:%s' "${_username}" "${_password}" | _base64)"
export _H2="Authorization: Basic ${_access_token}"
# Verify credentials are valid
_account_service_endpoint="$(echo "${_response}" | _redfish_get_odata_id 'AccountService')"
_redfish_rest GET "${_account_service_endpoint}" || return 1
_code="$(_redfish_response_code)"
if [ "${_code}" != '200' ]; then
_err "Redfish authentication failed (HTTP ${_code})"
_err "Response: ${_response}"
return 1
fi
else
# Create new session
_sessions_endpoint="$(echo "${_response}" | _redfish_get_odata_id 'Links.Sessions')"
_username="$(printf '%s\n' "${_username}" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')"
_password="$(printf '%s\n' "${_password}" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')"
_body="$(printf '{"UserName":"%s","Password":"%s"}' "${_username}" "${_password}")"
_redfish_rest POST "${_sessions_endpoint}" "${_body}" || return 1
_code="$(_redfish_response_code)"
# Verify authentication succeeded
if [ "${_code}" != '201' ]; then
_err "Redfish authentication failed (HTTP ${_code})"
_err "Response: ${_response}"
if [ "${_code}" = '401' ] && grep -qi '^WWW-Authenticate: Basic.*$' "${HTTP_HEADER}"; then
_err 'NOTE: Server supports Basic auth scheme. Consider setting DEPLOY_REDFISH_USE_BASIC_AUTH=1 and try again.'
fi
return 1
fi
_session="$(echo "${_response}" | _redfish_get_odata_id '')"
_auth_token="$(grep -i '^X-Auth-Token: .*$' "${HTTP_HEADER}" | _tail_n 1 | tr -d ' \r\n' | cut -d ':' -f 2)"
export _H2="X-Auth-Token: ${_auth_token}"
fi
trap '_redfish_log_out "${_session:-}"' EXIT INT
}
_redfish_log_out() {
_session_endpoint="${1:-}"
_info "Logging out of Redfish session on: ${DEPLOY_REDFISH_HOST}"
if [ -n "${_session_endpoint}" ]; then
_redfish_rest DELETE "${_session_endpoint}" || return 1
_code="$(_redfish_response_code)"
if ! _startswith "${_code}" '2'; then
_err "Failed to log out of Redfish server (HTTP ${_code})."
_err "Response: ${_response}"
fi
else
_info 'Using basic HTTP auth, no need to log out of Redfish API.'
fi
export _H1=
export _H2=
}
_redfish_get_certificate_service_endpoint() {
_redfish_rest GET '/redfish/v1/' || return 1
_certificate_service_endpoint="$(echo "${_response}" | _redfish_get_odata_id 'CertificateService')"
[ -n "${_certificate_service_endpoint}" ]
}
_redfish_supports_full_certificate_chains() {
_redfish_rest GET "/redfish/v1/\$metadata" || return 1
_newest_certificate_schema_version="$(echo "${_response}" |
_egrep_o 'Namespace="Certificate\.v[_0-9][_0-9]*"' |
sed -n 's/.*v\([_0-9][_0-9]*\)".*/\1/p' |
sort -n -t _ -k1,1 -k2,2 -k3,3 | _tail_n 1 | tr '_' '.')"
_debug _newest_certificate_schema_version "${_newest_certificate_schema_version}"
_schema_major=${_newest_certificate_schema_version%%.*}
_schema_patch_minor=${_newest_certificate_schema_version#*.}
_schema_minor=${_schema_patch_minor%%.*}
_schema_patch=${_schema_patch_minor#*.}
for var in _schema_major _schema_minor _schema_patch; do
_debug "$var" "$(eval echo "\$$var")"
case "$(eval echo "\$$var")" in
'' | *[!0-9]*) return 1 ;;
*) continue ;;
esac
done
[ "${_schema_major}" -gt '1' ] || { [ "${_schema_major}" -ge '1' ] && [ "${_schema_minor}" -ge '4' ]; }
}
_redfish_get_action() {
unset _action _action_info
_redfish_rest GET "$1" || return 1
_action="$(echo "${_response}" | sed -n "s/{.*\"Actions\":{.*\"#$2\":\({[^}]*}\).*/\1/p")"
_action_info_endpoint="$(echo "${_action}" | sed -n 's/.*"@Redfish\.ActionInfo":"\([^"]*\)".*/\1/p')"
if [ -n "${_action_info_endpoint}" ]; then
_redfish_rest GET "${_action_info_endpoint}" || return 1
_code="$(_redfish_response_code)"
if [ "${_code}" = '200' ]; then
_action_info="${_response}"
fi
fi
}
_redfish_get_action_allowable_values() {
_parameter="$1"
_allowable_values="$(echo "${_action}" |
sed -n "s/.*\"${_parameter}@Redfish\.AllowableValues\":[ ]*\[\([^]]*\)\].*/\1/p" |
tr -d '"')"
if [ -n "${_allowable_values}" ]; then
_debug2 "Found allowable values for '${_parameter}' in '@Redfish.Action': [${_allowable_values}]"
echo "${_allowable_values}"
elif [ -n "${_action_info}" ]; then
_parameter_info="$(echo "${_action_info}" |
sed -n 's/.*"Parameters":[ ]*\[\(\({[^}]*},\{0,1\}\)*\)\].*/\1/p' | _egrep_o '\{[^\}]{1,}\}' |
grep "\"Name\":\"${_parameter}\"")"
if [ -n "${_parameter_info}" ]; then
_debug2 "Found allowable values for '${_parameter}' in @Redfish.ActionInfo: ${_parameter_info}"
_data_type="$(echo "${_parameter_info}" | sed -n 's/.*"DataType":[ ]*"\([^"]*\)".*/\1/p')"
case "${_data_type}" in
String)
echo "${_parameter_info}" | sed -n 's/.*"AllowableValues":[ ]*\[\([^]]*\)\].*/\1/p' | tr -d '"'
;;
Number)
_min_value="$(echo "${_parameter_info}" | sed -n 's/.*"MinimumValue":[ ]*\([0-9]\{1,\}\).*/\1/p')"
_max_value="$(echo "${_parameter_info}" | sed -n 's/.*"MaximumValue":[ ]*\([0-9]\{1,\}\).*/\1/p')"
echo "${_min_value},${_max_value}"
;;
esac
fi
fi
}
_redfish_supports_private_key() {
_private_key="$1"
_certificate_service_endpoint="$2"
if _isRSA "${_private_key}"; then
_key_algo='RSA'
elif _isEcc "${_private_key}"; then
_key_algo='ECDSA'
else
_err 'Private key uses unknown cryptographic algorithm!'
return 1
fi
_redfish_get_action "${_certificate_service_endpoint}" 'CertificateService.GenerateCSR' || return 1
_allowed_key_algos="$(_redfish_get_action_allowable_values 'KeyPairAlgorithm')"
_allowed_key_bit_lengths="$(_redfish_get_action_allowable_values 'KeyBitLength')"
_allowed_key_curve_ids="$(_redfish_get_action_allowable_values 'KeyCurveId')"
if [ -z "${_allowed_key_algos}" ]; then
_contains "${_response}" '"KeyBitLength"' && _allowed_key_algos='TPM_ALG_RSA'
_contains "${_response}" '"KeyCurveId"' && _allowed_key_algos="${_allowed_key_algos},TPM_ALG_ECDSA"
_allowed_key_algos="${_allowed_key_algos#,}"
if [ -z "${_allowed_key_algos}" ]; then
_debug 'Failed to determine server crypto requirements, proceeding blindly.'
return 0
fi
_debug "GenerateCSR does not specify allowed KeyPairAlgorithm(s), so assuming ${_allowed_key_algos}."
fi
_debug _key_algo "${_key_algo}"
_debug _allowed_key_algos "${_allowed_key_algos}"
case "${_key_algo}:${_allowed_key_algos}" in
RSA:*RSA*)
_min_key_length="$(echo "${_allowed_key_bit_lengths}" | cut -d ',' -f 1)"
_max_key_length="${_allowed_key_bit_lengths##*,}"
_debug _min_key_length "${_min_key_length}"
_debug _max_key_length "${_max_key_length}"
if [ -z "${_min_key_length}" ] && [ -z "${_max_key_length}" ]; then
_debug "Server supports RSA private keys but doesn't specify minimum/maximum bit length, proceeding blindly."
return 0
fi
# shellcheck disable=SC2154 # Le_Keylength is set by acme.sh core, not this hook
if [ "${Le_Keylength}" -lt "${_min_key_length:-${Le_Keylength}}" ] || [ "${Le_Keylength}" -gt "${_max_key_length:-${Le_Keylength}}" ]; then
_err "Unsupported RSA private key length ${Le_Keylength}!"
_err "Please re-run acme.sh with --keylength set to a value between ${_min_key_length} and ${_max_key_length}."
return 1
fi
;;
ECDSA:*ECDSA*)
_debug _allowed_key_curve_ids "${_allowed_key_curve_ids}"
if [ -z "${_allowed_key_curve_ids}" ]; then
_debug "Server supports ECDSA private keys but doesn't specify which curves, proceeding blindly."
return 0
fi
# shellcheck disable=SC2154 # Le_Keylength is set by acme.sh core, not this hook
if ! _contains "${_allowed_key_curve_ids}" "TPM_ECC_NIST_P${Le_Keylength#ec-}"; then
_err "Unsupported ECDSA private key type! Server supports only: ${_allowed_key_curve_ids}"
return 1
fi
;;
*)
_err "Unsupported key pair algorithm ${_key_algo}! Server supports only: ${_allowed_key_algos}"
return 1
;;
esac
}
_redfish_get_primary_manager_endpoint() {
_redfish_rest GET '/redfish/v1/' || return 1
_managers_endpoint="$(echo "${_response}" | _redfish_get_odata_id 'Managers')"
_redfish_rest GET "${_managers_endpoint}" || return 1
_num_managers="$(echo "${_response}" | _redfish_get_odata_count)"
if [ "${_num_managers}" != '1' ]; then
_all_managers="$(echo "${_response}" | _redfish_get_odata_members | tr '\n' ',')"
_err "Multiple Redfish managers identified (${_all_managers}), but expected exactly one."
_err 'Please specify the correct manager in DEPLOY_REDFISH_MANAGER.'
return 1
fi
_manager_endpoint="$(echo "${_response}" | _redfish_get_odata_members | _head_n 1)"
[ -n "${_manager_endpoint}" ]
}
_redfish_get_manager_certificate_endpoint() {
_manager_endpoint="$1"
_redfish_rest GET "${_manager_endpoint}" || return 1
_protocol_endpoint="$(echo "${_response}" | _redfish_get_odata_id 'NetworkProtocol')"
_redfish_rest GET "${_protocol_endpoint}/HTTPS/Certificates" || return 1
_num_certificates="$(echo "${_response}" | _redfish_get_odata_count)"
if [ -z "${_num_certificates}" ]; then
_certificate_endpoint="${_protocol_endpoint}/HTTPS/Certificates/1"
return 0
elif [ "${_num_certificates}" != '1' ]; then
_all_certificates="$(echo "${_response}" | _redfish_get_odata_members | tr '\n' ',')"
_err "Multiple web service HTTPS certificates identified (${_all_certificates}), but expected exactly one."
_err "Please specify which certificate should be replaced in DEPLOY_REDFISH_CERTIFICATE."
return 1
fi
_certificate_endpoint="$(echo "${_response}" | _redfish_get_odata_members | _head_n 1)"
[ -n "${_certificate_endpoint}" ]
}
_redfish_attempt_graceful_restart() {
_manager_endpoint="$1"
_redfish_get_action "${_manager_endpoint}" 'Manager.Reset' || return 1
_allowed_reset_types="$(_redfish_get_action_allowable_values 'ResetType')"
if ! _contains "${_allowed_reset_types}" 'GracefulRestart'; then
_err "BMC doesn't support graceful restarts. Please wait up to 20 seconds to take effect, or restart the BMC manually."
return 1
fi
_redfish_rest POST "${_manager_endpoint}/Actions/Manager.Reset" '{"ResetType":"GracefulRestart"}' || return 1
_code="$(_redfish_response_code)"
if ! _startswith "${_code}" '2'; then
_err "Failed to gracefully restart BMC! (HTTP ${_code})"
_err "Response: ${_response}"
return 1
fi
}
+26 -4
View File
@@ -4,11 +4,20 @@
# Deploy the HTTPS server certificate to a Shelly device on the local network.
#
# ```sh
# export SHELLY_HOST=192.168.1.100
# export SHELLY_HOST=shelly.example.com
# export SHELLY_PASSWORD=mysecret # only if auth is enabled on the device
# export SHELLY_SCHEME=https # only if devices factory shipped with
# # firmware 2.0.0 or already has https
# # enabled
# acme.sh --deploy -d shelly.example.com --deploy-hook shelly
# ```
#
# If `SHELLY_SCHEME=https` is used, the first deploy is likely to require
# the additional `--insecure` parameter due to factory signed certificates.
# After the initial deployment make sure to retest deployment without
# `--insecure` simply using `acme.sh --deploy -d shelly.example.com`
# to ensure follow up deployment will work.
#
# Environment variables:
# SHELLY_HOST (required) IP or hostname of the Shelly device
# SHELLY_PASSWORD (optional) Admin password for digest authentication.
@@ -16,6 +25,8 @@
# SHELLY_USER (optional) Username for auth. Default: admin
# SHELLY_REBOOT (optional) Set to "0" to skip auto-reboot.
# Default: 1 (reboot after upload)
# SHELLY_SCHEME (optional) Scheme used to call the API: http or https
# Default: http
#
# Requirements:
# - Shelly Gen3+ device (Gen4 recommended)
@@ -23,7 +34,7 @@
# - curl or wget
# - openssl (for SHA-256 digest and random cnonce)
#
# The device must be reachable via HTTP on the local network.
# The device must be reachable on the local network.
# The hook uploads the fullchain.pem and private key,
# then reboots the device to apply the new certificate.
#
@@ -52,11 +63,13 @@ shelly_deploy() {
_getdeployconf SHELLY_PASSWORD
_getdeployconf SHELLY_USER
_getdeployconf SHELLY_REBOOT
_getdeployconf SHELLY_SCHEME
_debug SHELLY_HOST "$SHELLY_HOST"
_debug SHELLY_USER "$SHELLY_USER"
_secure_debug SHELLY_PASSWORD "$SHELLY_PASSWORD"
_debug SHELLY_REBOOT "$SHELLY_REBOOT"
_debug SHELLY_SCHEME "$SHELLY_SCHEME"
if [ -z "$SHELLY_HOST" ]; then
_err "SHELLY_HOST is required. Please set the IP or hostname of your Shelly device."
@@ -65,11 +78,20 @@ shelly_deploy() {
SHELLY_USER="${SHELLY_USER:-admin}"
SHELLY_REBOOT="${SHELLY_REBOOT:-1}"
SHELLY_SCHEME="${SHELLY_SCHEME:-http}"
case "$SHELLY_SCHEME" in
http | https) ;;
*)
_err "SHELLY_SCHEME must be http or https"
return 1
;;
esac
_savedeployconf SHELLY_HOST "$SHELLY_HOST"
_savedeployconf SHELLY_PASSWORD "$SHELLY_PASSWORD"
_savedeployconf SHELLY_USER "$SHELLY_USER"
_savedeployconf SHELLY_REBOOT "$SHELLY_REBOOT"
_savedeployconf SHELLY_SCHEME "$SHELLY_SCHEME"
# --- Auth handshake (only if password is set) ---
_shelly_auth_header=""
@@ -126,7 +148,7 @@ _shelly_handshake() {
# Use a protected method (Shelly.GetStatus) to trigger 401.
# Shelly.GetDeviceInfo is excluded from auth and would miss the challenge.
_post '{"id":1,"method":"Shelly.GetStatus"}' \
"http://${SHELLY_HOST}/rpc" "" "" "application/json"
"${SHELLY_SCHEME}://${SHELLY_HOST}/rpc" "" "" "application/json"
# Detect auth from HTTP status line rather than response body
if ! _shelly_has_auth_challenge "$HTTP_HEADER"; then
@@ -210,7 +232,7 @@ _shelly_rpc() {
export _H1=""
fi
_post "$_shelly_body" "http://${SHELLY_HOST}/rpc" "" "" "application/json"
_post "$_shelly_body" "${SHELLY_SCHEME}://${SHELLY_HOST}/rpc" "" "" "application/json"
_shelly_ret=$?
if [ "$_shelly_ret" != "0" ]; then
+40 -14
View File
@@ -189,6 +189,19 @@ ssh_deploy() {
_info "Required commands batched and sent in single call to remote host"
fi
# With a POSIX remote shell, steps are joined with && so that the first
# failed step stops the rest (no REMOTE_CMD after a failed write). Other
# shells ("powershell -c", "cmd /c") keep the plain ";" join.
_ssh_shell_name="${DEPLOY_SSH_REMOTE_SHELL%% *}"
case "${_ssh_shell_name##*/}" in
sh | bash | dash | ash | ksh | mksh | zsh)
_ssh_fail_fast="yes"
;;
*)
_ssh_fail_fast="no"
;;
esac
_returnCode=0
_deploy_ssh_servers="$DEPLOY_SSH_SERVER"
for DEPLOY_SSH_SERVER in $_deploy_ssh_servers; do
@@ -228,9 +241,9 @@ _ssh_deploy() {
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
# run cleanup on the backup directory, erase all older
# than 180 days (15552000 seconds).
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
_ssh_add_step "now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done"
# Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
_info "Backup of old certificate files will be placed in remote directory $_backupdir"
_info "Backup directories erased after 180 days."
@@ -245,7 +258,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite.
_cmdstr="$_cmdstr if [ -f $DEPLOY_SSH_KEYFILE ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null; fi;"
_ssh_add_step "if [ -f $DEPLOY_SSH_KEYFILE ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null; fi"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
@@ -261,10 +274,9 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
return $_err_code
fi
else
# If file doesn't exist, create it and change its permissions.
_cmdstr="$_cmdstr test ! -f $DEPLOY_SSH_KEYFILE && touch $DEPLOY_SSH_KEYFILE && chmod 600 $DEPLOY_SSH_KEYFILE;"
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
# If file doesn't exist, create it and change its permissions, then
# ssh echo to the file. One step: "test ! -f" fails if the file exists.
_ssh_add_step "test ! -f $DEPLOY_SSH_KEYFILE && touch $DEPLOY_SSH_KEYFILE && chmod 600 $DEPLOY_SSH_KEYFILE; echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE"
_info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
@@ -282,7 +294,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
_pipe=">>"
elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite.
_cmdstr="$_cmdstr if [ -f $DEPLOY_SSH_CERTFILE ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null; fi;"
_ssh_add_step "if [ -f $DEPLOY_SSH_CERTFILE ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null; fi"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
@@ -304,7 +316,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
_ssh_add_step "echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE"
_info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
@@ -323,7 +335,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
_pipe=">>"
elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite.
_cmdstr="$_cmdstr if [ -f $DEPLOY_SSH_CAFILE ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null; fi;"
_ssh_add_step "if [ -f $DEPLOY_SSH_CAFILE ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null; fi"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
@@ -348,7 +360,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
_ssh_add_step "echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE"
_info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
@@ -368,7 +380,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
_pipe=">>"
elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite.
_cmdstr="$_cmdstr if [ -f $DEPLOY_SSH_FULLCHAIN ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null; fi;"
_ssh_add_step "if [ -f $DEPLOY_SSH_FULLCHAIN ]; then mkdir -p $_backupdir; cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null; fi"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
@@ -396,7 +408,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
_ssh_add_step "echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN"
_info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
@@ -419,7 +431,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
fi
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
_cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
_ssh_add_step "$DEPLOY_SSH_REMOTE_CMD"
_info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
@@ -439,6 +451,20 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
return 0
}
#step
_ssh_add_step() {
if [ "$_ssh_fail_fast" = "yes" ]; then
if [ -n "$_cmdstr" ]; then
_cmdstr="$_cmdstr && "
fi
# the newline ends the step even if it ends with a comment, ";" or "&"
_cmdstr="$_cmdstr{ $1
}"
else
_cmdstr="$_cmdstr $1;"
fi
}
#cmd
_ssh_remote_cmd() {
_cmd="$1"
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_abion_info='Abion.com
Site: abion.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_abion
Options:
ABION_API_KEY API Key. Created in the Abion Core web interface.
Issues: github.com/acmesh-official/acme.sh/issues/7325
'
ABION_Api_Endpoint="https://api.abion.com/v1"
##################### Public functions #####################
# Usage: dns_abion_add <domain> <txt record>
# Example: dns_abion_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_abion_add() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
ABION_API_KEY="${ABION_API_KEY:-$(_readaccountconf_mutable ABION_API_KEY)}"
if [ -z "$ABION_API_KEY" ]; then
ABION_API_KEY=""
_err "You need to specify ABION_API_KEY."
return 1
fi
_saveaccountconf_mutable ABION_API_KEY "$ABION_API_KEY"
_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 ! _abion_get_txt_records; then
return 1
fi
if _contains "$_txt_records" "\"rdata\":\"$txtvalue\""; then
_info "TXT record already exists"
return 0
fi
_records=$(echo "$_txt_records" | tr '\n' ',' | sed 's/,*$//')
if [ -n "$_records" ]; then
_records="$_records,"
fi
_info "Adding record"
if ! _abion_patch_txt "[$_records{\"rdata\":\"$txtvalue\",\"ttl\":60}]"; then
return 1
fi
_info "Added, OK"
return 0
}
# Usage: dns_abion_rm <domain> <txt record>
dns_abion_rm() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
ABION_API_KEY="${ABION_API_KEY:-$(_readaccountconf_mutable ABION_API_KEY)}"
if [ -z "$ABION_API_KEY" ]; then
ABION_API_KEY=""
_err "You need to specify ABION_API_KEY."
return 1
fi
_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 ! _abion_get_txt_records; then
return 1
fi
if ! _contains "$_txt_records" "\"rdata\":\"$txtvalue\""; then
_info "TXT record not found, nothing to remove"
return 0
fi
_records=$(echo "$_txt_records" | grep -v "\"rdata\":\"$txtvalue\"" | tr '\n' ',' | sed 's/,*$//')
if [ -n "$_records" ]; then
_records="[$_records]"
else
_records="null"
fi
_info "Deleting record"
if ! _abion_patch_txt "$_records"; then
return 1
fi
_info "Deleted, OK"
return 0
}
##################### Private functions #####################
_get_root() {
domain=$1
i=1
p=0
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
if ! _abion_rest GET "zones/$h"; then
return 1
fi
if _contains "$response" '"type" *: *"zone"'; then
if [ "$p" = "0" ]; then
_sub_domain="@"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
fi
_domain="$h"
return 0
fi
if ! _contains "$response" "Zone not found"; then
_err "$response"
return 1
fi
p="$i"
i=$(_math "$i" + 1)
done
}
_abion_get_txt_records() {
if ! _abion_rest GET "zones/$_domain"; then
return 1
fi
_sub_re=$(printf "%s" "$_sub_domain" | sed 's/[.]/\\./g')
_txt_records=$(echo "$response" | _normalizeJson |
sed -n "s/.*\"$_sub_re\":{//p" |
sed 's/\] *}.*/]/' |
_egrep_o '"TXT":\[[^]]*\]' |
sed 's/^"TXT":\[ *//;s/ *\]$//;s/} *, *{/}\
{/g')
_debug2 _txt_records "$_txt_records"
return 0
}
_abion_patch_txt() {
if ! _abion_rest PATCH "zones/$_domain" "{\"data\":{\"type\":\"zone\",\"id\":\"$_domain\",\"attributes\":{\"records\":{\"$_sub_domain\":{\"TXT\":$1}}}}}"; then
return 1
fi
if ! _contains "$response" '"type" *: *"zone"'; then
_err "Unexpected API response: $response"
return 1
fi
return 0
}
_abion_rest() {
method=$1
endpoint=$2
data=$3
export _H1="X-API-KEY: $ABION_API_KEY"
export _H2="Content-Type: application/json"
if [ "$method" = "GET" ]; then
response="$(_get "$ABION_Api_Endpoint/$endpoint")"
else
response="$(_post "$data" "$ABION_Api_Endpoint/$endpoint" "" "$method")"
fi
if [ "$?" != "0" ]; then
_err "error $endpoint"
return 1
fi
_debug2 response "$response"
return 0
}
+2 -2
View File
@@ -18,7 +18,7 @@ CF_Api="https://api.cloudflare.com/client/v4"
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_cf_add() {
fulldomain=$1
fulldomain="$(_idn "$1")"
txtvalue=$2
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
@@ -109,7 +109,7 @@ dns_cf_add() {
#fulldomain txtvalue
dns_cf_rm() {
fulldomain=$1
fulldomain="$(_idn "$1")"
txtvalue=$2
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
+10 -5
View File
@@ -15,7 +15,7 @@ DNSimple_API="https://api.dnsimple.com/v2"
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dnsimple_add() {
fulldomain=$1
fulldomain=$(echo "$1" | _lower_case)
txtvalue=$2
DNSimple_OAUTH_TOKEN="${DNSimple_OAUTH_TOKEN:-$(_readaccountconf_mutable DNSimple_OAUTH_TOKEN)}"
@@ -56,7 +56,7 @@ dns_dnsimple_add() {
# fulldomain
dns_dnsimple_rm() {
fulldomain=$1
fulldomain=$(echo "$1" | _lower_case)
DNSimple_OAUTH_TOKEN="${DNSimple_OAUTH_TOKEN:-$(_readaccountconf_mutable DNSimple_OAUTH_TOKEN)}"
if [ -z "$DNSimple_OAUTH_TOKEN" ]; then
@@ -110,9 +110,13 @@ _get_root() {
return 1
fi
if _contains "$response" 'not found'; then
_debug "$h not found"
else
if ! _contains "$response" "\"name\":\"$h\""; then
if ! _dnsimple_rest GET "$_account_id/zones/$h/records?per_page=1"; then
return 1
fi
fi
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"pagination":'; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$previous")
_domain="$h"
@@ -121,6 +125,7 @@ _get_root() {
return 0
fi
_debug "$h not found"
previous="$i"
i=$(_math "$i" + 1)
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.