diff --git a/.travis.yml b/.travis.yml
index 91da2731..a9785d0c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: shell
-dist: trusty
+dist: bionic
os:
- linux
diff --git a/README.md b/README.md
index 0c69fb44..2da103b2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/acmesh-official/acme.sh.svg?branch=master)](https://travis-ci.org/acmesh-official/acme.sh)
[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Docker stars](https://img.shields.io/docker/stars/neilpang/acme.sh.svg)](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
+[![Docker pulls](https://img.shields.io/docker/pulls/neilpang/acme.sh.svg)](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
acme.sh is being sponsored by the following tool; please help to support us by taking a look and signing up to a free trial
@@ -309,7 +311,7 @@ https://github.com/acmesh-official/acme.sh/wiki/dnsapi
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
-If your dns provider doesn't support any api access, you can add the txt record by your hand.
+If your dns provider doesn't support any api access, you can add the txt record by hand.
```bash
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
diff --git a/acme.sh b/acme.sh
index f766ef2f..6a600087 100755
--- a/acme.sh
+++ b/acme.sh
@@ -5642,7 +5642,7 @@ _deactivate() {
_URL_NAME="uri"
fi
- entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
+ entries="$(echo "$response" | _egrep_o "[^{]*\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
if [ -z "$entries" ]; then
_info "No valid entries found."
if [ -z "$thumbprint" ]; then
@@ -6448,10 +6448,10 @@ _checkSudo() {
#it's root using sudo, no matter it's using sudo or not, just fine
return 0
fi
- if [ "$SUDO_COMMAND" = "/bin/su" ] || [ "$SUDO_COMMAND" = "/bin/bash" ]; then
+ if [ -n "$SUDO_COMMAND" ]; then
#it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`
- #fine
- return 0
+ _endswith "$SUDO_COMMAND" /bin/su || grep "^$SUDO_COMMAND\$" /etc/shells >/dev/null 2>&1
+ return $?
fi
#otherwise
return 1
diff --git a/deploy/openstack.sh b/deploy/openstack.sh
new file mode 100644
index 00000000..f2058853
--- /dev/null
+++ b/deploy/openstack.sh
@@ -0,0 +1,262 @@
+#!/usr/bin/env sh
+
+# OpenStack Barbican deploy hook
+#
+# This requires you to have OpenStackClient and python-barbicanclient
+# installed.
+#
+# You will require Keystone V3 credentials loaded into your environment, which
+# could be either password or v3applicationcredential type.
+#
+# Author: Andy Botting
+
+openstack_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 ! _exists openstack; then
+ _err "OpenStack client not found"
+ return 1
+ fi
+
+ _openstack_credentials || return $?
+
+ _info "Generate import pkcs12"
+ _import_pkcs12="$(_mktemp)"
+ if ! _openstack_to_pkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca"; then
+ _err "Error creating pkcs12 certificate"
+ return 1
+ fi
+ _debug _import_pkcs12 "$_import_pkcs12"
+ _base64_pkcs12=$(_base64 "multiline" <"$_import_pkcs12")
+
+ secretHrefs=$(_openstack_get_secrets)
+ _debug secretHrefs "$secretHrefs"
+ _openstack_store_secret || return $?
+
+ if [ -n "$secretHrefs" ]; then
+ _info "Cleaning up existing secret"
+ _openstack_delete_secrets || return $?
+ fi
+
+ _info "Certificate successfully deployed"
+ return 0
+}
+
+_openstack_store_secret() {
+ if ! openstack secret store --name "$_cdomain." -t 'application/octet-stream' -e base64 --payload "$_base64_pkcs12"; then
+ _err "Failed to create OpenStack secret"
+ return 1
+ fi
+ return
+}
+
+_openstack_delete_secrets() {
+ echo "$secretHrefs" | while read -r secretHref; do
+ _info "Deleting old secret $secretHref"
+ if ! openstack secret delete "$secretHref"; then
+ _err "Failed to delete OpenStack secret"
+ return 1
+ fi
+ done
+ return
+}
+
+_openstack_get_secrets() {
+ if ! secretHrefs=$(openstack secret list -f value --name "$_cdomain." | cut -d' ' -f1); then
+ _err "Failed to list secrets"
+ return 1
+ fi
+ echo "$secretHrefs"
+}
+
+_openstack_to_pkcs() {
+ # The existing _toPkcs command can't allow an empty password, due to sh
+ # -z test, so copied here and forcing the empty password.
+ _cpfx="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+
+ ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:"
+}
+
+_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
+}
diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index c57d50bc..2ec0ceb3 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -22,7 +22,7 @@
######## Public functions #####################
_syno_get_cookie_data() {
- grep "\W$1=" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
+ grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
}
#domain keyfile certfile cafile fullchain
@@ -79,7 +79,7 @@ synology_dsm_deploy() {
encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
encoded_did="$(printf "%s" "$SYNO_DID" | _url_encode)"
response=$(_get "$_base_url/webman/login.cgi?username=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_id=$encoded_did" 1)
- token=$(echo "$response" | grep "X-SYNO-TOKEN:" | sed -n 's/^X-SYNO-TOKEN: \(.*\)$/\1/p' | tr -d "\r\n")
+ token=$(echo "$response" | grep -i "X-SYNO-TOKEN:" | sed -n 's/^X-SYNO-TOKEN: \(.*\)$/\1/pI' | tr -d "\r\n")
_debug3 response "$response"
_debug token "$token"
diff --git a/deploy/vault_cli.sh b/deploy/vault_cli.sh
index 5395d87e..8b854137 100644
--- a/deploy/vault_cli.sh
+++ b/deploy/vault_cli.sh
@@ -43,7 +43,7 @@ vault_cli_deploy() {
return 1
fi
- VAULT_CMD=$(which vault)
+ VAULT_CMD=$(command -v vault)
if [ ! $? ]; then
_err "cannot find vault binary!"
return 1
diff --git a/dnsapi/dns_1984hosting.sh b/dnsapi/dns_1984hosting.sh
index 09f02796..bcb675ab 100755
--- a/dnsapi/dns_1984hosting.sh
+++ b/dnsapi/dns_1984hosting.sh
@@ -3,7 +3,7 @@
#So, here must be a method dns_1984hosting_add()
#Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error.
-#
+
#Author: Adrian Fedoreanu
#Report Bugs here: https://github.com/acmesh-official/acme.sh
# or here... https://github.com/acmesh-official/acme.sh/issues/2851
@@ -100,7 +100,7 @@ _1984hosting_add_txt_record() {
elif _contains "$response" ""; then
_err "1984Hosting failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
return 1
- elif [ "$response" = '{"auth": false, "ok": false}' ]; then
+ elif _contains "$response" '"auth": false'; then
_err "1984Hosting failed to add TXT record for $subdomain. Invalid or expired cookie"
return 1
fi
@@ -167,7 +167,7 @@ _1984hosting_login() {
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
- if [ "$response" = '{"loggedin": true, "ok": true}' ]; then
+ if _contains "$response" '"loggedin": true'; then
One984HOSTING_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _tail_n 1 | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
export One984HOSTING_COOKIE
_saveaccountconf_mutable One984HOSTING_COOKIE "$One984HOSTING_COOKIE"
@@ -196,7 +196,7 @@ _check_cookie() {
_authget "https://management.1984hosting.com/accounts/loginstatus/"
response="$(echo "$_response" | _normalizeJson)"
- if [ "$_response" = '{"ok": true}' ]; then
+ if _contains "$response" '"ok": true'; then
_debug "Cached cookie still valid"
return 0
fi
diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh
index bf7cf2bf..28b6572a 100644
--- a/dnsapi/dns_azure.sh
+++ b/dnsapi/dns_azure.sh
@@ -172,7 +172,7 @@ dns_azure_rm() {
_azure_rest GET "$acmeRecordURI" "" "$accesstoken"
timestamp="$(_time)"
if [ "$_code" = "200" ]; then
- vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v "$txtvalue")"
+ vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v -- "$txtvalue")"
values=""
comma=""
for v in $vlist; do
diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh
index df824e86..381d17ec 100755
--- a/dnsapi/dns_cloudns.sh
+++ b/dnsapi/dns_cloudns.sh
@@ -69,7 +69,7 @@ dns_cloudns_rm() {
for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
- if [ ! -z "$record_id" ]; then
+ if [ -n "$record_id" ]; then
_debug zone "$zone"
_debug host "$host"
_debug record "$record"
@@ -91,7 +91,7 @@ dns_cloudns_rm() {
#################### Private functions below ##################################
_dns_cloudns_init_check() {
- if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
+ if [ -n "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
return 0
fi
@@ -164,7 +164,7 @@ _dns_cloudns_http_api_call() {
_debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
- if [ ! -z "$CLOUDNS_SUB_AUTH_ID" ]; then
+ if [ -n "$CLOUDNS_SUB_AUTH_ID" ]; then
auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
else
auth_user="auth-id=$CLOUDNS_AUTH_ID"
diff --git a/dnsapi/dns_cyon.sh b/dnsapi/dns_cyon.sh
index 8db3011d..2dca90c0 100644
--- a/dnsapi/dns_cyon.sh
+++ b/dnsapi/dns_cyon.sh
@@ -66,7 +66,7 @@ _cyon_load_credentials() {
_debug "Save credentials to account.conf"
_saveaccountconf CY_Username "${CY_Username}"
_saveaccountconf CY_Password_B64 "$CY_Password_B64"
- if [ ! -z "${CY_OTP_Secret}" ]; then
+ if [ -n "${CY_OTP_Secret}" ]; then
_saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
else
_clearaccountconf CY_OTP_Secret
@@ -164,7 +164,7 @@ _cyon_login() {
# todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
# 2FA authentication with OTP?
- if [ ! -z "${CY_OTP_Secret}" ]; then
+ if [ -n "${CY_OTP_Secret}" ]; then
_info " - Authorising with OTP code..."
if ! _exists oathtool; then
diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh
index 515e87d5..ac14da48 100755
--- a/dnsapi/dns_dgon.sh
+++ b/dnsapi/dns_dgon.sh
@@ -122,12 +122,12 @@ dns_dgon_rm() {
## check for what we are looking for: "type":"A","name":"$_sub_domain"
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
- if [ ! -z "$record" ]; then
+ if [ -n "$record" ]; then
## we found records
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_ids "$rec_ids"
- if [ ! -z "$rec_ids" ]; then
+ if [ -n "$rec_ids" ]; then
echo "$rec_ids" | while IFS= read -r rec_id; do
## delete the record
## delete URL for removing the one we dont want
@@ -218,7 +218,7 @@ _get_base_domain() {
## we got part of a domain back - grep it out
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
## check if it exists
- if [ ! -z "$found" ]; then
+ if [ -n "$found" ]; then
## exists - exit loop returning the parts
sub_point=$(_math $i - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
diff --git a/dnsapi/dns_dynv6.sh b/dnsapi/dns_dynv6.sh
index 819372c2..50eda74b 100644
--- a/dnsapi/dns_dynv6.sh
+++ b/dnsapi/dns_dynv6.sh
@@ -80,7 +80,7 @@ _generate_new_key() {
_get_domain() {
_full_domain="$1"
_debug "getting domain for $_full_domain"
- if ! _contains "$_full_domain" 'dynv6.net' && ! _contains "$_full_domain" 'dns.army' && ! _contains "$_full_domain" 'dns.navy'; then
+ if ! _contains "$_full_domain" 'dynv6.net' && ! _contains "$_full_domain" 'dns.army' && ! _contains "$_full_domain" 'dns.navy' && ! _contains "$_full_domain" 'v6.rocks' ; then
_err "The hosts does not seem to be a dynv6 host"
return 1
fi
diff --git a/dnsapi/dns_netlify.sh b/dnsapi/dns_netlify.sh
new file mode 100644
index 00000000..4da97f48
--- /dev/null
+++ b/dnsapi/dns_netlify.sh
@@ -0,0 +1,160 @@
+#!/usr/bin/env sh
+
+#NETLIFY_ACCESS_TOKEN="xxxx"
+
+NETLIFY_HOST="api.netlify.com/api/v1/"
+NETLIFY_URL="https://$NETLIFY_HOST"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_netlify_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}"
+
+ if [ -z "$NETLIFY_ACCESS_TOKEN" ]; then
+ NETLIFY_ACCESS_TOKEN=""
+ _err "Please specify your Netlify Access Token and try again."
+ return 1
+ fi
+
+ _info "Using Netlify"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
+
+ if ! _get_root "$fulldomain" "$accesstoken"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ dnsRecordURI="dns_zones/$_domain_id/dns_records"
+
+ body="{\"type\":\"TXT\", \"hostname\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"ttl\":\"10\"}"
+
+ _netlify_rest POST "$dnsRecordURI" "$body" "$NETLIFY_ACCESS_TOKEN"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
+ _info "validation value added"
+ return 0
+ else
+ _err "error adding validation value ($_code)"
+ return 1
+ fi
+
+ _err "Not fully implemented!"
+ return 1
+}
+
+#Usage: dns_myapi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+#Remove the txt record after validation.
+dns_netlify_rm() {
+ _info "Using Netlify"
+ _debug txtdomain "$txtdomain"
+ _debug txt "$txt"
+
+ _saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
+
+ if ! _get_root "$txtdomain" "$accesstoken"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ dnsRecordURI="dns_zones/$_domain_id/dns_records"
+
+ _netlify_rest GET "$dnsRecordURI" "" "$NETLIFY_ACCESS_TOKEN"
+
+ _record_id=$(echo "$response" | _egrep_o "\"type\":\"TXT\",[^\}]*\"value\":\"$txt\"" | head -n 1 | _egrep_o "\"id\":\"[^\"\}]*\"" | cut -d : -f 2 | tr -d \" )
+ _debug _record_id "$_record_id"
+ if [ "$_record_id" ]; then
+ _netlify_rest DELETE "$dnsRecordURI/$_record_id" "" "$NETLIFY_ACCESS_TOKEN"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
+ _info "validation value removed"
+ return 0
+ else
+ _err "error removing validation value ($_code)"
+ return 1
+ fi
+ return 0
+ fi
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_get_root() {
+ domain=$1
+ accesstoken=$2
+ i=1
+ p=1
+
+ _netlify_rest GET "dns_zones" "" "$accesstoken"
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug2 "Checking domain: $h"
+ if [ -z "$h" ]; then
+ #not valid
+ _err "Invalid domain"
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ _domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h" | cut -d , -f 1 | tr -d \" )
+ if [ "$_domain_id" ]; then
+ if [ "$i" = 1 ]; then
+ #create the record at the domain apex (@) if only the domain name was provided as --domain-alias
+ _sub_domain="@"
+ else
+ _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
+ fi
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_netlify_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ token_trimmed=$(echo "$NETLIFY_ACCESS_TOKEN" | tr -d '"')
+
+ export _H1="Content-Type: application/json"
+ export _H2="Authorization: Bearer $token_trimmed"
+
+ :>"$HTTP_HEADER"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$NETLIFY_URL$ep" "" "$m")"
+ else
+ response="$(_get "$NETLIFY_URL$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
\ No newline at end of file
diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh
index 5721f994..0a2c3330 100755
--- a/dnsapi/dns_yandex.sh
+++ b/dnsapi/dns_yandex.sh
@@ -25,7 +25,7 @@ dns_yandex_add() {
_PDD_get_record_ids || return 1
_debug "Record_ids: $record_ids"
- if [ ! -z "$record_ids" ]; then
+ if [ -n "$record_ids" ]; then
_info "All existing $subdomain records from $domain will be removed at the very end."
fi
diff --git a/dnsapi/openstack.sh b/dnsapi/openstack.sh
new file mode 100755
index 00000000..38619e6f
--- /dev/null
+++ b/dnsapi/openstack.sh
@@ -0,0 +1,348 @@
+#!/usr/bin/env sh
+
+# OpenStack Designate API plugin
+#
+# This requires you to have OpenStackClient and python-desginateclient
+# installed.
+#
+# You will require Keystone V3 credentials loaded into your environment, which
+# could be either password or v3applicationcredential type.
+#
+# Author: Andy Botting
+
+######## 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 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 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
+}