mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-10-22 05:41:50 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292407d1e2 | ||
|
|
c9e9e24e24 | ||
|
|
aef81df5eb | ||
|
|
e3004b84a1 | ||
|
|
3288c35c2a | ||
|
|
5d07634e0f | ||
|
|
a57c8a45fa | ||
|
|
0d1d0092b8 | ||
|
|
c472636bae |
No files matched your search
@@ -666,8 +666,15 @@ _hex_dump() {
|
|||||||
xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " "
|
xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " "
|
||||||
else
|
else
|
||||||
_debug3 "using _ascii_hex"
|
_debug3 "using _ascii_hex"
|
||||||
str=$(cat)
|
#line by line: the cut -c in _ascii_hex works per line, and a $(cat)
|
||||||
_ascii_hex "$str"
|
#capture would drop the trailing newlines (illumos without od in PATH)
|
||||||
|
while IFS= read -r _hd_line; do
|
||||||
|
_ascii_hex "$_hd_line"
|
||||||
|
printf " 0a"
|
||||||
|
done
|
||||||
|
if [ -n "$_hd_line" ]; then
|
||||||
|
_ascii_hex "$_hd_line"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,7 +914,12 @@ _json_encode() {
|
|||||||
_j_str="$(sed 's/"/\\"/g' | sed "s/\r/\\r/g")"
|
_j_str="$(sed 's/"/\\"/g' | sed "s/\r/\\r/g")"
|
||||||
_debug3 "_json_encode"
|
_debug3 "_json_encode"
|
||||||
_debug3 "_j_str" "$_j_str"
|
_debug3 "_j_str" "$_j_str"
|
||||||
echo "$_j_str" | _hex_dump | _lower_case | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n"
|
#printf, not echo: dash's echo interprets backslash escapes and would
|
||||||
|
#collapse the \\ sequences (e.g. Telegram MarkdownV2 escapes, issue 7212)
|
||||||
|
_j_hex="$(printf "%s\n" "$_j_str" | _hex_dump | _lower_case)"
|
||||||
|
#the hex line must end with a newline: Solaris sed drops an unterminated
|
||||||
|
#last line, which emptied the whole output
|
||||||
|
printf "%s\n" "$_j_hex" | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
#from: http:\/\/ to http://
|
#from: http:\/\/ to http://
|
||||||
@@ -915,7 +927,7 @@ _json_decode() {
|
|||||||
_j_str="$(sed 's#\\/#/#g')"
|
_j_str="$(sed 's#\\/#/#g')"
|
||||||
_debug3 "_json_decode"
|
_debug3 "_json_decode"
|
||||||
_debug3 "_j_str" "$_j_str"
|
_debug3 "_j_str" "$_j_str"
|
||||||
echo "$_j_str"
|
printf "%s\n" "$_j_str"
|
||||||
}
|
}
|
||||||
|
|
||||||
#extract the authorization URLs from an order response on stdin, as a
|
#extract the authorization URLs from an order response on stdin, as a
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to an HP printer's Embedded Web Server (EWS).
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#47-deploy-the-certificate-to-an-hp-printer-ews
|
||||||
|
#
|
||||||
|
# Tested on an HP Color LaserJet Pro MFP M479fdw, firmware
|
||||||
|
# CLRWTRXXXN002.2539E.00. It should work on any modern HP printer whose EWS
|
||||||
|
# exposes the "Install Certificate" flow at
|
||||||
|
# /Security/DeviceCertificates/NewCertWithPassword/Upload -- that endpoint
|
||||||
|
# takes a password-protected PKCS#12 bundle, which this hook builds from the
|
||||||
|
# issued certificate, its key and the CA chain.
|
||||||
|
#
|
||||||
|
# ```sh
|
||||||
|
# export HP_EWS_PASSWORD=mysecret
|
||||||
|
# export HP_EWS_INSECURE=1 # needed until the printer serves a trusted cert
|
||||||
|
# acme.sh --deploy -d printer.example.com --deploy-hook hp_ews
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# Environment variables:
|
||||||
|
# HP_EWS_PASSWORD (required) EWS administrator password.
|
||||||
|
# HP_EWS_HOST (optional) IP or hostname of the printer.
|
||||||
|
# Default: the certificate's domain.
|
||||||
|
# HP_EWS_USERNAME (optional) EWS administrator user. Default: admin
|
||||||
|
# HP_EWS_PFX_PASSWORD (optional) Password for the generated PKCS#12
|
||||||
|
# bundle. A random one is used per run if
|
||||||
|
# unset; set it only if you want the
|
||||||
|
# bundle to be reproducible.
|
||||||
|
# HP_EWS_INSECURE (optional) Set to a non-empty value to skip TLS
|
||||||
|
# verification when talking to the
|
||||||
|
# printer. Needed on the first deploy,
|
||||||
|
# because the printer still serves its
|
||||||
|
# self-signed certificate at that point.
|
||||||
|
# acme.sh's global HTTPS_INSECURE is
|
||||||
|
# honoured as well.
|
||||||
|
#
|
||||||
|
# The hook authenticates with HTTP Basic auth. Printers whose EWS only offers
|
||||||
|
# Digest auth are not supported.
|
||||||
|
#
|
||||||
|
# Requires openssl, to build the PKCS#12 bundle.
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain pfx
|
||||||
|
hp_ews_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
_cpfx="$6"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
_debug _cpfx "$_cpfx"
|
||||||
|
|
||||||
|
_getdeployconf HP_EWS_HOST
|
||||||
|
_getdeployconf HP_EWS_USERNAME
|
||||||
|
_getdeployconf HP_EWS_PASSWORD
|
||||||
|
_getdeployconf HP_EWS_PFX_PASSWORD
|
||||||
|
_getdeployconf HP_EWS_INSECURE
|
||||||
|
|
||||||
|
HP_EWS_HOST="${HP_EWS_HOST:-$_cdomain}"
|
||||||
|
HP_EWS_USERNAME="${HP_EWS_USERNAME:-admin}"
|
||||||
|
|
||||||
|
_debug HP_EWS_HOST "$HP_EWS_HOST"
|
||||||
|
_debug HP_EWS_USERNAME "$HP_EWS_USERNAME"
|
||||||
|
_secure_debug HP_EWS_PASSWORD "$HP_EWS_PASSWORD"
|
||||||
|
_secure_debug HP_EWS_PFX_PASSWORD "$HP_EWS_PFX_PASSWORD"
|
||||||
|
_debug HP_EWS_INSECURE "$HP_EWS_INSECURE"
|
||||||
|
|
||||||
|
if [ -z "$HP_EWS_PASSWORD" ]; then
|
||||||
|
_err "HP_EWS_PASSWORD is required. Please set the password of the printer's EWS administrator account."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf HP_EWS_HOST "$HP_EWS_HOST"
|
||||||
|
_savedeployconf HP_EWS_USERNAME "$HP_EWS_USERNAME"
|
||||||
|
_savedeployconf HP_EWS_PASSWORD "$HP_EWS_PASSWORD" "base64"
|
||||||
|
_savedeployconf HP_EWS_PFX_PASSWORD "$HP_EWS_PFX_PASSWORD" "base64"
|
||||||
|
_savedeployconf HP_EWS_INSECURE "$HP_EWS_INSECURE"
|
||||||
|
|
||||||
|
if ! _exists "${ACME_OPENSSL_BIN:-openssl}"; then
|
||||||
|
_err "openssl is required by this deploy hook, but was not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The bundle password only has to survive the single upload below, so a
|
||||||
|
# fresh random one is used unless the operator asked for a fixed value.
|
||||||
|
_HP_EWS_PFXPW="$HP_EWS_PFX_PASSWORD"
|
||||||
|
if [ -z "$_HP_EWS_PFXPW" ]; then
|
||||||
|
_HP_EWS_PFXPW="$(${ACME_OPENSSL_BIN:-openssl} rand -hex 24)"
|
||||||
|
if [ -z "$_HP_EWS_PFXPW" ]; then
|
||||||
|
_err "Could not generate a random password for the PKCS#12 bundle."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
export _HP_EWS_PFXPW
|
||||||
|
|
||||||
|
# $6 is only materialised when Le_PFXPassword is set for this domain, and
|
||||||
|
# it would be built without the CA chain, so build our own bundle instead.
|
||||||
|
_hp_ews_pfx="$(_mktemp)"
|
||||||
|
_hp_ews_body="$(_mktemp)"
|
||||||
|
if [ -z "$_hp_ews_pfx" ] || [ -z "$_hp_ews_body" ]; then
|
||||||
|
_err "Could not create the temporary files needed for the upload."
|
||||||
|
_hp_ews_cleanup
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_hp_ews_ret=0
|
||||||
|
if ! _hp_ews_make_pfx; then
|
||||||
|
_hp_ews_ret=1
|
||||||
|
elif ! _hp_ews_upload; then
|
||||||
|
_hp_ews_ret=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_hp_ews_cleanup
|
||||||
|
|
||||||
|
return "$_hp_ews_ret"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_hp_ews_cleanup() {
|
||||||
|
[ -n "$_hp_ews_pfx" ] && rm -f "$_hp_ews_pfx"
|
||||||
|
[ -n "$_hp_ews_body" ] && rm -f "$_hp_ews_body"
|
||||||
|
unset _HP_EWS_PFXPW
|
||||||
|
# Do not leak the request header into any hook that runs after this one.
|
||||||
|
export _H1=""
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bundle key, certificate and chain into $_hp_ews_pfx, protected by
|
||||||
|
# $_HP_EWS_PFXPW. The password goes through the environment rather than the
|
||||||
|
# command line so that it does not show up in ps output.
|
||||||
|
_hp_ews_make_pfx() {
|
||||||
|
_info "Building the PKCS#12 bundle"
|
||||||
|
|
||||||
|
if ! ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export \
|
||||||
|
-out "$_hp_ews_pfx" \
|
||||||
|
-inkey "$_ckey" \
|
||||||
|
-in "$_ccert" \
|
||||||
|
-certfile "$_cca" \
|
||||||
|
-passout env:_HP_EWS_PFXPW; then
|
||||||
|
_err "Failed to build the PKCS#12 bundle."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$_hp_ews_pfx" ]; then
|
||||||
|
_err "The PKCS#12 bundle is empty."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build the multipart body in $_hp_ews_body and POST it to the printer.
|
||||||
|
_hp_ews_upload() {
|
||||||
|
_hp_ews_url="https://${HP_EWS_HOST}/Security/DeviceCertificates/NewCertWithPassword/Upload"
|
||||||
|
_hp_ews_boundary="----------------------------acmesh$(${ACME_OPENSSL_BIN:-openssl} rand -hex 16)"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf -- "--%s\r\n" "$_hp_ews_boundary"
|
||||||
|
printf "Content-Disposition: form-data; name=\"certificate\"; filename=\"%s.pfx\"\r\n" "$_cdomain"
|
||||||
|
printf "Content-Type: application/x-pkcs12\r\n\r\n"
|
||||||
|
cat "$_hp_ews_pfx"
|
||||||
|
printf "\r\n"
|
||||||
|
printf -- "--%s\r\n" "$_hp_ews_boundary"
|
||||||
|
printf "Content-Disposition: form-data; name=\"password\"\r\n\r\n%s\r\n" "$_HP_EWS_PFXPW"
|
||||||
|
printf -- "--%s--\r\n" "$_hp_ews_boundary"
|
||||||
|
} >"$_hp_ews_body"
|
||||||
|
|
||||||
|
_H1="Authorization: Basic $(printf "%s:%s" "$HP_EWS_USERNAME" "$HP_EWS_PASSWORD" | _base64)"
|
||||||
|
export _H1
|
||||||
|
if [ "$HP_EWS_INSECURE" ]; then
|
||||||
|
export HTTPS_INSECURE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Uploading the certificate to $_hp_ews_url"
|
||||||
|
if ! _post_file "$_hp_ews_body" "$_hp_ews_url" "" "POST" "multipart/form-data; boundary=$_hp_ews_boundary" >/dev/null; then
|
||||||
|
if [ -z "$HTTPS_INSECURE" ]; then
|
||||||
|
_err "If the printer still serves its self-signed certificate, set HP_EWS_INSECURE=1 and try again."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The EWS answers 201 Created on a successful install.
|
||||||
|
_hp_ews_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
|
||||||
|
_debug _hp_ews_code "$_hp_ews_code"
|
||||||
|
|
||||||
|
if [ "$_hp_ews_code" != "201" ] && [ "$_hp_ews_code" != "200" ]; then
|
||||||
|
_err "The printer rejected the certificate (HTTP $_hp_ews_code)."
|
||||||
|
if [ "$_hp_ews_code" = "401" ] || [ "$_hp_ews_code" = "403" ]; then
|
||||||
|
_err "Check HP_EWS_USERNAME and HP_EWS_PASSWORD. Only HTTP Basic authentication is"
|
||||||
|
_err "supported; printers that only offer Digest cannot be used with this hook."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Certificate installed (HTTP $_hp_ews_code). The printer restarts its web server shortly."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
Executable
+247
@@ -0,0 +1,247 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Deploy hook to deploy a certificate to pfSense using the pfREST API
|
||||||
|
# (https://pfrest.org). Tested with pfSense 2.9.0 and pfREST 2.10.2.
|
||||||
|
#
|
||||||
|
# The certificate is created under DEPLOY_PFSENSE_CERT_NAME, or key and
|
||||||
|
# certificate are replaced in place if it already exists. Then it is
|
||||||
|
# optionally assigned to the Unbound DNS-over-TLS listener and finally to the
|
||||||
|
# web UI, which gets restarted (the API is briefly unreachable afterwards).
|
||||||
|
#
|
||||||
|
# Preparation on the pfSense:
|
||||||
|
#
|
||||||
|
# - Install pfREST and enable key authentication:
|
||||||
|
# https://pfrest.org/INSTALL_AND_CONFIG/
|
||||||
|
# https://pfrest.org/AUTHENTICATION_AND_AUTHORIZATION/
|
||||||
|
# - Create the API key (System > REST API > Keys) with a user that has
|
||||||
|
# "WebCfg - All pages". Afterwards use a dedicated user with only these
|
||||||
|
# privileges, the key is bound to the user that creates it:
|
||||||
|
# REST API - /api/v2/system/certificate PATCH
|
||||||
|
# REST API - /api/v2/system/certificate POST
|
||||||
|
# REST API - /api/v2/system/certificates GET
|
||||||
|
# REST API - /api/v2/system/webgui/settings PATCH
|
||||||
|
# REST API - /api/v2/services/dns_resolver/apply POST
|
||||||
|
# REST API - /api/v2/services/dns_resolver/settings GET
|
||||||
|
# REST API - /api/v2/services/dns_resolver/settings PATCH
|
||||||
|
# - DNS-over-TLS only: "Enable SSL/TLS Service" must already be enabled in
|
||||||
|
# Services > DNS Resolver, otherwise Unbound is skipped.
|
||||||
|
#
|
||||||
|
# Environment variables:
|
||||||
|
#
|
||||||
|
# DEPLOY_PFSENSE_APIKEY - (REQUIRED)
|
||||||
|
# pfREST API key.
|
||||||
|
# DEPLOY_PFSENSE_HOST - pfsense.example.com, 192.168.1.1, etc. (OPTIONAL)
|
||||||
|
# Host/IP of the pfSense. Defaults to the
|
||||||
|
# certificate domain, required for wildcard
|
||||||
|
# certificates.
|
||||||
|
# DEPLOY_PFSENSE_PORT - 443, 8443, etc. (OPTIONAL)
|
||||||
|
# Web UI/API port. Defaults to 443.
|
||||||
|
# DEPLOY_PFSENSE_CERT_NAME - My Certificate, etc. (OPTIONAL)
|
||||||
|
# Certificate name shown in pfSense (printable
|
||||||
|
# ASCII only). Defaults to the certificate
|
||||||
|
# domain.
|
||||||
|
# DEPLOY_PFSENSE_WEBUI - yes/no (OPTIONAL)
|
||||||
|
# Assign to the web UI and restart it. Defaults
|
||||||
|
# to "yes".
|
||||||
|
# DEPLOY_PFSENSE_UNBOUND_DOT - yes/no (OPTIONAL)
|
||||||
|
# Also assign to Unbound DNS-over-TLS. Defaults
|
||||||
|
# to "no".
|
||||||
|
# DEPLOY_PFSENSE_INSECURE - yes/no (OPTIONAL)
|
||||||
|
# Do not verify the pfSense TLS certificate,
|
||||||
|
# needed on the first run with the self-signed
|
||||||
|
# default certificate. Defaults to "no".
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# export DEPLOY_PFSENSE_APIKEY="<api key>"
|
||||||
|
# acme.sh --deploy -d pfsense.example.com --deploy-hook pfsense
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
pfsense_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"
|
||||||
|
|
||||||
|
if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
|
||||||
|
_err "Key or fullchain file not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_APIKEY
|
||||||
|
if [ -z "$DEPLOY_PFSENSE_APIKEY" ]; then
|
||||||
|
_err "DEPLOY_PFSENSE_APIKEY is not set."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_secure_debug2 DEPLOY_PFSENSE_APIKEY "$DEPLOY_PFSENSE_APIKEY"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_HOST
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_PORT
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_CERT_NAME
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_WEBUI
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_UNBOUND_DOT
|
||||||
|
_getdeployconf DEPLOY_PFSENSE_INSECURE
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_PFSENSE_HOST" ]; then
|
||||||
|
if _startswith "$_cdomain" "*"; then
|
||||||
|
_err "DEPLOY_PFSENSE_HOST must be set for wildcard certificates."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
DEPLOY_PFSENSE_HOST="$_cdomain"
|
||||||
|
fi
|
||||||
|
[ -n "$DEPLOY_PFSENSE_PORT" ] || DEPLOY_PFSENSE_PORT="443"
|
||||||
|
[ -n "$DEPLOY_PFSENSE_CERT_NAME" ] || DEPLOY_PFSENSE_CERT_NAME="$_cdomain"
|
||||||
|
[ -n "$DEPLOY_PFSENSE_WEBUI" ] || DEPLOY_PFSENSE_WEBUI="yes"
|
||||||
|
[ -n "$DEPLOY_PFSENSE_UNBOUND_DOT" ] || DEPLOY_PFSENSE_UNBOUND_DOT="no"
|
||||||
|
[ -n "$DEPLOY_PFSENSE_INSECURE" ] || DEPLOY_PFSENSE_INSECURE="no"
|
||||||
|
|
||||||
|
# pfREST returns non-ASCII as \uXXXX, which cannot be matched reliably
|
||||||
|
if printf '%s' "$DEPLOY_PFSENSE_CERT_NAME" | LC_ALL=C grep -q '[^ -~]'; then
|
||||||
|
_err "DEPLOY_PFSENSE_CERT_NAME must only contain printable ASCII characters."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# pfREST rejects these characters in a certificate description
|
||||||
|
case "$DEPLOY_PFSENSE_CERT_NAME" in
|
||||||
|
*[\?\>\<\&/\\\"\']*)
|
||||||
|
_err "DEPLOY_PFSENSE_CERT_NAME must not contain any of: ? > < & / \\ \" '"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
_debug2 DEPLOY_PFSENSE_HOST "$DEPLOY_PFSENSE_HOST"
|
||||||
|
_debug2 DEPLOY_PFSENSE_PORT "$DEPLOY_PFSENSE_PORT"
|
||||||
|
_debug2 DEPLOY_PFSENSE_CERT_NAME "$DEPLOY_PFSENSE_CERT_NAME"
|
||||||
|
_debug2 DEPLOY_PFSENSE_WEBUI "$DEPLOY_PFSENSE_WEBUI"
|
||||||
|
_debug2 DEPLOY_PFSENSE_UNBOUND_DOT "$DEPLOY_PFSENSE_UNBOUND_DOT"
|
||||||
|
_debug2 DEPLOY_PFSENSE_INSECURE "$DEPLOY_PFSENSE_INSECURE"
|
||||||
|
|
||||||
|
_pfsense_api_url="https://${DEPLOY_PFSENSE_HOST}:${DEPLOY_PFSENSE_PORT}/api/v2"
|
||||||
|
if [ "$DEPLOY_PFSENSE_INSECURE" = "yes" ]; then
|
||||||
|
export HTTPS_INSECURE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Looking up certificate '$DEPLOY_PFSENSE_CERT_NAME' on pfSense"
|
||||||
|
_pfsense_call GET "/system/certificates?descr=$(printf '%s' "$DEPLOY_PFSENSE_CERT_NAME" | _url_encode)" || return 1
|
||||||
|
# do not rely on the server-side filter, only accept an exact descr match
|
||||||
|
_pf_match=""
|
||||||
|
_pf_objs="$(printf '%s\n' "$_pfsense_response" | sed 's/} *, *{/}\
|
||||||
|
{/g')"
|
||||||
|
while IFS= read -r _pf_line; do
|
||||||
|
case "$_pf_line" in
|
||||||
|
*"\"descr\":\"$DEPLOY_PFSENSE_CERT_NAME\""*)
|
||||||
|
_pf_match="$_pf_line"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<EOF
|
||||||
|
$_pf_objs
|
||||||
|
EOF
|
||||||
|
_pf_id="$(printf '%s' "$_pf_match" | _egrep_o '"id":[0-9]+' | _head_n 1 | cut -d : -f 2)"
|
||||||
|
_pf_refid="$(printf '%s' "$_pf_match" | _egrep_o '"refid":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
|
||||||
|
_debug2 "existing id/refid" "$_pf_id/$_pf_refid"
|
||||||
|
|
||||||
|
_pf_crt="$(_json_encode <"$_cfullchain")"
|
||||||
|
_pf_prv="$(_json_encode <"$_ckey")"
|
||||||
|
|
||||||
|
if [ -n "$_pf_id" ] && [ -n "$_pf_refid" ]; then
|
||||||
|
_info "Updating existing certificate (refid $_pf_refid)"
|
||||||
|
_pfsense_call PATCH "/system/certificate" \
|
||||||
|
"{\"id\": $_pf_id, \"crt\": \"$_pf_crt\", \"prv\": \"$_pf_prv\"}" || return 1
|
||||||
|
else
|
||||||
|
_info "Creating new certificate"
|
||||||
|
_pfsense_call POST "/system/certificate" \
|
||||||
|
"{\"descr\": \"$DEPLOY_PFSENSE_CERT_NAME\", \"type\": \"server\", \"crt\": \"$_pf_crt\", \"prv\": \"$_pf_prv\"}" || return 1
|
||||||
|
_pf_refid="$(printf '%s' "$_pfsense_response" | _egrep_o '"refid":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
|
||||||
|
if [ -z "$_pf_refid" ]; then
|
||||||
|
_err "Certificate was created but no refid was returned."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_APIKEY "$DEPLOY_PFSENSE_APIKEY"
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_HOST "$DEPLOY_PFSENSE_HOST"
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_PORT "$DEPLOY_PFSENSE_PORT"
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_CERT_NAME "$DEPLOY_PFSENSE_CERT_NAME"
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_WEBUI "$DEPLOY_PFSENSE_WEBUI"
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_UNBOUND_DOT "$DEPLOY_PFSENSE_UNBOUND_DOT"
|
||||||
|
_savedeployconf DEPLOY_PFSENSE_INSECURE "$DEPLOY_PFSENSE_INSECURE"
|
||||||
|
|
||||||
|
if [ "$DEPLOY_PFSENSE_UNBOUND_DOT" = "yes" ]; then
|
||||||
|
_pfsense_unbound_dot "$_pf_refid" || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEPLOY_PFSENSE_WEBUI" != "no" ]; then
|
||||||
|
_info "Assigning certificate to the web UI and restarting it"
|
||||||
|
_pfsense_call PATCH "/system/webgui/settings" "{\"sslcertref\": \"$_pf_refid\"}" || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Certificate successfully deployed to pfSense"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# method path [body] -> sets _pfsense_response, returns 1 on non-2xx
|
||||||
|
_pfsense_call() {
|
||||||
|
_pf_method="$1"
|
||||||
|
_pf_path="$2"
|
||||||
|
_pf_body="$3"
|
||||||
|
|
||||||
|
_H1="x-api-key: $DEPLOY_PFSENSE_APIKEY"
|
||||||
|
export _H1
|
||||||
|
_debug "$_pf_method" "$_pfsense_api_url$_pf_path"
|
||||||
|
|
||||||
|
if [ "$_pf_method" = "GET" ]; then
|
||||||
|
_pfsense_response="$(_get "$_pfsense_api_url$_pf_path")"
|
||||||
|
else
|
||||||
|
_pfsense_response="$(_post "$_pf_body" "$_pfsense_api_url$_pf_path" "" "$_pf_method" "application/json")"
|
||||||
|
fi
|
||||||
|
_pf_ret=$?
|
||||||
|
|
||||||
|
_pf_status="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
|
||||||
|
_pfsense_response="$(printf '%s' "$_pfsense_response" | tr -d '\r\n' | sed 's/" *: */":/g')"
|
||||||
|
_debug2 "HTTP status" "$_pf_status"
|
||||||
|
_secure_debug3 response "$_pfsense_response"
|
||||||
|
|
||||||
|
case "$_pf_status" in
|
||||||
|
2[0-9][0-9])
|
||||||
|
[ "$_pf_ret" = "0" ] && return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
_pf_msg="$(printf '%s' "$_pfsense_response" | _egrep_o '"message":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
|
||||||
|
_err "pfSense API call failed: $_pf_method $_pf_path (HTTP ${_pf_status:-none}). $_pf_msg"
|
||||||
|
if [ "$_pf_status" = "401" ] || [ "$_pf_status" = "403" ] || [ -z "$_pf_status" ]; then
|
||||||
|
_err "Check DEPLOY_PFSENSE_APIKEY, the user privileges and DEPLOY_PFSENSE_HOST/PORT. Use DEPLOY_PFSENSE_INSECURE=yes for an untrusted web UI certificate."
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# refid
|
||||||
|
_pfsense_unbound_dot() {
|
||||||
|
_pf_dot_refid="$1"
|
||||||
|
|
||||||
|
_info "Checking Unbound DNS-over-TLS"
|
||||||
|
_pfsense_call GET "/services/dns_resolver/settings" || return 1
|
||||||
|
if ! _contains "$_pfsense_response" '"enablessl":true'; then
|
||||||
|
_info "SSL/TLS is not enabled in the DNS Resolver, skipping Unbound"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Assigning certificate to Unbound and applying"
|
||||||
|
_pfsense_call PATCH "/services/dns_resolver/settings" "{\"sslcertref\": \"$_pf_dot_refid\"}" || return 1
|
||||||
|
_pfsense_call POST "/services/dns_resolver/apply" "{}" || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -59,6 +59,13 @@ dns_gandi_livedns_rm() {
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
|
GANDI_LIVEDNS_KEY="${GANDI_LIVEDNS_KEY:-$(_readaccountconf_mutable GANDI_LIVEDNS_KEY)}"
|
||||||
|
GANDI_LIVEDNS_TOKEN="${GANDI_LIVEDNS_TOKEN:-$(_readaccountconf_mutable GANDI_LIVEDNS_TOKEN)}"
|
||||||
|
if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then
|
||||||
|
_err "No Token or API key (deprecated) specified for Gandi LiveDNS."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
@@ -80,6 +87,20 @@ dns_gandi_livedns_rm() {
|
|||||||
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/\[ *,/\[/g")
|
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/\[ *,/\[/g")
|
||||||
_debug "New rrset_values" "$_new_rrset_values"
|
_debug "New rrset_values" "$_new_rrset_values"
|
||||||
|
|
||||||
|
# Last value removed: delete the record instead of leaving an empty TXT rrset.
|
||||||
|
if [ "$(echo "$_new_rrset_values" | tr -d ' ')" = "[]" ]; then
|
||||||
|
if ! _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_gandi_livedns_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
if [ "$_gandi_livedns_code" != "204" ]; then
|
||||||
|
_err "Error deleting record: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "Removing record $(__green "success")"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
_gandi_livedns_rest PUT \
|
_gandi_livedns_rest PUT \
|
||||||
"domains/$_domain/records/$_sub_domain/TXT" \
|
"domains/$_domain/records/$_sub_domain/TXT" \
|
||||||
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" &&
|
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" &&
|
||||||
|
|||||||
@@ -185,13 +185,14 @@ _get_root_by_getHosts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_namecheap_set_publicip() {
|
_namecheap_set_publicip() {
|
||||||
|
NAMECHEAP_SOURCEIP="${NAMECHEAP_SOURCEIP:-$(_readaccountconf_mutable NAMECHEAP_SOURCEIP)}"
|
||||||
|
|
||||||
if [ -z "$NAMECHEAP_SOURCEIP" ]; then
|
if [ -z "$NAMECHEAP_SOURCEIP" ]; then
|
||||||
_err "No Source IP specified for Namecheap API."
|
_err "No Source IP specified for Namecheap API."
|
||||||
_err "Use your public ip address or an url to retrieve it (e.g. https://ifconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
|
_err "Use your public ip address or an url to retrieve it (e.g. https://ifconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
_saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
|
_saveaccountconf_mutable NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
|
||||||
_debug sourceip "$NAMECHEAP_SOURCEIP"
|
_debug sourceip "$NAMECHEAP_SOURCEIP"
|
||||||
|
|
||||||
ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
|
ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
|
||||||
@@ -252,6 +253,8 @@ _namecheap_parse_host() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_namecheap_check_config() {
|
_namecheap_check_config() {
|
||||||
|
NAMECHEAP_API_KEY="${NAMECHEAP_API_KEY:-$(_readaccountconf_mutable NAMECHEAP_API_KEY)}"
|
||||||
|
NAMECHEAP_USERNAME="${NAMECHEAP_USERNAME:-$(_readaccountconf_mutable NAMECHEAP_USERNAME)}"
|
||||||
|
|
||||||
if [ -z "$NAMECHEAP_API_KEY" ]; then
|
if [ -z "$NAMECHEAP_API_KEY" ]; then
|
||||||
_err "No API key specified for Namecheap API."
|
_err "No API key specified for Namecheap API."
|
||||||
@@ -265,8 +268,8 @@ _namecheap_check_config() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_saveaccountconf NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY"
|
_saveaccountconf_mutable NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY"
|
||||||
_saveaccountconf NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME"
|
_saveaccountconf_mutable NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_primedns_info='PrimeDNS
|
||||||
|
Site: primedns.net
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_primedns
|
||||||
|
Options:
|
||||||
|
PRIMEDNS_API_KEY API Key. Scope acme:name:_acme-challenge.<hostname> for each name you issue (or dns:write)
|
||||||
|
OptionsAlt:
|
||||||
|
PRIMEDNS_API_URL API base URL. Optional, default https://api.primedns.net/v1
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/7330
|
||||||
|
Author: PrimeDNS <acme@primedns.net>
|
||||||
|
'
|
||||||
|
|
||||||
|
PRIMEDNS_Api_Default="https://api.primedns.net/v1"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_primedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_primedns_add() {
|
||||||
|
fulldomain=$(_idn "$1")
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
PRIMEDNS_API_KEY="${PRIMEDNS_API_KEY:-$(_readaccountconf_mutable PRIMEDNS_API_KEY)}"
|
||||||
|
if [ -z "$PRIMEDNS_API_KEY" ]; then
|
||||||
|
PRIMEDNS_API_KEY=""
|
||||||
|
_err "You didn't specify a PrimeDNS API key (PRIMEDNS_API_KEY) yet."
|
||||||
|
_err "Create one in the PrimeDNS panel (Account -> API Keys) with the scope acme:name:_acme-challenge.<hostname> for each name you issue."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PRIMEDNS_API_URL="${PRIMEDNS_API_URL:-$(_readaccountconf_mutable PRIMEDNS_API_URL)}"
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable PRIMEDNS_API_KEY "$PRIMEDNS_API_KEY"
|
||||||
|
if [ "$PRIMEDNS_API_URL" ]; then
|
||||||
|
_saveaccountconf_mutable PRIMEDNS_API_URL "$PRIMEDNS_API_URL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Using PrimeDNS"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _primedns_rest POST "$fulldomain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: dns_primedns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_primedns_rm() {
|
||||||
|
fulldomain=$(_idn "$1")
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
PRIMEDNS_API_KEY="${PRIMEDNS_API_KEY:-$(_readaccountconf_mutable PRIMEDNS_API_KEY)}"
|
||||||
|
if [ -z "$PRIMEDNS_API_KEY" ]; then
|
||||||
|
PRIMEDNS_API_KEY=""
|
||||||
|
_err "You didn't specify a PrimeDNS API key (PRIMEDNS_API_KEY) yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
PRIMEDNS_API_URL="${PRIMEDNS_API_URL:-$(_readaccountconf_mutable PRIMEDNS_API_URL)}"
|
||||||
|
|
||||||
|
_info "Using PrimeDNS"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _primedns_rest DELETE "$fulldomain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "Removed, OK"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
#Usage: _primedns_rest POST|DELETE _acme-challenge.www.domain.com txtvalue
|
||||||
|
#The API creates/removes the TXT record with exactly the name given, so the full
|
||||||
|
#name acme.sh passes (_acme-challenge.<domain>, or the name of a DNS alias) is
|
||||||
|
#sent as it is.
|
||||||
|
_primedns_rest() {
|
||||||
|
_method=$1
|
||||||
|
_name=$2
|
||||||
|
_value=$3
|
||||||
|
_url="${PRIMEDNS_API_URL:-$PRIMEDNS_Api_Default}/dns/acme-challenge"
|
||||||
|
|
||||||
|
#The body is built by hand, so refuse anything that would need JSON escaping.
|
||||||
|
case "$_name$_value" in
|
||||||
|
*\"* | *\\*)
|
||||||
|
_err "Refusing to send '$_name': it contains a quote or a backslash."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [ -z "$_name" ] || _is_idn "$_name"; then
|
||||||
|
_err "Cannot send '$_name': it is not a plain ASCII host name. For an IDN name install idn (acme.sh converts it to punycode) or pass the punycode name."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_body="{\"name\":\"$_name\",\"value\":\"$_value\"}"
|
||||||
|
_debug _body "$_body"
|
||||||
|
|
||||||
|
_primedns_retried=""
|
||||||
|
while true; do
|
||||||
|
#acme.sh sends _H1.._H5 with every later request of this process, so the API key
|
||||||
|
#header must not stay set once this call is done (it would reach other hosts).
|
||||||
|
_primedns_old_h1="$_H1"
|
||||||
|
export _H1="X-API-Key: $PRIMEDNS_API_KEY"
|
||||||
|
: >"$HTTP_HEADER"
|
||||||
|
response="$(_post "$_body" "$_url" "" "$_method" "application/json")"
|
||||||
|
_ret=$?
|
||||||
|
export _H1="$_primedns_old_h1"
|
||||||
|
_status=$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d '\r\n')
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Error calling the PrimeDNS API ($_method /dns/acme-challenge)."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
_debug response "$response"
|
||||||
|
_debug _status "$_status"
|
||||||
|
|
||||||
|
#Rate limited or temporary server error: wait and try once more.
|
||||||
|
if [ -z "$_primedns_retried" ] && { [ "$_status" = "429" ] || [ "$_status" = "502" ] || [ "$_status" = "503" ] || [ "$_status" = "504" ]; }; then
|
||||||
|
_wait="$(_retryafter_seconds <"$HTTP_HEADER")"
|
||||||
|
case "$_wait" in
|
||||||
|
'' | *[!0-9]*) _wait=5 ;;
|
||||||
|
esac
|
||||||
|
[ "$_wait" -gt 60 ] && _wait=60
|
||||||
|
_info "PrimeDNS API answered HTTP $_status, retrying in $_wait seconds."
|
||||||
|
_sleep "$_wait"
|
||||||
|
_primedns_retried=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
done
|
||||||
|
|
||||||
|
if _contains "$response" '"success":true'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_status" = "401" ]; then
|
||||||
|
_err "The PrimeDNS API refused the key (HTTP 401): it is wrong, revoked or expired. Check PRIMEDNS_API_KEY."
|
||||||
|
elif _contains "$response" 'IP_NOT_ALLOWED'; then
|
||||||
|
_err "This PrimeDNS API key may not be used from this address (allowed_ips). Add the address to the key or run acme.sh from an allowed host."
|
||||||
|
elif _contains "$response" 'NAME_NOT_ALLOWED'; then
|
||||||
|
_err "This PrimeDNS API key may not write $_name: add the scope acme:name:$_name to the key, or the zone to its allowed zones."
|
||||||
|
elif _contains "$response" 'INSUFFICIENT_SCOPE'; then
|
||||||
|
_err "This PrimeDNS API key needs the scope acme:name:$_name (or dns:write)."
|
||||||
|
elif _contains "$response" 'DOMAIN_NOT_FOUND'; then
|
||||||
|
_err "$_name is not in a zone of the PrimeDNS account that owns this API key."
|
||||||
|
elif [ "$_status" = "429" ]; then
|
||||||
|
_err "PrimeDNS API rate limit still hit after a retry. Try again in a minute."
|
||||||
|
else
|
||||||
|
_err "PrimeDNS API error (HTTP $_status): $response"
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
+6
-3
@@ -106,13 +106,16 @@ dns_yc_add() {
|
|||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
if ! _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
|
if ! _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$fulldomain"; then
|
||||||
_err "Error: $response"
|
_err "Error: $response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Adding record"
|
_info "Adding record"
|
||||||
if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
|
# The absolute name (trailing dot) also covers a record at the zone apex,
|
||||||
|
# e.g. with --domain-alias, which has no relative form: an empty name is
|
||||||
|
# rejected by the API.
|
||||||
|
if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
|
||||||
if _contains "$response" "\"done\": *true"; then
|
if _contains "$response" "\"done\": *true"; then
|
||||||
_info "Added, OK"
|
_info "Added, OK"
|
||||||
return 0
|
return 0
|
||||||
@@ -167,7 +170,7 @@ dns_yc_rm() {
|
|||||||
# upsertRecordSets.deletions removes only the given value from the rrset,
|
# upsertRecordSets.deletions removes only the given value from the rrset,
|
||||||
# leaving any other values at the same name (e.g. base + wildcard domain)
|
# leaving any other values at the same name (e.g. base + wildcard domain)
|
||||||
# intact -- no need to read the current data set and recompute it.
|
# intact -- no need to read the current data set and recompute it.
|
||||||
if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
|
if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"deletions\": [ { \"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
|
||||||
if _contains "$response" "\"done\": *true"; then
|
if _contains "$response" "\"done\": *true"; then
|
||||||
_info "Delete, OK"
|
_info "Delete, OK"
|
||||||
return 0
|
return 0
|
||||||
|
|||||||