Pål Håland
7 years ago
47 changed files with 5866 additions and 1048 deletions
-
24.travis.yml
-
20Dockerfile
-
220README.md
-
1491acme.sh
-
188deploy/README.md
-
29deploy/cpanel.sh
-
64deploy/cpanel_uapi.sh
-
108deploy/fritzbox.sh
-
205deploy/ssh.sh
-
55deploy/strongswan.sh
-
100deploy/unifi.sh
-
56deploy/vault_cli.sh
-
300dnsapi/README.md
-
47dnsapi/dns_ali.sh
-
264dnsapi/dns_autodns.sh
-
133dnsapi/dns_aws.sh
-
343dnsapi/dns_azure.sh
-
59dnsapi/dns_cf.sh
-
104dnsapi/dns_cloudns.sh
-
44dnsapi/dns_cx.sh
-
184dnsapi/dns_da.sh
-
30dnsapi/dns_dgon.sh
-
82dnsapi/dns_dp.sh
-
97dnsapi/dns_dreamhost.sh
-
128dnsapi/dns_duckdns.sh
-
339dnsapi/dns_dyn.sh
-
32dnsapi/dns_dynu.sh
-
177dnsapi/dns_freedns.sh
-
4dnsapi/dns_gandi_livedns.sh
-
67dnsapi/dns_gd.sh
-
158dnsapi/dns_he.sh
-
29dnsapi/dns_infoblox.sh
-
311dnsapi/dns_inwx.sh
-
1dnsapi/dns_ispconfig.sh
-
4dnsapi/dns_linode.sh
-
54dnsapi/dns_lua.sh
-
36dnsapi/dns_me.sh
-
166dnsapi/dns_namecom.sh
-
137dnsapi/dns_namesilo.sh
-
158dnsapi/dns_nsone.sh
-
125dnsapi/dns_ovh.sh
-
17dnsapi/dns_pdns.sh
-
161dnsapi/dns_selectel.sh
-
170dnsapi/dns_servercow.sh
-
202dnsapi/dns_unoeuro.sh
-
106dnsapi/dns_yandex.sh
-
85dnsapi/dns_zonomi.sh
1491
acme.sh
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,29 +0,0 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Here is the script to deploy the cert to your cpanel account by the cpanel APIs. |
|||
|
|||
#returns 0 means success, otherwise error. |
|||
|
|||
#export DEPLOY_CPANEL_USER=myusername |
|||
#export DEPLOY_CPANEL_PASSWORD=PASSWORD |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
cpanel_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" |
|||
|
|||
_err "Not implemented yet" |
|||
return 1 |
|||
|
|||
} |
@ -0,0 +1,64 @@ |
|||
#!/usr/bin/env sh |
|||
# Here is the script to deploy the cert to your cpanel using the cpanel API. |
|||
# Uses command line uapi. --user option is needed only if run as root. |
|||
# Returns 0 when success. |
|||
# Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info> |
|||
# Public domain, 2017 |
|||
|
|||
#export DEPLOY_CPANEL_USER=myusername |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
|
|||
cpanel_uapi_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 uapi; then |
|||
_err "The command uapi is not found." |
|||
return 1 |
|||
fi |
|||
if ! _exists php; then |
|||
_err "The command php is not found." |
|||
return 1 |
|||
fi |
|||
# read cert and key files and urlencode both |
|||
_certstr=$(cat "$_ccert") |
|||
_keystr=$(cat "$_ckey") |
|||
_cert=$(php -r "echo urlencode(\"$_certstr\");") |
|||
_key=$(php -r "echo urlencode(\"$_keystr\");") |
|||
|
|||
_debug _cert "$_cert" |
|||
_debug _key "$_key" |
|||
|
|||
if [ "$(id -u)" = 0 ]; then |
|||
if [ -z "$DEPLOY_CPANEL_USER" ]; then |
|||
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username" |
|||
return 1 |
|||
fi |
|||
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER" |
|||
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") |
|||
else |
|||
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") |
|||
fi |
|||
error_response="status: 0" |
|||
if test "${_response#*$error_response}" != "$_response"; then |
|||
_err "Error in deploying certificate:" |
|||
_err "$_response" |
|||
return 1 |
|||
fi |
|||
|
|||
_debug response "$_response" |
|||
_info "Certificate successfully deployed" |
|||
return 0 |
|||
} |
@ -0,0 +1,108 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Here is a script to deploy cert to an AVM FRITZ!Box router. |
|||
|
|||
#returns 0 means success, otherwise error. |
|||
|
|||
#DEPLOY_FRITZBOX_USERNAME="username" |
|||
#DEPLOY_FRITZBOX_PASSWORD="password" |
|||
#DEPLOY_FRITZBOX_URL="https://fritz.box" |
|||
|
|||
# Kudos to wikrie at Github for his FRITZ!Box update script: |
|||
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
fritzbox_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 iconv; then |
|||
_err "iconv not found" |
|||
return 1 |
|||
fi |
|||
|
|||
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" |
|||
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}" |
|||
_fritzbox_url="${DEPLOY_FRITZBOX_URL}" |
|||
|
|||
_debug _fritzbox_url "$_fritzbox_url" |
|||
_debug _fritzbox_username "$_fritzbox_username" |
|||
_secure_debug _fritzbox_password "$_fritzbox_password" |
|||
if [ -z "$_fritzbox_username" ]; then |
|||
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." |
|||
return 1 |
|||
fi |
|||
if [ -z "$_fritzbox_password" ]; then |
|||
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." |
|||
return 1 |
|||
fi |
|||
if [ -z "$_fritzbox_url" ]; then |
|||
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." |
|||
return 1 |
|||
fi |
|||
|
|||
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}" |
|||
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" |
|||
_saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" |
|||
|
|||
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate |
|||
export HTTPS_INSECURE=1 |
|||
|
|||
_info "Log in to the FRITZ!Box" |
|||
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')" |
|||
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" |
|||
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')" |
|||
|
|||
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then |
|||
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL." |
|||
return 1 |
|||
fi |
|||
|
|||
_info "Generate form POST request" |
|||
_post_request="$(_mktemp)" |
|||
_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" |
|||
# _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it! |
|||
_CERTPASSWORD_= |
|||
{ |
|||
printf -- "--" |
|||
printf -- "%s\r\n" "${_post_boundary}" |
|||
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}" |
|||
printf -- "--" |
|||
printf -- "%s\r\n" "${_post_boundary}" |
|||
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}" |
|||
printf -- "--" |
|||
printf -- "%s\r\n" "${_post_boundary}" |
|||
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" |
|||
printf "Content-Type: application/octet-stream\r\n\r\n" |
|||
cat "${_ckey}" "${_cfullchain}" |
|||
printf "\r\n" |
|||
printf -- "--" |
|||
printf -- "%s--" "${_post_boundary}" |
|||
} >>"${_post_request}" |
|||
|
|||
_info "Upload certificate to the FRITZ!Box" |
|||
|
|||
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}" |
|||
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL |
|||
|
|||
retval=$? |
|||
if [ $retval = 0 ]; then |
|||
_info "Upload successful" |
|||
else |
|||
_err "Upload failed" |
|||
fi |
|||
rm "${_post_request}" |
|||
|
|||
return $retval |
|||
} |
@ -0,0 +1,205 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# Script to deploy certificates to remote server by SSH |
|||
# Note that SSH must be able to login to remote host without a password... |
|||
# SSH Keys must have been exchanged with the remote host. Validate and |
|||
# test that you can login to USER@SERVER from the host running acme.sh before |
|||
# using this script. |
|||
# |
|||
# The following variables exported from environment will be used. |
|||
# If not set then values previously saved in domain.conf file are used. |
|||
# |
|||
# Only a username is required. All others are optional. |
|||
# |
|||
# The following examples are for QNAP NAS running QTS 4.2 |
|||
# export DEPLOY_SSH_CMD="" # defaults to ssh |
|||
# export DEPLOY_SSH_USER="admin" # required |
|||
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name |
|||
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" |
|||
# export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" |
|||
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" |
|||
# export DEPLOY_SSH_FULLCHAIN="" |
|||
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" |
|||
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes |
|||
# |
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
ssh_deploy() { |
|||
_cdomain="$1" |
|||
_ckey="$2" |
|||
_ccert="$3" |
|||
_cca="$4" |
|||
_cfullchain="$5" |
|||
_cmdstr="" |
|||
_homedir='~' |
|||
_backupprefix="$_homedir/.acme_ssh_deploy/$_cdomain-backup" |
|||
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')" |
|||
|
|||
if [ -f "$DOMAIN_CONF" ]; then |
|||
# shellcheck disable=SC1090 |
|||
. "$DOMAIN_CONF" |
|||
fi |
|||
|
|||
_debug _cdomain "$_cdomain" |
|||
_debug _ckey "$_ckey" |
|||
_debug _ccert "$_ccert" |
|||
_debug _cca "$_cca" |
|||
_debug _cfullchain "$_cfullchain" |
|||
|
|||
# USER is required to login by SSH to remote host. |
|||
if [ -z "$DEPLOY_SSH_USER" ]; then |
|||
if [ -z "$Le_Deploy_ssh_user" ]; then |
|||
_err "DEPLOY_SSH_USER not defined." |
|||
return 1 |
|||
fi |
|||
else |
|||
Le_Deploy_ssh_user="$DEPLOY_SSH_USER" |
|||
_savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user" |
|||
fi |
|||
|
|||
# SERVER is optional. If not provided then use _cdomain |
|||
if [ -n "$DEPLOY_SSH_SERVER" ]; then |
|||
Le_Deploy_ssh_server="$DEPLOY_SSH_SERVER" |
|||
_savedomainconf Le_Deploy_ssh_server "$Le_Deploy_ssh_server" |
|||
elif [ -z "$Le_Deploy_ssh_server" ]; then |
|||
Le_Deploy_ssh_server="$_cdomain" |
|||
fi |
|||
|
|||
# CMD is optional. If not provided then use ssh |
|||
if [ -n "$DEPLOY_SSH_CMD" ]; then |
|||
Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD" |
|||
_savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd" |
|||
elif [ -z "$Le_Deploy_ssh_cmd" ]; then |
|||
Le_Deploy_ssh_cmd="ssh" |
|||
fi |
|||
|
|||
# BACKUP is optional. If not provided then default to yes |
|||
if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then |
|||
Le_Deploy_ssh_backup="no" |
|||
elif [ -z "$Le_Deploy_ssh_backup" ]; then |
|||
Le_Deploy_ssh_backup="yes" |
|||
fi |
|||
_savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup" |
|||
|
|||
_info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server" |
|||
|
|||
# KEYFILE is optional. |
|||
# If provided then private key will be copied to provided filename. |
|||
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then |
|||
Le_Deploy_ssh_keyfile="$DEPLOY_SSH_KEYFILE" |
|||
_savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile" |
|||
fi |
|||
if [ -n "$Le_Deploy_ssh_keyfile" ]; then |
|||
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then |
|||
# backup file we are about to overwrite. |
|||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir >/dev/null;" |
|||
fi |
|||
# copy new certificate into file. |
|||
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;" |
|||
_info "will copy private key to remote file $Le_Deploy_ssh_keyfile" |
|||
fi |
|||
|
|||
# CERTFILE is optional. |
|||
# If provided then private key will be copied or appended to provided filename. |
|||
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then |
|||
Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE" |
|||
_savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile" |
|||
fi |
|||
if [ -n "$Le_Deploy_ssh_certfile" ]; then |
|||
_pipe=">" |
|||
if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then |
|||
# if filename is same as previous file then append. |
|||
_pipe=">>" |
|||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then |
|||
# backup file we are about to overwrite. |
|||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir >/dev/null;" |
|||
fi |
|||
# copy new certificate into file. |
|||
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;" |
|||
_info "will copy certificate to remote file $Le_Deploy_ssh_certfile" |
|||
fi |
|||
|
|||
# CAFILE is optional. |
|||
# If provided then CA intermediate certificate will be copied or appended to provided filename. |
|||
if [ -n "$DEPLOY_SSH_CAFILE" ]; then |
|||
Le_Deploy_ssh_cafile="$DEPLOY_SSH_CAFILE" |
|||
_savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile" |
|||
fi |
|||
if [ -n "$Le_Deploy_ssh_cafile" ]; then |
|||
_pipe=">" |
|||
if [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_keyfile" ] \ |
|||
|| [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_certfile" ]; then |
|||
# if filename is same as previous file then append. |
|||
_pipe=">>" |
|||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then |
|||
# backup file we are about to overwrite. |
|||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir >/dev/null;" |
|||
fi |
|||
# copy new certificate into file. |
|||
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;" |
|||
_info "will copy CA file to remote file $Le_Deploy_ssh_cafile" |
|||
fi |
|||
|
|||
# FULLCHAIN is optional. |
|||
# If provided then fullchain certificate will be copied or appended to provided filename. |
|||
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then |
|||
Le_Deploy_ssh_fullchain="$DEPLOY_SSH_FULLCHAIN" |
|||
_savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain" |
|||
fi |
|||
if [ -n "$Le_Deploy_ssh_fullchain" ]; then |
|||
_pipe=">" |
|||
if [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_keyfile" ] \ |
|||
|| [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_certfile" ] \ |
|||
|| [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_cafile" ]; then |
|||
# if filename is same as previous file then append. |
|||
_pipe=">>" |
|||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then |
|||
# backup file we are about to overwrite. |
|||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir >/dev/null;" |
|||
fi |
|||
# copy new certificate into file. |
|||
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;" |
|||
_info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain" |
|||
fi |
|||
|
|||
# REMOTE_CMD is optional. |
|||
# If provided then this command will be executed on remote host. |
|||
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then |
|||
Le_Deploy_ssh_remote_cmd="$DEPLOY_SSH_REMOTE_CMD" |
|||
_savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd" |
|||
fi |
|||
if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then |
|||
_cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;" |
|||
_info "Will execute remote command $Le_Deploy_ssh_remote_cmd" |
|||
fi |
|||
|
|||
if [ -z "$_cmdstr" ]; then |
|||
_err "No remote commands to excute. Failed to deploy certificates to remote server" |
|||
return 1 |
|||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then |
|||
# run cleanup on the backup directory, erase all older |
|||
# than 180 days (15552000 seconds). |
|||
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \ |
|||
do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \ |
|||
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr" |
|||
# Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr" |
|||
# Create our backup directory for overwritten cert files. |
|||
_cmdstr="mkdir -p $_backupdir; $_cmdstr" |
|||
_info "Backup of old certificate files will be placed in remote directory $_backupdir" |
|||
_info "Backup directories erased after 180 days." |
|||
fi |
|||
|
|||
_debug "Remote commands to execute: $_cmdstr" |
|||
_info "Submitting sequence of commands to remote server by ssh" |
|||
# quotations in bash cmd below intended. Squash travis spellcheck error |
|||
# shellcheck disable=SC2029 |
|||
$Le_Deploy_ssh_cmd -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmdstr'" |
|||
_ret="$?" |
|||
|
|||
if [ "$_ret" != "0" ]; then |
|||
_err "Error code $_ret returned from $Le_Deploy_ssh_cmd" |
|||
fi |
|||
|
|||
return $_ret |
|||
} |
@ -0,0 +1,55 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Here is a sample custom api script. |
|||
#This file name is "myapi.sh" |
|||
#So, here must be a method myapi_deploy() |
|||
#Which will be called by acme.sh to deploy the cert |
|||
#returns 0 means success, otherwise error. |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
strongswan_deploy() { |
|||
_cdomain="$1" |
|||
_ckey="$2" |
|||
_ccert="$3" |
|||
_cca="$4" |
|||
_cfullchain="$5" |
|||
|
|||
_info "Using strongswan" |
|||
|
|||
if [ -x /usr/sbin/ipsec ]; then |
|||
_ipsec=/usr/sbin/ipsec |
|||
elif [ -x /usr/sbin/strongswan ]; then |
|||
_ipsec=/usr/sbin/strongswan |
|||
elif [ -x /usr/local/sbin/ipsec ]; then |
|||
_ipsec=/usr/local/sbin/ipsec |
|||
else |
|||
_err "no strongswan or ipsec command is detected" |
|||
return 1 |
|||
fi |
|||
|
|||
_info _ipsec "$_ipsec" |
|||
|
|||
_confdir=$($_ipsec --confdir) |
|||
if [ $? -ne 0 ] || [ -z "$_confdir" ]; then |
|||
_err "no strongswan --confdir is detected" |
|||
return 1 |
|||
fi |
|||
|
|||
_info _confdir "$_confdir" |
|||
|
|||
_debug _cdomain "$_cdomain" |
|||
_debug _ckey "$_ckey" |
|||
_debug _ccert "$_ccert" |
|||
_debug _cca "$_cca" |
|||
_debug _cfullchain "$_cfullchain" |
|||
|
|||
cat "$_ckey" >"${_confdir}/ipsec.d/private/$(basename "$_ckey")" |
|||
cat "$_ccert" >"${_confdir}/ipsec.d/certs/$(basename "$_ccert")" |
|||
cat "$_cca" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cca")" |
|||
cat "$_cfullchain" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cfullchain")" |
|||
|
|||
$_ipsec reload |
|||
|
|||
} |
@ -0,0 +1,100 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Here is a script to deploy cert to unifi server. |
|||
|
|||
#returns 0 means success, otherwise error. |
|||
|
|||
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" |
|||
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise" |
|||
#DEPLOY_UNIFI_RELOAD="service unifi restart" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
unifi_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 keytool; then |
|||
_err "keytool not found" |
|||
return 1 |
|||
fi |
|||
|
|||
DEFAULT_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" |
|||
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-$DEFAULT_UNIFI_KEYSTORE}" |
|||
DEFAULT_UNIFI_KEYPASS="aircontrolenterprise" |
|||
_unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-$DEFAULT_UNIFI_KEYPASS}" |
|||
DEFAULT_UNIFI_RELOAD="service unifi restart" |
|||
_reload="${DEPLOY_UNIFI_RELOAD:-$DEFAULT_UNIFI_RELOAD}" |
|||
|
|||
_debug _unifi_keystore "$_unifi_keystore" |
|||
if [ ! -f "$_unifi_keystore" ]; then |
|||
if [ -z "$DEPLOY_UNIFI_KEYSTORE" ]; then |
|||
_err "unifi keystore is not found, please define DEPLOY_UNIFI_KEYSTORE" |
|||
return 1 |
|||
else |
|||
_err "It seems that the specified unifi keystore is not valid, please check." |
|||
return 1 |
|||
fi |
|||
fi |
|||
if [ ! -w "$_unifi_keystore" ]; then |
|||
_err "The file $_unifi_keystore is not writable, please change the permission." |
|||
return 1 |
|||
fi |
|||
|
|||
_info "Generate import pkcs12" |
|||
_import_pkcs12="$(_mktemp)" |
|||
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root |
|||
if [ "$?" != "0" ]; then |
|||
_err "Oops, error creating import pkcs12, please report bug to us." |
|||
return 1 |
|||
fi |
|||
|
|||
_info "Modify unifi keystore: $_unifi_keystore" |
|||
if keytool -importkeystore \ |
|||
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \ |
|||
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \ |
|||
-alias unifi -noprompt; then |
|||
_info "Import keystore success!" |
|||
rm "$_import_pkcs12" |
|||
else |
|||
_err "Import unifi keystore error, please report bug to us." |
|||
rm "$_import_pkcs12" |
|||
return 1 |
|||
fi |
|||
|
|||
_info "Run reload: $_reload" |
|||
if eval "$_reload"; then |
|||
_info "Reload success!" |
|||
if [ "$DEPLOY_UNIFI_KEYSTORE" ]; then |
|||
_savedomainconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" |
|||
else |
|||
_cleardomainconf DEPLOY_UNIFI_KEYSTORE |
|||
fi |
|||
if [ "$DEPLOY_UNIFI_KEYPASS" ]; then |
|||
_savedomainconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" |
|||
else |
|||
_cleardomainconf DEPLOY_UNIFI_KEYPASS |
|||
fi |
|||
if [ "$DEPLOY_UNIFI_RELOAD" ]; then |
|||
_savedomainconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" |
|||
else |
|||
_cleardomainconf DEPLOY_UNIFI_RELOAD |
|||
fi |
|||
return 0 |
|||
else |
|||
_err "Reload error" |
|||
return 1 |
|||
fi |
|||
return 0 |
|||
|
|||
} |
@ -0,0 +1,56 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# Here is a script to deploy cert to hashicorp vault |
|||
# (https://www.vaultproject.io/) |
|||
# |
|||
# it requires the vault binary to be available in PATH, and the following |
|||
# environment variables: |
|||
# |
|||
# VAULT_PREFIX - this contains the prefix path in vault |
|||
# VAULT_ADDR - vault requires this to find your vault server |
|||
# |
|||
# additionally, you need to ensure that VAULT_TOKEN is avialable or |
|||
# `vault auth` has applied the appropriate authorization for the vault binary |
|||
# to access the vault server |
|||
|
|||
#returns 0 means success, otherwise error. |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#domain keyfile certfile cafile fullchain |
|||
vault_cli_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" |
|||
|
|||
# validate required env vars |
|||
if [ -z "$VAULT_PREFIX" ]; then |
|||
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)" |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$VAULT_ADDR" ]; then |
|||
_err "VAULT_ADDR needs to be defined (contains vault connection address)" |
|||
return 1 |
|||
fi |
|||
|
|||
VAULT_CMD=$(which vault) |
|||
if [ ! $? ]; then |
|||
_err "cannot find vault binary!" |
|||
return 1 |
|||
fi |
|||
|
|||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1 |
|||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1 |
|||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1 |
|||
|
|||
} |
@ -0,0 +1,264 @@ |
|||
#!/usr/bin/env sh |
|||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- |
|||
|
|||
# This is the InternetX autoDNS xml api wrapper for acme.sh |
|||
# Author: auerswald@gmail.com |
|||
# Created: 2018-01-14 |
|||
# |
|||
# export AUTODNS_USER="username" |
|||
# export AUTODNS_PASSWORD="password" |
|||
# export AUTODNS_CONTEXT="context" |
|||
# |
|||
# Usage: |
|||
# acme.sh --issue --dns dns_autodns -d example.com |
|||
|
|||
AUTODNS_API="https://gateway.autodns.com" |
|||
|
|||
# Arguments: |
|||
# txtdomain |
|||
# txt |
|||
dns_autodns_add() { |
|||
fulldomain="$1" |
|||
txtvalue="$2" |
|||
|
|||
AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" |
|||
AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" |
|||
AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}" |
|||
|
|||
if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then |
|||
_err "You don't specify autodns user, password and context." |
|||
return 1 |
|||
fi |
|||
|
|||
_saveaccountconf_mutable AUTODNS_USER "$AUTODNS_USER" |
|||
_saveaccountconf_mutable AUTODNS_PASSWORD "$AUTODNS_PASSWORD" |
|||
_saveaccountconf_mutable AUTODNS_CONTEXT "$AUTODNS_CONTEXT" |
|||
|
|||
_debug "First detect the root zone" |
|||
|
|||
if ! _get_autodns_zone "$fulldomain"; then |
|||
_err "invalid domain" |
|||
return 1 |
|||
fi |
|||
|
|||
_debug _sub_domain "$_sub_domain" |
|||
_debug _zone "$_zone" |
|||
_debug _system_ns "$_system_ns" |
|||
|
|||
_info "Adding TXT record" |
|||
|
|||
autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")" |
|||
|
|||
if [ "$?" -eq "0" ]; then |
|||
_info "Added, OK" |
|||
return 0 |
|||
fi |
|||
|
|||
return 1 |
|||
} |
|||
|
|||
# Arguments: |
|||
# txtdomain |
|||
# txt |
|||
dns_autodns_rm() { |
|||
fulldomain="$1" |
|||
txtvalue="$2" |
|||
|
|||
AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" |
|||
AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" |
|||
AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}" |
|||
|
|||
if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then |
|||
_err "You don't specify autodns user, password and context." |
|||
return 1 |
|||
fi |
|||
|
|||
_debug "First detect the root zone" |
|||
|
|||
if ! _get_autodns_zone "$fulldomain"; then |
|||
_err "zone not found" |
|||
return 1 |
|||
fi |
|||
|
|||
_debug _sub_domain "$_sub_domain" |
|||
_debug _zone "$_zone" |
|||
_debug _system_ns "$_system_ns" |
|||
|
|||
_info "Delete TXT record" |
|||
|
|||
autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")" |
|||
|
|||
if [ "$?" -eq "0" ]; then |
|||
_info "Deleted, OK" |
|||
return 0 |
|||
fi |
|||
|
|||
return 1 |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
# Arguments: |
|||
# fulldomain |
|||
# Returns: |
|||
# _sub_domain=_acme-challenge.www |
|||
# _zone=domain.com |
|||
# _system_ns |
|||
_get_autodns_zone() { |
|||
domain="$1" |
|||
|
|||
i=2 |
|||
p=1 |
|||
|
|||
while true; do |
|||
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug h "$h" |
|||
|
|||
if [ -z "$h" ]; then |
|||
# not valid |
|||
return 1 |
|||
fi |
|||
|
|||
autodns_response="$(_autodns_zone_inquire "$h")" |
|||
|
|||
if [ "$?" -ne "0" ]; then |
|||
_err "invalid domain" |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$autodns_response" "<summary>1</summary>" >/dev/null; then |
|||
_zone="$(echo "$autodns_response" | _egrep_o '<name>[^<]*</name>' | cut -d '>' -f 2 | cut -d '<' -f 1)" |
|||
_system_ns="$(echo "$autodns_response" | _egrep_o '<system_ns>[^<]*</system_ns>' | cut -d '>' -f 2 | cut -d '<' -f 1)" |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
return 0 |
|||
fi |
|||
|
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
|
|||
return 1 |
|||
} |
|||
|
|||
_build_request_auth_xml() { |
|||
printf "<auth> |
|||
<user>%s</user> |
|||
<password>%s</password> |
|||
<context>%s</context> |
|||
</auth>" "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT" |
|||
} |
|||
|
|||
# Arguments: |
|||
# zone |
|||
_build_zone_inquire_xml() { |
|||
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?> |
|||
<request> |
|||
%s |
|||
<task> |
|||
<code>0205</code> |
|||
<view> |
|||
<children>1</children> |
|||
<limit>1</limit> |
|||
</view> |
|||
<where> |
|||
<key>name</key> |
|||
<operator>eq</operator> |
|||
<value>%s</value> |
|||
</where> |
|||
</task> |
|||
</request>" "$(_build_request_auth_xml)" "$1" |
|||
} |
|||
|
|||
# Arguments: |
|||
# zone |
|||
# subdomain |
|||
# txtvalue |
|||
# system_ns |
|||
_build_zone_update_xml() { |
|||
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?> |
|||
<request> |
|||
%s |
|||
<task> |
|||
<code>0202001</code> |
|||
<default> |
|||
<rr_add> |
|||
<name>%s</name> |
|||
<ttl>600</ttl> |
|||
<type>TXT</type> |
|||
<value>%s</value> |
|||
</rr_add> |
|||
</default> |
|||
<zone> |
|||
<name>%s</name> |
|||
<system_ns>%s</system_ns> |
|||
</zone> |
|||
</task> |
|||
</request>" "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4" |
|||
} |
|||
|
|||
# Arguments: |
|||
# zone |
|||
_autodns_zone_inquire() { |
|||
request_data="$(_build_zone_inquire_xml "$1")" |
|||
autodns_response="$(_autodns_api_call "$request_data")" |
|||
ret="$?" |
|||
|
|||
printf "%s" "$autodns_response" |
|||
return "$ret" |
|||
} |
|||
|
|||
# Arguments: |
|||
# zone |
|||
# subdomain |
|||
# txtvalue |
|||
# system_ns |
|||
_autodns_zone_update() { |
|||
request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")" |
|||
autodns_response="$(_autodns_api_call "$request_data")" |
|||
ret="$?" |
|||
|
|||
printf "%s" "$autodns_response" |
|||
return "$ret" |
|||
} |
|||
|
|||
# Arguments: |
|||
# zone |
|||
# subdomain |
|||
# txtvalue |
|||
# system_ns |
|||
_autodns_zone_cleanup() { |
|||
request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")" |
|||
# replace 'rr_add>' with 'rr_rem>' in request_data |
|||
request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')" |
|||
autodns_response="$(_autodns_api_call "$request_data")" |
|||
ret="$?" |
|||
|
|||
printf "%s" "$autodns_response" |
|||
return "$ret" |
|||
} |
|||
|
|||
# Arguments: |
|||
# request_data |
|||
_autodns_api_call() { |
|||
request_data="$1" |
|||
|
|||
_debug request_data "$request_data" |
|||
|
|||
autodns_response="$(_post "$request_data" "$AUTODNS_API")" |
|||
ret="$?" |
|||
|
|||
_debug autodns_response "$autodns_response" |
|||
|
|||
if [ "$ret" -ne "0" ]; then |
|||
_err "error" |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$autodns_response" "<type>success</type>" >/dev/null; then |
|||
_info "success" |
|||
printf "%s" "$autodns_response" |
|||
return 0 |
|||
fi |
|||
|
|||
return 1 |
|||
} |
@ -0,0 +1,343 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Azure-DNS" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
# Used to add txt record |
|||
# |
|||
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate |
|||
# |
|||
dns_azure_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" |
|||
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" |
|||
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" |
|||
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" |
|||
|
|||
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure Subscription ID " |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$AZUREDNS_TENANTID" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure Tenant ID " |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$AZUREDNS_APPID" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure App ID" |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure Client Secret" |
|||
return 1 |
|||
fi |
|||
#save account details to account conf file. |
|||
_saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID" |
|||
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID" |
|||
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID" |
|||
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET" |
|||
|
|||
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") |
|||
|
|||
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then |
|||
_err "invalid domain" |
|||
return 1 |
|||
fi |
|||
_debug _domain_id "$_domain_id" |
|||
_debug _sub_domain "$_sub_domain" |
|||
_debug _domain "$_domain" |
|||
|
|||
acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" |
|||
_debug "$acmeRecordURI" |
|||
# Get existing TXT record |
|||
_azure_rest GET "$acmeRecordURI" "" "$accesstoken" |
|||
values="{\"value\":[\"$txtvalue\"]}" |
|||
timestamp="$(_time)" |
|||
if [ "$_code" = "200" ]; then |
|||
vlist="$(echo "$response" | _egrep_o "\"value\"\s*:\s*\[\s*\"[^\"]*\"\s*]" | cut -d : -f 2 | tr -d "[]\"")" |
|||
_debug "existing TXT found" |
|||
_debug "$vlist" |
|||
existingts="$(echo "$response" | _egrep_o "\"acmetscheck\"\s*:\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")" |
|||
if [ -z "$existingts" ]; then |
|||
# the record was not created by acme.sh. Copy the exisiting entires |
|||
existingts=$timestamp |
|||
fi |
|||
_diff="$(_math "$timestamp - $existingts")" |
|||
_debug "existing txt age: $_diff" |
|||
# only use recently added records and discard if older than 2 hours because they are probably orphaned |
|||
if [ "$_diff" -lt 7200 ]; then |
|||
_debug "existing txt value: $vlist" |
|||
for v in $vlist; do |
|||
values="$values ,{\"value\":[\"$v\"]}" |
|||
done |
|||
fi |
|||
fi |
|||
# Add the txtvalue TXT Record |
|||
body="{\"properties\":{\"metadata\":{\"acmetscheck\":\"$timestamp\"},\"TTL\":10, \"TXTRecords\":[$values]}}" |
|||
_azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" |
|||
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then |
|||
_info "validation value added" |
|||
return 0 |
|||
else |
|||
_err "error adding validation value ($_code)" |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
# Usage: fulldomain txtvalue |
|||
# Used to remove the txt record after validation |
|||
# |
|||
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete |
|||
# |
|||
dns_azure_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" |
|||
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" |
|||
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" |
|||
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" |
|||
|
|||
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure Subscription ID " |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$AZUREDNS_TENANTID" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure Tenant ID " |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$AZUREDNS_APPID" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure App ID" |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then |
|||
AZUREDNS_SUBSCRIPTIONID="" |
|||
AZUREDNS_TENANTID="" |
|||
AZUREDNS_APPID="" |
|||
AZUREDNS_CLIENTSECRET="" |
|||
_err "You didn't specify the Azure Client Secret" |
|||
return 1 |
|||
fi |
|||
|
|||
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") |
|||
|
|||
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then |
|||
_err "invalid domain" |
|||
return 1 |
|||
fi |
|||
_debug _domain_id "$_domain_id" |
|||
_debug _sub_domain "$_sub_domain" |
|||
_debug _domain "$_domain" |
|||
|
|||
acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" |
|||
_debug "$acmeRecordURI" |
|||
# Get existing TXT record |
|||
_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")" |
|||
values="" |
|||
comma="" |
|||
for v in $vlist; do |
|||
values="$values$comma{\"value\":[\"$v\"]}" |
|||
comma="," |
|||
done |
|||
if [ -z "$values" ]; then |
|||
# No values left remove record |
|||
_debug "removing validation record completely $acmeRecordURI" |
|||
_azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" |
|||
if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then |
|||
_info "validation record removed" |
|||
else |
|||
_err "error removing validation record ($_code)" |
|||
return 1 |
|||
fi |
|||
else |
|||
# Remove only txtvalue from the TXT Record |
|||
body="{\"properties\":{\"metadata\":{\"acmetscheck\":\"$timestamp\"},\"TTL\":10, \"TXTRecords\":[$values]}}" |
|||
_azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" |
|||
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then |
|||
_info "validation value removed" |
|||
return 0 |
|||
else |
|||
_err "error removing validation value ($_code)" |
|||
return 1 |
|||
fi |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
################### Private functions below ################################## |
|||
|
|||
_azure_rest() { |
|||
m=$1 |
|||
ep="$2" |
|||
data="$3" |
|||
accesstoken="$4" |
|||
|
|||
MAX_REQUEST_RETRY_TIMES=5 |
|||
_request_retry_times=0 |
|||
while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do |
|||
_debug3 _request_retry_times "$_request_retry_times" |
|||
export _H1="authorization: Bearer $accesstoken" |
|||
export _H2="accept: application/json" |
|||
export _H3="Content-Type: application/json" |
|||
# clear headers from previous request to avoid getting wrong http code on timeouts |
|||
:>"$HTTP_HEADER" |
|||
_debug "$ep" |
|||
if [ "$m" != "GET" ]; then |
|||
_secure_debug2 "data $data" |
|||
response="$(_post "$data" "$ep" "" "$m")" |
|||
else |
|||
response="$(_get "$ep")" |
|||
fi |
|||
_ret="$?" |
|||
_secure_debug2 "response $response" |
|||
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" |
|||
_debug "http response code $_code" |
|||
if [ "$_code" = "401" ]; then |
|||
# we have an invalid access token set to expired |
|||
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0" |
|||
_err "access denied make sure your Azure settings are correct. See $WIKI" |
|||
return 1 |
|||
fi |
|||
# See https://docs.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes |
|||
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then |
|||
_request_retry_times="$(_math "$_request_retry_times" + 1)" |
|||
_info "REST call error $_code retrying $ep in $_request_retry_times s" |
|||
_sleep "$_request_retry_times" |
|||
continue |
|||
fi |
|||
break |
|||
done |
|||
if [ "$_request_retry_times" = "$MAX_REQUEST_RETRY_TIMES" ]; then |
|||
_err "Error Azure REST called was retried $MAX_REQUEST_RETRY_TIMES times." |
|||
_err "Calling $ep failed." |
|||
return 1 |
|||
fi |
|||
response="$(echo "$response" | _normalizeJson)" |
|||
return 0 |
|||
} |
|||
|
|||
## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token |
|||
_azure_getaccess_token() { |
|||
tenantID=$1 |
|||
clientID=$2 |
|||
clientSecret=$3 |
|||
|
|||
accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}" |
|||
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}" |
|||
|
|||
# can we reuse the bearer token? |
|||
if [ -n "$accesstoken" ] && [ -n "$expires_on" ]; then |
|||
if [ "$(_time)" -lt "$expires_on" ]; then |
|||
# brearer token is still valid - reuse it |
|||
_debug "reusing bearer token" |
|||
printf "%s" "$accesstoken" |
|||
return 0 |
|||
else |
|||
_debug "bearer token expired" |
|||
fi |
|||
fi |
|||
_debug "getting new bearer token" |
|||
|
|||
export _H1="accept: application/json" |
|||
export _H2="Content-Type: application/x-www-form-urlencoded" |
|||
|
|||
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials" |
|||
_secure_debug2 "data $body" |
|||
response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")" |
|||
_ret="$?" |
|||
_secure_debug2 "response $response" |
|||
response="$(echo "$response" | _normalizeJson)" |
|||
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") |
|||
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") |
|||
|
|||
if [ -z "$accesstoken" ]; then |
|||
_err "no acccess token received. Check your Azure settings see $WIKI" |
|||
return 1 |
|||
fi |
|||
if [ "$_ret" != "0" ]; then |
|||
_err "error $response" |
|||
return 1 |
|||
fi |
|||
_saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$accesstoken" |
|||
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on" |
|||
printf "%s" "$accesstoken" |
|||
return 0 |
|||
} |
|||
|
|||
_get_root() { |
|||
domain=$1 |
|||
subscriptionId=$2 |
|||
accesstoken=$3 |
|||
i=2 |
|||
p=1 |
|||
|
|||
## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list |
|||
## returns up to 100 zones in one response therefore handling more results is not not implemented |
|||
## (ZoneListResult with continuation token for the next page of results) |
|||
## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways |
|||
## |
|||
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" "$accesstoken" |
|||
# Find matching domain name is Json response |
|||
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 "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") |
|||
if [ "$_domain_id" ]; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain=$h |
|||
return 0 |
|||
fi |
|||
return 1 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
} |
@ -0,0 +1,184 @@ |
|||
#!/usr/bin/env sh |
|||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- |
|||
# vim: et ts=2 sw=2 |
|||
# |
|||
# DirectAdmin 1.41.0 API |
|||
# The DirectAdmin interface has it's own Let's encrypt functionality, but this |
|||
# script can be used to generate certificates for names which are not hosted on |
|||
# DirectAdmin |
|||
# |
|||
# User must provide login data and URL to DirectAdmin incl. port. |
|||
# You can create login key, by using the Login Keys function |
|||
# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to |
|||
# - CMD_API_DNS_CONTROL |
|||
# - CMD_API_SHOW_DOMAINS |
|||
# |
|||
# See also https://www.directadmin.com/api.php and |
|||
# https://www.directadmin.com/features.php?id=1298 |
|||
# |
|||
# Report bugs to https://github.com/TigerP/acme.sh/issues |
|||
# |
|||
# Values to export: |
|||
# export DA_Api="https://remoteUser:remotePassword@da.example.com:8443" |
|||
# export DA_Api_Insecure=1 |
|||
# |
|||
# Set DA_Api_Insecure to 1 for insecure and 0 for secure -> difference is |
|||
# whether ssl cert is checked for validity (0) or whether it is just accepted |
|||
# (1) |
|||
# |
|||
######## Public functions ##################### |
|||
|
|||
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
# Used to add txt record |
|||
dns_da_add() { |
|||
fulldomain="${1}" |
|||
txtvalue="${2}" |
|||
_debug "Calling: dns_da_add() '${fulldomain}' '${txtvalue}'" |
|||
_DA_credentials && _DA_getDomainInfo && _DA_addTxt |
|||
} |
|||
|
|||
# Usage: dns_da_rm _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
# Used to remove the txt record after validation |
|||
dns_da_rm() { |
|||
fulldomain="${1}" |
|||
txtvalue="${2}" |
|||
_debug "Calling: dns_da_rm() '${fulldomain}' '${txtvalue}'" |
|||
_DA_credentials && _DA_getDomainInfo && _DA_rmTxt |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
# Usage: _DA_credentials |
|||
# It will check if the needed settings are available |
|||
_DA_credentials() { |
|||
DA_Api="${DA_Api:-$(_readaccountconf_mutable DA_Api)}" |
|||
DA_Api_Insecure="${DA_Api_Insecure:-$(_readaccountconf_mutable DA_Api_Insecure)}" |
|||
if [ -z "${DA_Api}" ] || [ -z "${DA_Api_Insecure}" ]; then |
|||
DA_Api="" |
|||
DA_Api_Insecure="" |
|||
_err "You haven't specified the DirectAdmin Login data, URL and whether you want check the DirectAdmin SSL cert. Please try again." |
|||
return 1 |
|||
else |
|||
_saveaccountconf_mutable DA_Api "${DA_Api}" |
|||
_saveaccountconf_mutable DA_Api_Insecure "${DA_Api_Insecure}" |
|||
# Set whether curl should use secure or insecure mode |
|||
export HTTPS_INSECURE="${DA_Api_Insecure}" |
|||
fi |
|||
} |
|||
|
|||
# Usage: _get_root _acme-challenge.www.example.com |
|||
# Split the full domain to a domain and subdomain |
|||
#returns |
|||
# _sub_domain=_acme-challenge.www |
|||
# _domain=example.com |
|||
_get_root() { |
|||
domain=$1 |
|||
i=2 |
|||
p=1 |
|||
# Get a list of all the domains |
|||
# response will contain "list[]=example.com&list[]=example.org" |
|||
_da_api CMD_API_SHOW_DOMAINS "" "${domain}" |
|||
while true; do |
|||
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug h "$h" |
|||
if [ -z "$h" ]; then |
|||
# not valid |
|||
_debug "The given domain $h is not valid" |
|||
return 1 |
|||
fi |
|||
if _contains "$response" "$h" >/dev/null; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain=$h |
|||
return 0 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
_debug "Stop on 100" |
|||
return 1 |
|||
} |
|||
|
|||
# Usage: _da_api CMD_API_* data example.com |
|||
# Use the DirectAdmin API and check the result |
|||
# returns |
|||
# response="error=0&text=Result text&details=" |
|||
_da_api() { |
|||
cmd=$1 |
|||
data=$2 |
|||
domain=$3 |
|||
_debug "$domain; $data" |
|||
response="$(_post "$data" "$DA_Api/$cmd" "" "POST")" |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $cmd" |
|||
return 1 |
|||
fi |
|||
_debug response "$response" |
|||
|
|||
case "${cmd}" in |
|||
CMD_API_DNS_CONTROL) |
|||
# Parse the result in general |
|||
# error=0&text=Records Deleted&details= |
|||
# error=1&text=Cannot View Dns Record&details=No domain provided |
|||
err_field="$(_getfield "$response" 1 '&')" |
|||
txt_field="$(_getfield "$response" 2 '&')" |
|||
details_field="$(_getfield "$response" 3 '&')" |
|||
error="$(_getfield "$err_field" 2 '=')" |
|||
text="$(_getfield "$txt_field" 2 '=')" |
|||
details="$(_getfield "$details_field" 2 '=')" |
|||
_debug "error: ${error}, text: ${text}, details: ${details}" |
|||
if [ "$error" != "0" ]; then |
|||
_err "error $response" |
|||
return 1 |
|||
fi |
|||
;; |
|||
CMD_API_SHOW_DOMAINS) ;; |
|||
esac |
|||
return 0 |
|||
} |
|||
|
|||
# Usage: _DA_getDomainInfo |
|||
# Get the root zone if possible |
|||
_DA_getDomainInfo() { |
|||
_debug "First detect the root zone" |
|||
if ! _get_root "$fulldomain"; then |
|||
_err "invalid domain" |
|||
return 1 |
|||
else |
|||
_debug "The root domain: $_domain" |
|||
_debug "The sub domain: $_sub_domain" |
|||
fi |
|||
return 0 |
|||
} |
|||
|
|||
# Usage: _DA_addTxt |
|||
# Use the API to add a record |
|||
_DA_addTxt() { |
|||
curData="domain=${_domain}&action=add&type=TXT&name=${_sub_domain}&value=\"${txtvalue}\"" |
|||
_debug "Calling _DA_addTxt: '${curData}' '${DA_Api}/CMD_API_DNS_CONTROL'" |
|||
_da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}" |
|||
_debug "Result of _DA_addTxt: '$response'" |
|||
if _contains "${response}" 'error=0'; then |
|||
_debug "Add TXT succeeded" |
|||
return 0 |
|||
fi |
|||
_debug "Add TXT failed" |
|||
return 1 |
|||
} |
|||
|
|||
# Usage: _DA_rmTxt |
|||
# Use the API to remove a record |
|||
_DA_rmTxt() { |
|||
curData="domain=${_domain}&action=select&txtrecs0=name=${_sub_domain}&value=\"${txtvalue}\"" |
|||
_debug "Calling _DA_rmTxt: '${curData}' '${DA_Api}/CMD_API_DNS_CONTROL'" |
|||
if _da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}"; then |
|||
_debug "Result of _DA_rmTxt: '$response'" |
|||
else |
|||
_err "Result of _DA_rmTxt: '$response'" |
|||
fi |
|||
if _contains "${response}" 'error=0'; then |
|||
_debug "RM TXT succeeded" |
|||
return 0 |
|||
fi |
|||
_debug "RM TXT failed" |
|||
return 1 |
|||
} |
@ -0,0 +1,97 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Author: RhinoLance |
|||
#Report Bugs here: https://github.com/RhinoLance/acme.sh |
|||
# |
|||
|
|||
#define the api endpoint |
|||
DH_API_ENDPOINT="https://api.dreamhost.com/" |
|||
querystring="" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_dreamhost_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
if ! validate "$fulldomain" "$txtvalue"; then |
|||
return 1 |
|||
fi |
|||
|
|||
querystring="key=$DH_API_KEY&cmd=dns-add_record&record=$fulldomain&type=TXT&value=$txtvalue" |
|||
if ! submit "$querystring"; then |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
#Usage: fulldomain txtvalue |
|||
#Remove the txt record after validation. |
|||
dns_dreamhost_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
if ! validate "$fulldomain" "$txtvalue"; then |
|||
return 1 |
|||
fi |
|||
|
|||
querystring="key=$DH_API_KEY&cmd=dns-remove_record&record=$fulldomain&type=TXT&value=$txtvalue" |
|||
if ! submit "$querystring"; then |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
#send the command to the api endpoint. |
|||
submit() { |
|||
querystring=$1 |
|||
|
|||
url="$DH_API_ENDPOINT?$querystring" |
|||
|
|||
_debug url "$url" |
|||
|
|||
if ! response="$(_get "$url")"; then |
|||
_err "Error <$1>" |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$2" ]; then |
|||
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" |
|||
if [ -n "$message" ]; then |
|||
_err "$message" |
|||
return 1 |
|||
fi |
|||
fi |
|||
|
|||
_debug response "$response" |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
#check that we have a valid API Key |
|||
validate() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
_info "Using dreamhost" |
|||
_debug fulldomain "$fulldomain" |
|||
_debug txtvalue "$txtvalue" |
|||
|
|||
#retrieve the API key from the environment variable if it exists, otherwise look for a saved key. |
|||
DH_API_KEY="${DH_API_KEY:-$(_readaccountconf_mutable DH_API_KEY)}" |
|||
|
|||
if [ -z "$DH_API_KEY" ]; then |
|||
DH_API_KEY="" |
|||
_err "You didn't specify the DreamHost api key yet (export DH_API_KEY=\"<api key>\")" |
|||
_err "Please login to your control panel, create a key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key to the account conf file. |
|||
_saveaccountconf_mutable DH_API_KEY "$DH_API_KEY" |
|||
} |
@ -0,0 +1,128 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Created by RaidenII, to use DuckDNS's API to add/remove text records |
|||
#06/27/2017 |
|||
|
|||
# Pass credentials before "acme.sh --issue --dns dns_duckdns ..." |
|||
# -- |
|||
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" |
|||
# -- |
|||
# |
|||
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh |
|||
|
|||
DuckDNS_API="https://www.duckdns.org/update" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_duckdns_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}" |
|||
if [ -z "$DuckDNS_Token" ]; then |
|||
_err "You must export variable: DuckDNS_Token" |
|||
_err "The token for your DuckDNS account is necessary." |
|||
_err "You can look it up in your DuckDNS account." |
|||
return 1 |
|||
fi |
|||
|
|||
# Now save the credentials. |
|||
_saveaccountconf_mutable DuckDNS_Token "$DuckDNS_Token" |
|||
|
|||
# Unfortunately, DuckDNS does not seems to support lookup domain through API |
|||
# So I assume your credentials (which are your domain and token) are correct |
|||
# If something goes wrong, we will get a KO response from DuckDNS |
|||
|
|||
if ! _duckdns_get_domain; then |
|||
return 1 |
|||
fi |
|||
|
|||
# Now add the TXT record to DuckDNS |
|||
_info "Trying to add TXT record" |
|||
if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=$txtvalue"; then |
|||
if [ "$response" = "OK" ]; then |
|||
_info "TXT record has been successfully added to your DuckDNS domain." |
|||
_info "Note that all subdomains under this domain uses the same TXT record." |
|||
return 0 |
|||
else |
|||
_err "Errors happened during adding the TXT record, response=$response" |
|||
return 1 |
|||
fi |
|||
else |
|||
_err "Errors happened during adding the TXT record." |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
#Usage: fulldomain txtvalue |
|||
#Remove the txt record after validation. |
|||
dns_duckdns_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}" |
|||
if [ -z "$DuckDNS_Token" ]; then |
|||
_err "You must export variable: DuckDNS_Token" |
|||
_err "The token for your DuckDNS account is necessary." |
|||
_err "You can look it up in your DuckDNS account." |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _duckdns_get_domain; then |
|||
return 1 |
|||
fi |
|||
|
|||
# Now remove the TXT record from DuckDNS |
|||
_info "Trying to remove TXT record" |
|||
if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=&clear=true"; then |
|||
if [ "$response" = "OK" ]; then |
|||
_info "TXT record has been successfully removed from your DuckDNS domain." |
|||
return 0 |
|||
else |
|||
_err "Errors happened during removing the TXT record, response=$response" |
|||
return 1 |
|||
fi |
|||
else |
|||
_err "Errors happened during removing the TXT record." |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
#fulldomain=_acme-challenge.domain.duckdns.org |
|||
#returns |
|||
# _duckdns_domain=domain |
|||
_duckdns_get_domain() { |
|||
|
|||
# We'll extract the domain/username from full domain |
|||
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.]duckdns.org' | cut -d . -f 2)" |
|||
|
|||
if [ -z "$_duckdns_domain" ]; then |
|||
_err "Error extracting the domain." |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
#Usage: method URI |
|||
_duckdns_rest() { |
|||
method=$1 |
|||
param="$2" |
|||
_debug param "$param" |
|||
url="$DuckDNS_API?$param" |
|||
_debug url "$url" |
|||
|
|||
# DuckDNS uses GET to update domain info |
|||
if [ "$method" = "GET" ]; then |
|||
response="$(_get "$url")" |
|||
else |
|||
_err "Unsupported method" |
|||
return 1 |
|||
fi |
|||
|
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
@ -0,0 +1,339 @@ |
|||
#!/usr/bin/env sh |
|||
# |
|||
# Dyn.com Domain API |
|||
# |
|||
# Author: Gerd Naschenweng |
|||
# https://github.com/magicdude4eva |
|||
# |
|||
# Dyn Managed DNS API |
|||
# https://help.dyn.com/dns-api-knowledge-base/ |
|||
# |
|||
# It is recommended to add a "Dyn Managed DNS" user specific for API access. |
|||
# The "Zones & Records Permissions" required by this script are: |
|||
# -- |
|||
# RecordAdd |
|||
# RecordUpdate |
|||
# RecordDelete |
|||
# RecordGet |
|||
# ZoneGet |
|||
# ZoneAddNode |
|||
# ZoneRemoveNode |
|||
# ZonePublish |
|||
# -- |
|||
# |
|||
# Pass credentials before "acme.sh --issue --dns dns_dyn ..." |
|||
# -- |
|||
# export DYN_Customer="customer" |
|||
# export DYN_Username="apiuser" |
|||
# export DYN_Password="secret" |
|||
# -- |
|||
|
|||
DYN_API="https://api.dynect.net/REST" |
|||
|
|||
#REST_API |
|||
######## Public functions ##################### |
|||
|
|||
#Usage: add _acme-challenge.www.domain.com "Challenge-code" |
|||
dns_dyn_add() { |
|||
fulldomain="$1" |
|||
txtvalue="$2" |
|||
|
|||
DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" |
|||
DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" |
|||
DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" |
|||
if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then |
|||
DYN_Customer="" |
|||
DYN_Username="" |
|||
DYN_Password="" |
|||
_err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" |
|||
return 1 |
|||
fi |
|||
|
|||
#save the config variables to the account conf file. |
|||
_saveaccountconf_mutable DYN_Customer "$DYN_Customer" |
|||
_saveaccountconf_mutable DYN_Username "$DYN_Username" |
|||
_saveaccountconf_mutable DYN_Password "$DYN_Password" |
|||
|
|||
if ! _dyn_get_authtoken; then |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$_dyn_authtoken" ]; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_get_zone; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_add_record; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_publish_zone; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
_dyn_end_session |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
#Usage: fulldomain txtvalue |
|||
#Remove the txt record after validation. |
|||
dns_dyn_rm() { |
|||
fulldomain="$1" |
|||
txtvalue="$2" |
|||
|
|||
DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" |
|||
DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" |
|||
DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" |
|||
if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then |
|||
DYN_Customer="" |
|||
DYN_Username="" |
|||
DYN_Password="" |
|||
_err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_get_authtoken; then |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$_dyn_authtoken" ]; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_get_zone; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_get_record_id; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$_dyn_record_id" ]; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_rm_record; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _dyn_publish_zone; then |
|||
_dyn_end_session |
|||
return 1 |
|||
fi |
|||
|
|||
_dyn_end_session |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
#get Auth-Token |
|||
_dyn_get_authtoken() { |
|||
|
|||
_info "Start Dyn API Session" |
|||
|
|||
data="{\"customer_name\":\"$DYN_Customer\", \"user_name\":\"$DYN_Username\", \"password\":\"$DYN_Password\"}" |
|||
dyn_url="$DYN_API/Session/" |
|||
method="POST" |
|||
|
|||
_debug data "$data" |
|||
_debug dyn_url "$dyn_url" |
|||
|
|||
export _H1="Content-Type: application/json" |
|||
|
|||
response="$(_post "$data" "$dyn_url" "" "$method")" |
|||
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" |
|||
|
|||
_debug response "$response" |
|||
_debug sessionstatus "$sessionstatus" |
|||
|
|||
if [ "$sessionstatus" = "success" ]; then |
|||
_dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')" |
|||
_info "Token received" |
|||
_debug _dyn_authtoken "$_dyn_authtoken" |
|||
return 0 |
|||
fi |
|||
|
|||
_dyn_authtoken="" |
|||
_err "get token failed" |
|||
return 1 |
|||
} |
|||
|
|||
#fulldomain=_acme-challenge.www.domain.com |
|||
#returns |
|||
# _dyn_zone=domain.com |
|||
_dyn_get_zone() { |
|||
i=2 |
|||
while true; do |
|||
domain="$(printf "%s" "$fulldomain" | cut -d . -f "$i-100")" |
|||
if [ -z "$domain" ]; then |
|||
break |
|||
fi |
|||
|
|||
dyn_url="$DYN_API/Zone/$domain/" |
|||
|
|||
export _H1="Auth-Token: $_dyn_authtoken" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
response="$(_get "$dyn_url" "" "")" |
|||
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" |
|||
|
|||
_debug dyn_url "$dyn_url" |
|||
_debug response "$response" |
|||
_debug sessionstatus "$sessionstatus" |
|||
|
|||
if [ "$sessionstatus" = "success" ]; then |
|||
_dyn_zone="$domain" |
|||
return 0 |
|||
fi |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
|
|||
_dyn_zone="" |
|||
_err "get zone failed" |
|||
return 1 |
|||
} |
|||
|
|||
#add TXT record |
|||
_dyn_add_record() { |
|||
|
|||
_info "Adding TXT record" |
|||
|
|||
data="{\"rdata\":{\"txtdata\":\"$txtvalue\"},\"ttl\":\"300\"}" |
|||
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" |
|||
method="POST" |
|||
|
|||
export _H1="Auth-Token: $_dyn_authtoken" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
response="$(_post "$data" "$dyn_url" "" "$method")" |
|||
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" |
|||
|
|||
_debug response "$response" |
|||
_debug sessionstatus "$sessionstatus" |
|||
|
|||
if [ "$sessionstatus" = "success" ]; then |
|||
_info "TXT Record successfully added" |
|||
return 0 |
|||
fi |
|||
|
|||
_err "add TXT record failed" |
|||
return 1 |
|||
} |
|||
|
|||
#publish the zone |
|||
_dyn_publish_zone() { |
|||
|
|||
_info "Publishing zone" |
|||
|
|||
data="{\"publish\":\"true\"}" |
|||
dyn_url="$DYN_API/Zone/$_dyn_zone/" |
|||
method="PUT" |
|||
|
|||
export _H1="Auth-Token: $_dyn_authtoken" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
response="$(_post "$data" "$dyn_url" "" "$method")" |
|||
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" |
|||
|
|||
_debug response "$response" |
|||
_debug sessionstatus "$sessionstatus" |
|||
|
|||
if [ "$sessionstatus" = "success" ]; then |
|||
_info "Zone published" |
|||
return 0 |
|||
fi |
|||
|
|||
_err "publish zone failed" |
|||
return 1 |
|||
} |
|||
|
|||
#get record_id of TXT record so we can delete the record |
|||
_dyn_get_record_id() { |
|||
|
|||
_info "Getting record_id of TXT record" |
|||
|
|||
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" |
|||
|
|||
export _H1="Auth-Token: $_dyn_authtoken" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
response="$(_get "$dyn_url" "" "")" |
|||
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" |
|||
|
|||
_debug response "$response" |
|||
_debug sessionstatus "$sessionstatus" |
|||
|
|||
if [ "$sessionstatus" = "success" ]; then |
|||
_dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | _head_n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")" |
|||
_debug _dyn_record_id "$_dyn_record_id" |
|||
return 0 |
|||
fi |
|||
|
|||
_dyn_record_id="" |
|||
_err "getting record_id failed" |
|||
return 1 |
|||
} |
|||
|
|||
#delete TXT record |
|||
_dyn_rm_record() { |
|||
|
|||
_info "Deleting TXT record" |
|||
|
|||
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/$_dyn_record_id/" |
|||
method="DELETE" |
|||
|
|||
_debug dyn_url "$dyn_url" |
|||
|
|||
export _H1="Auth-Token: $_dyn_authtoken" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
response="$(_post "" "$dyn_url" "" "$method")" |
|||
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" |
|||
|
|||
_debug response "$response" |
|||
_debug sessionstatus "$sessionstatus" |
|||
|
|||
if [ "$sessionstatus" = "success" ]; then |
|||
_info "TXT record successfully deleted" |
|||
return 0 |
|||
fi |
|||
|
|||
_err "delete TXT record failed" |
|||
return 1 |
|||
} |
|||
|
|||
#logout |
|||
_dyn_end_session() { |
|||
|
|||
_info "End Dyn API Session" |
|||
|
|||
dyn_url="$DYN_API/Session/" |
|||
method="DELETE" |
|||
|
|||
_debug dyn_url "$dyn_url" |
|||
|
|||
export _H1="Auth-Token: $_dyn_authtoken" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
response="$(_post "" "$dyn_url" "" "$method")" |
|||
|
|||
_debug response "$response" |
|||
|
|||
_dyn_authtoken="" |
|||
return 0 |
|||
} |
@ -0,0 +1,158 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
######################################################################## |
|||
# Hurricane Electric hook script for acme.sh |
|||
# |
|||
# Environment variables: |
|||
# |
|||
# - $HE_Username (your dns.he.net username) |
|||
# - $HE_Password (your dns.he.net password) |
|||
# |
|||
# Author: Ondrej Simek <me@ondrejsimek.com> |
|||
# Git repo: https://github.com/angel333/acme.sh |
|||
|
|||
#-- dns_he_add() - Add TXT record -------------------------------------- |
|||
# Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..." |
|||
|
|||
dns_he_add() { |
|||
_full_domain=$1 |
|||
_txt_value=$2 |
|||
_info "Using DNS-01 Hurricane Electric hook" |
|||
|
|||
HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}" |
|||
HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}" |
|||
if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then |
|||
HE_Username= |
|||
HE_Password= |
|||
_err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password envoronment variables." |
|||
return 1 |
|||
fi |
|||
_saveaccountconf_mutable HE_Username "$HE_Username" |
|||
_saveaccountconf_mutable HE_Password "$HE_Password" |
|||
|
|||
# Fills in the $_zone_id |
|||
_find_zone "$_full_domain" || return 1 |
|||
_debug "Zone id \"$_zone_id\" will be used." |
|||
|
|||
body="email=${HE_Username}&pass=${HE_Password}" |
|||
body="$body&account=" |
|||
body="$body&menu=edit_zone" |
|||
body="$body&Type=TXT" |
|||
body="$body&hosted_dns_zoneid=$_zone_id" |
|||
body="$body&hosted_dns_recordid=" |
|||
body="$body&hosted_dns_editzone=1" |
|||
body="$body&Priority=" |
|||
body="$body&Name=$_full_domain" |
|||
body="$body&Content=$_txt_value" |
|||
body="$body&TTL=300" |
|||
body="$body&hosted_dns_editrecord=Submit" |
|||
response="$(_post "$body" "https://dns.he.net/")" |
|||
exit_code="$?" |
|||
if [ "$exit_code" -eq 0 ]; then |
|||
_info "TXT record added successfully." |
|||
else |
|||
_err "Couldn't add the TXT record." |
|||
fi |
|||
_debug2 response "$response" |
|||
return "$exit_code" |
|||
} |
|||
|
|||
#-- dns_he_rm() - Remove TXT record ------------------------------------ |
|||
# Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..." |
|||
|
|||
dns_he_rm() { |
|||
_full_domain=$1 |
|||
_txt_value=$2 |
|||
_info "Cleaning up after DNS-01 Hurricane Electric hook" |
|||
HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}" |
|||
HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}" |
|||
# fills in the $_zone_id |
|||
_find_zone "$_full_domain" || return 1 |
|||
_debug "Zone id \"$_zone_id\" will be used." |
|||
|
|||
# Find the record id to clean |
|||
body="email=${HE_Username}&pass=${HE_Password}" |
|||
body="$body&hosted_dns_zoneid=$_zone_id" |
|||
body="$body&menu=edit_zone" |
|||
body="$body&hosted_dns_editzone=" |
|||
|
|||
response="$(_post "$body" "https://dns.he.net/")" |
|||
_debug2 "response" "$response" |
|||
if ! _contains "$response" "$_txt_value"; then |
|||
_debug "The txt record is not found, just skip" |
|||
return 0 |
|||
fi |
|||
_record_id="$(echo "$response" | tr -d "#" | sed "s/<tr/#<tr/g" | tr -d "\n" | tr "#" "\n" | grep "$_full_domain" | grep '"dns_tr"' | grep "$_txt_value" | cut -d '"' -f 4)" |
|||
_debug2 _record_id "$_record_id" |
|||
if [ -z "$_record_id" ]; then |
|||
_err "Can not find record id" |
|||
return 1 |
|||
fi |
|||
# Remove the record |
|||
body="email=${HE_Username}&pass=${HE_Password}" |
|||
body="$body&menu=edit_zone" |
|||
body="$body&hosted_dns_zoneid=$_zone_id" |
|||
body="$body&hosted_dns_recordid=$_record_id" |
|||
body="$body&hosted_dns_editzone=1" |
|||
body="$body&hosted_dns_delrecord=1" |
|||
body="$body&hosted_dns_delconfirm=delete" |
|||
_post "$body" "https://dns.he.net/" \ |
|||
| grep '<div id="dns_status" onClick="hideThis(this);">Successfully removed record.</div>' \ |
|||
>/dev/null |
|||
exit_code="$?" |
|||
if [ "$exit_code" -eq 0 ]; then |
|||
_info "Record removed successfully." |
|||
else |
|||
_err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand." |
|||
return "$exit_code" |
|||
fi |
|||
} |
|||
|
|||
########################## PRIVATE FUNCTIONS ########################### |
|||
|
|||
_find_zone() { |
|||
_domain="$1" |
|||
body="email=${HE_Username}&pass=${HE_Password}" |
|||
response="$(_post "$body" "https://dns.he.net/")" |
|||
_debug2 response "$response" |
|||
_table="$(echo "$response" | tr -d "#" | sed "s/<table/#<table/g" | tr -d "\n" | tr "#" "\n" | grep 'id="domains_table"')" |
|||
_debug2 _table "$_table" |
|||
_matches="$(echo "$_table" | sed "s/<tr/#<tr/g" | tr "#" "\n" | grep 'alt="edit"' | tr -d " " | sed "s/<td/#<td/g" | tr "#" "\n" | grep 'hosted_dns_zoneid')" |
|||
_debug2 _matches "$_matches" |
|||
# Zone names and zone IDs are in same order |
|||
_zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&') |
|||
_zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2) |
|||
_debug2 "These are the zones on this HE account:" |
|||
_debug2 "$_zone_names" |
|||
_debug2 "And these are their respective IDs:" |
|||
_debug2 "$_zone_ids" |
|||
if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then |
|||
_err "Can not get zone names." |
|||
return 1 |
|||
fi |
|||
# Walk through all possible zone names |
|||
_strip_counter=1 |
|||
while true; do |
|||
_attempted_zone=$(echo "$_domain" | cut -d . -f ${_strip_counter}-) |
|||
|
|||
# All possible zone names have been tried |
|||
if [ -z "$_attempted_zone" ]; then |
|||
_err "No zone for domain \"$_domain\" found." |
|||
return 1 |
|||
fi |
|||
|
|||
_debug "Looking for zone \"${_attempted_zone}\"" |
|||
|
|||
line_num="$(echo "$_zone_names" | grep -n "$_attempted_zone" | cut -d : -f 1)" |
|||
|
|||
if [ "$line_num" ]; then |
|||
_zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p") |
|||
_debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." |
|||
return 0 |
|||
fi |
|||
|
|||
_debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less specific zone." |
|||
_strip_counter=$(_math "$_strip_counter" + 1) |
|||
done |
|||
} |
|||
# vim: et:ts=2:sw=2: |
@ -0,0 +1,311 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# |
|||
#INWX_User="username" |
|||
# |
|||
#INWX_Password="password" |
|||
|
|||
INWX_Api="https://api.domrobot.com/xmlrpc/" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_inwx_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}" |
|||
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}" |
|||
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then |
|||
INWX_User="" |
|||
INWX_Password="" |
|||
_err "You don't specify inwx user and password yet." |
|||
_err "Please create you key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key and email to the account conf file. |
|||
_saveaccountconf_mutable INWX_User "$INWX_User" |
|||
_saveaccountconf_mutable INWX_Password "$INWX_Password" |
|||
|
|||
_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" |
|||
|
|||
_info "Adding record" |
|||
_inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" |
|||
|
|||
} |
|||
|
|||
#fulldomain txtvalue |
|||
dns_inwx_rm() { |
|||
|
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}" |
|||
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}" |
|||
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then |
|||
INWX_User="" |
|||
INWX_Password="" |
|||
_err "You don't specify inwx user and password yet." |
|||
_err "Please create you key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key and email to the account conf file. |
|||
_saveaccountconf_mutable INWX_User "$INWX_User" |
|||
_saveaccountconf_mutable INWX_Password "$INWX_Password" |
|||
|
|||
_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 txt records" |
|||
|
|||
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> |
|||
<methodCall> |
|||
<methodName>nameserver.info</methodName> |
|||
<params> |
|||
<param> |
|||
<value> |
|||
<struct> |
|||
<member> |
|||
<name>domain</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>type</name> |
|||
<value> |
|||
<string>TXT</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>name</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
</struct> |
|||
</value> |
|||
</param> |
|||
</params> |
|||
</methodCall>' "$_domain" "$_sub_domain") |
|||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" |
|||
|
|||
if ! _contains "$response" "Command completed successfully"; then |
|||
_err "Error could not get txt records" |
|||
return 1 |
|||
fi |
|||
|
|||
if ! printf "%s" "$response" | grep "count" >/dev/null; then |
|||
_info "Do not need to delete record" |
|||
else |
|||
_record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+') |
|||
_info "Deleting record" |
|||
_inwx_delete_record "$_record_id" |
|||
fi |
|||
|
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
_inwx_login() { |
|||
|
|||
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> |
|||
<methodCall> |
|||
<methodName>account.login</methodName> |
|||
<params> |
|||
<param> |
|||
<value> |
|||
<struct> |
|||
<member> |
|||
<name>user</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>pass</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
</struct> |
|||
</value> |
|||
</param> |
|||
</params> |
|||
</methodCall>' $INWX_User $INWX_Password) |
|||
|
|||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" |
|||
|
|||
printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')" |
|||
|
|||
} |
|||
|
|||
_get_root() { |
|||
domain=$1 |
|||
_debug "get root" |
|||
|
|||
domain=$1 |
|||
i=2 |
|||
p=1 |
|||
|
|||
_H1=$(_inwx_login) |
|||
export _H1 |
|||
xml_content='<?xml version="1.0" encoding="UTF-8"?> |
|||
<methodCall> |
|||
<methodName>nameserver.list</methodName> |
|||
</methodCall>' |
|||
|
|||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" |
|||
while true; do |
|||
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug h "$h" |
|||
if [ -z "$h" ]; then |
|||
#not valid |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "$h"; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain="$h" |
|||
return 0 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
|
|||
} |
|||
|
|||
_inwx_delete_record() { |
|||
record_id=$1 |
|||
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> |
|||
<methodCall> |
|||
<methodName>nameserver.deleteRecord</methodName> |
|||
<params> |
|||
<param> |
|||
<value> |
|||
<struct> |
|||
<member> |
|||
<name>id</name> |
|||
<value> |
|||
<int>%s</int> |
|||
</value> |
|||
</member> |
|||
</struct> |
|||
</value> |
|||
</param> |
|||
</params> |
|||
</methodCall>' "$record_id") |
|||
|
|||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" |
|||
|
|||
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then |
|||
_err "Error" |
|||
return 1 |
|||
fi |
|||
return 0 |
|||
|
|||
} |
|||
|
|||
_inwx_update_record() { |
|||
record_id=$1 |
|||
txtval=$2 |
|||
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> |
|||
<methodCall> |
|||
<methodName>nameserver.updateRecord</methodName> |
|||
<params> |
|||
<param> |
|||
<value> |
|||
<struct> |
|||
<member> |
|||
<name>content</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>id</name> |
|||
<value> |
|||
<int>%s</int> |
|||
</value> |
|||
</member> |
|||
</struct> |
|||
</value> |
|||
</param> |
|||
</params> |
|||
</methodCall>' "$txtval" "$record_id") |
|||
|
|||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" |
|||
|
|||
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then |
|||
_err "Error" |
|||
return 1 |
|||
fi |
|||
return 0 |
|||
|
|||
} |
|||
|
|||
_inwx_add_record() { |
|||
|
|||
domain=$1 |
|||
sub_domain=$2 |
|||
txtval=$3 |
|||
|
|||
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> |
|||
<methodCall> |
|||
<methodName>nameserver.createRecord</methodName> |
|||
<params> |
|||
<param> |
|||
<value> |
|||
<struct> |
|||
<member> |
|||
<name>domain</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>type</name> |
|||
<value> |
|||
<string>TXT</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>content</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
<member> |
|||
<name>name</name> |
|||
<value> |
|||
<string>%s</string> |
|||
</value> |
|||
</member> |
|||
</struct> |
|||
</value> |
|||
</param> |
|||
</params> |
|||
</methodCall>' "$domain" "$txtval" "$sub_domain") |
|||
|
|||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")" |
|||
|
|||
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then |
|||
_err "Error" |
|||
return 1 |
|||
fi |
|||
return 0 |
|||
} |
@ -0,0 +1,166 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Author: RaidenII |
|||
#Created 06/28/2017 |
|||
#Updated 03/01/2018, rewrote to support name.com API v4 |
|||
#Utilize name.com API to finish dns-01 verifications. |
|||
######## Public functions ##################### |
|||
|
|||
Namecom_API="https://api.name.com/v4" |
|||
|
|||
#Usage: dns_namecom_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_namecom_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
# First we need name.com credentials. |
|||
if [ -z "$Namecom_Username" ]; then |
|||
Namecom_Username="" |
|||
_err "Username for name.com is missing." |
|||
_err "Please specify that in your environment variable." |
|||
return 1 |
|||
fi |
|||
|
|||
if [ -z "$Namecom_Token" ]; then |
|||
Namecom_Token="" |
|||
_err "API token for name.com is missing." |
|||
_err "Please specify that in your environment variable." |
|||
return 1 |
|||
fi |
|||
|
|||
# Save them in configuration. |
|||
_saveaccountconf Namecom_Username "$Namecom_Username" |
|||
_saveaccountconf Namecom_Token "$Namecom_Token" |
|||
|
|||
# Login in using API |
|||
if ! _namecom_login; then |
|||
return 1 |
|||
fi |
|||
|
|||
# Find domain in domain list. |
|||
if ! _namecom_get_root "$fulldomain"; then |
|||
_err "Unable to find domain specified." |
|||
return 1 |
|||
fi |
|||
|
|||
# Add TXT record. |
|||
_namecom_addtxt_json="{\"host\":\"$_sub_domain\",\"type\":\"TXT\",\"answer\":\"$txtvalue\",\"ttl\":\"300\"}" |
|||
if _namecom_rest POST "domains/$_domain/records" "$_namecom_addtxt_json"; then |
|||
_retvalue=$(printf "%s\n" "$response" | _egrep_o "\"$_sub_domain\"") |
|||
if [ "$_retvalue" ]; then |
|||
_info "Successfully added TXT record, ready for validation." |
|||
return 0 |
|||
else |
|||
_err "Unable to add the DNS record." |
|||
return 1 |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
#Usage: fulldomain txtvalue |
|||
#Remove the txt record after validation. |
|||
dns_namecom_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
if ! _namecom_login; then |
|||
return 1 |
|||
fi |
|||
|
|||
# Find domain in domain list. |
|||
if ! _namecom_get_root "$fulldomain"; then |
|||
_err "Unable to find domain specified." |
|||
return 1 |
|||
fi |
|||
|
|||
# Get the record id. |
|||
if _namecom_rest GET "domains/$_domain/records"; then |
|||
_record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+,\"domainName\":\"$_domain\",\"host\":\"$_sub_domain\",\"fqdn\":\"$fulldomain.\",\"type\":\"TXT\",\"answer\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+) |
|||
_debug record_id "$_record_id" |
|||
if [ "$_record_id" ]; then |
|||
_info "Successfully retrieved the record id for ACME challenge." |
|||
else |
|||
_err "Unable to retrieve the record id." |
|||
return 1 |
|||
fi |
|||
fi |
|||
|
|||
# Remove the DNS record using record id. |
|||
if _namecom_rest DELETE "domains/$_domain/records/$_record_id"; then |
|||
_info "Successfully removed the TXT record." |
|||
return 0 |
|||
else |
|||
_err "Unable to delete record id." |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
_namecom_rest() { |
|||
method=$1 |
|||
param=$2 |
|||
data=$3 |
|||
|
|||
export _H1="Authorization: Basic $_namecom_auth" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
if [ "$method" != "GET" ]; then |
|||
response="$(_post "$data" "$Namecom_API/$param" "" "$method")" |
|||
else |
|||
response="$(_get "$Namecom_API/$param")" |
|||
fi |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $param" |
|||
return 1 |
|||
fi |
|||
|
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
|||
|
|||
_namecom_login() { |
|||
# Auth string |
|||
# Name.com API v4 uses http basic auth to authenticate |
|||
# need to convert the token for http auth |
|||
_namecom_auth=$(printf "%s:%s" "$Namecom_Username" "$Namecom_Token" | base64) |
|||
|
|||
if _namecom_rest GET "hello"; then |
|||
retcode=$(printf "%s\n" "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"") |
|||
if [ "$retcode" ]; then |
|||
_info "Successfully logged in." |
|||
else |
|||
_err "Logging in failed." |
|||
return 1 |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
_namecom_get_root() { |
|||
domain=$1 |
|||
i=2 |
|||
p=1 |
|||
|
|||
if ! _namecom_rest GET "domains"; then |
|||
return 1 |
|||
fi |
|||
|
|||
# Need to exclude the last field (tld) |
|||
numfields=$(echo "$domain" | _egrep_o "\." | wc -l) |
|||
while [ $i -le "$numfields" ]; do |
|||
host=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug host "$host" |
|||
if [ -z "$host" ]; then |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "$host"; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain="$host" |
|||
return 0 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
} |
@ -0,0 +1,137 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
#Author: meowthink |
|||
#Created 01/14/2017 |
|||
#Utilize namesilo.com API to finish dns-01 verifications. |
|||
|
|||
Namesilo_API="https://www.namesilo.com/api" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_namesilo_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
if [ -z "$Namesilo_Key" ]; then |
|||
Namesilo_Key="" |
|||
_err "API token for namesilo.com is missing." |
|||
_err "Please specify that in your environment variable." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key and email to the account conf file. |
|||
_saveaccountconf Namesilo_Key "$Namesilo_Key" |
|||
|
|||
if ! _get_root "$fulldomain"; then |
|||
_err "Unable to find domain specified." |
|||
return 1 |
|||
fi |
|||
|
|||
_debug _sub_domain "$_sub_domain" |
|||
_debug _domain "$_domain" |
|||
|
|||
_debug txtvalue "$txtvalue" |
|||
if _namesilo_rest GET "dnsAddRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrtype=TXT&rrhost=$_sub_domain&rrvalue=$txtvalue"; then |
|||
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300") |
|||
if [ "$retcode" ]; then |
|||
_info "Successfully added TXT record, ready for validation." |
|||
return 0 |
|||
else |
|||
_err "Unable to add the DNS record." |
|||
return 1 |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
#Usage: fulldomain txtvalue |
|||
#Remove the txt record after validation. |
|||
dns_namesilo_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
if ! _get_root "$fulldomain"; then |
|||
_err "Unable to find domain specified." |
|||
return 1 |
|||
fi |
|||
|
|||
# Get the record id. |
|||
if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then |
|||
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300") |
|||
if [ "$retcode" ]; then |
|||
_record_id=$(printf "%s\n" "$response" | _egrep_o "<record_id>([^<]*)</record_id><type>TXT</type><host>$fulldomain</host>" | _egrep_o "<record_id>([^<]*)</record_id>" | sed -r "s/<record_id>([^<]*)<\/record_id>/\1/" | tail -n 1) |
|||
_debug record_id "$_record_id" |
|||
_info "Successfully retrieved the record id for ACME challenge." |
|||
else |
|||
_err "Unable to retrieve the record id." |
|||
return 1 |
|||
fi |
|||
fi |
|||
|
|||
# Remove the DNS record using record id. |
|||
if _namesilo_rest GET "dnsDeleteRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrid=$_record_id"; then |
|||
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300") |
|||
if [ "$retcode" ]; then |
|||
_info "Successfully removed the TXT record." |
|||
return 0 |
|||
else |
|||
_err "Unable to remove the DNS record." |
|||
return 1 |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
# _acme-challenge.www.domain.com |
|||
# returns |
|||
# _sub_domain=_acme-challenge.www |
|||
# _domain=domain.com |
|||
_get_root() { |
|||
domain=$1 |
|||
i=2 |
|||
p=1 |
|||
|
|||
if ! _namesilo_rest GET "listDomains?version=1&type=xml&key=$Namesilo_Key"; then |
|||
return 1 |
|||
fi |
|||
|
|||
# Need to exclude the last field (tld) |
|||
numfields=$(echo "$domain" | _egrep_o "\." | wc -l) |
|||
while [ $i -le "$numfields" ]; do |
|||
host=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug host "$host" |
|||
if [ -z "$host" ]; then |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "$host"; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain="$host" |
|||
return 0 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
} |
|||
|
|||
_namesilo_rest() { |
|||
method=$1 |
|||
param=$2 |
|||
data=$3 |
|||
|
|||
if [ "$method" != "GET" ]; then |
|||
response="$(_post "$data" "$Namesilo_API/$param" "" "$method")" |
|||
else |
|||
response="$(_get "$Namesilo_API/$param")" |
|||
fi |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $param" |
|||
return 1 |
|||
fi |
|||
|
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
@ -0,0 +1,158 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# bug reports to dev@1e.ca |
|||
|
|||
# |
|||
#NS1_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" |
|||
# |
|||
|
|||
NS1_Api="https://api.nsone.net/v1" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_nsone_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
if [ -z "$NS1_Key" ]; then |
|||
NS1_Key="" |
|||
_err "You didn't specify nsone dns api key yet." |
|||
_err "Please create you key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key and email to the account conf file. |
|||
_saveaccountconf NS1_Key "$NS1_Key" |
|||
|
|||
_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 txt records" |
|||
_nsone_rest GET "zones/${_domain}" |
|||
|
|||
if ! _contains "$response" "\"records\":"; then |
|||
_err "Error" |
|||
return 1 |
|||
fi |
|||
|
|||
count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ") |
|||
_debug count "$count" |
|||
if [ "$count" = "0" ]; then |
|||
_info "Adding record" |
|||
|
|||
if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; then |
|||
if _contains "$response" "$fulldomain"; then |
|||
_info "Added" |
|||
#todo: check if the record takes effect |
|||
return 0 |
|||
else |
|||
_err "Add txt record error." |
|||
return 1 |
|||
fi |
|||
fi |
|||
_err "Add txt record error." |
|||
else |
|||
_info "Updating record" |
|||
prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1) |
|||
_debug "prev_txt" "$prev_txt" |
|||
|
|||
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}" |
|||
if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then |
|||
_info "Updated!" |
|||
#todo: check if the record takes effect |
|||
return 0 |
|||
fi |
|||
_err "Update error" |
|||
return 1 |
|||
fi |
|||
|
|||
} |
|||
|
|||
#fulldomain |
|||
dns_nsone_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
_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 txt records" |
|||
_nsone_rest GET "zones/${_domain}/$fulldomain/TXT" |
|||
|
|||
count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",.*\"type\":\"TXT\"" | wc -l | tr -d " ") |
|||
_debug count "$count" |
|||
if [ "$count" = "0" ]; then |
|||
_info "Don't need to remove." |
|||
else |
|||
if ! _nsone_rest DELETE "zones/${_domain}/$fulldomain/TXT"; then |
|||
_err "Delete record error." |
|||
return 1 |
|||
fi |
|||
_contains "$response" "" |
|||
fi |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
#_acme-challenge.www.domain.com |
|||
#returns |
|||
# _sub_domain=_acme-challenge.www |
|||
# _domain=domain.com |
|||
# _domain_id=sdjkglgdfewsdfg |
|||
_get_root() { |
|||
domain=$1 |
|||
i=2 |
|||
p=1 |
|||
if ! _nsone_rest GET "zones"; then |
|||
return 1 |
|||
fi |
|||
while true; do |
|||
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug h "$h" |
|||
if [ -z "$h" ]; then |
|||
#not valid |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "\"zone\":\"$h\""; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain="$h" |
|||
return 0 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
} |
|||
|
|||
_nsone_rest() { |
|||
m=$1 |
|||
ep="$2" |
|||
data="$3" |
|||
_debug "$ep" |
|||
|
|||
export _H1="Accept: application/json" |
|||
export _H2="X-NSONE-Key: $NS1_Key" |
|||
if [ "$m" != "GET" ]; then |
|||
_debug data "$data" |
|||
response="$(_post "$data" "$NS1_Api/$ep" "" "$m")" |
|||
else |
|||
response="$(_get "$NS1_Api/$ep")" |
|||
fi |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $ep" |
|||
return 1 |
|||
fi |
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
@ -0,0 +1,161 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# |
|||
#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" |
|||
# |
|||
|
|||
SL_Api="https://api.selectel.ru/domains/v1" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_selectel_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" |
|||
|
|||
if [ -z "$SL_Key" ]; then |
|||
SL_Key="" |
|||
_err "You don't specify selectel.ru api key yet." |
|||
_err "Please create you key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key to the account conf file. |
|||
_saveaccountconf_mutable SL_Key "$SL_Key" |
|||
|
|||
_debug "First 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" |
|||
|
|||
_info "Adding record" |
|||
if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then |
|||
if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then |
|||
_info "Added, OK" |
|||
return 0 |
|||
fi |
|||
fi |
|||
_err "Add txt record error." |
|||
return 1 |
|||
} |
|||
|
|||
#fulldomain txtvalue |
|||
dns_selectel_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" |
|||
|
|||
if [ -z "$SL_Key" ]; then |
|||
SL_Key="" |
|||
_err "You don't specify slectel api key yet." |
|||
_err "Please create you key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
_debug "First 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" |
|||
|
|||
_debug "Getting txt records" |
|||
_sl_rest GET "/${_domain_id}/records/" |
|||
|
|||
if ! _contains "$response" "$txtvalue"; then |
|||
_err "Txt record not found" |
|||
return 1 |
|||
fi |
|||
|
|||
_record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")" |
|||
_debug2 "_record_seg" "$_record_seg" |
|||
if [ -z "$_record_seg" ]; then |
|||
_err "can not find _record_seg" |
|||
return 1 |
|||
fi |
|||
|
|||
_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)" |
|||
_debug2 "_record_id" "$_record_id" |
|||
if [ -z "$_record_id" ]; then |
|||
_err "can not find _record_id" |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then |
|||
_err "Delete record error." |
|||
return 1 |
|||
fi |
|||
return 0 |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
#_acme-challenge.www.domain.com |
|||
#returns |
|||
# _sub_domain=_acme-challenge.www |
|||
# _domain=domain.com |
|||
# _domain_id=sdjkglgdfewsdfg |
|||
_get_root() { |
|||
domain=$1 |
|||
|
|||
if ! _sl_rest GET "/"; then |
|||
return 1 |
|||
fi |
|||
|
|||
i=2 |
|||
p=1 |
|||
while true; do |
|||
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug h "$h" |
|||
if [ -z "$h" ]; then |
|||
#not valid |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "\"name\": \"$h\","; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain=$h |
|||
_debug "Getting domain id for $h" |
|||
if ! _sl_rest GET "/$h"; then |
|||
return 1 |
|||
fi |
|||
_domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" |
|||
return 0 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
} |
|||
|
|||
_sl_rest() { |
|||
m=$1 |
|||
ep="$2" |
|||
data="$3" |
|||
_debug "$ep" |
|||
|
|||
export _H1="X-Token: $SL_Key" |
|||
export _H2="Content-Type: application/json" |
|||
|
|||
if [ "$m" != "GET" ]; then |
|||
_debug data "$data" |
|||
response="$(_post "$data" "$SL_Api/$ep" "" "$m")" |
|||
else |
|||
response="$(_get "$SL_Api/$ep")" |
|||
fi |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $ep" |
|||
return 1 |
|||
fi |
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
@ -0,0 +1,170 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
########## |
|||
# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/Neilpang/acme.sh) |
|||
# |
|||
# Usage: |
|||
# export SERVERCOW_API_Username=username |
|||
# export SERVERCOW_API_Password=password |
|||
# acme.sh --issue -d example.com --dns dns_servercow |
|||
# |
|||
# Issues: |
|||
# Any issues / questions / suggestions can be posted here: |
|||
# https://github.com/jhartlep/servercow-dns-api/issues |
|||
# |
|||
# Author: Jens Hartlep |
|||
########## |
|||
|
|||
SERVERCOW_API="https://api.servercow.de/dns/v1/domains" |
|||
|
|||
# Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz" |
|||
dns_servercow_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
_info "Using servercow" |
|||
_debug fulldomain "$fulldomain" |
|||
_debug txtvalue "$txtvalue" |
|||
|
|||
SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" |
|||
SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" |
|||
if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then |
|||
SERVERCOW_API_Username="" |
|||
SERVERCOW_API_Password="" |
|||
_err "You don't specify servercow api username and password yet." |
|||
_err "Please create your username and password and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
# save the credentials to the account conf file |
|||
_saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username" |
|||
_saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password" |
|||
|
|||
_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 _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then |
|||
if printf -- "%s" "$response" | grep "ok" >/dev/null; then |
|||
_info "Added, OK" |
|||
return 0 |
|||
else |
|||
_err "add txt record error." |
|||
return 1 |
|||
fi |
|||
fi |
|||
_err "add txt record error." |
|||
|
|||
return 1 |
|||
} |
|||
|
|||
# Usage fulldomain txtvalue |
|||
# Remove the txt record after validation |
|||
dns_servercow_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
_info "Using servercow" |
|||
_debug fulldomain "$fulldomain" |
|||
_debug txtvalue "$fulldomain" |
|||
|
|||
SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" |
|||
SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" |
|||
if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then |
|||
SERVERCOW_API_Username="" |
|||
SERVERCOW_API_Password="" |
|||
_err "You don't specify servercow api username and password yet." |
|||
_err "Please create your username and password and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
_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 _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then |
|||
if printf -- "%s" "$response" | grep "ok" >/dev/null; then |
|||
_info "Deleted, OK" |
|||
_contains "$response" '"message":"ok"' |
|||
else |
|||
_err "delete txt record error." |
|||
return 1 |
|||
fi |
|||
fi |
|||
|
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
# _acme-challenge.www.domain.com |
|||
# returns |
|||
# _sub_domain=_acme-challenge.www |
|||
# _domain=domain.com |
|||
_get_root() { |
|||
fulldomain=$1 |
|||
i=2 |
|||
p=1 |
|||
|
|||
while true; do |
|||
_domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) |
|||
|
|||
_debug _domain "$_domain" |
|||
if [ -z "$_domain" ]; then |
|||
# not valid |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _servercow_api GET "$_domain"; then |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then |
|||
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) |
|||
if [ -z "$_sub_domain" ]; then |
|||
# not valid |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
fi |
|||
|
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
|
|||
return 1 |
|||
} |
|||
|
|||
_servercow_api() { |
|||
method=$1 |
|||
domain=$2 |
|||
data="$3" |
|||
|
|||
export _H1="Content-Type: application/json" |
|||
export _H2="X-Auth-Username: $SERVERCOW_API_Username" |
|||
export _H3="X-Auth-Password: $SERVERCOW_API_Password" |
|||
|
|||
if [ "$method" != "GET" ]; then |
|||
_debug data "$data" |
|||
response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")" |
|||
else |
|||
response="$(_get "$SERVERCOW_API/$domain")" |
|||
fi |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $domain" |
|||
return 1 |
|||
fi |
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
@ -0,0 +1,202 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# |
|||
#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" |
|||
# |
|||
#UNO_User="UExxxxxx" |
|||
|
|||
Uno_Api="https://api.unoeuro.com/1" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_unoeuro_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}" |
|||
UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}" |
|||
if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then |
|||
UNO_Key="" |
|||
UNO_User="" |
|||
_err "You haven't specified a UnoEuro api key and account yet." |
|||
_err "Please create your key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _contains "$UNO_User" "UE"; then |
|||
_err "It seems that the UNO_User=$UNO_User is not a valid username." |
|||
_err "Please check and retry." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key and email to the account conf file. |
|||
_saveaccountconf_mutable UNO_Key "$UNO_Key" |
|||
_saveaccountconf_mutable UNO_User "$UNO_User" |
|||
|
|||
_debug "First 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" |
|||
|
|||
_debug "Getting txt records" |
|||
_uno_rest GET "my/products/$h/dns/records" |
|||
|
|||
if ! _contains "$response" "\"status\": 200" >/dev/null; then |
|||
_err "Error" |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _contains "$response" "$_sub_domain" >/dev/null; then |
|||
_info "Adding record" |
|||
|
|||
if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then |
|||
if _contains "$response" "\"status\": 200" >/dev/null; then |
|||
_info "Added, OK" |
|||
return 0 |
|||
else |
|||
_err "Add txt record error." |
|||
return 1 |
|||
fi |
|||
fi |
|||
_err "Add txt record error." |
|||
else |
|||
_info "Updating record" |
|||
record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) |
|||
record_line_number=$(_math "$record_line_number" - 1) |
|||
record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") |
|||
_debug "record_id" "$record_id" |
|||
|
|||
_uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" |
|||
if _contains "$response" "\"status\": 200" >/dev/null; then |
|||
_info "Updated, OK" |
|||
return 0 |
|||
fi |
|||
_err "Update error" |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
#fulldomain txtvalue |
|||
dns_unoeuro_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}" |
|||
UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}" |
|||
if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then |
|||
UNO_Key="" |
|||
UNO_User="" |
|||
_err "You haven't specified a UnoEuro api key and account yet." |
|||
_err "Please create your key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _contains "$UNO_User" "UE"; then |
|||
_err "It seems that the UNO_User=$UNO_User is not a valid username." |
|||
_err "Please check and retry." |
|||
return 1 |
|||
fi |
|||
|
|||
_debug "First 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" |
|||
|
|||
_debug "Getting txt records" |
|||
_uno_rest GET "my/products/$h/dns/records" |
|||
|
|||
if ! _contains "$response" "\"status\": 200"; then |
|||
_err "Error" |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _contains "$response" "$_sub_domain"; then |
|||
_info "Don't need to remove." |
|||
else |
|||
record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) |
|||
record_line_number=$(_math "$record_line_number" - 1) |
|||
record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") |
|||
_debug "record_id" "$record_id" |
|||
|
|||
if [ -z "$record_id" ]; then |
|||
_err "Can not get record id to remove." |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then |
|||
_err "Delete record error." |
|||
return 1 |
|||
fi |
|||
_contains "$response" "\"status\": 200" |
|||
fi |
|||
|
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
#_acme-challenge.www.domain.com |
|||
#returns |
|||
# _sub_domain=_acme-challenge.www |
|||
# _domain=domain.com |
|||
# _domain_id=sdjkglgdfewsdfg |
|||
_get_root() { |
|||
domain=$1 |
|||
i=2 |
|||
p=1 |
|||
while true; do |
|||
h=$(printf "%s" "$domain" | cut -d . -f $i-100) |
|||
_debug h "$h" |
|||
if [ -z "$h" ]; then |
|||
#not valid |
|||
return 1 |
|||
fi |
|||
|
|||
if ! _uno_rest GET "my/products/$h/dns/records"; then |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "\"status\": 200"; then |
|||
_domain_id=$h |
|||
if [ "$_domain_id" ]; then |
|||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) |
|||
_domain=$h |
|||
return 0 |
|||
fi |
|||
return 1 |
|||
fi |
|||
p=$i |
|||
i=$(_math "$i" + 1) |
|||
done |
|||
return 1 |
|||
} |
|||
|
|||
_uno_rest() { |
|||
m=$1 |
|||
ep="$2" |
|||
data="$3" |
|||
_debug "$ep" |
|||
|
|||
export _H1="Content-Type: application/json" |
|||
|
|||
if [ "$m" != "GET" ]; then |
|||
_debug data "$data" |
|||
response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")" |
|||
else |
|||
response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")" |
|||
fi |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
_err "error $ep" |
|||
return 1 |
|||
fi |
|||
_debug2 response "$response" |
|||
return 0 |
|||
} |
@ -0,0 +1,106 @@ |
|||
#!/usr/bin/env sh |
|||
# Author: non7top@gmail.com |
|||
# 07 Jul 2017 |
|||
# report bugs at https://github.com/non7top/acme.sh |
|||
|
|||
# Values to export: |
|||
# export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_yandex_add() { |
|||
fulldomain="${1}" |
|||
txtvalue="${2}" |
|||
_debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" |
|||
_PDD_credentials || return 1 |
|||
export _H1="PddToken: $PDD_Token" |
|||
|
|||
_PDD_get_domain "$fulldomain" |
|||
_debug "Found suitable domain in pdd: $curDomain" |
|||
curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" |
|||
curUri="https://pddimp.yandex.ru/api2/admin/dns/add" |
|||
curResult="$(_post "${curData}" "${curUri}")" |
|||
_debug "Result: $curResult" |
|||
} |
|||
|
|||
#Usage: dns_myapi_rm _acme-challenge.www.domain.com |
|||
dns_yandex_rm() { |
|||
fulldomain="${1}" |
|||
_debug "Calling: dns_yandex_rm() '${fulldomain}'" |
|||
_PDD_credentials || return 1 |
|||
export _H1="PddToken: $PDD_Token" |
|||
record_id=$(pdd_get_record_id "${fulldomain}") |
|||
_debug "Result: $record_id" |
|||
|
|||
_PDD_get_domain "$fulldomain" |
|||
_debug "Found suitable domain in pdd: $curDomain" |
|||
|
|||
curUri="https://pddimp.yandex.ru/api2/admin/dns/del" |
|||
curData="domain=${curDomain}&record_id=${record_id}" |
|||
curResult="$(_post "${curData}" "${curUri}")" |
|||
_debug "Result: $curResult" |
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
|
|||
_PDD_get_domain() { |
|||
fulldomain="${1}" |
|||
__page=1 |
|||
__last=0 |
|||
while [ $__last -eq 0 ]; do |
|||
uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" |
|||
res1="$(_get "$uri1" | _normalizeJson)" |
|||
_debug2 "res1" "$res1" |
|||
__found="$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p')" |
|||
_debug "found: $__found results on page" |
|||
if [ "$__found" -lt 20 ]; then |
|||
_debug "last page: $__page" |
|||
__last=1 |
|||
fi |
|||
|
|||
__all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" |
|||
|
|||
__page=$(_math $__page + 1) |
|||
done |
|||
|
|||
k=2 |
|||
while [ $k -lt 10 ]; do |
|||
__t=$(echo "$fulldomain" | cut -d . -f $k-100) |
|||
_debug "finding zone for domain $__t" |
|||
for d in $__all_domains; do |
|||
if [ "$d" = "$__t" ]; then |
|||
p=$(_math $k - 1) |
|||
curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")" |
|||
curDomain="$__t" |
|||
return 0 |
|||
fi |
|||
done |
|||
k=$(_math $k + 1) |
|||
done |
|||
_err "No suitable domain found in your account" |
|||
return 1 |
|||
} |
|||
|
|||
_PDD_credentials() { |
|||
if [ -z "${PDD_Token}" ]; then |
|||
PDD_Token="" |
|||
_err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx" |
|||
_err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token" |
|||
return 1 |
|||
else |
|||
_saveaccountconf PDD_Token "${PDD_Token}" |
|||
fi |
|||
} |
|||
|
|||
pdd_get_record_id() { |
|||
fulldomain="${1}" |
|||
|
|||
_PDD_get_domain "$fulldomain" |
|||
_debug "Found suitable domain in pdd: $curDomain" |
|||
|
|||
curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" |
|||
curResult="$(_get "${curUri}" | _normalizeJson)" |
|||
_debug "Result: $curResult" |
|||
echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' |
|||
} |
@ -0,0 +1,85 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# |
|||
#ZM_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" |
|||
# |
|||
#https://zonomi.com dns api |
|||
|
|||
ZM_Api="https://zonomi.com/app/dns/dyndns.jsp" |
|||
|
|||
######## Public functions ##################### |
|||
|
|||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|||
dns_zonomi_add() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}" |
|||
|
|||
if [ -z "$ZM_Key" ]; then |
|||
ZM_Key="" |
|||
_err "You don't specify zonomi api key yet." |
|||
_err "Please create your key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
#save the api key to the account conf file. |
|||
_saveaccountconf_mutable ZM_Key "$ZM_Key" |
|||
|
|||
_info "Get existing txt records for $fulldomain" |
|||
if ! _zm_request "action=QUERY&name=$fulldomain"; then |
|||
_err "error" |
|||
return 1 |
|||
fi |
|||
|
|||
if _contains "$response" "<record"; then |
|||
_debug "get and update records" |
|||
_qstr="action[1]=SET&type[1]=TXT&name[1]=$fulldomain&value[1]=$txtvalue" |
|||
_qindex=2 |
|||
for t in $(echo "$response" | tr -d "\r\n" | _egrep_o '<action.*</action>' | tr "<" "\n" | grep record | grep 'type="TXT"' | cut -d '"' -f 6); do |
|||
_debug2 t "$t" |
|||
_qstr="$_qstr&action[$_qindex]=SET&type[$_qindex]=TXT&name[$_qindex]=$fulldomain&value[$_qindex]=$t" |
|||
_qindex="$(_math "$_qindex" + 1)" |
|||
done |
|||
_zm_request "$_qstr" |
|||
else |
|||
_debug "Just add record" |
|||
_zm_request "action=SET&type=TXT&name=$fulldomain&value=$txtvalue" |
|||
fi |
|||
|
|||
} |
|||
|
|||
#fulldomain txtvalue |
|||
dns_zonomi_rm() { |
|||
fulldomain=$1 |
|||
txtvalue=$2 |
|||
|
|||
ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}" |
|||
if [ -z "$ZM_Key" ]; then |
|||
ZM_Key="" |
|||
_err "You don't specify zonomi api key yet." |
|||
_err "Please create your key and try again." |
|||
return 1 |
|||
fi |
|||
|
|||
_zm_request "action=DELETE&type=TXT&name=$fulldomain" |
|||
|
|||
} |
|||
|
|||
#################### Private functions below ################################## |
|||
#qstr |
|||
_zm_request() { |
|||
qstr="$1" |
|||
|
|||
_debug2 "qstr" "$qstr" |
|||
|
|||
_zm_url="$ZM_Api?api_key=$ZM_Key&$qstr" |
|||
_debug2 "_zm_url" "$_zm_url" |
|||
response="$(_get "$_zm_url")" |
|||
|
|||
if [ "$?" != "0" ]; then |
|||
return 1 |
|||
fi |
|||
_debug2 response "$response" |
|||
_contains "$response" "<is_ok>OK:" |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue