6b7b5caf54
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>
356 lines
12 KiB
Bash
Executable File
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
|
|
}
|