Merge branch 'acmesh-official:dev' into dev
commit
a0fa04dcaa
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034,SC2154
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud CDN
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
|
||||||
|
#
|
||||||
|
# To specify the CDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com"
|
||||||
|
# If you have multiple CDN domains using the same certificate, just
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
|
||||||
|
#
|
||||||
|
# For DCDN, see ali_dcdn deploy hook
|
||||||
|
|
||||||
|
Ali_CDN_API="https://cdn.aliyuncs.com/"
|
||||||
|
|
||||||
|
ali_cdn_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"
|
||||||
|
|
||||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$dnsapi_ali"; then
|
||||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_prepare_ali_credentials || return 1
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ALI_CDN_DOMAIN
|
||||||
|
if [ "$DEPLOY_ALI_CDN_DOMAIN" ]; then
|
||||||
|
_savedeployconf DEPLOY_ALI_CDN_DOMAIN "$DEPLOY_ALI_CDN_DOMAIN"
|
||||||
|
else
|
||||||
|
DEPLOY_ALI_CDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain")
|
||||||
|
_key=$(_url_encode upper-hex <"$_ckey")
|
||||||
|
|
||||||
|
_debug2 _cert "$_cert"
|
||||||
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
for domain in $DEPLOY_ALI_CDN_DOMAIN; do
|
||||||
|
_set_cdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
|
||||||
|
if _ali_rest "Set CDN domain SSL certificate for $domain" "" POST; then
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_cdn_domain_ssl_certificate_query() {
|
||||||
|
endpoint=$Ali_CDN_API
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=SetCdnDomainSSLCertificate'
|
||||||
|
query=$query'&CertType=upload'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SSLPri='$3
|
||||||
|
query=$query'&SSLProtocol=on'
|
||||||
|
query=$query'&SSLPub='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Version=2018-05-10'
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034,SC2154
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud DCDN
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
|
||||||
|
#
|
||||||
|
# To specify the DCDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
|
||||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn.example.com"
|
||||||
|
# If you have multiple CDN domains using the same certificate, just
|
||||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn1.example.com dcdn2.example.com"
|
||||||
|
#
|
||||||
|
# For regular CDN, see ali_cdn deploy hook
|
||||||
|
|
||||||
|
Ali_DCDN_API="https://dcdn.aliyuncs.com/"
|
||||||
|
|
||||||
|
ali_dcdn_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"
|
||||||
|
|
||||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$dnsapi_ali"; then
|
||||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_prepare_ali_credentials || return 1
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ALI_DCDN_DOMAIN
|
||||||
|
if [ "$DEPLOY_ALI_DCDN_DOMAIN" ]; then
|
||||||
|
_savedeployconf DEPLOY_ALI_DCDN_DOMAIN "$DEPLOY_ALI_DCDN_DOMAIN"
|
||||||
|
else
|
||||||
|
DEPLOY_ALI_DCDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain")
|
||||||
|
_key=$(_url_encode upper-hex <"$_ckey")
|
||||||
|
|
||||||
|
_debug2 _cert "$_cert"
|
||||||
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
for domain in $DEPLOY_ALI_DCDN_DOMAIN; do
|
||||||
|
_set_dcdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
|
||||||
|
if _ali_rest "Set DCDN domain SSL certificate for $domain" "" POST; then
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_dcdn_domain_ssl_certificate_query() {
|
||||||
|
endpoint=$Ali_DCDN_API
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=SetDcdnDomainSSLCertificate'
|
||||||
|
query=$query'&CertType=upload'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SSLPri='$3
|
||||||
|
query=$query'&SSLProtocol=on'
|
||||||
|
query=$query'&SSLPub='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Version=2018-01-15'
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Deploy certificates to a proxmox backup server using the API.
|
||||||
|
#
|
||||||
|
# Environment variables that can be set are:
|
||||||
|
# `DEPLOY_PROXMOXBS_SERVER`: The hostname of the proxmox backup server. Defaults to
|
||||||
|
# _cdomain.
|
||||||
|
# `DEPLOY_PROXMOXBS_SERVER_PORT`: The port number the management interface is on.
|
||||||
|
# Defaults to 8007.
|
||||||
|
# `DEPLOY_PROXMOXBS_USER`: The user we'll connect as. Defaults to root.
|
||||||
|
# `DEPLOY_PROXMOXBS_USER_REALM`: The authentication realm the user authenticates
|
||||||
|
# with. Defaults to pam.
|
||||||
|
# `DEPLOY_PROXMOXBS_API_TOKEN_NAME`: The name of the API token created for the
|
||||||
|
# user account. Defaults to acme.
|
||||||
|
# `DEPLOY_PROXMOXBS_API_TOKEN_KEY`: The API token. Required.
|
||||||
|
|
||||||
|
proxmoxbs_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug2 _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# "Sane" defaults.
|
||||||
|
_getdeployconf DEPLOY_PROXMOXBS_SERVER
|
||||||
|
if [ -z "$DEPLOY_PROXMOXBS_SERVER" ]; then
|
||||||
|
_target_hostname="$_cdomain"
|
||||||
|
else
|
||||||
|
_target_hostname="$DEPLOY_PROXMOXBS_SERVER"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXBS_SERVER "$DEPLOY_PROXMOXBS_SERVER"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXBS_SERVER "$_target_hostname"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXBS_SERVER_PORT
|
||||||
|
if [ -z "$DEPLOY_PROXMOXBS_SERVER_PORT" ]; then
|
||||||
|
_target_port="8007"
|
||||||
|
else
|
||||||
|
_target_port="$DEPLOY_PROXMOXBS_SERVER_PORT"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXBS_SERVER_PORT "$DEPLOY_PROXMOXBS_SERVER_PORT"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXBS_SERVER_PORT "$_target_port"
|
||||||
|
|
||||||
|
# Complete URL.
|
||||||
|
_target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/localhost/certificates/custom"
|
||||||
|
_debug TARGET_URL "$_target_url"
|
||||||
|
|
||||||
|
# More "sane" defaults.
|
||||||
|
_getdeployconf DEPLOY_PROXMOXBS_USER
|
||||||
|
if [ -z "$DEPLOY_PROXMOXBS_USER" ]; then
|
||||||
|
_proxmoxbs_user="root"
|
||||||
|
else
|
||||||
|
_proxmoxbs_user="$DEPLOY_PROXMOXBS_USER"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXBS_USER "$DEPLOY_PROXMOXBS_USER"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXBS_USER "$_proxmoxbs_user"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXBS_USER_REALM
|
||||||
|
if [ -z "$DEPLOY_PROXMOXBS_USER_REALM" ]; then
|
||||||
|
_proxmoxbs_user_realm="pam"
|
||||||
|
else
|
||||||
|
_proxmoxbs_user_realm="$DEPLOY_PROXMOXBS_USER_REALM"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXBS_USER_REALM "$DEPLOY_PROXMOXBS_USER_REALM"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXBS_USER_REALM "$_proxmoxbs_user_realm"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME
|
||||||
|
if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_NAME" ]; then
|
||||||
|
_proxmoxbs_api_token_name="acme"
|
||||||
|
else
|
||||||
|
_proxmoxbs_api_token_name="$DEPLOY_PROXMOXBS_API_TOKEN_NAME"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME "$DEPLOY_PROXMOXBS_API_TOKEN_NAME"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXBS_API_TOKEN_NAME "$_proxmoxbs_api_token_name"
|
||||||
|
|
||||||
|
# This is required.
|
||||||
|
_getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY
|
||||||
|
if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_KEY" ]; then
|
||||||
|
_err "API key not provided."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_proxmoxbs_api_token_key="$DEPLOY_PROXMOXBS_API_TOKEN_KEY"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY "$DEPLOY_PROXMOXBS_API_TOKEN_KEY"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXBS_API_TOKEN_KEY "$_proxmoxbs_api_token_key"
|
||||||
|
|
||||||
|
# PBS API Token header value. Used in "Authorization: PBSAPIToken".
|
||||||
|
_proxmoxbs_header_api_token="${_proxmoxbs_user}@${_proxmoxbs_user_realm}!${_proxmoxbs_api_token_name}:${_proxmoxbs_api_token_key}"
|
||||||
|
_debug2 "Auth Header" "$_proxmoxbs_header_api_token"
|
||||||
|
|
||||||
|
# Ugly. I hate putting heredocs inside functions because heredocs don't
|
||||||
|
# account for whitespace correctly but it _does_ work and is several times
|
||||||
|
# cleaner than anything else I had here.
|
||||||
|
#
|
||||||
|
# This dumps the json payload to a variable that should be passable to the
|
||||||
|
# _psot function.
|
||||||
|
_json_payload=$(
|
||||||
|
cat <<HEREDOC
|
||||||
|
{
|
||||||
|
"certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')",
|
||||||
|
"key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')",
|
||||||
|
"node":"localhost",
|
||||||
|
"restart":true,
|
||||||
|
"force":true
|
||||||
|
}
|
||||||
|
HEREDOC
|
||||||
|
)
|
||||||
|
_debug2 Payload "$_json_payload"
|
||||||
|
|
||||||
|
_info "Push certificates to server"
|
||||||
|
export HTTPS_INSECURE=1
|
||||||
|
export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}"
|
||||||
|
_post "$_json_payload" "$_target_url" "" POST "application/json"
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to Ruckus ZoneDirector / Unleashed.
|
||||||
|
#
|
||||||
|
# Public domain, 2024, Tony Rielly <https://github.com/ms264556>
|
||||||
|
#
|
||||||
|
# ```sh
|
||||||
|
# acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# Then you need to set the environment variables for the
|
||||||
|
# deploy script to work.
|
||||||
|
#
|
||||||
|
# ```sh
|
||||||
|
# export RUCKUS_HOST=myruckus.example.com
|
||||||
|
# export RUCKUS_USER=myruckususername
|
||||||
|
# export RUCKUS_PASS=myruckuspassword
|
||||||
|
#
|
||||||
|
# acme.sh --deploy -d myruckus.example.com --deploy-hook ruckus
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
ruckus_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
_err_code=0
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf RUCKUS_HOST
|
||||||
|
_getdeployconf RUCKUS_USER
|
||||||
|
_getdeployconf RUCKUS_PASS
|
||||||
|
|
||||||
|
if [ -z "$RUCKUS_HOST" ]; then
|
||||||
|
_debug "Using _cdomain as RUCKUS_HOST, please set if not correct."
|
||||||
|
RUCKUS_HOST="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$RUCKUS_USER" ]; then
|
||||||
|
_err "Need to set the env variable RUCKUS_USER"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$RUCKUS_PASS" ]; then
|
||||||
|
_err "Need to set the env variable RUCKUS_PASS"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf RUCKUS_HOST "$RUCKUS_HOST"
|
||||||
|
_savedeployconf RUCKUS_USER "$RUCKUS_USER"
|
||||||
|
_savedeployconf RUCKUS_PASS "$RUCKUS_PASS"
|
||||||
|
|
||||||
|
_debug RUCKUS_HOST "$RUCKUS_HOST"
|
||||||
|
_debug RUCKUS_USER "$RUCKUS_USER"
|
||||||
|
_secure_debug RUCKUS_PASS "$RUCKUS_PASS"
|
||||||
|
|
||||||
|
export ACME_HTTP_NO_REDIRECTS=1
|
||||||
|
|
||||||
|
_info "Discovering the login URL"
|
||||||
|
_get "https://$RUCKUS_HOST" >/dev/null
|
||||||
|
_login_url="$(_response_header 'Location')"
|
||||||
|
if [ -n "$_login_url" ]; then
|
||||||
|
_login_path=$(echo "$_login_url" | sed 's|https\?://[^/]\+||')
|
||||||
|
if [ -z "$_login_path" ]; then
|
||||||
|
# redirect was to a different host
|
||||||
|
_err "Connection failed: redirected to a different host. Configure Unleashed with a Preferred Master or Management Interface."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${_login_url}" ]; then
|
||||||
|
_err "Connection failed: couldn't find login page."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_base_url=$(dirname "$_login_url")
|
||||||
|
_login_page=$(basename "$_login_url")
|
||||||
|
|
||||||
|
if [ "$_login_page" = "index.html" ]; then
|
||||||
|
_err "Connection temporarily unavailable: Unleashed Rebuilding."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_login_page" = "wizard.jsp" ]; then
|
||||||
|
_err "Connection failed: Setup Wizard not complete."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Login"
|
||||||
|
_username_encoded="$(printf "%s" "$RUCKUS_USER" | _url_encode)"
|
||||||
|
_password_encoded="$(printf "%s" "$RUCKUS_PASS" | _url_encode)"
|
||||||
|
_login_query="$(printf "%s" "username=${_username_encoded}&password=${_password_encoded}&ok=Log+In")"
|
||||||
|
_post "$_login_query" "$_login_url" >/dev/null
|
||||||
|
|
||||||
|
_login_code="$(_response_code)"
|
||||||
|
if [ "$_login_code" = "200" ]; then
|
||||||
|
_err "Login failed: incorrect credentials."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Collect Session Cookie"
|
||||||
|
_H1="Cookie: $(_response_cookie)"
|
||||||
|
export _H1
|
||||||
|
_info "Collect CSRF Token"
|
||||||
|
_H2="X-CSRF-Token: $(_response_header 'HTTP_X_CSRF_TOKEN')"
|
||||||
|
export _H2
|
||||||
|
|
||||||
|
_info "Uploading certificate"
|
||||||
|
_post_upload "uploadcert" "$_cfullchain"
|
||||||
|
|
||||||
|
_info "Uploading private key"
|
||||||
|
_post_upload "uploadprivatekey" "$_ckey"
|
||||||
|
|
||||||
|
_info "Replacing certificate"
|
||||||
|
_replace_cert_ajax='<ajax-request action="docmd" comp="system" updater="rid.0.5" xcmd="replace-cert" checkAbility="6" timeout="-1"><xcmd cmd="replace-cert" cn="'$RUCKUS_HOST'"/></ajax-request>'
|
||||||
|
_post "$_replace_cert_ajax" "$_base_url/_cmdstat.jsp" >/dev/null
|
||||||
|
|
||||||
|
_info "Rebooting"
|
||||||
|
_cert_reboot_ajax='<ajax-request action="docmd" comp="worker" updater="rid.0.5" xcmd="cert-reboot" checkAbility="6"><xcmd cmd="cert-reboot" action="undefined"/></ajax-request>'
|
||||||
|
_post "$_cert_reboot_ajax" "$_base_url/_cmdstat.jsp" >/dev/null
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_response_code() {
|
||||||
|
_egrep_o <"$HTTP_HEADER" "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n" | _egrep_o "^[0-9]*"
|
||||||
|
}
|
||||||
|
|
||||||
|
_response_header() {
|
||||||
|
grep <"$HTTP_HEADER" -i "^$1:" | cut -d ':' -f 2- | tr -d "\r\n\t "
|
||||||
|
}
|
||||||
|
|
||||||
|
_response_cookie() {
|
||||||
|
_response_header 'Set-Cookie' | sed 's/;.*//'
|
||||||
|
}
|
||||||
|
|
||||||
|
_post_upload() {
|
||||||
|
_post_action="$1"
|
||||||
|
_post_file="$2"
|
||||||
|
|
||||||
|
_post_boundary="----FormBoundary$(date "+%s%N")"
|
||||||
|
|
||||||
|
_post_data="$({
|
||||||
|
printf -- "--%s\r\n" "$_post_boundary"
|
||||||
|
printf -- "Content-Disposition: form-data; name=\"u\"; filename=\"%s\"\r\n" "$_post_action"
|
||||||
|
printf -- "Content-Type: application/octet-stream\r\n\r\n"
|
||||||
|
printf -- "%s\r\n" "$(cat "$_post_file")"
|
||||||
|
|
||||||
|
printf -- "--%s\r\n" "$_post_boundary"
|
||||||
|
printf -- "Content-Disposition: form-data; name=\"action\"\r\n\r\n"
|
||||||
|
printf -- "%s\r\n" "$_post_action"
|
||||||
|
|
||||||
|
printf -- "--%s\r\n" "$_post_boundary"
|
||||||
|
printf -- "Content-Disposition: form-data; name=\"callback\"\r\n\r\n"
|
||||||
|
printf -- "%s\r\n" "uploader_$_post_action"
|
||||||
|
|
||||||
|
printf -- "--%s--\r\n\r\n" "$_post_boundary"
|
||||||
|
})"
|
||||||
|
|
||||||
|
_post "$_post_data" "$_base_url/_upload.jsp?request_type=xhr" "" "" "multipart/form-data; boundary=$_post_boundary" >/dev/null
|
||||||
|
}
|
@ -0,0 +1,294 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# TrueNAS deploy script for SCALE/CORE using websocket
|
||||||
|
# It is recommend to use a wildcard certificate
|
||||||
|
#
|
||||||
|
# Websocket Documentation: https://www.truenas.com/docs/api/scale_websocket_api.html
|
||||||
|
#
|
||||||
|
# Tested with TrueNAS Scale - Electric Eel 24.10
|
||||||
|
# Changes certificate in the following services:
|
||||||
|
# - Web UI
|
||||||
|
# - FTP
|
||||||
|
# - iX Apps
|
||||||
|
#
|
||||||
|
# The following environment variables must be set:
|
||||||
|
# ------------------------------------------------
|
||||||
|
#
|
||||||
|
# # API KEY
|
||||||
|
# # Use the folowing URL to create a new API token: <TRUENAS_HOSTNAME OR IP>/ui/apikeys
|
||||||
|
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
|
||||||
|
#
|
||||||
|
|
||||||
|
### Private functions
|
||||||
|
|
||||||
|
# Call websocket method
|
||||||
|
# Usage:
|
||||||
|
# _ws_response=$(_ws_call "math.dummycalc" "'{"x": 4, "y": 5}'")
|
||||||
|
# _info "$_ws_response"
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# {"z": 9}
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# $@ - midclt arguments for call
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# JSON/JOBID
|
||||||
|
_ws_call() {
|
||||||
|
_debug "_ws_call arg1" "$1"
|
||||||
|
_debug "_ws_call arg2" "$2"
|
||||||
|
_debug "_ws_call arg3" "$3"
|
||||||
|
if [ $# -eq 3 ]; then
|
||||||
|
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2" "$3")
|
||||||
|
fi
|
||||||
|
if [ $# -eq 2 ]; then
|
||||||
|
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2")
|
||||||
|
fi
|
||||||
|
if [ $# -eq 1 ]; then
|
||||||
|
_ws_response=$(midclt -K "$DEPLOY_TRUENAS_APIKEY" call "$1")
|
||||||
|
fi
|
||||||
|
_debug "_ws_response" "$_ws_response"
|
||||||
|
printf "%s" "$_ws_response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check argument is a number
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# n/a
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# $1 - Anything
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# 0: true
|
||||||
|
# 1: false
|
||||||
|
_ws_check_jobid() {
|
||||||
|
case "$1" in
|
||||||
|
[0-9]*)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wait for job to finish and return result as JSON
|
||||||
|
# Usage:
|
||||||
|
# _ws_result=$(_ws_get_job_result "$_ws_jobid")
|
||||||
|
# _new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"')
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# JSON result of the job
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# $1 - JobID
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# n/a
|
||||||
|
_ws_get_job_result() {
|
||||||
|
while true; do
|
||||||
|
sleep 2
|
||||||
|
_ws_response=$(_ws_call "core.get_jobs" "[[\"id\", \"=\", $1]]")
|
||||||
|
if [ "$(printf "%s" "$_ws_response" | jq -r '.[]."state"')" != "RUNNING" ]; then
|
||||||
|
_ws_result="$(printf "%s" "$_ws_response" | jq '.[]."result"')"
|
||||||
|
_debug "_ws_result" "$_ws_result"
|
||||||
|
printf "%s" "$_ws_result"
|
||||||
|
_ws_error="$(printf "%s" "$_ws_response" | jq '.[]."error"')"
|
||||||
|
if [ "$_ws_error" != "null" ]; then
|
||||||
|
_err "Job $1 failed:"
|
||||||
|
_err "$_ws_error"
|
||||||
|
return 7
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
########################
|
||||||
|
### Public functions ###
|
||||||
|
########################
|
||||||
|
|
||||||
|
# truenas_ws_deploy
|
||||||
|
#
|
||||||
|
# Deploy new certificate to TrueNAS services
|
||||||
|
#
|
||||||
|
# Arguments
|
||||||
|
# 1: Domain
|
||||||
|
# 2: Key-File
|
||||||
|
# 3: Certificate-File
|
||||||
|
# 4: CA-File
|
||||||
|
# 5: FullChain-File
|
||||||
|
# Returns:
|
||||||
|
# 0: Success
|
||||||
|
# 1: Missing API Key
|
||||||
|
# 2: TrueNAS not ready
|
||||||
|
# 3: Not a JobID
|
||||||
|
# 4: FTP cert error
|
||||||
|
# 5: WebUI cert error
|
||||||
|
# 6: Job error
|
||||||
|
# 7: WS call error
|
||||||
|
# 10: No CORE or SCALE detected
|
||||||
|
#
|
||||||
|
truenas_ws_deploy() {
|
||||||
|
_domain="$1"
|
||||||
|
_file_key="$2"
|
||||||
|
_file_cert="$3"
|
||||||
|
_file_ca="$4"
|
||||||
|
_file_fullchain="$5"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _file_key "$_file_key"
|
||||||
|
_debug _file_cert "$_file_cert"
|
||||||
|
_debug _file_ca "$_file_ca"
|
||||||
|
_debug _file_fullchain "$_file_fullchain"
|
||||||
|
|
||||||
|
########## Environment check
|
||||||
|
|
||||||
|
_info "Checking environment variables..."
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_APIKEY
|
||||||
|
# Check API Key
|
||||||
|
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
|
||||||
|
_err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_info "Environment variables: OK"
|
||||||
|
|
||||||
|
########## Health check
|
||||||
|
|
||||||
|
_info "Checking TrueNAS health..."
|
||||||
|
_ws_response=$(_ws_call "system.ready" | tr '[:lower:]' '[:upper:]')
|
||||||
|
_ws_ret=$?
|
||||||
|
if [ $_ws_ret -gt 0 ]; then
|
||||||
|
_err "Error calling system.ready:"
|
||||||
|
_err "$_ws_response"
|
||||||
|
return $_ws_ret
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_ws_response" != "TRUE" ]; then
|
||||||
|
_err "TrueNAS is not ready."
|
||||||
|
_err "Please check environment variables DEPLOY_TRUENAS_APIKEY, DEPLOY_TRUENAS_HOSTNAME and DEPLOY_TRUENAS_PROTOCOL."
|
||||||
|
_err "Verify API key."
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_info "TrueNAS health: OK"
|
||||||
|
|
||||||
|
########## System info
|
||||||
|
|
||||||
|
_info "Gather system info..."
|
||||||
|
_ws_response=$(_ws_call "system.info")
|
||||||
|
_truenas_system=$(printf "%s" "$_ws_response" | jq -r '."version"' | cut -d '-' -f 2 | tr '[:lower:]' '[:upper:]')
|
||||||
|
_truenas_version=$(printf "%s" "$_ws_response" | jq -r '."version"' | cut -d '-' -f 3)
|
||||||
|
_info "TrueNAS system: $_truenas_system"
|
||||||
|
_info "TrueNAS version: $_truenas_version"
|
||||||
|
if [ "$_truenas_system" != "SCALE" ] && [ "$_truenas_system" != "CORE" ]; then
|
||||||
|
_err "Cannot gather TrueNAS system. Nor CORE oder SCALE detected."
|
||||||
|
return 10
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## Gather current certificate
|
||||||
|
|
||||||
|
_info "Gather current WebUI certificate..."
|
||||||
|
_ws_response="$(_ws_call "system.general.config")"
|
||||||
|
_ui_certificate_id=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"')
|
||||||
|
_ui_certificate_name=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."name"')
|
||||||
|
_info "Current WebUI certificate ID: $_ui_certificate_id"
|
||||||
|
_info "Current WebUI certificate name: $_ui_certificate_name"
|
||||||
|
|
||||||
|
########## Upload new certificate
|
||||||
|
|
||||||
|
_info "Upload new certificate..."
|
||||||
|
_certname="acme_$(_utc_date | tr -d '\-\:' | tr ' ' '_')"
|
||||||
|
_info "New WebUI certificate name: $_certname"
|
||||||
|
_debug _certname "$_certname"
|
||||||
|
_ws_jobid=$(_ws_call "certificate.create" "{\"name\": \"${_certname}\", \"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"certificate\": \"$(_json_encode <"$_file_fullchain")\", \"privatekey\": \"$(_json_encode <"$_file_key")\", \"passphrase\": \"\"}")
|
||||||
|
_debug "_ws_jobid" "$_ws_jobid"
|
||||||
|
if ! _ws_check_jobid "$_ws_jobid"; then
|
||||||
|
_err "No JobID returned from websocket method."
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
_ws_result=$(_ws_get_job_result "$_ws_jobid")
|
||||||
|
_ws_ret=$?
|
||||||
|
if [ $_ws_ret -gt 0 ]; then
|
||||||
|
return $_ws_ret
|
||||||
|
fi
|
||||||
|
_debug "_ws_result" "$_ws_result"
|
||||||
|
_new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"')
|
||||||
|
_info "New certificate ID: $_new_certid"
|
||||||
|
|
||||||
|
########## FTP
|
||||||
|
|
||||||
|
_info "Replace FTP certificate..."
|
||||||
|
_ws_response=$(_ws_call "ftp.update" "{\"ssltls_certificate\": $_new_certid}")
|
||||||
|
_ftp_certid=$(printf "%s" "$_ws_response" | jq -r '."ssltls_certificate"')
|
||||||
|
if [ "$_ftp_certid" != "$_new_certid" ]; then
|
||||||
|
_err "Cannot set FTP certificate."
|
||||||
|
_debug "_ws_response" "$_ws_response"
|
||||||
|
return 4
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## ix Apps (SCALE only)
|
||||||
|
|
||||||
|
if [ "$_truenas_system" = "SCALE" ]; then
|
||||||
|
_info "Replace app certificates..."
|
||||||
|
_ws_response=$(_ws_call "app.query")
|
||||||
|
for _app_name in $(printf "%s" "$_ws_response" | jq -r '.[]."name"'); do
|
||||||
|
_info "Checking app $_app_name..."
|
||||||
|
_ws_response=$(_ws_call "app.config" "$_app_name")
|
||||||
|
if [ "$(printf "%s" "$_ws_response" | jq -r '."network" | has("certificate_id")')" = "true" ]; then
|
||||||
|
_info "App has certificate option, setup new certificate..."
|
||||||
|
_info "App will be redeployed after updating the certificate."
|
||||||
|
_ws_jobid=$(_ws_call "app.update" "$_app_name" "{\"values\": {\"network\": {\"certificate_id\": $_new_certid}}}")
|
||||||
|
_debug "_ws_jobid" "$_ws_jobid"
|
||||||
|
if ! _ws_check_jobid "$_ws_jobid"; then
|
||||||
|
_err "No JobID returned from websocket method."
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
_ws_result=$(_ws_get_job_result "$_ws_jobid")
|
||||||
|
_ws_ret=$?
|
||||||
|
if [ $_ws_ret -gt 0 ]; then
|
||||||
|
return $_ws_ret
|
||||||
|
fi
|
||||||
|
_debug "_ws_result" "$_ws_result"
|
||||||
|
_info "App certificate replaced."
|
||||||
|
else
|
||||||
|
_info "App has no certificate option, skipping..."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## WebUI
|
||||||
|
|
||||||
|
_info "Replace WebUI certificate..."
|
||||||
|
_ws_response=$(_ws_call "system.general.update" "{\"ui_certificate\": $_new_certid}")
|
||||||
|
_changed_certid=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"')
|
||||||
|
if [ "$_changed_certid" != "$_new_certid" ]; then
|
||||||
|
_err "WebUI certificate change error.."
|
||||||
|
return 5
|
||||||
|
else
|
||||||
|
_info "WebUI certificate replaced."
|
||||||
|
fi
|
||||||
|
_info "Restarting WebUI..."
|
||||||
|
_ws_response=$(_ws_call "system.general.ui_restart")
|
||||||
|
_info "Waiting for UI restart..."
|
||||||
|
sleep 6
|
||||||
|
|
||||||
|
########## Certificates
|
||||||
|
|
||||||
|
_info "Deleting old certificate..."
|
||||||
|
_ws_jobid=$(_ws_call "certificate.delete" "$_ui_certificate_id")
|
||||||
|
if ! _ws_check_jobid "$_ws_jobid"; then
|
||||||
|
_err "No JobID returned from websocket method."
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
_ws_result=$(_ws_get_job_result "$_ws_jobid")
|
||||||
|
_ws_ret=$?
|
||||||
|
if [ $_ws_ret -gt 0 ]; then
|
||||||
|
return $_ws_ret
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Have a nice day...bye!"
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_alviy_info='Alviy.com
|
||||||
|
Site: Alviy.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_alviy
|
||||||
|
Options:
|
||||||
|
Alviy_token API token. Get it from the https://cloud.alviy.com/token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/5115
|
||||||
|
'
|
||||||
|
|
||||||
|
Alviy_Api="https://cloud.alviy.com/api/v1"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_alviy_add _acme-challenge.www.domain.com "content"
|
||||||
|
dns_alviy_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
|
||||||
|
if [ -z "$Alviy_token" ]; then
|
||||||
|
Alviy_token=""
|
||||||
|
_err "Please specify Alviy token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable Alviy_token "$Alviy_token"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Getting existing records"
|
||||||
|
if _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_info "This record already exists, skipping"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_add_data="{\"content\":\"$txtvalue\",\"type\":\"TXT\"}"
|
||||||
|
_debug2 _add_data "$_add_data"
|
||||||
|
_info "Adding record"
|
||||||
|
if _alviy_rest POST "zone/$_domain/domain/$fulldomain/" "$_add_data"; then
|
||||||
|
_debug "Checking updated records of '${fulldomain}'"
|
||||||
|
|
||||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
_err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_sleep 10
|
||||||
|
_info "Added TXT record '${txtvalue}' for '${fulldomain}'."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain
|
||||||
|
dns_alviy_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_info "The record does not exist, skip"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_add_data=""
|
||||||
|
uuid=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | tr "," "\n" | grep uuid | cut -d \" -f4)
|
||||||
|
# delete record
|
||||||
|
_debug "Delete TXT record for '${fulldomain}'"
|
||||||
|
if ! _alviy_rest DELETE "zone/$_domain/record/$uuid" "{\"confirm\":1}"; then
|
||||||
|
_err "Cannot delete empty TXT record for '$fulldomain'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "The record '$fulldomain'='$txtvalue' deleted"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=3
|
||||||
|
a="init"
|
||||||
|
while [ -n "$a" ]; do
|
||||||
|
a=$(printf "%s" "$domain" | cut -d . -f $i-)
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
n=$((i - 3))
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $n-)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
_alviy_rest GET "zone/$domain/"
|
||||||
|
_debug "can't get host from $domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _alviy_rest GET "zone/$h/"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" '"code":"NOT_FOUND"'; then
|
||||||
|
_debug "$h not found"
|
||||||
|
else
|
||||||
|
s=$((n - 1))
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f -$s)
|
||||||
|
_domain="$h"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_alviy_txt_exists() {
|
||||||
|
zone=$1
|
||||||
|
domain=$2
|
||||||
|
content_data=$3
|
||||||
|
_debug "Getting existing records"
|
||||||
|
|
||||||
|
if ! _alviy_rest GET "zone/$zone/domain/$domain/TXT/"; then
|
||||||
|
_info "The record does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$3"; then
|
||||||
|
_info "The record has other value"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# GOOD code return - TRUE function
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_alviy_rest() {
|
||||||
|
method=$1
|
||||||
|
path="$2"
|
||||||
|
content_data="$3"
|
||||||
|
_debug "$path"
|
||||||
|
|
||||||
|
export _H1="Authorization: Bearer $Alviy_token"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$content_data" ] || [ "$method" = "DELETE" ]; then
|
||||||
|
_debug "data ($method): " "$content_data"
|
||||||
|
response="$(_post "$content_data" "$Alviy_Api/$path" "" "$method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$Alviy_Api/$path")"
|
||||||
|
fi
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
if [ "$_code" = "401" ]; then
|
||||||
|
_err "It seems that your api key or secret is not correct."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" != "200" ]; then
|
||||||
|
_err "API call error ($method): $path Response code $_code"
|
||||||
|
fi
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error on rest call ($method): $path. Response:"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
@ -0,0 +1,281 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_beget_info='Beget.com
|
||||||
|
Site: Beget.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_beget
|
||||||
|
Options:
|
||||||
|
BEGET_User API user
|
||||||
|
BEGET_Password API password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/6200
|
||||||
|
Author: ARNik arnik@arnik.ru
|
||||||
|
'
|
||||||
|
|
||||||
|
Beget_Api="https://api.beget.com/api"
|
||||||
|
|
||||||
|
#################### Public functions ####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_beget_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_debug "dns_beget_add() $fulldomain $txtvalue"
|
||||||
|
fulldomain=$(echo "$fulldomain" | _lower_case)
|
||||||
|
|
||||||
|
Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}"
|
||||||
|
Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}"
|
||||||
|
|
||||||
|
if [ -z "$Beget_Username" ] || [ -z "$Beget_Password" ]; then
|
||||||
|
Beget_Username=""
|
||||||
|
Beget_Password=""
|
||||||
|
_err "You must export variables: Beget_Username, and Beget_Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable Beget_Username "$Beget_Username"
|
||||||
|
_saveaccountconf_mutable Beget_Password "$Beget_Password"
|
||||||
|
|
||||||
|
_info "Prepare subdomain."
|
||||||
|
if ! _prepare_subdomain "$fulldomain"; then
|
||||||
|
_err "Can't prepare subdomain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Get domain records"
|
||||||
|
data="{\"fqdn\":\"$fulldomain\"}"
|
||||||
|
res=$(_api_call "$Beget_Api/dns/getData" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't get domain records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Add new TXT record"
|
||||||
|
data="{\"fqdn\":\"$fulldomain\",\"records\":{"
|
||||||
|
data=${data}$(_parce_records "$res" "A")
|
||||||
|
data=${data}$(_parce_records "$res" "AAAA")
|
||||||
|
data=${data}$(_parce_records "$res" "CAA")
|
||||||
|
data=${data}$(_parce_records "$res" "MX")
|
||||||
|
data=${data}$(_parce_records "$res" "SRV")
|
||||||
|
data=${data}$(_parce_records "$res" "TXT")
|
||||||
|
data=$(echo "$data" | sed 's/,$//')
|
||||||
|
data=${data}'}}'
|
||||||
|
|
||||||
|
str=$(_txt_to_dns_json "$txtvalue")
|
||||||
|
data=$(_add_record "$data" "TXT" "$str")
|
||||||
|
|
||||||
|
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't change domain records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_beget_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_debug "dns_beget_rm() $fulldomain $txtvalue"
|
||||||
|
fulldomain=$(echo "$fulldomain" | _lower_case)
|
||||||
|
|
||||||
|
Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}"
|
||||||
|
Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}"
|
||||||
|
|
||||||
|
_info "Get current domain records"
|
||||||
|
data="{\"fqdn\":\"$fulldomain\"}"
|
||||||
|
res=$(_api_call "$Beget_Api/dns/getData" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't get domain records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Remove TXT record"
|
||||||
|
data="{\"fqdn\":\"$fulldomain\",\"records\":{"
|
||||||
|
data=${data}$(_parce_records "$res" "A")
|
||||||
|
data=${data}$(_parce_records "$res" "AAAA")
|
||||||
|
data=${data}$(_parce_records "$res" "CAA")
|
||||||
|
data=${data}$(_parce_records "$res" "MX")
|
||||||
|
data=${data}$(_parce_records "$res" "SRV")
|
||||||
|
data=${data}$(_parce_records "$res" "TXT")
|
||||||
|
data=$(echo "$data" | sed 's/,$//')
|
||||||
|
data=${data}'}}'
|
||||||
|
|
||||||
|
str=$(_txt_to_dns_json "$txtvalue")
|
||||||
|
data=$(_rm_record "$data" "$str")
|
||||||
|
|
||||||
|
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't change domain records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ####################
|
||||||
|
|
||||||
|
# Create subdomain if needed
|
||||||
|
# Usage: _prepare_subdomain [fulldomain]
|
||||||
|
_prepare_subdomain() {
|
||||||
|
fulldomain=$1
|
||||||
|
|
||||||
|
_info "Detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
if [ -z "$_sub_domain" ]; then
|
||||||
|
_debug "$fulldomain is a root domain."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Get subdomain list"
|
||||||
|
res=$(_api_call "$Beget_Api/domain/getSubdomainList")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't get subdomain list."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$res" "\"fqdn\":\"$fulldomain\""; then
|
||||||
|
_debug "Subdomain $fulldomain already exist."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Subdomain $fulldomain does not exist. Let's create one."
|
||||||
|
data="{\"subdomain\":\"$_sub_domain\",\"domain_id\":$_domain_id}"
|
||||||
|
res=$(_api_call "$Beget_Api/domain/addSubdomainVirtual" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't create subdomain."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Cleanup subdomen records"
|
||||||
|
data="{\"fqdn\":\"$fulldomain\",\"records\":{}}"
|
||||||
|
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_debug "Can't cleanup $fulldomain records."
|
||||||
|
fi
|
||||||
|
|
||||||
|
data="{\"fqdn\":\"www.$fulldomain\",\"records\":{}}"
|
||||||
|
res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_debug "Can't cleanup www.$fulldomain records."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _get_root _acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=32436365
|
||||||
|
_get_root() {
|
||||||
|
fulldomain=$1
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
|
||||||
|
_debug "Get domain list"
|
||||||
|
res=$(_api_call "$Beget_Api/domain/getList")
|
||||||
|
if ! _is_api_reply_ok "$res"; then
|
||||||
|
_err "Can't get domain list."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100)
|
||||||
|
_debug h "$h"
|
||||||
|
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$res" "$h"; then
|
||||||
|
_domain_id=$(echo "$res" | _egrep_o "\"id\":[0-9]*,\"fqdn\":\"$h\"" | cut -d , -f1 | cut -d : -f2)
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
if [ "$h" != "$fulldomain" ]; then
|
||||||
|
_sub_domain=$(echo "$fulldomain" | cut -d . -f 1-"$p")
|
||||||
|
else
|
||||||
|
_sub_domain=""
|
||||||
|
fi
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p="$i"
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parce DNS records from json string
|
||||||
|
# Usage: _parce_records [j_str] [record_name]
|
||||||
|
_parce_records() {
|
||||||
|
j_str=$1
|
||||||
|
record_name=$2
|
||||||
|
res="\"$record_name\":["
|
||||||
|
res=${res}$(echo "$j_str" | _egrep_o "\"$record_name\":\[.*" | cut -d '[' -f2 | cut -d ']' -f1)
|
||||||
|
res=${res}"],"
|
||||||
|
echo "$res"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _add_record [data] [record_name] [record_data]
|
||||||
|
_add_record() {
|
||||||
|
data=$1
|
||||||
|
record_name=$2
|
||||||
|
record_data=$3
|
||||||
|
echo "$data" | sed "s/\"$record_name\":\[/\"$record_name\":\[$record_data,/" | sed "s/,\]/\]/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _rm_record [data] [record_data]
|
||||||
|
_rm_record() {
|
||||||
|
data=$1
|
||||||
|
record_data=$2
|
||||||
|
echo "$data" | sed "s/$record_data//g" | sed "s/,\+/,/g" |
|
||||||
|
sed "s/{,/{/g" | sed "s/,}/}/g" |
|
||||||
|
sed "s/\[,/\[/g" | sed "s/,\]/\]/g"
|
||||||
|
}
|
||||||
|
|
||||||
|
_txt_to_dns_json() {
|
||||||
|
echo "{\"ttl\":600,\"txtdata\":\"$1\"}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _api_call [api_url] [input_data]
|
||||||
|
_api_call() {
|
||||||
|
api_url="$1"
|
||||||
|
input_data="$2"
|
||||||
|
|
||||||
|
_debug "_api_call $api_url"
|
||||||
|
_debug "Request: $input_data"
|
||||||
|
|
||||||
|
# res=$(curl -s -L -D ./http.header \
|
||||||
|
# "$api_url" \
|
||||||
|
# --data-urlencode login=$Beget_Username \
|
||||||
|
# --data-urlencode passwd=$Beget_Password \
|
||||||
|
# --data-urlencode input_format=json \
|
||||||
|
# --data-urlencode output_format=json \
|
||||||
|
# --data-urlencode "input_data=$input_data")
|
||||||
|
|
||||||
|
url="$api_url?login=$Beget_Username&passwd=$Beget_Password&input_format=json&output_format=json"
|
||||||
|
if [ -n "$input_data" ]; then
|
||||||
|
url=${url}"&input_data="
|
||||||
|
url=${url}$(echo "$input_data" | _url_encode)
|
||||||
|
fi
|
||||||
|
res=$(_get "$url")
|
||||||
|
|
||||||
|
_debug "Reply: $res"
|
||||||
|
echo "$res"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _is_api_reply_ok [api_reply]
|
||||||
|
_is_api_reply_ok() {
|
||||||
|
_contains "$1" '^{"status":"success","answer":{"status":"success","result":.*}}$'
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_he_ddns_info='Hurricane Electric HE.net DDNS
|
||||||
|
Site: dns.he.net
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_he_ddns
|
||||||
|
Options:
|
||||||
|
HE_DDNS_KEY The DDNS key
|
||||||
|
Author: Markku Leiniö
|
||||||
|
'
|
||||||
|
|
||||||
|
HE_DDNS_URL="https://dyn.dns.he.net/nic/update"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_he_ddns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_he_ddns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
HE_DDNS_KEY="${HE_DDNS_KEY:-$(_readaccountconf_mutable HE_DDNS_KEY)}"
|
||||||
|
if [ -z "$HE_DDNS_KEY" ]; then
|
||||||
|
HE_DDNS_KEY=""
|
||||||
|
_err "You didn't specify a DDNS key for accessing the TXT record in HE API."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#Save the DDNS key to the account conf file.
|
||||||
|
_saveaccountconf_mutable HE_DDNS_KEY "$HE_DDNS_KEY"
|
||||||
|
|
||||||
|
_info "Using Hurricane Electric DDNS API"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
response="$(_post "hostname=$fulldomain&password=$HE_DDNS_KEY&txt=$txtvalue" "$HE_DDNS_URL")"
|
||||||
|
_info "Response: $response"
|
||||||
|
_contains "$response" "good" && return 0 || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# dns_he_ddns_rm() is not implemented because the API call always updates the
|
||||||
|
# contents of the existing record (that the API key gives access to).
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue