From 989651c23be6f8fbf2d507aaae76ec5c774f2644 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 4 Feb 2017 23:17:24 -0500 Subject: [PATCH 001/272] Initial version --- deploy/sshdeploy.sh | 128 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 deploy/sshdeploy.sh diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh new file mode 100644 index 00000000..d33aad7a --- /dev/null +++ b/deploy/sshdeploy.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env sh + +#Here is a script to deploy certificates to remote server by ssh +#This file name is "sshdeploy.sh" +#So, here must be a method sshdeploy_deploy() +#Which will be called by acme.sh to deploy the cert +#returns 0 means success, otherwise error. + +# The following variables exported from environment will be used. +# If not set then values previously saved in domain.conf file are used. +# +# export ACME_DEPLOY_SSH_URL="admin@qnap" +# export ACME_DEPLOY_SSH_SERVICE_STOP="/etc/init.d/stunnel.sh stop" +# export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" +# export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" +# export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" +# export ACME_DEPLOY_SSH_FULLCHAIN="" +# export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" +# export ACME_DEPLOY_SSH_SERVICE_START="/etc/init.d/stunnel.sh stop" + +. "$DOMAIN_CONF" + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +sshdeploy_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + _cmdstr="{" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if [ -z "$ACME_DEPLOY_SSH_URL" ]; then + if [ -z "$Le_Deploy_ssh_url" ]; then + _err "ACME_DEPLOY_SSH_URL not defined." + return 1 + fi + else + Le_Deploy_ssh_url="$ACME_DEPLOY_SSH_URL" + _savedomainconf Le_Deploy_ssh_url "$Le_Deploy_ssh_url" + fi + + _info "Deploy certificates to remote server $Le_Deploy_ssh_url" + + if [ -n "$ACME_DEPLOY_SSH_SERVICE_STOP" ]; then + Le_Deploy_ssh_service_stop="$ACME_DEPLOY_SSH_SERVICE_STOP" + _savedomainconf Le_Deploy_ssh_service_stop "$Le_Deploy_ssh_service_stop" + fi + if [ -n "$Le_Deploy_ssh_service_stop" ]; then + _cmdstr="$_cmdstr $Le_Deploy_ssh_service_stop ;" + _info "Will stop remote service with command $Le_Deploy_ssh_service_stop" + fi + + if [ -n "$ACME_DEPLOY_SSH_KEYFILE" ]; then + Le_Deploy_ssh_keyfile="$ACME_DEPLOY_SSH_KEYFILE" + _savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile" + fi + if [ -n "$Le_Deploy_ssh_keyfile" ]; then + _cmdstr="$_cmdstr echo \"$(cat $_ckey)\" > $Le_Deploy_ssh_keyfile ;" + _info "will copy private key to remote file $Le_Deploy_ssh_keyfile" + fi + + if [ -n "$ACME_DEPLOY_SSH_CERTFILE" ]; then + Le_Deploy_ssh_certfile="$ACME_DEPLOY_SSH_CERTFILE" + _savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile" + fi + if [ -n "$Le_Deploy_ssh_certfile" ]; then + if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then + _cmdstr="$_cmdstr echo \"$(cat $_ccert)\" >> $Le_Deploy_ssh_certfile ;" + _info "will append certificate to same file" + else + _cmdstr="$_cmdstr echo \"$(cat $_ccert)\" > $Le_Deploy_ssh_certfile ;" + _info "will copy certificate to remote file $Le_Deploy_ssh_certfile" + fi + fi + + if [ -n "$ACME_DEPLOY_SSH_CAFILE" ]; then + Le_Deploy_ssh_cafile="$ACME_DEPLOY_SSH_CAFILE" + _savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile" + fi + if [ -n "$Le_Deploy_ssh_cafile" ]; then + _cmdstr="$_cmdstr echo \"$(cat $_cca)\" > $Le_Deploy_ssh_cafile ;" + _info "will copy CA file to remote file $Le_Deploy_ssh_cafile" + fi + + if [ -n "$ACME_DEPLOY_SSH_FULLCHAIN" ]; then + Le_Deploy_ssh_fullchain="$ACME_DEPLOY_SSH_FULLCHAIN" + _savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain" + fi + if [ -n "$Le_Deploy_ssh_fullchain" ]; then + _cmdstr="$_cmdstr echo \"$(cat $_cfullchain)\" > $Le_Deploy_ssh_fullchain ;" + _info "will copy full chain to remote file $Le_Deploy_ssh_fullchain" + fi + + if [ -n "$ACME_DEPLOY_SSH_REMOTE_CMD" ]; then + Le_Deploy_ssh_remote_cmd="$ACME_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 sleep 2 ; $Le_Deploy_ssh_remote_cmd ;" + _info "Will sleep 2 seconds then execute remote command $Le_Deploy_ssh_remote_cmd" + fi + + if [ -n "$ACME_DEPLOY_SSH_SERVICE_START" ]; then + Le_Deploy_ssh_service_start="$ACME_DEPLOY_SSH_SERVICE_START" + _savedomainconf Le_Deploy_ssh_service_start "$Le_Deploy_ssh_service_start" + fi + if [ -n "$Le_Deploy_ssh_service_start" ]; then + _cmdstr="$_cmdstr sleep 2 ; $Le_Deploy_ssh_service_start ;" + _info "Will sleep 2 seconds then start remote service with command $Le_Deploy_ssh_remote_cmd" + fi + + _cmdstr="$_cmdstr }" + + _debug "Remote command to execute: $_cmdstr" + + _info "Submitting sequence of commands to remote server by ssh" + ssh -T "$Le_Deploy_ssh_url" bash -c "'$_cmdstr'" + + return 0 +} From 7d75ad4c56a97ae7576bcc89eb41610df1d856b5 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 14:35:05 -0500 Subject: [PATCH 002/272] Backup old certificates before overwriting. Add userid export parameter. And generate error if nothing to do at remote server. --- deploy/sshdeploy.sh | 104 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 22 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index d33aad7a..aa924136 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -1,22 +1,26 @@ #!/usr/bin/env sh -#Here is a script to deploy certificates to remote server by ssh -#This file name is "sshdeploy.sh" -#So, here must be a method sshdeploy_deploy() -#Which will be called by acme.sh to deploy the cert -#returns 0 means success, otherwise error. - +# 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@URL 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. # -# export ACME_DEPLOY_SSH_URL="admin@qnap" -# export ACME_DEPLOY_SSH_SERVICE_STOP="/etc/init.d/stunnel.sh stop" +# Only a username is required. All others are optional. +# +# The following examples are for QNAP NAS running QTS 4.2 +# export ACME_DEPLOY_SSH_USER="admin" +# export ACME_DEPLOY_SSH_URL="qnap" +# export ACME_DEPLOY_SSH_SERVICE_STOP="" # export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" # export ACME_DEPLOY_SSH_FULLCHAIN="" # export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" -# export ACME_DEPLOY_SSH_SERVICE_START="/etc/init.d/stunnel.sh stop" +# export ACME_DEPLOY_SSH_SERVICE_START="" . "$DOMAIN_CONF" @@ -29,7 +33,9 @@ sshdeploy_deploy() { _ccert="$3" _cca="$4" _cfullchain="$5" - _cmdstr="{" + _cmdstr="" + _homedir="~/.acme_ssh_deploy" + _backupdir="$_homedir/certs-backup-$(date +%Y%m%d%H%M%S)" _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" @@ -37,18 +43,29 @@ sshdeploy_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - if [ -z "$ACME_DEPLOY_SSH_URL" ]; then - if [ -z "$Le_Deploy_ssh_url" ]; then - _err "ACME_DEPLOY_SSH_URL not defined." + # USER is required to login by SSH to remote host. + if [ -z "$ACME_DEPLOY_SSH_USER" ]; then + if [ -z "$Le_Deploy_ssh_user" ]; then + _err "ACME_DEPLOY_SSH_USER not defined." return 1 fi else + Le_Deploy_ssh_user="$ACME_DEPLOY_SSH_USER" + _savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user" + fi + + # URL is optional. If not provided then use _cdomain + if [ -n "$ACME_DEPLOY_SSH_URL" ]; then Le_Deploy_ssh_url="$ACME_DEPLOY_SSH_URL" _savedomainconf Le_Deploy_ssh_url "$Le_Deploy_ssh_url" + elif [ -z "$Le_Deploy_ssh_url" ]; then + Le_Deploy_ssh_url="$_cdomain" fi - _info "Deploy certificates to remote server $Le_Deploy_ssh_url" + _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_url" + # SERVICE_STOP is optional. + # If provided then this command will be executed on remote host. if [ -n "$ACME_DEPLOY_SSH_SERVICE_STOP" ]; then Le_Deploy_ssh_service_stop="$ACME_DEPLOY_SSH_SERVICE_STOP" _savedomainconf Le_Deploy_ssh_service_stop "$Le_Deploy_ssh_service_stop" @@ -58,71 +75,114 @@ sshdeploy_deploy() { _info "Will stop remote service with command $Le_Deploy_ssh_service_stop" fi + # KEYFILE is optional. + # If provided then private key will be copied to provided filename. if [ -n "$ACME_DEPLOY_SSH_KEYFILE" ]; then Le_Deploy_ssh_keyfile="$ACME_DEPLOY_SSH_KEYFILE" _savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile" fi if [ -n "$Le_Deploy_ssh_keyfile" ]; then + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir ;" + # 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 "$ACME_DEPLOY_SSH_CERTFILE" ]; then Le_Deploy_ssh_certfile="$ACME_DEPLOY_SSH_CERTFILE" _savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile" fi if [ -n "$Le_Deploy_ssh_certfile" ]; then if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then + # if filename is same as that provided for private key then append. _cmdstr="$_cmdstr echo \"$(cat $_ccert)\" >> $Le_Deploy_ssh_certfile ;" _info "will append certificate to same file" else + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir ;" + # copy new certificate into file. _cmdstr="$_cmdstr echo \"$(cat $_ccert)\" > $Le_Deploy_ssh_certfile ;" _info "will copy certificate to remote file $Le_Deploy_ssh_certfile" fi fi + # CAFILE is optional. + # If provided then CA intermediate certificate will be copied to provided filename. if [ -n "$ACME_DEPLOY_SSH_CAFILE" ]; then Le_Deploy_ssh_cafile="$ACME_DEPLOY_SSH_CAFILE" _savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile" fi if [ -n "$Le_Deploy_ssh_cafile" ]; then + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir ;" + # copy new certificate into file. _cmdstr="$_cmdstr echo \"$(cat $_cca)\" > $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 to provided filename. if [ -n "$ACME_DEPLOY_SSH_FULLCHAIN" ]; then Le_Deploy_ssh_fullchain="$ACME_DEPLOY_SSH_FULLCHAIN" _savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain" fi if [ -n "$Le_Deploy_ssh_fullchain" ]; then + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir ;" + # copy new certificate into file. _cmdstr="$_cmdstr echo \"$(cat $_cfullchain)\" > $Le_Deploy_ssh_fullchain ;" _info "will copy full chain to remote file $Le_Deploy_ssh_fullchain" fi + # REMOTE_CMD is optional. + # If provided then this command will be executed on remote host. + # A 2 second delay is inserted to allow system to stabalize after + # executing a service stop. if [ -n "$ACME_DEPLOY_SSH_REMOTE_CMD" ]; then Le_Deploy_ssh_remote_cmd="$ACME_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 sleep 2 ; $Le_Deploy_ssh_remote_cmd ;" - _info "Will sleep 2 seconds then execute remote command $Le_Deploy_ssh_remote_cmd" + if [ -n "$Le_Deploy_ssh_service_stop" ]; then + _cmdstr="$_cmdstr sleep 2 ;" + fi + _cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd ;" + _info "Will execute remote command $Le_Deploy_ssh_remote_cmd" fi + # SERVICE_START is optional. + # If provided then this command will be executed on remote host. + # A 2 second delay is inserted to allow system to stabalize after + # executing a service stop or previous command. if [ -n "$ACME_DEPLOY_SSH_SERVICE_START" ]; then Le_Deploy_ssh_service_start="$ACME_DEPLOY_SSH_SERVICE_START" _savedomainconf Le_Deploy_ssh_service_start "$Le_Deploy_ssh_service_start" fi if [ -n "$Le_Deploy_ssh_service_start" ]; then - _cmdstr="$_cmdstr sleep 2 ; $Le_Deploy_ssh_service_start ;" - _info "Will sleep 2 seconds then start remote service with command $Le_Deploy_ssh_remote_cmd" + if [ -n "$Le_Deploy_ssh_service_stop" ] || [ -n "$Le_Deploy_ssh_remote_cmd" ] ; then + _cmdstr="$_cmdstr sleep 2 ;" + fi + _cmdstr="$_cmdstr $Le_Deploy_ssh_service_start ;" + _info "Will start remote service with command $Le_Deploy_ssh_remote_cmd" fi - _cmdstr="$_cmdstr }" - - _debug "Remote command to execute: $_cmdstr" + if [ -z "$_cmdstr" ]; then + _err "No remote commands to excute. Failed to deploy certificates to remote server" + return 1 + else + # something to execute. + # run cleanup on the backup directory, erase all older than 180 days. + _cmdstr="find $_homedir/* -type d -mtime +180 2>/dev/null | xargs rm -rf ; $_cmdstr" + # Create our backup directory for overwritten cert files. + _cmdstr="mkdir -p $_backupdir ; $_cmdstr" + fi + _debug "Remote commands to execute: $_cmdstr" _info "Submitting sequence of commands to remote server by ssh" - ssh -T "$Le_Deploy_ssh_url" bash -c "'$_cmdstr'" + ssh -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_url" bash -c "'$_cmdstr'" return 0 } From 5d3de4b670fefbc4f0fc54b390e0c196ea8583a7 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 14:39:25 -0500 Subject: [PATCH 003/272] Additional info messages for backup management --- deploy/sshdeploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index aa924136..e34371a1 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -178,6 +178,8 @@ sshdeploy_deploy() { _cmdstr="find $_homedir/* -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" From 62e7d904b40af35884bfb1dd59d9a0486c993207 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 15:02:59 -0500 Subject: [PATCH 004/272] Travis errors --- deploy/sshdeploy.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index e34371a1..949bc1d3 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -22,8 +22,6 @@ # export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" # export ACME_DEPLOY_SSH_SERVICE_START="" -. "$DOMAIN_CONF" - ######## Public functions ##################### #domain keyfile certfile cafile fullchain @@ -34,9 +32,12 @@ sshdeploy_deploy() { _cca="$4" _cfullchain="$5" _cmdstr="" - _homedir="~/.acme_ssh_deploy" + _homedir='~' + _homedir="$_homedir/.acme_ssh_deploy" _backupdir="$_homedir/certs-backup-$(date +%Y%m%d%H%M%S)" + . "$DOMAIN_CONF" + _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" _debug _ccert "$_ccert" @@ -61,7 +62,7 @@ sshdeploy_deploy() { elif [ -z "$Le_Deploy_ssh_url" ]; then Le_Deploy_ssh_url="$_cdomain" fi - + _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_url" # SERVICE_STOP is optional. @@ -146,9 +147,9 @@ sshdeploy_deploy() { _savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd" fi if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then - if [ -n "$Le_Deploy_ssh_service_stop" ]; then - _cmdstr="$_cmdstr sleep 2 ;" - fi + if [ -n "$Le_Deploy_ssh_service_stop" ]; then + _cmdstr="$_cmdstr sleep 2 ;" + fi _cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd ;" _info "Will execute remote command $Le_Deploy_ssh_remote_cmd" fi @@ -162,9 +163,9 @@ sshdeploy_deploy() { _savedomainconf Le_Deploy_ssh_service_start "$Le_Deploy_ssh_service_start" fi if [ -n "$Le_Deploy_ssh_service_start" ]; then - if [ -n "$Le_Deploy_ssh_service_stop" ] || [ -n "$Le_Deploy_ssh_remote_cmd" ] ; then - _cmdstr="$_cmdstr sleep 2 ;" - fi + if [ -n "$Le_Deploy_ssh_service_stop" ] || [ -n "$Le_Deploy_ssh_remote_cmd" ] ; then + _cmdstr="$_cmdstr sleep 2 ;" + fi _cmdstr="$_cmdstr $Le_Deploy_ssh_service_start ;" _info "Will start remote service with command $Le_Deploy_ssh_remote_cmd" fi From ff60dc4d24b27998fe2e435c94135e1494dd2cc8 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 15:12:23 -0500 Subject: [PATCH 005/272] More Travis issues !!! --- deploy/sshdeploy.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index 949bc1d3..672b25cd 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -36,8 +36,16 @@ sshdeploy_deploy() { _homedir="$_homedir/.acme_ssh_deploy" _backupdir="$_homedir/certs-backup-$(date +%Y%m%d%H%M%S)" + if [ -z "$DOMAIN_CONF" ]; then + DOMAIN_CONF="" + fi + if [ ! -f "$DOMAIN_CONF" ]; then + _err "$DOMAIN_CONF does not exist." + return 1 + fi + . "$DOMAIN_CONF" - + _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" _debug _ccert "$_ccert" @@ -163,7 +171,7 @@ sshdeploy_deploy() { _savedomainconf Le_Deploy_ssh_service_start "$Le_Deploy_ssh_service_start" fi if [ -n "$Le_Deploy_ssh_service_start" ]; then - if [ -n "$Le_Deploy_ssh_service_stop" ] || [ -n "$Le_Deploy_ssh_remote_cmd" ] ; then + if [ -n "$Le_Deploy_ssh_service_stop" ] || [ -n "$Le_Deploy_ssh_remote_cmd" ]; then _cmdstr="$_cmdstr sleep 2 ;" fi _cmdstr="$_cmdstr $Le_Deploy_ssh_service_start ;" From 9ab6353d73c4ac747e1063ec8d2e40017222b6ba Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 15:16:05 -0500 Subject: [PATCH 006/272] Trying again. --- deploy/sshdeploy.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index 672b25cd..63d72fb8 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -36,15 +36,9 @@ sshdeploy_deploy() { _homedir="$_homedir/.acme_ssh_deploy" _backupdir="$_homedir/certs-backup-$(date +%Y%m%d%H%M%S)" - if [ -z "$DOMAIN_CONF" ]; then - DOMAIN_CONF="" + if [ -f "$DOMAIN_CONF" ]; then + . "$DOMAIN_CONF" fi - if [ ! -f "$DOMAIN_CONF" ]; then - _err "$DOMAIN_CONF does not exist." - return 1 - fi - - . "$DOMAIN_CONF" _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" From 6c1561f415c18d66f8d7294c479ef3c47a443702 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 15:20:06 -0500 Subject: [PATCH 007/272] Grasping at straws now !! --- deploy/sshdeploy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index 63d72fb8..39819ba9 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -37,6 +37,7 @@ sshdeploy_deploy() { _backupdir="$_homedir/certs-backup-$(date +%Y%m%d%H%M%S)" if [ -f "$DOMAIN_CONF" ]; then + # shellcheck disable=SC1090 . "$DOMAIN_CONF" fi From 3812b275e9995ada19604da863f2d2d6dd31d5d2 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 15:26:55 -0500 Subject: [PATCH 008/272] Moving on to the next batch of travis errors. --- deploy/sshdeploy.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index 39819ba9..96eabc74 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -89,7 +89,7 @@ sshdeploy_deploy() { # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir ;" # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat $_ckey)\" > $Le_Deploy_ssh_keyfile ;" + _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile ;" _info "will copy private key to remote file $Le_Deploy_ssh_keyfile" fi @@ -102,13 +102,13 @@ sshdeploy_deploy() { if [ -n "$Le_Deploy_ssh_certfile" ]; then if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then # if filename is same as that provided for private key then append. - _cmdstr="$_cmdstr echo \"$(cat $_ccert)\" >> $Le_Deploy_ssh_certfile ;" + _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" >> $Le_Deploy_ssh_certfile ;" _info "will append certificate to same file" else # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir ;" # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat $_ccert)\" > $Le_Deploy_ssh_certfile ;" + _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" > $Le_Deploy_ssh_certfile ;" _info "will copy certificate to remote file $Le_Deploy_ssh_certfile" fi fi @@ -123,7 +123,7 @@ sshdeploy_deploy() { # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir ;" # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat $_cca)\" > $Le_Deploy_ssh_cafile ;" + _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" > $Le_Deploy_ssh_cafile ;" _info "will copy CA file to remote file $Le_Deploy_ssh_cafile" fi @@ -137,7 +137,7 @@ sshdeploy_deploy() { # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir ;" # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat $_cfullchain)\" > $Le_Deploy_ssh_fullchain ;" + _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" > $Le_Deploy_ssh_fullchain ;" _info "will copy full chain to remote file $Le_Deploy_ssh_fullchain" fi @@ -188,6 +188,8 @@ sshdeploy_deploy() { _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 ssh -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_url" bash -c "'$_cmdstr'" return 0 From 9507b121acfe49b922683809d853f27c2f229ecd Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 5 Feb 2017 15:34:03 -0500 Subject: [PATCH 009/272] More selective pruning of certificate backup directories. --- deploy/sshdeploy.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index 96eabc74..55d248ca 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -33,8 +33,8 @@ sshdeploy_deploy() { _cfullchain="$5" _cmdstr="" _homedir='~' - _homedir="$_homedir/.acme_ssh_deploy" - _backupdir="$_homedir/certs-backup-$(date +%Y%m%d%H%M%S)" + _backupprefix="$_homedir/.acme_ssh_deploy/certs-backup" + _backupdir="$_backupprefix-$(date +%Y%m%d%H%M%S)" if [ -f "$DOMAIN_CONF" ]; then # shellcheck disable=SC1090 @@ -179,7 +179,7 @@ sshdeploy_deploy() { else # something to execute. # run cleanup on the backup directory, erase all older than 180 days. - _cmdstr="find $_homedir/* -type d -mtime +180 2>/dev/null | xargs rm -rf ; $_cmdstr" + _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" From f158caa2ebba77a343ca7badddba8395e822a609 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Mon, 6 Feb 2017 21:49:20 -0500 Subject: [PATCH 010/272] Updates from code review --- deploy/sshdeploy.sh | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/deploy/sshdeploy.sh b/deploy/sshdeploy.sh index 55d248ca..e7ca78a8 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/sshdeploy.sh @@ -3,7 +3,7 @@ # 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@URL from the host running acme.sh before +# 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. @@ -13,7 +13,8 @@ # # The following examples are for QNAP NAS running QTS 4.2 # export ACME_DEPLOY_SSH_USER="admin" -# export ACME_DEPLOY_SSH_URL="qnap" +# export ACME_DEPLOY_SSH_SERVER="qnap" +# export ACME_DEPLOY_SSH_PORT="22" # export ACME_DEPLOY_SSH_SERVICE_STOP="" # export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" @@ -34,7 +35,7 @@ sshdeploy_deploy() { _cmdstr="" _homedir='~' _backupprefix="$_homedir/.acme_ssh_deploy/certs-backup" - _backupdir="$_backupprefix-$(date +%Y%m%d%H%M%S)" + _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')" if [ -f "$DOMAIN_CONF" ]; then # shellcheck disable=SC1090 @@ -58,15 +59,23 @@ sshdeploy_deploy() { _savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user" fi - # URL is optional. If not provided then use _cdomain - if [ -n "$ACME_DEPLOY_SSH_URL" ]; then - Le_Deploy_ssh_url="$ACME_DEPLOY_SSH_URL" - _savedomainconf Le_Deploy_ssh_url "$Le_Deploy_ssh_url" - elif [ -z "$Le_Deploy_ssh_url" ]; then - Le_Deploy_ssh_url="$_cdomain" + # SERVER is optional. If not provided then use _cdomain + if [ -n "$ACME_DEPLOY_SSH_SERVER" ]; then + Le_Deploy_ssh_server="$ACME_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 - _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_url" + # PORT is optional. If not provided then use port 22 + if [ -n "$ACME_DEPLOY_SSH_PORT" ]; then + Le_Deploy_ssh_port="$ACME_DEPLOY_SSH_PORT" + _savedomainconf Le_Deploy_ssh_port "$Le_Deploy_ssh_port" + elif [ -z "$Le_Deploy_ssh_port" ]; then + Le_Deploy_ssh_port="22" + fi + + _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server on port $Le_Deploy_ssh_port" # SERVICE_STOP is optional. # If provided then this command will be executed on remote host. @@ -190,7 +199,7 @@ sshdeploy_deploy() { _info "Submitting sequence of commands to remote server by ssh" # quotations in bash cmd below intended. Squash travis spellcheck error # shellcheck disable=SC2029 - ssh -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_url" bash -c "'$_cmdstr'" + ssh -T -p "$Le_Deploy_ssh_port" "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmdstr'" - return 0 + return $? } From 3be5a68e12bd366ab80c93c8d8f353d1ebda268a Mon Sep 17 00:00:00 2001 From: David Kerr Date: Tue, 7 Feb 2017 13:05:22 -0500 Subject: [PATCH 011/272] Rename sshdeploy.sh to ssh.sh --- deploy/{sshdeploy.sh => ssh.sh} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename deploy/{sshdeploy.sh => ssh.sh} (99%) diff --git a/deploy/sshdeploy.sh b/deploy/ssh.sh similarity index 99% rename from deploy/sshdeploy.sh rename to deploy/ssh.sh index e7ca78a8..c3b89448 100644 --- a/deploy/sshdeploy.sh +++ b/deploy/ssh.sh @@ -26,7 +26,7 @@ ######## Public functions ##################### #domain keyfile certfile cafile fullchain -sshdeploy_deploy() { +ssh_deploy() { _cdomain="$1" _ckey="$2" _ccert="$3" From 3365df77789b97c8e2b54eae02afd581d5714636 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 8 Feb 2017 10:15:39 -0500 Subject: [PATCH 012/272] Make certificate domain name part of the backup directory name. --- deploy/ssh.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/ssh.sh b/deploy/ssh.sh index c3b89448..6d628436 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -34,7 +34,7 @@ ssh_deploy() { _cfullchain="$5" _cmdstr="" _homedir='~' - _backupprefix="$_homedir/.acme_ssh_deploy/certs-backup" + _backupprefix="$_homedir/.acme_ssh_deploy/$_cdomain-backup" _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')" if [ -f "$DOMAIN_CONF" ]; then From 1a5989350fad48a824dda39c7d83703d6ae490f1 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 8 Feb 2017 21:02:00 -0500 Subject: [PATCH 013/272] Some documentation in README --- deploy/README.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index 580eaac8..8096073d 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1 +1,92 @@ #Using deploy api + +#Using the ssh deploy plugin + +The ssh deploy plugin allows you to deploy certificates to a remote host +using SSH command to connect to the remote server. The ssh plugin is invoked +with the following command... + +```bash +acme.sh --deploy -d example.com --deploy-hook ssh +``` +Prior to running this for the first time you must tell the plugin where +and how to deploy the certificates. This is done by exporting the following +environment variables. + +This is not required for subsequent runs as the +values are stored by acme.sh in the domain configuration files. + +Required... +```bash +export ACME_DEPLOY_SSH_USER="admin" +``` +Optional... +```bash +export ACME_DEPLOY_SSH_SERVER="qnap" +export ACME_DEPLOY_SSH_PORT="22" +export ACME_DEPLOY_SSH_SERVICE_STOP="" +export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" +export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" +export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" +export ACME_DEPLOY_SSH_FULLCHAIN="" +export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" +export ACME_DEPLOY_SSH_SERVICE_START="" +``` +The values used above are illustrative only and represent those used +to deploy certificates to a QNAP NAS device running QTS 4.2 + +###ACME_DEPLOY_SSH_USER +Username at the remote host that SSH will login with. 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@URL from the host running acme.sh before using this script. + +The USER@URL at the remote server must also have has permissions to write to +the target location of the certificate files and to execute any commands +(e.g. to stop/start services). + +###ACME_DEPLOY_SSH_SERVER +URL or IP Address of the remote server. If not provided then the domain +name provided on the acme.sh --deploy command line is used. + +###ACME_DEPLOY_SSH_PORT +Port number that SSH will attempt to connect to at the remote server. If +not specified then defaults to 22. + +###ACME_DEPLOY_SSH_SERVICE_STOP +Command to execute on the remote server prior to copying any certificates. This +would typically be used to stop the service for which the certificates are +being deployed. + +###ACME_DEPLOY_SSH_KEYFILE +###ACME_DEPLOY_SSH_CERTFILE +###ACME_DEPLOY_SSH_CAFILE +###ACME_DEPLOY_SSH_FULLCHAIN +These four variables identify the target location for the respective +certificates issued by LetsEncrypt. Directory path and filenames are those +on the remote server and the SSH user must have write permissions. + +###ACME_DEPLOY_SSH_REMOTE_CMD +Command to execute on the remote server after copying any certificates. This +could be any additional command required prior to starting the service again, +or could be a all-inclusive restart (stop and start of service). If +ACME_DEPLOY_SSH_SERVICE_STOP value was provided then a 2 second sleep is +inserted prior to calling this command to allow the system to stabalize. + +###ACME_DEPLOY_SSH_SERVICE_START +Command to execute on the remote server after copying any certificates. This +would typically be used to stop the service for which the certificates are +being deployed. If ACME_DEPLOY_SSH_SERVICE_STOP or ACME_DEPLOY_SSH_REMOTE_CMD +value were provided then a 2 second sleep is inserted prior to calling +this command to allow the system to stabalize. + +##Backups +Before writing a certificate file to the remote server the existing +certificate will be copied to a backup directory on the remote server. +These are placed in a hidden directory in the home directory of the SSH +user +```bash +~/.acme_ssh_deploy/[domain name]-backup-[timestamp] +``` +Any backups older than 180 days will be deleted when new certificates +are deployed. From e3feac3fd87db1bbd0838c3e851c8724d17a2fe1 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 8 Feb 2017 21:13:00 -0500 Subject: [PATCH 014/272] Documentation updates --- deploy/README.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 8096073d..2e490a17 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -11,9 +11,7 @@ acme.sh --deploy -d example.com --deploy-hook ssh ``` Prior to running this for the first time you must tell the plugin where and how to deploy the certificates. This is done by exporting the following -environment variables. - -This is not required for subsequent runs as the +environment variables. This is not required for subsequent runs as the values are stored by acme.sh in the domain configuration files. Required... @@ -32,8 +30,8 @@ export ACME_DEPLOY_SSH_FULLCHAIN="" export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" export ACME_DEPLOY_SSH_SERVICE_START="" ``` -The values used above are illustrative only and represent those used -to deploy certificates to a QNAP NAS device running QTS 4.2 +The values used above are illustrative only and represent those that could +be used to deploy certificates to a QNAP NAS device running QTS 4.2 ###ACME_DEPLOY_SSH_USER Username at the remote host that SSH will login with. Note that @@ -44,40 +42,37 @@ can login to USER@URL from the host running acme.sh before using this script. The USER@URL at the remote server must also have has permissions to write to the target location of the certificate files and to execute any commands (e.g. to stop/start services). - ###ACME_DEPLOY_SSH_SERVER URL or IP Address of the remote server. If not provided then the domain name provided on the acme.sh --deploy command line is used. - ###ACME_DEPLOY_SSH_PORT Port number that SSH will attempt to connect to at the remote server. If -not specified then defaults to 22. - +not provided then defaults to 22. ###ACME_DEPLOY_SSH_SERVICE_STOP Command to execute on the remote server prior to copying any certificates. This would typically be used to stop the service for which the certificates are being deployed. - ###ACME_DEPLOY_SSH_KEYFILE +Target filename for the private key issued by LetsEncrypt. ###ACME_DEPLOY_SSH_CERTFILE +Target filename for the certificate issued by LetsEncrypt. If this filename +is the same as that provided for ACME_DEPLOY_SSH_KEYFILE then this certificate +is appended to the same file as the private key. ###ACME_DEPLOY_SSH_CAFILE +Target filename for the CA intermediate certificate issued by LetsEncrypt. ###ACME_DEPLOY_SSH_FULLCHAIN -These four variables identify the target location for the respective -certificates issued by LetsEncrypt. Directory path and filenames are those -on the remote server and the SSH user must have write permissions. - +Target filename for the fullchain certificate issued by LetsEncrypt. ###ACME_DEPLOY_SSH_REMOTE_CMD Command to execute on the remote server after copying any certificates. This could be any additional command required prior to starting the service again, or could be a all-inclusive restart (stop and start of service). If ACME_DEPLOY_SSH_SERVICE_STOP value was provided then a 2 second sleep is inserted prior to calling this command to allow the system to stabalize. - ###ACME_DEPLOY_SSH_SERVICE_START Command to execute on the remote server after copying any certificates. This would typically be used to stop the service for which the certificates are being deployed. If ACME_DEPLOY_SSH_SERVICE_STOP or ACME_DEPLOY_SSH_REMOTE_CMD -value were provided then a 2 second sleep is inserted prior to calling +values were provided then a 2 second sleep is inserted prior to calling this command to allow the system to stabalize. ##Backups From 68d708e56da391e86bd13ea8900d015048d7f279 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 11 Feb 2017 16:11:27 -0500 Subject: [PATCH 015/272] Reduce and simplify number of exported variables. Also allow any cert file to append to previous file. --- deploy/README.md | 33 ++++++--------- deploy/ssh.sh | 106 ++++++++++++++++++++--------------------------- 2 files changed, 58 insertions(+), 81 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 15b7ae1d..95f31d45 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -42,15 +42,13 @@ export ACME_DEPLOY_SSH_USER="admin" ``` Optional... ```bash +export ACME_DEPLOY_SSH_CMD="" export ACME_DEPLOY_SSH_SERVER="qnap" -export ACME_DEPLOY_SSH_PORT="22" -export ACME_DEPLOY_SSH_SERVICE_STOP="" export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" export ACME_DEPLOY_SSH_FULLCHAIN="" export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" -export ACME_DEPLOY_SSH_SERVICE_START="" ``` The values used above are illustrative only and represent those that could be used to deploy certificates to a QNAP NAS device running QTS 4.2 @@ -64,16 +62,13 @@ can login to USER@URL from the host running acme.sh before using this script. The USER@URL at the remote server must also have has permissions to write to the target location of the certificate files and to execute any commands (e.g. to stop/start services). +###ACME_DEPLOY_SSH_CMD +You can customize the ssh command used to connect to the remote host. For example +if you need to connect to a specific port at the remote server you can set this +to, for example, "ssh -p 22" ###ACME_DEPLOY_SSH_SERVER URL or IP Address of the remote server. If not provided then the domain name provided on the acme.sh --deploy command line is used. -###ACME_DEPLOY_SSH_PORT -Port number that SSH will attempt to connect to at the remote server. If -not provided then defaults to 22. -###ACME_DEPLOY_SSH_SERVICE_STOP -Command to execute on the remote server prior to copying any certificates. This -would typically be used to stop the service for which the certificates are -being deployed. ###ACME_DEPLOY_SSH_KEYFILE Target filename for the private key issued by LetsEncrypt. ###ACME_DEPLOY_SSH_CERTFILE @@ -82,22 +77,18 @@ is the same as that provided for ACME_DEPLOY_SSH_KEYFILE then this certificate is appended to the same file as the private key. ###ACME_DEPLOY_SSH_CAFILE Target filename for the CA intermediate certificate issued by LetsEncrypt. +If this is the same as a previous filename then it is appended to the same +file ###ACME_DEPLOY_SSH_FULLCHAIN Target filename for the fullchain certificate issued by LetsEncrypt. +If this is the same as a previous filename then it is appended to the same +file ###ACME_DEPLOY_SSH_REMOTE_CMD Command to execute on the remote server after copying any certificates. This -could be any additional command required prior to starting the service again, -or could be a all-inclusive restart (stop and start of service). If -ACME_DEPLOY_SSH_SERVICE_STOP value was provided then a 2 second sleep is -inserted prior to calling this command to allow the system to stabalize. -###ACME_DEPLOY_SSH_SERVICE_START -Command to execute on the remote server after copying any certificates. This -would typically be used to stop the service for which the certificates are -being deployed. If ACME_DEPLOY_SSH_SERVICE_STOP or ACME_DEPLOY_SSH_REMOTE_CMD -values were provided then a 2 second sleep is inserted prior to calling -this command to allow the system to stabalize. +could be any additional command required for example to stop and restart +the service. -##Backups +###Backups Before writing a certificate file to the remote server the existing certificate will be copied to a backup directory on the remote server. These are placed in a hidden directory in the home directory of the SSH diff --git a/deploy/ssh.sh b/deploy/ssh.sh index 6d628436..0adeba89 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -12,16 +12,14 @@ # Only a username is required. All others are optional. # # The following examples are for QNAP NAS running QTS 4.2 +# export ACME_DEPLOY_SSH_CMD="" # export ACME_DEPLOY_SSH_USER="admin" # export ACME_DEPLOY_SSH_SERVER="qnap" -# export ACME_DEPLOY_SSH_PORT="22" -# export ACME_DEPLOY_SSH_SERVICE_STOP="" # export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" # export ACME_DEPLOY_SSH_FULLCHAIN="" -# export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" -# export ACME_DEPLOY_SSH_SERVICE_START="" +# export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" ######## Public functions ##################### @@ -67,26 +65,15 @@ ssh_deploy() { Le_Deploy_ssh_server="$_cdomain" fi - # PORT is optional. If not provided then use port 22 - if [ -n "$ACME_DEPLOY_SSH_PORT" ]; then - Le_Deploy_ssh_port="$ACME_DEPLOY_SSH_PORT" - _savedomainconf Le_Deploy_ssh_port "$Le_Deploy_ssh_port" - elif [ -z "$Le_Deploy_ssh_port" ]; then - Le_Deploy_ssh_port="22" + # CMD is optional. If not provided then use ssh + if [ -n "$ACME_DEPLOY_SSH_CMD" ]; then + Le_Deploy_ssh_cmd="$ACME_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 - _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server on port $Le_Deploy_ssh_port" - - # SERVICE_STOP is optional. - # If provided then this command will be executed on remote host. - if [ -n "$ACME_DEPLOY_SSH_SERVICE_STOP" ]; then - Le_Deploy_ssh_service_stop="$ACME_DEPLOY_SSH_SERVICE_STOP" - _savedomainconf Le_Deploy_ssh_service_stop "$Le_Deploy_ssh_service_stop" - fi - if [ -n "$Le_Deploy_ssh_service_stop" ]; then - _cmdstr="$_cmdstr $Le_Deploy_ssh_service_stop ;" - _info "Will stop remote service with command $Le_Deploy_ssh_service_stop" - fi + _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. @@ -110,78 +97,72 @@ ssh_deploy() { fi if [ -n "$Le_Deploy_ssh_certfile" ]; then if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then - # if filename is same as that provided for private key then append. - _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" >> $Le_Deploy_ssh_certfile ;" - _info "will append certificate to same file" + # if filename is same as previous file then append. + _pipe=">>" else # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir ;" - # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" > $Le_Deploy_ssh_certfile ;" - _info "will copy certificate to remote file $Le_Deploy_ssh_certfile" + _pipe=">" 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 to provided filename. + # If provided then CA intermediate certificate will be copied or appended to provided filename. if [ -n "$ACME_DEPLOY_SSH_CAFILE" ]; then Le_Deploy_ssh_cafile="$ACME_DEPLOY_SSH_CAFILE" _savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile" fi if [ -n "$Le_Deploy_ssh_cafile" ]; then - # backup file we are about to overwrite. - _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir ;" + 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=">>" + else + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir ;" + _pipe=">" + fi # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" > $Le_Deploy_ssh_cafile ;" + _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 to provided filename. + # If provided then fullchain certificate will be copied or appended to provided filename. if [ -n "$ACME_DEPLOY_SSH_FULLCHAIN" ]; then Le_Deploy_ssh_fullchain="$ACME_DEPLOY_SSH_FULLCHAIN" _savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain" fi if [ -n "$Le_Deploy_ssh_fullchain" ]; then - # backup file we are about to overwrite. - _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir ;" + 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=">>" + else + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir ;" + _pipe=">" + fi # copy new certificate into file. - _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" > $Le_Deploy_ssh_fullchain ;" - _info "will copy full chain to remote file $Le_Deploy_ssh_fullchain" + _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. - # A 2 second delay is inserted to allow system to stabalize after - # executing a service stop. if [ -n "$ACME_DEPLOY_SSH_REMOTE_CMD" ]; then Le_Deploy_ssh_remote_cmd="$ACME_DEPLOY_SSH_REMOTE_CMD" _savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd" fi if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then - if [ -n "$Le_Deploy_ssh_service_stop" ]; then - _cmdstr="$_cmdstr sleep 2 ;" - fi _cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd ;" _info "Will execute remote command $Le_Deploy_ssh_remote_cmd" fi - # SERVICE_START is optional. - # If provided then this command will be executed on remote host. - # A 2 second delay is inserted to allow system to stabalize after - # executing a service stop or previous command. - if [ -n "$ACME_DEPLOY_SSH_SERVICE_START" ]; then - Le_Deploy_ssh_service_start="$ACME_DEPLOY_SSH_SERVICE_START" - _savedomainconf Le_Deploy_ssh_service_start "$Le_Deploy_ssh_service_start" - fi - if [ -n "$Le_Deploy_ssh_service_start" ]; then - if [ -n "$Le_Deploy_ssh_service_stop" ] || [ -n "$Le_Deploy_ssh_remote_cmd" ]; then - _cmdstr="$_cmdstr sleep 2 ;" - fi - _cmdstr="$_cmdstr $Le_Deploy_ssh_service_start ;" - _info "Will start remote service with 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 @@ -199,7 +180,12 @@ ssh_deploy() { _info "Submitting sequence of commands to remote server by ssh" # quotations in bash cmd below intended. Squash travis spellcheck error # shellcheck disable=SC2029 - ssh -T -p "$Le_Deploy_ssh_port" "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmdstr'" + $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 $? + return $_ret } From a4b2cebef61e6add91c423aa146ab7313d7ab740 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 11 Feb 2017 16:42:44 -0500 Subject: [PATCH 016/272] Make backup of certs on remote server optional. Defaults to yes. --- deploy/README.md | 6 +++--- deploy/ssh.sh | 50 +++++++++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 95f31d45..01705838 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -49,6 +49,7 @@ export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" export ACME_DEPLOY_SSH_FULLCHAIN="" export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" +export ACME_DEPLOY_SSH_BACKUP="" ``` The values used above are illustrative only and represent those that could be used to deploy certificates to a QNAP NAS device running QTS 4.2 @@ -87,8 +88,7 @@ file Command to execute on the remote server after copying any certificates. This could be any additional command required for example to stop and restart the service. - -###Backups +###ACME_DEPLOY_SSH_BACKUP Before writing a certificate file to the remote server the existing certificate will be copied to a backup directory on the remote server. These are placed in a hidden directory in the home directory of the SSH @@ -97,4 +97,4 @@ user ~/.acme_ssh_deploy/[domain name]-backup-[timestamp] ``` Any backups older than 180 days will be deleted when new certificates -are deployed. +are deployed. This defaults to "yes" set to "no" to disable backup. diff --git a/deploy/ssh.sh b/deploy/ssh.sh index 0adeba89..26963ed5 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -12,15 +12,16 @@ # Only a username is required. All others are optional. # # The following examples are for QNAP NAS running QTS 4.2 -# export ACME_DEPLOY_SSH_CMD="" -# export ACME_DEPLOY_SSH_USER="admin" -# export ACME_DEPLOY_SSH_SERVER="qnap" +# export ACME_DEPLOY_SSH_CMD="" # defaults to ssh +# export ACME_DEPLOY_SSH_USER="admin" # required +# export ACME_DEPLOY_SSH_SERVER="qnap" # defaults to domain name # export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" # export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" # export ACME_DEPLOY_SSH_FULLCHAIN="" -# export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" - +# export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" +# export ACME_DEPLOY_SSH_BACKUP="" # yes or no, default to yes +# ######## Public functions ##################### #domain keyfile certfile cafile fullchain @@ -73,6 +74,14 @@ ssh_deploy() { Le_Deploy_ssh_cmd="ssh" fi + # BACKUP is optional. If not provided then default to yes + if [ "$ACME_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. @@ -82,8 +91,10 @@ ssh_deploy() { _savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile" fi if [ -n "$Le_Deploy_ssh_keyfile" ]; then - # backup file we are about to overwrite. - _cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir ;" + if [ "$Le_Deploy_ssh_backup" = "yes" ]; then + # backup file we are about to overwrite. + _cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir ;" + 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" @@ -96,13 +107,13 @@ ssh_deploy() { _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=">>" - else + elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir ;" - _pipe=">" fi # copy new certificate into file. _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile ;" @@ -116,14 +127,14 @@ ssh_deploy() { _savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile" fi if [ -n "$Le_Deploy_ssh_cafile" ]; then - if [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_keyfile" ] || - [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_certfile" ]; 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=">>" - else + elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir ;" - _pipe=">" fi # copy new certificate into file. _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile ;" @@ -137,15 +148,15 @@ ssh_deploy() { _savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain" fi if [ -n "$Le_Deploy_ssh_fullchain" ]; then - 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 + _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=">>" - else + elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # backup file we are about to overwrite. _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir ;" - _pipe=">" fi # copy new certificate into file. _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain ;" @@ -166,8 +177,7 @@ ssh_deploy() { if [ -z "$_cmdstr" ]; then _err "No remote commands to excute. Failed to deploy certificates to remote server" return 1 - else - # something to execute. + elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # run cleanup on the backup directory, erase all older than 180 days. _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf ; $_cmdstr" # Create our backup directory for overwritten cert files. From 18a90734d94f77ab90e45aa6e32bf065567e2cc2 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sat, 11 Feb 2017 17:55:05 -0500 Subject: [PATCH 017/272] Alternate backup cleanup after 180 days method. --- deploy/ssh.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/deploy/ssh.sh b/deploy/ssh.sh index 26963ed5..eb3690a6 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -75,7 +75,7 @@ ssh_deploy() { fi # BACKUP is optional. If not provided then default to yes - if [ "$ACME_DEPLOY_SSH_BACKUP" = "no"]; then + if [ "$ACME_DEPLOY_SSH_BACKUP" = "no" ]; then Le_Deploy_ssh_backup="no" elif [ -z "$Le_Deploy_ssh_backup" ]; then Le_Deploy_ssh_backup="yes" @@ -178,8 +178,12 @@ ssh_deploy() { _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. - _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf ; $_cmdstr" + # 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" From 3a77a6eded17122416ecec87babe59b8492ef927 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 12 Feb 2017 11:17:23 -0500 Subject: [PATCH 018/272] cleanup documentation and suppress some remote messages. --- deploy/README.md | 104 ++++++++++++++++++++++++++++++++++------------- deploy/ssh.sh | 22 +++++----- 2 files changed, 87 insertions(+), 39 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 01705838..8f386056 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -38,23 +38,21 @@ values are stored by acme.sh in the domain configuration files. Required... ```bash -export ACME_DEPLOY_SSH_USER="admin" +export ACME_DEPLOY_SSH_USER=username ``` Optional... ```bash -export ACME_DEPLOY_SSH_CMD="" -export ACME_DEPLOY_SSH_SERVER="qnap" -export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" -export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" -export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" -export ACME_DEPLOY_SSH_FULLCHAIN="" -export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" -export ACME_DEPLOY_SSH_BACKUP="" +export ACME_DEPLOY_SSH_CMD=custom ssh command +export ACME_DEPLOY_SSH_SERVER=url or ip address of remote host +export ACME_DEPLOY_SSH_KEYFILE=filename for private key +export ACME_DEPLOY_SSH_CERTFILE=filename for certificate file +export ACME_DEPLOY_SSH_CAFILE=filename for intermediate CA file +export ACME_DEPLOY_SSH_FULLCHAIN=filename forfullchain file +export ACME_DEPLOY_SSH_REMOTE_CMD=command to execute on remote host +export ACME_DEPLOY_SSH_BACKUP=yes or no ``` -The values used above are illustrative only and represent those that could -be used to deploy certificates to a QNAP NAS device running QTS 4.2 -###ACME_DEPLOY_SSH_USER +**ACME_DEPLOY_SSH_USER** Username at the remote host that SSH will login with. 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 @@ -63,32 +61,42 @@ can login to USER@URL from the host running acme.sh before using this script. The USER@URL at the remote server must also have has permissions to write to the target location of the certificate files and to execute any commands (e.g. to stop/start services). -###ACME_DEPLOY_SSH_CMD + +**ACME_DEPLOY_SSH_CMD** You can customize the ssh command used to connect to the remote host. For example if you need to connect to a specific port at the remote server you can set this -to, for example, "ssh -p 22" -###ACME_DEPLOY_SSH_SERVER +to, for example, "ssh -p 22" or to use `sshpass` to provide password inline +instead of exchanging ssh keys (this is not recommended, using keys is +more secure). + +**ACME_DEPLOY_SSH_SERVER** URL or IP Address of the remote server. If not provided then the domain name provided on the acme.sh --deploy command line is used. -###ACME_DEPLOY_SSH_KEYFILE + +**ACME_DEPLOY_SSH_KEYFILE** Target filename for the private key issued by LetsEncrypt. -###ACME_DEPLOY_SSH_CERTFILE -Target filename for the certificate issued by LetsEncrypt. If this filename -is the same as that provided for ACME_DEPLOY_SSH_KEYFILE then this certificate -is appended to the same file as the private key. -###ACME_DEPLOY_SSH_CAFILE + +**ACME_DEPLOY_SSH_CERTFILE** +Target filename for the certificate issued by LetsEncrypt. +If this is the same as the previous filename (for keyfile) then it is +appended to the same file. + +**ACME_DEPLOY_SSH_CAFILE** Target filename for the CA intermediate certificate issued by LetsEncrypt. -If this is the same as a previous filename then it is appended to the same -file -###ACME_DEPLOY_SSH_FULLCHAIN +If this is the same as a previous filename (for keyfile or certfile) then +it is appended to the same file. + +**ACME_DEPLOY_SSH_FULLCHAIN** Target filename for the fullchain certificate issued by LetsEncrypt. -If this is the same as a previous filename then it is appended to the same -file -###ACME_DEPLOY_SSH_REMOTE_CMD +If this is the same as a previous filename (for keyfile, certfile or +cafile) then it is appended to the same file. + +**ACME_DEPLOY_SSH_REMOTE_CMD** Command to execute on the remote server after copying any certificates. This could be any additional command required for example to stop and restart the service. -###ACME_DEPLOY_SSH_BACKUP + +**ACME_DEPLOY_SSH_BACKUP** Before writing a certificate file to the remote server the existing certificate will be copied to a backup directory on the remote server. These are placed in a hidden directory in the home directory of the SSH @@ -98,3 +106,43 @@ user ``` Any backups older than 180 days will be deleted when new certificates are deployed. This defaults to "yes" set to "no" to disable backup. + + +###Eamples using SSH deploy +The following example illustrates deploying certifcates to a QNAP NAS +running QTS 4.2 + +```bash +export ACME_DEPLOY_SSH_USER="admin" +export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" +export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" +export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" +export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" + +acme.sh --deploy -d qnap.example.com --deploy-hook ssh +``` + +The next example illustates deploying certificates to a Unifi +Contolller (tested with version 5.4.11). + +```bash +export ACME_DEPLOY_SSH_USER="root" +export ACME_DEPLOY_SSH_KEYFILE="/var/lib/unifi/unifi.example.com.key" +export ACME_DEPLOY_SSH_FULLCHAIN="/var/lib/unifi/unifi.example.com.cer" +export ACME_DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \ + -inkey /var/lib/unifi/unifi.example.com.key \ + -in /var/lib/unifi/unifi.example.com.cer \ + -out /var/lib/unifi/unifi.example.com.p12 \ + -name ubnt -password pass:temppass \ + && keytool -importkeystore -deststorepass aircontrolenterprise \ + -destkeypass aircontrolenterprise -destkeystore /var/lib/unifi/keystore \ + -srckeystore /var/lib/unifi/unifi.example.com.p12 \ + -srcstoretype PKCS12 -srcstorepass temppass -alias ubnt -noprompt \ + && service unifi restart" + +acme.sh --deploy -d qnap.example.com --deploy-hook ssh +``` +Note how in this exmple we execute several commands on the remote host +after the certificate files have been copied... to generate a pkcs12 file +compatible with Unifi, to import it into the Unifi keystore and then finaly +to restart the service. diff --git a/deploy/ssh.sh b/deploy/ssh.sh index eb3690a6..a8ed6a10 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -93,10 +93,10 @@ ssh_deploy() { 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 ;" + _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 ;" + _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;" _info "will copy private key to remote file $Le_Deploy_ssh_keyfile" fi @@ -113,10 +113,10 @@ ssh_deploy() { _pipe=">>" elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # backup file we are about to overwrite. - _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir ;" + _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 ;" + _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;" _info "will copy certificate to remote file $Le_Deploy_ssh_certfile" fi @@ -134,10 +134,10 @@ ssh_deploy() { _pipe=">>" elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # backup file we are about to overwrite. - _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir ;" + _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 ;" + _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;" _info "will copy CA file to remote file $Le_Deploy_ssh_cafile" fi @@ -156,10 +156,10 @@ ssh_deploy() { _pipe=">>" elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then # backup file we are about to overwrite. - _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir ;" + _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 ;" + _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;" _info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain" fi @@ -170,7 +170,7 @@ ssh_deploy() { _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 ;" + _cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;" _info "Will execute remote command $Le_Deploy_ssh_remote_cmd" fi @@ -183,9 +183,9 @@ ssh_deploy() { _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" + # 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" + _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 From e35e31324078803cd1db2c398d070ce2cc184252 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 12 Feb 2017 11:20:16 -0500 Subject: [PATCH 019/272] Fix error in Unifi example --- deploy/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 8f386056..8cc40cd3 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -135,12 +135,13 @@ export ACME_DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \ -out /var/lib/unifi/unifi.example.com.p12 \ -name ubnt -password pass:temppass \ && keytool -importkeystore -deststorepass aircontrolenterprise \ - -destkeypass aircontrolenterprise -destkeystore /var/lib/unifi/keystore \ + -destkeypass aircontrolenterprise \ + -destkeystore /var/lib/unifi/keystore \ -srckeystore /var/lib/unifi/unifi.example.com.p12 \ -srcstoretype PKCS12 -srcstorepass temppass -alias ubnt -noprompt \ && service unifi restart" -acme.sh --deploy -d qnap.example.com --deploy-hook ssh +acme.sh --deploy -d unifi.example.com --deploy-hook ssh ``` Note how in this exmple we execute several commands on the remote host after the certificate files have been copied... to generate a pkcs12 file From 6f4abe95cb8be54272f080a98a3991e7ab9ee2ec Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 12 Feb 2017 11:24:00 -0500 Subject: [PATCH 020/272] update markdown examples. --- deploy/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 8cc40cd3..9c22e80b 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -28,7 +28,7 @@ The ssh deploy plugin allows you to deploy certificates to a remote host using SSH command to connect to the remote server. The ssh plugin is invoked with the following command... -```bash +```sh acme.sh --deploy -d example.com --deploy-hook ssh ``` Prior to running this for the first time you must tell the plugin where @@ -37,17 +37,17 @@ environment variables. This is not required for subsequent runs as the values are stored by acme.sh in the domain configuration files. Required... -```bash +``` export ACME_DEPLOY_SSH_USER=username ``` Optional... -```bash +``` export ACME_DEPLOY_SSH_CMD=custom ssh command export ACME_DEPLOY_SSH_SERVER=url or ip address of remote host export ACME_DEPLOY_SSH_KEYFILE=filename for private key export ACME_DEPLOY_SSH_CERTFILE=filename for certificate file export ACME_DEPLOY_SSH_CAFILE=filename for intermediate CA file -export ACME_DEPLOY_SSH_FULLCHAIN=filename forfullchain file +export ACME_DEPLOY_SSH_FULLCHAIN=filename for fullchain file export ACME_DEPLOY_SSH_REMOTE_CMD=command to execute on remote host export ACME_DEPLOY_SSH_BACKUP=yes or no ``` @@ -101,7 +101,7 @@ Before writing a certificate file to the remote server the existing certificate will be copied to a backup directory on the remote server. These are placed in a hidden directory in the home directory of the SSH user -```bash +```sh ~/.acme_ssh_deploy/[domain name]-backup-[timestamp] ``` Any backups older than 180 days will be deleted when new certificates @@ -112,7 +112,7 @@ are deployed. This defaults to "yes" set to "no" to disable backup. The following example illustrates deploying certifcates to a QNAP NAS running QTS 4.2 -```bash +```sh export ACME_DEPLOY_SSH_USER="admin" export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" @@ -125,7 +125,7 @@ acme.sh --deploy -d qnap.example.com --deploy-hook ssh The next example illustates deploying certificates to a Unifi Contolller (tested with version 5.4.11). -```bash +```sh export ACME_DEPLOY_SSH_USER="root" export ACME_DEPLOY_SSH_KEYFILE="/var/lib/unifi/unifi.example.com.key" export ACME_DEPLOY_SSH_FULLCHAIN="/var/lib/unifi/unifi.example.com.cer" From 76c1ed6628009d8752a8135e80c2614a28bb5e18 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 12 Feb 2017 18:08:17 -0500 Subject: [PATCH 021/272] Additional documentation for the unifi example. --- deploy/README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index 9c22e80b..10f355d6 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -143,7 +143,21 @@ export ACME_DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \ acme.sh --deploy -d unifi.example.com --deploy-hook ssh ``` -Note how in this exmple we execute several commands on the remote host +In this exmple we execute several commands on the remote host after the certificate files have been copied... to generate a pkcs12 file compatible with Unifi, to import it into the Unifi keystore and then finaly to restart the service. + +Note also that once the certificate is imported +into the keystore the individual certificate files are no longer +required. We could if we desired delete those files immediately. If we +do that then we should disable backup at the remote host (as there are +no files to backup -- they were erased during deployment). For example... +```sh +export ACME_DEPLOY_SSH_BACKUP=no +# modify the end of the remte command... +&& rm /var/lib/unifi/unifi.example.com.key \ + /var/lib/unifi/unifi.example.com.cer \ + /var/lib/unifi/unifi.example.com.p12 \ +&& service unifi restart +``` From d04ccb7a3ff71fbf114ace288aea5a49faf9db1a Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 12 Feb 2017 18:20:43 -0500 Subject: [PATCH 022/272] fix spelling error in readme --- deploy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index 10f355d6..df0cdf0a 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -155,7 +155,7 @@ do that then we should disable backup at the remote host (as there are no files to backup -- they were erased during deployment). For example... ```sh export ACME_DEPLOY_SSH_BACKUP=no -# modify the end of the remte command... +# modify the end of the remote command... && rm /var/lib/unifi/unifi.example.com.key \ /var/lib/unifi/unifi.example.com.cer \ /var/lib/unifi/unifi.example.com.p12 \ From 68a35155e4c2ab8ecd13c60a6e723fb0bdd8eaf3 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Mon, 13 Feb 2017 20:32:12 -0500 Subject: [PATCH 023/272] Improve documentation in readme --- deploy/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index df0cdf0a..ab38f275 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -110,7 +110,7 @@ are deployed. This defaults to "yes" set to "no" to disable backup. ###Eamples using SSH deploy The following example illustrates deploying certifcates to a QNAP NAS -running QTS 4.2 +(tested with QTS version 4.2.3) ```sh export ACME_DEPLOY_SSH_USER="admin" @@ -121,6 +121,10 @@ export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" acme.sh --deploy -d qnap.example.com --deploy-hook ssh ``` +Note how in this example both the private key and certificate point to +the same file. This will result in the certificate being appended +to the same file as the private key... a common requirement of several +services. The next example illustates deploying certificates to a Unifi Contolller (tested with version 5.4.11). From 06492067966ef03344c92892d19b6fccee1cb86e Mon Sep 17 00:00:00 2001 From: David Kerr Date: Tue, 7 Mar 2017 11:57:03 -0500 Subject: [PATCH 024/272] remove _ACME prefix from all exported variables. --- deploy/README.md | 56 ++++++++++++++++++++++++------------------------ deploy/ssh.sh | 54 +++++++++++++++++++++++----------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index ab38f275..22b8e8d2 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -38,21 +38,21 @@ values are stored by acme.sh in the domain configuration files. Required... ``` -export ACME_DEPLOY_SSH_USER=username +export DEPLOY_SSH_USER=username ``` Optional... ``` -export ACME_DEPLOY_SSH_CMD=custom ssh command -export ACME_DEPLOY_SSH_SERVER=url or ip address of remote host -export ACME_DEPLOY_SSH_KEYFILE=filename for private key -export ACME_DEPLOY_SSH_CERTFILE=filename for certificate file -export ACME_DEPLOY_SSH_CAFILE=filename for intermediate CA file -export ACME_DEPLOY_SSH_FULLCHAIN=filename for fullchain file -export ACME_DEPLOY_SSH_REMOTE_CMD=command to execute on remote host -export ACME_DEPLOY_SSH_BACKUP=yes or no +export DEPLOY_SSH_CMD=custom ssh command +export DEPLOY_SSH_SERVER=url or ip address of remote host +export DEPLOY_SSH_KEYFILE=filename for private key +export DEPLOY_SSH_CERTFILE=filename for certificate file +export DEPLOY_SSH_CAFILE=filename for intermediate CA file +export DEPLOY_SSH_FULLCHAIN=filename for fullchain file +export DEPLOY_SSH_REMOTE_CMD=command to execute on remote host +export DEPLOY_SSH_BACKUP=yes or no ``` -**ACME_DEPLOY_SSH_USER** +**DEPLOY_SSH_USER** Username at the remote host that SSH will login with. 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 @@ -62,41 +62,41 @@ The USER@URL at the remote server must also have has permissions to write to the target location of the certificate files and to execute any commands (e.g. to stop/start services). -**ACME_DEPLOY_SSH_CMD** +**DEPLOY_SSH_CMD** You can customize the ssh command used to connect to the remote host. For example if you need to connect to a specific port at the remote server you can set this to, for example, "ssh -p 22" or to use `sshpass` to provide password inline instead of exchanging ssh keys (this is not recommended, using keys is more secure). -**ACME_DEPLOY_SSH_SERVER** +**DEPLOY_SSH_SERVER** URL or IP Address of the remote server. If not provided then the domain name provided on the acme.sh --deploy command line is used. -**ACME_DEPLOY_SSH_KEYFILE** +**DEPLOY_SSH_KEYFILE** Target filename for the private key issued by LetsEncrypt. -**ACME_DEPLOY_SSH_CERTFILE** +**DEPLOY_SSH_CERTFILE** Target filename for the certificate issued by LetsEncrypt. If this is the same as the previous filename (for keyfile) then it is appended to the same file. -**ACME_DEPLOY_SSH_CAFILE** +**DEPLOY_SSH_CAFILE** Target filename for the CA intermediate certificate issued by LetsEncrypt. If this is the same as a previous filename (for keyfile or certfile) then it is appended to the same file. -**ACME_DEPLOY_SSH_FULLCHAIN** +**DEPLOY_SSH_FULLCHAIN** Target filename for the fullchain certificate issued by LetsEncrypt. If this is the same as a previous filename (for keyfile, certfile or cafile) then it is appended to the same file. -**ACME_DEPLOY_SSH_REMOTE_CMD** +**DEPLOY_SSH_REMOTE_CMD** Command to execute on the remote server after copying any certificates. This could be any additional command required for example to stop and restart the service. -**ACME_DEPLOY_SSH_BACKUP** +**DEPLOY_SSH_BACKUP** Before writing a certificate file to the remote server the existing certificate will be copied to a backup directory on the remote server. These are placed in a hidden directory in the home directory of the SSH @@ -113,11 +113,11 @@ The following example illustrates deploying certifcates to a QNAP NAS (tested with QTS version 4.2.3) ```sh -export ACME_DEPLOY_SSH_USER="admin" -export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" -export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" -export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" -export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" +export DEPLOY_SSH_USER="admin" +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_REMOTE_CMD="/etc/init.d/stunnel.sh restart" acme.sh --deploy -d qnap.example.com --deploy-hook ssh ``` @@ -130,10 +130,10 @@ The next example illustates deploying certificates to a Unifi Contolller (tested with version 5.4.11). ```sh -export ACME_DEPLOY_SSH_USER="root" -export ACME_DEPLOY_SSH_KEYFILE="/var/lib/unifi/unifi.example.com.key" -export ACME_DEPLOY_SSH_FULLCHAIN="/var/lib/unifi/unifi.example.com.cer" -export ACME_DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \ +export DEPLOY_SSH_USER="root" +export DEPLOY_SSH_KEYFILE="/var/lib/unifi/unifi.example.com.key" +export DEPLOY_SSH_FULLCHAIN="/var/lib/unifi/unifi.example.com.cer" +export DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \ -inkey /var/lib/unifi/unifi.example.com.key \ -in /var/lib/unifi/unifi.example.com.cer \ -out /var/lib/unifi/unifi.example.com.p12 \ @@ -158,7 +158,7 @@ required. We could if we desired delete those files immediately. If we do that then we should disable backup at the remote host (as there are no files to backup -- they were erased during deployment). For example... ```sh -export ACME_DEPLOY_SSH_BACKUP=no +export DEPLOY_SSH_BACKUP=no # modify the end of the remote command... && rm /var/lib/unifi/unifi.example.com.key \ /var/lib/unifi/unifi.example.com.cer \ diff --git a/deploy/ssh.sh b/deploy/ssh.sh index a8ed6a10..a68da356 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -12,15 +12,15 @@ # Only a username is required. All others are optional. # # The following examples are for QNAP NAS running QTS 4.2 -# export ACME_DEPLOY_SSH_CMD="" # defaults to ssh -# export ACME_DEPLOY_SSH_USER="admin" # required -# export ACME_DEPLOY_SSH_SERVER="qnap" # defaults to domain name -# export ACME_DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" -# export ACME_DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" -# export ACME_DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" -# export ACME_DEPLOY_SSH_FULLCHAIN="" -# export ACME_DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart" -# export ACME_DEPLOY_SSH_BACKUP="" # yes or no, default to yes +# 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 ##################### @@ -48,34 +48,34 @@ ssh_deploy() { _debug _cfullchain "$_cfullchain" # USER is required to login by SSH to remote host. - if [ -z "$ACME_DEPLOY_SSH_USER" ]; then + if [ -z "$DEPLOY_SSH_USER" ]; then if [ -z "$Le_Deploy_ssh_user" ]; then - _err "ACME_DEPLOY_SSH_USER not defined." + _err "DEPLOY_SSH_USER not defined." return 1 fi else - Le_Deploy_ssh_user="$ACME_DEPLOY_SSH_USER" + 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 "$ACME_DEPLOY_SSH_SERVER" ]; then - Le_Deploy_ssh_server="$ACME_DEPLOY_SSH_SERVER" + 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 "$ACME_DEPLOY_SSH_CMD" ]; then - Le_Deploy_ssh_cmd="$ACME_DEPLOY_SSH_CMD" + 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 [ "$ACME_DEPLOY_SSH_BACKUP" = "no" ]; then + if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then Le_Deploy_ssh_backup="no" elif [ -z "$Le_Deploy_ssh_backup" ]; then Le_Deploy_ssh_backup="yes" @@ -86,8 +86,8 @@ ssh_deploy() { # KEYFILE is optional. # If provided then private key will be copied to provided filename. - if [ -n "$ACME_DEPLOY_SSH_KEYFILE" ]; then - Le_Deploy_ssh_keyfile="$ACME_DEPLOY_SSH_KEYFILE" + 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 @@ -102,8 +102,8 @@ ssh_deploy() { # CERTFILE is optional. # If provided then private key will be copied or appended to provided filename. - if [ -n "$ACME_DEPLOY_SSH_CERTFILE" ]; then - Le_Deploy_ssh_certfile="$ACME_DEPLOY_SSH_CERTFILE" + 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 @@ -122,8 +122,8 @@ ssh_deploy() { # CAFILE is optional. # If provided then CA intermediate certificate will be copied or appended to provided filename. - if [ -n "$ACME_DEPLOY_SSH_CAFILE" ]; then - Le_Deploy_ssh_cafile="$ACME_DEPLOY_SSH_CAFILE" + 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 @@ -143,8 +143,8 @@ ssh_deploy() { # FULLCHAIN is optional. # If provided then fullchain certificate will be copied or appended to provided filename. - if [ -n "$ACME_DEPLOY_SSH_FULLCHAIN" ]; then - Le_Deploy_ssh_fullchain="$ACME_DEPLOY_SSH_FULLCHAIN" + 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 @@ -165,8 +165,8 @@ ssh_deploy() { # REMOTE_CMD is optional. # If provided then this command will be executed on remote host. - if [ -n "$ACME_DEPLOY_SSH_REMOTE_CMD" ]; then - Le_Deploy_ssh_remote_cmd="$ACME_DEPLOY_SSH_REMOTE_CMD" + 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 From 158abf5c6ca5c6de5a015569196403e4dad43395 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Tue, 7 Mar 2017 12:09:07 -0500 Subject: [PATCH 025/272] Remove line from README.md that I mistakenly added during merge with master. --- deploy/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 4008f64a..60ab34ae 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -26,8 +26,6 @@ Before you can deploy your cert, you must [issue the cert first](https://github. ## 3. Deploy the cert to remote server through SSH access. -Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). - The ssh deploy plugin allows you to deploy certificates to a remote host using SSH command to connect to the remote server. The ssh plugin is invoked with the following command... From 212d0f24d8d264c30969d5f19bb61c5840ae5108 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Tue, 31 Oct 2017 22:35:15 -0700 Subject: [PATCH 026/272] [cloudns] Add support for sub user IDs --- dnsapi/README.md | 7 +++++-- dnsapi/dns_cloudns.sh | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index cff59c79..7aeef011 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -409,10 +409,13 @@ acme.sh --issue --dns dns_dgon -d example.com -d www.example.com ## 21. Use ClouDNS.net API -You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/ +You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/. For security reasons, it's recommended to use a sub user ID that only has access to the necessary zones, as a regular API user has access to your entire account. ``` -export CLOUDNS_AUTH_ID=XXXXX +# Use this for a sub auth ID +export CLOUDNS_SUB_AUTH_ID=XXXXX +# Use this for a regular auth ID +#export CLOUDNS_AUTH_ID=XXXXX export CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" ``` diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index b1861b24..c459551f 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -97,17 +97,19 @@ _dns_cloudns_init_check() { fi CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}" + CLOUDNS_SUB_AUTH_ID="${CLOUDNS_SUB_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_SUB_AUTH_ID)}" CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}" - if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then + if [ -z "$CLOUDNS_AUTH_ID$CLOUDNS_SUB_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then CLOUDNS_AUTH_ID="" + CLOUDNS_SUB_AUTH_ID="" CLOUDNS_AUTH_PASSWORD="" _err "You don't specify cloudns api id and password yet." _err "Please create you id and password and try again." return 1 fi - if [ -z "$CLOUDNS_AUTH_ID" ]; then - _err "CLOUDNS_AUTH_ID is not configured" + if [ -z "$CLOUDNS_AUTH_ID" ] && [ -z "$CLOUDNS_SUB_AUTH_ID" ]; then + _err "CLOUDNS_AUTH_ID or CLOUDNS_SUB_AUTH_ID is not configured" return 1 fi @@ -125,6 +127,7 @@ _dns_cloudns_init_check() { #save the api id and password to the account conf file. _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _saveaccountconf_mutable CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID" _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" CLOUDNS_INIT_CHECK_COMPLETED=1 @@ -168,12 +171,19 @@ _dns_cloudns_http_api_call() { method=$1 _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID" _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" + if [ ! -z "$CLOUDNS_SUB_AUTH_ID" ]; then + auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID" + else + auth_user="auth-id=$CLOUDNS_AUTH_ID" + fi; + if [ -z "$2" ]; then - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" + data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD" else - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" + data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" fi response="$(_get "$CLOUDNS_API/$method?$data")" From bab4f691c5bade8a50a88ef715b834c8ec18cb1e Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 12 Nov 2017 18:38:30 -0800 Subject: [PATCH 027/272] Fix lint warning --- dnsapi/dns_cloudns.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index c459551f..4a1ae641 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -4,6 +4,7 @@ # Repository: https://github.com/ClouDNS/acme.sh/ #CLOUDNS_AUTH_ID=XXXXX +#CLOUDNS_SUB_AUTH_ID=XXXXX #CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" CLOUDNS_API="https://api.cloudns.net" @@ -178,7 +179,7 @@ _dns_cloudns_http_api_call() { auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID" else auth_user="auth-id=$CLOUDNS_AUTH_ID" - fi; + fi if [ -z "$2" ]; then data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD" From f62457a24e2f286f309c245b09bd52631f8e00f9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 13 Nov 2017 20:54:29 +0800 Subject: [PATCH 028/272] fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b45cc464..dda8eb10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: openssl version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true; export PATH="$_old_path"; - else sudo apt-get install socat; + else sudo apt-get -y update && sudo apt-get install -y socat; fi script: From ceafe389afb406b92f77392fd7fe2005cb6b750f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 26 Nov 2017 20:57:02 +0800 Subject: [PATCH 029/272] fix https://github.com/Neilpang/acme.sh/issues/1109 --- dnsapi/dns_freedns.sh | 77 ++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 53da4118..ae77d061 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -53,6 +53,8 @@ dns_freedns_add() { i="$(_math "$i" - 1)" sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" + _debug top_domain "$top_domain" + _debug sub_domain "$sub_domain" # Sometimes FreeDNS does not return the subdomain page but rather # returns a page regarding becoming a premium member. This usually # happens after a period of inactivity. Immediately trying again @@ -61,7 +63,6 @@ dns_freedns_add() { attempts=2 while [ "$attempts" -gt "0" ]; do attempts="$(_math "$attempts" - 1)" - htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then @@ -70,19 +71,11 @@ dns_freedns_add() { fi return 1 fi + _debug2 htmlpage "$htmlpage" + + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '
' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $top_domain)" + _debug2 subdomain_csv "$subdomain_csv" - # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo "$htmlpage" \ - | grep -i -e ']*>/\n/Ig' \ - | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ - | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ - | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ - | grep 'edit.php?' \ - | grep "$top_domain")" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the top domain we are looking for. # So all we should be left with is CSV of table of subdomains we are @@ -90,30 +83,32 @@ dns_freedns_add() { # Now we have to read through this table and extract the data we need lines="$(echo "$subdomain_csv" | wc -l)" - nl=' -' i=0 found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" - tmp="$(echo "$line" | cut -d ',' -f 1)" - if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then + line="$(echo "$subdomain_csv" | sed -n ${i}p)" + _debug2 line "$line" + if [ $found = 0 ] && _contains "$line" "$top_domain"; then # this line will contain DNSdomainid for the top_domain - DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')" + DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" + _debug2 DNSdomainid "$DNSdomainid" found=1 else # lines contain DNS records for all subdomains - DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" - DNStype="$(echo "$line" | cut -d ',' -f 3)" + DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _debug2 DNSname "$DNSname" + DNStype="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNStype "$DNStype" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" + DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" # Now get current value for the TXT record. This method may # not produce accurate results as the value field is truncated # on this webpage. To get full value we would need to load # another page. However we don't really need this so long as # there is only one TXT record for the acme challenge subdomain. - DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" + DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNSvalue "$DNSvalue" if [ $found != 0 ]; then break # we are breaking out of the loop at the first match of DNS name @@ -169,8 +164,7 @@ dns_freedns_add() { return 0 else # Delete the old TXT record (with the wrong value) - _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" - if [ "$?" = "0" ]; then + if _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"; then # And add in new TXT record with the value provided _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" fi @@ -210,18 +204,9 @@ dns_freedns_rm() { return 1 fi - # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo "$htmlpage" \ - | grep -i -e ']*>/\n/Ig' \ - | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ - | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ - | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ - | grep 'edit.php?' \ - | grep "$fulldomain")" + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $fulldomain)" + _debug2 subdomain_csv "$subdomain_csv" + # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the domain name we are looking for. # So all we should be left with is CSV of table of subdomains we are @@ -229,19 +214,21 @@ dns_freedns_rm() { # Now we have to read through this table and extract the data we need lines="$(echo "$subdomain_csv" | wc -l)" - nl=' -' i=0 found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" - DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" - DNStype="$(echo "$line" | cut -d ',' -f 3)" + line="$(echo "$subdomain_csv" | sed -n ${i}p)" + _debug2 line "$line" + DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _debug2 DNSname "$DNSname" + DNStype="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNStype "$DNStype" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" - DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" - _debug "DNSvalue: $DNSvalue" + DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" + _debug2 DNSdataid "$DNSdataid" + DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNSvalue "$DNSvalue" # if [ "$DNSvalue" = "$txtvalue" ]; then # Testing value match fails. Website is truncating the value # field. So for now we will assume that there is only one TXT From b615cce92d69c0de937a15538ef764a21fda33b5 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Dec 2017 19:54:33 +0800 Subject: [PATCH 030/272] fix https://github.com/Neilpang/acme.sh/issues/1127 --- Dockerfile | 2 +- acme.sh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 97626ba0..b2866739 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine +FROM alpine:3.6 RUN apk update -f \ && apk --no-cache add -f \ diff --git a/acme.sh b/acme.sh index 9c8d6d4e..98f4067a 100755 --- a/acme.sh +++ b/acme.sh @@ -463,8 +463,7 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then fi _h2b() { - if _exists xxd; then - xxd -r -p + if _exists xxd && xxd -r -p 2>/dev/null; then return fi From 529cbc037900506969e996e98903d7e60b211751 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Dec 2017 12:51:51 +0800 Subject: [PATCH 031/272] run ci in docker --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dda8eb10..ab5632dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,14 @@ language: shell sudo: required +dist: trusty os: - linux - osx +services: + - docker + env: global: - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 @@ -30,7 +34,6 @@ install: openssl version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true; export PATH="$_old_path"; - else sudo apt-get -y update && sudo apt-get install -y socat; fi script: @@ -44,7 +47,7 @@ script: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi - cd .. - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest - - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi + - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi From dcf8457f4d4b6d80236557fbc77ecd7060f56ec0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Dec 2017 13:16:37 +0800 Subject: [PATCH 032/272] fix format --- dnsapi/dns_freedns.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index ae77d061..afd8b796 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -73,7 +73,7 @@ dns_freedns_add() { fi _debug2 htmlpage "$htmlpage" - subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $top_domain)" + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")" _debug2 subdomain_csv "$subdomain_csv" # The above beauty ends with striping out rows that do not have an @@ -87,7 +87,7 @@ dns_freedns_add() { found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | sed -n ${i}p)" + line="$(echo "$subdomain_csv" | sed -n "${i}p")" _debug2 line "$line" if [ $found = 0 ] && _contains "$line" "$top_domain"; then # this line will contain DNSdomainid for the top_domain @@ -204,7 +204,7 @@ dns_freedns_rm() { return 1 fi - subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $fulldomain)" + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")" _debug2 subdomain_csv "$subdomain_csv" # The above beauty ends with striping out rows that do not have an @@ -218,7 +218,7 @@ dns_freedns_rm() { found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | sed -n ${i}p)" + line="$(echo "$subdomain_csv" | sed -n "${i}p")" _debug2 line "$line" DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" _debug2 DNSname "$DNSname" From 9eeebb147f9747a68e567ac40e202c51dc7aa4a3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Dec 2017 20:57:25 +0800 Subject: [PATCH 033/272] fix osx build --- .travis.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab5632dd..b6b57423 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,20 +22,10 @@ addons: install: - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then - brew update && brew install openssl socat; - brew info openssl; - ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; - ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; - ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl; - _old_path="$PATH"; - echo "PATH=$PATH"; - export PATH=""; - export ACME_OPENSSL_BIN="/usr/local/openssl"; - openssl version 2>&1 || true; - $ACME_OPENSSL_BIN version 2>&1 || true; - export PATH="$_old_path"; + brew update && brew install socat; + export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ; fi - + script: - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" - command -V openssl && openssl version From 0ca3141088ead8a638c5067519e6c97216f699cf Mon Sep 17 00:00:00 2001 From: Aarup Date: Wed, 6 Dec 2017 11:50:54 +0100 Subject: [PATCH 034/272] Added support for UnoEuro api --- dnsapi/dns_unoeuro.sh | 194 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 dnsapi/dns_unoeuro.sh diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh new file mode 100644 index 00000000..985b441d --- /dev/null +++ b/dnsapi/dns_unoeuro.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env sh + +# +#Uno_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# +#Uno_User="UExxxxxx" + +Uno_Api="https://api.unoeuro.com/1/$Uno_User/$Uno_Key" + +######## 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 email address." + _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_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -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 + + _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 "Don't need to remove." + else + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -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" >/dev/null; 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="X-Auth-Email: $Uno_User" + #export _H2="X-Auth-Key: $Uno_Key" + export _H1="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$Uno_Api/$ep" "" "$m")" + else + response="$(_get "$Uno_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 70702e41e994ce98dac23b06bf59e2bbcdf4f560 Mon Sep 17 00:00:00 2001 From: Aarup Date: Wed, 6 Dec 2017 11:55:29 +0100 Subject: [PATCH 035/272] Use _head_n --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 985b441d..4774187d 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -65,7 +65,7 @@ dns_unoeuro_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -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}" @@ -114,7 +114,7 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Don't need to remove." else - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" if [ -z "$record_id" ]; then From 78712245f70800f4bd4875732271b9e96af1c4f9 Mon Sep 17 00:00:00 2001 From: Aarup Date: Wed, 6 Dec 2017 12:13:40 +0100 Subject: [PATCH 036/272] Add UnoEuro to README --- README.md | 1 + dnsapi/README.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index 7d4353c6..9e64e613 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,7 @@ You don't have to do anything manually! 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) 1. Hurricane Electric DNS service (https://dns.he.net) +1. UnoEuro API (https://www.unoeuro.com/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index cff59c79..5fdcd849 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -602,6 +602,22 @@ The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/accoun Please report any issues to https://github.com/angel333/acme.sh or to . +## 32. Use UnoEuro API to automatically issue cert + +First you need to login to your UnoEuro account to get your API key. + +``` +export Uno_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +export Uno_User="UExxxxxx" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_unoeuro -d example.com -d www.example.com +``` + +The `Uno_Key` and `Uno_User` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From 657334fb67e25b5503d02fc9abba2f5a2dbca410 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 11:47:01 +0100 Subject: [PATCH 037/272] Add support for inwx.de API --- README.md | 1 + dnsapi/README.md | 17 +++ dnsapi/dns_inwx.sh | 365 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100755 dnsapi/dns_inwx.sh diff --git a/README.md b/README.md index 7d4353c6..e8f94644 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,7 @@ You don't have to do anything manually! 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) 1. Hurricane Electric DNS service (https://dns.he.net) +1. INWX (https://www.inwx.de/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index cff59c79..768f69a3 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -602,6 +602,23 @@ The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/accoun Please report any issues to https://github.com/angel333/acme.sh or to . +## 31. Use INWX + +INWX offers an xmlrpc api with your standard login credentials, set them like so: + +``` +export INWX_User="yourusername" +export INWX_Password="password" +``` + +Then you can issue your certificates with: + +``` +acme.sh --issue --dns dns_inwx -d example.com -d www.example.com +``` + +The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh new file mode 100755 index 00000000..db5b18c8 --- /dev/null +++ b/dnsapi/dns_inwx.sh @@ -0,0 +1,365 @@ +#!/usr/local/bin/bash + +# +#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" + + _debug "Getting txt records" + + + printf -v xml_content ' + + nameserver.info + + + + + + domain + + %s + + + + type + + TXT + + + + name + + %s + + + + + + + ' $_domain $_sub_domain + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + _err "Error could net get txt records" + return 1 + fi + + if ! printf "%s" "$response" | grep "count" -q; then + _info "Adding record" + _inwx_add_record $_domain $_sub_domain $txtvalue + else + _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _info "Updating record" + _inwx_update_record $_record_id $txtvalue + fi + +} + +#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" + + + printf -v xml_content ' + + nameserver.info + + + + + + domain + + %s + + + + type + + TXT + + + + name + + %s + + + + + + + ' $_domain $_sub_domain + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + _err "Error could not get txt records" + return 1 + fi + + if ! printf "%s" "$response" | grep "count" -q; then + _info "Do not need to delete record" + else + _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _info "Deleting record" + _inwx_delete_record $_record_id + fi + + +} + +#################### Private functions below ################################## + +_inwx_login() { + + printf -v xml_content ' + + account.login + + + + + + user + + %s + + + + pass + + %s + + + + + + + ' $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 + + + export _H1=$(_inwx_login) + printf -v xml_content ' + + nameserver.list + ' + + 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 + printf -v xml_content ' + + nameserver.deleteRecord + + + + + + id + + %s + + + + + + + ' $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 + printf -v xml_content ' + + nameserver.updateRecord + + + + + + content + + %s + + + + id + + %s + + + + + + + ' $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 + + _debug domain: $domain + _debug value: $txtval + _debug subd: $sub_domain + + printf -v xml_content ' + + nameserver.createRecord + + + + + + domain + + %s + + + + type + + TXT + + + + content + + %s + + + + name + + %s + + + + + + + ' $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 +} From a8202d4b3780e04b3b68cc7196ee47871e63ee3d Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 12:05:03 +0100 Subject: [PATCH 038/272] Fix CI issues --- dnsapi/dns_inwx.sh | 64 +++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index db5b18c8..9e2302b7 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -35,11 +35,9 @@ dns_inwx_add() { fi _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - - printf -v xml_content ' + printf -v xml_content ' nameserver.info @@ -68,21 +66,21 @@ dns_inwx_add() { - ' $_domain $_sub_domain + ' "$_domain" "$_sub_domain" response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error could net get txt records" return 1 fi if ! printf "%s" "$response" | grep "count" -q; then _info "Adding record" - _inwx_add_record $_domain $_sub_domain $txtvalue + _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" else - _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') _info "Updating record" - _inwx_update_record $_record_id $txtvalue + _inwx_update_record "$_record_id" "$txtvalue" fi } @@ -117,8 +115,7 @@ dns_inwx_rm() { _debug "Getting txt records" - - printf -v xml_content ' + printf -v xml_content ' nameserver.info @@ -147,10 +144,10 @@ dns_inwx_rm() { - ' $_domain $_sub_domain + ' "$_domain" "$_sub_domain" response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error could not get txt records" return 1 fi @@ -158,12 +155,11 @@ dns_inwx_rm() { if ! printf "%s" "$response" | grep "count" -q; then _info "Do not need to delete record" else - _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') _info "Deleting record" - _inwx_delete_record $_record_id + _inwx_delete_record "$_record_id" fi - } #################### Private functions below ################################## @@ -196,26 +192,26 @@ _inwx_login() { ' $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 + domain=$1 _debug "get root" domain=$1 i=2 p=1 - - export _H1=$(_inwx_login) + _H1=$(_inwx_login) + export _H1 printf -v xml_content ' nameserver.list ' - + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -256,17 +252,16 @@ _inwx_delete_record() { - ' $record_id - + ' "$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() { @@ -295,17 +290,16 @@ _inwx_update_record() { - ' $txtval $record_id - - + ' "$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() { @@ -314,10 +308,6 @@ _inwx_add_record() { sub_domain=$2 txtval=$3 - _debug domain: $domain - _debug value: $txtval - _debug subd: $sub_domain - printf -v xml_content ' nameserver.createRecord @@ -353,10 +343,10 @@ _inwx_add_record() { - ' $domain $txtval $sub_domain - + ' "$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 From ecba959dd99cda64d72c88c1d9c7d77894f72f80 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 13:07:05 +0100 Subject: [PATCH 039/272] Fix missed whitespaces --- dnsapi/dns_inwx.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 9e2302b7..b02d1fb1 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -155,7 +155,7 @@ dns_inwx_rm() { if ! printf "%s" "$response" | grep "count" -q; then _info "Do not need to delete record" else - _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') _info "Deleting record" _inwx_delete_record "$_record_id" fi @@ -253,7 +253,7 @@ _inwx_delete_record() { ' "$record_id" - + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then @@ -261,7 +261,7 @@ _inwx_delete_record() { return 1 fi return 0 - + } _inwx_update_record() { @@ -293,13 +293,13 @@ _inwx_update_record() { ' "$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() { From a00169451fc21653763efefd0dc7938362a0e5fa Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 13:10:59 +0100 Subject: [PATCH 040/272] Change bash to sh to fit project requirements --- dnsapi/dns_inwx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index b02d1fb1..78e101e9 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -1,4 +1,4 @@ -#!/usr/local/bin/bash +#!/usr/local/bin/sh # #INWX_User="username" From 4a9f607d318a6a5e451f6962e44697c857580eea Mon Sep 17 00:00:00 2001 From: JAA Date: Thu, 7 Dec 2017 13:53:27 +0100 Subject: [PATCH 041/272] Cleanup --- dnsapi/dns_unoeuro.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 4774187d..f48b8b9d 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -5,7 +5,7 @@ # #Uno_User="UExxxxxx" -Uno_Api="https://api.unoeuro.com/1/$Uno_User/$Uno_Key" +Uno_Api="https://api.unoeuro.com/1" ######## Public functions ##################### @@ -25,7 +25,7 @@ dns_unoeuro_add() { fi if ! _contains "$Uno_User" "UE"; then - _err "It seems that the Uno_User=$Uno_User is not a valid email address." + _err "It seems that the Uno_User=$Uno_User is not a valid username." _err "Please check and retry." return 1 fi @@ -94,6 +94,12 @@ dns_unoeuro_rm() { 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" @@ -174,15 +180,13 @@ _uno_rest() { data="$3" _debug "$ep" - #export _H1="X-Auth-Email: $Uno_User" - #export _H2="X-Auth-Key: $Uno_Key" export _H1="Content-Type: application/json" if [ "$m" != "GET" ]; then _debug data "$data" - response="$(_post "$data" "$Uno_Api/$ep" "" "$m")" + response="$(_post "$data" "$Uno_Api/$Uno_User/$Uno_Key/$ep" "" "$m")" else - response="$(_get "$Uno_Api/$ep")" + response="$(_get "$Uno_Api/$Uno_User/$Uno_Key/$ep")" fi if [ "$?" != "0" ]; then From b91c0a0616f11f3475d21269883cf3107201581f Mon Sep 17 00:00:00 2001 From: JAA Date: Thu, 7 Dec 2017 13:53:40 +0100 Subject: [PATCH 042/272] Don't use grep -B --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index f48b8b9d..d19e9eb4 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -65,7 +65,7 @@ dns_unoeuro_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -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}" @@ -120,7 +120,7 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Don't need to remove." else - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" if [ -z "$record_id" ]; then From 9a1f769828c426b26589110b70f324bde4e6c7e2 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 14:18:54 +0100 Subject: [PATCH 043/272] Avoid usage of `sed -E` `grep -q` and `printf -v` --- dnsapi/dns_inwx.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 78e101e9..2a2895f1 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -37,7 +37,7 @@ dns_inwx_add() { _debug _domain "$_domain" _debug "Getting txt records" - printf -v xml_content ' + xml_content=$(printf ' nameserver.info @@ -66,7 +66,7 @@ dns_inwx_add() { - ' "$_domain" "$_sub_domain" + ' "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then @@ -74,11 +74,11 @@ dns_inwx_add() { return 1 fi - if ! printf "%s" "$response" | grep "count" -q; then + if ! printf "%s" "$response" | grep "count" >/dev/null; then _info "Adding record" _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" else - _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') _info "Updating record" _inwx_update_record "$_record_id" "$txtvalue" fi @@ -115,7 +115,7 @@ dns_inwx_rm() { _debug "Getting txt records" - printf -v xml_content ' + xml_content=$(printf ' nameserver.info @@ -144,7 +144,7 @@ dns_inwx_rm() { - ' "$_domain" "$_sub_domain" + ' "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then @@ -152,10 +152,10 @@ dns_inwx_rm() { return 1 fi - if ! printf "%s" "$response" | grep "count" -q; then + if ! printf "%s" "$response" | grep "count" >/dev/null; then _info "Do not need to delete record" else - _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') _info "Deleting record" _inwx_delete_record "$_record_id" fi @@ -166,7 +166,7 @@ dns_inwx_rm() { _inwx_login() { - printf -v xml_content ' + xml_content=$(printf ' account.login @@ -189,7 +189,7 @@ _inwx_login() { - ' $INWX_User $INWX_Password + ' $INWX_User $INWX_Password) response="$(_post "$xml_content" "$INWX_Api" "" "POST")" @@ -207,7 +207,7 @@ _get_root() { _H1=$(_inwx_login) export _H1 - printf -v xml_content ' + xml_content=' nameserver.list ' @@ -235,7 +235,7 @@ _get_root() { _inwx_delete_record() { record_id=$1 - printf -v xml_content ' + xml_content=$(printf ' nameserver.deleteRecord @@ -252,7 +252,7 @@ _inwx_delete_record() { - ' "$record_id" + ' "$record_id") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" @@ -267,7 +267,7 @@ _inwx_delete_record() { _inwx_update_record() { record_id=$1 txtval=$2 - printf -v xml_content ' + xml_content=$(printf ' nameserver.updateRecord @@ -290,7 +290,7 @@ _inwx_update_record() { - ' "$txtval" "$record_id" + ' "$txtval" "$record_id") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" @@ -308,7 +308,7 @@ _inwx_add_record() { sub_domain=$2 txtval=$3 - printf -v xml_content ' + xml_content=$(printf ' nameserver.createRecord @@ -343,7 +343,7 @@ _inwx_add_record() { - ' "$domain" "$txtval" "$sub_domain" + ' "$domain" "$txtval" "$sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" From 5911594906f07aa2db4b7d3256388e7514f28b92 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 14:21:37 +0100 Subject: [PATCH 044/272] Fix egrep invocation --- dnsapi/dns_inwx.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 2a2895f1..ae20074b 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -78,7 +78,7 @@ dns_inwx_add() { _info "Adding record" _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" else - _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+') _info "Updating record" _inwx_update_record "$_record_id" "$txtvalue" fi @@ -155,7 +155,7 @@ dns_inwx_rm() { if ! printf "%s" "$response" | grep "count" >/dev/null; then _info "Do not need to delete record" else - _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+') _info "Deleting record" _inwx_delete_record "$_record_id" fi From f87890cb4b534feecd23dd2e7042e8601f15a3c3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 7 Dec 2017 21:32:17 +0800 Subject: [PATCH 045/272] v2.7.6 fix ECC 384 signature https://github.com/Neilpang/acme.sh/issues/1130 https://github.com/Neilpang/acme.sh/issues/990 --- acme.sh | 149 ++++++++++++++++++++++---------------------------------- 1 file changed, 57 insertions(+), 92 deletions(-) diff --git a/acme.sh b/acme.sh index 98f4067a..0755f78a 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.5 +VER=2.7.6 PROJECT_NAME="acme.sh" @@ -15,7 +15,7 @@ _SUB_FOLDERS="dnsapi deploy" _OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" -DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf" + DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -900,17 +900,11 @@ _sign() { fi _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile " - if [ "$alg" = "sha256" ]; then - _sign_openssl="$_sign_openssl -$alg" - else - _err "$alg is not supported yet" - return 1 - fi if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then - $_sign_openssl | _base64 + $_sign_openssl -$alg | _base64 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then - if ! _signedECText="$($_sign_openssl | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then + if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then _err "Sign failed: $_sign_openssl" _err "Key file: $keyfile" _err "Key content:$(wc -l <"$keyfile") lines" @@ -1433,7 +1427,11 @@ _calcjwk() { _debug "EC key" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" _debug3 crv "$crv" - + __ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2) + if [ "$__ECC_KEY_LEN" = "521" ]; then + __ECC_KEY_LEN=512 + fi + _debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN" if [ -z "$crv" ]; then _debug "Let's try ASN1 OID" crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")" @@ -1441,12 +1439,15 @@ _calcjwk() { case "${crv_oid}" in "prime256v1") crv="P-256" + __ECC_KEY_LEN=256 ;; "secp384r1") crv="P-384" + __ECC_KEY_LEN=384 ;; "secp521r1") crv="P-521" + __ECC_KEY_LEN=512 ;; *) _err "ECC oid : $crv_oid" @@ -1488,9 +1489,9 @@ _calcjwk() { jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}' _debug3 jwk "$jwk" - JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}' + JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' else _err "Only RSA or EC key is supported." return 1 @@ -2160,17 +2161,6 @@ _initAPI() { _api_server="${1:-$ACME_DIRECTORY}" _debug "_init api for server: $_api_server" - if [ "$_api_server" = "$DEFAULT_CA" ]; then - #just for performance, hardcode the default entry points - export ACME_KEY_CHANGE="https://acme-v01.api.letsencrypt.org/acme/key-change" - export ACME_NEW_AUTHZ="https://acme-v01.api.letsencrypt.org/acme/new-authz" - export ACME_NEW_ORDER="https://acme-v01.api.letsencrypt.org/acme/new-cert" - export ACME_NEW_ORDER_RES="new-cert" - export ACME_NEW_ACCOUNT="https://acme-v01.api.letsencrypt.org/acme/new-reg" - export ACME_NEW_ACCOUNT_RES="new-reg" - export ACME_REVOKE_CERT="https://acme-v01.api.letsencrypt.org/acme/revoke-cert" - fi - if [ -z "$ACME_NEW_ACCOUNT" ]; then response=$(_get "$_api_server") if [ "$?" != "0" ]; then @@ -2209,14 +2199,18 @@ _initAPI() { ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) export ACME_NEW_NONCE + + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) + export ACME_AGREEMENT - fi + _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" + _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ" + _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER" + _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" + _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" + _debug "ACME_AGREEMENT" "$ACME_AGREEMENT" - _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" - _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ" - _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER" - _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" - _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" + fi } #[domain] [keylength or isEcc flag] @@ -3058,7 +3052,7 @@ __calc_account_thumbprint() { _regAccount() { _initpath _reg_length="$1" - + _debug3 _regAccount "$_regAccount" mkdir -p "$CA_DIR" if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" @@ -3081,75 +3075,46 @@ _regAccount() { return 1 fi _initAPI - _updateTos="" _reg_res="$ACME_NEW_ACCOUNT_RES" - while true; do - _debug AGREEMENT "$AGREEMENT" + regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + fi - regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}' + if [ -z "$_updateTos" ]; then + _info "Registering account" - if [ "$ACCOUNT_EMAIL" ]; then - regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' - fi + if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then + _err "Register account Error: $response" + return 1 + fi - if [ -z "$_updateTos" ]; then - _info "Registering account" + if [ "$code" = "" ] || [ "$code" = '201' ]; then + echo "$response" >"$ACCOUNT_JSON_PATH" + _info "Registered" + elif [ "$code" = '409' ]; then + _info "Already registered" + else + _err "Register account Error: $response" + return 1 + fi - if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then - _err "Register account Error: $response" - return 1 - fi + _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _debug "_accUri" "$_accUri" + _savecaconf "ACCOUNT_URL" "$_accUri" - if [ "$code" = "" ] || [ "$code" = '201' ]; then - echo "$response" >"$ACCOUNT_JSON_PATH" - _info "Registered" - elif [ "$code" = '409' ]; then - _info "Already registered" - else - _err "Register account Error: $response" - return 1 - fi + echo "$response" >"$ACCOUNT_JSON_PATH" + CA_KEY_HASH="$(__calcAccountKeyHash)" + _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" + _savecaconf CA_KEY_HASH "$CA_KEY_HASH" - _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" - _debug "_accUri" "$_accUri" - _savecaconf "ACCOUNT_URL" "$_accUri" - _tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')" - _debug "_tos" "$_tos" - if [ -z "$_tos" ]; then - _debug "Use default tos: $DEFAULT_AGREEMENT" - _tos="$DEFAULT_AGREEMENT" - fi - if [ "$_tos" != "$AGREEMENT" ]; then - _updateTos=1 - AGREEMENT="$_tos" - _reg_res="reg" - continue - fi + if [ "$code" = '403' ]; then + _err "It seems that the account key is already deactivated, please use a new account key." + return 1 + fi - else - _debug "Update tos: $_tos" - if ! _send_signed_request "$_accUri" "$regjson"; then - _err "Update tos error." - return 1 - fi - if [ "$code" = '202' ]; then - _info "Update account tos info success." - echo "$response" >"$ACCOUNT_JSON_PATH" - CA_KEY_HASH="$(__calcAccountKeyHash)" - _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" - _savecaconf CA_KEY_HASH "$CA_KEY_HASH" - elif [ "$code" = '403' ]; then - _err "It seems that the account key is already deactivated, please use a new account key." - return 1 - else - _err "Update account error." - return 1 - fi - fi - ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" - _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" - return 0 - done + ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" + _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" } From db3043553cbfb9ca05f5a6807590603ee381a80a Mon Sep 17 00:00:00 2001 From: JAA Date: Thu, 7 Dec 2017 14:52:09 +0100 Subject: [PATCH 046/272] Also don't use sed --- dnsapi/dns_unoeuro.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index d19e9eb4..6b582ddd 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -53,6 +53,7 @@ dns_unoeuro_add() { 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" @@ -65,7 +66,9 @@ dns_unoeuro_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") + record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) + record_line_number=$(($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}" @@ -76,7 +79,6 @@ dns_unoeuro_add() { _err "Update error" return 1 fi - } #fulldomain txtvalue @@ -120,7 +122,9 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Don't need to remove." else - record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") + record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) + record_line_number=$(($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 From 1f635b90e76a5fb61a92f038a20a78b5381b1ea2 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 08:26:52 +0100 Subject: [PATCH 047/272] Try to fix build --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 6b582ddd..8a1928de 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -67,7 +67,7 @@ dns_unoeuro_add() { else _info "Updating record" record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number-1)) + record_line_number=$(($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" @@ -123,7 +123,7 @@ dns_unoeuro_rm() { _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number-1)) + record_line_number=$(($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" From 3f1e6c128ff0d1af3fc1fc896f115821b55af885 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 08:31:24 +0100 Subject: [PATCH 048/272] Try again --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 8a1928de..be0fadbe 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -67,7 +67,7 @@ dns_unoeuro_add() { else _info "Updating record" record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number - 1)) + record_line_number=$((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" @@ -123,7 +123,7 @@ dns_unoeuro_rm() { _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number - 1)) + record_line_number=$((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" From ca7ebd933324db4c9ea4a383321413c6deb399f3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 8 Dec 2017 19:49:18 +0800 Subject: [PATCH 049/272] fix typo --- acme.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0755f78a..2affeb93 100755 --- a/acme.sh +++ b/acme.sh @@ -3081,8 +3081,7 @@ _regAccount() { regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' fi - if [ -z "$_updateTos" ]; then - _info "Registering account" + _info "Registering account" if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then _err "Register account Error: $response" From 4249e13eb4089deb518d0e54e10b8bd24d2f5640 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 8 Dec 2017 19:54:25 +0800 Subject: [PATCH 050/272] fix format --- acme.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 2affeb93..88273cf5 100755 --- a/acme.sh +++ b/acme.sh @@ -16,7 +16,6 @@ _SUB_FOLDERS="dnsapi deploy" _OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" - DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -2199,7 +2198,7 @@ _initAPI() { ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) export ACME_NEW_NONCE - + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) export ACME_AGREEMENT From dbc3ad130461a4c9dde8b2ce778ae1881960508f Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 12:59:02 +0100 Subject: [PATCH 051/272] use _math --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index be0fadbe..1479d54d 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -67,7 +67,7 @@ dns_unoeuro_add() { else _info "Updating record" record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$((record_line_number - 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" @@ -123,7 +123,7 @@ dns_unoeuro_rm() { _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$((record_line_number - 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" From f763e1edd7c677e9d313288b8426223f86ca9e49 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 13:20:25 +0100 Subject: [PATCH 052/272] fix contains usage --- dnsapi/dns_unoeuro.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 1479d54d..f75de39e 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -114,12 +114,12 @@ dns_unoeuro_rm() { _debug "Getting txt records" _uno_rest GET "my/products/$h/dns/records" - if ! _contains "$response" "\"status\": 200" >/dev/null; then + if ! _contains "$response" "\"status\": 200"; then _err "Error" return 1 fi - if ! _contains "$response" "$_sub_domain" >/dev/null; then + 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) @@ -163,7 +163,7 @@ _get_root() { return 1 fi - if _contains "$response" "\"status\": 200" >/dev/null; then + if _contains "$response" "\"status\": 200"; then _domain_id=$h if [ "$_domain_id" ]; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) From f9b8d7a9d8316ad0e6b663572f552bb32a6236a4 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 13:22:17 +0100 Subject: [PATCH 053/272] renamed uno_user and uno_key --- dnsapi/dns_unoeuro.sh | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index f75de39e..a3803a21 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -1,9 +1,9 @@ #!/usr/bin/env sh # -#Uno_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" # -#Uno_User="UExxxxxx" +#UNO_User="UExxxxxx" Uno_Api="https://api.unoeuro.com/1" @@ -14,25 +14,25 @@ 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="" + 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." + 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" + _saveaccountconf_mutable UNO_Key "$UNO_Key" + _saveaccountconf_mutable UNO_User "$UNO_User" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -86,18 +86,18 @@ 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="" + 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." + 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 @@ -188,9 +188,9 @@ _uno_rest() { if [ "$m" != "GET" ]; then _debug data "$data" - response="$(_post "$data" "$Uno_Api/$Uno_User/$Uno_Key/$ep" "" "$m")" + response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")" else - response="$(_get "$Uno_Api/$Uno_User/$Uno_Key/$ep")" + response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")" fi if [ "$?" != "0" ]; then From fb6e0658cf23a79cf4c16a3898fd57d281be0a32 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 14:09:04 +0100 Subject: [PATCH 054/272] update README --- dnsapi/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 5fdcd849..8a486aee 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -607,8 +607,8 @@ Please report any issues to https://github.com/angel333/acme.sh or to Date: Sat, 9 Dec 2017 14:13:05 +0100 Subject: [PATCH 055/272] Fix shebang --- dnsapi/dns_inwx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index ae20074b..74440bd7 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -1,4 +1,4 @@ -#!/usr/local/bin/sh +#!/usr/bin/env sh # #INWX_User="username" From f7c346de0928fcc57eb0c8f7561fada69118022d Mon Sep 17 00:00:00 2001 From: speedmann Date: Sat, 9 Dec 2017 14:20:36 +0100 Subject: [PATCH 056/272] Fix order in README and add link to inwx.de and apidocs --- README.md | 2 +- dnsapi/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8420c02c..eb122917 100644 --- a/README.md +++ b/README.md @@ -339,8 +339,8 @@ You don't have to do anything manually! 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) 1. Hurricane Electric DNS service (https://dns.he.net) -1. INWX (https://www.inwx.de/) 1. UnoEuro API (https://www.unoeuro.com/) +1. INWX (https://www.inwx.de/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 11b6b3af..c16e7598 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -620,7 +620,7 @@ The `UNO_Key` and `UNO_User` will be saved in `~/.acme.sh/account.conf` and will ## 33. Use INWX -INWX offers an xmlrpc api with your standard login credentials, set them like so: +[INWX](https://www.inwx.de/) offers an [xmlrpc api](https://www.inwx.de/de/help/apidoc) with your standard login credentials, set them like so: ``` export INWX_User="yourusername" From 8201458332ea5898177118097621dbac842ad64f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 9 Dec 2017 21:50:45 +0800 Subject: [PATCH 057/272] fix https://github.com/Neilpang/acme.sh/issues/1123 --- acme.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 88273cf5..472975a6 100755 --- a/acme.sh +++ b/acme.sh @@ -4308,7 +4308,12 @@ _installcert() { if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then cp "$_real_key" "$_backup_path/key.bak" fi - cat "$CERT_KEY_PATH" >"$_real_key" + if [ -f "$_real_key" ]; then + cat "$CERT_KEY_PATH" >"$_real_key" + else + cat "$CERT_KEY_PATH" >"$_real_key" + chmod 700 "$_real_key" + fi fi if [ "$_real_fullchain" ]; then From ae29929714b8cb8e4b7c612a354b56433f8ac47c Mon Sep 17 00:00:00 2001 From: Jens Hartlep Date: Sat, 9 Dec 2017 19:32:53 +0100 Subject: [PATCH 058/272] added dns api for servercow --- dnsapi/README.md | 16 ++++ dnsapi/dns_servercow.sh | 170 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100755 dnsapi/dns_servercow.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index c16e7598..d357c053 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -635,6 +635,22 @@ acme.sh --issue --dns dns_inwx -d example.com -d www.example.com The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 34. User Servercow API v1 + +Create a new user from the servercow control center. Don't forget to activate **DNS API** for this user. + +``` +export SERVERCOW_API_Username=username +export SERVERCOW_API_Password=password +``` + +Now you cann issue a cert: + +``` +acme.sh --issue --dns dns_servercow -d example.com -d www.example.com +``` +Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh new file mode 100755 index 00000000..e7049598 --- /dev/null +++ b/dnsapi/dns_servercow.sh @@ -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 +} From b140e2553bdfce40b99fb3ecd246ef3e64d33d42 Mon Sep 17 00:00:00 2001 From: Jens Hartlep Date: Sat, 9 Dec 2017 19:33:48 +0100 Subject: [PATCH 059/272] added Servercow to supported list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb122917..5574b67a 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,7 @@ You don't have to do anything manually! 1. Hurricane Electric DNS service (https://dns.he.net) 1. UnoEuro API (https://www.unoeuro.com/) 1. INWX (https://www.inwx.de/) - +1. Servercow (https://servercow.de) And: From 488745f3783e767adf5421b20ff96fc82aae9a37 Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:05:05 +0100 Subject: [PATCH 060/272] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5574b67a..0d942757 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,7 @@ You don't have to do anything manually! 1. INWX (https://www.inwx.de/) 1. Servercow (https://servercow.de) + And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api From 1c9b19833cf0ebd0087f171f9f3f5cf24fc96ecc Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:14:46 +0100 Subject: [PATCH 061/272] Update dns_servercow.sh replaced tab with space --- dnsapi/dns_servercow.sh | 260 ++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index e7049598..3ffc393e 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -19,88 +19,88 @@ 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 + 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 + 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 } @@ -111,60 +111,60 @@ dns_servercow_rm() { # _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 + 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 + 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 } From 8101aceab5dad0ac83c8446380d814d77f44fecf Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:18:05 +0100 Subject: [PATCH 062/272] Update dns_servercow.sh fixed remaining issues from shellcheck --- dnsapi/dns_servercow.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index 3ffc393e..872ae58c 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -50,7 +50,7 @@ dns_servercow_add() { _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 + if printf -- "%s" "$response" | grep "ok" >/dev/null; then _info "Added, OK" return 0 else @@ -92,8 +92,8 @@ dns_servercow_rm() { _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 + 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 @@ -140,7 +140,7 @@ _get_root() { p=$i i=$(_math "$i" + 1) - done; + done return 1 } From a95ccc7e4cecd7fa92144dc8af7bca5ffccb107a Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:22:09 +0100 Subject: [PATCH 063/272] Update dns_servercow.sh ... didn't see this line in spellcheck ... :S --- dnsapi/dns_servercow.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index 872ae58c..be4e59da 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -128,7 +128,7 @@ _get_root() { return 1 fi - if ! _contains "$response" '"error":"no such domain in user context"' > /dev/null; then + 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 From e75b56073b3e412d0b85c94527a632a6de5913f1 Mon Sep 17 00:00:00 2001 From: MaomiHz Date: Mon, 11 Dec 2017 16:03:02 -0600 Subject: [PATCH 064/272] Fix digitalocean api not remove record --- dnsapi/dns_dgon.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh index 7e1f1fec..1dc1a858 100755 --- a/dnsapi/dns_dgon.sh +++ b/dnsapi/dns_dgon.sh @@ -92,11 +92,11 @@ dns_dgon_rm() { domain_list="$(_get "$GURL")" ## 2) find record ## check for what we are looing for: "type":"A","name":"$_sub_domain" - record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*\d+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" + record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[[:digit:]]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" ## 3) check record and get next page if [ -z "$record" ]; then ## find the next page if we dont have a match - nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=\d+")" + nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[[:digit:]]+")" if [ -z "$nextpage" ]; then _err "no record and no nextpage in digital ocean DNS removal" return 1 @@ -108,7 +108,7 @@ dns_dgon_rm() { done ## we found the record - rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*\d+" | _egrep_o "\d+")" + rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[[:digit:]]+" | _egrep_o "[[:digit:]]+")" _debug rec_id "$rec_id" ## delete the record From 9c4f7aa6889a2883028673e7276b8c2b15dfaf5b Mon Sep 17 00:00:00 2001 From: MaomiHz Date: Mon, 11 Dec 2017 16:20:27 -0600 Subject: [PATCH 065/272] check for env var exist in DigitalOcean API --- dnsapi/dns_dgon.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh index 1dc1a858..57613349 100755 --- a/dnsapi/dns_dgon.sh +++ b/dnsapi/dns_dgon.sh @@ -20,12 +20,22 @@ dns_dgon_add() { fulldomain="$(echo "$1" | _lower_case)" txtvalue=$2 + + DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}" + # Check if API Key Exist + if [ -z "$DO_API_KEY" ]; then + DO_API_KEY="" + _err "You did not specify DigitalOcean API key." + _err "Please export DO_API_KEY and try again." + return 1 + fi + _info "Using digitalocean dns validation - add record" _debug fulldomain "$fulldomain" _debug txtvalue "$txtvalue" ## save the env vars (key and domain split location) for later automated use - _saveaccountconf DO_API_KEY "$DO_API_KEY" + _saveaccountconf_mutable DO_API_KEY "$DO_API_KEY" ## split the domain for DO API if ! _get_base_domain "$fulldomain"; then @@ -39,7 +49,7 @@ dns_dgon_add() { export _H1="Content-Type: application/json" export _H2="Authorization: Bearer $DO_API_KEY" PURL='https://api.digitalocean.com/v2/domains/'$_domain'/records' - PBODY='{"type":"TXT","name":"'$_sub_domain'","data":"'$txtvalue'"}' + PBODY='{"type":"TXT","name":"'$_sub_domain'","data":"'$txtvalue'","ttl":120}' _debug PURL "$PURL" _debug PBODY "$PBODY" @@ -65,6 +75,16 @@ dns_dgon_add() { dns_dgon_rm() { fulldomain="$(echo "$1" | _lower_case)" txtvalue=$2 + + DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}" + # Check if API Key Exist + if [ -z "$DO_API_KEY" ]; then + DO_API_KEY="" + _err "You did not specify DigitalOcean API key." + _err "Please export DO_API_KEY and try again." + return 1 + fi + _info "Using digitalocean dns validation - remove record" _debug fulldomain "$fulldomain" _debug txtvalue "$txtvalue" From a582e7c2fb7c4ef85c324d69377f3e9644406eba Mon Sep 17 00:00:00 2001 From: sjau Date: Tue, 2 Jan 2018 15:05:26 +0100 Subject: [PATCH 066/272] dns_ispconfig.sh: remove unnecessary permission in api user --- dnsapi/dns_ispconfig.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_ispconfig.sh b/dnsapi/dns_ispconfig.sh index 6d1f34c5..1e500ad6 100755 --- a/dnsapi/dns_ispconfig.sh +++ b/dnsapi/dns_ispconfig.sh @@ -2,7 +2,6 @@ # ISPConfig 3.1 API # User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to: -# - DNS zone Functions # - DNS txt Functions # Report bugs to https://github.com/sjau/acme.sh From 8ea800205c2e5496b63e3244dc4849d629acc1ad Mon Sep 17 00:00:00 2001 From: hiska Date: Thu, 4 Jan 2018 19:01:57 +0900 Subject: [PATCH 067/272] support both debian and redhat --- deploy/strongswan.sh | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/deploy/strongswan.sh b/deploy/strongswan.sh index 2de18f88..f991d690 100644 --- a/deploy/strongswan.sh +++ b/deploy/strongswan.sh @@ -16,17 +16,38 @@ strongswan_deploy() { _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 + 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" >"/etc/ipsec.d/private/$(basename "$_ckey")" - cat "$_ccert" >"/etc/ipsec.d/certs/$(basename "$_ccert")" - cat "$_cca" >"/etc/ipsec.d/cacerts/$(basename "$_cca")" - cat "$_cfullchain" >"/etc/ipsec.d/cacerts/$(basename "$_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 + $_ipsec reload } From 6541492a5598eb4d27aa9c3cd06f8ba7317da41c Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 12:45:24 +0800 Subject: [PATCH 068/272] first version to support ACME v2 --- acme.sh | 308 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 239 insertions(+), 69 deletions(-) diff --git a/acme.sh b/acme.sh index 472975a6..604bff04 100755 --- a/acme.sh +++ b/acme.sh @@ -13,8 +13,15 @@ _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" -_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" -DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" + +LETSENCRYPT_CA_V2="https://acme-v02.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/directory" + +DEFAULT_CA=$LETSENCRYPT_CA_V1 +DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 + DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -24,13 +31,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048 DEFAULT_OPENSSL_BIN="openssl" -STAGE_CA="https://acme-staging.api.letsencrypt.org/directory" +_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org" VTYPE_HTTP="http-01" VTYPE_DNS="dns-01" VTYPE_TLS="tls-sni-01" -#VTYPE_TLS2="tls-sni-02" +VTYPE_TLS2="tls-sni-02" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -1044,13 +1051,14 @@ _createcsr() { if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" + printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf" else domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" else - alt="DNS:$domainlist" + alt="DNS:$domain,DNS:$domainlist" fi #multi _info "Multi domain" "$alt" @@ -1421,7 +1429,7 @@ _calcjwk() { JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "RS256"' elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then _debug "EC key" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" @@ -1490,7 +1498,7 @@ _calcjwk() { JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"' else _err "Only RSA or EC key is supported." return 1 @@ -1580,7 +1588,7 @@ _inithttp() { # body url [needbase64] [POST|PUT] _post() { body="$1" - url="$2" + _post_url="$2" needbase64="$3" httpmethod="$4" @@ -1588,7 +1596,7 @@ _post() { httpmethod="POST" fi _debug $httpmethod - _debug "url" "$url" + _debug "_post_url" "$_post_url" _debug2 "body" "$body" _inithttp @@ -1600,9 +1608,9 @@ _post() { fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url")" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" fi _ret="$?" if [ "$_ret" != "0" ]; then @@ -1620,15 +1628,15 @@ _post() { _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" fi else if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi fi _ret="$?" @@ -1776,7 +1784,15 @@ _send_signed_request() { nonce="$_CACHED_NONCE" _debug2 nonce "$nonce" - protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2" + if [ "$ACME_VERSION" = "2" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' + fi + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + fi _debug3 protected "$protected" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)" @@ -1791,7 +1807,11 @@ _send_signed_request() { sig="$(printf "%s" "$_sig_t" | _url_replace)" _debug3 sig "$sig" - body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + if [ "$ACME_VERSION" = "2" ]; then + body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + else + body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + fi _debug3 body "$body" response="$(_post "$body" "$url" "$needbase64")" @@ -2170,9 +2190,15 @@ _initAPI() { _debug2 "response" "$response" ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_KEY_CHANGE" ]; then + ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_KEY_CHANGE ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_AUTHZ" ]; then + ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_AUTHZ ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3) @@ -2180,6 +2206,9 @@ _initAPI() { if [ -z "$ACME_NEW_ORDER" ]; then ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER_RES="new-order" + if [ -z "$ACME_NEW_ORDER" ]; then + ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3) + fi fi export ACME_NEW_ORDER export ACME_NEW_ORDER_RES @@ -2189,17 +2218,32 @@ _initAPI() { if [ -z "$ACME_NEW_ACCOUNT" ]; then ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ACCOUNT_RES="new-account" + if [ -z "$ACME_NEW_ACCOUNT" ]; then + ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3) + if [ "$ACME_NEW_ACCOUNT" ]; then + export ACME_VERSION=2 + fi + fi fi export ACME_NEW_ACCOUNT export ACME_NEW_ACCOUNT_RES ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_REVOKE_CERT" ]; then + ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_REVOKE_CERT ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_NONCE" ]; then + ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_NONCE ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_AGREEMENT" ]; then + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_AGREEMENT _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" @@ -2208,6 +2252,8 @@ _initAPI() { _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" _debug "ACME_AGREEMENT" "$ACME_AGREEMENT" + _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE" + _debug "ACME_VERSION" "$ACME_VERSION" fi } @@ -2236,7 +2282,7 @@ _initpath() { if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" else - ACME_DIRECTORY="$STAGE_CA" + ACME_DIRECTORY="$DEFAULT_STAGING_CA" _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY" fi fi @@ -2951,6 +2997,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err + _cleardomainconf "ORDER_FINALIZE" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3052,6 +3099,8 @@ _regAccount() { _initpath _reg_length="$1" _debug3 _regAccount "$_regAccount" + _initAPI + mkdir -p "$CA_DIR" if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" @@ -3073,11 +3122,18 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - _initAPI - _reg_res="$ACME_NEW_ACCOUNT_RES" - regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' - if [ "$ACCOUNT_EMAIL" ]; then - regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + + if [ "$ACME_VERSION" = "2" ]; then + regjson='{"termsOfServiceAgreed": true}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}' + fi + else + _reg_res="$ACME_NEW_ACCOUNT_RES" + regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + fi fi _info "Registering account" @@ -3100,8 +3156,8 @@ _regAccount() { _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _debug "_accUri" "$_accUri" _savecaconf "ACCOUNT_URL" "$_accUri" + export ACCOUNT_URL="$ACCOUNT_URL" - echo "$response" >"$ACCOUNT_JSON_PATH" CA_KEY_HASH="$(__calcAccountKeyHash)" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" _savecaconf CA_KEY_HASH "$CA_KEY_HASH" @@ -3113,7 +3169,6 @@ _regAccount() { ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" - } #Implement deactivate account @@ -3251,7 +3306,11 @@ __trigger_validation() { _debug2 _t_url "$_t_url" _t_key_authz="$2" _debug2 _t_key_authz "$_t_key_authz" - _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + if [ "$ACME_VERSION" = "2" ]; then + _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" + else + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + fi } #webroot, domain domainlist keylength @@ -3393,32 +3452,107 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then + if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + #make new order request + _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" + for d in $(echo "$_alt_domains" | tr ',' ' '); do + #todo: check wildcard ? + if [ "$d" ]; then + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" + fi + done + _debug2 _identifiers "$_identifiers" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Create new order error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug ORDER_FINALIZE "$ORDER_FINALIZE" + if [ -z "$ORDER_FINALIZE" ]; then + _err "ORDER_FINALIZE not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #for dns manual mode + _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + + _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #domain and authz map + _authorizations_map="" + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + _debug2 "_authz_url" "$_authz_url" + if ! response="$(_get "$_authz_url")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + _debug2 _d "$_d" + _authorizations_map="$_d,$response +$_authorizations_map" + done + _debug2 _authorizations_map "$_authorizations_map" + fi + alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ') - _index=1 + _index=0 _currentRoot="" for d in $alldomains; do _info "Getting webroot for domain" "$d" + _index=$(_math $_index + 1) _w="$(echo $_web_roots | cut -d , -f $_index)" _debug _w "$_w" if [ "$_w" ]; then _currentRoot="$_w" fi _debug "_currentRoot" "$_currentRoot" - _index=$(_math $_index + 1) vtype="$VTYPE_HTTP" + #todo, v2 wildcard force to use dns if _startswith "$_currentRoot" "dns"; then vtype="$VTYPE_DNS" fi if [ "$_currentRoot" = "$W_TLS" ]; then - vtype="$VTYPE_TLS" + if [ "$ACME_VERSION" = "2" ]; then + vtype="$VTYPE_TLS2" + else + vtype="$VTYPE_TLS" + fi fi - if ! __get_domain_new_authz "$d"; then - _clearup - _on_issue_err "$_post_hook" - return 1 + if [ "$ACME_VERSION" = "2" ]; then + response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")" + _debug2 "response" "$response" + if [ -z "$response" ]; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! __get_domain_new_authz "$d"; then + _clearup + _on_issue_err "$_post_hook" + return 1 + fi fi if [ -z "$thumbprint" ]; then @@ -3436,14 +3570,18 @@ issue() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + if [ "$ACME_VERSION" = "2" ]; then + uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" + else + uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + fi _debug uri "$uri" keyauthorization="$token.$thumbprint" _debug keyauthorization "$keyauthorization" if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then - _debug "$d is already verified, skip." + _debug "$d is already verified." keyauthorization="$STATE_VERIFIED" _debug keyauthorization "$keyauthorization" fi @@ -3685,12 +3823,16 @@ issue() { return 1 fi - if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then - _err "$d:Challenge error: $response" - _clearupwebbroot "$_currentRoot" "$removelevel" "$token" - _clearup - _on_issue_err "$_post_hook" "$vlist" - return 1 + if [ "$code" ] && [ "$code" != '202' ]; then + if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then + _debug "trigger validation code: $code" + else + _err "$d:Challenge error: $response" + _clearupwebbroot "$_currentRoot" "$removelevel" "$token" + _clearup + _on_issue_err "$_post_hook" "$vlist" + return 1 + fi fi waittimes=0 @@ -3773,18 +3915,32 @@ issue() { _info "Verify finished, start to sign." der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" - if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then - _err "Sign failed." - _on_issue_err "$_post_hook" - return 1 - fi - - _rcert="$response" - Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" - _debug "Le_LinkCert" "$Le_LinkCert" - _savedomainconf "Le_LinkCert" "$Le_LinkCert" + if [ "$ACME_VERSION" = "2" ]; then + if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + if [ "$code" != "200" ]; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if [ "$Le_LinkCert" ]; then + if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + _rcert="$response" + Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" echo "$BEGIN_CERT" >"$CERT_PATH" #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then @@ -3798,6 +3954,12 @@ issue() { fi echo "$END_CERT" >>"$CERT_PATH" + fi + + _debug "Le_LinkCert" "$Le_LinkCert" + _savedomainconf "Le_LinkCert" "$Le_LinkCert" + + if [ "$Le_LinkCert" ]; then _info "$(__green "Cert success.")" cat "$CERT_PATH" @@ -3825,29 +3987,37 @@ issue() { _cleardomainconf "Le_Vlist" Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - if ! _contains "$Le_LinkIssuer" ":"; then - _info "$(__red "Relative issuer link found.")" - Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" - fi - _debug Le_LinkIssuer "$Le_LinkIssuer" - _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" if [ "$Le_LinkIssuer" ]; then + if ! _contains "$Le_LinkIssuer" ":"; then + _info "$(__red "Relative issuer link found.")" + Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" + fi + _debug Le_LinkIssuer "$Le_LinkIssuer" + _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" + _link_issuer_retry=0 _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then - echo "$BEGIN_CERT" >"$CA_CERT_PATH" - _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" - echo "$END_CERT" >>"$CA_CERT_PATH" + + if [ "$ACME_VERSION" = "2" ]; then + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then + break + fi + else + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then + echo "$BEGIN_CERT" >"$CA_CERT_PATH" + _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" + echo "$END_CERT" >>"$CA_CERT_PATH" - _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" - cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" - _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" + _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" + cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" + _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" - rm -f "$CA_CERT_PATH.der" - break + rm -f "$CA_CERT_PATH.der" + break + fi fi _link_issuer_retry=$(_math $_link_issuer_retry + 1) _sleep "$_link_issuer_retry" @@ -3957,7 +4127,7 @@ renew() { _savedomainconf Le_API "$Le_API" fi if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then - export Le_API="$STAGE_CA" + export Le_API="$DEFAULT_STAGING_CA" _savedomainconf Le_API "$Le_API" fi export ACME_DIRECTORY="$Le_API" From 136aebc009cf9e32a9a16dead43378a1ba9effe1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 13:57:35 +0800 Subject: [PATCH 069/272] fix format --- acme.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 604bff04..890cdd82 100755 --- a/acme.sh +++ b/acme.sh @@ -22,7 +22,6 @@ LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/director DEFAULT_CA=$LETSENCRYPT_CA_V1 DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 - DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -3122,7 +3121,7 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - + if [ "$ACME_VERSION" = "2" ]; then regjson='{"termsOfServiceAgreed": true}' if [ "$ACCOUNT_EMAIL" ]; then @@ -3469,7 +3468,7 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug ORDER_FINALIZE "$ORDER_FINALIZE" if [ -z "$ORDER_FINALIZE" ]; then _err "ORDER_FINALIZE not found." @@ -3481,7 +3480,7 @@ issue() { #for dns manual mode _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" - _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" if [ -z "$_authorizations_seg" ]; then _err "_authorizations_seg not found." @@ -3492,7 +3491,7 @@ issue() { #domain and authz map _authorizations_map="" - for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do _debug2 "_authz_url" "$_authz_url" if ! response="$(_get "$_authz_url")"; then _err "get to authz error." @@ -3926,9 +3925,9 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi - Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + if ! _get "$Le_LinkCert" >"$CERT_PATH"; then _err "Sign failed, code is not 200." _on_issue_err "$_post_hook" return 1 @@ -4000,7 +3999,6 @@ $_authorizations_map" _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if [ "$ACME_VERSION" = "2" ]; then if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then break From 6ae3911972d6c67e4170cc90e941e733e21429fd Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 17:39:15 +0800 Subject: [PATCH 070/272] support ACME v2 wildcard cert --- acme.sh | 38 +++++++++++++++++++++++++++++------- dnsapi/dns_cf.sh | 51 +++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/acme.sh b/acme.sh index 890cdd82..1021dcd0 100755 --- a/acme.sh +++ b/acme.sh @@ -997,7 +997,7 @@ _createkey() { _is_idn() { _is_idn_d="$1" _debug2 _is_idn_d "$_is_idn_d" - _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '.,-') + _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-') _debug2 _idn_temp "$_idn_temp" [ "$_idn_temp" ] } @@ -1055,7 +1055,7 @@ _createcsr() { domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" else alt="DNS:$domain,DNS:$domainlist" fi @@ -1663,7 +1663,7 @@ _get() { onlyheader="$2" t="$3" _debug url "$url" - _debug "timeout" "$t" + _debug "timeout=$t" _inithttp @@ -2277,6 +2277,11 @@ _initpath() { CA_HOME="$DEFAULT_CA_HOME" fi + if [ "$ACME_VERSION" = "2" ]; then + DEFAULT_CA="$LETSENCRYPT_CA_V2" + DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2" + fi + if [ -z "$ACME_DIRECTORY" ]; then if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" @@ -2863,7 +2868,11 @@ _clearupdns() { return 1 fi - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" @@ -3503,6 +3512,9 @@ issue() { response="$(echo "$response" | _normalizeJson)" _debug2 response "$response" _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + if _contains "$response" "\"wildcard\" *: *true"; then + _d="*.$_d" + fi _debug2 _d "$_d" _authorizations_map="$_d,$response $_authorizations_map" @@ -3600,7 +3612,7 @@ $_authorizations_map" keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) vtype=$(echo "$ventry" | cut -d "$sep" -f 4) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) - + _debug d "$d" if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then _debug "$d is already verified, skip $vtype." continue @@ -3608,12 +3620,16 @@ $_authorizations_map" if [ "$vtype" = "$VTYPE_DNS" ]; then dnsadded='0' - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" - d_api="$(_findHook "$d" dnsapi "$_currentRoot")" + d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" _debug d_api "$d_api" @@ -5476,8 +5492,16 @@ _process() { fi if [ -z "$_domain" ]; then + if _startswith "$_dvalue" "*."; then + _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." + return 1 + fi _domain="$_dvalue" else + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 57a2e884..abe7700c 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,33 +51,36 @@ dns_cf_add() { return 1 fi - count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" - if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep "$fulldomain" >/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_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - _debug "record_id" "$record_id" - - _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - if [ "$?" = "0" ]; then - _info "Updated, OK" +# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so +# we can not use updating anymore. +# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) +# _debug count "$count" +# if [ "$count" = "0" ]; then + _info "Adding record" + if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then + _info "Added, OK" return 0 + else + _err "Add txt record error." + return 1 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 +# else +# _info "Updating record" +# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) +# _debug "record_id" "$record_id" +# +# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" +# if [ "$?" = "0" ]; then +# _info "Updated, OK" +# return 0 +# fi +# _err "Update error" +# return 1 +# fi } From 4f3b3a273f67b0dcc6a6ab4055133298d8e21c9f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 19:42:29 +0800 Subject: [PATCH 071/272] fix format --- dnsapi/dns_cf.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index abe7700c..68264a42 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,11 +51,11 @@ dns_cf_add() { return 1 fi -# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so -# we can not use updating anymore. -# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) -# _debug count "$count" -# if [ "$count" = "0" ]; then + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then _info "Adding record" if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then @@ -68,19 +68,19 @@ dns_cf_add() { fi _err "Add txt record error." return 1 -# else -# _info "Updating record" -# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) -# _debug "record_id" "$record_id" -# -# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" -# if [ "$?" = "0" ]; then -# _info "Updated, OK" -# return 0 -# fi -# _err "Update error" -# return 1 -# fi + # else + # _info "Updating record" + # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) + # _debug "record_id" "$record_id" + # + # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" + # if [ "$?" = "0" ]; then + # _info "Updated, OK" + # return 0 + # fi + # _err "Update error" + # return 1 + # fi } From 2823306810ab2574bc2ca5312a75e7b3f371bd6e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:33:27 +0800 Subject: [PATCH 072/272] fix ACME v2: deactivate/deactivate-account/revoke --- acme.sh | 103 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/acme.sh b/acme.sh index 1021dcd0..5a5f0337 100755 --- a/acme.sh +++ b/acme.sh @@ -1784,7 +1784,7 @@ _send_signed_request() { _debug2 nonce "$nonce" if [ "$ACME_VERSION" = "2" ]; then - if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' else protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "ORDER_FINALIZE" + _cleardomainconf "Le_OrderFinalize" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3212,7 +3212,12 @@ deactivateaccount() { fi _initAPI - if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}" + fi + if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate account success for $_accUri." _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,') elif [ "$code" = "403" ]; then @@ -3334,6 +3339,11 @@ issue() { _web_roots="$1" _main_domain="$2" _alt_domains="$3" + + if _startswith "$_main_domain" "*."; then + _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." + return 1 + fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -3460,7 +3470,7 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then - if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + if [ "$ACME_VERSION" = "2" ]; then #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do @@ -3477,17 +3487,17 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" - _debug ORDER_FINALIZE "$ORDER_FINALIZE" - if [ -z "$ORDER_FINALIZE" ]; then - _err "ORDER_FINALIZE not found." + Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug Le_OrderFinalize "$Le_OrderFinalize" + if [ -z "$Le_OrderFinalize" ]; then + _err "Le_OrderFinalize not found." _clearup _on_issue_err "$_post_hook" return 1 fi #for dns manual mode - _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize" _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" @@ -3931,7 +3941,7 @@ $_authorizations_map" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" if [ "$ACME_VERSION" = "2" ]; then - if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then _err "Sign failed." _on_issue_err "$_post_hook" return 1 @@ -4632,7 +4642,11 @@ revoke() { _initAPI - data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + if [ "$ACME_VERSION" = "2" ]; then + data="{\"certificate\": \"$cert\"}" + else + data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + fi uri="${ACME_REVOKE_CERT}" if [ -f "$CERT_KEY_PATH" ]; then @@ -4703,27 +4717,56 @@ _deactivate() { _d_type="$2" _initpath - if ! __get_domain_new_authz "$_d_domain"; then - _err "Can not get domain new authz token." - return 1 - fi + if [ "$ACME_VERSION" = "2" ]; then + _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Can not get domain new order." + return 1 + fi + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" - _debug "authzUri" "$authzUri" + authzUri="$_authorizations_seg" + _debug2 "authzUri" "$authzUri" + if ! response="$(_get "$authzUri")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - if [ "$code" ] && [ ! "$code" = '201' ]; then - _err "new-authz error: $response" - return 1 + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _URL_NAME="url" + else + if ! __get_domain_new_authz "$_d_domain"; then + _err "Can not get domain new authz token." + return 1 + fi + + authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _debug "authzUri" "$authzUri" + if [ "$code" ] && [ ! "$code" = '201' ]; then + _err "new-authz error: $response" + return 1 + fi + _URL_NAME="uri" fi - entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')" + entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")" if [ -z "$entries" ]; then _info "No valid entries found." if [ -z "$thumbprint" ]; then thumbprint="$(__calc_account_thumbprint)" fi _debug "Trigger validation." - vtype="$VTYPE_HTTP" + vtype="$VTYPE_DNS" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then @@ -4733,7 +4776,7 @@ _deactivate() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" keyauthorization="$token.$thumbprint" @@ -4759,7 +4802,7 @@ _deactivate() { _debug _vtype "$_vtype" _info "Found $_vtype" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then @@ -4769,7 +4812,13 @@ _deactivate() { _info "Deactivate: $_vtype" - if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}" + fi + + if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate: $_vtype success." else _err "Can not deactivate $_vtype." @@ -5492,10 +5541,6 @@ _process() { fi if [ -z "$_domain" ]; then - if _startswith "$_dvalue" "*."; then - _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." - return 1 - fi _domain="$_dvalue" else if _startswith "$_dvalue" "*."; then From 4a139934f696867f81cb135296c409a788a39ffd Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:50:57 +0800 Subject: [PATCH 073/272] fix dns manual mode. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 5a5f0337..4433e814 100755 --- a/acme.sh +++ b/acme.sh @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "Le_OrderFinalize" + if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else From ee6f78805f2eff9c8c4cf969470662fca6bf7874 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 7 Jan 2018 12:12:40 +0800 Subject: [PATCH 074/272] update doc for v2 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0d942757..2845488e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. +- Support ACME v1 and ACME v2 +- Support ACME v2 wildcard certs - Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Bash, dash and sh compatible. - Simplest shell script for Let's Encrypt free certificate client. From c99d4948b7e3525f9d9ff1aed760d21284bbad2a Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 20:43:23 +0800 Subject: [PATCH 075/272] we should not use "updating" to support wildcard --- dnsapi/dns_ovh.sh | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index eaa90bdf..03b75d97 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -137,48 +137,27 @@ dns_ovh_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" - - if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then - _info "Adding record" - if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then - if _contains "$response" "$txtvalue"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 - return 0 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1) - if [ -z "$record_id" ]; then - _err "Can not get record id." - return 1 - fi - _debug "record_id" "$record_id" - - if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then - if _contains "$response" "null"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Updated, sleeping 10 seconds" - sleep 10 - return 0 - fi + _info "Adding record" + if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then + if _contains "$response" "$txtvalue"; then + _ovh_rest POST "domain/zone/$_domain/refresh" + _debug "Refresh:$response" + _info "Added, sleeping 10 seconds" + sleep 10 + return 0 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 } #fulldomain dns_ovh_rm() { fulldomain=$1 + txtvalue=$2 + _debug "Getting txt records" + #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" } From 6d5e7826aebcce830636b8b34eb9b36295d52a44 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 21:36:48 +0800 Subject: [PATCH 076/272] support dns_ovh_rm() --- dnsapi/dns_ovh.sh | 56 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 03b75d97..96c2044b 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,13 +78,8 @@ _ovh_get_api() { esac } -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_ovh_add() { - fulldomain=$1 - txtvalue=$2 +_initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" OVH_AS="" @@ -127,6 +122,19 @@ dns_ovh_add() { return 1 fi _info "Consumer key is ok." + return 0 +} + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ovh_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _initAuth; then + return 1 + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -156,9 +164,39 @@ dns_ovh_add() { dns_ovh_rm() { fulldomain=$1 txtvalue=$2 + + if ! _initAuth; then + 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" _debug "Getting txt records" - #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" + if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then + return 1 + fi + for rid in $(echo "$response" | tr '[,]' ' '); do + _debug rid "$rid" + if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then + return 1 + fi + if _contains "$response" "\"target\":\"$txtvalue\""; then + _debug "Found txt id:$rid" + if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then + return 1 + fi + return 0 + fi + done + + return 1 } #################### Private functions below ################################## @@ -170,7 +208,7 @@ _ovh_authentication() { _H3="" _H4="" - _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' + _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" _debug3 response "$response" @@ -258,7 +296,7 @@ _ovh_rest() { export _H3="X-Ovh-Timestamp: $_ovh_t" export _H4="X-Ovh-Consumer: $OVH_CK" export _H5="Content-Type: application/json;charset=utf-8" - if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then + if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then _debug data "$data" response="$(_post "$data" "$_ovh_url" "" "$m")" else From 2befb5e7849c3dddbdc32633e5d5b11a64ef1795 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:04:03 +0800 Subject: [PATCH 077/272] fix ovh --- dnsapi/dns_ovh.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 96c2044b..60ce1898 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -114,8 +114,7 @@ _initAuth() { _info "Checking authentication" - response="$(_ovh_rest GET "domain")" - if _contains "$response" "INVALID_CREDENTIAL"; then + if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then _err "The consumer key is invalid: $OVH_CK" _err "Please retry to create a new one." _clearaccountconf OVH_CK @@ -150,8 +149,7 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 + _info "Added" return 0 fi fi @@ -303,8 +301,8 @@ _ovh_rest() { response="$(_get "$_ovh_url")" fi - if [ "$?" != "0" ]; then - _err "error $ep" + if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then + _err "error $response" return 1 fi _debug2 response "$response" From a8ae23d0a29d796ce5f465a7572538bb2c232cc9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:47:01 +0800 Subject: [PATCH 078/272] add more sleep for ovh --- dnsapi/dns_ovh.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 60ce1898..de69bd91 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -149,7 +149,8 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added" + _info "Added, sleep 10 seconds." + _sleep 10 return 0 fi fi From 06a2e5fc82e5cf91922d921cb5490e04893afc61 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 23:05:55 +0800 Subject: [PATCH 079/272] fix format --- dnsapi/dns_ovh.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index de69bd91..60094739 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,7 +78,6 @@ _ovh_get_api() { esac } - _initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" @@ -181,7 +180,7 @@ dns_ovh_rm() { return 1 fi - for rid in $(echo "$response" | tr '[,]' ' '); do + for rid in $(echo "$response" | tr '][,' ' '); do _debug rid "$rid" if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then return 1 From eb207322d3ee34ddc2098c246f76fc980129fcc9 Mon Sep 17 00:00:00 2001 From: Meowthink Date: Sun, 14 Jan 2018 14:19:33 +0800 Subject: [PATCH 080/272] Add namesilo.com dns api support --- README.md | 1 + dnsapi/README.md | 15 +++++ dnsapi/dns_namesilo.sh | 137 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100755 dnsapi/dns_namesilo.sh diff --git a/README.md b/README.md index 0d942757..bd39a202 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,7 @@ You don't have to do anything manually! 1. UnoEuro API (https://www.unoeuro.com/) 1. INWX (https://www.inwx.de/) 1. Servercow (https://servercow.de) +1. Namesilo (https://www.namesilo.com) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index d357c053..ed36b97c 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -651,6 +651,21 @@ acme.sh --issue --dns dns_servercow -d example.com -d www.example.com ``` Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 35. Use Namesilo.com API + +You'll need to generate an API key at https://www.namesilo.com/account_api.php +Optionally you may restrict the access to an IP range there. + +``` +export Namesilo_Key="xxxxxxxxxxxxxxxxxxxxxxxx" +``` + +And now you can issue certs with: + +``` +acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com +``` + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh new file mode 100755 index 00000000..dc1a4fda --- /dev/null +++ b/dnsapi/dns_namesilo.sh @@ -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 "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 "300") + if [ "$retcode" ]; then + _record_id=$(printf "%s\n" "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/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 "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 +} From cd2fe698bb627c4fb9a12a775f71672df1d07d6e Mon Sep 17 00:00:00 2001 From: AA Date: Mon, 15 Jan 2018 10:59:50 +0100 Subject: [PATCH 081/272] Create dns_autodns.sh --- dnsapi/dns_autodns.sh | 264 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 dnsapi/dns_autodns.sh diff --git a/dnsapi/dns_autodns.sh b/dnsapi/dns_autodns.sh new file mode 100644 index 00000000..92534489 --- /dev/null +++ b/dnsapi/dns_autodns.sh @@ -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" "1" >/dev/null; then + _zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | 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 " + %s + %s + %s + " "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT" +} + +# Arguments: +# zone +_build_zone_inquire_xml() { + printf " + + %s + + 0205 + + 1 + 1 + + + name + eq + %s + + + " "$(_build_request_auth_xml)" "$1" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_build_zone_update_xml() { + printf " + + %s + + 0202001 + + + %s + 600 + TXT + %s + + + + %s + %s + + + " "$(_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" "success" >/dev/null; then + _info "success" + printf "%s" "$autodns_response" + return 0 + fi + + return 1 +} From a01da2fd92dafbf0a1e67dbe8ff45dce6a5be33b Mon Sep 17 00:00:00 2001 From: AA Date: Mon, 15 Jan 2018 11:00:44 +0100 Subject: [PATCH 082/272] Update README.md --- dnsapi/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index ed36b97c..83ef4ea7 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -666,6 +666,24 @@ And now you can issue certs with: acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com ``` +## 37. Use autoDNS (InternetX) + +[InternetX](https://www.internetx.com/) offers an [xml api](https://help.internetx.com/display/API/AutoDNS+XML-API) with your standard login credentials, set them like so: + +``` +export AUTODNS_USER="yourusername" +export AUTODNS_PASSWORD="password" +export AUTODNS_CONTEXT="context" +``` + +Then you can issue your certificates with: + +``` +acme.sh --issue --dns dns_autodns -d example.com -d www.example.com +``` + +The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From 775aae7082efab15175449d9183114a976c3a4d1 Mon Sep 17 00:00:00 2001 From: AA Date: Mon, 15 Jan 2018 11:15:26 +0100 Subject: [PATCH 083/272] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd39a202..a2c210e8 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,7 @@ You don't have to do anything manually! 1. INWX (https://www.inwx.de/) 1. Servercow (https://servercow.de) 1. Namesilo (https://www.namesilo.com) - +1. InternetX autoDNS API (https://internetx.com) And: From 7e212c4d406e3d8669b23b89b2107ea1c2b6b390 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 19:48:57 +0800 Subject: [PATCH 084/272] typo --- acme.sh | 1 - dnsapi/dns_aws.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 4433e814..6c3f6e05 100755 --- a/acme.sh +++ b/acme.sh @@ -3474,7 +3474,6 @@ issue() { #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do - #todo: check wildcard ? if [ "$d" ]; then _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" fi diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 5a716514..8c1513d1 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -23,7 +23,7 @@ dns_aws_add() { AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" _err "You don't specify aws route53 api key id and and api key secret yet." - _err "Please create you key and try again. see $(__green $AWS_WIKI)" + _err "Please create your key and try again. see $(__green $AWS_WIKI)" return 1 fi From e6cda79ee8411255d191f7781df42522bbf3aedc Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 21:55:40 +0800 Subject: [PATCH 085/272] support only one wildcard domain. fix https://github.com/Neilpang/acme.sh/issues/1188#issuecomment-357684744 --- acme.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 6c3f6e05..74b437cb 100755 --- a/acme.sh +++ b/acme.sh @@ -3340,10 +3340,6 @@ issue() { _main_domain="$2" _alt_domains="$3" - if _startswith "$_main_domain" "*."; then - _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." - return 1 - fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -5539,13 +5535,13 @@ _process() { return 1 fi + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ -z "$_domain" ]; then _domain="$_dvalue" else - if _startswith "$_dvalue" "*."; then - _debug "Wildcard domain" - export ACME_VERSION=2 - fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else From 3164b5ab1307321548aca49c9e97e117d72d9e8f Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 16 Jan 2018 20:55:07 +0800 Subject: [PATCH 086/272] fix https://github.com/Neilpang/acme.sh/issues/1189 --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 74b437cb..59a7d960 100755 --- a/acme.sh +++ b/acme.sh @@ -2259,6 +2259,8 @@ _initAPI() { #[domain] [keylength or isEcc flag] _initpath() { + domain="$1" + _ilength="$2" __initHome @@ -2346,13 +2348,10 @@ _initpath() { ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" fi - if [ -z "$1" ]; then + if [ -z "$domain" ]; then return 0 fi - domain="$1" - _ilength="$2" - if [ -z "$DOMAIN_PATH" ]; then domainhome="$CERT_HOME/$domain" domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX" @@ -4234,8 +4233,6 @@ signcsr() { return 1 fi - _initpath - _csrsubj=$(_readSubjectFromCSR "$_csrfile") if [ "$?" != "0" ]; then _err "Can not read subject from csr: $_csrfile" @@ -4272,6 +4269,9 @@ signcsr() { return 1 fi + if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then + export ACME_VERSION=2 + fi _initpath "$_csrsubj" "$_csrkeylength" mkdir -p "$DOMAIN_PATH" From c1151b0d459c4e9cfac45c8dd2cfc70cedeecfff Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 12:45:24 +0800 Subject: [PATCH 087/272] first version to support ACME v2 --- acme.sh | 308 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 239 insertions(+), 69 deletions(-) diff --git a/acme.sh b/acme.sh index 472975a6..604bff04 100755 --- a/acme.sh +++ b/acme.sh @@ -13,8 +13,15 @@ _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" -_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" -DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" + +LETSENCRYPT_CA_V2="https://acme-v02.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/directory" + +DEFAULT_CA=$LETSENCRYPT_CA_V1 +DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 + DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -24,13 +31,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048 DEFAULT_OPENSSL_BIN="openssl" -STAGE_CA="https://acme-staging.api.letsencrypt.org/directory" +_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org" VTYPE_HTTP="http-01" VTYPE_DNS="dns-01" VTYPE_TLS="tls-sni-01" -#VTYPE_TLS2="tls-sni-02" +VTYPE_TLS2="tls-sni-02" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -1044,13 +1051,14 @@ _createcsr() { if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" + printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf" else domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" else - alt="DNS:$domainlist" + alt="DNS:$domain,DNS:$domainlist" fi #multi _info "Multi domain" "$alt" @@ -1421,7 +1429,7 @@ _calcjwk() { JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "RS256"' elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then _debug "EC key" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" @@ -1490,7 +1498,7 @@ _calcjwk() { JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"' else _err "Only RSA or EC key is supported." return 1 @@ -1580,7 +1588,7 @@ _inithttp() { # body url [needbase64] [POST|PUT] _post() { body="$1" - url="$2" + _post_url="$2" needbase64="$3" httpmethod="$4" @@ -1588,7 +1596,7 @@ _post() { httpmethod="POST" fi _debug $httpmethod - _debug "url" "$url" + _debug "_post_url" "$_post_url" _debug2 "body" "$body" _inithttp @@ -1600,9 +1608,9 @@ _post() { fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url")" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" fi _ret="$?" if [ "$_ret" != "0" ]; then @@ -1620,15 +1628,15 @@ _post() { _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" fi else if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi fi _ret="$?" @@ -1776,7 +1784,15 @@ _send_signed_request() { nonce="$_CACHED_NONCE" _debug2 nonce "$nonce" - protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2" + if [ "$ACME_VERSION" = "2" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' + fi + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + fi _debug3 protected "$protected" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)" @@ -1791,7 +1807,11 @@ _send_signed_request() { sig="$(printf "%s" "$_sig_t" | _url_replace)" _debug3 sig "$sig" - body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + if [ "$ACME_VERSION" = "2" ]; then + body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + else + body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + fi _debug3 body "$body" response="$(_post "$body" "$url" "$needbase64")" @@ -2170,9 +2190,15 @@ _initAPI() { _debug2 "response" "$response" ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_KEY_CHANGE" ]; then + ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_KEY_CHANGE ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_AUTHZ" ]; then + ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_AUTHZ ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3) @@ -2180,6 +2206,9 @@ _initAPI() { if [ -z "$ACME_NEW_ORDER" ]; then ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER_RES="new-order" + if [ -z "$ACME_NEW_ORDER" ]; then + ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3) + fi fi export ACME_NEW_ORDER export ACME_NEW_ORDER_RES @@ -2189,17 +2218,32 @@ _initAPI() { if [ -z "$ACME_NEW_ACCOUNT" ]; then ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ACCOUNT_RES="new-account" + if [ -z "$ACME_NEW_ACCOUNT" ]; then + ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3) + if [ "$ACME_NEW_ACCOUNT" ]; then + export ACME_VERSION=2 + fi + fi fi export ACME_NEW_ACCOUNT export ACME_NEW_ACCOUNT_RES ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_REVOKE_CERT" ]; then + ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_REVOKE_CERT ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_NONCE" ]; then + ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_NONCE ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_AGREEMENT" ]; then + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_AGREEMENT _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" @@ -2208,6 +2252,8 @@ _initAPI() { _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" _debug "ACME_AGREEMENT" "$ACME_AGREEMENT" + _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE" + _debug "ACME_VERSION" "$ACME_VERSION" fi } @@ -2236,7 +2282,7 @@ _initpath() { if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" else - ACME_DIRECTORY="$STAGE_CA" + ACME_DIRECTORY="$DEFAULT_STAGING_CA" _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY" fi fi @@ -2951,6 +2997,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err + _cleardomainconf "ORDER_FINALIZE" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3052,6 +3099,8 @@ _regAccount() { _initpath _reg_length="$1" _debug3 _regAccount "$_regAccount" + _initAPI + mkdir -p "$CA_DIR" if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" @@ -3073,11 +3122,18 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - _initAPI - _reg_res="$ACME_NEW_ACCOUNT_RES" - regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' - if [ "$ACCOUNT_EMAIL" ]; then - regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + + if [ "$ACME_VERSION" = "2" ]; then + regjson='{"termsOfServiceAgreed": true}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}' + fi + else + _reg_res="$ACME_NEW_ACCOUNT_RES" + regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + fi fi _info "Registering account" @@ -3100,8 +3156,8 @@ _regAccount() { _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _debug "_accUri" "$_accUri" _savecaconf "ACCOUNT_URL" "$_accUri" + export ACCOUNT_URL="$ACCOUNT_URL" - echo "$response" >"$ACCOUNT_JSON_PATH" CA_KEY_HASH="$(__calcAccountKeyHash)" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" _savecaconf CA_KEY_HASH "$CA_KEY_HASH" @@ -3113,7 +3169,6 @@ _regAccount() { ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" - } #Implement deactivate account @@ -3251,7 +3306,11 @@ __trigger_validation() { _debug2 _t_url "$_t_url" _t_key_authz="$2" _debug2 _t_key_authz "$_t_key_authz" - _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + if [ "$ACME_VERSION" = "2" ]; then + _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" + else + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + fi } #webroot, domain domainlist keylength @@ -3393,32 +3452,107 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then + if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + #make new order request + _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" + for d in $(echo "$_alt_domains" | tr ',' ' '); do + #todo: check wildcard ? + if [ "$d" ]; then + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" + fi + done + _debug2 _identifiers "$_identifiers" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Create new order error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug ORDER_FINALIZE "$ORDER_FINALIZE" + if [ -z "$ORDER_FINALIZE" ]; then + _err "ORDER_FINALIZE not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #for dns manual mode + _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + + _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #domain and authz map + _authorizations_map="" + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + _debug2 "_authz_url" "$_authz_url" + if ! response="$(_get "$_authz_url")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + _debug2 _d "$_d" + _authorizations_map="$_d,$response +$_authorizations_map" + done + _debug2 _authorizations_map "$_authorizations_map" + fi + alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ') - _index=1 + _index=0 _currentRoot="" for d in $alldomains; do _info "Getting webroot for domain" "$d" + _index=$(_math $_index + 1) _w="$(echo $_web_roots | cut -d , -f $_index)" _debug _w "$_w" if [ "$_w" ]; then _currentRoot="$_w" fi _debug "_currentRoot" "$_currentRoot" - _index=$(_math $_index + 1) vtype="$VTYPE_HTTP" + #todo, v2 wildcard force to use dns if _startswith "$_currentRoot" "dns"; then vtype="$VTYPE_DNS" fi if [ "$_currentRoot" = "$W_TLS" ]; then - vtype="$VTYPE_TLS" + if [ "$ACME_VERSION" = "2" ]; then + vtype="$VTYPE_TLS2" + else + vtype="$VTYPE_TLS" + fi fi - if ! __get_domain_new_authz "$d"; then - _clearup - _on_issue_err "$_post_hook" - return 1 + if [ "$ACME_VERSION" = "2" ]; then + response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")" + _debug2 "response" "$response" + if [ -z "$response" ]; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! __get_domain_new_authz "$d"; then + _clearup + _on_issue_err "$_post_hook" + return 1 + fi fi if [ -z "$thumbprint" ]; then @@ -3436,14 +3570,18 @@ issue() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + if [ "$ACME_VERSION" = "2" ]; then + uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" + else + uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + fi _debug uri "$uri" keyauthorization="$token.$thumbprint" _debug keyauthorization "$keyauthorization" if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then - _debug "$d is already verified, skip." + _debug "$d is already verified." keyauthorization="$STATE_VERIFIED" _debug keyauthorization "$keyauthorization" fi @@ -3685,12 +3823,16 @@ issue() { return 1 fi - if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then - _err "$d:Challenge error: $response" - _clearupwebbroot "$_currentRoot" "$removelevel" "$token" - _clearup - _on_issue_err "$_post_hook" "$vlist" - return 1 + if [ "$code" ] && [ "$code" != '202' ]; then + if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then + _debug "trigger validation code: $code" + else + _err "$d:Challenge error: $response" + _clearupwebbroot "$_currentRoot" "$removelevel" "$token" + _clearup + _on_issue_err "$_post_hook" "$vlist" + return 1 + fi fi waittimes=0 @@ -3773,18 +3915,32 @@ issue() { _info "Verify finished, start to sign." der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" - if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then - _err "Sign failed." - _on_issue_err "$_post_hook" - return 1 - fi - - _rcert="$response" - Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" - _debug "Le_LinkCert" "$Le_LinkCert" - _savedomainconf "Le_LinkCert" "$Le_LinkCert" + if [ "$ACME_VERSION" = "2" ]; then + if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + if [ "$code" != "200" ]; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if [ "$Le_LinkCert" ]; then + if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + _rcert="$response" + Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" echo "$BEGIN_CERT" >"$CERT_PATH" #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then @@ -3798,6 +3954,12 @@ issue() { fi echo "$END_CERT" >>"$CERT_PATH" + fi + + _debug "Le_LinkCert" "$Le_LinkCert" + _savedomainconf "Le_LinkCert" "$Le_LinkCert" + + if [ "$Le_LinkCert" ]; then _info "$(__green "Cert success.")" cat "$CERT_PATH" @@ -3825,29 +3987,37 @@ issue() { _cleardomainconf "Le_Vlist" Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - if ! _contains "$Le_LinkIssuer" ":"; then - _info "$(__red "Relative issuer link found.")" - Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" - fi - _debug Le_LinkIssuer "$Le_LinkIssuer" - _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" if [ "$Le_LinkIssuer" ]; then + if ! _contains "$Le_LinkIssuer" ":"; then + _info "$(__red "Relative issuer link found.")" + Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" + fi + _debug Le_LinkIssuer "$Le_LinkIssuer" + _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" + _link_issuer_retry=0 _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then - echo "$BEGIN_CERT" >"$CA_CERT_PATH" - _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" - echo "$END_CERT" >>"$CA_CERT_PATH" + + if [ "$ACME_VERSION" = "2" ]; then + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then + break + fi + else + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then + echo "$BEGIN_CERT" >"$CA_CERT_PATH" + _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" + echo "$END_CERT" >>"$CA_CERT_PATH" - _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" - cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" - _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" + _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" + cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" + _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" - rm -f "$CA_CERT_PATH.der" - break + rm -f "$CA_CERT_PATH.der" + break + fi fi _link_issuer_retry=$(_math $_link_issuer_retry + 1) _sleep "$_link_issuer_retry" @@ -3957,7 +4127,7 @@ renew() { _savedomainconf Le_API "$Le_API" fi if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then - export Le_API="$STAGE_CA" + export Le_API="$DEFAULT_STAGING_CA" _savedomainconf Le_API "$Le_API" fi export ACME_DIRECTORY="$Le_API" From f8d22c486e243704b58a40b377a1b15dc8366d3e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 13:57:35 +0800 Subject: [PATCH 088/272] fix format --- acme.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 604bff04..890cdd82 100755 --- a/acme.sh +++ b/acme.sh @@ -22,7 +22,6 @@ LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/director DEFAULT_CA=$LETSENCRYPT_CA_V1 DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 - DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -3122,7 +3121,7 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - + if [ "$ACME_VERSION" = "2" ]; then regjson='{"termsOfServiceAgreed": true}' if [ "$ACCOUNT_EMAIL" ]; then @@ -3469,7 +3468,7 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug ORDER_FINALIZE "$ORDER_FINALIZE" if [ -z "$ORDER_FINALIZE" ]; then _err "ORDER_FINALIZE not found." @@ -3481,7 +3480,7 @@ issue() { #for dns manual mode _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" - _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" if [ -z "$_authorizations_seg" ]; then _err "_authorizations_seg not found." @@ -3492,7 +3491,7 @@ issue() { #domain and authz map _authorizations_map="" - for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do _debug2 "_authz_url" "$_authz_url" if ! response="$(_get "$_authz_url")"; then _err "get to authz error." @@ -3926,9 +3925,9 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi - Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + if ! _get "$Le_LinkCert" >"$CERT_PATH"; then _err "Sign failed, code is not 200." _on_issue_err "$_post_hook" return 1 @@ -4000,7 +3999,6 @@ $_authorizations_map" _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if [ "$ACME_VERSION" = "2" ]; then if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then break From 72f54ca6c13c33943aafbacaee03423907f5e738 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 17:39:15 +0800 Subject: [PATCH 089/272] support ACME v2 wildcard cert --- acme.sh | 38 +++++++++++++++++++++++++++++------- dnsapi/dns_cf.sh | 51 +++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/acme.sh b/acme.sh index 890cdd82..1021dcd0 100755 --- a/acme.sh +++ b/acme.sh @@ -997,7 +997,7 @@ _createkey() { _is_idn() { _is_idn_d="$1" _debug2 _is_idn_d "$_is_idn_d" - _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '.,-') + _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-') _debug2 _idn_temp "$_idn_temp" [ "$_idn_temp" ] } @@ -1055,7 +1055,7 @@ _createcsr() { domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" else alt="DNS:$domain,DNS:$domainlist" fi @@ -1663,7 +1663,7 @@ _get() { onlyheader="$2" t="$3" _debug url "$url" - _debug "timeout" "$t" + _debug "timeout=$t" _inithttp @@ -2277,6 +2277,11 @@ _initpath() { CA_HOME="$DEFAULT_CA_HOME" fi + if [ "$ACME_VERSION" = "2" ]; then + DEFAULT_CA="$LETSENCRYPT_CA_V2" + DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2" + fi + if [ -z "$ACME_DIRECTORY" ]; then if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" @@ -2863,7 +2868,11 @@ _clearupdns() { return 1 fi - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" @@ -3503,6 +3512,9 @@ issue() { response="$(echo "$response" | _normalizeJson)" _debug2 response "$response" _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + if _contains "$response" "\"wildcard\" *: *true"; then + _d="*.$_d" + fi _debug2 _d "$_d" _authorizations_map="$_d,$response $_authorizations_map" @@ -3600,7 +3612,7 @@ $_authorizations_map" keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) vtype=$(echo "$ventry" | cut -d "$sep" -f 4) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) - + _debug d "$d" if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then _debug "$d is already verified, skip $vtype." continue @@ -3608,12 +3620,16 @@ $_authorizations_map" if [ "$vtype" = "$VTYPE_DNS" ]; then dnsadded='0' - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" - d_api="$(_findHook "$d" dnsapi "$_currentRoot")" + d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" _debug d_api "$d_api" @@ -5476,8 +5492,16 @@ _process() { fi if [ -z "$_domain" ]; then + if _startswith "$_dvalue" "*."; then + _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." + return 1 + fi _domain="$_dvalue" else + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 57a2e884..abe7700c 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,33 +51,36 @@ dns_cf_add() { return 1 fi - count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" - if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep "$fulldomain" >/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_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - _debug "record_id" "$record_id" - - _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - if [ "$?" = "0" ]; then - _info "Updated, OK" +# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so +# we can not use updating anymore. +# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) +# _debug count "$count" +# if [ "$count" = "0" ]; then + _info "Adding record" + if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then + _info "Added, OK" return 0 + else + _err "Add txt record error." + return 1 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 +# else +# _info "Updating record" +# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) +# _debug "record_id" "$record_id" +# +# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" +# if [ "$?" = "0" ]; then +# _info "Updated, OK" +# return 0 +# fi +# _err "Update error" +# return 1 +# fi } From 506c41cb157bf45fc27d5930458688eb2ff666b4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 19:42:29 +0800 Subject: [PATCH 090/272] fix format --- dnsapi/dns_cf.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index abe7700c..68264a42 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,11 +51,11 @@ dns_cf_add() { return 1 fi -# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so -# we can not use updating anymore. -# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) -# _debug count "$count" -# if [ "$count" = "0" ]; then + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then _info "Adding record" if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then @@ -68,19 +68,19 @@ dns_cf_add() { fi _err "Add txt record error." return 1 -# else -# _info "Updating record" -# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) -# _debug "record_id" "$record_id" -# -# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" -# if [ "$?" = "0" ]; then -# _info "Updated, OK" -# return 0 -# fi -# _err "Update error" -# return 1 -# fi + # else + # _info "Updating record" + # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) + # _debug "record_id" "$record_id" + # + # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" + # if [ "$?" = "0" ]; then + # _info "Updated, OK" + # return 0 + # fi + # _err "Update error" + # return 1 + # fi } From d2cde379ad310767fc84e2f6f92599b9b6d8c458 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:33:27 +0800 Subject: [PATCH 091/272] fix ACME v2: deactivate/deactivate-account/revoke --- acme.sh | 103 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/acme.sh b/acme.sh index 1021dcd0..5a5f0337 100755 --- a/acme.sh +++ b/acme.sh @@ -1784,7 +1784,7 @@ _send_signed_request() { _debug2 nonce "$nonce" if [ "$ACME_VERSION" = "2" ]; then - if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' else protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "ORDER_FINALIZE" + _cleardomainconf "Le_OrderFinalize" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3212,7 +3212,12 @@ deactivateaccount() { fi _initAPI - if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}" + fi + if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate account success for $_accUri." _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,') elif [ "$code" = "403" ]; then @@ -3334,6 +3339,11 @@ issue() { _web_roots="$1" _main_domain="$2" _alt_domains="$3" + + if _startswith "$_main_domain" "*."; then + _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." + return 1 + fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -3460,7 +3470,7 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then - if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + if [ "$ACME_VERSION" = "2" ]; then #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do @@ -3477,17 +3487,17 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" - _debug ORDER_FINALIZE "$ORDER_FINALIZE" - if [ -z "$ORDER_FINALIZE" ]; then - _err "ORDER_FINALIZE not found." + Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug Le_OrderFinalize "$Le_OrderFinalize" + if [ -z "$Le_OrderFinalize" ]; then + _err "Le_OrderFinalize not found." _clearup _on_issue_err "$_post_hook" return 1 fi #for dns manual mode - _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize" _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" @@ -3931,7 +3941,7 @@ $_authorizations_map" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" if [ "$ACME_VERSION" = "2" ]; then - if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then _err "Sign failed." _on_issue_err "$_post_hook" return 1 @@ -4632,7 +4642,11 @@ revoke() { _initAPI - data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + if [ "$ACME_VERSION" = "2" ]; then + data="{\"certificate\": \"$cert\"}" + else + data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + fi uri="${ACME_REVOKE_CERT}" if [ -f "$CERT_KEY_PATH" ]; then @@ -4703,27 +4717,56 @@ _deactivate() { _d_type="$2" _initpath - if ! __get_domain_new_authz "$_d_domain"; then - _err "Can not get domain new authz token." - return 1 - fi + if [ "$ACME_VERSION" = "2" ]; then + _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Can not get domain new order." + return 1 + fi + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" - _debug "authzUri" "$authzUri" + authzUri="$_authorizations_seg" + _debug2 "authzUri" "$authzUri" + if ! response="$(_get "$authzUri")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - if [ "$code" ] && [ ! "$code" = '201' ]; then - _err "new-authz error: $response" - return 1 + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _URL_NAME="url" + else + if ! __get_domain_new_authz "$_d_domain"; then + _err "Can not get domain new authz token." + return 1 + fi + + authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _debug "authzUri" "$authzUri" + if [ "$code" ] && [ ! "$code" = '201' ]; then + _err "new-authz error: $response" + return 1 + fi + _URL_NAME="uri" fi - entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')" + entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")" if [ -z "$entries" ]; then _info "No valid entries found." if [ -z "$thumbprint" ]; then thumbprint="$(__calc_account_thumbprint)" fi _debug "Trigger validation." - vtype="$VTYPE_HTTP" + vtype="$VTYPE_DNS" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then @@ -4733,7 +4776,7 @@ _deactivate() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" keyauthorization="$token.$thumbprint" @@ -4759,7 +4802,7 @@ _deactivate() { _debug _vtype "$_vtype" _info "Found $_vtype" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then @@ -4769,7 +4812,13 @@ _deactivate() { _info "Deactivate: $_vtype" - if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}" + fi + + if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate: $_vtype success." else _err "Can not deactivate $_vtype." @@ -5492,10 +5541,6 @@ _process() { fi if [ -z "$_domain" ]; then - if _startswith "$_dvalue" "*."; then - _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." - return 1 - fi _domain="$_dvalue" else if _startswith "$_dvalue" "*."; then From cd8fc35968b6009d349284a3acb24f10d531e50f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:50:57 +0800 Subject: [PATCH 092/272] fix dns manual mode. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 5a5f0337..4433e814 100755 --- a/acme.sh +++ b/acme.sh @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "Le_OrderFinalize" + if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else From 79a2bed640430d68da691a956ef64229de954c1a Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 7 Jan 2018 12:12:40 +0800 Subject: [PATCH 093/272] update doc for v2 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a2c210e8..ea509e24 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. +- Support ACME v1 and ACME v2 +- Support ACME v2 wildcard certs - Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Bash, dash and sh compatible. - Simplest shell script for Let's Encrypt free certificate client. From ea25492c2823a4971b3d2eb28fa0dcd2b5105db9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 20:43:23 +0800 Subject: [PATCH 094/272] we should not use "updating" to support wildcard --- dnsapi/dns_ovh.sh | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index eaa90bdf..03b75d97 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -137,48 +137,27 @@ dns_ovh_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" - - if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then - _info "Adding record" - if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then - if _contains "$response" "$txtvalue"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 - return 0 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1) - if [ -z "$record_id" ]; then - _err "Can not get record id." - return 1 - fi - _debug "record_id" "$record_id" - - if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then - if _contains "$response" "null"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Updated, sleeping 10 seconds" - sleep 10 - return 0 - fi + _info "Adding record" + if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then + if _contains "$response" "$txtvalue"; then + _ovh_rest POST "domain/zone/$_domain/refresh" + _debug "Refresh:$response" + _info "Added, sleeping 10 seconds" + sleep 10 + return 0 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 } #fulldomain dns_ovh_rm() { fulldomain=$1 + txtvalue=$2 + _debug "Getting txt records" + #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" } From be186bd39ba8f0fdd971ca23e12138b81d5e95c4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 21:36:48 +0800 Subject: [PATCH 095/272] support dns_ovh_rm() --- dnsapi/dns_ovh.sh | 56 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 03b75d97..96c2044b 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,13 +78,8 @@ _ovh_get_api() { esac } -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_ovh_add() { - fulldomain=$1 - txtvalue=$2 +_initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" OVH_AS="" @@ -127,6 +122,19 @@ dns_ovh_add() { return 1 fi _info "Consumer key is ok." + return 0 +} + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ovh_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _initAuth; then + return 1 + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -156,9 +164,39 @@ dns_ovh_add() { dns_ovh_rm() { fulldomain=$1 txtvalue=$2 + + if ! _initAuth; then + 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" _debug "Getting txt records" - #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" + if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then + return 1 + fi + for rid in $(echo "$response" | tr '[,]' ' '); do + _debug rid "$rid" + if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then + return 1 + fi + if _contains "$response" "\"target\":\"$txtvalue\""; then + _debug "Found txt id:$rid" + if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then + return 1 + fi + return 0 + fi + done + + return 1 } #################### Private functions below ################################## @@ -170,7 +208,7 @@ _ovh_authentication() { _H3="" _H4="" - _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' + _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" _debug3 response "$response" @@ -258,7 +296,7 @@ _ovh_rest() { export _H3="X-Ovh-Timestamp: $_ovh_t" export _H4="X-Ovh-Consumer: $OVH_CK" export _H5="Content-Type: application/json;charset=utf-8" - if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then + if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then _debug data "$data" response="$(_post "$data" "$_ovh_url" "" "$m")" else From f823f170e64a641639fdf2c97a8c6a334f3264b7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:04:03 +0800 Subject: [PATCH 096/272] fix ovh --- dnsapi/dns_ovh.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 96c2044b..60ce1898 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -114,8 +114,7 @@ _initAuth() { _info "Checking authentication" - response="$(_ovh_rest GET "domain")" - if _contains "$response" "INVALID_CREDENTIAL"; then + if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then _err "The consumer key is invalid: $OVH_CK" _err "Please retry to create a new one." _clearaccountconf OVH_CK @@ -150,8 +149,7 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 + _info "Added" return 0 fi fi @@ -303,8 +301,8 @@ _ovh_rest() { response="$(_get "$_ovh_url")" fi - if [ "$?" != "0" ]; then - _err "error $ep" + if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then + _err "error $response" return 1 fi _debug2 response "$response" From 01cc2e13d81cbcda3485c8256a591230defe2626 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:47:01 +0800 Subject: [PATCH 097/272] add more sleep for ovh --- dnsapi/dns_ovh.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 60ce1898..de69bd91 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -149,7 +149,8 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added" + _info "Added, sleep 10 seconds." + _sleep 10 return 0 fi fi From 0170c20e9a7d9632f8c83417c73c56fe0d985c51 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 23:05:55 +0800 Subject: [PATCH 098/272] fix format --- dnsapi/dns_ovh.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index de69bd91..60094739 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,7 +78,6 @@ _ovh_get_api() { esac } - _initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" @@ -181,7 +180,7 @@ dns_ovh_rm() { return 1 fi - for rid in $(echo "$response" | tr '[,]' ' '); do + for rid in $(echo "$response" | tr '][,' ' '); do _debug rid "$rid" if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then return 1 From 60814ecfe10427238a8bcb39caa778ed520f0f7f Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 19:48:57 +0800 Subject: [PATCH 099/272] typo --- acme.sh | 1 - dnsapi/dns_aws.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 4433e814..6c3f6e05 100755 --- a/acme.sh +++ b/acme.sh @@ -3474,7 +3474,6 @@ issue() { #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do - #todo: check wildcard ? if [ "$d" ]; then _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" fi diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 5a716514..8c1513d1 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -23,7 +23,7 @@ dns_aws_add() { AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" _err "You don't specify aws route53 api key id and and api key secret yet." - _err "Please create you key and try again. see $(__green $AWS_WIKI)" + _err "Please create your key and try again. see $(__green $AWS_WIKI)" return 1 fi From 9e9f839d96053cae020aab3817cdb94d9622df18 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 21:55:40 +0800 Subject: [PATCH 100/272] support only one wildcard domain. fix https://github.com/Neilpang/acme.sh/issues/1188#issuecomment-357684744 --- acme.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 6c3f6e05..74b437cb 100755 --- a/acme.sh +++ b/acme.sh @@ -3340,10 +3340,6 @@ issue() { _main_domain="$2" _alt_domains="$3" - if _startswith "$_main_domain" "*."; then - _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." - return 1 - fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -5539,13 +5535,13 @@ _process() { return 1 fi + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ -z "$_domain" ]; then _domain="$_dvalue" else - if _startswith "$_dvalue" "*."; then - _debug "Wildcard domain" - export ACME_VERSION=2 - fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else From cd9fb3b63581a2f4dadca299c847b365528ea718 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 16 Jan 2018 20:55:07 +0800 Subject: [PATCH 101/272] fix https://github.com/Neilpang/acme.sh/issues/1189 --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 74b437cb..59a7d960 100755 --- a/acme.sh +++ b/acme.sh @@ -2259,6 +2259,8 @@ _initAPI() { #[domain] [keylength or isEcc flag] _initpath() { + domain="$1" + _ilength="$2" __initHome @@ -2346,13 +2348,10 @@ _initpath() { ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" fi - if [ -z "$1" ]; then + if [ -z "$domain" ]; then return 0 fi - domain="$1" - _ilength="$2" - if [ -z "$DOMAIN_PATH" ]; then domainhome="$CERT_HOME/$domain" domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX" @@ -4234,8 +4233,6 @@ signcsr() { return 1 fi - _initpath - _csrsubj=$(_readSubjectFromCSR "$_csrfile") if [ "$?" != "0" ]; then _err "Can not read subject from csr: $_csrfile" @@ -4272,6 +4269,9 @@ signcsr() { return 1 fi + if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then + export ACME_VERSION=2 + fi _initpath "$_csrsubj" "$_csrkeylength" mkdir -p "$DOMAIN_PATH" From 78d1cfb4648dcf6fbb5ae30074f95e6a517057c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20B=C3=B6hnke?= Date: Wed, 17 Jan 2018 19:21:14 +0100 Subject: [PATCH 102/272] fix bug in the --ca-bundle param of passing -f to _readlink When _readlink is called the -f param must not be passed. _readlink (with leading underscore) is a wrapper around readlink (without leading underscore). _readlink already passes -f to readlink, that's why it must not be passed to _readlink. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 472975a6..2d245518 100755 --- a/acme.sh +++ b/acme.sh @@ -5510,7 +5510,7 @@ _process() { HTTPS_INSECURE="1" ;; --ca-bundle) - _ca_bundle="$(_readlink -f "$2")" + _ca_bundle="$(_readlink "$2")" CA_BUNDLE="$_ca_bundle" shift ;; From c1f8ffa3861f93d624674732180f60ac1db768ed Mon Sep 17 00:00:00 2001 From: MaomiHz Date: Wed, 17 Jan 2018 21:39:13 -0600 Subject: [PATCH 103/272] Use [0-9] instead --- dnsapi/dns_dgon.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh index 57613349..5d38ef76 100755 --- a/dnsapi/dns_dgon.sh +++ b/dnsapi/dns_dgon.sh @@ -112,11 +112,11 @@ dns_dgon_rm() { domain_list="$(_get "$GURL")" ## 2) find record ## check for what we are looing for: "type":"A","name":"$_sub_domain" - record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[[:digit:]]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" + record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" ## 3) check record and get next page if [ -z "$record" ]; then ## find the next page if we dont have a match - nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[[:digit:]]+")" + nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")" if [ -z "$nextpage" ]; then _err "no record and no nextpage in digital ocean DNS removal" return 1 @@ -128,7 +128,7 @@ dns_dgon_rm() { done ## we found the record - rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[[:digit:]]+" | _egrep_o "[[:digit:]]+")" + rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")" _debug rec_id "$rec_id" ## delete the record From 6ba4f8b54cbc5019ce0b4da537975d10d39c0251 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 18 Jan 2018 21:04:06 +0800 Subject: [PATCH 104/272] fix https://github.com/Neilpang/acme.sh/issues/1204 --- dnsapi/dns_aws.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 5a716514..450e42de 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -19,6 +19,8 @@ dns_aws_add() { fulldomain=$1 txtvalue=$2 + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" @@ -27,10 +29,9 @@ dns_aws_add() { return 1 fi - if [ -z "$AWS_SESSION_TOKEN" ]; then - _saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" - _saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" - fi + #save for future use + _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" + _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -56,6 +57,8 @@ dns_aws_rm() { fulldomain=$1 txtvalue=$2 + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" From 37f39c0870ce3f381a586f331ee5fe41cc3d89b3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 19 Jan 2018 22:41:42 +0800 Subject: [PATCH 105/272] minor --- acme.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 2d245518..bd460521 100755 --- a/acme.sh +++ b/acme.sh @@ -2548,10 +2548,7 @@ _setNginx() { _d="$1" _croot="$2" _thumbpt="$3" - if ! _exists "nginx"; then - _err "nginx command is not found." - return 1 - fi + FOUND_REAL_NGINX_CONF="" FOUND_REAL_NGINX_CONF_LN="" BACKUP_NGINX_CONF="" @@ -2561,6 +2558,10 @@ _setNginx() { if [ -z "$_start_f" ]; then _debug "find start conf from nginx command" if [ -z "$NGINX_CONF" ]; then + if ! _exists "nginx"; then + _err "nginx command is not found." + return 1 + fi NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")" _debug NGINX_CONF "$NGINX_CONF" NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)" @@ -2605,6 +2606,10 @@ _setNginx() { return 1 fi + if ! _exists "nginx"; then + _err "nginx command is not found." + return 1 + fi _info "Check the nginx conf before setting up." if ! _exec "nginx -t" >/dev/null; then _exec_err From 258cf20c92d3ea541283cf3eff0ecc0b07305ae7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 19 Jan 2018 22:48:06 +0800 Subject: [PATCH 106/272] minor --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 83ef4ea7..cf88462a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -666,7 +666,7 @@ And now you can issue certs with: acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com ``` -## 37. Use autoDNS (InternetX) +## 36. Use autoDNS (InternetX) [InternetX](https://www.internetx.com/) offers an [xml api](https://help.internetx.com/display/API/AutoDNS+XML-API) with your standard login credentials, set them like so: From 04a609b51f38f1db8792d57dbc0af0eadaf108fe Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 21 Jan 2018 20:20:54 +0800 Subject: [PATCH 107/272] fix https://github.com/Neilpang/acme.sh/issues/1209 --- acme.sh | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index bd460521..920f15fc 100755 --- a/acme.sh +++ b/acme.sh @@ -2702,7 +2702,7 @@ _isRealNginxConf() { for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do _debug _fln "$_fln" if [ "$_fln" ]; then - _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1) + _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *" | grep -v server_name | _tail_n 1) _debug "_start" "$_start" _start_n=$(echo "$_start" | cut -d : -f 1) _start_nn=$(_math $_start_n + 1) @@ -2711,8 +2711,8 @@ _isRealNginxConf() { _left="$(sed -n "${_start_nn},99999p" "$2")" _debug2 _left "$_left" - if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then - _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1) + if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" >/dev/null; then + _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | _head_n 1) _debug "_end" "$_end" _end_n=$(echo "$_end" | cut -d : -f 1) _debug "_end_n" "$_end_n" @@ -2723,8 +2723,20 @@ _isRealNginxConf() { _debug "_seg_n" "$_seg_n" - if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \ - || [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then + _skip_ssl=1 + for _listen_i in $(echo "$_seg_n" | grep "^ *listen" | tr -d " "); do + if [ "$_listen_i" ]; then + if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl[ |;]")" ]; then + _debug2 "$_listen_i is ssl" + else + _debug2 "$_listen_i is plain text" + _skip_ssl="" + break; + fi + fi + done + + if [ "$_skip_ssl" = "1" ]; then _debug "ssl on, skip" else FOUND_REAL_NGINX_CONF_LN=$_fln From c05eb0b1b2b859a1e20f82d0ee2593f6840f13a4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 23 Jan 2018 19:42:57 +0800 Subject: [PATCH 108/272] fx format --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 920f15fc..77ab439e 100755 --- a/acme.sh +++ b/acme.sh @@ -2731,8 +2731,8 @@ _isRealNginxConf() { else _debug2 "$_listen_i is plain text" _skip_ssl="" - break; - fi + break + fi fi done From 9134b6ea989fb0f390ecf680e62f867cae3063a9 Mon Sep 17 00:00:00 2001 From: Herve Couplet Date: Tue, 23 Jan 2018 17:53:40 +0100 Subject: [PATCH 109/272] if local-address is present, bind to that ip instead of 0.0.0.0 --- acme.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 77ab439e..c3cba1e7 100755 --- a/acme.sh +++ b/acme.sh @@ -1984,9 +1984,15 @@ _startserver() { _NC="$_NC -6" fi + SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork + + #Adding bind to local-address + if [ "$_local_address" ]; then + $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${_local_address}" + fi + _debug "_NC" "$_NC" - #todo listen address - $_NC TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From e90f3b84c1a1694f7f5c13a3ebfabc2da246a78d Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 23 Jan 2018 18:24:08 +0100 Subject: [PATCH 110/272] Add support for Azure DNS Adding support for Azure DNS See https://docs.microsoft.com/en-us/azure/dns/ --- dnsapi/dns_azure.sh | 241 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 dnsapi/dns_azure.sh diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh new file mode 100644 index 00000000..40997740 --- /dev/null +++ b/dnsapi/dns_azure.sh @@ -0,0 +1,241 @@ +#!/usr/bin/env sh + + +######## 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 then 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 + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" + _debug $response +} + +# 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 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 + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" + _debug $response +} + +################### Private functions below ################################## + +_azure_rest() { + m=$1 + ep="$2" + data="$3" + accesstoken="$4" + + _debug "$ep" + + export _H1="authorization: Bearer $accesstoken" + export _H2="accept: application/json" + export _H3="Content-Type: application/json" + _H1="authorization: Bearer $accesstoken" + _H2="accept: application/json" + _H3="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$ep" "" "$m")" + else + response="$(_get "$ep")" + fi + _debug2 response "$response" + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + 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 + + export _H1="accept: application/json" + export _H2="Content-Type: application/x-www-form-urlencoded" + export _H3="" + + 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" + _debug data "$body" + response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" + accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + + if [ "$?" != "0" ]; then + _err "error $response" + return 1 + fi + printf $accesstoken + _debug2 response "$response" + 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=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | 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 +} + From b6fc8398cf03a0c7c6b173f6f61704c09047c578 Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 23 Jan 2018 18:25:52 +0100 Subject: [PATCH 111/272] Usage instructions for Azure DNS --- dnsapi/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index cf88462a..76b0cdf4 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -684,6 +684,25 @@ acme.sh --issue --dns dns_autodns -d example.com -d www.example.com The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 37. Use Azure DNS + +You have to create a service principal first. See: https://github.com/Neilpang/acme.sh/wiki/How-to-use-AzureDns-API + +``` +export AZUREDNS_SUBSCRIPTIONID="12345678-9abc-def0-1234-567890abcdef" +export AZUREDNS_TENANTID="11111111-2222-3333-4444-555555555555" +export AZUREDNS_APPID="3b5033b5-7a66-43a5-b3b9-a36b9e7c25ed" +export AZUREDNS_CLIENTSECRET="1b0224ef-34d4-5af9-110f-77f527d561bd" +``` + +Then you can issue your certificates with: + +``` +acme.sh --issue --dns dns_azure -d example.com -d www.example.com +``` + +`AZUREDNS_SUBSCRIPTIONID`, `AZUREDNS_TENANTID`,`AZUREDNS_APPID` and `AZUREDNS_CLIENTSECRET` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From c82cd90ed6f64a982acfc45ce94db518c7603cad Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 23 Jan 2018 18:46:59 +0100 Subject: [PATCH 112/272] Relative link to Azure DNS wiki page --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 76b0cdf4..1148ea7f 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -686,7 +686,7 @@ The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be sa ## 37. Use Azure DNS -You have to create a service principal first. See: https://github.com/Neilpang/acme.sh/wiki/How-to-use-AzureDns-API +You have to create a service principal first. See:[How to use Azure DNS](../../../wiki/How-to-use-Azure-DNS) ``` export AZUREDNS_SUBSCRIPTIONID="12345678-9abc-def0-1234-567890abcdef" From 3fdbbafcb5eb10a55f195c90d77f31a9aa342850 Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 24 Jan 2018 09:59:05 +0100 Subject: [PATCH 113/272] fix getroot with multiple dns zones --- dnsapi/dns_azure.sh | 96 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 40997740..583a37d8 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -1,56 +1,55 @@ #!/usr/bin/env sh - ######## Public functions ##################### - # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# 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() -{ +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 " + 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 then Azure Tenant ID " + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify then 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" + 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" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Client Secret" return 1 fi #save account details to account conf file. @@ -61,15 +60,15 @@ dns_azure_add() 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" - + _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 body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" @@ -82,62 +81,62 @@ dns_azure_add() # # Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete # -dns_azure_rm() -{ +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 " + 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 " + 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" + 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 Azure Client Secret" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify 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" - + _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 body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" @@ -152,7 +151,7 @@ _azure_rest() { ep="$2" data="$3" accesstoken="$4" - + _debug "$ep" export _H1="authorization: Bearer $accesstoken" @@ -181,16 +180,16 @@ _azure_getaccess_token() { TENANTID=$1 clientID=$2 clientSecret=$3 - + export _H1="accept: application/json" export _H2="Content-Type: application/x-www-form-urlencoded" export _H3="" - + 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" _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") - + if [ "$?" != "0" ]; then _err "error $response" return 1 @@ -208,11 +207,11 @@ _get_root() { 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 + ## 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 + ## 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 + _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 @@ -225,7 +224,7 @@ _get_root() { fi if _contains "$response" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _domain_id=$(printf "%s\n" "$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 @@ -238,4 +237,3 @@ _get_root() { done return 1 } - From f7d4698ef038b245ad6163bf604d167ceb51eb8b Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 24 Jan 2018 13:39:38 +0100 Subject: [PATCH 114/272] improve error checking --- dnsapi/dns_azure.sh | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 583a37d8..24b13bba 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -73,7 +73,11 @@ dns_azure_add() _debug $acmeRecordURI body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" - _debug $response + if [ "$_code" = "200" ] || [ "$code" = '201' ]; then + _info "validation record added" + else + _err "error adding validation record ($_code)" + fi } # Usage: fulldomain txtvalue @@ -141,7 +145,11 @@ dns_azure_rm() _debug $acmeRecordURI body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" - _debug $response + if [ "$_code" = "200" ] || [ "$code" = '204' ]; then + _info "validation record removed" + else + _err "error removing validation record ($_code)" + fi } ################### Private functions below ################################## @@ -168,6 +176,10 @@ _azure_rest() { response="$(_get "$ep")" fi _debug2 response "$response" + + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + _debug2 "http response code $_code" + if [ "$?" != "0" ]; then _err "error $ep" return 1 @@ -189,13 +201,18 @@ _azure_getaccess_token() { _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") - + _debug2 "response $response" + + if [ -Z "$accesstoken" ] ; then + _err "no acccess token received" + return 1 + fi if [ "$?" != "0" ]; then _err "error $response" return 1 fi printf $accesstoken - _debug2 response "$response" + return 0 } From d51c383866a7fb029479e99b28a86c1ff4b9d9aa Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 24 Jan 2018 15:08:06 +0100 Subject: [PATCH 115/272] remove unused code and fix error handling --- dnsapi/dns_azure.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 24b13bba..1841b070 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -77,6 +77,7 @@ dns_azure_add() _info "validation record added" else _err "error adding validation record ($_code)" + return 1 fi } @@ -149,6 +150,7 @@ dns_azure_rm() _info "validation record removed" else _err "error removing validation record ($_code)" + return 1 fi } @@ -160,15 +162,11 @@ _azure_rest() { data="$3" accesstoken="$4" - _debug "$ep" - export _H1="authorization: Bearer $accesstoken" export _H2="accept: application/json" export _H3="Content-Type: application/json" - _H1="authorization: Bearer $accesstoken" - _H2="accept: application/json" - _H3="Content-Type: application/json" - + + _debug "$ep" if [ "$m" != "GET" ]; then _debug data "$data" response="$(_post "$data" "$ep" "" "$m")" @@ -195,7 +193,6 @@ _azure_getaccess_token() { export _H1="accept: application/json" export _H2="Content-Type: application/x-www-form-urlencoded" - export _H3="" 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" _debug data "$body" @@ -203,7 +200,7 @@ _azure_getaccess_token() { accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" - if [ -Z "$accesstoken" ] ; then + if [ -z "$accesstoken" ] ; then _err "no acccess token received" return 1 fi From c7b8debb6eb1e378b63b2df151fcadeae660adf2 Mon Sep 17 00:00:00 2001 From: martgras Date: Thu, 25 Jan 2018 07:01:39 +0100 Subject: [PATCH 116/272] fix travis issues --- dnsapi/dns_azure.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 1841b070..b7ebe546 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -69,11 +69,11 @@ dns_azure_add() _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 + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" - if [ "$_code" = "200" ] || [ "$code" = '201' ]; then + if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then _info "validation record added" else _err "error adding validation record ($_code)" @@ -142,8 +142,8 @@ dns_azure_rm() _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 + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" if [ "$_code" = "200" ] || [ "$code" = '204' ]; then @@ -194,7 +194,7 @@ _azure_getaccess_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" + 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" _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") @@ -208,7 +208,7 @@ _azure_getaccess_token() { _err "error $response" return 1 fi - printf $accesstoken + printf "$accesstoken" return 0 } @@ -225,7 +225,7 @@ _get_root() { ## (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 + _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 From 00781dd4e1f0be3c9e83872ca9fc7a53bb0370bd Mon Sep 17 00:00:00 2001 From: martgras Date: Thu, 25 Jan 2018 07:06:42 +0100 Subject: [PATCH 117/272] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a2c210e8..8e1b22da 100644 --- a/README.md +++ b/README.md @@ -344,6 +344,7 @@ You don't have to do anything manually! 1. Servercow (https://servercow.de) 1. Namesilo (https://www.namesilo.com) 1. InternetX autoDNS API (https://internetx.com) +1. Azure DNS And: From 441c26dd3286bb326523e4d56ea5d303c2571a65 Mon Sep 17 00:00:00 2001 From: martgras Date: Thu, 25 Jan 2018 07:40:35 +0100 Subject: [PATCH 118/272] Update dns_azure.sh --- dnsapi/dns_azure.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index b7ebe546..3c7e4de9 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -146,7 +146,7 @@ dns_azure_rm() _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" - if [ "$_code" = "200" ] || [ "$code" = '204' ]; then + if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then _info "validation record removed" else _err "error removing validation record ($_code)" @@ -208,7 +208,7 @@ _azure_getaccess_token() { _err "error $response" return 1 fi - printf "$accesstoken" + printf "%s" "$accesstoken" return 0 } From d1067c60bf93011eb74695ab34fc9cce52fdd0b1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Jan 2018 21:08:20 +0800 Subject: [PATCH 119/272] fix https://github.com/Neilpang/acme.sh/issues/1193#issuecomment-360025170 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 77ab439e..48297c7d 100755 --- a/acme.sh +++ b/acme.sh @@ -2724,7 +2724,7 @@ _isRealNginxConf() { _debug "_seg_n" "$_seg_n" _skip_ssl=1 - for _listen_i in $(echo "$_seg_n" | grep "^ *listen" | tr -d " "); do + for _listen_i in $(echo "$_seg_n" | tr "\t" ' ' | grep "^ *listen" | tr -d " "); do if [ "$_listen_i" ]; then if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl[ |;]")" ]; then _debug2 "$_listen_i is ssl" From 91607bb2a1e207fa693d3c09dfafee15dd8a912e Mon Sep 17 00:00:00 2001 From: neil Date: Thu, 25 Jan 2018 22:58:11 +0800 Subject: [PATCH 120/272] Update dns_azure.sh --- dnsapi/dns_azure.sh | 406 ++++++++++++++++++++++---------------------- 1 file changed, 201 insertions(+), 205 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 3c7e4de9..104a81a6 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -7,78 +7,76 @@ # # 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 +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="" + if [ -z "$AZUREDNS_TENANTID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" AZUREDNS_CLIENTSECRET="" _err "You didn't specify then 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" - + 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") + accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" + 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" + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" + if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then + _info "validation record added" + else + _err "error adding validation record ($_code)" 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" - body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" - _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" - if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then - _info "validation record added" - else - _err "error adding validation record ($_code)" - return 1 - fi + fi } # Usage: fulldomain txtvalue @@ -86,156 +84,154 @@ dns_azure_add() # # 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 Azure Client Secret" - return 1 - fi +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 Azure Client Secret" + return 1 + fi - accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") + accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" + 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" + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _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 - _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" - body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" - _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 + fi } ################### Private functions below ################################## _azure_rest() { - m=$1 - ep="$2" - data="$3" - accesstoken="$4" - - export _H1="authorization: Bearer $accesstoken" - export _H2="accept: application/json" - export _H3="Content-Type: application/json" - - _debug "$ep" - if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$ep" "" "$m")" - else - response="$(_get "$ep")" - fi - _debug2 response "$response" - - _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" - _debug2 "http response code $_code" - - if [ "$?" != "0" ]; then - _err "error $ep" - return 1 - fi - return 0 + m=$1 + ep="$2" + data="$3" + accesstoken="$4" + + export _H1="authorization: Bearer $accesstoken" + export _H2="accept: application/json" + export _H3="Content-Type: application/json" + + _debug "$ep" + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$ep" "" "$m")" + else + response="$(_get "$ep")" + fi + _debug2 response "$response" + + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + _debug2 "http response code $_code" + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + 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 - - 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" - _debug data "$body" - response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" - accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") - _debug2 "response $response" - - if [ -z "$accesstoken" ] ; then - _err "no acccess token received" - return 1 - fi - if [ "$?" != "0" ]; then - _err "error $response" - return 1 - fi - printf "%s" "$accesstoken" - - return 0 + TENANTID=$1 + clientID=$2 + clientSecret=$3 + + 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" + _debug data "$body" + response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" + accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _debug2 "response $response" + + if [ -z "$accesstoken" ] ; then + _err "no acccess token received" + return 1 + fi + if [ "$?" != "0" ]; then + _err "error $response" + return 1 + fi + 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 + 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=$(printf "%s\n" "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") @@ -245,9 +241,9 @@ _get_root() { return 0 fi return 1 - fi - p=$i - i=$(_math "$i" + 1) - done - return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 } From e4b24d20acb68b741422d3e078fbf572d1d86f36 Mon Sep 17 00:00:00 2001 From: neil Date: Thu, 25 Jan 2018 23:08:56 +0800 Subject: [PATCH 121/272] Update dns_azure.sh --- dnsapi/dns_azure.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 104a81a6..9952a16e 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -29,9 +29,9 @@ dns_azure_add() { AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify then Azure Tenant ID " - return 1 + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify then Azure Tenant ID " + return 1 fi if [ -z "$AZUREDNS_APPID" ] ; then @@ -194,7 +194,7 @@ _azure_getaccess_token() { 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" _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" - accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" if [ -z "$accesstoken" ] ; then @@ -234,7 +234,7 @@ _get_root() { fi if _contains "$response" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(printf "%s\n" "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _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 From a4fc802d1b84c49c3357aa2765dead14ec4320e7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Jan 2018 23:18:37 +0800 Subject: [PATCH 122/272] Add selectel.com(selectel.ru) DNS API. fix https://github.com/Neilpang/acme.sh/issues/1220 --- README.md | 1 + dnsapi/README.md | 17 +++++ dnsapi/dns_selectel.sh | 161 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 dnsapi/dns_selectel.sh diff --git a/README.md b/README.md index 8e1b22da..ad3befcd 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,7 @@ You don't have to do anything manually! 1. Namesilo (https://www.namesilo.com) 1. InternetX autoDNS API (https://internetx.com) 1. Azure DNS +1. selectel.com(selectel.ru) DNS API And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 1148ea7f..32eca131 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -703,6 +703,23 @@ acme.sh --issue --dns dns_azure -d example.com -d www.example.com `AZUREDNS_SUBSCRIPTIONID`, `AZUREDNS_TENANTID`,`AZUREDNS_APPID` and `AZUREDNS_CLIENTSECRET` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 38. Use selectel.com(selectel.ru) domain API to automatically issue cert + +First you need to login to your account to get your API key from: https://my.selectel.ru/profile/apikeys. + +```sh +export SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" + +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_selectel -d example.com -d www.example.com +``` + +The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh new file mode 100644 index 00000000..8dd9b352 --- /dev/null +++ b/dnsapi/dns_selectel.sh @@ -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"; 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\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\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 +} From 03140865f031dfe2d9ee9bb3c3017b23d52f8971 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Jan 2018 23:25:50 +0800 Subject: [PATCH 123/272] fix for existing record --- dnsapi/dns_selectel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh index 8dd9b352..c5e40788 100644 --- a/dnsapi/dns_selectel.sh +++ b/dnsapi/dns_selectel.sh @@ -36,7 +36,7 @@ dns_selectel_add() { _info "Adding record" if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then - if _contains "$response" "$txtvalue"; then + if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then _info "Added, OK" return 0 fi From 72fe7396d6d2779b03b40ec0d7c76fc137423f12 Mon Sep 17 00:00:00 2001 From: martgras Date: Fri, 26 Jan 2018 07:53:47 +0100 Subject: [PATCH 124/272] spelling mistake in error message --- dnsapi/dns_azure.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 9952a16e..6e53131f 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -30,7 +30,7 @@ dns_azure_add() { AZUREDNS_TENANTID="" AZUREDNS_APPID="" AZUREDNS_CLIENTSECRET="" - _err "You didn't specify then Azure Tenant ID " + _err "You didn't specify the Azure Tenant ID " return 1 fi @@ -125,7 +125,7 @@ dns_azure_rm() { AZUREDNS_TENANTID="" AZUREDNS_APPID="" AZUREDNS_CLIENTSECRET="" - _err "You didn't specify Azure Client Secret" + _err "You didn't specify the Azure Client Secret" return 1 fi From dd171ca44a2ed193c5dd9e21c1aedc6cfe251fde Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Jan 2018 20:14:51 +0800 Subject: [PATCH 125/272] fix format --- dnsapi/dns_azure.sh | 48 +++++++++++++++++++++--------------------- dnsapi/dns_selectel.sh | 4 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 6e53131f..2c39a00c 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -15,7 +15,7 @@ dns_azure_add() { 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="" @@ -25,7 +25,7 @@ dns_azure_add() { return 1 fi - if [ -z "$AZUREDNS_TENANTID" ] ; then + if [ -z "$AZUREDNS_TENANTID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -34,7 +34,7 @@ dns_azure_add() { return 1 fi - if [ -z "$AZUREDNS_APPID" ] ; then + if [ -z "$AZUREDNS_APPID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -59,15 +59,15 @@ dns_azure_add() { accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" - return 1 + 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" + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" @@ -76,7 +76,7 @@ dns_azure_add() { else _err "error adding validation record ($_code)" return 1 - fi + fi } # Usage: fulldomain txtvalue @@ -102,7 +102,7 @@ dns_azure_rm() { return 1 fi - if [ -z "$AZUREDNS_TENANTID" ] ; then + if [ -z "$AZUREDNS_TENANTID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -111,7 +111,7 @@ dns_azure_rm() { return 1 fi - if [ -z "$AZUREDNS_APPID" ] ;then + if [ -z "$AZUREDNS_APPID" ];then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -131,15 +131,15 @@ dns_azure_rm() { accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" - return 1 + 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" + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" @@ -165,10 +165,10 @@ _azure_rest() { _debug "$ep" if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$ep" "" "$m")" + _debug data "$data" + response="$(_post "$data" "$ep" "" "$m")" else - response="$(_get "$ep")" + response="$(_get "$ep")" fi _debug2 response "$response" @@ -176,8 +176,8 @@ _azure_rest() { _debug2 "http response code $_code" if [ "$?" != "0" ]; then - _err "error $ep" - return 1 + _err "error $ep" + return 1 fi return 0 } @@ -191,15 +191,15 @@ _azure_getaccess_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" + 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" _debug data "$body" - response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" - accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST")" + accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" - if [ -z "$accesstoken" ] ; then + if [ -z "$accesstoken" ]; then _err "no acccess token received" - return 1 + return 1 fi if [ "$?" != "0" ]; then _err "error $response" diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh index c5e40788..460f89c3 100644 --- a/dnsapi/dns_selectel.sh +++ b/dnsapi/dns_selectel.sh @@ -42,7 +42,7 @@ dns_selectel_add() { fi fi _err "Add txt record error." - return 1 + return 1 } #fulldomain txtvalue @@ -94,7 +94,7 @@ dns_selectel_rm() { _err "Delete record error." return 1 fi - return 0; + return 0 } #################### Private functions below ################################## From 8c88757451b2ca2bb61814180442b4606555fe16 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Jan 2018 20:39:41 +0800 Subject: [PATCH 126/272] fix format --- dnsapi/dns_azure.sh | 8 ++++---- dnsapi/dns_selectel.sh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 2c39a00c..0834ede7 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -111,7 +111,7 @@ dns_azure_rm() { return 1 fi - if [ -z "$AZUREDNS_APPID" ];then + if [ -z "$AZUREDNS_APPID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -162,7 +162,7 @@ _azure_rest() { export _H1="authorization: Bearer $accesstoken" export _H2="accept: application/json" export _H3="Content-Type: application/json" - + _debug "$ep" if [ "$m" != "GET" ]; then _debug data "$data" @@ -173,7 +173,7 @@ _azure_rest() { _debug2 response "$response" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" - _debug2 "http response code $_code" + _debug2 "http response code $_code" if [ "$?" != "0" ]; then _err "error $ep" @@ -197,7 +197,7 @@ _azure_getaccess_token() { accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" - if [ -z "$accesstoken" ]; then + if [ -z "$accesstoken" ]; then _err "no acccess token received" return 1 fi diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh index 460f89c3..94252d81 100644 --- a/dnsapi/dns_selectel.sh +++ b/dnsapi/dns_selectel.sh @@ -83,7 +83,7 @@ dns_selectel_rm() { return 1 fi - _record_id="$(echo "$_record_seg" | tr ",}" "\n\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)" + _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" @@ -127,7 +127,7 @@ _get_root() { if ! _sl_rest GET "/$h"; then return 1 fi - _domain_id="$(echo "$response" | tr ",}" "\n\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" + _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" return 0 fi p=$i From 1c35f46b457fa65612ed7eaa7a9236d6f1f0c3c9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Jan 2018 21:37:30 +0800 Subject: [PATCH 127/272] fix https://github.com/Neilpang/acme.sh/issues/1212 --- acme.sh | 82 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/acme.sh b/acme.sh index 9bdb66aa..f64955d3 100755 --- a/acme.sh +++ b/acme.sh @@ -3969,6 +3969,16 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi + + if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then + _debug "Found cert chain" + cat "$CERT_PATH" > "$CERT_FULLCHAIN_PATH" + _end_n="$(grep -n -- "$END_CERT" "$CERT_FULLCHAIN_PATH" | _head_n 1 | cut -d : -f 1)" + _debug _end_n "$_end_n" + sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" > "$CERT_PATH" + _end_n="$(_math $_end_n + 1)" + sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" > "$CA_CERT_PATH" + fi else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then _err "Sign failed." @@ -4022,47 +4032,49 @@ $_authorizations_map" _cleardomainconf "Le_Vlist" - Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - - if [ "$Le_LinkIssuer" ]; then - if ! _contains "$Le_LinkIssuer" ":"; then - _info "$(__red "Relative issuer link found.")" - Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" - fi - _debug Le_LinkIssuer "$Le_LinkIssuer" - _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" - - _link_issuer_retry=0 - _MAX_ISSUER_RETRY=5 - while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do - _debug _link_issuer_retry "$_link_issuer_retry" - if [ "$ACME_VERSION" = "2" ]; then - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then - break - fi - else - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then - echo "$BEGIN_CERT" >"$CA_CERT_PATH" - _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" - echo "$END_CERT" >>"$CA_CERT_PATH" + if [ "$ACME_VERSION" = "2" ]; then + _debug "v2 chain." + else + Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" - cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" - _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" + if [ "$Le_LinkIssuer" ]; then + if ! _contains "$Le_LinkIssuer" ":"; then + _info "$(__red "Relative issuer link found.")" + Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" + fi + _debug Le_LinkIssuer "$Le_LinkIssuer" + _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" - rm -f "$CA_CERT_PATH.der" - break + _link_issuer_retry=0 + _MAX_ISSUER_RETRY=5 + while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do + _debug _link_issuer_retry "$_link_issuer_retry" + if [ "$ACME_VERSION" = "2" ]; then + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then + break + fi + else + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then + echo "$BEGIN_CERT" >"$CA_CERT_PATH" + _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" + echo "$END_CERT" >>"$CA_CERT_PATH" + cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" + rm -f "$CA_CERT_PATH.der" + break + fi fi + _link_issuer_retry=$(_math $_link_issuer_retry + 1) + _sleep "$_link_issuer_retry" + done + if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then + _err "Max retry for issuer ca cert is reached." fi - _link_issuer_retry=$(_math $_link_issuer_retry + 1) - _sleep "$_link_issuer_retry" - done - if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then - _err "Max retry for issuer ca cert is reached." + else + _debug "No Le_LinkIssuer header found." fi - else - _debug "No Le_LinkIssuer header found." fi + [ -f "$CA_CERT_PATH" ] && _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" + [ -f "$CERT_FULLCHAIN_PATH" ] && _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" Le_CertCreateTime=$(_time) _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime" From 120cde169bd5c3b815994b6190ea830b3bdb7d70 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 27 Jan 2018 17:20:38 +0800 Subject: [PATCH 128/272] fix format --- acme.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index f64955d3..2438fd10 100755 --- a/acme.sh +++ b/acme.sh @@ -3972,12 +3972,12 @@ $_authorizations_map" if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then _debug "Found cert chain" - cat "$CERT_PATH" > "$CERT_FULLCHAIN_PATH" + cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH" _end_n="$(grep -n -- "$END_CERT" "$CERT_FULLCHAIN_PATH" | _head_n 1 | cut -d : -f 1)" _debug _end_n "$_end_n" - sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" > "$CERT_PATH" + sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" >"$CERT_PATH" _end_n="$(_math $_end_n + 1)" - sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" > "$CA_CERT_PATH" + sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH" fi else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then From a4964b90730621e0f6f0b8f4bdc4266b6a9383ff Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Thu, 1 Feb 2018 18:06:26 +0100 Subject: [PATCH 129/272] Add Readme-section about cert removal (#1137) --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ad3befcd..9b42fc76 100644 --- a/README.md +++ b/README.md @@ -409,7 +409,18 @@ acme.sh --renew -d example.com --force --ecc ``` -# 12. How to upgrade `acme.sh` +# 12. How to stop cert renewal + +To stop renewal of a cert, you can execute: + +``` +acme.sh --remove -d example.com [--ecc] +``` + +or remove the respective directory (e.g. `~/.acme.sh/example.com`). + + +# 13. How to upgrade `acme.sh` acme.sh is in constant development, so it's strongly recommended to use the latest code. @@ -434,26 +445,26 @@ acme.sh --upgrade --auto-upgrade 0 ``` -# 13. Issue a cert from an existing CSR +# 14. Issue a cert from an existing CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR -# 14. Under the Hood +# 15. Under the Hood Speak ACME language using shell, directly to "Let's Encrypt". TODO: -# 15. Acknowledgments +# 16. Acknowledgments 1. Acme-tiny: https://github.com/diafygi/acme-tiny 2. ACME protocol: https://github.com/ietf-wg-acme/acme 3. Certbot: https://github.com/certbot/certbot -# 16. License & Others +# 17. License & Others License is GPLv3 @@ -462,7 +473,7 @@ Please Star and Fork me. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. -# 17. Donate +# 18. Donate Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) From 47b49f1be93db798529ab6275aa77645a4fc7c86 Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Thu, 1 Feb 2018 18:07:29 +0100 Subject: [PATCH 130/272] Slightly improved --help output, fixes #1137 . --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 48297c7d..dbcc872f 100755 --- a/acme.sh +++ b/acme.sh @@ -5017,7 +5017,7 @@ Commands: --renew, -r Renew a cert. --renew-all Renew all the certs. --revoke Revoke a cert. - --remove Remove the cert from $PROJECT + --remove Remove the cert from list of certs known to $PROJECT_NAME. --list List all the certs. --showcsr Show the content of a csr. --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job. From a51f109930f34ecdbdc306d30e6ae05d7460bd7d Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 5 Feb 2018 20:28:50 +0800 Subject: [PATCH 131/272] fix https://github.com/Neilpang/acme.sh/issues/1234 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 522d9c53..4b80c120 100755 --- a/acme.sh +++ b/acme.sh @@ -5152,7 +5152,7 @@ install() { #Modify shebang if _exists bash; then _info "Good, bash is found, so change the shebang to use bash as preferred." - _shebang='#!'"$(env bash -c "command -v bash")" + _shebang='#!'"$(bash -c "command -v bash")" _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang" for subf in $_SUB_FOLDERS; do if [ -d "$LE_WORKING_DIR/$subf" ]; then From e27dfbb0bbafa277dd766f8bc2dcb527ffb5e369 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 5 Feb 2018 21:19:48 +0800 Subject: [PATCH 132/272] update doc --- README.md | 135 +++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 9ef1352f..c66b7f6c 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Ok, you are ready to issue certs now. Show help message: -``` +```sh root@v1:~# acme.sh -h ``` @@ -166,16 +166,16 @@ You must have at least one domain there. You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. -Generated/issued certs will be placed in `~/.acme.sh/example.com/` +The certs will be placed in `~/.acme.sh/example.com/` -The issued cert will be renewed automatically every **60** days. +The certs will be renewed automatically every **60** days. More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 3. Install the issued cert to Apache/Nginx etc. +# 3. Install the cert to Apache/Nginx etc. -After you issue a cert, you probably want to install/copy the cert to your Apache/Nginx or other servers. +After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers. You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future. **Apache** example: @@ -197,9 +197,9 @@ acme.sh --install-cert -d example.com \ Only the domain is required, all the other parameters are optional. -The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission. +The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission. -Install/copy the issued cert/key to the production Apache or Nginx path. +Install/copy the cert/key to the production Apache or Nginx path. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. @@ -242,7 +242,7 @@ Particularly, if you are running an Apache server, you should use Apache mode in Just set string "apache" as the second argument and it will force use of apache plugin automatically. -``` +```sh acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com ``` @@ -262,47 +262,13 @@ It will configure nginx server automatically to verify the domain and then resto So, the config is not changed. -``` +```sh acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 8. Use DNS mode: - -Support the `dns-01` challenge. - -```bash -acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com -``` - -You should get an output like below: - -``` -Add the following txt record: -Domain:_acme-challenge.example.com -Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c - -Add the following txt record: -Domain:_acme-challenge.www.example.com -Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - -Please add those txt records to the domains. Waiting for the dns to take effect. -``` - -Then just rerun with `renew` argument: - -```bash -acme.sh --renew -d example.com -``` - -Ok, it's finished. - -**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** - -**Please use dns api mode instead.** - -# 9. Automatic DNS API integration +# 8. Automatic DNS API integration If your DNS provider supports API access, we can use that API to automatically issue the certs. @@ -362,6 +328,39 @@ If your DNS provider is not on the supported list above, you can write your own For more details: [How to use DNS API](dnsapi) +# 9. Use DNS manual mode: + +If your dns provider doesn't support any api access, you will have to add the txt record by your hand. + +```bash +acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com +``` + +You should get an output like below: + +```sh +Add the following txt record: +Domain:_acme-challenge.example.com +Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c + +Add the following txt record: +Domain:_acme-challenge.www.example.com +Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +Please add those txt records to the domains. Waiting for the dns to take effect. +``` + +Then just rerun with `renew` argument: + +```bash +acme.sh --renew -d example.com +``` + +Ok, it's done. + +**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** + +**Please use dns api mode instead.** # 10. Issue ECC certificates @@ -394,47 +393,60 @@ Valid values are: 3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** -# 11. How to renew the issued certs -No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. +# 11. Issue Wildcard certificates -However, you can also force to renew any cert: +It's simple, just give a wildcard domain as the `-d` parameter. +```sh +acme.sh --issue -d example.com -d *.example.com --dns dns_cf ``` + + + +# 12. How to renew the certs + +No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. + +However, you can also force to renew a cert: + +```sh acme.sh --renew -d example.com --force ``` or, for ECC cert: -``` +```sh acme.sh --renew -d example.com --force --ecc ``` -# 12. How to stop cert renewal +# 13. How to stop cert renewal -To stop renewal of a cert, you can execute: +To stop renewal of a cert, you can execute the following to remove the cert from the renewal list: -``` +```sh acme.sh --remove -d example.com [--ecc] ``` -or remove the respective directory (e.g. `~/.acme.sh/example.com`). +The cert/key file is not removed from the disk. +You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. -# 13. How to upgrade `acme.sh` + +# 14. How to upgrade `acme.sh` acme.sh is in constant development, so it's strongly recommended to use the latest code. You can update acme.sh to the latest code: -``` +```sh acme.sh --upgrade ``` You can also enable auto upgrade: -``` +```sh acme.sh --upgrade --auto-upgrade ``` @@ -442,31 +454,30 @@ Then **acme.sh** will be kept up to date automatically. Disable auto upgrade: -``` +```sh acme.sh --upgrade --auto-upgrade 0 ``` -# 14. Issue a cert from an existing CSR +# 15. Issue a cert from an existing CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR -# 15. Under the Hood +# 16. Under the Hood Speak ACME language using shell, directly to "Let's Encrypt". TODO: -# 16. Acknowledgments +# 17. Acknowledgments 1. Acme-tiny: https://github.com/diafygi/acme-tiny 2. ACME protocol: https://github.com/ietf-wg-acme/acme -3. Certbot: https://github.com/certbot/certbot -# 17. License & Others +# 18. License & Others License is GPLv3 @@ -475,7 +486,7 @@ Please Star and Fork me. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. -# 18. Donate +# 19. Donate Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) From 7df20e5049e5e6d48ee5ae359d898876cef5dfb5 Mon Sep 17 00:00:00 2001 From: jim-p Date: Mon, 5 Feb 2018 16:37:57 -0500 Subject: [PATCH 133/272] When registering, consider a 200 final response code as an account that already exists. Fixes #1242 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4b80c120..266a2ccc 100755 --- a/acme.sh +++ b/acme.sh @@ -3170,7 +3170,7 @@ _regAccount() { if [ "$code" = "" ] || [ "$code" = '201' ]; then echo "$response" >"$ACCOUNT_JSON_PATH" _info "Registered" - elif [ "$code" = '409' ]; then + elif [ "$code" = '409' ] || [ "$code" = '200' ]; then _info "Already registered" else _err "Register account Error: $response" From 6b798b01a80ad0b44d664f62e467676ab31398bd Mon Sep 17 00:00:00 2001 From: jim-p Date: Mon, 5 Feb 2018 16:38:42 -0500 Subject: [PATCH 134/272] Add braces around ACCOUNT_URL when forming the protected= line. Fixes #1243 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4b80c120..c778e2e9 100755 --- a/acme.sh +++ b/acme.sh @@ -1787,7 +1787,7 @@ _send_signed_request() { if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' else - protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}' fi else protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' From 584fb2904b6b63cf13d71ba20fa184ade62730a0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 6 Feb 2018 21:23:08 +0800 Subject: [PATCH 135/272] fix https://github.com/Neilpang/acme.sh/issues/1191 https://github.com/Neilpang/acme.sh/issues/1246 --- acme.sh | 14 ++++++++++++- dnsapi/dns_inwx.sh | 50 +++------------------------------------------- 2 files changed, 16 insertions(+), 48 deletions(-) diff --git a/acme.sh b/acme.sh index 928a31f4..2a0a7ff0 100755 --- a/acme.sh +++ b/acme.sh @@ -3594,7 +3594,7 @@ $_authorizations_map" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then - _err "Error, can not get domain token $d" + _err "Error, can not get domain token entry $d" _clearup _on_issue_err "$_post_hook" return 1 @@ -3602,6 +3602,12 @@ $_authorizations_map" token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" + if [ -z "$token" ]; then + _err "Error, can not get domain token $entry" + _clearup + _on_issue_err "$_post_hook" + return 1 + fi if [ "$ACME_VERSION" = "2" ]; then uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" else @@ -3609,6 +3615,12 @@ $_authorizations_map" fi _debug uri "$uri" + if [ -z "$uri" ]; then + _err "Error, can not get domain uri. $entry" + _clearup + _on_issue_err "$_post_hook" + return 1 + fi keyauthorization="$token.$thumbprint" _debug keyauthorization "$keyauthorization" diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 74440bd7..5dfba7d1 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -35,53 +35,9 @@ dns_inwx_add() { fi _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - - xml_content=$(printf ' - - nameserver.info - - - - - - domain - - %s - - - - type - - TXT - - - - name - - %s - - - - - - - ' "$_domain" "$_sub_domain") - response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - - if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then - _err "Error could net get txt records" - return 1 - fi - if ! printf "%s" "$response" | grep "count" >/dev/null; then - _info "Adding record" - _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" - else - _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+') - _info "Updating record" - _inwx_update_record "$_record_id" "$txtvalue" - fi + _info "Adding record" + _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" } @@ -147,7 +103,7 @@ dns_inwx_rm() { ' "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then + if ! _contains "$response" "Command completed successfully"; then _err "Error could not get txt records" return 1 fi From 52b945164cb8d13bd0557d0e2e6177ded2a65369 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 20:54:05 +0800 Subject: [PATCH 136/272] fix https://github.com/Neilpang/acme.sh/issues/1247 --- dnsapi/dns_yandex.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index eb60d5af..a0d8a02c 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -18,7 +18,6 @@ dns_yandex_add() { curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" - curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${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}")" @@ -36,7 +35,6 @@ dns_yandex_rm() { curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" - curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" @@ -61,7 +59,7 @@ _PDD_get_domain() { __last=1 fi - __all_domains="$__all_domains $(echo "$res1" | sed -e "s@,@\n@g" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" + __all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" __page=$(_math $__page + 1) done @@ -72,6 +70,8 @@ _PDD_get_domain() { _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)" echo "$__t" return fi @@ -98,7 +98,6 @@ pdd_get_record_id() { curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" - curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}" | _normalizeJson)" From 7f59d7ea48f53cb31c0b4a709c3d1ce3cc90087f Mon Sep 17 00:00:00 2001 From: Skid Date: Wed, 7 Feb 2018 14:07:14 +0100 Subject: [PATCH 137/272] Fix the command to generate tsig key for knot api --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 32eca131..cabbc16f 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -354,7 +354,7 @@ acme.sh --issue --dns dns_gandi_livedns -d example.com -d www.example.com First, generate a TSIG key for updating the zone. ``` -keymgr tsig generate acme_key algorithm hmac-sha512 > /etc/knot/acme.key +keymgr tsig generate -t acme_key hmac-sha512 > /etc/knot/acme.key ``` Include this key in your knot configuration file. From 694af4aeb108386e9b41d0baa319fb9bfc111976 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 21:12:49 +0800 Subject: [PATCH 138/272] fix https://github.com/Neilpang/acme.sh/issues/1234 --- acme.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 2a0a7ff0..0874edc0 100755 --- a/acme.sh +++ b/acme.sh @@ -5163,8 +5163,14 @@ install() { if [ -z "$NO_DETECT_SH" ]; then #Modify shebang if _exists bash; then + _bash_path="$(bash -c "command -v bash 2>/dev/null")" + if [ -z "$_bash_path" ]; then + _bash_path="$(bash -c 'echo $SHELL')" + fi + fi + if [ "$_bash_path" ]; then _info "Good, bash is found, so change the shebang to use bash as preferred." - _shebang='#!'"$(bash -c "command -v bash")" + _shebang='#!'"$_bash_path" _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang" for subf in $_SUB_FOLDERS; do if [ -d "$LE_WORKING_DIR/$subf" ]; then From 3e3161c747a60e6782179a42affac11b073518bd Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 22:18:04 +0800 Subject: [PATCH 139/272] fix format --- dnsapi/dns_yandex.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index a0d8a02c..7ebb15dc 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -71,7 +71,7 @@ _PDD_get_domain() { for d in $__all_domains; do if [ "$d" = "$__t" ]; then p=$(_math $k - 1) - curSubdomain="$(echo "$fulldomain" | cut -d . -f 1-$p)" + curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")" echo "$__t" return fi From 78915896d5eed0eba9ffd65c2403315f7285452f Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 23:49:08 +0800 Subject: [PATCH 140/272] minor, fix error message --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0874edc0..a21b24e7 100755 --- a/acme.sh +++ b/acme.sh @@ -3321,7 +3321,7 @@ __get_domain_new_authz() { _err "new-authz retry reach the max $_Max_new_authz_retry_times times." fi - if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then + if [ "$code" ] && [ "$code" != '201' ]; then _err "new-authz error: $response" return 1 fi @@ -3501,7 +3501,7 @@ issue() { Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug Le_OrderFinalize "$Le_OrderFinalize" if [ -z "$Le_OrderFinalize" ]; then - _err "Le_OrderFinalize not found." + _err "Create new order error. Le_OrderFinalize not found. $response" _clearup _on_issue_err "$_post_hook" return 1 From da0bd5a9dc414c3b934371145fb8c1ce4073c661 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 09:03:55 +0800 Subject: [PATCH 141/272] begin 2.7.7 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index a21b24e7..907ab30d 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.6 +VER=2.7.7 PROJECT_NAME="acme.sh" From 875625b1477b4e82513c4e9576f3030ee792fc52 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 10:45:29 +0800 Subject: [PATCH 142/272] Support domain alias mode --- README.md | 1 + acme.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c66b7f6c..ef699080 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ https://github.com/Neilpang/acmetest - Apache mode - Nginx mode ( Beta ) - DNS mode +- [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode) - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) diff --git a/acme.sh b/acme.sh index 907ab30d..46e58d90 100755 --- a/acme.sh +++ b/acme.sh @@ -105,6 +105,8 @@ _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations" _STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode" +_DNS_ALIAS_WIKI="https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode" + _DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." _DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR" @@ -2845,8 +2847,9 @@ _clearupdns() { _debug "skip dns." return fi - + _info "Removing DNS records." ventries=$(echo "$vlist" | tr ',' ' ') + _alias_index=1 for ventry in $ventries; do d=$(echo "$ventry" | cut -d "$sep" -f 1) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) @@ -2860,7 +2863,7 @@ _clearupdns() { fi if [ "$vtype" != "$VTYPE_DNS" ]; then - _info "Skip $d for $vtype" + _debug "Skip $d for $vtype" continue fi @@ -2888,7 +2891,15 @@ _clearupdns() { if _startswith "$_dns_root_d" "*."; then _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" fi - txtdomain="_acme-challenge.$_dns_root_d" + + _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")" + _alias_index="$(_math "$_alias_index" + 1)" + _debug "_d_alias" "$_d_alias" + if [ "$_d_alias" ]; then + txtdomain="_acme-challenge.$_d_alias" + else + txtdomain="_acme-challenge.$_dns_root_d" + fi if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" @@ -3370,7 +3381,7 @@ issue() { _post_hook="${11}" _renew_hook="${12}" _local_addr="${13}" - + _challenge_alias="${14}" #remove these later. if [ "$_web_roots" = "dns-cf" ]; then _web_roots="dns_cf" @@ -3423,7 +3434,13 @@ issue() { else _cleardomainconf "Le_LocalAddress" fi - + if [ "$_challenge_alias" ]; then + _savedomainconf "Le_ChallengeAlias" "$_challenge_alias" + else + _cleardomainconf "Le_ChallengeAlias" + fi + + Le_API="$ACME_DIRECTORY" _savedomainconf "Le_API" "$Le_API" @@ -3640,6 +3657,7 @@ $_authorizations_map" #add entry dnsadded="" ventries=$(echo "$vlist" | tr "$dvsep" ' ') + _alias_index=1; for ventry in $ventries; do d=$(echo "$ventry" | cut -d "$sep" -f 1) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) @@ -3657,7 +3675,14 @@ $_authorizations_map" if _startswith "$_dns_root_d" "*."; then _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" fi - txtdomain="_acme-challenge.$_dns_root_d" + _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")" + _alias_index="$(_math "$_alias_index" + 1)" + _debug "_d_alias" "$_d_alias" + if [ "$_d_alias" ]; then + txtdomain="_acme-challenge.$_d_alias" + else + txtdomain="_acme-challenge.$_dns_root_d" + fi _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" @@ -4210,7 +4235,7 @@ renew() { fi IS_RENEW="1" - issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" + issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" "$Le_ChallengeAlias" res="$?" if [ "$res" != "0" ]; then return "$res" @@ -4274,6 +4299,17 @@ signcsr() { return 1 fi + _real_cert="$3" + _real_key="$4" + _real_ca="$5" + _reload_cmd="$6" + _real_fullchain="$7" + _pre_hook="${8}" + _post_hook="${9}" + _renew_hook="${10}" + _local_addr="${11}" + _challenge_alias="${12}" + _csrsubj=$(_readSubjectFromCSR "$_csrfile") if [ "$?" != "0" ]; then _err "Can not read subject from csr: $_csrfile" @@ -4319,7 +4355,7 @@ signcsr() { _info "Copy csr to: $CSR_PATH" cp "$_csrfile" "$CSR_PATH" - issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" + issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias" } @@ -5293,6 +5329,7 @@ Commands: Parameters: --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc. + --challenge-alias domain.tld The domain alis for DNS alias mode: $_DNS_ALIAS_WIKI --force, -f Used to force to install or force to renew a cert immediately. --staging, --test Use staging server, just for test. --debug Output debug info. @@ -5443,6 +5480,7 @@ _process() { _domain="" _altdomains="$NO_VALUE" _webroot="" + _challenge_alias="" _keylength="" _accountkeylength="" _cert_file="" @@ -5632,6 +5670,11 @@ _process() { fi shift ;; + --challenge-alias) + cvalue="$2" + _challenge_alias="$_challenge_alias$cvalue," + shift + ;; --standalone) wvalue="$NO_VALUE" if [ -z "$_webroot" ]; then @@ -5953,13 +5996,13 @@ _process() { uninstall) uninstall "$_nocron" ;; upgrade) upgrade ;; issue) - issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" + issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" ;; deploy) deploy "$_domain" "$_deploy_hook" "$_ecc" ;; signcsr) - signcsr "$_csr" "$_webroot" + signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" ;; showcsr) showcsr "$_csr" "$_domain" From 6ca5f3d8f6164cb17cc4670dd3f7a6983ad76e21 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 23:23:31 +0800 Subject: [PATCH 143/272] support Zonomi.com dns api: https://github.com/Neilpang/acme.sh/issues/1255 --- README.md | 2 +- acme.sh | 3 ++ dnsapi/README.md | 22 +++++++++++ dnsapi/dns_zonomi.sh | 87 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_zonomi.sh diff --git a/README.md b/README.md index c66b7f6c..d6b2c555 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,7 @@ You don't have to do anything manually! 1. InternetX autoDNS API (https://internetx.com) 1. Azure DNS 1. selectel.com(selectel.ru) DNS API - +1. zonomi.com DNS API And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/acme.sh b/acme.sh index 907ab30d..ebddf81d 100755 --- a/acme.sh +++ b/acme.sh @@ -1561,6 +1561,9 @@ _inithttp() { _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE " fi + if _contains "$(curl --help 2>&1)" "--globoff"; then + _ACME_CURL="$_ACME_CURL -g " + fi fi if [ -z "$_ACME_WGET" ] && _exists "wget"; then diff --git a/dnsapi/README.md b/dnsapi/README.md index 32eca131..5ded260c 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -719,6 +719,28 @@ acme.sh --issue --dns dns_selectel -d example.com -d www.example.com The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 39. Use zonomi.com domain API to automatically issue cert + +First you need to login to your account to find your API key from: http://zonomi.com/app/dns/dyndns.jsp + +Your will find your api key in the example urls: + +```sh +https://zonomi.com/app/dns/dyndns.jsp?host=example.com&api_key=1063364558943540954358668888888888 +``` + +```sh +export ZM_Key="1063364558943540954358668888888888" + +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_zonomi -d example.com -d www.example.com +``` + +The `ZM_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh new file mode 100644 index 00000000..e3a5f440 --- /dev/null +++ b/dnsapi/dns_zonomi.sh @@ -0,0 +1,87 @@ +#!/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" "' | 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 "action" "$action" + _debug2 "qstr" "$qstr" + + _zm_url="$ZM_Api?api_key=$ZM_Key&$qstr" + _debug2 "_zm_url" "$_zm_url" + response="$(_get "$_zm_url")" + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + _contains "$response" "OK:" +} From 0159277dbf8c73e7c1fce21fcbba92ef991b46bf Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 23:24:43 +0800 Subject: [PATCH 144/272] fix format --- dnsapi/dns_zonomi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh index e3a5f440..efc9b1fd 100644 --- a/dnsapi/dns_zonomi.sh +++ b/dnsapi/dns_zonomi.sh @@ -31,7 +31,7 @@ dns_zonomi_add() { _err "error" return 1 fi - + if _contains "$response" " Date: Sat, 10 Feb 2018 23:34:34 +0800 Subject: [PATCH 145/272] fix format --- dnsapi/dns_zonomi.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh index efc9b1fd..52a889ea 100644 --- a/dnsapi/dns_zonomi.sh +++ b/dnsapi/dns_zonomi.sh @@ -35,7 +35,7 @@ dns_zonomi_add() { if _contains "$response" "' | 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" @@ -71,7 +71,6 @@ dns_zonomi_rm() { _zm_request() { qstr="$1" - _debug2 "action" "$action" _debug2 "qstr" "$qstr" _zm_url="$ZM_Api?api_key=$ZM_Key&$qstr" @@ -79,7 +78,6 @@ _zm_request() { response="$(_get "$_zm_url")" if [ "$?" != "0" ]; then - _err "error $ep" return 1 fi _debug2 response "$response" From 2c83224f07ff1a29cbf1ea88cbf09aac1ccd36ac Mon Sep 17 00:00:00 2001 From: Martin Donlon Date: Sun, 11 Feb 2018 07:37:15 -0800 Subject: [PATCH 146/272] Fixup dns_dreamhost travis failures --- README.md | 1 + dnsapi/README.md | 14 +++++- dnsapi/dns_dreamhost.sh | 97 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_dreamhost.sh diff --git a/README.md b/README.md index c66b7f6c..e7b62f7a 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ You don't have to do anything manually! 1. Dynu API (https://www.dynu.com) 1. DNSimple API 1. NS1.com API +1. DreamHost.com API 1. DuckDNS.org API 1. Name.com API 1. Dyn Managed DNS API diff --git a/dnsapi/README.md b/dnsapi/README.md index 32eca131..aef528eb 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -512,7 +512,7 @@ acme.sh --issue --dns dns_nsone -d example.com -d www.example.com export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" ``` -Please note that since DuckDNS uses StartSSL as their cert provider, thus +Please note that since DuckDNS uses StartSSL as their cert provider, thus --insecure may need to be used when issuing certs: ``` acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org @@ -719,6 +719,18 @@ acme.sh --issue --dns dns_selectel -d example.com -d www.example.com The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 39. Use DreamHost DNS API + +DNS API keys may be created at https://panel.dreamhost.com/?tree=home.api. +Ensure the created key has add and remove privelages. + +``` +export DH_API_Key="" +acme.sh --issue --dns dns_dreamhost -d example.com -d www.example.com +``` + +The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will +be reused when needed. # Use custom API diff --git a/dnsapi/dns_dreamhost.sh b/dnsapi/dns_dreamhost.sh new file mode 100644 index 00000000..35b34443 --- /dev/null +++ b/dnsapi/dns_dreamhost.sh @@ -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="$(printf "%s" "$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=\"\")" + _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" +} From 012dd6986b259f73ef82610084fc393b841e07d1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 12 Feb 2018 20:01:40 +0800 Subject: [PATCH 147/272] nginx --- acme.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index ebddf81d..a4224fc9 100755 --- a/acme.sh +++ b/acme.sh @@ -2763,9 +2763,9 @@ _isRealNginxConf() { _left="$(sed -n "${_start_nn},99999p" "$2")" _debug2 _left "$_left" - if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" >/dev/null; then - _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | _head_n 1) - _debug "_end" "$_end" + _end="$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | grep -v server_name | _head_n 1)" + _debug "_end" "$_end" + if [ "$_end" ]; then _end_n=$(echo "$_end" | cut -d : -f 1) _debug "_end_n" "$_end_n" _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p") From 2655e726c9dc2fee656bc642ea0e8e5c7aa866e6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 12 Feb 2018 20:40:24 +0800 Subject: [PATCH 148/272] update dns he --- README.md | 7 +++++++ dnsapi/README.md | 2 +- dnsapi/dns_he.sh | 9 ++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6b2c555..9e3ec1a5 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,13 @@ You don't have to do anything manually! 1. Azure DNS 1. selectel.com(selectel.ru) DNS API 1. zonomi.com DNS API + + + + + + + And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/dnsapi/README.md b/dnsapi/README.md index 5ded260c..fa0780b0 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -585,7 +585,7 @@ For issues, please report to https://github.com/non7top/acme.sh/issues. ## 31. Use Hurricane Electric -Hurricane Electric doesn't have an API so just set your login credentials like so: +Hurricane Electric (https://dns.he.net/) doesn't have an API so just set your login credentials like so: ``` export HE_Username="yourusername" diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 4d1973ad..7b854ead 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -19,14 +19,16 @@ dns_he_add() { _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 HE_Username "$HE_Username" - _saveaccountconf HE_Password "$HE_Password" + _saveaccountconf_mutable HE_Username "$HE_Username" + _saveaccountconf_mutable HE_Password "$HE_Password" # Fills in the $_zone_id _find_zone "$_full_domain" || return 1 @@ -62,7 +64,8 @@ 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." From 64821ad4f594786093f05afc36c35901e408ff00 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 12 Feb 2018 21:49:22 +0800 Subject: [PATCH 149/272] support "--domain-alias" --- acme.sh | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 46e58d90..b04270a2 100755 --- a/acme.sh +++ b/acme.sh @@ -47,6 +47,7 @@ DEFAULT_DNS_SLEEP=120 NO_VALUE="no" W_TLS="tls" +DNS_ALIAS_PREFIX="=" MODE_STATELESS="stateless" @@ -2896,7 +2897,11 @@ _clearupdns() { _alias_index="$(_math "$_alias_index" + 1)" _debug "_d_alias" "$_d_alias" if [ "$_d_alias" ]; then - txtdomain="_acme-challenge.$_d_alias" + if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then + txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")" + else + txtdomain="_acme-challenge.$_d_alias" + fi else txtdomain="_acme-challenge.$_dns_root_d" fi @@ -3679,7 +3684,11 @@ $_authorizations_map" _alias_index="$(_math "$_alias_index" + 1)" _debug "_d_alias" "$_d_alias" if [ "$_d_alias" ]; then - txtdomain="_acme-challenge.$_d_alias" + if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then + txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")" + else + txtdomain="_acme-challenge.$_d_alias" + fi else txtdomain="_acme-challenge.$_dns_root_d" fi @@ -5329,7 +5338,8 @@ Commands: Parameters: --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc. - --challenge-alias domain.tld The domain alis for DNS alias mode: $_DNS_ALIAS_WIKI + --challenge-alias domain.tld The challenge domain alias for DNS alias mode: $_DNS_ALIAS_WIKI + --domain-alias domain.tld The domain alias for DNS alias mode: $_DNS_ALIAS_WIKI --force, -f Used to force to install or force to renew a cert immediately. --staging, --test Use staging server, just for test. --debug Output debug info. @@ -5675,6 +5685,11 @@ _process() { _challenge_alias="$_challenge_alias$cvalue," shift ;; + --domain-alias) + cvalue="$DNS_ALIAS_PREFIX$2" + _challenge_alias="$_challenge_alias$cvalue," + shift + ;; --standalone) wvalue="$NO_VALUE" if [ -z "$_webroot" ]; then From 9144ce746e0e138c0eb34fc05f6efdb3c811375c Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 19:30:54 +0800 Subject: [PATCH 150/272] fix for v2 wildcard --- dnsapi/dns_aws.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index ed317460..bbc54284 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -42,7 +42,7 @@ dns_aws_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _aws_tmpl_xml="UPSERT$fulldomainTXT300\"$txtvalue\"" + _aws_tmpl_xml="CREATE$fulldomainTXT300\"$txtvalue\"" if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "txt record updated success." From 849a6c12be49ae3d03a8b6068451b241b3c5f284 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 20:08:05 +0800 Subject: [PATCH 151/272] fix for acme v2 --- dnsapi/dns_cx.sh | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index e2f0f099..d27cd841 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -36,33 +36,18 @@ dns_cx_add() { return 1 fi - existing_records "$_domain" "$_sub_domain" - _debug count "$count" - if [ "$?" != "0" ]; then - _err "Error get existing records." - return 1 - fi - - if [ "$count" = "0" ]; then - add_record "$_domain" "$_sub_domain" "$txtvalue" - else - update_record "$_domain" "$_sub_domain" "$txtvalue" - fi - - if [ "$?" = "0" ]; then - return 0 - fi - return 1 + add_record "$_domain" "$_sub_domain" "$txtvalue" } -#fulldomain +#fulldomain txtvalue dns_cx_rm() { fulldomain=$1 + txtvalue=$2 REST_API="$CX_Api" if _get_root "$fulldomain"; then record_id="" - existing_records "$_domain" "$_sub_domain" - if ! [ "$record_id" = "" ]; then + existing_records "$_domain" "$_sub_domain" "$txtvalue" + if [ "$record_id" ]; then _rest DELETE "record/$record_id/$_domain_id" "{}" _info "Deleted record ${fulldomain}" fi @@ -114,23 +99,6 @@ add_record() { return 0 } -#update the txt record -#Usage: root sub txtvalue -update_record() { - root=$1 - sub=$2 - txtvalue=$3 - fulldomain="$sub.$root" - - _info "Updating record" - - if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then - return 0 - fi - - return 1 -} - #################### Private functions below ################################## #_acme-challenge.www.domain.com #returns From 64f07d9bf39be06b53475e4219ceb83530345611 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 22:17:20 +0800 Subject: [PATCH 152/272] fix aws for acme v2 --- dnsapi/dns_aws.sh | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index bbc54284..33e7e707 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -42,7 +42,26 @@ dns_aws_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _aws_tmpl_xml="CREATE$fulldomainTXT300\"$txtvalue\"" + _info "Geting existing records for $fulldomain" + if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then + return 1 + fi + + if _contains "$response" "$fulldomain."; then + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _debug "_resource_record" "$_resource_record" + else + _debug "single new add" + fi + + if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then + _info "The txt record already exists, skip" + return 0 + fi + + _debug "Adding records" + + _aws_tmpl_xml="UPSERT$fulldomainTXT300$_resource_record\"$txtvalue\"" if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "txt record updated success." @@ -68,7 +87,20 @@ dns_aws_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _aws_tmpl_xml="DELETE\"$txtvalue\"$fulldomain.TXT300" + _info "Geting existing records for $fulldomain" + if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then + return 1 + fi + + if _contains "$response" "$fulldomain."; then + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _debug "_resource_record" "$_resource_record" + else + _debug "no records exists, skip" + return 0 + fi + + _aws_tmpl_xml="DELETE$_resource_record$fulldomain.TXT300" if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "txt record deleted success." @@ -87,7 +119,6 @@ _get_root() { p=1 if aws_rest GET "2013-04-01/hostedzone"; then - _debug "response" "$response" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug2 "Checking domain: $h" @@ -236,6 +267,7 @@ aws_rest() { fi _ret="$?" + _debug2 response "$response" if [ "$_ret" = "0" ]; then if _contains "$response" " Date: Tue, 13 Feb 2018 22:23:36 +0800 Subject: [PATCH 153/272] fix format --- dnsapi/dns_aws.sh | 4 ++-- dnsapi/dns_cx.sh | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 33e7e707..ee8efb38 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -48,7 +48,7 @@ dns_aws_add() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "single new add" @@ -93,7 +93,7 @@ dns_aws_rm() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "no records exists, skip" diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index d27cd841..b3e04032 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -74,7 +74,6 @@ existing_records() { fi if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then - count=1 record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1) _debug record_id "$record_id" return 0 From 1f7df33e289001d72c2637411ce3df499cf4c171 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 22:26:36 +0800 Subject: [PATCH 154/272] fix format --- acme.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index b04270a2..ce00aed6 100755 --- a/acme.sh +++ b/acme.sh @@ -3444,8 +3444,7 @@ issue() { else _cleardomainconf "Le_ChallengeAlias" fi - - + Le_API="$ACME_DIRECTORY" _savedomainconf "Le_API" "$Le_API" @@ -3662,7 +3661,7 @@ $_authorizations_map" #add entry dnsadded="" ventries=$(echo "$vlist" | tr "$dvsep" ' ') - _alias_index=1; + _alias_index=1 for ventry in $ventries; do d=$(echo "$ventry" | cut -d "$sep" -f 1) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) @@ -5689,7 +5688,7 @@ _process() { cvalue="$DNS_ALIAS_PREFIX$2" _challenge_alias="$_challenge_alias$cvalue," shift - ;; + ;; --standalone) wvalue="$NO_VALUE" if [ -z "$_webroot" ]; then From 84649e9d20603e402462b09fd1481371cd1b5ebc Mon Sep 17 00:00:00 2001 From: Martin Donlon Date: Tue, 13 Feb 2018 21:02:38 -0800 Subject: [PATCH 155/272] Addressing PR feedback Replace printf with echo Move dreamhost to bottom of DNS API list --- README.md | 2 +- dnsapi/dns_dreamhost.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6c73ba5..5e3709a1 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,6 @@ You don't have to do anything manually! 1. Dynu API (https://www.dynu.com) 1. DNSimple API 1. NS1.com API -1. DreamHost.com API 1. DuckDNS.org API 1. Name.com API 1. Dyn Managed DNS API @@ -316,6 +315,7 @@ You don't have to do anything manually! 1. Azure DNS 1. selectel.com(selectel.ru) DNS API 1. zonomi.com DNS API +1. DreamHost.com API And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/dnsapi/dns_dreamhost.sh b/dnsapi/dns_dreamhost.sh index 35b34443..a4017938 100644 --- a/dnsapi/dns_dreamhost.sh +++ b/dnsapi/dns_dreamhost.sh @@ -61,7 +61,7 @@ submit() { fi if [ -z "$2" ]; then - message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" + message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" if [ -n "$message" ]; then _err "$message" return 1 From 7b92371a035bb013746bbffb0d62858850f2690b Mon Sep 17 00:00:00 2001 From: Jose Luis Duran Date: Wed, 14 Feb 2018 06:44:06 -0200 Subject: [PATCH 156/272] Fix key file permissions Introduced in 8201458332ea5898177118097621dbac842ad64f. Related to #1256. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index a4224fc9..b9e57a7b 100755 --- a/acme.sh +++ b/acme.sh @@ -4545,7 +4545,7 @@ _installcert() { cat "$CERT_KEY_PATH" >"$_real_key" else cat "$CERT_KEY_PATH" >"$_real_key" - chmod 700 "$_real_key" + chmod 600 "$_real_key" fi fi From 5f345d208939bad2408c7e652646dc42ef5bf6d4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 19:39:47 +0800 Subject: [PATCH 157/272] fix https://github.com/Neilpang/acme.sh/issues/1262 --- dnsapi/dns_aws.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index ee8efb38..71f969f0 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -48,7 +48,7 @@ dns_aws_add() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | sed 's//"/g' | tr '"' "\n" | grep "$fulldomain." | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "single new add" @@ -93,7 +93,7 @@ dns_aws_rm() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | sed 's//"/g' | tr '"' "\n" | grep "$fulldomain." | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "no records exists, skip" From 28145a9debc32232ebfe987fe73bec189ac2bf30 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 20:40:49 +0800 Subject: [PATCH 158/272] fix ovh --- dnsapi/dns_cx.sh | 1 - dnsapi/dns_ovh.sh | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index b3e04032..f2d3eadb 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -62,7 +62,6 @@ existing_records() { _debug "Getting txt records" root=$1 sub=$2 - count=0 if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then return 1 fi diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 60094739..296a2698 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -79,6 +79,9 @@ _ovh_get_api() { } _initAuth() { + OVH_AK="${OVH_AK:-$(_readaccountconf_mutable OVH_AK)}" + OVH_AS="${OVH_AS:-$(_readaccountconf_mutable OVH_AS)}" + if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" OVH_AS="" @@ -87,21 +90,22 @@ _initAuth() { return 1 fi - #save the api key and email to the account conf file. - _saveaccountconf OVH_AK "$OVH_AK" - _saveaccountconf OVH_AS "$OVH_AS" + _saveaccountconf_mutable OVH_AK "$OVH_AK" + _saveaccountconf_mutable OVH_AS "$OVH_AS" + OVH_END_POINT="${OVH_END_POINT:-$(_readaccountconf_mutable OVH_END_POINT)}" if [ -z "$OVH_END_POINT" ]; then OVH_END_POINT="ovh-eu" fi _info "Using OVH endpoint: $OVH_END_POINT" if [ "$OVH_END_POINT" != "ovh-eu" ]; then - _saveaccountconf OVH_END_POINT "$OVH_END_POINT" + _saveaccountconf_mutable OVH_END_POINT "$OVH_END_POINT" fi OVH_API="$(_ovh_get_api $OVH_END_POINT)" _debug OVH_API "$OVH_API" + OVH_CK="${OVH_CK:-$(_readaccountconf_mutable OVH_CK)}" if [ -z "$OVH_CK" ]; then _info "OVH consumer key is empty, Let's get one:" if ! _ovh_authentication; then From a6b6e31cdaf05f0f18d7be98c488a8fe13624376 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 20:52:06 +0800 Subject: [PATCH 159/272] fix dp --- dnsapi/dns_dp.sh | 75 ++++++------------------------------------------ 1 file changed, 9 insertions(+), 66 deletions(-) diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index 301a1f6c..bf623e26 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -15,6 +15,8 @@ dns_dp_add() { fulldomain=$1 txtvalue=$2 + DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}" + DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}" if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then DP_Id="" DP_Key="" @@ -24,8 +26,8 @@ dns_dp_add() { fi #save the api key and email to the account conf file. - _saveaccountconf DP_Id "$DP_Id" - _saveaccountconf DP_Key "$DP_Key" + _saveaccountconf_mutable DP_Id "$DP_Id" + _saveaccountconf_mutable DP_Key "$DP_Key" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -33,24 +35,18 @@ dns_dp_add() { return 1 fi - existing_records "$_domain" "$_sub_domain" - _debug count "$count" - if [ "$?" != "0" ]; then - _err "Error get existing records." - return 1 - fi + add_record "$_domain" "$_sub_domain" "$txtvalue" - if [ "$count" = "0" ]; then - add_record "$_domain" "$_sub_domain" "$txtvalue" - else - update_record "$_domain" "$_sub_domain" "$txtvalue" - fi } #fulldomain txtvalue dns_dp_rm() { fulldomain=$1 txtvalue=$2 + + DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}" + DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}" + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -83,37 +79,6 @@ dns_dp_rm() { } -#usage: root sub -#return if the sub record already exists. -#echos the existing records count. -# '0' means doesn't exist -existing_records() { - _debug "Getting txt records" - root=$1 - sub=$2 - - if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then - return 1 - fi - - if _contains "$response" 'No records'; then - count=0 - return 0 - fi - - if _contains "$response" "Action completed successful"; then - count=$(printf "%s" "$response" | grep -c 'TXT' | tr -d ' ') - record_id=$(printf "%s" "$response" | grep '^' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1) - _debug record_id "$record_id" - return 0 - else - _err "get existing records error." - return 1 - fi - - count=0 -} - #add the txt record. #usage: root sub txtvalue add_record() { @@ -136,28 +101,6 @@ add_record() { return 1 #error } -#update the txt record -#Usage: root sub txtvalue -update_record() { - root=$1 - sub=$2 - txtvalue=$3 - fulldomain="$sub.$root" - - _info "Updating record" - - if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then - return 1 - fi - - if _contains "$response" "Action completed successful"; then - - return 0 - fi - - return 1 #error -} - #################### Private functions below ################################## #_acme-challenge.www.domain.com #returns From c6f5c7f1a3bd48bcbe4193b363f2a6b82add58b2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 22:31:02 +0800 Subject: [PATCH 160/272] fix gd --- dnsapi/dns_gd.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh index f2dd1fd5..0e25f9d8 100755 --- a/dnsapi/dns_gd.sh +++ b/dnsapi/dns_gd.sh @@ -15,6 +15,8 @@ dns_gd_add() { fulldomain=$1 txtvalue=$2 + GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}" + GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}" if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then GD_Key="" GD_Secret="" @@ -24,8 +26,8 @@ dns_gd_add() { fi #save the api key and email to the account conf file. - _saveaccountconf GD_Key "$GD_Key" - _saveaccountconf GD_Secret "$GD_Secret" + _saveaccountconf_mutable GD_Key "$GD_Key" + _saveaccountconf_mutable GD_Secret "$GD_Secret" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -36,8 +38,27 @@ dns_gd_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" + _debug "Getting existing records" + if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then + return 1 + fi + + if _contains "$response" "$txtvalue"; then + _info "The record is existing, skip" + return 0; + fi + + _add_data="{\"data\":\"$txtvalue\"}" + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + _debug2 t "$t" + if [ "$t" ]; then + _add_data="$_add_data,{\"data\":$t}" + fi + done + _debug2 _add_data "$_add_data" + _info "Adding record" - if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then + if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then if [ "$response" = "{}" ]; then _info "Added, sleeping 10 seconds" _sleep 10 @@ -56,7 +77,47 @@ dns_gd_add() { #fulldomain dns_gd_rm() { fulldomain=$1 + txtvalue=$2 + + GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}" + GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting existing records" + if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then + return 1 + fi + + if ! _contains "$response" "$txtvalue"; then + _info "The record is not existing, skip" + return 0; + fi + + _add_data="" + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + _debug2 t "$t" + if [ "$t" ] && [ "$t" != "\"$txtvalue\"" ]; then + if [ "$_add_data" ]; then + _add_data="$_add_data,{\"data\":$t}" + else + _add_data="{\"data\":$t}" + fi + fi + done + if [ -z "$_add_data" ]; then + _add_data="{\"data\":\"\"}" + fi + _debug2 _add_data "$_add_data" + _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; } #################### Private functions below ################################## From d8eb08e21405dc65dc2d9312ec8e040fd016e565 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 22:36:17 +0800 Subject: [PATCH 161/272] fix format --- dnsapi/dns_gd.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh index 0e25f9d8..5fb1b174 100755 --- a/dnsapi/dns_gd.sh +++ b/dnsapi/dns_gd.sh @@ -45,11 +45,11 @@ dns_gd_add() { if _contains "$response" "$txtvalue"; then _info "The record is existing, skip" - return 0; + return 0 fi _add_data="{\"data\":\"$txtvalue\"}" - for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do _debug2 t "$t" if [ "$t" ]; then _add_data="$_add_data,{\"data\":$t}" @@ -98,11 +98,11 @@ dns_gd_rm() { if ! _contains "$response" "$txtvalue"; then _info "The record is not existing, skip" - return 0; + return 0 fi _add_data="" - for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do _debug2 t "$t" if [ "$t" ] && [ "$t" != "\"$txtvalue\"" ]; then if [ "$_add_data" ]; then @@ -117,7 +117,7 @@ dns_gd_rm() { fi _debug2 _add_data "$_add_data" - _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; + _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]" } #################### Private functions below ################################## From b51ed9bbb74356fa54e83b86204232499ccb5edd Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 10:29:03 +0800 Subject: [PATCH 162/272] https://github.com/Neilpang/acme.sh/issues/1251 --- acme.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acme.sh b/acme.sh index b9e57a7b..8cd9c594 100755 --- a/acme.sh +++ b/acme.sh @@ -3598,6 +3598,10 @@ $_authorizations_map" _debug entry "$entry" if [ -z "$entry" ]; then _err "Error, can not get domain token entry $d" + _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" + if [ "$_supported_vtypes" ]; then + _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype" + fi _clearup _on_issue_err "$_post_hook" return 1 From ce6c7d4b594ccc0ecc253c459ffa8020fb1ec447 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 10:51:13 +0800 Subject: [PATCH 163/272] fix dp --- dnsapi/dns_dp.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index bf623e26..3cc720aa 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -93,12 +93,7 @@ add_record() { return 1 fi - if _contains "$response" "Action completed successful"; then - - return 0 - fi - - return 1 #error + _contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists" } #################### Private functions below ################################## From 0096ef4ddb5e6b86c512a6a4a57afc62ba725701 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 12:26:35 +0800 Subject: [PATCH 164/272] fix ali --- dnsapi/dns_ali.sh | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh index f796f076..543a0a54 100755 --- a/dnsapi/dns_ali.sh +++ b/dnsapi/dns_ali.sh @@ -10,6 +10,8 @@ dns_ali_add() { fulldomain=$1 txtvalue=$2 + Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}" + Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}" if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then Ali_Key="" Ali_Secret="" @@ -18,8 +20,8 @@ dns_ali_add() { fi #save the api key and secret to the account conf file. - _saveaccountconf Ali_Key "$Ali_Key" - _saveaccountconf Ali_Secret "$Ali_Secret" + _saveaccountconf_mutable Ali_Key "$Ali_Key" + _saveaccountconf_mutable Ali_Secret "$Ali_Secret" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -32,6 +34,15 @@ dns_ali_add() { dns_ali_rm() { fulldomain=$1 + txtvalue=$2 + Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}" + Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + return 1 + fi + _clean } @@ -76,16 +87,14 @@ _ali_rest() { return 1 fi + _debug2 response "$response" if [ -z "$2" ]; then - message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" - if [ -n "$message" ]; then + message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" + if [ "$message" ]; then _err "$message" return 1 fi fi - - _debug2 response "$response" - return 0 } _ali_urlencode() { @@ -112,12 +121,14 @@ _ali_nonce() { } _check_exist_query() { + _qdomain="$1" + _qsubdomain="$2" query='' query=$query'AccessKeyId='$Ali_Key query=$query'&Action=DescribeDomainRecords' - query=$query'&DomainName='$1 + query=$query'&DomainName='$_qdomain query=$query'&Format=json' - query=$query'&RRKeyWord=_acme-challenge' + query=$query'&RRKeyWord='$_qsubdomain query=$query'&SignatureMethod=HMAC-SHA1' query=$query"&SignatureNonce=$(_ali_nonce)" query=$query'&SignatureVersion=1.0' @@ -169,17 +180,21 @@ _describe_records_query() { } _clean() { - _check_exist_query "$_domain" + _check_exist_query "$_domain" "$_sub_domain" if ! _ali_rest "Check exist records" "ignore"; then return 1 fi - records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" - printf "%s" "$records" \ - | while read -r record_id; do - _delete_record_query "$record_id" - _ali_rest "Delete record $record_id" "ignore" - done + record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)" + _debug2 record_id "$record_id" + + if [ -z "$record_id" ]; then + _debug "record not found, skip" + else + _delete_record_query "$record_id" + _ali_rest "Delete record $record_id" "ignore" + fi + } _timestamp() { From f213215c81c2b3df4ea18cdc0ac86e757099c050 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 12:38:45 +0800 Subject: [PATCH 165/272] fix lua --- dnsapi/dns_lua.sh | 50 +++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index 00c54430..be678b9d 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -17,6 +17,8 @@ dns_lua_add() { fulldomain=$1 txtvalue=$2 + LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" + LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then LUA_Key="" LUA_Email="" @@ -26,8 +28,8 @@ dns_lua_add() { fi #save the api key and email to the account conf file. - _saveaccountconf LUA_Key "$LUA_Key" - _saveaccountconf LUA_Email "$LUA_Email" + _saveaccountconf_mutable LUA_Key "$LUA_Key" + _saveaccountconf_mutable LUA_Email "$LUA_Email" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -38,50 +40,26 @@ dns_lua_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - _LUA_rest GET "zones/${_domain_id}/records" - - if ! _contains "$response" "\"id\":"; then - _err "Error" - return 1 - fi - - count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ") - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" - if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; 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" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1) - _debug "record_id" "$record_id" - - _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}" - if [ "$?" = "0" ] && _contains "$response" "updated_at"; then - _info "Updated!" + _info "Adding record" + if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; 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 - _err "Update error" - return 1 fi - } #fulldomain dns_lua_rm() { fulldomain=$1 txtvalue=$2 + + LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" + LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" From abd0dad2bf500885f2fdb005b51c43f76380fcf9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 20:35:31 +0800 Subject: [PATCH 166/272] fix https://github.com/Neilpang/acme.sh/issues/1145#issuecomment-365863118 --- dnsapi/dns_ovh.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 296a2698..2669cc86 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -90,6 +90,10 @@ _initAuth() { return 1 fi + if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then + _info "It seems that your ovh key is changed, let's clear consumer key first." + _clearaccountconf OVH_CK + fi _saveaccountconf_mutable OVH_AK "$OVH_AK" _saveaccountconf_mutable OVH_AS "$OVH_AS" From a63766a0050065b90596eec429c93015a6099e3f Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 21:04:53 +0800 Subject: [PATCH 167/272] fix format --- README.md | 6 +++--- acme.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 66cac020..db2e024e 100644 --- a/README.md +++ b/README.md @@ -317,13 +317,13 @@ You don't have to do anything manually! 1. zonomi.com DNS API 1. DreamHost.com API + And: -1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api - (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) +**lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api + (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)** - **More APIs coming soon...** If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. diff --git a/acme.sh b/acme.sh index 8cd9c594..2be989f2 100755 --- a/acme.sh +++ b/acme.sh @@ -3598,7 +3598,7 @@ $_authorizations_map" _debug entry "$entry" if [ -z "$entry" ]; then _err "Error, can not get domain token entry $d" - _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" + _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" if [ "$_supported_vtypes" ]; then _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype" fi From 90e587a9742569f2ba0752ac2d18a553869c05a4 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Thu, 15 Feb 2018 15:34:47 -0500 Subject: [PATCH 168/272] add vault deploy hook script --- deploy/vault.sh | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 deploy/vault.sh diff --git a/deploy/vault.sh b/deploy/vault.sh new file mode 100644 index 00000000..b95d8af5 --- /dev/null +++ b/deploy/vault.sh @@ -0,0 +1,59 @@ +#!/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_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 + +} From d5865989cfb0a424362b364f04d5f88a5b264e35 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 10:48:25 +0800 Subject: [PATCH 169/272] update doc --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index db2e024e..220168b7 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ https://github.com/Neilpang/acmetest - Webroot mode - Standalone mode - Apache mode -- Nginx mode ( Beta ) +- Nginx mode - DNS mode - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) @@ -238,7 +238,7 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. -Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder. +Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder. Just set string "apache" as the second argument and it will force use of apache plugin automatically. @@ -246,6 +246,10 @@ Just set string "apache" as the second argument and it will force use of apache acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com ``` +**This apache mode is only to issue the cert, it will not change your apache config files. +You will need to configure your website config files to use the cert by yourself. +We don't want to mess your apache server, don't worry.** + More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert # 7. Use Nginx mode @@ -266,6 +270,10 @@ So, the config is not changed. acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` +**This apache mode is only to issue the cert, it will not change your apache config files. +You will need to configure your website config files to use the cert by yourself. +We don't want to mess your apache server, don't worry.** + More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert # 8. Automatic DNS API integration @@ -332,7 +340,7 @@ For more details: [How to use DNS API](dnsapi) # 9. Use DNS manual mode: -If your dns provider doesn't support any api access, you will have to add the txt record by your hand. +If your dns provider doesn't support any api access, you can add the txt record by your hand. ```bash acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com @@ -370,7 +378,7 @@ Ok, it's done. And we support them too! -Just set the `length` parameter with a prefix `ec-`. +Just set the `keylength` parameter with a prefix `ec-`. For example: @@ -386,7 +394,7 @@ acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 ``` -Please look at the last parameter above. +Please look at the `keylength` parameter above. Valid values are: From c1f5229906414e78d24330568409c9d91a4fef7a Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 11:21:14 +0800 Subject: [PATCH 170/272] update doc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 220168b7..1f9002c9 100644 --- a/README.md +++ b/README.md @@ -270,9 +270,9 @@ So, the config is not changed. acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` -**This apache mode is only to issue the cert, it will not change your apache config files. +**This nginx mode is only to issue the cert, it will not change your nginx config files. You will need to configure your website config files to use the cert by yourself. -We don't want to mess your apache server, don't worry.** +We don't want to mess your nginx server, don't worry.** More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert From fac0beaa0ab48a6e63b08474c66b3159dd748bca Mon Sep 17 00:00:00 2001 From: Jose Luis Duran Date: Fri, 16 Feb 2018 11:23:10 -0200 Subject: [PATCH 171/272] Add support for strongSwan deploys in FreeBSD Related to 8ea800205c2e5496b63e3244dc4849d629acc1ad --- deploy/strongswan.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/strongswan.sh b/deploy/strongswan.sh index f991d690..3d5f1b34 100644 --- a/deploy/strongswan.sh +++ b/deploy/strongswan.sh @@ -22,6 +22,8 @@ strongswan_deploy() { _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 From b8418ced44167f1923354ff34441a58ddcef0d88 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Fri, 16 Feb 2018 09:01:26 -0500 Subject: [PATCH 172/272] syntax fixes --- deploy/vault.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/deploy/vault.sh b/deploy/vault.sh index b95d8af5..8a6c55be 100644 --- a/deploy/vault.sh +++ b/deploy/vault.sh @@ -33,21 +33,18 @@ vault_deploy() { _debug _cfullchain "$_cfullchain" # validate required env vars - if [ -z "$VAULT_PREFIX" ] - then + 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 + 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 + if [ ! $? ]; then _err "cannot find vault binary!" return 1 fi From c86755f1ab80e0de9c238e266bdd05137b366000 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Fri, 16 Feb 2018 09:19:47 -0500 Subject: [PATCH 173/272] format fix --- deploy/vault.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/vault.sh b/deploy/vault.sh index 8a6c55be..7b91ec51 100644 --- a/deploy/vault.sh +++ b/deploy/vault.sh @@ -44,7 +44,7 @@ vault_deploy() { fi VAULT_CMD=$(which vault) - if [ ! $? ]; then + if [ ! $? ]; then _err "cannot find vault binary!" return 1 fi From 6d6b2efdb5131e77a1c4efa08f857b29013ae1d3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 23:16:25 +0800 Subject: [PATCH 174/272] fix he for solaris --- acme.sh | 2 +- dnsapi/dns_he.sh | 76 ++++++++++++++++++------------------------------ 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/acme.sh b/acme.sh index 2be989f2..4829507b 100755 --- a/acme.sh +++ b/acme.sh @@ -1838,7 +1838,7 @@ _send_signed_request() { _body="$response" if [ "$needbase64" ]; then _body="$(echo "$_body" | _dbase64)" - _debug2 _body "$_body" + _debug3 _body "$_body" fi if _contains "$_body" "JWS has invalid anti-replay nonce"; then diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 7b854ead..d1744dc4 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -75,17 +75,19 @@ dns_he_rm() { body="$body&hosted_dns_zoneid=$_zone_id" body="$body&menu=edit_zone" body="$body&hosted_dns_editzone=" - domain_regex="$(echo "$_full_domain" | sed 's/\./\\./g')" # escape dots - _record_id=$(_post "$body" "https://dns.he.net/" \ - | tr -d '\n' \ - | _egrep_o "data=\""${_txt_value}"([^>]+>){6}[^<]+<[^;]+;deleteRecord\('[0-9]+','${domain_regex}','TXT'\)" \ - | _egrep_o "[0-9]+','${domain_regex}','TXT'\)$" \ - | _egrep_o "^[0-9]+" - ) - # The series of egreps above could have been done a bit shorter but - # I wanted to double-check whether it's the correct record (in case - # HE changes their website somehow). + 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/ Date: Fri, 16 Feb 2018 15:40:05 -0200 Subject: [PATCH 175/272] Set the account key file permissions --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index 4829507b..d958b074 100755 --- a/acme.sh +++ b/acme.sh @@ -1281,6 +1281,7 @@ _create_account_key() { else #generate account key _createkey "$length" "$ACCOUNT_KEY_PATH" + chmod 600 "$ACCOUNT_KEY_PATH" fi } From d84665cb64afb4dbb81eed775e50ce7f7fddf060 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 17 Feb 2018 10:16:04 +0800 Subject: [PATCH 176/272] fix https://github.com/Neilpang/acme.sh/issues/1271 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 1586b73b..f79ccc85 100755 --- a/acme.sh +++ b/acme.sh @@ -2010,8 +2010,8 @@ _startserver() { SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork #Adding bind to local-address - if [ "$_local_address" ]; then - $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${_local_address}" + if [ "$ncaddr" ]; then + $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi _debug "_NC" "$_NC" From 5c568d6999905414781812f2d8089fe6332d03b4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 17 Feb 2018 10:31:34 +0800 Subject: [PATCH 177/272] https://github.com/Neilpang/acme.sh/issues/1277 --- acme.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index f79ccc85..20f446aa 100755 --- a/acme.sh +++ b/acme.sh @@ -2007,6 +2007,10 @@ _startserver() { _NC="$_NC -6" fi + if [ "$DEBUG" -gt "1" ]; then + _NC="$_NC -d -d -v" + fi + SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork #Adding bind to local-address @@ -2014,8 +2018,8 @@ _startserver() { $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi - _debug "_NC" "$_NC" - $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & + _debug "_NC" "$_NC $SOCAT_OPTIONS" + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From b00919c6928571baacacb2210662ad30dc2b8ba0 Mon Sep 17 00:00:00 2001 From: nytral Date: Sat, 17 Feb 2018 15:08:13 +0100 Subject: [PATCH 178/272] various fixes --- dnsapi/dns_lua.sh | 6 ++++-- dnsapi/dns_nsone.sh | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index be678b9d..9bf8cd9c 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/bash # bug reports to dev@1e.ca @@ -8,7 +8,6 @@ #LUA_Email="user@luadns.net" LUA_Api="https://api.luadns.com/v1" -LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64) ######## Public functions ##################### @@ -19,6 +18,8 @@ dns_lua_add() { LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" + LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64) + if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then LUA_Key="" LUA_Email="" @@ -60,6 +61,7 @@ dns_lua_rm() { LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" + LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64) _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" diff --git a/dnsapi/dns_nsone.sh b/dnsapi/dns_nsone.sh index adf1f422..00e186d2 100644 --- a/dnsapi/dns_nsone.sh +++ b/dnsapi/dns_nsone.sh @@ -59,10 +59,10 @@ dns_nsone_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain.\",[^{]*\"type\":\"TXT\",\"id\":\"[^,]*\"" | _head_n 1 | cut -d: -f7 | cut -d, -f1) - _debug "record_id" "$record_id" + 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\"]}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}" + _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 From 55787ff7b9d65ac64684dac19d7fe9bc5d1ba813 Mon Sep 17 00:00:00 2001 From: nytral Date: Sat, 17 Feb 2018 15:12:19 +0100 Subject: [PATCH 179/272] other fixes --- dnsapi/dns_lua.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index 9bf8cd9c..30c15579 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env sh # bug reports to dev@1e.ca From d6f8d6374231df715f02ec8efbdd5a8d6fee7118 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 19 Feb 2018 12:43:56 +0800 Subject: [PATCH 180/272] fix https://github.com/Neilpang/acme.sh/issues/1286 --- dnsapi/dns_yandex.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 7ebb15dc..5fbb09d8 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,7 +16,7 @@ dns_yandex_add() { _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" - curDomain=$(_PDD_get_domain "$fulldomain") + _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" @@ -33,7 +33,7 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curDomain=$(_PDD_get_domain "$fulldomain") + _PDD_get_domain "$fulldomain" _debug "Found suitable domain in pdd: $curDomain" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" @@ -72,8 +72,8 @@ _PDD_get_domain() { if [ "$d" = "$__t" ]; then p=$(_math $k - 1) curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")" - echo "$__t" - return + curDomain="$__t" + return 0 fi done k=$(_math $k + 1) @@ -96,7 +96,7 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain=$(_PDD_get_domain "$fulldomain") + _PDD_get_domain "$fulldomain" _debug "Found suitable domain in pdd: $curDomain" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" From 41e3ecad4603c0dcac9d3e26db49cd02922e91bd Mon Sep 17 00:00:00 2001 From: Boyan Peychev Date: Mon, 19 Feb 2018 14:14:08 +0200 Subject: [PATCH 181/272] Update dns api to support v2 wildcard cert #1261 --- dnsapi/dns_cloudns.sh | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index 4a1ae641..c9d38ed8 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -26,30 +26,18 @@ dns_cloudns_add() { host="$(echo "$1" | sed "s/\.$zone\$//")" record=$2 - record_id=$(_dns_cloudns_get_record_id "$zone" "$host") _debug zone "$zone" _debug host "$host" _debug record "$record" - _debug record_id "$record_id" - if [ -z "$record_id" ]; then - _info "Adding the TXT record for $1" - _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "Record cannot be added." - return 1 - fi - _info "Added." - else - _info "Updating the TXT record for $1" - _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "The TXT record for $1 cannot be updated." - return 1 - fi - _info "Updated." + _info "Adding the TXT record for $1" + _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "Record cannot be added." + return 1 fi + _info "Added." return 0 } From 9ad7ac632aa6920d71b9aaa21f93a8cede0518fe Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 19 Feb 2018 21:07:01 +0800 Subject: [PATCH 182/272] fix https://github.com/Neilpang/acme.sh/issues/1284#issuecomment-366616855 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 20f446aa..3aa2c7be 100755 --- a/acme.sh +++ b/acme.sh @@ -2007,7 +2007,7 @@ _startserver() { _NC="$_NC -6" fi - if [ "$DEBUG" -gt "1" ]; then + if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then _NC="$_NC -d -d -v" fi @@ -2015,7 +2015,7 @@ _startserver() { #Adding bind to local-address if [ "$ncaddr" ]; then - $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" + SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi _debug "_NC" "$_NC $SOCAT_OPTIONS" From 48eaa0e5bfb7579b6719a640eb6253f92c93c842 Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Mon, 19 Feb 2018 17:48:54 +0000 Subject: [PATCH 183/272] Let AWS DNS API code pull creds from instance role Add option (AWS_USE_INSTANCE_ROLE) to have the AWS DNS API driver pull the necessary credentials from the AWS EC2 instance metadata endpoint when required. This is a non-breaking change as it only takes effect when explicitly turned on via the environment variable, and fails safe back to the normal code path. --- dnsapi/dns_aws.sh | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 71f969f0..c754341c 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -9,6 +9,7 @@ AWS_HOST="route53.amazonaws.com" AWS_URL="https://$AWS_HOST" +AWS_METADATA_URL="http://169.254.169.254/latest/meta-data" AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" @@ -19,6 +20,10 @@ dns_aws_add() { fulldomain=$1 txtvalue=$2 + if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + _use_instance_role + fi + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then @@ -30,8 +35,12 @@ dns_aws_add() { fi #save for future use - _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" - _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" + if [ -n "$AWS_USE_INSTANCE_ROLE" ]; then + _saveaccountconf_mutable AWS_USE_INSTANCE_ROLE "$AWS_USE_INSTANCE_ROLE" + else + _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" + _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -76,6 +85,10 @@ dns_aws_rm() { fulldomain=$1 txtvalue=$2 + if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + _use_instance_role + fi + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" _debug "First detect the root zone" @@ -162,6 +175,34 @@ _get_root() { return 1 } +_use_instance_role() { + if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true | _head_n 1 | grep -Fq 200; then + _err "Unable to fetch IAM role from AWS instance metadata." + return + fi + _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/") + _debug "_aws_role" "$_aws_role" + _aws_creds="$( + _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" \ + | _normalizeJson \ + | tr '{,}' '\n' \ + | while read -r _line; do + _key="$(echo "${_line%%:*}" | tr -d '"')" + _value="${_line#*:}" + _debug3 "_key" "$_key" + _secure_debug3 "_value" "$_value" + case "$_key" in + AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;; + SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;; + Token) echo "AWS_SESSION_TOKEN=$_value" ;; + esac + done \ + | paste -sd' ' - + )" + _secure_debug "_aws_creds" "$_aws_creds" + eval "$_aws_creds" +} + #method uri qstr data aws_rest() { mtd="$1" From 693627a858e22391201ef385388756508da9c070 Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Tue, 20 Feb 2018 00:34:55 +0000 Subject: [PATCH 184/272] Emulate Boto when using role metadata Use the behavior established in the botocore python library to inform how and when instance metadata is fetched in an attempt to acquire valid AWS credentials. - Use it as a fallback when no other credentials are provided - Set the timeout of metadata requests to 1 second --- dnsapi/dns_aws.sh | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index c754341c..05a62008 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -20,12 +20,13 @@ dns_aws_add() { fulldomain=$1 txtvalue=$2 - if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" + + if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then _use_instance_role fi - AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" - AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" @@ -34,10 +35,8 @@ dns_aws_add() { return 1 fi - #save for future use - if [ -n "$AWS_USE_INSTANCE_ROLE" ]; then - _saveaccountconf_mutable AWS_USE_INSTANCE_ROLE "$AWS_USE_INSTANCE_ROLE" - else + #save for future use, unless using a role which will be fetched as needed + if [ -z "$_using_instance_role" ]; then _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" fi @@ -85,12 +84,13 @@ dns_aws_rm() { fulldomain=$1 txtvalue=$2 - if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" + + if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then _use_instance_role fi - AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" - AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -176,14 +176,14 @@ _get_root() { } _use_instance_role() { - if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true | _head_n 1 | grep -Fq 200; then + if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true 1 | _head_n 1 | grep -Fq 200; then _err "Unable to fetch IAM role from AWS instance metadata." return fi - _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/") + _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/" "" 1) _debug "_aws_role" "$_aws_role" _aws_creds="$( - _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" \ + _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" "" 1 \ | _normalizeJson \ | tr '{,}' '\n' \ | while read -r _line; do @@ -201,6 +201,7 @@ _use_instance_role() { )" _secure_debug "_aws_creds" "$_aws_creds" eval "$_aws_creds" + _using_instance_role=true } #method uri qstr data From 5309afc347d22917cb250d59ca4c97638223decb Mon Sep 17 00:00:00 2001 From: Boyan Peychev Date: Tue, 20 Feb 2018 11:09:37 +0200 Subject: [PATCH 185/272] Update dns api to support v2 wildcard cert #1261 --- dnsapi/dns_cloudns.sh | 49 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index c9d38ed8..dfab302b 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -60,22 +60,32 @@ dns_cloudns_rm() { host="$(echo "$1" | sed "s/\.$zone\$//")" record=$2 - record_id=$(_dns_cloudns_get_record_id "$zone" "$host") - _debug zone "$zone" - _debug host "$host" - _debug record "$record" - _debug record_id "$record_id" + _dns_cloudns_http_api_call "dns/records.json" "domain-name=$zone&host=$host&type=TXT" + if ! _contains "$response" "\"id\":"; then + return 1 + fi - if [ ! -z "$record_id" ]; then - _info "Deleting the TXT record for $1" - _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "The TXT record for $1 cannot be deleted." - return 1 + for i in $(echo $response | tr '{' "\n" | grep $record); do + record_id=$(echo $i | tr ',' "\n"| grep -E '^"id"'| sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g'); + + if [ ! -z "$record_id" ]; then + _debug zone "$zone" + _debug host "$host" + _debug record "$record" + _debug record_id "$record_id" + + _info "Deleting the TXT record for $1" + _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" + + if ! _contains "$response" "\"status\":\"Success\""; then + _err "The TXT record for $1 cannot be deleted." + else + _info "Deleted." + fi fi - _info "Deleted." - fi + done + return 0 } @@ -114,7 +124,7 @@ _dns_cloudns_init_check() { return 1 fi - #save the api id and password to the account conf file. + # save the api id and password to the account conf file. _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" _saveaccountconf_mutable CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID" _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" @@ -147,15 +157,6 @@ _dns_cloudns_get_zone_name() { return 1 } -_dns_cloudns_get_record_id() { - _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" - if _contains "$response" "\"id\":"; then - echo "$response" | cut -d '"' -f 2 - return 0 - fi - return 1 -} - _dns_cloudns_http_api_call() { method=$1 @@ -177,7 +178,7 @@ _dns_cloudns_http_api_call() { response="$(_get "$CLOUDNS_API/$method?$data")" - _debug2 response "$response" + _debug response "$response" return 0 } From 9f6832d636316e7dfacea6c93cf455a835b084ca Mon Sep 17 00:00:00 2001 From: Boyan Peychev Date: Tue, 20 Feb 2018 11:16:42 +0200 Subject: [PATCH 186/272] Update dns api to support v2 wildcard cert #1261 --- dnsapi/dns_cloudns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index dfab302b..6f2cbfe6 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -66,8 +66,8 @@ dns_cloudns_rm() { return 1 fi - for i in $(echo $response | tr '{' "\n" | grep $record); do - record_id=$(echo $i | tr ',' "\n"| grep -E '^"id"'| sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g'); + for i in $(echo "$response" | tr '{' "\n" | grep $record); do + record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"'| sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g') if [ ! -z "$record_id" ]; then _debug zone "$zone" From 28355335f82a04f68a1aecbc1ce7bec865df4fe8 Mon Sep 17 00:00:00 2001 From: Boyan Peychev Date: Tue, 20 Feb 2018 11:22:06 +0200 Subject: [PATCH 187/272] Update dns api to support v2 wildcard cert #1261 --- dnsapi/dns_cloudns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index 6f2cbfe6..df824e86 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -66,8 +66,8 @@ dns_cloudns_rm() { return 1 fi - for i in $(echo "$response" | tr '{' "\n" | grep $record); do - record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"'| sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g') + for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do + record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g') if [ ! -z "$record_id" ]; then _debug zone "$zone" From 759f4f2c62bd5118495937713134f9e7960bd98e Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Tue, 20 Feb 2018 12:40:24 +0000 Subject: [PATCH 188/272] Make the instance metadata fetcher self-contained This is to provide a clean path to future extension work such as adding a _use_container_role function to offer similar support for ECS containers. The $_using_role flag has also been made generic so that future role providers can also make use of it. --- dnsapi/dns_aws.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 05a62008..9c9e9313 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -9,7 +9,6 @@ AWS_HOST="route53.amazonaws.com" AWS_URL="https://$AWS_HOST" -AWS_METADATA_URL="http://169.254.169.254/latest/meta-data" AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" @@ -36,7 +35,7 @@ dns_aws_add() { fi #save for future use, unless using a role which will be fetched as needed - if [ -z "$_using_instance_role" ]; then + if [ -z "$_using_role" ]; then _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" fi @@ -176,14 +175,16 @@ _get_root() { } _use_instance_role() { - if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true 1 | _head_n 1 | grep -Fq 200; then + _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" + _debug "_url" "$_url" + if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then _err "Unable to fetch IAM role from AWS instance metadata." return fi - _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/" "" 1) + _aws_role=$(_get "$_url" "" 1) _debug "_aws_role" "$_aws_role" _aws_creds="$( - _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" "" 1 \ + _get "$_url$_aws_role" "" 1 \ | _normalizeJson \ | tr '{,}' '\n' \ | while read -r _line; do @@ -201,7 +202,7 @@ _use_instance_role() { )" _secure_debug "_aws_creds" "$_aws_creds" eval "$_aws_creds" - _using_instance_role=true + _using_role=true } #method uri qstr data From 2c45f27356de74567ecd2fcc82842e9a8863ccc3 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Tue, 20 Feb 2018 09:11:45 -0500 Subject: [PATCH 189/272] rename deploy hook vault to vault_cli --- deploy/{vault.sh => vault_cli.sh} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename deploy/{vault.sh => vault_cli.sh} (98%) diff --git a/deploy/vault.sh b/deploy/vault_cli.sh similarity index 98% rename from deploy/vault.sh rename to deploy/vault_cli.sh index 7b91ec51..02617c5e 100644 --- a/deploy/vault.sh +++ b/deploy/vault_cli.sh @@ -18,7 +18,7 @@ ######## Public functions ##################### #domain keyfile certfile cafile fullchain -vault_deploy() { +vault_cli_deploy() { _cdomain="$1" _ckey="$2" From bae50da799e73b8069393248824da3de14137e72 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Feb 2018 09:45:36 +0800 Subject: [PATCH 190/272] fix https://github.com/Neilpang/acme.sh/issues/1266 --- README.md | 4 +++- acme.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f9002c9..1e2defb7 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) -# Who are using **acme.sh** +# Who: - [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) - [ruby-china.org](https://ruby-china.org/topics/31983) - [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer)) @@ -204,6 +204,8 @@ Install/copy the cert/key to the production Apache or Nginx path. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. +**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewwed cert in 60 days.** + # 4. Use Standalone server to issue cert **(requires you to be root/sudoer or have permission to listen on port 80 (TCP))** diff --git a/acme.sh b/acme.sh index 3aa2c7be..a81b42d3 100755 --- a/acme.sh +++ b/acme.sh @@ -2019,7 +2019,7 @@ _startserver() { fi _debug "_NC" "$_NC $SOCAT_OPTIONS" - $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From 86ef0a260942ef5962ba01e0e7f1aeeeaaa846ab Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Feb 2018 10:05:27 +0800 Subject: [PATCH 191/272] fix https://github.com/Neilpang/acme.sh/issues/1295 --- acme.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index a81b42d3..56d10f88 100755 --- a/acme.sh +++ b/acme.sh @@ -5073,7 +5073,7 @@ _installalias() { } -# nocron confighome +# nocron confighome noprofile install() { if [ -z "$LE_WORKING_DIR" ]; then @@ -5082,6 +5082,7 @@ install() { _nocron="$1" _c_home="$2" + _noprofile="$3" if ! _initpath; then _err "Install failed." return 1 @@ -5147,7 +5148,7 @@ install() { _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" - if [ "$IN_CRON" != "1" ]; then + if [ "$IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then _installalias "$_c_home" fi @@ -5373,10 +5374,11 @@ Parameters: " } -# nocron +# nocron noprofile _installOnline() { _info "Installing from online archive." _nocron="$1" + _noprofile="$2" if [ ! "$BRANCH" ]; then BRANCH="master" fi @@ -5397,7 +5399,7 @@ _installOnline() { cd "$PROJECT_NAME-$BRANCH" chmod +x $PROJECT_ENTRY - if ./$PROJECT_ENTRY install "$_nocron"; then + if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then _info "Install success!" fi @@ -5413,7 +5415,7 @@ upgrade() { _initpath export LE_WORKING_DIR cd "$LE_WORKING_DIR" - _installOnline "nocron" + _installOnline "nocron" "noprofile" ); then _info "Upgrade success!" exit 0 From 58f753136a6b92607fe8767b3367ab56108efe56 Mon Sep 17 00:00:00 2001 From: hebbet Date: Wed, 21 Feb 2018 09:01:56 +0100 Subject: [PATCH 192/272] small typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e2defb7..ad2a2459 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ Install/copy the cert/key to the production Apache or Nginx path. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. -**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewwed cert in 60 days.** +**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewed cert in 60 days.** # 4. Use Standalone server to issue cert From f49f55f4a51e0e5eb84212d670a3ac373ff78acb Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Tue, 20 Feb 2018 14:55:05 +0000 Subject: [PATCH 193/272] Pull AWS creds from container role Extend the AWS DNS API driver to support ECS container metadata by using the special environment variable ECS sets in containers. --- dnsapi/dns_aws.sh | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 9c9e9313..8ce7c347 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -23,7 +23,7 @@ dns_aws_add() { AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then - _use_instance_role + _use_container_role || _use_instance_role fi if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then @@ -87,7 +87,7 @@ dns_aws_rm() { AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then - _use_instance_role + _use_container_role || _use_instance_role fi _debug "First detect the root zone" @@ -174,17 +174,30 @@ _get_root() { return 1 } +_use_container_role() { + # automatically set if running inside ECS + if [ -z "$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" ]; then + _debug "No ECS environment variable detected" + return 1 + fi + _use_metadata "169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" +} + _use_instance_role() { _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" _debug "_url" "$_url" if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then - _err "Unable to fetch IAM role from AWS instance metadata." - return + _debug "Unable to fetch IAM role from instance metadata" + return 1 fi _aws_role=$(_get "$_url" "" 1) _debug "_aws_role" "$_aws_role" + _use_metadata "$_url$_aws_role" +} + +_use_metadata() { _aws_creds="$( - _get "$_url$_aws_role" "" 1 \ + _get "$1" "" 1 \ | _normalizeJson \ | tr '{,}' '\n' \ | while read -r _line; do @@ -201,6 +214,11 @@ _use_instance_role() { | paste -sd' ' - )" _secure_debug "_aws_creds" "$_aws_creds" + + if [ -z "$_aws_creds" ]; then + return 1 + fi + eval "$_aws_creds" _using_role=true } From 83b1a98db18dded8eecb21d3937e4339ca64a2d9 Mon Sep 17 00:00:00 2001 From: martgras Date: Sun, 18 Feb 2018 16:32:39 +0100 Subject: [PATCH 194/272] Azure DNS API - support for ACME v2 and reliability improvments support adding 2 txt records Adding retry logic for REST API calls Reusing bearer token removes 50% of required REST calls --- dnsapi/dns_azure.sh | 158 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 124 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 0834ede7..677a9f75 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -1,5 +1,7 @@ #!/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" @@ -69,12 +71,36 @@ dns_azure_add() { acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug "$acmeRecordURI" - body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + # 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 record added" + _info "validation value added" else - _err "error adding validation record ($_code)" + _err "error adding validation value ($_code)" return 1 fi } @@ -141,13 +167,38 @@ dns_azure_rm() { acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug "$acmeRecordURI" - body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" - _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" - if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then - _info "validation record removed" - else - _err "error removing validation record ($_code)" - return 1 + # 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" + else + _err "error removing validation value ($_code)" + return 1 + fi + fi fi } @@ -159,52 +210,92 @@ _azure_rest() { data="$3" accesstoken="$4" - export _H1="authorization: Bearer $accesstoken" - export _H2="accept: application/json" - export _H3="Content-Type: application/json" - - _debug "$ep" - if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$ep" "" "$m")" - else - response="$(_get "$ep")" - fi - _debug2 response "$response" - - _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" - _debug2 "http response code $_code" - - if [ "$?" != "0" ]; then - _err "error $ep" + 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 + _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 [ "$?" != "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 + 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" - _debug data "$body" - response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST")" + _secure_debug2 "data $body" + response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")" + _secure_debug2 "response $response" + response="$(echo "$response" | _normalizeJson)" accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") - _debug2 "response $response" + 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" + _err "no acccess token received. Check your Azure settings see $WIKI" return 1 fi if [ "$?" != "0" ]; then _err "error $response" return 1 fi + _saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$accesstoken" + _saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on" printf "%s" "$accesstoken" return 0 } @@ -222,7 +313,6 @@ _get_root() { ## 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) From e26f9b8095291f83b4fa5a0a56f5958824b404f1 Mon Sep 17 00:00:00 2001 From: nytral Date: Sat, 24 Feb 2018 09:08:44 +0100 Subject: [PATCH 195/272] DNSMadeEasy ACMEv2 support --- dnsapi/dns_me.sh | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index 3393fb75..dec07b71 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -45,8 +45,7 @@ dns_me_add() { count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2) _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" +# _info "Adding record" if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep \"id\": >/dev/null; then _info "Added" @@ -58,20 +57,7 @@ dns_me_add() { fi fi _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | head -n 1) - _debug "record_id" "$record_id" - - _me_rest PUT "$_domain_id/records/$record_id/" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}" - if [ "$?" = "0" ]; then - _info "Updated" - #todo: check if the record takes effect - return 0 - fi - _err "Update error" return 1 - fi } @@ -96,7 +82,7 @@ dns_me_rm() { if [ "$count" = "0" ]; then _info "Don't need to remove." else - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | head -n 1) + record_id=$(printf "%s\n" "$response" | _egrep_o ",\"value\":\"..$txtvalue..\",\"id\":[^,]*" | cut -d : -f 3 | head -n 1) _debug "record_id" "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id to remove." @@ -152,7 +138,7 @@ _me_rest() { data="$3" _debug "$ep" - cdate=$(date -u +"%a, %d %b %Y %T %Z") + cdate=$(LANG=C date -u +"%a, %d %b %Y %T %Z") hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex) export _H1="x-dnsme-apiKey: $ME_Key" From 5a883889a221f9983ae6d5cc94f7271401f8b225 Mon Sep 17 00:00:00 2001 From: nytral Date: Mon, 26 Feb 2018 14:53:31 +0100 Subject: [PATCH 196/272] fixes --- dnsapi/dns_me.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index dec07b71..ca36607f 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -43,8 +43,6 @@ dns_me_add() { return 1 fi - count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2) - _debug count "$count" # _info "Adding record" if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep \"id\": >/dev/null; then @@ -56,8 +54,6 @@ dns_me_add() { return 1 fi fi - _err "Add txt record error." - return 1 } From 3bc59a0327df3e1a1662fff2351ffd29c834ad6c Mon Sep 17 00:00:00 2001 From: nytral Date: Mon, 26 Feb 2018 21:47:51 +0100 Subject: [PATCH 197/272] first attempt to fix CI errors --- dnsapi/dns_me.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index ca36607f..382eeedd 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -43,17 +43,17 @@ dns_me_add() { return 1 fi -# _info "Adding record" - if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep \"id\": >/dev/null; then - _info "Added" - #todo: check if the record takes effect - return 0 - else - _err "Add txt record error." - return 1 - fi + _info "Adding record" + if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep \"id\": >/dev/null; then + _info "Added" + #todo: check if the record takes effect + return 0 + else + _err "Add txt record error." + return 1 fi + fi } From d064260bf1c6ae3403601314cac49ca0f2ddaea2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 1 Mar 2018 21:59:46 +0800 Subject: [PATCH 198/272] fix https://github.com/Neilpang/acme.sh/issues/1315 --- dnsapi/dns_he.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index d1744dc4..f42d56af 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -117,7 +117,7 @@ _find_zone() { _debug2 response "$response" _table="$(echo "$response" | tr -d "#" | sed "s/ Date: Thu, 1 Mar 2018 14:19:43 -0500 Subject: [PATCH 199/272] Rewrote to adapt the new name.com v4 API. --- dnsapi/dns_namecom.sh | 126 +++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 76 deletions(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 3af8bf4c..40908904 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -1,36 +1,42 @@ #!/usr/bin/env sh -#Author: RaidneII +#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/api" +Namecom_API="https://api.name.com/v4" + +# 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" + +# Auth string +# Name.com API v4 uses http basic auth to authenticate +# need to convert the token for http auth +_namecom_auth=`echo -n "$Namecom_Username:$Namecom_Token" | base64` #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 @@ -39,21 +45,18 @@ dns_namecom_add() { # Find domain in domain list. if ! _namecom_get_root "$fulldomain"; then _err "Unable to find domain specified." - _namecom_logout return 1 fi # Add TXT record. - _namecom_addtxt_json="{\"hostname\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":\"300\",\"priority\":\"10\"}" - if _namecom_rest POST "dns/create/$_domain" "$_namecom_addtxt_json"; then - retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") - if [ "$retcode" ]; then + _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." - _namecom_logout return 0 else _err "Unable to add the DNS record." - _namecom_logout return 1 fi fi @@ -72,37 +75,28 @@ dns_namecom_rm() { # Find domain in domain list. if ! _namecom_get_root "$fulldomain"; then _err "Unable to find domain specified." - _namecom_logout return 1 fi # Get the record id. - if _namecom_rest GET "dns/list/$_domain"; then - retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") - if [ "$retcode" ]; then - _record_id=$(printf "%s\n" "$response" | _egrep_o "\"record_id\":\"[0-9]+\",\"name\":\"$fulldomain\",\"type\":\"TXT\"" | cut -d \" -f 4) - _debug record_id "$_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\"" | 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." - _namecom_logout return 1 fi fi # Remove the DNS record using record id. - _namecom_rmtxt_json="{\"record_id\":\"$_record_id\"}" - if _namecom_rest POST "dns/delete/$_domain" "$_namecom_rmtxt_json"; then - retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") - if [ "$retcode" ]; then - _info "Successfully removed the TXT record." - _namecom_logout - return 0 - else - _err "Unable to remove the DNS record." - _namecom_logout - return 1 - fi + 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 } @@ -112,8 +106,9 @@ _namecom_rest() { param=$2 data=$3 - export _H1="Content-Type: application/json" - export _H2="Api-Session-Token: $sessionkey" + export _H1="Authorization: Basic $_namecom_auth" + export _H2="Content-Type: application/json" + if [ "$method" != "GET" ]; then response="$(_post "$data" "$Namecom_API/$param" "" "$method")" else @@ -125,25 +120,15 @@ _namecom_rest() { return 1 fi - _debug2 response "$response" + _debug response "$response" return 0 } _namecom_login() { - namecom_login_json="{\"username\":\"$Namecom_Username\",\"api_token\":\"$Namecom_Token\"}" - - if _namecom_rest POST "login" "$namecom_login_json"; then - retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") + if _namecom_rest GET "hello"; then + retcode=$(printf "%s\n" "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"") if [ "$retcode" ]; then - _info "Successfully logged in. Fetching session token..." - sessionkey=$(printf "%s\n" "$response" | _egrep_o "\"session_token\":\".+" | cut -d \" -f 4) - if [ ! -z "$sessionkey" ]; then - _debug sessionkey "$sessionkey" - _info "Session key obtained." - else - _err "Unable to get session key." - return 1 - fi + _info "Successfully logged in." else _err "Logging in failed." return 1 @@ -151,24 +136,12 @@ _namecom_login() { fi } -_namecom_logout() { - if _namecom_rest GET "logout"; then - retcode=$(printf "%s\n" "$response" | _egrep_o "\"code\":100") - if [ "$retcode" ]; then - _info "Successfully logged out." - else - _err "Error logging out." - return 1 - fi - fi -} - _namecom_get_root() { domain=$1 i=2 p=1 - if ! _namecom_rest GET "domain/list"; then + if ! _namecom_rest GET "domains"; then return 1 fi @@ -191,3 +164,4 @@ _namecom_get_root() { done return 1 } + From 14c27554369623e8162b11fe4b1007cc8479cebe Mon Sep 17 00:00:00 2001 From: TigerP Date: Thu, 1 Mar 2018 19:39:10 +0100 Subject: [PATCH 200/272] Add support for DirectAdmin --- dnsapi/README.md | 28 +++++++ dnsapi/dns_da.sh | 185 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100755 dnsapi/dns_da.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 8c43806c..4effb8d6 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -757,6 +757,34 @@ acme.sh --issue --dns dns_dreamhost -d example.com -d www.example.com The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 41. Use DirectAdmin 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 the DirectAdmin incl. port. +You can create an user which only has access to + +- CMD_API_DNS_CONTROL +- CMD_API_SHOW_DOMAINS + +By using the Login Keys function. +See also https://www.directadmin.com/api.php and https://www.directadmin.com/features.php?id=1298 + +``` +export DA_Api="https://remoteUser:remotePassword@da.domain.tld: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) + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_da -d example.com -d www.example.com +``` + +The `DA_Api` and `DA_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_da.sh b/dnsapi/dns_da.sh new file mode 100755 index 00000000..0cf02c6a --- /dev/null +++ b/dnsapi/dns_da.sh @@ -0,0 +1,185 @@ +#!/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 '=')" + 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})" == "0" ]; 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 +} + From 19277aec87f9fc2dfc8770f4a06a00d131bc7ef2 Mon Sep 17 00:00:00 2001 From: raidenii Date: Thu, 1 Mar 2018 14:29:14 -0500 Subject: [PATCH 201/272] Use printf instead of echo, hopefully fix SC2039. --- dnsapi/dns_namecom.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 40908904..702bc6e2 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -30,7 +30,7 @@ _saveaccountconf Namecom_Token "$Namecom_Token" # Auth string # Name.com API v4 uses http basic auth to authenticate # need to convert the token for http auth -_namecom_auth=`echo -n "$Namecom_Username:$Namecom_Token" | base64` +_namecom_auth=`printf "$Namecom_Username:$Namecom_Token" | base64` #Usage: dns_namecom_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_namecom_add() { From a6c2d4b0e245829ff32555afe0891b15f12a8940 Mon Sep 17 00:00:00 2001 From: raidenii Date: Thu, 1 Mar 2018 14:38:49 -0500 Subject: [PATCH 202/272] Another fix. --- dnsapi/dns_namecom.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 702bc6e2..2d3293c8 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -30,7 +30,7 @@ _saveaccountconf Namecom_Token "$Namecom_Token" # Auth string # Name.com API v4 uses http basic auth to authenticate # need to convert the token for http auth -_namecom_auth=`printf "$Namecom_Username:$Namecom_Token" | base64` +_namecom_auth=$(printf "$Namecom_Username:$Namecom_Token" | base64) #Usage: dns_namecom_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_namecom_add() { From 3e1a94cbcdf6c82958c19b29b3cf8e9317399dda Mon Sep 17 00:00:00 2001 From: raidenii Date: Thu, 1 Mar 2018 14:43:08 -0500 Subject: [PATCH 203/272] Yet another fix. --- dnsapi/dns_namecom.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 2d3293c8..667f7688 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -30,7 +30,7 @@ _saveaccountconf Namecom_Token "$Namecom_Token" # Auth string # Name.com API v4 uses http basic auth to authenticate # need to convert the token for http auth -_namecom_auth=$(printf "$Namecom_Username:$Namecom_Token" | base64) +_namecom_auth=$(printf "%s:%s" "$Namecom_Username" "$Namecom_Token" | base64) #Usage: dns_namecom_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_namecom_add() { From 628a6ffa073f8f4bf1144b8f240d84cf28d1b66b Mon Sep 17 00:00:00 2001 From: raidenii Date: Thu, 1 Mar 2018 15:03:28 -0500 Subject: [PATCH 204/272] Tried to fix some weird problems --- dnsapi/dns_namecom.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 667f7688..bb813bdd 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -164,4 +164,3 @@ _namecom_get_root() { done return 1 } - From 399d6592b839f9e937a843d7bb230983b47c0f3e Mon Sep 17 00:00:00 2001 From: TigerP Date: Thu, 1 Mar 2018 21:25:24 +0100 Subject: [PATCH 205/272] Fix some quotes and a check --- dnsapi/dns_da.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_da.sh b/dnsapi/dns_da.sh index 0cf02c6a..598c1714 100755 --- a/dnsapi/dns_da.sh +++ b/dnsapi/dns_da.sh @@ -76,7 +76,7 @@ _get_root() { 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} + _da_api CMD_API_SHOW_DOMAINS "" "${domain}" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" @@ -125,13 +125,13 @@ _da_api() { 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) - ;; + CMD_API_SHOW_DOMAINS) ;; esac return 0 } @@ -155,7 +155,7 @@ _DA_getDomainInfo() { _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} + _da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}" _debug "Result of _DA_addTxt: '$response'" if _contains "${response}" 'error=0'; then _debug "Add TXT succeeded" @@ -170,7 +170,7 @@ _DA_addTxt() { _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})" == "0" ]; then + if _da_api CMD_API_DNS_CONTROL "${curData}" "${_domain}"; then _debug "Result of _DA_rmTxt: '$response'" else _err "Result of _DA_rmTxt: '$response'" From e8d808d708d997cc92f0bfbeace3d9431d8f4553 Mon Sep 17 00:00:00 2001 From: TigerP Date: Sat, 3 Mar 2018 14:40:23 +0100 Subject: [PATCH 206/272] Remove empty line at the end --- dnsapi/dns_da.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_da.sh b/dnsapi/dns_da.sh index 598c1714..7755c7e1 100755 --- a/dnsapi/dns_da.sh +++ b/dnsapi/dns_da.sh @@ -182,4 +182,3 @@ _DA_rmTxt() { _debug "RM TXT failed" return 1 } - From 2d7b9817cb4f694ef1ec717e5f363a1fc356d52e Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Sat, 3 Mar 2018 16:27:17 +0100 Subject: [PATCH 207/272] Fix typos --- deploy/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index cab1f4fb..0b820dff 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -114,8 +114,8 @@ user Any backups older than 180 days will be deleted when new certificates are deployed. This defaults to "yes" set to "no" to disable backup. -###Eamples using SSH deploy -The following example illustrates deploying certifcates to a QNAP NAS +###Examples using SSH deploy +The following example illustrates deploying certificates to a QNAP NAS (tested with QTS version 4.2.3) ```sh @@ -132,8 +132,8 @@ the same file. This will result in the certificate being appended to the same file as the private key... a common requirement of several services. -The next example illustates deploying certificates to a Unifi -Contolller (tested with version 5.4.11). +The next example illustrates deploying certificates to a Unifi +Controller (tested with version 5.4.11). ```sh export DEPLOY_SSH_USER="root" @@ -153,9 +153,9 @@ export DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \ acme.sh --deploy -d unifi.example.com --deploy-hook ssh ``` -In this exmple we execute several commands on the remote host +In this example we execute several commands on the remote host after the certificate files have been copied... to generate a pkcs12 file -compatible with Unifi, to import it into the Unifi keystore and then finaly +compatible with Unifi, to import it into the Unifi keystore and then finally to restart the service. Note also that once the certificate is imported @@ -233,7 +233,7 @@ DEPLOY_CPANEL_USER is required only if you run the script as root and it should export DEPLOY_CPANEL_USER=username acme.sh --deploy -d example.com --deploy-hook cpanel_uapi ``` -Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain. +Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separate certificate for each domain. ## 8. Deploy the cert to your FRITZ!Box router From 9fa207e613cc417514f1a97b3c5ada10b81076e1 Mon Sep 17 00:00:00 2001 From: raidenii Date: Sun, 4 Mar 2018 14:13:14 -0500 Subject: [PATCH 208/272] Move code to fit DNS API dev guide. --- dnsapi/dns_namecom.sh | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index bb813bdd..931bf30e 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -8,35 +8,30 @@ Namecom_API="https://api.name.com/v4" -# 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" - -# 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) - #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 @@ -125,6 +120,11 @@ _namecom_rest() { } _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 From 508012342daf716d3c99228a1bec5f7c893285a9 Mon Sep 17 00:00:00 2001 From: raidenii Date: Sun, 4 Mar 2018 17:22:13 -0500 Subject: [PATCH 209/272] Make sure the removal of DNS record is the desired one (i.e., by txtvalue) --- dnsapi/dns_namecom.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 931bf30e..e8bce7b3 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -75,7 +75,7 @@ dns_namecom_rm() { # 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\"" | cut -d \" -f 3 | _egrep_o [0-9]+) + _record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+,\"domainName\":\"$_domain\",\"host\":\"$_sub_domain\",\"fqdn\":\"$fulldomain.\",\"type\":\"$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." @@ -115,7 +115,7 @@ _namecom_rest() { return 1 fi - _debug response "$response" + _debug2 response "$response" return 0 } From 3052ba433a70aae287f0fa90698f0bf8081db58e Mon Sep 17 00:00:00 2001 From: raidenii Date: Sun, 4 Mar 2018 17:27:34 -0500 Subject: [PATCH 210/272] Fix an obvious stupidity. --- dnsapi/dns_namecom.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index e8bce7b3..b712fa94 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -75,7 +75,7 @@ dns_namecom_rm() { # 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\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+) + _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." From 2bbc25c1ebfbb478fbc182f9b0a322335c2bfebd Mon Sep 17 00:00:00 2001 From: TigerP Date: Tue, 6 Mar 2018 17:00:19 +0100 Subject: [PATCH 211/272] Add DirectAdmin to the main README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1e2defb7..4565b464 100644 --- a/README.md +++ b/README.md @@ -326,6 +326,7 @@ You don't have to do anything manually! 1. selectel.com(selectel.ru) DNS API 1. zonomi.com DNS API 1. DreamHost.com API +1. DirectAdmin API And: From 7445a3be591b8804ef29b693a7e4df64a00497de Mon Sep 17 00:00:00 2001 From: jim-p Date: Tue, 6 Mar 2018 17:00:57 -0500 Subject: [PATCH 212/272] Add ACME v2 test around cert copy. Fixes #1330 Without this, the work done a few lines above is clobbered, leaving the fullchain.cer containing only the certificate, not the CA and certificate chain. --- acme.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 2ca7fa97..5ea331c9 100755 --- a/acme.sh +++ b/acme.sh @@ -4078,7 +4078,9 @@ $_authorizations_map" _info "Your cert key is in $(__green " $CERT_KEY_PATH ")" fi - cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH" + if [ "$ACME_VERSION" != "2" ]; then + cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH" + fi if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then USER_PATH="$PATH" From e1db5db8ac4604c123fbbfe3c87615b25657820c Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Mar 2018 21:25:07 +0800 Subject: [PATCH 213/272] fix https://github.com/Neilpang/acme.sh/issues/1105 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 5ea331c9..550d89b2 100755 --- a/acme.sh +++ b/acme.sh @@ -1841,7 +1841,7 @@ _send_signed_request() { _body="$response" if [ "$needbase64" ]; then - _body="$(echo "$_body" | _dbase64)" + _body="$(echo "$_body" | _dbase64 | tr -d '\0')" _debug3 _body "$_body" fi From 45e386b26ddf69bb614278767e425a88809a5cfc Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 8 Mar 2018 19:49:53 +0800 Subject: [PATCH 214/272] fix https://github.com/Neilpang/acme.sh/issues/1336 --- acme.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 550d89b2..f906d0a2 100755 --- a/acme.sh +++ b/acme.sh @@ -63,6 +63,7 @@ END_CSR="-----END CERTIFICATE REQUEST-----" BEGIN_CERT="-----BEGIN CERTIFICATE-----" END_CERT="-----END CERTIFICATE-----" +CONTENT_TYPE_JSON="application/jose+json" RENEW_SKIP=2 ECC_SEP="_" @@ -1591,12 +1592,13 @@ _inithttp() { } -# body url [needbase64] [POST|PUT] +# body url [needbase64] [POST|PUT] [ContentType] _post() { body="$1" _post_url="$2" needbase64="$3" httpmethod="$4" + _postContentType="$5" if [ -z "$httpmethod" ]; then httpmethod="POST" @@ -1612,6 +1614,9 @@ _post() { if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " fi + if [ "$_postContentType" ]; then + _CURL="$_CURL -H \"Content-Type: $_postContentType\" " + fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" @@ -1631,6 +1636,9 @@ _post() { if [ "$HTTPS_INSECURE" ]; then _WGET="$_WGET --no-check-certificate " fi + if [ "$_postContentType" ]; then + _WGET="$_WGET --header \"Content-Type: $_postContentType\" " + fi _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then @@ -1765,7 +1773,7 @@ _send_signed_request() { if [ "$ACME_NEW_NONCE" ]; then _debug2 "Get nonce. ACME_NEW_NONCE" "$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE" - if _post "" "$nonceurl" "" "HEAD"; then + if _post "" "$nonceurl" "" "HEAD" "$CONTENT_TYPE_JSON"; then _headers="$(cat "$HTTP_HEADER")" fi fi @@ -1820,7 +1828,7 @@ _send_signed_request() { fi _debug3 body "$body" - response="$(_post "$body" "$url" "$needbase64")" + response="$(_post "$body" "$url" "$needbase64" "POST" "$CONTENT_TYPE_JSON")" _CACHED_NONCE="" if [ "$?" != "0" ]; then From ef871775b72e0cd201276c9fafa68e0c129f8765 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 8 Mar 2018 21:27:52 +0800 Subject: [PATCH 215/272] fix https://github.com/Neilpang/acme.sh/issues/1336 for wget --- acme.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/acme.sh b/acme.sh index f906d0a2..a93ada8c 100755 --- a/acme.sh +++ b/acme.sh @@ -1636,21 +1636,34 @@ _post() { if [ "$HTTPS_INSECURE" ]; then _WGET="$_WGET --no-check-certificate " fi - if [ "$_postContentType" ]; then - _WGET="$_WGET --header \"Content-Type: $_postContentType\" " - fi _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + fi else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + fi fi else if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + fi else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + fi fi fi _ret="$?" From 183063a244b389ff125425a5bd14c237b64de259 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 9 Mar 2018 08:06:42 +0800 Subject: [PATCH 216/272] add more safe check --- acme.sh | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/acme.sh b/acme.sh index a93ada8c..914292bc 100755 --- a/acme.sh +++ b/acme.sh @@ -849,6 +849,16 @@ _dbase64() { fi } +#file +_checkcert() { + _cf="$1" + if [ "$DEBUG" ]; then + openssl x509 -noout -text -in "$_cf" + else + openssl x509 -noout -text -in "$_cf" >/dev/null 2>&1 + fi +} + #Usage: hashalg [outputhex] #Output Base64-encoded digest _digest() { @@ -4089,6 +4099,13 @@ $_authorizations_map" _debug "Le_LinkCert" "$Le_LinkCert" _savedomainconf "Le_LinkCert" "$Le_LinkCert" + if [ -z "$Le_LinkCert" ] || ! _checkcert "$CERT_PATH"; then + response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)" + _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')" + _on_issue_err "$_post_hook" + return 1 + fi + if [ "$Le_LinkCert" ]; then _info "$(__green "Cert success.")" cat "$CERT_PATH" @@ -4099,28 +4116,18 @@ $_authorizations_map" _info "Your cert key is in $(__green " $CERT_KEY_PATH ")" fi - if [ "$ACME_VERSION" != "2" ]; then - cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH" - fi - if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then USER_PATH="$PATH" _saveaccountconf "USER_PATH" "$USER_PATH" fi fi - if [ -z "$Le_LinkCert" ]; then - response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)" - _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')" - _on_issue_err "$_post_hook" - return 1 - fi - _cleardomainconf "Le_Vlist" if [ "$ACME_VERSION" = "2" ]; then _debug "v2 chain." else + cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH" Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') if [ "$Le_LinkIssuer" ]; then @@ -4144,6 +4151,10 @@ $_authorizations_map" echo "$BEGIN_CERT" >"$CA_CERT_PATH" _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" echo "$END_CERT" >>"$CA_CERT_PATH" + if !_checkcert "$CA_CERT_PATH"; then + _err "Can not get the ca cert." + break + fi cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" rm -f "$CA_CERT_PATH.der" break From 7e381f8e5d5f9f95cb914102200481d7c77a7e45 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 9 Mar 2018 08:09:32 +0800 Subject: [PATCH 217/272] fix format --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 914292bc..cf3a8ac2 100755 --- a/acme.sh +++ b/acme.sh @@ -853,9 +853,9 @@ _dbase64() { _checkcert() { _cf="$1" if [ "$DEBUG" ]; then - openssl x509 -noout -text -in "$_cf" + openssl x509 -noout -text -in "$_cf" else - openssl x509 -noout -text -in "$_cf" >/dev/null 2>&1 + openssl x509 -noout -text -in "$_cf" >/dev/null 2>&1 fi } From 716f727753c18527b909ea3207ad8a72fbf110fd Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 9 Mar 2018 20:14:41 +0800 Subject: [PATCH 218/272] fix https://github.com/Neilpang/acme.sh/issues/1105 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index cf3a8ac2..0fe6b819 100755 --- a/acme.sh +++ b/acme.sh @@ -4100,7 +4100,7 @@ $_authorizations_map" _savedomainconf "Le_LinkCert" "$Le_LinkCert" if [ -z "$Le_LinkCert" ] || ! _checkcert "$CERT_PATH"; then - response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)" + response="$(echo "$response" | _dbase64 "multiline" | tr -d '\0' | _normalizeJson)" _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')" _on_issue_err "$_post_hook" return 1 From e3ddb677e174c033282c78e50e1135a10380c8a8 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Fri, 9 Mar 2018 16:39:08 -0500 Subject: [PATCH 219/272] Adding support for API v2 (multiple TXT records) --- dnsapi/dns_freedns.sh | 146 ++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 91 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index afd8b796..1f03791a 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -53,8 +53,9 @@ dns_freedns_add() { i="$(_math "$i" - 1)" sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" - _debug top_domain "$top_domain" - _debug sub_domain "$sub_domain" + _debug "top_domain: $top_domain" + _debug "sub_domain: $sub_domain" + # Sometimes FreeDNS does not return the subdomain page but rather # returns a page regarding becoming a premium member. This usually # happens after a period of inactivity. Immediately trying again @@ -63,6 +64,7 @@ dns_freedns_add() { attempts=2 while [ "$attempts" -gt "0" ]; do attempts="$(_math "$attempts" - 1)" + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then @@ -71,10 +73,9 @@ dns_freedns_add() { fi return 1 fi - _debug2 htmlpage "$htmlpage" subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")" - _debug2 subdomain_csv "$subdomain_csv" + _debug3 "subdomain_csv: $subdomain_csv" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the top domain we are looking for. @@ -85,55 +86,25 @@ dns_freedns_add() { lines="$(echo "$subdomain_csv" | wc -l)" i=0 found=0 + DNSdomainid="" while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" line="$(echo "$subdomain_csv" | sed -n "${i}p")" - _debug2 line "$line" + _debug2 "line $line" if [ $found = 0 ] && _contains "$line" ""; then # this line will contain DNSdomainid for the top_domain DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" - _debug2 DNSdomainid "$DNSdomainid" + _debug2 "DNSdomainid: $DNSdomainid" found=1 - else - # lines contain DNS records for all subdomains - DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" - _debug2 DNSname "$DNSname" - DNStype="$(echo "$line" | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")" - _debug2 subdomain_csv "$subdomain_csv" + _debug3 "subdomain_csv: $subdomain_csv" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the domain name we are looking for. @@ -216,35 +164,51 @@ dns_freedns_rm() { lines="$(echo "$subdomain_csv" | wc -l)" i=0 found=0 + DNSdataid="" while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" line="$(echo "$subdomain_csv" | sed -n "${i}p")" - _debug2 line "$line" + _debug3 "line: $line" DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" - _debug2 DNSname "$DNSname" - DNStype="$(echo "$line" | sed 's/"; then # this line will contain DNSdomainid for the top_domain DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" _debug2 "DNSdomainid: $DNSdomainid" found=1 - break; + break fi done @@ -191,7 +191,7 @@ dns_freedns_rm() { fi _debug2 "DNSvalue: $DNSvalue" - if [ -n "$DNSdataid" ] && _startswith "$txtvalue" "$DNSvalue"; then + if [ -n "$DNSdataid" ] && _startswith "$txtvalue" "$DNSvalue"; then # Found a match. But note... Website is truncating the # value field so we are only testing that part that is not # truncated. This should be accurate enough. From a5a0e564dd5682ca12bdcd66dd2d9cd6e9afb9be Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Mar 2018 10:33:33 +0800 Subject: [PATCH 221/272] fix https://github.com/Neilpang/acme.sh/issues/1322 --- acme.sh | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/acme.sh b/acme.sh index 0fe6b819..2a3138cb 100755 --- a/acme.sh +++ b/acme.sh @@ -5399,7 +5399,6 @@ Parameters: --webroot, -w /path/to/webroot Specifies the web root folder for web root mode. --standalone Use standalone mode. --stateless Use stateless mode, see: $_STATELESS_WIKI - --tls Use standalone tls mode. --apache Use apache mode. --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api. --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds. @@ -5429,7 +5428,6 @@ Parameters: --accountkey Specifies the account key path, Only valid for the '--install' command. --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days. --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer. - --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer. --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses. --listraw Only used for '--list' command, list the certs in raw format. --stopRenewOnError, -se Only valid for '--renew-all' command. Stop if one cert has error in renewal. @@ -5780,14 +5778,6 @@ _process() { _webroot="$_webroot,$wvalue" fi ;; - --tls) - wvalue="$W_TLS" - if [ -z "$_webroot" ]; then - _webroot="$wvalue" - else - _webroot="$_webroot,$wvalue" - fi - ;; --dns) wvalue="dns" if [ "$2" ] && ! _startswith "$2" "-"; then @@ -5883,12 +5873,6 @@ _process() { Le_HTTPPort="$_httpport" shift ;; - --tlsport) - _tlsport="$2" - Le_TLSPort="$_tlsport" - shift - ;; - --listraw) _listraw="raw" ;; From 224e0c298a6de49fa41754839855ffaf256578d8 Mon Sep 17 00:00:00 2001 From: martgras Date: Mon, 12 Mar 2018 10:27:56 +0100 Subject: [PATCH 222/272] Fix missing success return value from dns_azure_add/rm --- dnsapi/dns_azure.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 677a9f75..e0d9516f 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -99,6 +99,7 @@ dns_azure_add() { _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 @@ -194,6 +195,7 @@ dns_azure_rm() { _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 @@ -226,6 +228,7 @@ _azure_rest() { 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" @@ -236,7 +239,7 @@ _azure_rest() { 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 [ "$?" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then + 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" @@ -281,6 +284,7 @@ _azure_getaccess_token() { 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 \") @@ -290,7 +294,7 @@ _azure_getaccess_token() { _err "no acccess token received. Check your Azure settings see $WIKI" return 1 fi - if [ "$?" != "0" ]; then + if [ "$_ret" != "0" ]; then _err "error $response" return 1 fi From 0f120c41f1649d1b0a704a439927b47ab214aae8 Mon Sep 17 00:00:00 2001 From: anabis <7274563+anabis@users.noreply.github.com> Date: Wed, 14 Mar 2018 11:05:57 +0100 Subject: [PATCH 223/272] fix syntax error missing space --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 2a3138cb..5d9b63c2 100755 --- a/acme.sh +++ b/acme.sh @@ -4151,7 +4151,7 @@ $_authorizations_map" echo "$BEGIN_CERT" >"$CA_CERT_PATH" _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" echo "$END_CERT" >>"$CA_CERT_PATH" - if !_checkcert "$CA_CERT_PATH"; then + if ! _checkcert "$CA_CERT_PATH"; then _err "Can not get the ca cert." break fi From 749c0e51e681c77fe10f38c083332c5c71969cb9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 19:42:02 +0800 Subject: [PATCH 224/272] start 2.7.8 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 2a3138cb..151b8e90 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.7 +VER=2.7.8 PROJECT_NAME="acme.sh" From c5f1cca3a0057e17ca817c8260027d4895e5d4f4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 20:30:51 +0800 Subject: [PATCH 225/272] fix https://github.com/Neilpang/acme.sh/issues/1372 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 439e0384..178852a5 100755 --- a/acme.sh +++ b/acme.sh @@ -4264,7 +4264,7 @@ renew() { fi . "$DOMAIN_CONF" - + _debug Le_API "$Le_API" if [ "$Le_API" ]; then if [ "$_OLD_CA_HOST" = "$Le_API" ]; then export Le_API="$DEFAULT_CA" From 664446631f0601d21a607cb0e76ba06d4acb3536 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 20:52:18 +0800 Subject: [PATCH 226/272] add debug info --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 178852a5..2898beb8 100755 --- a/acme.sh +++ b/acme.sh @@ -2341,7 +2341,7 @@ _initpath() { fi fi - _debug2 ACME_DIRECTORY "$ACME_DIRECTORY" + _debug ACME_DIRECTORY "$ACME_DIRECTORY" _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)" _debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST" From 3881f22192ac16f298e2d5dd603971e4450124c2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 21:20:27 +0800 Subject: [PATCH 227/272] fix https://github.com/Neilpang/acme.sh/issues/1375 add more info --- acme.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 2898beb8..337d8b9b 100755 --- a/acme.sh +++ b/acme.sh @@ -47,6 +47,7 @@ DEFAULT_DNS_SLEEP=120 NO_VALUE="no" W_TLS="tls" +W_DNS="dns" DNS_ALIAS_PREFIX="=" MODE_STATELESS="stateless" @@ -3118,7 +3119,7 @@ _on_issue_err() { ) fi - if [ "$IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "dns"; then + if [ "$IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "$W_DNS"; then _err "$_DNS_MANUAL_ERR" fi @@ -3154,7 +3155,7 @@ _on_issue_success() { fi fi - if _hasfield "$Le_Webroot" "dns"; then + if _hasfield "$Le_Webroot" "$W_DNS"; then _err "$_DNS_MANUAL_WARN" fi @@ -3624,7 +3625,7 @@ $_authorizations_map" vtype="$VTYPE_HTTP" #todo, v2 wildcard force to use dns - if _startswith "$_currentRoot" "dns"; then + if _startswith "$_currentRoot" "$W_DNS"; then vtype="$VTYPE_DNS" fi @@ -3751,6 +3752,10 @@ $_authorizations_map" if [ "$d_api" ]; then _info "Found domain api file: $d_api" else + if [ "$_currentRoot" != "$W_DNS" ]; then + _err "Can not find dns api hook for: $_currentRoot" + _info "You need to add the txt record manually." + fi _info "$(__red "Add the following TXT record:")" _info "$(__red "Domain: '$(__green "$txtdomain")'")" _info "$(__red "TXT value: '$(__green "$txt")'")" @@ -5779,7 +5784,7 @@ _process() { fi ;; --dns) - wvalue="dns" + wvalue="$W_DNS" if [ "$2" ] && ! _startswith "$2" "-"; then wvalue="$2" shift From 263c38caecbcf735e94646cb3483381680233960 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 21:27:29 +0800 Subject: [PATCH 228/272] add more debug info --- acme.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acme.sh b/acme.sh index 337d8b9b..e65b74af 100755 --- a/acme.sh +++ b/acme.sh @@ -3592,6 +3592,8 @@ issue() { _debug2 "_authz_url" "$_authz_url" if ! response="$(_get "$_authz_url")"; then _err "get to authz error." + _err "_authorizations_seg" "$_authorizations_seg" + _err "_authz_url" "$_authz_url" _clearup _on_issue_err "$_post_hook" return 1 @@ -3642,6 +3644,7 @@ $_authorizations_map" _debug2 "response" "$response" if [ -z "$response" ]; then _err "get to authz error." + _err "_authorizations_map" "$_authorizations_map" _clearup _on_issue_err "$_post_hook" return 1 @@ -4873,6 +4876,8 @@ _deactivate() { _debug2 "authzUri" "$authzUri" if ! response="$(_get "$authzUri")"; then _err "get to authz error." + _err "_authorizations_seg" "$_authorizations_seg" + _err "authzUri" "$authzUri" _clearup _on_issue_err "$_post_hook" return 1 From 674b50889e30cbbb10eea33fd4a0ace9e259d51e Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 21:42:12 +0800 Subject: [PATCH 229/272] fix wildcard domains --- acme.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index e65b74af..16f82fdb 100755 --- a/acme.sh +++ b/acme.sh @@ -3422,6 +3422,9 @@ issue() { _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") fi + _debug _main_domain "$_main_domain" + _debug _alt_domains "$_alt_domains" + _key_length="$4" _real_cert="$5" _real_key="$6" @@ -3552,10 +3555,15 @@ issue() { if [ "$ACME_VERSION" = "2" ]; then #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" - for d in $(echo "$_alt_domains" | tr ',' ' '); do - if [ "$d" ]; then - _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" + while true; do + _w_index=1 + d="$(echo "$$_alt_domains" | cut -d , -f "$_w_index")" + _w_index="$(_math "$_w_index" + 1)" + _debug d "$d" + if [ -z "$d" ]; then + break fi + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" done _debug2 _identifiers "$_identifiers" if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then From dd17124ec6eb5d89a68c515bfa4972b5c6e6a4dc Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 21:45:16 +0800 Subject: [PATCH 230/272] fix error --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 16f82fdb..1fb9b476 100755 --- a/acme.sh +++ b/acme.sh @@ -3557,7 +3557,7 @@ issue() { _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" while true; do _w_index=1 - d="$(echo "$$_alt_domains" | cut -d , -f "$_w_index")" + d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")" _w_index="$(_math "$_w_index" + 1)" _debug d "$d" if [ -z "$d" ]; then From 88bbe55b859fef97d18a2d7a67dfee297e41b40f Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 21:54:32 +0800 Subject: [PATCH 231/272] fix wrong wildcard domain interpretation --- acme.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 1fb9b476..fecc1b55 100755 --- a/acme.sh +++ b/acme.sh @@ -3620,10 +3620,16 @@ $_authorizations_map" _debug2 _authorizations_map "$_authorizations_map" fi - alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ') _index=0 _currentRoot="" - for d in $alldomains; do + while true; do + _w_index=1 + d="$(echo "$_main_domain,$_alt_domains," | cut -d , -f "$_w_index")" + _w_index="$(_math "$_w_index" + 1)" + _debug d "$d" + if [ -z "$d" ]; then + break + fi _info "Getting webroot for domain" "$d" _index=$(_math $_index + 1) _w="$(echo $_web_roots | cut -d , -f $_index)" From 931d19eece68266299e492f680ae37747edaa0e8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 21:56:40 +0800 Subject: [PATCH 232/272] fix for wildcard domain interpretation --- acme.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index fecc1b55..4710947b 100755 --- a/acme.sh +++ b/acme.sh @@ -3019,11 +3019,17 @@ _on_before_issue() { _debug Le_LocalAddress "$_chk_local_addr" - alldomains=$(echo "$_chk_main_domain,$_chk_alt_domains" | tr ',' ' ') _index=1 _currentRoot="" _addrIndex=1 - for d in $alldomains; do + while true; do + _w_index=1 + d="$(echo "$_chk_main_domain,$_chk_alt_domains," | cut -d , -f "$_w_index")" + _w_index="$(_math "$_w_index" + 1)" + _debug d "$d" + if [ -z "$d" ]; then + break + fi _debug "Check for domain" "$d" _currentRoot="$(_getfield "$_chk_web_roots" $_index)" _debug "_currentRoot" "$_currentRoot" From 38f1b4d205c67a6b75084956759d2d0d321aadf7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 22:03:58 +0800 Subject: [PATCH 233/272] fix wildcard interpretation --- acme.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 4710947b..e7c40c01 100755 --- a/acme.sh +++ b/acme.sh @@ -2999,6 +2999,8 @@ _on_before_issue() { _chk_pre_hook="$4" _chk_local_addr="$5" _debug _on_before_issue + _debug _chk_main_domain "$_chk_main_domain" + _debug _chk_alt_domains "$_chk_alt_domains" #run pre hook if [ "$_chk_pre_hook" ]; then _info "Run pre hook:'$_chk_pre_hook'" @@ -3022,8 +3024,8 @@ _on_before_issue() { _index=1 _currentRoot="" _addrIndex=1 + _w_index=1 while true; do - _w_index=1 d="$(echo "$_chk_main_domain,$_chk_alt_domains," | cut -d , -f "$_w_index")" _w_index="$(_math "$_w_index" + 1)" _debug d "$d" @@ -3561,8 +3563,8 @@ issue() { if [ "$ACME_VERSION" = "2" ]; then #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" + _w_index=1 while true; do - _w_index=1 d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")" _w_index="$(_math "$_w_index" + 1)" _debug d "$d" @@ -3628,8 +3630,8 @@ $_authorizations_map" _index=0 _currentRoot="" + _w_index=1 while true; do - _w_index=1 d="$(echo "$_main_domain,$_alt_domains," | cut -d , -f "$_w_index")" _w_index="$(_math "$_w_index" + 1)" _debug d "$d" From 28d83d42e2a629d308afddc0cb2526c42bea192d Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Mar 2018 22:09:34 +0800 Subject: [PATCH 234/272] remove tls mode from doc https://github.com/Neilpang/acme.sh/issues/1322 --- README.md | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index bfcb477f..5471c573 100644 --- a/README.md +++ b/README.md @@ -220,22 +220,7 @@ acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 5. Use Standalone TLS server to issue cert - -**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))** - -acme.sh supports `tls-sni-01` validation. - -Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. - -```bash -acme.sh --issue --tls -d example.com -d www.example.com -d cp.example.com -``` - -More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert - - -# 6. Use Apache mode +# 5. Use Apache mode **(requires you to be root/sudoer, since it is required to interact with Apache server)** @@ -255,7 +240,7 @@ We don't want to mess your apache server, don't worry.** More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 7. Use Nginx mode +# 6. Use Nginx mode **(requires you to be root/sudoer, since it is required to interact with Nginx server)** @@ -279,7 +264,7 @@ We don't want to mess your nginx server, don't worry.** More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 8. Automatic DNS API integration +# 7. Automatic DNS API integration If your DNS provider supports API access, we can use that API to automatically issue the certs. @@ -342,7 +327,7 @@ If your DNS provider is not on the supported list above, you can write your own For more details: [How to use DNS API](dnsapi) -# 9. Use DNS manual mode: +# 8. Use DNS manual mode: If your dns provider doesn't support any api access, you can add the txt record by your hand. @@ -376,7 +361,7 @@ Ok, it's done. **Please use dns api mode instead.** -# 10. Issue ECC certificates +# 9. Issue ECC certificates `Let's Encrypt` can now issue **ECDSA** certificates. @@ -408,7 +393,7 @@ Valid values are: -# 11. Issue Wildcard certificates +# 10. Issue Wildcard certificates It's simple, just give a wildcard domain as the `-d` parameter. @@ -418,7 +403,7 @@ acme.sh --issue -d example.com -d *.example.com --dns dns_cf -# 12. How to renew the certs +# 11. How to renew the certs No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. @@ -435,7 +420,7 @@ acme.sh --renew -d example.com --force --ecc ``` -# 13. How to stop cert renewal +# 12. How to stop cert renewal To stop renewal of a cert, you can execute the following to remove the cert from the renewal list: @@ -448,7 +433,7 @@ The cert/key file is not removed from the disk. You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. -# 14. How to upgrade `acme.sh` +# 13. How to upgrade `acme.sh` acme.sh is in constant development, so it's strongly recommended to use the latest code. @@ -473,25 +458,25 @@ acme.sh --upgrade --auto-upgrade 0 ``` -# 15. Issue a cert from an existing CSR +# 14. Issue a cert from an existing CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR -# 16. Under the Hood +# 15. Under the Hood Speak ACME language using shell, directly to "Let's Encrypt". TODO: -# 17. Acknowledgments +# 16. Acknowledgments 1. Acme-tiny: https://github.com/diafygi/acme-tiny 2. ACME protocol: https://github.com/ietf-wg-acme/acme -# 18. License & Others +# 17. License & Others License is GPLv3 @@ -500,7 +485,7 @@ Please Star and Fork me. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. -# 19. Donate +# 18. Donate Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) From 6b26d2b62de49e59517f7f5a0d14c4c91cb6dc2a Mon Sep 17 00:00:00 2001 From: Rid Date: Thu, 15 Mar 2018 09:50:54 +0000 Subject: [PATCH 235/272] Fixed grammatical errors --- dnsapi/dns_cf.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 68264a42..3595b9b0 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -19,8 +19,8 @@ dns_cf_add() { if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then CF_Key="" CF_Email="" - _err "You don't specify cloudflare api key and email yet." - _err "Please create you key and try again." + _err "You didn't specify a cloudflare api key and email yet." + _err "Please create the key and try again." return 1 fi @@ -94,8 +94,8 @@ dns_cf_rm() { if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then CF_Key="" CF_Email="" - _err "You don't specify cloudflare api key and email yet." - _err "Please create you key and try again." + _err "You didn't specify a cloudflare api key and email yet." + _err "Please create the key and try again." return 1 fi From a8b62261f6a02a6601d0fec235325e48deccbd05 Mon Sep 17 00:00:00 2001 From: "Claus F. Strasburger" Date: Fri, 16 Mar 2018 11:21:03 +0100 Subject: [PATCH 236/272] Documentation: what to do when using dns-manual Change the hint that tells you how to use DNS manual (second run needs to be --renew) --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index e7c40c01..79717ad5 100755 --- a/acme.sh +++ b/acme.sh @@ -3819,7 +3819,7 @@ $_authorizations_map" if [ "$dnsadded" = '0' ]; then _savedomainconf "Le_Vlist" "$vlist" _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit." - _err "Please add the TXT records to the domains, and retry again." + _err "Please add the TXT records to the domains, and re-run with --renew." _clearup _on_issue_err "$_post_hook" return 1 From a5c1c30368a176ec76dcb2c379ca82a24d95ab5b Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Mar 2018 21:29:38 +0800 Subject: [PATCH 237/272] update doc --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5471c573..2898629c 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) - [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) - [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) +- [CentOS Web Panel](http://centos-webpanel.com/) +- [lnmp.org](https://lnmp.org/) - [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) # Tested OS From 4ae108009c0000f3d3d1e52c6a544a51a7bf57e4 Mon Sep 17 00:00:00 2001 From: Rafael Gieschke Date: Thu, 15 Mar 2018 02:38:17 +0100 Subject: [PATCH 238/272] dns_pdns.sh: Allow "." as root zone --- dnsapi/dns_pdns.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_pdns.sh b/dnsapi/dns_pdns.sh index 7d807c81..3d99e103 100755 --- a/dnsapi/dns_pdns.sh +++ b/dnsapi/dns_pdns.sh @@ -90,7 +90,7 @@ set_record() { full=$2 txtvalue=$3 - if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then + if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then _err "Set txt record error." return 1 fi @@ -107,7 +107,7 @@ rm_record() { root=$1 full=$2 - if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then + if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then _err "Delete txt record error." return 1 fi @@ -122,7 +122,7 @@ rm_record() { notify_slaves() { root=$1 - if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then + if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root/notify"; then _err "Notify slaves error." return 1 fi @@ -144,15 +144,18 @@ _get_root() { while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) - if [ -z "$h" ]; then - return 1 - fi if _contains "$_zones_response" "\"name\": \"$h.\""; then - _domain="$h" + _domain="$h." + if [ -z "$h" ]; then + _domain="=2E" + fi return 0 fi + if [ -z "$h" ]; then + return 1 + fi i=$(_math $i + 1) done _debug "$domain not found" From 9082862b9dd5df4d893e8bcd37c1804f446e8b6c Mon Sep 17 00:00:00 2001 From: Casper Date: Sat, 17 Mar 2018 14:45:49 +0100 Subject: [PATCH 239/272] Updated --accountemail help https://github.com/Neilpang/acme.sh/issues/1074#issuecomment-337672763 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index e7c40c01..d9bc701a 100755 --- a/acme.sh +++ b/acme.sh @@ -5456,8 +5456,8 @@ Parameters: --cert-home Specifies the home dir to save all the certs, only valid for '--install' command. --config-home Specifies the home dir to save all the configurations. --useragent Specifies the user agent string. it will be saved for future use too. - --accountemail Specifies the account email for registering, Only valid for the '--install' command. - --accountkey Specifies the account key path, Only valid for the '--install' command. + --accountemail Specifies the account email, only valid for the '--install' and '--update-account' command. + --accountkey Specifies the account key path, only valid for the '--install' command. --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days. --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer. --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses. From 32d8f349c9207226f3e030999bc8c880b73c6da5 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 11:04:14 +0800 Subject: [PATCH 240/272] add debug info --- dnsapi/dns_yandex.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 5fbb09d8..318dee0c 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -50,9 +50,9 @@ _PDD_get_domain() { __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) - #_debug "$res1" - __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p') + 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" From 323febe8c78d2d17a40153c97e40e7b641747268 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 11:14:03 +0800 Subject: [PATCH 241/272] add more debug log --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 90627c8d..07cfb0ec 100755 --- a/acme.sh +++ b/acme.sh @@ -4105,7 +4105,7 @@ $_authorizations_map" fi else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then - _err "Sign failed." + _err "Sign failed. $response" _on_issue_err "$_post_hook" return 1 fi From f2aa5c02352308a24d784e1abac08015e0f3e6b3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 11:18:37 +0800 Subject: [PATCH 242/272] update doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2898629c..8a12d9fe 100644 --- a/README.md +++ b/README.md @@ -400,7 +400,7 @@ Valid values are: It's simple, just give a wildcard domain as the `-d` parameter. ```sh -acme.sh --issue -d example.com -d *.example.com --dns dns_cf +acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf ``` From 5d8d217a133977899ebdf62db3eb5462304af422 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 11:36:04 +0800 Subject: [PATCH 243/272] add more debug info --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index 07cfb0ec..db2547dd 100755 --- a/acme.sh +++ b/acme.sh @@ -3247,6 +3247,7 @@ _regAccount() { return 1 fi + _debug2 responseHeaders "$responseHeaders" _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _debug "_accUri" "$_accUri" _savecaconf "ACCOUNT_URL" "$_accUri" From 7e0b334b384e09e50f50c7b94553191513466ba4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 18:20:29 +0800 Subject: [PATCH 244/272] fix empty ACCOUNT_URL for v2 for the first time use --- acme.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index db2547dd..4a2f8253 100755 --- a/acme.sh +++ b/acme.sh @@ -3250,8 +3250,13 @@ _regAccount() { _debug2 responseHeaders "$responseHeaders" _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _debug "_accUri" "$_accUri" + if [ -z "$_accUri" ]; then + _err "Can not find account id url." + _err "$responseHeaders" + return 1 + fi _savecaconf "ACCOUNT_URL" "$_accUri" - export ACCOUNT_URL="$ACCOUNT_URL" + export ACCOUNT_URL="$_accUri" CA_KEY_HASH="$(__calcAccountKeyHash)" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" From 36a7a84080c25eec363e5de9bc35f084a6ea6133 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 18:34:35 +0800 Subject: [PATCH 245/272] fix https://github.com/Neilpang/acme.sh/issues/1411 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4a2f8253..fafd514a 100755 --- a/acme.sh +++ b/acme.sh @@ -4116,7 +4116,7 @@ $_authorizations_map" return 1 fi _rcert="$response" - Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" + Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" echo "$BEGIN_CERT" >"$CERT_PATH" #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then From 6a66ba8a21c7c4ecef5860c48a22186349f60766 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 18:57:56 +0800 Subject: [PATCH 246/272] fix https://github.com/Neilpang/acme.sh/issues/1411 --- acme.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index fafd514a..4cc9483f 100755 --- a/acme.sh +++ b/acme.sh @@ -1617,6 +1617,7 @@ _post() { _debug $httpmethod _debug "_post_url" "$_post_url" _debug2 "body" "$body" + _debug2 "_postContentType" "$_postContentType" _inithttp @@ -1785,6 +1786,11 @@ _send_signed_request() { return 1 fi + if [ "$ACME_VERSION" = "2" ]; then + __request_conent_type="$CONTENT_TYPE_JSON" + else + __request_conent_type="" + fi payload64=$(printf "%s" "$payload" | _base64 | _url_replace) _debug3 payload64 "$payload64" @@ -1797,7 +1803,7 @@ _send_signed_request() { if [ "$ACME_NEW_NONCE" ]; then _debug2 "Get nonce. ACME_NEW_NONCE" "$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE" - if _post "" "$nonceurl" "" "HEAD" "$CONTENT_TYPE_JSON"; then + if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then _headers="$(cat "$HTTP_HEADER")" fi fi @@ -1852,7 +1858,7 @@ _send_signed_request() { fi _debug3 body "$body" - response="$(_post "$body" "$url" "$needbase64" "POST" "$CONTENT_TYPE_JSON")" + response="$(_post "$body" "$url" "$needbase64" "POST" "$__request_conent_type")" _CACHED_NONCE="" if [ "$?" != "0" ]; then From 39852662a62b428d6de14a6bf5fbfb26a744cd9b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 19:29:02 +0800 Subject: [PATCH 247/272] fix content type --- acme.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 4cc9483f..6d4223eb 100755 --- a/acme.sh +++ b/acme.sh @@ -1626,14 +1626,19 @@ _post() { if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " fi - if [ "$_postContentType" ]; then - _CURL="$_CURL -H \"Content-Type: $_postContentType\" " - fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + fi else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + fi fi _ret="$?" if [ "$_ret" != "0" ]; then From e8b54a50876e687c52c572afd87736df08547a9d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 19:32:45 +0800 Subject: [PATCH 248/272] fix ACCOUNT_URL --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 6d4223eb..72d0e2af 100755 --- a/acme.sh +++ b/acme.sh @@ -3538,7 +3538,7 @@ issue() { _saved_account_key_hash="$(_readcaconf "CA_KEY_HASH")" _debug2 _saved_account_key_hash "$_saved_account_key_hash" - if [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then + if [ -z "$ACCOUNT_URL" ] || [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then if ! _regAccount "$_accountkeylength"; then _on_issue_err "$_post_hook" return 1 From 668c43abf36d3ba42216faee096e7e18c8adacda Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 18 Mar 2018 21:06:37 +0800 Subject: [PATCH 249/272] add more debug info --- acme.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 72d0e2af..a69d4bb0 100755 --- a/acme.sh +++ b/acme.sh @@ -4100,13 +4100,15 @@ $_authorizations_map" fi if [ "$code" != "200" ]; then _err "Sign failed, code is not 200." + _err "$response" _on_issue_err "$_post_hook" return 1 fi Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" if ! _get "$Le_LinkCert" >"$CERT_PATH"; then - _err "Sign failed, code is not 200." + _err "Sign failed, can not download cert:$Le_LinkCert." + _err "$response" _on_issue_err "$_post_hook" return 1 fi From 912bcf94873bc4e5906af188e60210517fdcda39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20G=C3=A5rdenberg?= Date: Mon, 19 Mar 2018 11:15:25 +0100 Subject: [PATCH 250/272] Fixed HTTPS-url with regard to #1192 --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 0d8fae73..7262755e 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -279,7 +279,7 @@ _freedns_add_txt_record() { domain_id="$2" subdomain="$3" value="$(printf '%s' "$4" | _url_encode)" - url="http://freedns.afraid.org/subdomain/save.php?step=2" + url="https://freedns.afraid.org/subdomain/save.php?step=2" htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")" From ae329385316511db8a4f377f3c630b2ba31e01d7 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 19 Mar 2018 12:17:47 -0300 Subject: [PATCH 251/272] added dnsapi/dns_kinghost.sh --- dnsapi/dns_kinghost.sh | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 dnsapi/dns_kinghost.sh diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh new file mode 100644 index 00000000..3697e4ae --- /dev/null +++ b/dnsapi/dns_kinghost.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env sh + +#KINGHOST_username="xxxx@sss.com" +#KINGHOST_Password="sdfsdfsdfljlbjkljlkjsdfoiwje" + +KING_Api="https://api.kinghost.net/acme" + +# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Used to add txt record +dns_kinghost_add() { + fulldomain=$1 + txtvalue=$2 + + KINGHOST_username="${KINGHOST_username:-$(_readaccountconf_mutable KINGHOST_username)}" + KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" + if [ -z "$KINGHOST_username" ] || [ -z "$KINGHOST_Password" ]; then + KINGHOST_username="" + KINGHOST_Password="" + _err "You don't specify KingHost api password and email yet." + _err "Please create you key and try again." + return 1 + fi + + #save the credentials to the account conf file. + _saveaccountconf_mutable KINGHOST_username "$KINGHOST_username" + _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" + + _debug "Getting txt records" + _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" + + #This API call returns "status":"ok" if dns record does not exists + #We are creating a new txt record here, so we expect the "ok" status + if ! printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + _kinghost_rest POST "dns" "name=$fulldomain&content=$txtvalue" + if ! printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + return 0; +} + +# Usage: fulldomain txtvalue +# Used to remove the txt record after validation +dns_kinghost_rm() { + fulldomain=$1 + txtvalue=$2 + + KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" + KINGHOST_username="${KINGHOST_username:-$(_readaccountconf_mutable KINGHOST_username)}" + if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_username" ]; then + KINGHOST_Password="" + KINGHOST_username="" + _err "You don't specify KingHost api key and email yet." + _err "Please create you key and try again." + return 1 + fi + + _debug "Getting txt records" + _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" + + #This API call returns "status":"ok" if dns record does not exists + #We are removing a txt record here, so the record must exists + if printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + _kinghost_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" + if ! printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + return 0; +} + + +#################### Private functions below ################################## +_kinghost_rest() { + method=$1 + uri="$2" + data="$3" + _debug "$uri" + + export _H1="X-Auth-Email: $KINGHOST_username" + export _H2="X-Auth-Key: $KINGHOST_Password" + + if [ "$method" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$KING_Api/$uri.json" "" "$method")" + else + response="$(_get "$KING_Api/$uri.json?$data")" + fi + + if [ "$?" != "0" ]; then + _err "error $uri" + return 1 + fi + _debug2 response "$response" + return 0 +} From 2ff6f4d3cfe9494f552fdd0fd72e418a75ece7e5 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 19 Mar 2018 12:26:54 -0300 Subject: [PATCH 252/272] updated docs for dns_kinghost api usage --- README.md | 1 + dnsapi/README.md | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 5471c573..0faea3ed 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ You don't have to do anything manually! 1. zonomi.com DNS API 1. DreamHost.com API 1. DirectAdmin API +1. KingHost (https://www.kinghost.com.br/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 8b4a8358..caae7cff 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -784,6 +784,17 @@ acme.sh --issue --dns dns_da -d example.com -d www.example.com The `DA_Api` and `DA_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 42. Use KingHost DNS API + +API access must be enabled at https://painel.kinghost.com.br/painel.api.php + +``` +export KINGHOST_username="yourusername" +export KINGHOST_Password="yourpassword" +acme.sh --issue --dns dns_kinghost -d example.com -d *.example.com +``` + +The `KINGHOST_username` and `KINGHOST_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. # Use custom API From 48bdfa23771393bfe62d858aacb1110ed1eb5639 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 19 Mar 2018 13:49:58 -0300 Subject: [PATCH 253/272] added doc header to dns_kinghost.sh --- dnsapi/dns_kinghost.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index 3697e4ae..5d3a4935 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -1,7 +1,16 @@ #!/usr/bin/env sh -#KINGHOST_username="xxxx@sss.com" -#KINGHOST_Password="sdfsdfsdfljlbjkljlkjsdfoiwje" +############################################################ +# KingHost API support # +# http://api.kinghost.net/doc/ # +# # +# Author: Felipe Keller Braz # +# Report Bugs here: https://github.com/kinghost/acme.sh # +# # +# Values to export: # +# export KINGHOST_username="email@provider.com" # +# export KINGHOST_Password="xxxxxxxxxx" # +############################################################ KING_Api="https://api.kinghost.net/acme" @@ -83,7 +92,6 @@ dns_kinghost_rm() { return 0; } - #################### Private functions below ################################## _kinghost_rest() { method=$1 From 6787c81abe86a8339d917069f74e2601b4f878ff Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Tue, 20 Mar 2018 09:58:10 -0300 Subject: [PATCH 254/272] renamed KINGHOST_username => KINGHOST_Username --- dnsapi/dns_kinghost.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index 5d3a4935..fc436bbd 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -8,7 +8,7 @@ # Report Bugs here: https://github.com/kinghost/acme.sh # # # # Values to export: # -# export KINGHOST_username="email@provider.com" # +# export KINGHOST_Username="email@provider.com" # # export KINGHOST_Password="xxxxxxxxxx" # ############################################################ @@ -20,10 +20,10 @@ dns_kinghost_add() { fulldomain=$1 txtvalue=$2 - KINGHOST_username="${KINGHOST_username:-$(_readaccountconf_mutable KINGHOST_username)}" + KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" - if [ -z "$KINGHOST_username" ] || [ -z "$KINGHOST_Password" ]; then - KINGHOST_username="" + if [ -z "$KINGHOST_Username" ] || [ -z "$KINGHOST_Password" ]; then + KINGHOST_Username="" KINGHOST_Password="" _err "You don't specify KingHost api password and email yet." _err "Please create you key and try again." @@ -31,7 +31,7 @@ dns_kinghost_add() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable KINGHOST_username "$KINGHOST_username" + _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" _debug "Getting txt records" @@ -62,10 +62,10 @@ dns_kinghost_rm() { txtvalue=$2 KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" - KINGHOST_username="${KINGHOST_username:-$(_readaccountconf_mutable KINGHOST_username)}" - if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_username" ]; then + KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" + if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_Username" ]; then KINGHOST_Password="" - KINGHOST_username="" + KINGHOST_Username="" _err "You don't specify KingHost api key and email yet." _err "Please create you key and try again." return 1 @@ -99,7 +99,7 @@ _kinghost_rest() { data="$3" _debug "$uri" - export _H1="X-Auth-Email: $KINGHOST_username" + export _H1="X-Auth-Email: $KINGHOST_Username" export _H2="X-Auth-Key: $KINGHOST_Password" if [ "$method" != "GET" ]; then From aa9975ad0d03d08a7cd7beaad60b4ff29adc3277 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Tue, 20 Mar 2018 10:08:52 -0300 Subject: [PATCH 255/272] dns_kinghost.sh :: changed printf to echo --- dnsapi/dns_kinghost.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index fc436bbd..2dc57cb8 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -39,14 +39,14 @@ dns_kinghost_add() { #This API call returns "status":"ok" if dns record does not exists #We are creating a new txt record here, so we expect the "ok" status - if ! printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then _err "Error" _err "$response" return 1 fi _kinghost_rest POST "dns" "name=$fulldomain&content=$txtvalue" - if ! printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then _err "Error" _err "$response" return 1 @@ -76,14 +76,14 @@ dns_kinghost_rm() { #This API call returns "status":"ok" if dns record does not exists #We are removing a txt record here, so the record must exists - if printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + if echo "$response" | grep '"status":"ok"' >/dev/null; then _err "Error" _err "$response" return 1 fi _kinghost_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" - if ! printf "%s" "$response" | grep '"status":"ok"' >/dev/null; then + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then _err "Error" _err "$response" return 1 From a0923622aea8f650be8d92116f464d1c426e49ba Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Mar 2018 20:30:52 +0800 Subject: [PATCH 256/272] fix https://github.com/Neilpang/acme.sh/issues/1029 https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode --- acme.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/acme.sh b/acme.sh index a69d4bb0..c1298c44 100755 --- a/acme.sh +++ b/acme.sh @@ -110,10 +110,14 @@ _STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode" _DNS_ALIAS_WIKI="https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode" +_DNS_MANUAL_WIKI="https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode" + _DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." _DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR" +_DNS_MANUAL_ERROR="It seems that you are using dns manual mode. Read this link first: $_DNS_MANUAL_WIKI" + __INTERACTIVE="" if [ -t 1 ]; then __INTERACTIVE="1" @@ -3477,6 +3481,11 @@ issue() { mkdir -p "$DOMAIN_PATH" fi + if _hasfield "$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then + _err "$_DNS_MANUAL_ERROR" + return 1 + fi + _debug "Using ACME_DIRECTORY: $ACME_DIRECTORY" _initAPI @@ -5500,6 +5509,7 @@ Parameters: --listen-v6 Force standalone/tls server to listen at ipv6. --openssl-bin Specifies a custom openssl bin location. --use-wget Force to use wget, if you have both curl and wget installed. + --yes-I-know-dns-manual-mode-enough-go-ahead-please Force to use dns manual mode: $_DNS_MANUAL_WIKI " } @@ -5988,6 +5998,9 @@ _process() { shift fi ;; + --yes-I-know-dns-manual-mode-enough-go-ahead-please) + export FORCE_DNS_MANUAL=1 + ;; --log | --logfile) _log="1" _logfile="$2" From 46ac97a3ff455b6c16b9fabfc4b42f331d9cc7ef Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Mar 2018 20:57:48 +0800 Subject: [PATCH 257/272] update doc --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8a12d9fe..6a268350 100644 --- a/README.md +++ b/README.md @@ -331,6 +331,8 @@ For more details: [How to use DNS API](dnsapi) # 8. Use DNS manual mode: +See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. + If your dns provider doesn't support any api access, you can add the txt record by your hand. ```bash From fbd8ab47eab3b62515978e17bac8609336c32cd5 Mon Sep 17 00:00:00 2001 From: pyriand3r Date: Thu, 22 Mar 2018 11:23:16 +0100 Subject: [PATCH 258/272] only reseller can use do.de's reseller interface --- dnsapi/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index 8b4a8358..504a8b57 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -325,6 +325,8 @@ The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.s ## 17. Use Domain-Offensive/Resellerinterface/Domainrobot API +ATTENTION: You need to be a registered Reseller to be able to use the ResellerInterface. As a normal user you can not use this method. + You will need your login credentials (Partner ID+Password) to the Resellerinterface, and export them before you run `acme.sh`: ``` export DO_PID="KD-1234567" From 6b15cf3f722632bf183a2a2c081652dba531738b Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 22 Mar 2018 13:45:43 -0400 Subject: [PATCH 259/272] Remove template text --- deploy/keychain.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/deploy/keychain.sh b/deploy/keychain.sh index a99ed465..d86b4d03 100644 --- a/deploy/keychain.sh +++ b/deploy/keychain.sh @@ -1,11 +1,5 @@ #!/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 From ba9e7fbf64b907c4bd53864b6a938b885201e346 Mon Sep 17 00:00:00 2001 From: James Gibson Date: Thu, 22 Mar 2018 22:46:21 -0600 Subject: [PATCH 260/272] Clarified the language around the Name.com steps Name.com has simplified the process to obtain API tokens, this clarifies the language around requesting a key. --- dnsapi/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 8b4a8358..ffd61dc6 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -525,8 +525,9 @@ For issues, please report to https://github.com/raidenii/acme.sh/issues. ## 28. Use Name.com API -You'll need to fill out the form at https://www.name.com/reseller/apply to apply -for API username and token. +Create your API token here: https://www.name.com/account/settings/api + +Note: `Namecom_Username` should be your Name.com username and not the token name. If you accidentally run the script with the token name as the username see `~/.acme.sh/account.conf` to fix the issue ``` export Namecom_Username="testuser" From aad309ee4f41da300daf61ac303d4eb6fd3d6bca Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 24 Mar 2018 00:06:39 +0800 Subject: [PATCH 261/272] fix https://github.com/Neilpang/acme.sh/issues/1430 --- acme.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index c1298c44..da8e60c9 100755 --- a/acme.sh +++ b/acme.sh @@ -1806,6 +1806,7 @@ _send_signed_request() { MAX_REQUEST_RETRY_TIMES=5 _request_retry_times=0 while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do + _request_retry_times=$(_math "$_request_retry_times" + 1) _debug3 _request_retry_times "$_request_retry_times" if [ -z "$_CACHED_NONCE" ]; then _headers="" @@ -1836,7 +1837,11 @@ _send_signed_request() { fi nonce="$_CACHED_NONCE" _debug2 nonce "$nonce" - + if [ -z "$nonce" ]; then + _info "Could not get nonce, let's try again." + _sleep 2 + continue + fi if [ "$ACME_VERSION" = "2" ]; then if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' @@ -1894,7 +1899,6 @@ _send_signed_request() { if _contains "$_body" "JWS has invalid anti-replay nonce"; then _info "It seems the CA server is busy now, let's wait and retry." - _request_retry_times=$(_math "$_request_retry_times" + 1) _sleep 5 continue fi From fe843bc466b3d3267ac0b1866bf3e3365663c505 Mon Sep 17 00:00:00 2001 From: martgras Date: Sun, 25 Mar 2018 14:32:51 +0200 Subject: [PATCH 262/272] dns_he - proposed fix for #1438 if you have more than one zone of a domain (e.g. example.com and subdomain.example.com) _find_zone fails. This fix removes partials matches. --- dnsapi/dns_he.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index f42d56af..d196fbec 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -143,7 +143,7 @@ _find_zone() { _debug "Looking for zone \"${_attempted_zone}\"" - line_num="$(echo "$_zone_names" | grep -n "$_attempted_zone" | cut -d : -f 1)" + 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") From 7588fc0989dcfb5f99da70a3ad70621b54cef533 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 26 Mar 2018 09:32:41 +0200 Subject: [PATCH 263/272] Fixes DNSimple for Wildcard certificates --- dnsapi/dns_dnsimple.sh | 55 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/dnsapi/dns_dnsimple.sh b/dnsapi/dns_dnsimple.sh index 0bfe2b99..0a5b79bb 100644 --- a/dnsapi/dns_dnsimple.sh +++ b/dnsapi/dns_dnsimple.sh @@ -39,7 +39,7 @@ dns_dnsimple_add() { _get_records "$_account_id" "$_domain" "$_sub_domain" - if [ "$_records_count" = "0" ]; then +# if [ "$_records_count" = "0" ]; then _info "Adding record" if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then @@ -51,22 +51,22 @@ dns_dnsimple_add() { fi fi _err "Add txt record error." - else - _info "Updating record" - _extract_record_id "$_records" "$_sub_domain" - - if _dnsimple_rest \ - PATCH \ - "$_account_id/zones/$_domain/records/$_record_id" \ - "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - - _info "Updated!" - return 0 - fi - - _err "Update error" - return 1 - fi +# else +# _info "Updating record" +# _extract_record_id "$_records" "$_sub_domain" + +# if _dnsimple_rest \ +# PATCH \ +# "$_account_id/zones/$_domain/records/$_record_id" \ +# "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + +# _info "Updated!" +# return 0 +# fi + +# _err "Update error" +# return 1 +# fi } # fulldomain @@ -84,19 +84,20 @@ dns_dnsimple_rm() { fi _get_records "$_account_id" "$_domain" "$_sub_domain" - _extract_record_id "$_records" "$_sub_domain" + _extract_record_id "$_records" "$_sub_domain" if [ "$_record_id" ]; then - - if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$_record_id"; then - _info "removed record" "$_record_id" - return 0 - fi + echo "$_record_id" | while read -r item + do + if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$item"; then + _info "removed record" "$item" + return 0 + else + _err "failed to remove record" "$item" + return 1 + fi + done fi - - _err "failed to remove record" "$_record_id" - return 1 - } #################### Private functions bellow ################################## From 30283282d2f99afcfd39c45e4b3cdbaa0fef4035 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 26 Mar 2018 09:40:33 +0200 Subject: [PATCH 264/272] Fixing code style according to Travis --- dnsapi/dns_dnsimple.sh | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/dnsapi/dns_dnsimple.sh b/dnsapi/dns_dnsimple.sh index 0a5b79bb..0dd3918a 100644 --- a/dnsapi/dns_dnsimple.sh +++ b/dnsapi/dns_dnsimple.sh @@ -39,34 +39,17 @@ dns_dnsimple_add() { _get_records "$_account_id" "$_domain" "$_sub_domain" -# if [ "$_records_count" = "0" ]; then - _info "Adding record" - if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then - _info "Added" - return 0 - else - _err "Unexpected response while adding text record." - return 1 - fi + _info "Adding record" + if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then + _info "Added" + return 0 + else + _err "Unexpected response while adding text record." + return 1 fi - _err "Add txt record error." -# else -# _info "Updating record" -# _extract_record_id "$_records" "$_sub_domain" - -# if _dnsimple_rest \ -# PATCH \ -# "$_account_id/zones/$_domain/records/$_record_id" \ -# "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - -# _info "Updated!" -# return 0 -# fi - -# _err "Update error" -# return 1 -# fi + fi + _err "Add txt record error." } # fulldomain @@ -87,8 +70,7 @@ dns_dnsimple_rm() { _extract_record_id "$_records" "$_sub_domain" if [ "$_record_id" ]; then - echo "$_record_id" | while read -r item - do + echo "$_record_id" | while read -r item; do if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$item"; then _info "removed record" "$item" return 0 From 4d2a0697edfbded749df960e0cbb8a770794bcfc Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 10:49:34 -0300 Subject: [PATCH 265/272] fix identation dnsapi/dns_kinghost.sh --- .gitignore | 2 + dnsapi/dns_kinghost.sh | 136 ++++++++++++++++++++--------------------- 2 files changed, 70 insertions(+), 68 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a13ea469 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +ae.sh diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index 2dc57cb8..ddf9f899 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -17,79 +17,79 @@ KING_Api="https://api.kinghost.net/acme" # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" # Used to add txt record dns_kinghost_add() { - fulldomain=$1 - txtvalue=$2 - - KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" - KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" - if [ -z "$KINGHOST_Username" ] || [ -z "$KINGHOST_Password" ]; then - KINGHOST_Username="" - KINGHOST_Password="" - _err "You don't specify KingHost api password and email yet." - _err "Please create you key and try again." - return 1 - fi - - #save the credentials to the account conf file. - _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" - _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" - - _debug "Getting txt records" - _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" - - #This API call returns "status":"ok" if dns record does not exists - #We are creating a new txt record here, so we expect the "ok" status - if ! echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - _kinghost_rest POST "dns" "name=$fulldomain&content=$txtvalue" - if ! echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - return 0; + fulldomain=$1 + txtvalue=$2 + + KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" + KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" + if [ -z "$KINGHOST_Username" ] || [ -z "$KINGHOST_Password" ]; then + KINGHOST_Username="" + KINGHOST_Password="" + _err "You don't specify KingHost api password and email yet." + _err "Please create you key and try again." + return 1 + fi + + #save the credentials to the account conf file. + _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" + _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" + + _debug "Getting txt records" + _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" + + #This API call returns "status":"ok" if dns record does not exists + #We are creating a new txt record here, so we expect the "ok" status + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + _kinghost_rest POST "dns" "name=$fulldomain&content=$txtvalue" + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + return 0; } # Usage: fulldomain txtvalue # Used to remove the txt record after validation dns_kinghost_rm() { - fulldomain=$1 - txtvalue=$2 - - KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" - KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" - if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_Username" ]; then - KINGHOST_Password="" - KINGHOST_Username="" - _err "You don't specify KingHost api key and email yet." - _err "Please create you key and try again." - return 1 - fi - - _debug "Getting txt records" - _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" - - #This API call returns "status":"ok" if dns record does not exists - #We are removing a txt record here, so the record must exists - if echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - _kinghost_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" - if ! echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - return 0; + fulldomain=$1 + txtvalue=$2 + + KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" + KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" + if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_Username" ]; then + KINGHOST_Password="" + KINGHOST_Username="" + _err "You don't specify KingHost api key and email yet." + _err "Please create you key and try again." + return 1 + fi + + _debug "Getting txt records" + _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" + + #This API call returns "status":"ok" if dns record does not exists + #We are removing a txt record here, so the record must exists + if echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + _kinghost_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + return 0; } #################### Private functions below ################################## From 7efa54666559b797d38a676f3d8ac9d1a7e61b09 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 10:58:22 -0300 Subject: [PATCH 266/272] removed local .gitignore file --- .gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a13ea469..00000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.idea -ae.sh From e8fd373e6c68a7044a90b82690b917c1b9ce530e Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 10:58:56 -0300 Subject: [PATCH 267/272] removed blank space at ending of dnsapi/dns_kinghost.sh --- dnsapi/dns_kinghost.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index ddf9f899..d3a8fb3a 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -115,4 +115,4 @@ _kinghost_rest() { fi _debug2 response "$response" return 0 -} +} \ No newline at end of file From 86ef6e6987d6f8413f0f9184d008a6a5ea7e62b5 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 11:21:12 -0300 Subject: [PATCH 268/272] fixes on dnsapi/dns_kinghost.sh and dnsapi/README.md --- dnsapi/README.md | 2 +- dnsapi/dns_kinghost.sh | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index f99671e0..e459094e 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -792,7 +792,7 @@ The `DA_Api` and `DA_Api_Insecure` will be saved in `~/.acme.sh/account.conf` an API access must be enabled at https://painel.kinghost.com.br/painel.api.php ``` -export KINGHOST_username="yourusername" +export KINGHOST_Username="yourusername" export KINGHOST_Password="yourpassword" acme.sh --issue --dns dns_kinghost -d example.com -d *.example.com ``` diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index d3a8fb3a..7c0d159b 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -31,8 +31,8 @@ dns_kinghost_add() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" - _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" + _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" + _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" _debug "Getting txt records" _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" @@ -52,7 +52,7 @@ dns_kinghost_add() { return 1 fi - return 0; + return 0 } # Usage: fulldomain txtvalue @@ -63,7 +63,7 @@ dns_kinghost_rm() { KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}" KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}" - if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_Username" ]; then + if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_Username" ]; then KINGHOST_Password="" KINGHOST_Username="" _err "You don't specify KingHost api key and email yet." @@ -89,7 +89,7 @@ dns_kinghost_rm() { return 1 fi - return 0; + return 0 } #################### Private functions below ################################## @@ -115,4 +115,4 @@ _kinghost_rest() { fi _debug2 response "$response" return 0 -} \ No newline at end of file +} From f8fb0e67b458821a913e981111b26196001e3a61 Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 12:17:10 -0300 Subject: [PATCH 269/272] fix dnsapi/dns_kinghost.sh with shfmt utility --- dnsapi/dns_kinghost.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index 7c0d159b..2ac8e6df 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -31,8 +31,8 @@ dns_kinghost_add() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" - _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" + _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username" + _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password" _debug "Getting txt records" _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" From 37bc099d393f661bce904097be027e4ccef87a9a Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 14:27:21 -0300 Subject: [PATCH 270/272] removed redundant api call --- dnsapi/dns_infraws.sh | 102 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 dnsapi/dns_infraws.sh diff --git a/dnsapi/dns_infraws.sh b/dnsapi/dns_infraws.sh new file mode 100644 index 00000000..30abcbfb --- /dev/null +++ b/dnsapi/dns_infraws.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env sh + +############################################################ +# Plugin para criação automática da entrada de DNS txt # +# Uso com o sistema acme.sh # +# # +# Author: Felipe Keller Braz # +# Report Bugs here: infra_interno@kinghost.com.br # +# # +# Values to export: # +# export INFRAWS_Hash="PASSWORD" # +############################################################ + +INFRAWS_Api="http://infra-ws.kinghost.net/serverbackend/acme" + +# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Used to add txt record +dns_infraws_add() { + fulldomain=$1 + txtvalue=$2 + + INFRAWS_Hash="${INFRAWS_Hash:-$(_readaccountconf_mutable INFRAWS_Hash)}" + + if [ -z "$INFRAWS_Hash" ]; then + INFRAWS_Hash="" + _err "You don't specify KingHost api password and email yet." + _err "Please create you key and try again." + return 1 + fi + + #save the credentials to the account conf file. + _saveaccountconf_mutable INFRAWS_Hash "$INFRAWS_Hash" + + _debug "Getting txt records" + infraws_rest GET "dns" "name=$fulldomain&content=$txtvalue" + + #This API call returns "status":"ok" if dns record does not exists + #We are creating a new txt record here, so we expect the "ok" status + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + infraws_rest POST "dns" "name=$fulldomain&content=$txtvalue" + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + return 0 +} + +# Usage: fulldomain txtvalue +# Used to remove the txt record after validation +dns_infraws_rm() { + fulldomain=$1 + txtvalue=$2 + + INFRAWS_Hash="${INFRAWS_Hash:-$(_readaccountconf_mutable INFRAWS_Hash)}" + if [ -z "$INFRAWS_Hash" ]; then + INFRAWS_Hash="" + _err "You don't specify KingHost api key and email yet." + _err "Please create you key and try again." + return 1 + fi + + _debug "Getting txt records" + infraws_rest GET "dns" "name=$fulldomain&content=$txtvalue" + + infraws_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" + if ! echo "$response" | grep '"status":"ok"' >/dev/null; then + _err "Error" + _err "$response" + return 1 + fi + + return 0 +} + +#################### Private functions below ################################## +infraws_rest() { + method=$1 + uri="$2" + data="$3" + _debug "$uri" + + if [ "$method" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$INFRAWS_Api/hash/$INFRAWS_Hash/" "" "$method")" + else + response="$(_get "$INFRAWS_Api/hash/$INFRAWS_Hash/?$data")" + fi + + if [ "$?" != "0" ]; then + _err "error $uri" + return 1 + fi + _debug2 response "$response" + return 0 +} From 2d1d512d0f968f0bdbca36aafdd043ec427a7bed Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Mon, 26 Mar 2018 14:28:52 -0300 Subject: [PATCH 271/272] removed redundant api call --- dnsapi/dns_kinghost.sh | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dnsapi/dns_kinghost.sh b/dnsapi/dns_kinghost.sh index 2ac8e6df..898ab286 100644 --- a/dnsapi/dns_kinghost.sh +++ b/dnsapi/dns_kinghost.sh @@ -71,17 +71,6 @@ dns_kinghost_rm() { return 1 fi - _debug "Getting txt records" - _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue" - - #This API call returns "status":"ok" if dns record does not exists - #We are removing a txt record here, so the record must exists - if echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - _kinghost_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" if ! echo "$response" | grep '"status":"ok"' >/dev/null; then _err "Error" From 986f61ac92bcb38b95d7eaff3612d8fb1ec6a5eb Mon Sep 17 00:00:00 2001 From: Felipe Braz Date: Wed, 28 Mar 2018 10:18:43 -0300 Subject: [PATCH 272/272] deleted wrog file --- dnsapi/dns_infraws.sh | 102 ------------------------------------------ 1 file changed, 102 deletions(-) delete mode 100644 dnsapi/dns_infraws.sh diff --git a/dnsapi/dns_infraws.sh b/dnsapi/dns_infraws.sh deleted file mode 100644 index 30abcbfb..00000000 --- a/dnsapi/dns_infraws.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env sh - -############################################################ -# Plugin para criação automática da entrada de DNS txt # -# Uso com o sistema acme.sh # -# # -# Author: Felipe Keller Braz # -# Report Bugs here: infra_interno@kinghost.com.br # -# # -# Values to export: # -# export INFRAWS_Hash="PASSWORD" # -############################################################ - -INFRAWS_Api="http://infra-ws.kinghost.net/serverbackend/acme" - -# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -# Used to add txt record -dns_infraws_add() { - fulldomain=$1 - txtvalue=$2 - - INFRAWS_Hash="${INFRAWS_Hash:-$(_readaccountconf_mutable INFRAWS_Hash)}" - - if [ -z "$INFRAWS_Hash" ]; then - INFRAWS_Hash="" - _err "You don't specify KingHost api password and email yet." - _err "Please create you key and try again." - return 1 - fi - - #save the credentials to the account conf file. - _saveaccountconf_mutable INFRAWS_Hash "$INFRAWS_Hash" - - _debug "Getting txt records" - infraws_rest GET "dns" "name=$fulldomain&content=$txtvalue" - - #This API call returns "status":"ok" if dns record does not exists - #We are creating a new txt record here, so we expect the "ok" status - if ! echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - infraws_rest POST "dns" "name=$fulldomain&content=$txtvalue" - if ! echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - return 0 -} - -# Usage: fulldomain txtvalue -# Used to remove the txt record after validation -dns_infraws_rm() { - fulldomain=$1 - txtvalue=$2 - - INFRAWS_Hash="${INFRAWS_Hash:-$(_readaccountconf_mutable INFRAWS_Hash)}" - if [ -z "$INFRAWS_Hash" ]; then - INFRAWS_Hash="" - _err "You don't specify KingHost api key and email yet." - _err "Please create you key and try again." - return 1 - fi - - _debug "Getting txt records" - infraws_rest GET "dns" "name=$fulldomain&content=$txtvalue" - - infraws_rest DELETE "dns" "name=$fulldomain&content=$txtvalue" - if ! echo "$response" | grep '"status":"ok"' >/dev/null; then - _err "Error" - _err "$response" - return 1 - fi - - return 0 -} - -#################### Private functions below ################################## -infraws_rest() { - method=$1 - uri="$2" - data="$3" - _debug "$uri" - - if [ "$method" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$INFRAWS_Api/hash/$INFRAWS_Hash/" "" "$method")" - else - response="$(_get "$INFRAWS_Api/hash/$INFRAWS_Hash/?$data")" - fi - - if [ "$?" != "0" ]; then - _err "error $uri" - return 1 - fi - _debug2 response "$response" - return 0 -}
$top_domain' -f 2 | cut -d '<' -f 1)" - _debug2 DNStype "$DNStype" - if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" - # Now get current value for the TXT record. This method may - # not produce accurate results as the value field is truncated - # on this webpage. To get full value we would need to load - # another page. However we don't really need this so long as - # there is only one TXT record for the acme challenge subdomain. - DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" - _debug2 DNSvalue "$DNSvalue" - if [ $found != 0 ]; then - break - # we are breaking out of the loop at the first match of DNS name - # and DNS type (if we are past finding the domainid). This assumes - # that there is only ever one TXT record for the LetsEncrypt/acme - # challenge subdomain. This seems to be a reasonable assumption - # as the acme client deletes the TXT record on successful validation. - fi - else - DNSname="" - DNStype="" - fi + break; fi done - _debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid" - _debug "DNSvalue: $DNSvalue" - if [ -z "$DNSdomainid" ]; then # If domain ID is empty then something went wrong (top level # domain not found at FreeDNS). if [ "$attempts" = "0" ]; then # exhausted maximum retry attempts - _debug "$htmlpage" - _debug "$subdomain_csv" _err "Domain $top_domain not found at FreeDNS" return 1 fi @@ -145,33 +116,10 @@ dns_freedns_add() { _info "Retry loading subdomain page ($attempts attempts remaining)" done - if [ -z "$DNSdataid" ]; then - # If data ID is empty then specific subdomain does not exist yet, need - # to create it this should always be the case as the acme client - # deletes the entry after domain is validated. - _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" - return $? - else - if [ "$txtvalue" = "$DNSvalue" ]; then - # if value in TXT record matches value requested then DNS record - # does not need to be updated. But... - # Testing value match fails. Website is truncating the value field. - # So for now we will always go down the else path. Though in theory - # should never come here anyway as the acme client deletes - # the TXT record on successful validation, so we should not even - # have found a TXT record !! - _info "No update necessary for $fulldomain at FreeDNS" - return 0 - else - # Delete the old TXT record (with the wrong value) - if _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"; then - # And add in new TXT record with the value provided - _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" - fi - return $? - fi - fi - return 0 + # Add in new TXT record with the value provided + _debug "Adding TXT record for $fulldomain, $txtvalue" + _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" + return $? } #Usage: fulldomain txtvalue @@ -205,7 +153,7 @@ dns_freedns_rm() { fi subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's/
' -f 2 | cut -d '<' -f 1)" - _debug2 DNStype "$DNStype" - if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" - _debug2 DNSdataid "$DNSdataid" - DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" - _debug2 DNSvalue "$DNSvalue" - # if [ "$DNSvalue" = "$txtvalue" ]; then - # Testing value match fails. Website is truncating the value - # field. So for now we will assume that there is only one TXT - # field for the sub domain and just delete it. Currently this - # is a safe assumption. - _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" - return $? - # fi + _debug2 "DNSname: $DNSname" + if [ "$DNSname" = "$fulldomain" ]; then + DNStype="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 "DNStype: $DNStype" + if [ "$DNStype" = "TXT" ]; then + DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" + _debug2 "DNSdataid: $DNSdataid" + DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + if _startswith "$DNSvalue" """; then + # remove the quotation from the start + DNSvalue="$(echo "$DNSvalue" | cut -c 7-)" + fi + if _endswith "$DNSvalue" "..."; then + # value was truncated, remove the dot dot dot from the end + DNSvalue="$(echo "$DNSvalue" | sed 's/...$//')" + elif _endswith "$DNSvalue" """; then + # else remove the closing quotation from the end + DNSvalue="$(echo "$DNSvalue" | sed 's/......$//')" + fi + _debug2 "DNSvalue: $DNSvalue" + + if [ -n "$DNSdataid" ] && _startswith "$txtvalue" "$DNSvalue"; then + # Found a match. But note... Website is truncating the + # value field so we are only testing that part that is not + # truncated. This should be accurate enough. + _debug "Deleting TXT record for $fulldomain, $txtvalue" + _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" + return $? + fi + + fi fi done done # If we get this far we did not find a match (after two attempts) # Not necessarily an error, but log anyway. - _debug2 "$subdomain_csv" - _info "Cannot delete TXT record for $fulldomain/$txtvalue. Does not exist at FreeDNS" + _debug3 "$subdomain_csv" + _info "Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS" return 0 } @@ -272,7 +236,7 @@ _freedns_login() { # if cookies is not empty then logon successful if [ -z "$cookies" ]; then - _debug "$htmlpage" + _debug3 "htmlpage: $htmlpage" _err "FreeDNS login failed for user $username. Check $HTTP_HEADER file" return 1 fi @@ -301,7 +265,7 @@ _freedns_retrieve_subdomain_page() { return 1 fi - _debug2 "$htmlpage" + _debug3 "htmlpage: $htmlpage" printf "%s" "$htmlpage" return 0 @@ -323,17 +287,17 @@ _freedns_add_txt_record() { _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post" return 1 elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then - _debug "$htmlpage" + _debug3 "htmlpage: $htmlpage" _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" return 1 elif _contains "$htmlpage" "security code was incorrect"; then - _debug "$htmlpage" + _debug3 "htmlpage: $htmlpage" _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code" _err "Note that you cannot use automatic DNS validation for FreeDNS public domains" return 1 fi - _debug2 "$htmlpage" + _debug3 "htmlpage: $htmlpage" _info "Added acme challenge TXT record for $fulldomain at FreeDNS" return 0 } @@ -352,7 +316,7 @@ _freedns_delete_txt_record() { _err "FreeDNS failed to delete TXT record for $data_id bad RC from _get" return 1 elif ! _contains "$htmlheader" "200 OK"; then - _debug "$htmlheader" + _debug2 "htmlheader: $htmlheader" _err "FreeDNS failed to delete TXT record $data_id" return 1 fi From 62dd3a5380eaffd76edf0ad558e14cd494ac236d Mon Sep 17 00:00:00 2001 From: David Kerr Date: Fri, 9 Mar 2018 16:54:42 -0500 Subject: [PATCH 220/272] Fix Travis CI errors. --- dnsapi/dns_freedns.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 1f03791a..0d8fae73 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -90,13 +90,13 @@ dns_freedns_add() { while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" line="$(echo "$subdomain_csv" | sed -n "${i}p")" - _debug2 "line $line" + _debug2 "line: $line" if [ $found = 0 ] && _contains "$line" "$top_domain