acme.sh/dnsapi/dns_openstack.sh
Sergey Ponomarev 6b7b5caf54 DNS provider API: structured description
Instead of using comments declare info in a special variable.
Then the variable can be used to print the DNS API provider usage.
The usage can be parsed on UI and show all needed inputs for options.

The info is stored in plain string that it's both human-readable and easy to parse:

    dns_example_info='API name
     An extended description.
     Multiline.
    Domains: list of alternative domains to find
    Site: the dns provider website e.g. example.com
    Docs: Link to ACME.sh wiki for the provider
    Options:
     VARIABLE1 Title for the option1.
     VARIABLE2 Title for the option2. Default "default value".
     VARIABLE3 Title for the option3. Description to show on UI. Optional.
    Issues: Link to a support ticket on https://github.com/acmesh-official/acme.sh
    Author: First Lastname <authoremail@example.com>, Another Author <https://github.com/example>;
    '

Here:
VARIABLE1 will be required.
VARIABLE2 will be required too but will be populated with a "default value".
VARIABLE3 is optional and can be empty.

A DNS provider may have alternative options like CloudFlare may use API KEY or API Token.
You can use a second section OptionsAlt: section.

Some providers may have alternative names or domains e.g. Aliyun and AlibabaCloud.
Add them to Domains: section.

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
2024-05-18 12:06:41 +03:00

356 lines
12 KiB
Bash
Executable File

#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_openstack_info='OpenStack Designate API
Depends on OpenStackClient and python-desginateclient.
You will require Keystone V3 credentials loaded into your environment,
which could be either password or v3 application credential type.
Site: docs.openstack.org/api-ref/dns/
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_openstack
Options:
OS_AUTH_URL Auth URL. E.g. "https://keystone.example.com:5000/"
OS_USERNAME Username
OS_PASSWORD Password
OS_PROJECT_NAME Project name
OS_PROJECT_DOMAIN_NAME Project domain name. E.g. "Default"
OS_USER_DOMAIN_NAME User domain name. E.g. "Default"
Issues: github.com/acmesh-official/acme.sh/issues/3054
Author: Andy Botting <andy@andybotting.com>
'
######## Public functions #####################
# Usage: dns_openstack_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_openstack_add() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_dns_openstack_credentials || return $?
_dns_openstack_check_setup || return $?
_dns_openstack_find_zone || return $?
_dns_openstack_get_recordset || return $?
_debug _recordset_id "$_recordset_id"
if [ -n "$_recordset_id" ]; then
_dns_openstack_get_records || return $?
_debug _records "$_records"
fi
_dns_openstack_create_recordset || return $?
}
# Usage: dns_openstack_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Remove the txt record after validation.
dns_openstack_rm() {
fulldomain=$1
txtvalue=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_dns_openstack_credentials || return $?
_dns_openstack_check_setup || return $?
_dns_openstack_find_zone || return $?
_dns_openstack_get_recordset || return $?
_debug _recordset_id "$_recordset_id"
if [ -n "$_recordset_id" ]; then
_dns_openstack_get_records || return $?
_debug _records "$_records"
fi
_dns_openstack_delete_recordset || return $?
}
#################### Private functions below ##################################
_dns_openstack_create_recordset() {
if [ -z "$_recordset_id" ]; then
_info "Creating a new recordset"
if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record="$txtvalue" "$_zone_id" "$fulldomain."); then
_err "No recordset ID found after create"
return 1
fi
else
_info "Updating existing recordset"
# Build new list of --record=<rec> args for update
_record_args="--record=$txtvalue"
for _rec in $_records; do
_record_args="$_record_args --record=$_rec"
done
# shellcheck disable=SC2086
if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
_err "Recordset update failed"
return 1
fi
fi
_max_retries=60
_sleep_sec=5
_retry_times=0
while [ "$_retry_times" -lt "$_max_retries" ]; do
_retry_times=$(_math "$_retry_times" + 1)
_debug3 _retry_times "$_retry_times"
_record_status=$(openstack recordset show -c status -f value "$_zone_id" "$_recordset_id")
_info "Recordset status is $_record_status"
if [ "$_record_status" = "ACTIVE" ]; then
return 0
elif [ "$_record_status" = "ERROR" ]; then
return 1
else
_sleep $_sleep_sec
fi
done
_err "Recordset failed to become ACTIVE"
return 1
}
_dns_openstack_delete_recordset() {
if [ "$_records" = "$txtvalue" ]; then
_info "Only one record found, deleting recordset"
if ! openstack recordset delete "$_zone_id" "$fulldomain." >/dev/null; then
_err "Failed to delete recordset"
return 1
fi
else
_info "Found existing records, updating recordset"
# Build new list of --record=<rec> args for update
_record_args=""
for _rec in $_records; do
if [ "$_rec" = "$txtvalue" ]; then
continue
fi
_record_args="$_record_args --record=$_rec"
done
# shellcheck disable=SC2086
if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then
_err "Recordset update failed"
return 1
fi
fi
}
_dns_openstack_get_root() {
# Take the full fqdn and strip away pieces until we get an exact zone name
# match. For example, _acme-challenge.something.domain.com might need to go
# into something.domain.com or domain.com
_zone_name=$1
_zone_list=$2
while [ "$_zone_name" != "" ]; do
_zone_name="$(echo "$_zone_name" | sed 's/[^.]*\.*//')"
echo "$_zone_list" | while read -r id name; do
if _startswith "$_zone_name." "$name"; then
echo "$id"
fi
done
done | _head_n 1
}
_dns_openstack_find_zone() {
if ! _zone_list="$(openstack zone list -c id -c name -f value)"; then
_err "Can't list zones. Check your OpenStack credentials"
return 1
fi
_debug _zone_list "$_zone_list"
if ! _zone_id="$(_dns_openstack_get_root "$fulldomain" "$_zone_list")"; then
_err "Can't find a matching zone. Check your OpenStack credentials"
return 1
fi
_debug _zone_id "$_zone_id"
}
_dns_openstack_get_records() {
if ! _records=$(openstack recordset show -c records -f value "$_zone_id" "$fulldomain."); then
_err "Failed to get records"
return 1
fi
return 0
}
_dns_openstack_get_recordset() {
if ! _recordset_id=$(openstack recordset list -c id -f value --name "$fulldomain." "$_zone_id"); then
_err "Failed to get recordset"
return 1
fi
return 0
}
_dns_openstack_check_setup() {
if ! _exists openstack; then
_err "OpenStack client not found"
return 1
fi
}
_dns_openstack_credentials() {
_debug "Check OpenStack credentials"
# If we have OS_AUTH_URL already set in the environment, then assume we want
# to use those, otherwise use stored credentials
if [ -n "$OS_AUTH_URL" ]; then
_debug "OS_AUTH_URL env var found, using environment"
else
_debug "OS_AUTH_URL not found, loading stored credentials"
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
fi
# Check each var and either save or clear it depending on whether its set.
# The helps us clear out old vars in the case where a user may want
# to switch between password and app creds
_debug "OS_AUTH_URL" "$OS_AUTH_URL"
if [ -n "$OS_AUTH_URL" ]; then
export OS_AUTH_URL
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
else
unset OS_AUTH_URL
_clearaccountconf SAVED_OS_AUTH_URL
fi
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
if [ -n "$OS_IDENTITY_API_VERSION" ]; then
export OS_IDENTITY_API_VERSION
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
else
unset OS_IDENTITY_API_VERSION
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION
fi
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
if [ -n "$OS_AUTH_TYPE" ]; then
export OS_AUTH_TYPE
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
else
unset OS_AUTH_TYPE
_clearaccountconf SAVED_OS_AUTH_TYPE
fi
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
export OS_APPLICATION_CREDENTIAL_ID
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
else
unset OS_APPLICATION_CREDENTIAL_ID
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
fi
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
export OS_APPLICATION_CREDENTIAL_SECRET
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
else
unset OS_APPLICATION_CREDENTIAL_SECRET
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
fi
_debug "OS_USERNAME" "$OS_USERNAME"
if [ -n "$OS_USERNAME" ]; then
export OS_USERNAME
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
else
unset OS_USERNAME
_clearaccountconf SAVED_OS_USERNAME
fi
_secure_debug "OS_PASSWORD" "$OS_PASSWORD"
if [ -n "$OS_PASSWORD" ]; then
export OS_PASSWORD
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
else
unset OS_PASSWORD
_clearaccountconf SAVED_OS_PASSWORD
fi
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
if [ -n "$OS_PROJECT_NAME" ]; then
export OS_PROJECT_NAME
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
else
unset OS_PROJECT_NAME
_clearaccountconf SAVED_OS_PROJECT_NAME
fi
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
if [ -n "$OS_PROJECT_ID" ]; then
export OS_PROJECT_ID
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
else
unset OS_PROJECT_ID
_clearaccountconf SAVED_OS_PROJECT_ID
fi
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
if [ -n "$OS_USER_DOMAIN_NAME" ]; then
export OS_USER_DOMAIN_NAME
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
else
unset OS_USER_DOMAIN_NAME
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME
fi
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
if [ -n "$OS_USER_DOMAIN_ID" ]; then
export OS_USER_DOMAIN_ID
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
else
unset OS_USER_DOMAIN_ID
_clearaccountconf SAVED_OS_USER_DOMAIN_ID
fi
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
export OS_PROJECT_DOMAIN_NAME
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
else
unset OS_PROJECT_DOMAIN_NAME
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
fi
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
export OS_PROJECT_DOMAIN_ID
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
else
unset OS_PROJECT_DOMAIN_ID
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
fi
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
# Application Credential auth
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
_err "Please check your credentials and try again."
return 1
fi
else
# Password auth
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
_err "OpenStack username or password not found."
_err "Please check your credentials and try again."
return 1
fi
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
_err "When using password authentication, OS_PROJECT_NAME or"
_err "OS_PROJECT_ID must be set."
_err "Please check your credentials and try again."
return 1
fi
fi
return 0
}