From b8a8e2280db53b68e405f0e37e45d2c569ad6639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Sun, 5 Mar 2017 13:43:01 +0100 Subject: [PATCH 001/403] Added deploy script to deploy to the routeros system --- deploy/README.md | 16 +++++++++++ deploy/routeros.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 deploy/routeros.sh diff --git a/deploy/README.md b/deploy/README.md index 4a13e096..65d8cc28 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -72,3 +72,19 @@ export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart" acme.sh --deploy -d ftp.example.com --deploy-hook exim4 ``` +## 6. Deploy the cert to remote routeros + +```sh +acme.sh --deploy -d ftp.example.com --deploy-hook routeros +``` + +Before you can deploy the certificate to router os, you need to add the id_rsa.pub key to the routeros and assign a user to that key. +The user need to have access to ssh, ftp, read and write. + +Then you need to set the environment variables for the deploy script to work. +```sh +export ROUTER_OS_USERNAME=certuser +export ROUTER_OS_HOST=router.example.com + +acme.sh --deploy -d ftp.example.com --deploy-hook routeros +``` diff --git a/deploy/routeros.sh b/deploy/routeros.sh new file mode 100644 index 00000000..e4e8c464 --- /dev/null +++ b/deploy/routeros.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +#Here is a script to deploy cert to routeros router. + +#returns 0 means success, otherwise error. + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +routeros_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if [ -z "$ROUTER_OS_HOST" ]; then + _err "Need to set the env variable ROUTER_OS_HOST" + return 1 + fi + + if [ -z "$ROUTER_OS_USERNAME" ]; then + _err "Need to set the env variable ROUTER_OS_USERNAME" + return 1 + fi + + _info "Trying to push key '$_ckey' to router" + scp $_ckey $ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain".key" + _info "Trying to push cert '$_ccert' to router" + scp $_ccert $ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain".cer" + _info "Trying to push ca cert '$_cca' to router" + scp $_cca $ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain".ca" + + ssh $ROUTER_OS_USERNAME@$ROUTER_OS_HOST bash -c "' + +/certificate remove $_cdomain.cer_0 + +/certificate remove $_cdomain.ca_0 + +delay 1 + +/certificate import file-name=$_cdomain.cer passphrase=\"\" + +/certificate import file-name=$_cdomain.ca passphrase=\"\" + +/certificate import file-name=$_cdomain.key passphrase=\"\" + +delay 1 + +/file remove $_cdomain.cer + +/file remove $_cdomain.key + +delay 2 + +/ip service set www-ssl certificate=$_cdomain.cer_0 + +'" + + + return 0 +} From 8a604bd2a172a15a58420ec401595c6604d8146f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Mon, 6 Mar 2017 19:39:55 +0100 Subject: [PATCH 002/403] Fixing syntax for schell script checking --- deploy/routeros.sh | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/deploy/routeros.sh b/deploy/routeros.sh index e4e8c464..9471ba8e 100644 --- a/deploy/routeros.sh +++ b/deploy/routeros.sh @@ -31,38 +31,40 @@ routeros_deploy() { fi _info "Trying to push key '$_ckey' to router" - scp $_ckey $ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain".key" + scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key" _info "Trying to push cert '$_ccert' to router" - scp $_ccert $ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain".cer" + scp "$_ccert" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer" _info "Trying to push ca cert '$_cca' to router" - scp $_cca $ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain".ca" - - ssh $ROUTER_OS_USERNAME@$ROUTER_OS_HOST bash -c "' + scp "$_cca" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.ca" + # shellcheck disable=SC2029 + ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" bash -c "' /certificate remove $_cdomain.cer_0 +/certificate remove $_cdomain.cer_1 + /certificate remove $_cdomain.ca_0 delay 1 /certificate import file-name=$_cdomain.cer passphrase=\"\" -/certificate import file-name=$_cdomain.ca passphrase=\"\" - /certificate import file-name=$_cdomain.key passphrase=\"\" +/certificate import file-name=$_cdomain.ca passphrase=\"\" + delay 1 /file remove $_cdomain.cer /file remove $_cdomain.key +/file remove $_cdomain.ca + delay 2 /ip service set www-ssl certificate=$_cdomain.cer_0 '" - - return 0 } From 52351d7dc8f0cccf3139e16ea56e5f1d001e6deb Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 13 Mar 2018 12:43:07 +0100 Subject: [PATCH 003/403] avoid side effects in _printargs A possible fix for https://github.com/Neilpang/acme.sh/issues/1356 --- acme.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acme.sh b/acme.sh index 2a3138cb..d3dea32a 100755 --- a/acme.sh +++ b/acme.sh @@ -139,6 +139,7 @@ __red() { } _printargs() { + local _exitstatus="$?" if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then printf -- "%s" "[$(date)] " fi @@ -148,6 +149,8 @@ _printargs() { printf -- "%s" "$1='$2'" fi printf "\n" + # return the saved exit status + return "$_exitstatus" } _dlg_versions() { From 65a7d56957dd9fa9ffd7b341dd1ad4c3368ab2c9 Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 14 Mar 2018 09:52:58 +0100 Subject: [PATCH 004/403] remove local keyword --- acme.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index d3dea32a..88605b22 100755 --- a/acme.sh +++ b/acme.sh @@ -139,7 +139,7 @@ __red() { } _printargs() { - local _exitstatus="$?" + _exitstatus="$?" if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then printf -- "%s" "[$(date)] " fi @@ -186,6 +186,7 @@ _dlg_versions() { #class _syslog() { + _exitstatus="$?" if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then return fi @@ -199,6 +200,7 @@ _syslog() { fi fi $__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1 + return "$_exitstatus" } _log() { From e629985cf494b388fb23cc8dfc8c5a6179a45de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Mon, 26 Mar 2018 07:41:56 +0200 Subject: [PATCH 005/403] Use _cdomain if ROUTER_OS_HOST is missing --- deploy/routeros.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/routeros.sh b/deploy/routeros.sh index 9471ba8e..27fc3770 100644 --- a/deploy/routeros.sh +++ b/deploy/routeros.sh @@ -21,8 +21,8 @@ routeros_deploy() { _debug _cfullchain "$_cfullchain" if [ -z "$ROUTER_OS_HOST" ]; then - _err "Need to set the env variable ROUTER_OS_HOST" - return 1 + _debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct." + ROUTER_OS_HOST = "$_cdomain" fi if [ -z "$ROUTER_OS_USERNAME" ]; then From 7b327d47c0f2bae9f585030e10e1e847da43bc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Mon, 26 Mar 2018 08:21:31 +0200 Subject: [PATCH 006/403] Fix documentation --- deploy/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/README.md b/deploy/README.md index 6ec033f5..fd2a5fcd 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -254,6 +254,7 @@ acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox ```sh acme.sh --deploy -d ftp.example.com --deploy-hook strongswan +``` ## 10. Deploy the cert to remote routeros From d698c1093aba8d2c2361e3ad968fe15e25024a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Mon, 26 Mar 2018 08:24:04 +0200 Subject: [PATCH 007/403] remove spaces around assignment --- deploy/routeros.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/routeros.sh b/deploy/routeros.sh index 27fc3770..1db74b44 100644 --- a/deploy/routeros.sh +++ b/deploy/routeros.sh @@ -22,7 +22,7 @@ routeros_deploy() { if [ -z "$ROUTER_OS_HOST" ]; then _debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct." - ROUTER_OS_HOST = "$_cdomain" + ROUTER_OS_HOST="$_cdomain" fi if [ -z "$ROUTER_OS_USERNAME" ]; then From 8d38cf4d1f7e8e7059cb629b691a99aff88290c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Mon, 26 Mar 2018 22:00:01 +0200 Subject: [PATCH 008/403] Use allchain instead of ca an cert, add documentation after review --- deploy/README.md | 16 ++++++++++++++++ deploy/routeros.sh | 12 ++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index fd2a5fcd..2693b6e4 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -265,6 +265,8 @@ acme.sh --deploy -d ftp.example.com --deploy-hook routeros Before you can deploy the certificate to router os, you need to add the id_rsa.pub key to the routeros and assign a user to that key. The user need to have access to ssh, ftp, read and write. +There are no need to enable ftp service for the script to work, as they are transmitted over SCP, however ftp is needed to store the files on the router. + Then you need to set the environment variables for the deploy script to work. ```sh export ROUTER_OS_USERNAME=certuser @@ -272,3 +274,17 @@ export ROUTER_OS_HOST=router.example.com acme.sh --deploy -d ftp.example.com --deploy-hook routeros ``` + +The deploy script will remove previously deployed certificates, and it does this with an assumption on how RouterOS names imported certificates, adding a "cer_0" suffix at the end. This is true for versions 6.32 -> 6.41.3, but it is not guaranteed that it will be true for future versions when upgrading. + +If the router have other certificates with the same name as the one beeing deployed, then this script will remove those certificates. + +At the end of the script, the services that use those certificates could be updated. Currently only the www-ssl service is beeing updated, but more services could be added. + +For instance: +``` +/ip service set www-ssl certificate=$_cdomain.cer_0 +/ip service set api-ssl certificate=$_cdomain.cer_0 +``` + +One optional thing to do as well is to create a script that updates all the required services and run that script in a single command. diff --git a/deploy/routeros.sh b/deploy/routeros.sh index 1db74b44..d590bc9a 100644 --- a/deploy/routeros.sh +++ b/deploy/routeros.sh @@ -32,10 +32,8 @@ routeros_deploy() { _info "Trying to push key '$_ckey' to router" scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key" - _info "Trying to push cert '$_ccert' to router" - scp "$_ccert" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer" - _info "Trying to push ca cert '$_cca' to router" - scp "$_cca" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.ca" + _info "Trying to push cert '$_cfullchain' to router" + scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer" # shellcheck disable=SC2029 ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" bash -c "' @@ -43,24 +41,18 @@ routeros_deploy() { /certificate remove $_cdomain.cer_1 -/certificate remove $_cdomain.ca_0 - delay 1 /certificate import file-name=$_cdomain.cer passphrase=\"\" /certificate import file-name=$_cdomain.key passphrase=\"\" -/certificate import file-name=$_cdomain.ca passphrase=\"\" - delay 1 /file remove $_cdomain.cer /file remove $_cdomain.key -/file remove $_cdomain.ca - delay 2 /ip service set www-ssl certificate=$_cdomain.cer_0 From e36fbd6af5c56b4672077694e7db9adebcb49d25 Mon Sep 17 00:00:00 2001 From: Grant Millar Date: Fri, 20 Apr 2018 09:41:07 +0100 Subject: [PATCH 009/403] Fix DNSimple when zone has > 100 records The _get_records function currently returns the first 100 records. As our TXT is added most recently, if you have > 100 records it will not be returned. I've changed the function to sort by ID DESC, so it will always return the latest 100 records. --- dnsapi/dns_dnsimple.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dnsimple.sh b/dnsapi/dns_dnsimple.sh index 0dd3918a..b2cba584 100644 --- a/dnsapi/dns_dnsimple.sh +++ b/dnsapi/dns_dnsimple.sh @@ -152,7 +152,7 @@ _get_records() { sub_domain=$3 _debug "fetching txt records" - _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100" + _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100&sort=id:desc" if ! _contains "$response" "\"id\":"; then _err "failed to retrieve records" From 676402d918cb064999d05d33289e9f41fb3fe48a Mon Sep 17 00:00:00 2001 From: Kordian Bruck Date: Thu, 26 Apr 2018 11:40:17 +0200 Subject: [PATCH 010/403] Increase serial when adding txt records --- dnsapi/dns_ispconfig.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ispconfig.sh b/dnsapi/dns_ispconfig.sh index 1e500ad6..c8f7eedc 100755 --- a/dnsapi/dns_ispconfig.sh +++ b/dnsapi/dns_ispconfig.sh @@ -128,7 +128,7 @@ _ISPC_addTxt() { curSerial="$(date +%s)" curStamp="$(date +'%F %T')" params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\"" - curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}}}" + curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}},\"update_serial\":true}}" curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")" _debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'" _debug "Result of _ISPC_addTxt: '$curResult'" From 4e05062def15d4b99dcc6e26457ebebe853e977f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Bli=C5=BE=C3=ADk?= Date: Mon, 30 Apr 2018 15:09:07 +0200 Subject: [PATCH 011/403] add tele3-dns plugin --- README.md | 1 + dnsapi/README.md | 12 ++++++++ dnsapi/dns_tele3.sh | 70 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 dnsapi/dns_tele3.sh diff --git a/README.md b/README.md index 52f79974..f395e49a 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,7 @@ You don't have to do anything manually! 1. Zilore (https://zilore.com) 1. Loopia.se API 1. acme-dns (https://github.com/joohoi/acme-dns) +1. TELE3 (https://www.tele3.cz) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index a90b3f75..ef61a244 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -859,7 +859,19 @@ acme.sh --issue --dns dns_acmedns -d example.com -d www.example.com The credentials will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 46. Use TELE3 API +First you need to login to your TELE3 account to set your API-KEY. +https://www.tele3.cz/system-acme-api.html + +``` +export TELE3_Key="MS2I4uPPaI..." +export TELE3_Secret="kjhOIHGJKHg" + +acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com +``` + +The TELE3_Key and TELE3_Secret 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_tele3.sh b/dnsapi/dns_tele3.sh new file mode 100644 index 00000000..3dced486 --- /dev/null +++ b/dnsapi/dns_tele3.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env sh +# +# tele3.cz DNS API +# +# Author: Roman Blizik +# Report Bugs here: https://github.com/par-pa/acme.sh +# +# -- +# export TELE3_Key="MS2I4uPPaI..." +# export TELE3_Secret="kjhOIHGJKHg" +# -- + +TELE3_API="https://www.tele3.cz/acme/" + +######## Public functions ##################### + +dns_tele3_add() { + _info "Using TELE3 DNS" + data="\"ope\":\"add\", \"domain\":\"$1\", \"value\":\"$2\"" + if ! _tele3_call; then + _err "Publish zone failed" + return 1 + fi + + _info "Zone published" +} + +dns_tele3_rm() { + _info "Using TELE3 DNS" + data="\"ope\":\"rm\", \"domain\":\"$1\", \"value\":\"$2\"" + if ! _tele3_call; then + _err "delete TXT record failed" + return 1 + fi + + _info "TXT record successfully deleted" +} + +#################### Private functions below ################################## + +_tele3_init() { + TELE3_Key="${TELE3_Key:-$(_readaccountconf_mutable TELE3_Key)}" + TELE3_Secret="${TELE3_Secret:-$(_readaccountconf_mutable TELE3_Secret)}" + if [ -z "$TELE3_Key" ] || [ -z "$TELE3_Secret" ]; then + TELE3_Key="" + TELE3_Secret="" + _err "You must export variables: TELE3_Key and TELE3_Secret" + return 1 + fi + + #save the config variables to the account conf file. + _saveaccountconf_mutable TELE3_Key "$TELE3_Key" + _saveaccountconf_mutable TELE3_Secret "$TELE3_Secret" +} + +_tele3_call() { + _tele3_init + data="{\"key\":\"$TELE3_Key\", \"secret\":\"$TELE3_Secret\", $data}" + + _debug data "$data" + + response="$(_post "$data" "$TELE3_API" "" "POST")" + _debug response "$response" + + if [ "$response" != "success" ]; then + _err "$response" + return 1 + fi +} + From 70b56eb527400f645e78004c56fd4154b9b2df1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Bli=C5=BE=C3=ADk?= Date: Wed, 2 May 2018 11:13:10 +0200 Subject: [PATCH 012/403] remove whitespace --- dnsapi/dns_tele3.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_tele3.sh b/dnsapi/dns_tele3.sh index 3dced486..76c90913 100644 --- a/dnsapi/dns_tele3.sh +++ b/dnsapi/dns_tele3.sh @@ -67,4 +67,3 @@ _tele3_call() { return 1 fi } - From 03a1386902fc38ce042c04e7af11c1fa629b6c58 Mon Sep 17 00:00:00 2001 From: Kordian Bruck Date: Wed, 2 May 2018 23:01:52 +0200 Subject: [PATCH 013/403] Update serial also when deleting the token --- dnsapi/dns_ispconfig.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_ispconfig.sh b/dnsapi/dns_ispconfig.sh index c8f7eedc..2d8d6b0a 100755 --- a/dnsapi/dns_ispconfig.sh +++ b/dnsapi/dns_ispconfig.sh @@ -128,7 +128,7 @@ _ISPC_addTxt() { curSerial="$(date +%s)" curStamp="$(date +'%F %T')" params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\"" - curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}},\"update_serial\":true}}" + curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}},\"update_serial\":true}" curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")" _debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'" _debug "Result of _ISPC_addTxt: '$curResult'" @@ -160,7 +160,7 @@ _ISPC_rmTxt() { *) unset IFS _info "Retrieved Record ID." - curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\"}" + curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\",\"update_serial\":true}" curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")" _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'" _debug "Result of _ISPC_rmTxt: '$curResult'" From c58465d6304afc7d4e1d052fdf3b70af8ce84a7e Mon Sep 17 00:00:00 2001 From: Vlad Roskov Date: Thu, 3 May 2018 00:57:50 +0300 Subject: [PATCH 014/403] fix comparison on empty var --- 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 318dee0c..496fcde4 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -54,7 +54,7 @@ _PDD_get_domain() { _debug2 "res1" "$res1" __found="$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p')" _debug "found: $__found results on page" - if [ "$__found" -lt 20 ]; then + if [ "0$__found" -lt 20 ]; then _debug "last page: $__page" __last=1 fi From f254bb39a541801d136f8e08dc973dbd5d9f3cda Mon Sep 17 00:00:00 2001 From: Vlad Roskov Date: Thu, 3 May 2018 00:58:25 +0300 Subject: [PATCH 015/403] bail out on no access --- dnsapi/dns_yandex.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 496fcde4..fc122f02 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" - _PDD_get_domain "$fulldomain" + _PDD_get_domain "$fulldomain" || return 1 _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" - _PDD_get_domain "$fulldomain" + _PDD_get_domain "$fulldomain" || return 1 _debug "Found suitable domain in pdd: $curDomain" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" From 2f15ad4be091e4cf6ce1ea394f1ee6edb16ada38 Mon Sep 17 00:00:00 2001 From: Vlad Roskov Date: Thu, 3 May 2018 01:00:51 +0300 Subject: [PATCH 016/403] fix authentication --- dnsapi/dns_yandex.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index fc122f02..6d928098 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -30,12 +30,13 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" - record_id=$(pdd_get_record_id "${fulldomain}") - _debug "Result: $record_id" _PDD_get_domain "$fulldomain" || return 1 _debug "Found suitable domain in pdd: $curDomain" + record_id=$(pdd_get_record_id "${fulldomain}") + _debug "Result: $record_id" + curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" From f85348ba949f8a25f5cbdf9ca2252e91e5077c15 Mon Sep 17 00:00:00 2001 From: Vlad Roskov Date: Thu, 3 May 2018 01:01:14 +0300 Subject: [PATCH 017/403] fix delete multiple records --- dnsapi/dns_yandex.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 6d928098..a4f39784 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -37,10 +37,12 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curUri="https://pddimp.yandex.ru/api2/admin/dns/del" - curData="domain=${curDomain}&record_id=${record_id}" - curResult="$(_post "${curData}" "${curUri}")" - _debug "Result: $curResult" + for rec_i in $record_id; do + curUri="https://pddimp.yandex.ru/api2/admin/dns/del" + curData="domain=${curDomain}&record_id=${rec_i}" + curResult="$(_post "${curData}" "${curUri}")" + _debug "Result: $curResult" + done } #################### Private functions below ################################## From 6567bb4c12d684f5856a96115777770ae762ccf3 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Thu, 10 May 2018 11:51:59 +0800 Subject: [PATCH 018/403] Update haproxy deploy hook Add functionality to add OCSP stapling info (.ocsp file), issuer (.issuer file) and multi-cert bundles (suffix on pem file based on key type). This also corrects the order of key, certificate and intermediate in the PEM file, which although HAProxy does not seem to care, was incorrect in the prior version. --- deploy/haproxy.sh | 268 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 228 insertions(+), 40 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 5c1a40e2..02f6a069 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -1,8 +1,32 @@ #!/usr/bin/env sh -#Here is a script to deploy cert to haproxy server. - -#returns 0 means success, otherwise error. +# Script for acme.sh to deploy certificates to haproxy +# +# The following variables can be exported: +# +# export DEPLOY_HAPROXY_PEM="" +# +# REQUIRED: Defines location of PEM file for HAProxy +# +# export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" +# +# OPTIONAL: Reload command used post deploy +# +# export DEPLOY_HAPROXY_ISSUER="no" +# +# OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer" +# Note: Required for OCSP stapling to work +# +# export DEPLOY_HAPROXY_BUNDLE="no" +# +# OPTIONAL: Deploy this certificate as part of a multi-cert bundle +# This adds a suffix to the certificate based on the certificate type +# eg RSA certificates will have .rsa as a suffix to the file name +# HAProxy will load all certificates and provide one or the other +# depending on client capabilities +# Note: This functionality requires HAProxy was compiled against +# a version of OpenSSL that supports this. +# ######## Public functions ##################### @@ -14,45 +38,209 @@ haproxy_deploy() { _cca="$4" _cfullchain="$5" - _debug _cdomain "$_cdomain" - _debug _ckey "$_ckey" - _debug _ccert "$_ccert" - _debug _cca "$_cca" - _debug _cfullchain "$_cfullchain" - - # handle reload preference - DEFAULT_HAPROXY_RELOAD="/usr/sbin/service haproxy restart" - if [ -z "${DEPLOY_HAPROXY_RELOAD}" ]; then - _reload="${DEFAULT_HAPROXY_RELOAD}" - _cleardomainconf DEPLOY_HAPROXY_RELOAD + # Some defaults + DEPLOY_HAPROXY_BUNDLE_DEFAULT="no" + DEPLOY_HAPROXY_ISSUER_DEFAULT="no" + DEPLOY_HAPROXY_RELOAD_DEFAULT="systemctl reload haproxy" + + if [ -f "${DOMAIN_CONF}" ]; then + # shellcheck disable=SC1090 + . "${DOMAIN_CONF}" + fi + + _debug _cdomain "${_cdomain}" + _debug _ckey "${_ckey}" + _debug _ccert "${_ccert}" + _debug _cca "${_cca}" + _debug _cfullchain "${_cfullchain}" + + # CERT is required + if [ -z "${DEPLOY_HAPROXY_PEM}" ]; then + if [ -z "${Le_Deploy_haproxy_pem}" ]; then + _err "{DEPLOY_HAPROXY_PEM} not defined." + return 1 + fi + else + Le_Deploy_haproxy_cert="${DEPLOY_HAPROXY_PEM}" + _savedomainconf Le_Deploy_haproxy_cert "${Le_Deploy_haproxy_pem}" + fi + + # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then + Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}" + _savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}" + elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then + Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" + fi + + # ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then + Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}" + _savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}" + elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then + Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER_DEFAULT}" + fi + + # RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then + Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}" + _savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}" + elif [ -z "${Le_Deploy_haproxy_reload}" ]; then + Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}" + fi + + # Set the suffix depending if we are creating a bundle or not + if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then + _info "Bundle creation requested" + # Initialise $Le_KeyLength if its not already set + if [ -z "${Le_KeyLength}" ]; then + Le_KeyLength="" + fi + if _isEccKey "${Le_KeyLength}"; then + _info "ECC key type so set suffix to .ecc" + _suffix=".ecc" + else + _info "RSA key type so set suffix to .rsa" + _suffix=".rsa" + fi else - _reload="${DEPLOY_HAPROXY_RELOAD}" - _savedomainconf DEPLOY_HAPROXY_RELOAD "$DEPLOY_HAPROXY_RELOAD" - fi - _savedomainconf DEPLOY_HAPROXY_PEM_PATH "$DEPLOY_HAPROXY_PEM_PATH" - - # work out the path where the PEM file should go - _pem_path="${DEPLOY_HAPROXY_PEM_PATH}" - if [ -z "$_pem_path" ]; then - _err "Path to save PEM file not found. Please define DEPLOY_HAPROXY_PEM_PATH." - return 1 - fi - _pem_full_path="$_pem_path/$_cdomain.pem" - _info "Full path to PEM $_pem_full_path" - - # combine the key and fullchain into a single pem and install - cat "$_cfullchain" "$_ckey" >"$_pem_full_path" - chmod 600 "$_pem_full_path" - _info "Certificate successfully deployed" - - # restart HAProxy - _info "Run reload: $_reload" - if eval "$_reload"; then - _info "Reload success!" - return 0 + _suffix="" + fi + + # Set variables for later + _pem="${Le_Deploy_haproxy_pem}${_suffix}" + _issuer="${_pem}.issuer" + _ocsp="${_pem}.ocsp" + _reload="${Le_Deploy_haproxy_reload}" + + _info "Deploying PEM file" + # Create a temporary PEM file + _temppem="$(_mktemp)" + _debug _temppem "${_temppem}" + cat "${_ckey}" "${_ccert}" "${_cca}" > "${_temppem}" + _ret="$?" + + # Check that we could create the temporary file + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} returned during PEM file creation" + [ -f "${_temppem}" ] && rm -f "${_temppem}" + return ${_ret} + fi + + # Move PEM file into place + _info "Moving new certificate into place" + _debug _pem "${_pem}" + cat "${_temppem}" > "${_pem}" + _ret=$? + + # Clean up temp file + [ -f "${_temppem}" ] && rm -f "${_temppem}" + + # Deal with any failure of moving PEM file into place + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} returned while moving new certificate into place" + return ${_ret} + fi + + # Update .issuer file if requested + if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then + _info "Updating .issuer file" + _debug _issuer "${_issuer}" + cat "${_cca}" > "${_issuer}" + _ret="$?" + + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} returned while copying issuer/CA certificate into place" + return ${_ret} + fi + else + [ -f "${_issuer}" ] _err "Issuer file update not requested but .issuer file exists" + fi + + # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option + if [ -z "${Le_OCSP_Staple}" ]; then + Le_OCSP_Staple="0" + fi + if [ "${Le_OCSP_Staple}" = "1" ]; then + _info "Updating OCSP stapling info" + _debug _ocsp "${_ocsp}" + _info "Extracting OCSP URL" + _ocsp_url=$(openssl x509 -noout -ocsp_uri -in "${_pem}") + _debug _ocsp_url "${_ocsp_url}" + + # Only process OCSP if URL was present + if [ "${_ocsp_url}" != "" ]; then + # Extract the hostname from the OCSP URL + _info "Extracting OCSP URL" + _ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3) + _debug _ocsp_host "${_ocsp_host}" + + # Only process the certificate if we have a .issuer file + if [ -r "${_issuer}" ]; then + # Check if issuer cert is also a root CA cert + _subjectdn=$(openssl x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) + _debug _subjectdn "${_subjectdn}" + _issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) + _debug _issuerdn "${_issuerdn}" + _info "Requesting OCSP response" + # Request the OCSP response from the issuer and store it + if [ "${_subjectdn}" = "${_issuerdn}" ]; then + # If the issuer is a CA cert then our command line has "-CAfile" added + openssl ocsp \ + -issuer "${_issuer}" \ + -cert "${_pem}" \ + -url "${_ocsp_url}" \ + -header Host "${_ocsp_host}" \ + -respout "${_ocsp}" \ + -verify_other "${_issuer}" \ + -no_nonce \ + -CAfile "${_issuer}" + _ret=$? + else + # Issuer is not a root CA so no "-CAfile" option + openssl ocsp \ + -issuer "${_issuer}" \ + -cert "${_pem}" \ + -url "${_ocsp_url}" \ + -header Host "${_ocsp_host}" \ + -respout "${_ocsp}" \ + -verify_other "${_issuer}" \ + -no_nonce + _ret=$? + fi + else + # Non fatal: No issuer file was present so no OCSP stapling file created + _err "OCSP stapling in use but no .issuer file was present" + fi + else + # Non fatal: No OCSP url was found int the certificate + _err "OCSP update requested but no OCSP URL was found in certificate" + fi + + # Check return code of openssl command + if [ "${_ret}" != "0" ]; then + _err "Updating OCSP stapling failed with return code ${_ret}" + return ${_ret} + fi + else + # An OCSP file was already present but certificate did not have OCSP extension + if [ -f "${_ocsp}" ]; then + _err "OCSP was not requested but .ocsp file exists." + # Should remove the file at this step, although HAProxy just ignores it in this case + # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file" + fi + fi + + # Reload HAProxy + _debug _reload "${_reload}" + eval "${_reload}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _info "Reload successful" else - _err "Reload error" - return 1 + _err "Error code ${_ret} during reload" + return ${_ret} fi + return 0 } From 3a95bfb699b602a5ce544f375a2aba5b266a3d94 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Thu, 10 May 2018 12:02:58 +0800 Subject: [PATCH 019/403] Document updated haproxy deploy hook --- deploy/README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 181989da..621e15fc 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -258,15 +258,27 @@ acme.sh --deploy -d ftp.example.com --deploy-hook strongswan ## 10. Deploy the cert to HAProxy -You must specify the path where you want the concatenated key and certificate chain written. +You must specify the file where you want the concatenated key and certificate chain written. ```sh -export DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy +export DEPLOY_HAPROXY_PEM=/etc/haproxy/server.pem ``` You may optionally define the command to reload HAProxy. The value shown below will be used as the default if you don't set this environment variable. ```sh -export DEPLOY_HAPROXY_RELOAD="/usr/sbin/service haproxy restart" +export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" +``` + +You may optionally specify that the issuer certificate is transferred to "${DEPLOY_HAPROXY_PEM}.issuer". This is a requirement to support OCSP stapling in HAProxy. The value shown below will be used as the default if you don't set this environment variable. + +```sh +export DEPLOY_HAPROXY_ISSUER="no" +``` + +You may optionally specify that you wish to support HAProxy's multi-cert bundle functionality. This allows serving of both RSA and ECC certificates on the same proxy. This adds a ".rsa" or ".ecc" suffix to the files generated (.pem, .ocsp and .issuer). The value shown below will be used as the default if you don't set this environment variable. + +```sh +export DEPLOY_HAPROXY_BUNDLE="no" ``` You can then deploy the certificate as follows From c47e67e52c95f18a0133413763287741c7d02865 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Thu, 10 May 2018 12:06:25 +0800 Subject: [PATCH 020/403] Fix variable name --- deploy/haproxy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 02f6a069..06bd74ea 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -61,8 +61,8 @@ haproxy_deploy() { return 1 fi else - Le_Deploy_haproxy_cert="${DEPLOY_HAPROXY_PEM}" - _savedomainconf Le_Deploy_haproxy_cert "${Le_Deploy_haproxy_pem}" + Le_Deploy_haproxy_pem="${DEPLOY_HAPROXY_PEM}" + _savedomainconf Le_Deploy_haproxy_pem "${Le_Deploy_haproxy_pem}" fi # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" From 707e053949c839073c4b1f46db09a4ebb299aab5 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Thu, 10 May 2018 12:18:03 +0800 Subject: [PATCH 021/403] whitespace fixes --- deploy/haproxy.sh | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 06bd74ea..47a935bc 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -117,7 +117,7 @@ haproxy_deploy() { # Create a temporary PEM file _temppem="$(_mktemp)" _debug _temppem "${_temppem}" - cat "${_ckey}" "${_ccert}" "${_cca}" > "${_temppem}" + cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}" _ret="$?" # Check that we could create the temporary file @@ -130,7 +130,7 @@ haproxy_deploy() { # Move PEM file into place _info "Moving new certificate into place" _debug _pem "${_pem}" - cat "${_temppem}" > "${_pem}" + cat "${_temppem}" >"${_pem}" _ret=$? # Clean up temp file @@ -146,7 +146,7 @@ haproxy_deploy() { if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then _info "Updating .issuer file" _debug _issuer "${_issuer}" - cat "${_cca}" > "${_issuer}" + cat "${_cca}" >"${_issuer}" _ret="$?" if [ "${_ret}" != "0" ]; then @@ -187,25 +187,25 @@ haproxy_deploy() { if [ "${_subjectdn}" = "${_issuerdn}" ]; then # If the issuer is a CA cert then our command line has "-CAfile" added openssl ocsp \ - -issuer "${_issuer}" \ - -cert "${_pem}" \ - -url "${_ocsp_url}" \ - -header Host "${_ocsp_host}" \ - -respout "${_ocsp}" \ - -verify_other "${_issuer}" \ - -no_nonce \ - -CAfile "${_issuer}" + -issuer "${_issuer}" \ + -cert "${_pem}" \ + -url "${_ocsp_url}" \ + -header Host "${_ocsp_host}" \ + -respout "${_ocsp}" \ + -verify_other "${_issuer}" \ + -no_nonce \ + -CAfile "${_issuer}" _ret=$? else # Issuer is not a root CA so no "-CAfile" option openssl ocsp \ - -issuer "${_issuer}" \ - -cert "${_pem}" \ - -url "${_ocsp_url}" \ - -header Host "${_ocsp_host}" \ - -respout "${_ocsp}" \ - -verify_other "${_issuer}" \ - -no_nonce + -issuer "${_issuer}" \ + -cert "${_pem}" \ + -url "${_ocsp_url}" \ + -header Host "${_ocsp_host}" \ + -respout "${_ocsp}" \ + -verify_other "${_issuer}" \ + -no_nonce _ret=$? fi else @@ -219,8 +219,8 @@ haproxy_deploy() { # Check return code of openssl command if [ "${_ret}" != "0" ]; then - _err "Updating OCSP stapling failed with return code ${_ret}" - return ${_ret} + _err "Updating OCSP stapling failed with return code ${_ret}" + return ${_ret} fi else # An OCSP file was already present but certificate did not have OCSP extension @@ -228,7 +228,7 @@ haproxy_deploy() { _err "OCSP was not requested but .ocsp file exists." # Should remove the file at this step, although HAProxy just ignores it in this case # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file" - fi + fi fi # Reload HAProxy From ba20af48d32720fa011be9b27c6b5597cb32ff54 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Thu, 10 May 2018 15:25:28 +0800 Subject: [PATCH 022/403] Support HAPROXY_DEPLOY_PEM_PATH Adds compatibility to original haproxy deploy hook while still allowing custom PEM file name (via HAPROXY_DEPLOY_PEM_NAME) --- deploy/haproxy.sh | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 47a935bc..cadc8a60 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -4,9 +4,15 @@ # # The following variables can be exported: # -# export DEPLOY_HAPROXY_PEM="" +# export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem" # -# REQUIRED: Defines location of PEM file for HAProxy +# Defines the name of the PEM file. +# Defaults to "domain.pem" +# +# export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy" +# +# Defines location of PEM file for HAProxy. +# Defaults to /etc/haproxy # # export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" # @@ -39,6 +45,8 @@ haproxy_deploy() { _cfullchain="$5" # Some defaults + DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy" + DEPLOY_HAPROXY_PEM_NAME_DEFAULT="${_cdomain}.pem" DEPLOY_HAPROXY_BUNDLE_DEFAULT="no" DEPLOY_HAPROXY_ISSUER_DEFAULT="no" DEPLOY_HAPROXY_RELOAD_DEFAULT="systemctl reload haproxy" @@ -54,15 +62,28 @@ haproxy_deploy() { _debug _cca "${_cca}" _debug _cfullchain "${_cfullchain}" - # CERT is required - if [ -z "${DEPLOY_HAPROXY_PEM}" ]; then - if [ -z "${Le_Deploy_haproxy_pem}" ]; then - _err "{DEPLOY_HAPROXY_PEM} not defined." - return 1 - fi + # PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then + Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}" + _savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}" + elif [ -z "${Le_Deploy_haproxy_pem_path}" ]; then + Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" + fi + + # Ensure PEM_PATH exists + if [ -d "${Le_Deploy_haproxy_pem_path}" ]; then + _debug "PEM_PATH ${Le_Deploy_haproxy_pem_path} exists" else - Le_Deploy_haproxy_pem="${DEPLOY_HAPROXY_PEM}" - _savedomainconf Le_Deploy_haproxy_pem "${Le_Deploy_haproxy_pem}" + _err "PEM_PATH ${Le_Deploy_haproxy_pem_path} does not exist" + return 1 + fi + + # PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" + if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then + Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}" + _savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}" + elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then + Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" fi # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" @@ -108,7 +129,7 @@ haproxy_deploy() { fi # Set variables for later - _pem="${Le_Deploy_haproxy_pem}${_suffix}" + _pem="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}" _issuer="${_pem}.issuer" _ocsp="${_pem}.ocsp" _reload="${Le_Deploy_haproxy_reload}" From 675e2d25d6f7c75745d866c6b08f9414977134a4 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Thu, 10 May 2018 15:28:54 +0800 Subject: [PATCH 023/403] update for new haproxy deploy vars --- deploy/README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 621e15fc..7b058c4d 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -258,9 +258,16 @@ acme.sh --deploy -d ftp.example.com --deploy-hook strongswan ## 10. Deploy the cert to HAProxy -You must specify the file where you want the concatenated key and certificate chain written. +You may specify the directory where you want the concatenated key and certificate chain written. The value shown below will be used as the default if you don't set this environment variable. + +```sh +export DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy +``` + +You may optionally specify the file name where you want the concatenated key and certificate chain written. The value shown below will be used as the default if you don't set this environment variable. + ```sh -export DEPLOY_HAPROXY_PEM=/etc/haproxy/server.pem +export DEPLOY_HAPROXY_PEM_PATH=$domain ``` You may optionally define the command to reload HAProxy. The value shown below will be used as the default if you don't set this environment variable. From 08d29a8342309e4c4a7c9a63c88af9d2dea26735 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Mon, 14 May 2018 10:58:46 +0800 Subject: [PATCH 024/403] Fix return from reload --- deploy/haproxy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index cadc8a60..cf5dc329 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -257,10 +257,10 @@ haproxy_deploy() { eval "${_reload}" _ret=$? if [ "${_ret}" != "0" ]; then - _info "Reload successful" - else _err "Error code ${_ret} during reload" return ${_ret} + else + _info "Reload successful" fi return 0 From 733b4e0a342d2bb2096b9e88e8ca7b93ba2449d5 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Mon, 14 May 2018 11:26:03 +0800 Subject: [PATCH 025/403] Fix Le_Keylength case --- deploy/haproxy.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index cf5dc329..75e76ef0 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -113,11 +113,11 @@ haproxy_deploy() { # Set the suffix depending if we are creating a bundle or not if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then _info "Bundle creation requested" - # Initialise $Le_KeyLength if its not already set - if [ -z "${Le_KeyLength}" ]; then - Le_KeyLength="" + # Initialise $Le_Keylength if its not already set + if [ -z "${Le_Keylength}" ]; then + Le_Keylength="" fi - if _isEccKey "${Le_KeyLength}"; then + if _isEccKey "${Le_Keylength}"; then _info "ECC key type so set suffix to .ecc" _suffix=".ecc" else From 7d19d784dfd34691cca574c26ef004e6df303e9a Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Mon, 14 May 2018 13:16:56 +0800 Subject: [PATCH 026/403] Update cert suffix for bundles .ocsp generation --- deploy/haproxy.sh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 75e76ef0..0f5874d6 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -118,15 +118,16 @@ haproxy_deploy() { Le_Keylength="" fi if _isEccKey "${Le_Keylength}"; then - _info "ECC key type so set suffix to .ecc" - _suffix=".ecc" + _info "ECC key type detected" + _suffix=".ecdsa" else - _info "RSA key type so set suffix to .rsa" + _info "RSA key type detected" _suffix=".rsa" fi else _suffix="" fi + _debug _suffix "${_suffix}" # Set variables for later _pem="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}" @@ -215,7 +216,8 @@ haproxy_deploy() { -respout "${_ocsp}" \ -verify_other "${_issuer}" \ -no_nonce \ - -CAfile "${_issuer}" + -CAfile "${_issuer}" | \ + grep -q "${_pem}: good" _ret=$? else # Issuer is not a root CA so no "-CAfile" option @@ -226,7 +228,8 @@ haproxy_deploy() { -header Host "${_ocsp_host}" \ -respout "${_ocsp}" \ -verify_other "${_issuer}" \ - -no_nonce + -no_nonce | \ + grep -q "${_pem}: good" _ret=$? fi else @@ -238,10 +241,9 @@ haproxy_deploy() { _err "OCSP update requested but no OCSP URL was found in certificate" fi - # Check return code of openssl command + # Non fatal: Check return code of openssl command if [ "${_ret}" != "0" ]; then _err "Updating OCSP stapling failed with return code ${_ret}" - return ${_ret} fi else # An OCSP file was already present but certificate did not have OCSP extension From 8d348954a7f9af9418727159f5c4376133c06a60 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Mon, 14 May 2018 13:22:46 +0800 Subject: [PATCH 027/403] Whitepspace --- deploy/haproxy.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 0f5874d6..f6e3716f 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -216,8 +216,8 @@ haproxy_deploy() { -respout "${_ocsp}" \ -verify_other "${_issuer}" \ -no_nonce \ - -CAfile "${_issuer}" | \ - grep -q "${_pem}: good" + -CAfile "${_issuer}" \ + | grep -q "${_pem}: good" _ret=$? else # Issuer is not a root CA so no "-CAfile" option @@ -228,8 +228,8 @@ haproxy_deploy() { -header Host "${_ocsp_host}" \ -respout "${_ocsp}" \ -verify_other "${_issuer}" \ - -no_nonce | \ - grep -q "${_pem}: good" + -no_nonce \ + | grep -q "${_pem}: good" _ret=$? fi else From e9782c3219722e590f84f2aa3d6cc056564a141e Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 15 May 2018 13:18:50 +0200 Subject: [PATCH 028/403] Create dns_netcup.sh --- dnsapi/dns_netcup.sh | 146 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 dnsapi/dns_netcup.sh diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh new file mode 100644 index 00000000..7a8002a7 --- /dev/null +++ b/dnsapi/dns_netcup.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env sh + +#Requirments: jq +#developed by linux-insideDE + +NC_Apikey="${NC_Apikey:-$(_readaccountconf_mutable NC_Apikey)}" +NC_Apipw="${NC_Apipw:-$(_readaccountconf_mutable NC_Apipw)}" +NC_CID="${NC_CID:-$(_readaccountconf_mutable NC_CID)}" +end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON" +client="" + +dns_netcup_add() { + login + if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then + _err "No Credentials given" + return 1 + fi + _saveaccountconf_mutable NC_Apikey "$NC_Apikey" + _saveaccountconf_mutable NC_Apipw "$NC_Apipw" + _saveaccountconf_mutable NC_CID "$NC_CID" + fulldomain=$1 + txtvalue=$2 + tld="" + domain="" + exit=0 + i=20 + while [ "$i" -gt 0 ]; + do + tmp=$(echo "$fulldomain" | cut -d'.' -f$i) + if [ "$tmp" != "" ]; then + if [ "$tld" = "" ]; then + tld=$tmp + else + domain=$tmp + exit=$i + break; + fi + fi + i=$((i - 1)) + done + inc="" + i=1 + while [ "$i" -lt "$exit" ]; + do + if [ "$((exit-1))" = "$i" ]; then + inc="$inc$i" + break; + else + if [ "$inc" = "" ]; then + inc="$i," + else + inc="$inc$i," + fi + fi + i=$((i + 1)) + done + + tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain.$tld\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$tmp\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") + _debug "$msg" + if [ "$(echo "$msg" | jq -r .status)" != "success" ]; then + _err "$msg" + return 1 + fi + logout +} + +dns_netcup_rm() { + login + fulldomain=$1 + txtvalue=$2 + tld="" + domain="" + exit=0 + i=20 + while [ "$i" -gt 0 ]; + do + tmp=$(echo "$fulldomain" | cut -d'.' -f$i) + if [ "$tmp" != "" ]; then + if [ "$tld" = "" ]; then + tld=$tmp + else + domain=$tmp + exit=$i + break; + fi + fi + i=$((i - 1)) + done + inc="" + i=1 + while [ "$i" -lt "$exit" ]; + do + if [ "$((exit-1))" = "$i" ]; then + inc="$inc$i" + break; + else + if [ "$inc" = "" ]; then + inc="$i," + else + inc="$inc$i," + fi + fi + i=$((i + 1)) + done + tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) + doma="$domain.$tld" + rec=$(getRecords "$doma") + ids=$(echo "$rec" | jq -r ".[]|select(.destination==\"$txtvalue\")|.id") + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$doma\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$tmp\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") + _debug "$msg" + if [ "$(echo "$msg" | jq -r .status)" != "success" ]; then + _err "$msg" + return 1 + fi + logout +} + +login() { + tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") + sid=$(echo "$tmp" | jq -r .responsedata.apisessionid) + _debug "$tmp" + if [ "$(echo "$tmp" | jq -r .status)" != "success" ]; then + _err "$tmp" + return 1 + fi +} +logout() { + tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") + _debug "$tmp" + if [ "$(echo "$tmp" | jq -r .status)" != "success" ]; then + _err "$tmp" + return 1 + fi +} +getRecords() { + tmp2=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$1\"}}" "$end" "" "POST") + xxd=$(echo "$tmp2" | jq -r ".responsedata.dnsrecords" | tr '[' ' ' | tr ']' ' ') + xcd=$(echo "$xxd" | sed 's/}\s{/},{/g') + echo "[ $xcd ]" + _debug "$tmp2" + if [ "$(echo "$tmp2" | jq -r .status)" != "success" ]; then + _err "$tmp2" + return 1 + fi +} From 3cd5b9ca2ed24ce74d5f81ef300879e7c24a0bff Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 15 May 2018 13:21:25 +0200 Subject: [PATCH 029/403] added netcup dns api --- dnsapi/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index ef6c9d09..ed165362 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -876,6 +876,22 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com ``` The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. +## 47. Use netcup DNS API to automatically issue cert + +First you need to login to your CCP account to get your API Key and API Password. +This script requires ``jq`` +``` +export NC_Apikey="" +export NC_Apipw="" +export NC_CID="" +``` + +Now, let's issue a cert: +``` +acme.sh --issue --dns dns_netcup -d example.com -d www.example.com +``` + +The `NC_Apikey`,`NC_Apipw` and `NC_CID` 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 f3a622d1a747f2460ea3ec231e14461e8a15049c Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 15 May 2018 13:22:55 +0200 Subject: [PATCH 030/403] added netcup dns api --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f395e49a..18b878d0 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,7 @@ You don't have to do anything manually! 1. Loopia.se API 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) +1. netcup DNS API (https://www.netcup.de) And: From 6a4aad1aa8287c3362b566d7216ea92416f2e7d9 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 15 May 2018 14:38:29 +0200 Subject: [PATCH 031/403] replaced increment/decrement with _math function --- dnsapi/dns_netcup.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 7a8002a7..2e31e13d 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -36,7 +36,7 @@ dns_netcup_add() { break; fi fi - i=$((i - 1)) + i=$(_math "$i" - 1) done inc="" i=1 @@ -52,7 +52,7 @@ dns_netcup_add() { inc="$inc$i," fi fi - i=$((i + 1)) + i=$(_math "$i" + 1) done tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) @@ -85,7 +85,7 @@ dns_netcup_rm() { break; fi fi - i=$((i - 1)) + i=$(_math "$i" - 1) done inc="" i=1 @@ -101,7 +101,7 @@ dns_netcup_rm() { inc="$inc$i," fi fi - i=$((i + 1)) + i=$(_math "$i" + 1) done tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) doma="$domain.$tld" From ca1d62bec07ef4233383d9652a6a8ce6f2e509b5 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 15 May 2018 16:21:57 +0200 Subject: [PATCH 032/403] removed jq dependencies --- dnsapi/dns_netcup.sh | 47 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 2e31e13d..7e52dd9f 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -#Requirments: jq + #developed by linux-insideDE NC_Apikey="${NC_Apikey:-$(_readaccountconf_mutable NC_Apikey)}" @@ -58,7 +58,7 @@ dns_netcup_add() { tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain.$tld\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$tmp\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") _debug "$msg" - if [ "$(echo "$msg" | jq -r .status)" != "success" ]; then + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" return 1 fi @@ -106,10 +106,29 @@ dns_netcup_rm() { tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) doma="$domain.$tld" rec=$(getRecords "$doma") - ids=$(echo "$rec" | jq -r ".[]|select(.destination==\"$txtvalue\")|.id") + + ida=0000 + idv=0001 + ids=0000000000 + i=1 + while [ "$i" -ne 0 ]; + do + specrec=$(_getfield "$rec" "$i" ";") + idv="$ida" + ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g') + txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g') + i=$(_math "$i" + 1) + if [ "$txtvalue" = "$txtv" ]; then + i=0 + ids="$ida" + fi + if [ "$ida" = "$idv" ]; then + i=0 + fi + done msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$doma\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$tmp\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") _debug "$msg" - if [ "$(echo "$msg" | jq -r .status)" != "success" ]; then + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" return 1 fi @@ -117,30 +136,28 @@ dns_netcup_rm() { } login() { - tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - sid=$(echo "$tmp" | jq -r .responsedata.apisessionid) + tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") + sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') _debug "$tmp" - if [ "$(echo "$tmp" | jq -r .status)" != "success" ]; then - _err "$tmp" + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" return 1 fi } logout() { tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") _debug "$tmp" - if [ "$(echo "$tmp" | jq -r .status)" != "success" ]; then - _err "$tmp" + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" return 1 fi } getRecords() { tmp2=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$1\"}}" "$end" "" "POST") - xxd=$(echo "$tmp2" | jq -r ".responsedata.dnsrecords" | tr '[' ' ' | tr ']' ' ') - xcd=$(echo "$xxd" | sed 's/}\s{/},{/g') - echo "[ $xcd ]" + echo $(echo "$tmp2" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') _debug "$tmp2" - if [ "$(echo "$tmp2" | jq -r .status)" != "success" ]; then - _err "$tmp2" + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" return 1 fi } From ed2ba6bc3aa88fa1d9ba8761ea4b92c3939441c4 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 15 May 2018 16:22:40 +0200 Subject: [PATCH 033/403] removed jq dependencies --- dnsapi/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index ed165362..cc2f476a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -879,7 +879,6 @@ The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will ## 47. Use netcup DNS API to automatically issue cert First you need to login to your CCP account to get your API Key and API Password. -This script requires ``jq`` ``` export NC_Apikey="" export NC_Apipw="" From 4715a1a5e0d6a1a6c0c2d462cba0f01a37389d88 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Wed, 16 May 2018 22:07:44 +0200 Subject: [PATCH 034/403] satisfy shellcheck --- dnsapi/dns_netcup.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 7e52dd9f..755d22b9 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -1,6 +1,4 @@ #!/usr/bin/env sh - - #developed by linux-insideDE NC_Apikey="${NC_Apikey:-$(_readaccountconf_mutable NC_Apikey)}" @@ -154,7 +152,8 @@ logout() { } getRecords() { tmp2=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$1\"}}" "$end" "" "POST") - echo $(echo "$tmp2" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') + out=$(echo "$tmp2" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') + echo "$out" _debug "$tmp2" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" From 48e8022095a9bd993ed0633066fd7a65d51a0bd8 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 29 May 2018 16:23:28 +0200 Subject: [PATCH 035/403] improved handling for third level domains --- dnsapi/dns_netcup.sh | 131 +++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 81 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 755d22b9..00edb5b5 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -18,48 +18,33 @@ dns_netcup_add() { _saveaccountconf_mutable NC_CID "$NC_CID" fulldomain=$1 txtvalue=$2 - tld="" domain="" - exit=0 - i=20 - while [ "$i" -gt 0 ]; - do - tmp=$(echo "$fulldomain" | cut -d'.' -f$i) - if [ "$tmp" != "" ]; then - if [ "$tld" = "" ]; then - tld=$tmp - else - domain=$tmp - exit=$i - break; - fi - fi - i=$(_math "$i" - 1) - done - inc="" - i=1 - while [ "$i" -lt "$exit" ]; + exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) + exit=$(_math "$exit" + 1) + i=$exit + + while [ "$exit" -gt 0 ] do - if [ "$((exit-1))" = "$i" ]; then - inc="$inc$i" - break; + tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") + if [ "$(_math "$i" - "$exit")" -eq 0 ]; then + domain="$tmp" else - if [ "$inc" = "" ]; then - inc="$i," - else - inc="$inc$i," - fi - fi - i=$(_math "$i" + 1) + domain="$tmp.$domain" + fi + if [ "$(_math "$i" - "$exit")" -ge 1 ]; then + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") + _debug "$msg" + if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + else + break; + fi + fi + fi + exit=$(_math "$exit" - 1) done - - tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) - msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain.$tld\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$tmp\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") - _debug "$msg" - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - fi logout } @@ -67,43 +52,36 @@ dns_netcup_rm() { login fulldomain=$1 txtvalue=$2 - tld="" + domain="" - exit=0 - i=20 - while [ "$i" -gt 0 ]; - do - tmp=$(echo "$fulldomain" | cut -d'.' -f$i) - if [ "$tmp" != "" ]; then - if [ "$tld" = "" ]; then - tld=$tmp - else - domain=$tmp - exit=$i - break; - fi - fi - i=$(_math "$i" - 1) - done - inc="" - i=1 - while [ "$i" -lt "$exit" ]; + exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) + exit=$(_math "$exit" + 1) + i=$exit + rec="" + + while [ "$exit" -gt 0 ] do - if [ "$((exit-1))" = "$i" ]; then - inc="$inc$i" - break; + tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") + if [ "$(_math "$i" - "$exit")" -eq 0 ]; then + domain="$tmp" else - if [ "$inc" = "" ]; then - inc="$i," - else - inc="$inc$i," + domain="$tmp.$domain" + fi + if [ "$(_math "$i" - "$exit")" -ge 1 ]; then + msg=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$domain\"}}" "$end" "" "POST") + rec=$(echo "$msg" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') + _debug "$msg" + if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + else + break; + fi fi fi - i=$(_math "$i" + 1) + exit=$(_math "$exit" - 1) done - tmp=$(echo "$fulldomain" | cut -d'.' -f$inc) - doma="$domain.$tld" - rec=$(getRecords "$doma") ida=0000 idv=0001 @@ -123,8 +101,9 @@ dns_netcup_rm() { if [ "$ida" = "$idv" ]; then i=0 fi - done - msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$doma\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$tmp\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") + done + + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") _debug "$msg" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" @@ -150,13 +129,3 @@ logout() { return 1 fi } -getRecords() { - tmp2=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$1\"}}" "$end" "" "POST") - out=$(echo "$tmp2" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') - echo "$out" - _debug "$tmp2" - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - fi -} From 206be3c1619a699af3e53636935e64f51493cd2f Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 29 May 2018 22:38:52 +0800 Subject: [PATCH 036/403] fix https://github.com/Neilpang/acme.sh/issues/1633 --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 5e794a5a..713170b7 100755 --- a/acme.sh +++ b/acme.sh @@ -4676,19 +4676,19 @@ _installcert() { if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then cp "$_real_cert" "$_backup_path/cert.bak" fi - cat "$CERT_PATH" >"$_real_cert" + cat "$CERT_PATH" >"$_real_cert" || return 1 fi if [ "$_real_ca" ]; then _info "Installing CA to:$_real_ca" if [ "$_real_ca" = "$_real_cert" ]; then echo "" >>"$_real_ca" - cat "$CA_CERT_PATH" >>"$_real_ca" + cat "$CA_CERT_PATH" >>"$_real_ca" || return 1 else if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then cp "$_real_ca" "$_backup_path/ca.bak" fi - cat "$CA_CERT_PATH" >"$_real_ca" + cat "$CA_CERT_PATH" >"$_real_ca" || return 1 fi fi @@ -4698,9 +4698,9 @@ _installcert() { cp "$_real_key" "$_backup_path/key.bak" fi if [ -f "$_real_key" ]; then - cat "$CERT_KEY_PATH" >"$_real_key" + cat "$CERT_KEY_PATH" >"$_real_key" || return 1 else - cat "$CERT_KEY_PATH" >"$_real_key" + cat "$CERT_KEY_PATH" >"$_real_key" || return 1 chmod 600 "$_real_key" fi fi @@ -4710,7 +4710,7 @@ _installcert() { if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then cp "$_real_fullchain" "$_backup_path/fullchain.bak" fi - cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" + cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" || return 1 fi if [ "$_reload_cmd" ]; then From c7b904501c7ecc3054cee92937733d45647e3690 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 29 May 2018 16:56:07 +0200 Subject: [PATCH 037/403] make shfmt happy --- dnsapi/dns_netcup.sh | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 00edb5b5..59e92703 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -13,18 +13,18 @@ dns_netcup_add() { _err "No Credentials given" return 1 fi - _saveaccountconf_mutable NC_Apikey "$NC_Apikey" - _saveaccountconf_mutable NC_Apipw "$NC_Apipw" - _saveaccountconf_mutable NC_CID "$NC_CID" + _saveaccountconf_mutable NC_Apikey "$NC_Apikey" + _saveaccountconf_mutable NC_Apipw "$NC_Apipw" + _saveaccountconf_mutable NC_CID "$NC_CID" fulldomain=$1 txtvalue=$2 domain="" exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) exit=$(_math "$exit" + 1) i=$exit - - while [ "$exit" -gt 0 ] - do + + while + [ "$exit" -gt 0 ]; do tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") if [ "$(_math "$i" - "$exit")" -eq 0 ]; then domain="$tmp" @@ -34,13 +34,13 @@ dns_netcup_add() { if [ "$(_math "$i" - "$exit")" -ge 1 ]; then msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") _debug "$msg" - if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then + if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" return 1 else - break; - fi + break + fi fi fi exit=$(_math "$exit" - 1) @@ -52,57 +52,57 @@ dns_netcup_rm() { login fulldomain=$1 txtvalue=$2 - + domain="" exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) exit=$(_math "$exit" + 1) i=$exit rec="" - - while [ "$exit" -gt 0 ] - do + + while + [ "$exit" -gt 0 ]; do tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") if [ "$(_math "$i" - "$exit")" -eq 0 ]; then domain="$tmp" else domain="$tmp.$domain" fi - if [ "$(_math "$i" - "$exit")" -ge 1 ]; then + if [ "$(_math "$i" - "$exit")" -ge 1 ]; then msg=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$domain\"}}" "$end" "" "POST") rec=$(echo "$msg" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') - _debug "$msg" - if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then + _debug "$msg" + if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" return 1 else - break; - fi + break + fi fi fi exit=$(_math "$exit" - 1) done - + ida=0000 idv=0001 - ids=0000000000 + ids=0000000000 i=1 - while [ "$i" -ne 0 ]; - do + while + [ "$i" -ne 0 ]; do specrec=$(_getfield "$rec" "$i" ";") idv="$ida" ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g') - txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g') + txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g') i=$(_math "$i" + 1) if [ "$txtvalue" = "$txtv" ]; then i=0 ids="$ida" - fi + fi if [ "$ida" = "$idv" ]; then i=0 fi done - + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") _debug "$msg" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then @@ -113,7 +113,7 @@ dns_netcup_rm() { } login() { - tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") + tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') _debug "$tmp" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then From 69b780ee321c15dd5e8348766389a140277d9871 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 29 May 2018 17:24:53 +0200 Subject: [PATCH 038/403] Update dns_netcup.sh --- dnsapi/dns_netcup.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 59e92703..573550ed 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -102,7 +102,6 @@ dns_netcup_rm() { i=0 fi done - msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") _debug "$msg" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then From f90a2ae1959ea359412a0b570de99e083babed65 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 12 Jun 2018 21:19:27 +0800 Subject: [PATCH 039/403] check UNABLE_TO_AUTHENTICATE --- dnsapi/dns_gd.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh index 97902dfe..7cf47386 100755 --- a/dnsapi/dns_gd.sh +++ b/dnsapi/dns_gd.sh @@ -168,5 +168,9 @@ _gd_rest() { return 1 fi _debug2 response "$response" + if _contains "$response" "UNABLE_TO_AUTHENTICATE"; then + _err "It seems that your api key or secret is not correct." + return 1 + fi return 0 } From d987d61ea96da50a2efd733724b7f6b49c8da7df Mon Sep 17 00:00:00 2001 From: Santeri Kannisto Date: Thu, 28 Jun 2018 09:38:14 +0200 Subject: [PATCH 040/403] Issue #1328 bug fix v3 Eliminated php dependency with a private function for urlencode using sed. Php had failed on godaddy due to multiple php instances and naturally cron using the one without the necessary -r option. Compared to previous PR the sed code is now POSIX and should work on all environments. --- deploy/cpanel_uapi.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh index 4563b9c4..053a0c92 100644 --- a/deploy/cpanel_uapi.sh +++ b/deploy/cpanel_uapi.sh @@ -2,8 +2,12 @@ # Here is the script to deploy the cert to your cpanel using the cpanel API. # Uses command line uapi. --user option is needed only if run as root. # Returns 0 when success. -# Written by Santeri Kannisto -# Public domain, 2017 +# +# Please note that I am no longer using Github. If you want to report an issue +# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/ +# +# Written by Santeri Kannisto +# Public domain, 2017-2018 #export DEPLOY_CPANEL_USER=myusername @@ -28,15 +32,9 @@ cpanel_uapi_deploy() { _err "The command uapi is not found." return 1 fi - if ! _exists php; then - _err "The command php is not found." - return 1 - fi # read cert and key files and urlencode both - _certstr=$(cat "$_ccert") - _keystr=$(cat "$_ckey") - _cert=$(php -r "echo urlencode(\"$_certstr\");") - _key=$(php -r "echo urlencode(\"$_keystr\");") + _cert=$(cat "$_ccert" | _url_encode) + _key=$(cat "$_ckey" | _url_encode) _debug _cert "$_cert" _debug _key "$_key" From 05dea7b22ac15c6748e79f4beed02c22b39b3784 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 28 Jun 2018 20:34:29 +0800 Subject: [PATCH 041/403] fix warning --- deploy/cpanel_uapi.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh index 053a0c92..01cb94ee 100644 --- a/deploy/cpanel_uapi.sh +++ b/deploy/cpanel_uapi.sh @@ -33,8 +33,8 @@ cpanel_uapi_deploy() { return 1 fi # read cert and key files and urlencode both - _cert=$(cat "$_ccert" | _url_encode) - _key=$(cat "$_ckey" | _url_encode) + _cert=$(_url_encode < "$_ccert") + _key=$(_url_encode < "$_ckey") _debug _cert "$_cert" _debug _key "$_key" From 9c545059ae43396c49b35c1870496ec58406b495 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 28 Jun 2018 22:21:22 +0800 Subject: [PATCH 042/403] fix warning --- deploy/cpanel_uapi.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh index 01cb94ee..44844f79 100644 --- a/deploy/cpanel_uapi.sh +++ b/deploy/cpanel_uapi.sh @@ -33,8 +33,8 @@ cpanel_uapi_deploy() { return 1 fi # read cert and key files and urlencode both - _cert=$(_url_encode < "$_ccert") - _key=$(_url_encode < "$_ckey") + _cert=$(_url_encode <"$_ccert") + _key=$(_url_encode <"$_ckey") _debug _cert "$_cert" _debug _key "$_key" From 26c669e42da5e87c8d616e6c20c53e26e94d2c21 Mon Sep 17 00:00:00 2001 From: Will Date: Sun, 1 Jul 2018 18:53:47 -0400 Subject: [PATCH 043/403] Update README.md - HTTPS For centminmod.com Link Update README.md - HTTPS For centminmod.com Link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f395e49a..cb2c8cb2 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) - [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt) - [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty) - [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) -- [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html) +- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) - [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) From 28e4bcf67f82c9aa6e88224aa528cb629eff5743 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 16:04:18 +0200 Subject: [PATCH 044/403] initial version with Euserv.eu DNS API Support --- dnsapi/dns_euserv.sh | 358 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 dnsapi/dns_euserv.sh diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh new file mode 100644 index 00000000..1a58df51 --- /dev/null +++ b/dnsapi/dns_euserv.sh @@ -0,0 +1,358 @@ +#!/usr/bin/env sh + +#This is the euserv.eu api wrapper for acme.sh +# +#Author: Michael Brueckner +#Report Bugs: https://www.github.com/initit/acme.sh or mbr@initit.de + +# +#EUSERV_Username="username" +# +#EUSERV_Password="password" +# +# Dependencies: +# ------------- +# - none - + +EUSERV_Api="https://api.euserv.net" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_euserv_add() { + fulldomain="$(echo "$1" | _lower_case)" + txtvalue=$2 + + EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}" + EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}" + if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then + EUSERV_Username="" + EUSERV_Password="" + _err "You don't specify euserv user and password yet." + _err "Please create your key and try again." + return 1 + fi + + #save the user and email to the account conf file. + _saveaccountconf_mutable EUSERV_Username "$EUSERV_Username" + _saveaccountconf_mutable EUSERV_Password "$EUSERV_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _info "Adding record" + _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue" + +} + +#fulldomain txtvalue +dns_euserv_rm() { + + fulldomain="$(echo "$1" | _lower_case)" + txtvalue=$2 + + EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}" + EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}" + if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then + EUSERV_Username="" + EUSERV_Password="" + _err "You don't specify euserv user and password yet." + _err "Please create your key and try again." + return 1 + fi + + #save the user and email to the account conf file. + _saveaccountconf_mutable EUSERV_Username "$EUSERV_Username" + _saveaccountconf_mutable EUSERV_Password "$EUSERV_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug "_sub_domain" "$_sub_domain" + _debug "_domain" "$_domain" + + _debug "Getting txt records" + + xml_content=$(printf ' + + domain.dns_get_active_records + + + + + + login + + %s + + + + password + + %s + + + + domain_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get txt records" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + +# _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) +# _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + + if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then + _info "Do not need to delete record" + else + # find block where txtvalue is in. the record_id is allways prior this line! + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + # record_id is the last entry with a number, identified by the postfix of + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + _info "Deleting record" + _euserv_delete_record "$_record_id" + fi + +} + +#################### Private functions below ################################## + +_euserv_get_domain_orders() { +# returns: _euserv_domain_orders + + _debug "get domain_orders" + + xml_content=$(printf ' + + domain.get_domain_orders + + + + + + login + %s + + + password + %s + + + + + + ' "$EUSERV_Username" "$EUSERV_Password") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get domain orders" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + + _euserv_domain_orders="$response" + return 0 +} + +_euserv_get_domain_id() { +# returns: _euserv_domain_id + domain=$1 + _debug "get domain_id" + + _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) + _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + + if [ -z "$_euserv_domain_id" ] ; then + _err "Could not find domain_id for domain $domain" + _debug "_euserv_domain_orders" "$_euserv_domain_orders" + return 1 + fi + return 0 + +} + +_get_root() { + domain=$1 + _debug "get root" + + # Just to read the domain_orders once + + domain=$1 + i=2 + p=1 + _euserv_get_domain_orders + response="$_euserv_domain_orders" + + 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" + if ! _euserv_get_domain_id "$_domain"; then + _err "invalid domain" + return 1 + fi + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 + +} + +# TODO +_euserv_delete_record() { + record_id=$1 + xml_content=$(printf ' + + domain.dns_delete_record + + + + + + login + + %s + + + + password + + %s + + + + dns_record_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$record_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error deleting record" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + + return 0 + +} + +_euserv_add_record() { + domain=$1 + sub_domain=$2 + txtval=$3 + + xml_content=$(printf ' + + domain.dns_create_record + + + + + + login + + %s + + + + password + + %s + + + domain_id + + %s + + + + dns_record_subdomain + + %s + + + + dns_record_type + + TXT + + + + dns_record_value + + %s + + + + dns_record_ttl + + 300 + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval" ) + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not create record" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi +# _dns_record_id="$(echo "$response" | _egrep_o "[\s\S]dns_record_id<\/name>[\s]*?[\s]*?(\K\d*)")" +# _debug "_dns_record_id" "$_dns_record_id" + return 0 +} From 94f91ae6878f8652cc947a98aa7b78dd42334c0c Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 16:04:18 +0200 Subject: [PATCH 045/403] initial version with Euserv.eu DNS API Support - added dnsapi/dns_euserv.sh - modified dnsapi/README.md --- dnsapi/README.md | 25 ++- dnsapi/dns_euserv.sh | 358 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_euserv.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index ef6c9d09..9f60764f 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -876,6 +876,29 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com ``` The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. +## 47. euserv.eu API to automatically issue cert + +First you need to login to your euserv.eu account to activate your API Administration (API Verwaltung). +[https://support.euserv.com](https://support.euserv.com) + +Once you've activate, login to your API Admin Interface and create an Account. +Please specify the scope (active groups: domain) and assign the allowed IPs. + +Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates! + +``` +export EUSERV_Username="99999.user123" +export EUSERV_Password="Asbe54gHde" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure +``` + +The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +Please report any issues to https://github.com/initit/acme.sh or to # Use custom API If your API is not supported yet, you can write your own DNS API. @@ -896,4 +919,4 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API -https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api +https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api \ No newline at end of file diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh new file mode 100644 index 00000000..1a58df51 --- /dev/null +++ b/dnsapi/dns_euserv.sh @@ -0,0 +1,358 @@ +#!/usr/bin/env sh + +#This is the euserv.eu api wrapper for acme.sh +# +#Author: Michael Brueckner +#Report Bugs: https://www.github.com/initit/acme.sh or mbr@initit.de + +# +#EUSERV_Username="username" +# +#EUSERV_Password="password" +# +# Dependencies: +# ------------- +# - none - + +EUSERV_Api="https://api.euserv.net" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_euserv_add() { + fulldomain="$(echo "$1" | _lower_case)" + txtvalue=$2 + + EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}" + EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}" + if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then + EUSERV_Username="" + EUSERV_Password="" + _err "You don't specify euserv user and password yet." + _err "Please create your key and try again." + return 1 + fi + + #save the user and email to the account conf file. + _saveaccountconf_mutable EUSERV_Username "$EUSERV_Username" + _saveaccountconf_mutable EUSERV_Password "$EUSERV_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _info "Adding record" + _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue" + +} + +#fulldomain txtvalue +dns_euserv_rm() { + + fulldomain="$(echo "$1" | _lower_case)" + txtvalue=$2 + + EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}" + EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}" + if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then + EUSERV_Username="" + EUSERV_Password="" + _err "You don't specify euserv user and password yet." + _err "Please create your key and try again." + return 1 + fi + + #save the user and email to the account conf file. + _saveaccountconf_mutable EUSERV_Username "$EUSERV_Username" + _saveaccountconf_mutable EUSERV_Password "$EUSERV_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug "_sub_domain" "$_sub_domain" + _debug "_domain" "$_domain" + + _debug "Getting txt records" + + xml_content=$(printf ' + + domain.dns_get_active_records + + + + + + login + + %s + + + + password + + %s + + + + domain_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get txt records" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + +# _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) +# _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + + if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then + _info "Do not need to delete record" + else + # find block where txtvalue is in. the record_id is allways prior this line! + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + # record_id is the last entry with a number, identified by the postfix of + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + _info "Deleting record" + _euserv_delete_record "$_record_id" + fi + +} + +#################### Private functions below ################################## + +_euserv_get_domain_orders() { +# returns: _euserv_domain_orders + + _debug "get domain_orders" + + xml_content=$(printf ' + + domain.get_domain_orders + + + + + + login + %s + + + password + %s + + + + + + ' "$EUSERV_Username" "$EUSERV_Password") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not get domain orders" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + + _euserv_domain_orders="$response" + return 0 +} + +_euserv_get_domain_id() { +# returns: _euserv_domain_id + domain=$1 + _debug "get domain_id" + + _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) + _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + + if [ -z "$_euserv_domain_id" ] ; then + _err "Could not find domain_id for domain $domain" + _debug "_euserv_domain_orders" "$_euserv_domain_orders" + return 1 + fi + return 0 + +} + +_get_root() { + domain=$1 + _debug "get root" + + # Just to read the domain_orders once + + domain=$1 + i=2 + p=1 + _euserv_get_domain_orders + response="$_euserv_domain_orders" + + 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" + if ! _euserv_get_domain_id "$_domain"; then + _err "invalid domain" + return 1 + fi + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 + +} + +# TODO +_euserv_delete_record() { + record_id=$1 + xml_content=$(printf ' + + domain.dns_delete_record + + + + + + login + + %s + + + + password + + %s + + + + dns_record_id + + %s + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$record_id") + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error deleting record" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi + + return 0 + +} + +_euserv_add_record() { + domain=$1 + sub_domain=$2 + txtval=$3 + + xml_content=$(printf ' + + domain.dns_create_record + + + + + + login + + %s + + + + password + + %s + + + domain_id + + %s + + + + dns_record_subdomain + + %s + + + + dns_record_type + + TXT + + + + dns_record_value + + %s + + + + dns_record_ttl + + 300 + + + + + + + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval" ) + + export _H1="Content-Type: text/xml" + response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" + + ok="$(printf '%s' "$response" | grep "status100")" + if [ -z "$ok" ]; then + _err "Error could not create record" + _debug "xml_content" "$xml_content" + _debug "response" "$response" + return 1 + fi +# _dns_record_id="$(echo "$response" | _egrep_o "[\s\S]dns_record_id<\/name>[\s]*?[\s]*?(\K\d*)")" +# _debug "_dns_record_id" "$_dns_record_id" + return 0 +} From d99968ee6d39c759909c8742592c349a00f336fd Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 16:25:35 +0200 Subject: [PATCH 046/403] Modified dnsapi/README.md --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 9f60764f..49f2625d 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -878,10 +878,10 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. ## 47. euserv.eu API to automatically issue cert -First you need to login to your euserv.eu account to activate your API Administration (API Verwaltung). +First you need to login to your euserv.eu account and activate your API Administration (API Verwaltung). [https://support.euserv.com](https://support.euserv.com) -Once you've activate, login to your API Admin Interface and create an Account. +Once you've activate, login to your API Admin Interface and create an API account. Please specify the scope (active groups: domain) and assign the allowed IPs. Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates! From 616b0b6baa0eb08670ac0876f6b74a7627076629 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 22:50:52 +0200 Subject: [PATCH 047/403] fixed shfmt related errors in dns_euserv.sh and modified README.md --- README.md | 1 + dnsapi/README.md | 6 +- dnsapi/dns_euserv.sh | 142 +++++++++++++++++++++---------------------- 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index f395e49a..bda7252f 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,7 @@ You don't have to do anything manually! 1. Loopia.se API 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) +1. EUSERV.EU (https://www.euserv.eu) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 49f2625d..1f394f92 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -876,7 +876,7 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com ``` The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. -## 47. euserv.eu API to automatically issue cert +## 47. Use Euserv.eu API First you need to login to your euserv.eu account and activate your API Administration (API Verwaltung). [https://support.euserv.com](https://support.euserv.com) @@ -884,14 +884,12 @@ First you need to login to your euserv.eu account and activate your API Administ Once you've activate, login to your API Admin Interface and create an API account. Please specify the scope (active groups: domain) and assign the allowed IPs. -Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates! - ``` export EUSERV_Username="99999.user123" export EUSERV_Password="Asbe54gHde" ``` -Ok, let's issue a cert now: +Ok, let's issue a cert now: (Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates!) ``` acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure ``` diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index 1a58df51..cb1e0a4d 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -42,11 +42,12 @@ dns_euserv_add() { _err "invalid domain" return 1 fi - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - + _debug "_sub_domain" "$_sub_domain" + _debug "_domain" "$_domain" _info "Adding record" - _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue" + if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then + return 1 + fi } @@ -114,24 +115,20 @@ dns_euserv_rm() { export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then - _err "Error could not get txt records" + if ! _contains "$response" "status100"; then + _err "Error could not get txt records" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi -# _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) -# _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) - if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then _info "Do not need to delete record" else - # find block where txtvalue is in. the record_id is allways prior this line! - _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) - # record_id is the last entry with a number, identified by the postfix of - _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + # find XML block where txtvalue is in. The record_id is allways prior this line! + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + # record_id is the last Tag with a number before the row _endLine, identified by + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) _info "Deleting record" _euserv_delete_record "$_record_id" fi @@ -140,11 +137,52 @@ dns_euserv_rm() { #################### Private functions below ################################## +_get_root() { + domain=$1 + _debug "get root" + + # Just to read the domain_orders once + + domain=$1 + i=2 + p=1 + + if ! _euserv_get_domain_orders; then + return 1 + fi + + # Get saved response with domain_orders + response="$_euserv_domain_orders" + + 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" + if ! _euserv_get_domain_id "$_domain"; then + _err "invalid domain" + return 1 + fi + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + _euserv_get_domain_orders() { -# returns: _euserv_domain_orders + # returns: _euserv_domain_orders _debug "get domain_orders" - + xml_content=$(printf ' domain.get_domain_orders @@ -165,76 +203,41 @@ _euserv_get_domain_orders() { ' "$EUSERV_Username" "$EUSERV_Password") - + export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then + + if ! _contains "$response" "status100"; then _err "Error could not get domain orders" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi - + + # save response to reduce API calls _euserv_domain_orders="$response" return 0 } _euserv_get_domain_id() { -# returns: _euserv_domain_id + # returns: _euserv_domain_id domain=$1 _debug "get domain_id" - _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1 ) - _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/' ) + # find line where the domain name is within the $response + _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1) + # next occurency of domain_id after the domain_name is the correct one + _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/') - if [ -z "$_euserv_domain_id" ] ; then + if [ -z "$_euserv_domain_id" ]; then _err "Could not find domain_id for domain $domain" _debug "_euserv_domain_orders" "$_euserv_domain_orders" return 1 fi - return 0 - -} - -_get_root() { - domain=$1 - _debug "get root" - - # Just to read the domain_orders once - - domain=$1 - i=2 - p=1 - _euserv_get_domain_orders - response="$_euserv_domain_orders" - - 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" - if ! _euserv_get_domain_id "$_domain"; then - _err "invalid domain" - return 1 - fi - return 0 - fi - p=$i - i=$(_math "$i" + 1) - done - return 1 + return 0 } -# TODO _euserv_delete_record() { record_id=$1 xml_content=$(printf ' @@ -271,14 +274,13 @@ _euserv_delete_record() { export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then + if ! _contains "$response" "status100"; then _err "Error deleting record" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi - + return 0 } @@ -340,19 +342,17 @@ _euserv_add_record() { - ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval" ) + ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval") export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" - ok="$(printf '%s' "$response" | grep "status100")" - if [ -z "$ok" ]; then + if ! _contains "$response" "status100"; then _err "Error could not create record" _debug "xml_content" "$xml_content" _debug "response" "$response" return 1 fi -# _dns_record_id="$(echo "$response" | _egrep_o "[\s\S]dns_record_id<\/name>[\s]*?[\s]*?(\K\d*)")" -# _debug "_dns_record_id" "$_dns_record_id" + return 0 } From 261cc448f78729ec52992b9a010e7eb32daca0e1 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 8 Jul 2018 23:00:26 +0200 Subject: [PATCH 048/403] fixed shfmt related errors in dns_euserv.sh and modified README.md --- dnsapi/dns_euserv.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index cb1e0a4d..44120a3d 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -43,10 +43,10 @@ dns_euserv_add() { return 1 fi _debug "_sub_domain" "$_sub_domain" - _debug "_domain" "$_domain" + _debug "_domain" "$_domain" _info "Adding record" - if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then - return 1 + if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then + return 1 fi } @@ -80,7 +80,7 @@ dns_euserv_rm() { _debug "_domain" "$_domain" _debug "Getting txt records" - + xml_content=$(printf ' domain.dns_get_active_records @@ -111,7 +111,7 @@ dns_euserv_rm() { ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id") - + export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" @@ -126,9 +126,9 @@ dns_euserv_rm() { _info "Do not need to delete record" else # find XML block where txtvalue is in. The record_id is allways prior this line! - _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1 ) + _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) # record_id is the last Tag with a number before the row _endLine, identified by - _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/' ) + _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') _info "Deleting record" _euserv_delete_record "$_record_id" fi @@ -168,7 +168,7 @@ _get_root() { if ! _euserv_get_domain_id "$_domain"; then _err "invalid domain" return 1 - fi + fi return 0 fi p=$i @@ -343,7 +343,7 @@ _euserv_add_record() { ' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval") - + export _H1="Content-Type: text/xml" response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")" From 2945b230e4dd4c03de5c48f1f65ca07fe45045fd Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 9 Jul 2018 22:54:34 +0200 Subject: [PATCH 049/403] replaced tail/head with _tail_n/_head_n and printf with echo --- dnsapi/dns_euserv.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index 44120a3d..38101565 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -122,13 +122,13 @@ dns_euserv_rm() { return 1 fi - if ! printf "%s" "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then + if ! echo "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then _info "Do not need to delete record" else # find XML block where txtvalue is in. The record_id is allways prior this line! - _endLine=$(printf '%s' "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) + _endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) # record_id is the last Tag with a number before the row _endLine, identified by - _record_id=$(printf '%s' "$response" | sed -n '1,'"$_endLine"'p' | grep '' | tail -n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') + _record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '' | _tail_n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') _info "Deleting record" _euserv_delete_record "$_record_id" fi @@ -155,7 +155,7 @@ _get_root() { response="$_euserv_domain_orders" while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) + h=$(echo "$domain" | cut -d . -f $i-100) _debug h "$h" if [ -z "$h" ]; then #not valid @@ -163,7 +163,7 @@ _get_root() { fi if _contains "$response" "$h"; then - _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _sub_domain=$(echo "$domain" | cut -d . -f 1-$p) _domain="$h" if ! _euserv_get_domain_id "$_domain"; then _err "invalid domain" @@ -225,9 +225,9 @@ _euserv_get_domain_id() { _debug "get domain_id" # find line where the domain name is within the $response - _startLine=$(printf '%s' "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1) + _startLine=$(echo "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1) # next occurency of domain_id after the domain_name is the correct one - _euserv_domain_id=$(printf '%s' "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | head -n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/') + _euserv_domain_id=$(echo "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | _head_n 1 | sed 's/.*\([0-9]*\)<\/i4>.*/\1/') if [ -z "$_euserv_domain_id" ]; then _err "Could not find domain_id for domain $domain" From 9cecd525e20cec8500e33629690fefd8182006d8 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 18 Jul 2018 00:26:21 +0800 Subject: [PATCH 050/403] fix JWS has an invalid anti-replay nonce https://github.com/Neilpang/acme.sh/issues/1630 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 713170b7..c23942fb 100755 --- a/acme.sh +++ b/acme.sh @@ -1607,7 +1607,7 @@ _inithttp() { } -# body url [needbase64] [POST|PUT] [ContentType] +# body url [needbase64] [POST|PUT|DELETE] [ContentType] _post() { body="$1" _post_url="$2" @@ -1897,7 +1897,7 @@ _send_signed_request() { _debug3 _body "$_body" fi - if _contains "$_body" "JWS has invalid anti-replay nonce"; then + if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then _info "It seems the CA server is busy now, let's wait and retry." _sleep 5 continue From b9b703238670f646c6e7f20637715a74b93f52c1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 18 Jul 2018 00:33:07 +0800 Subject: [PATCH 051/403] lets start v2.8.0 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index c23942fb..8fd321ba 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.9 +VER=2.8.0 PROJECT_NAME="acme.sh" From 411b342a2758c4a2e75b519453fcd8be3730f78d Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 18 Jul 2018 22:00:09 +0800 Subject: [PATCH 052/403] request a new nonce for invalid anti-replay nonce error --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index 8fd321ba..43b3f630 100755 --- a/acme.sh +++ b/acme.sh @@ -1899,6 +1899,7 @@ _send_signed_request() { if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then _info "It seems the CA server is busy now, let's wait and retry." + _CACHED_NONCE="" _sleep 5 continue fi From 8d230dd798f2b25cfa37761ebc20591f57ebad1c Mon Sep 17 00:00:00 2001 From: Old?ich Jedli?ka Date: Tue, 24 Jul 2018 15:39:48 +0200 Subject: [PATCH 053/403] Added dns_lexicon_rm command. Remove created TXT record when finished. Works with lexicon version 2.3.0 and later. --- dnsapi/dns_lexicon.sh | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns_lexicon.sh b/dnsapi/dns_lexicon.sh index c09f16fd..9c0f9860 100755 --- a/dnsapi/dns_lexicon.sh +++ b/dnsapi/dns_lexicon.sh @@ -7,15 +7,7 @@ lexicon_cmd="lexicon" wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api" -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_lexicon_add() { - fulldomain=$1 - txtvalue=$2 - - domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999) - +_initLexicon() { if ! _exists "$lexicon_cmd"; then _err "Please install $lexicon_cmd first: $wiki" return 1 @@ -66,13 +58,36 @@ dns_lexicon_add() { eval export "$Lx_domaintoken" _saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v" fi +} + +######## Public functions ##################### + +#Usage: dns_lexicon_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_lexicon_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _initLexicon; then + return 1 + fi + + domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999) $lexicon_cmd "$PROVIDER" create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" } -#fulldomain +#Usage: dns_lexicon_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_lexicon_rm() { fulldomain=$1 + txtvalue=$2 + + if ! _initLexicon; then + return 1 + fi + + domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999) + + $lexicon_cmd "$PROVIDER" delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" } From 0366e8758cb908eb1224fd346b06d3973611799b Mon Sep 17 00:00:00 2001 From: Old?ich Jedli?ka Date: Tue, 24 Jul 2018 22:14:39 +0200 Subject: [PATCH 054/403] Added reading of stored config. --- dnsapi/dns_lexicon.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_lexicon.sh b/dnsapi/dns_lexicon.sh index 9c0f9860..4ec1631d 100755 --- a/dnsapi/dns_lexicon.sh +++ b/dnsapi/dns_lexicon.sh @@ -7,12 +7,13 @@ lexicon_cmd="lexicon" wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api" -_initLexicon() { +_lexicon_init() { if ! _exists "$lexicon_cmd"; then _err "Please install $lexicon_cmd first: $wiki" return 1 fi + PROVIDER="${PROVIDER:-$(_readdomainconf PROVIDER)}" if [ -z "$PROVIDER" ]; then PROVIDER="" _err "Please define env PROVIDER first: $wiki" @@ -25,38 +26,42 @@ _initLexicon() { # e.g. busybox-ash does not know [:upper:] # shellcheck disable=SC2018,SC2019 Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z') + eval $Lx_name="\${$Lx_name:-$(_readaccountconf_mutable $Lx_name)}" Lx_name_v=$(eval echo \$"$Lx_name") _secure_debug "$Lx_name" "$Lx_name_v" if [ "$Lx_name_v" ]; then - _saveaccountconf "$Lx_name" "$Lx_name_v" + _saveaccountconf_mutable "$Lx_name" "$Lx_name_v" eval export "$Lx_name" fi # shellcheck disable=SC2018,SC2019 Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z') + eval $Lx_token="\${$Lx_token:-$(_readaccountconf_mutable $Lx_token)}" Lx_token_v=$(eval echo \$"$Lx_token") _secure_debug "$Lx_token" "$Lx_token_v" if [ "$Lx_token_v" ]; then - _saveaccountconf "$Lx_token" "$Lx_token_v" + _saveaccountconf_mutable "$Lx_token" "$Lx_token_v" eval export "$Lx_token" fi # shellcheck disable=SC2018,SC2019 Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z') + eval $Lx_password="\${$Lx_password:-$(_readaccountconf_mutable $Lx_password)}" Lx_password_v=$(eval echo \$"$Lx_password") _secure_debug "$Lx_password" "$Lx_password_v" if [ "$Lx_password_v" ]; then - _saveaccountconf "$Lx_password" "$Lx_password_v" + _saveaccountconf_mutable "$Lx_password" "$Lx_password_v" eval export "$Lx_password" fi # shellcheck disable=SC2018,SC2019 Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z') + eval $Lx_domaintoken="\${$Lx_domaintoken:-$(_readaccountconf_mutable $Lx_domaintoken)}" Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken") _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v" if [ "$Lx_domaintoken_v" ]; then + _saveaccountconf_mutable "$Lx_domaintoken" "$Lx_domaintoken_v" eval export "$Lx_domaintoken" - _saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v" fi } @@ -67,7 +72,7 @@ dns_lexicon_add() { fulldomain=$1 txtvalue=$2 - if ! _initLexicon; then + if ! _lexicon_init; then return 1 fi @@ -82,7 +87,7 @@ dns_lexicon_rm() { fulldomain=$1 txtvalue=$2 - if ! _initLexicon; then + if ! _lexicon_init; then return 1 fi From cb11580981bf67058257da90a165a441558f0ac1 Mon Sep 17 00:00:00 2001 From: Jesse Miller Date: Tue, 24 Jul 2018 22:32:38 -0500 Subject: [PATCH 055/403] BSD fix _time2str() date -u -d@"12345" does not produce an error on *BSD and outputs the current date in UTC, which is not the expected output from _time2str() Fix, reorder _time2str() to attempt BSD style date first, which errors on Linux, so cascade style OS detection works correctly. --- acme.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 43b3f630..69584cca 100755 --- a/acme.sh +++ b/acme.sh @@ -1374,17 +1374,17 @@ _url_replace() { } _time2str() { - #Linux - if date -u -d@"$1" 2>/dev/null; then + #BSD + if date -u -r "$1" 2>/dev/null; then return fi - #BSD - if date -u -r "$1" 2>/dev/null; then + #Linux + if date -u -d@"$1" 2>/dev/null; then return fi - #Soaris + #Solaris if _exists adb; then _t_s_a=$(echo "0t${1}=Y" | adb) echo "$_t_s_a" From 436940285594dd9397161d5ca16f6e3973b4312c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Wed, 25 Jul 2018 10:40:57 +0200 Subject: [PATCH 056/403] Cleaned-up shellcheck warnings. --- dnsapi/dns_lexicon.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_lexicon.sh b/dnsapi/dns_lexicon.sh index 4ec1631d..ab180fb2 100755 --- a/dnsapi/dns_lexicon.sh +++ b/dnsapi/dns_lexicon.sh @@ -26,7 +26,7 @@ _lexicon_init() { # e.g. busybox-ash does not know [:upper:] # shellcheck disable=SC2018,SC2019 Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z') - eval $Lx_name="\${$Lx_name:-$(_readaccountconf_mutable $Lx_name)}" + eval "$Lx_name=\${$Lx_name:-$(_readaccountconf_mutable "$Lx_name")}" Lx_name_v=$(eval echo \$"$Lx_name") _secure_debug "$Lx_name" "$Lx_name_v" if [ "$Lx_name_v" ]; then @@ -36,7 +36,7 @@ _lexicon_init() { # shellcheck disable=SC2018,SC2019 Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z') - eval $Lx_token="\${$Lx_token:-$(_readaccountconf_mutable $Lx_token)}" + eval "$Lx_token=\${$Lx_token:-$(_readaccountconf_mutable "$Lx_token")}" Lx_token_v=$(eval echo \$"$Lx_token") _secure_debug "$Lx_token" "$Lx_token_v" if [ "$Lx_token_v" ]; then @@ -46,7 +46,7 @@ _lexicon_init() { # shellcheck disable=SC2018,SC2019 Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z') - eval $Lx_password="\${$Lx_password:-$(_readaccountconf_mutable $Lx_password)}" + eval "$Lx_password=\${$Lx_password:-$(_readaccountconf_mutable "$Lx_password")}" Lx_password_v=$(eval echo \$"$Lx_password") _secure_debug "$Lx_password" "$Lx_password_v" if [ "$Lx_password_v" ]; then @@ -56,7 +56,7 @@ _lexicon_init() { # shellcheck disable=SC2018,SC2019 Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z') - eval $Lx_domaintoken="\${$Lx_domaintoken:-$(_readaccountconf_mutable $Lx_domaintoken)}" + eval "$Lx_domaintoken=\${$Lx_domaintoken:-$(_readaccountconf_mutable "$Lx_domaintoken")}" Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken") _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v" if [ "$Lx_domaintoken_v" ]; then From cc2d59468d69ed40527113d99f0aee275a72885d Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 26 Jul 2018 21:57:22 +0800 Subject: [PATCH 057/403] use json content type for both v1 and v2 --- acme.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 69584cca..bd0c390d 100755 --- a/acme.sh +++ b/acme.sh @@ -1795,11 +1795,8 @@ _send_signed_request() { return 1 fi - if [ "$ACME_VERSION" = "2" ]; then - __request_conent_type="$CONTENT_TYPE_JSON" - else - __request_conent_type="" - fi + __request_conent_type="$CONTENT_TYPE_JSON" + payload64=$(printf "%s" "$payload" | _base64 | _url_replace) _debug3 payload64 "$payload64" From d3c9d0b331b2c49327e5a4c6d3d54839e69aecd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 26 Jul 2018 19:59:15 +0200 Subject: [PATCH 058/403] Fix inwx account without Mobile TAN --- dnsapi/dns_inwx.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index cd5af91b..f4590cf8 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -158,7 +158,8 @@ _inwx_login() { export _H1 #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71 - if _contains "$response" "tfa"; then + if _contains "$response" "code1000" \ + && _contains "$response" "tfaGOOGLE-AUTH"; then if [ -z "$INWX_Shared_Secret" ]; then _err "Mobile TAN detected." _err "Please define a shared secret." From 709a3fb06fceaa2f26655b5b1b64aabe51f22446 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 28 Jul 2018 22:02:03 +0800 Subject: [PATCH 059/403] add more retry for badnonce error --- acme.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index bd0c390d..32219d9d 100755 --- a/acme.sh +++ b/acme.sh @@ -1800,7 +1800,8 @@ _send_signed_request() { payload64=$(printf "%s" "$payload" | _base64 | _url_replace) _debug3 payload64 "$payload64" - MAX_REQUEST_RETRY_TIMES=5 + MAX_REQUEST_RETRY_TIMES=20 + _sleep_retry_sec=1 _request_retry_times=0 while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do _request_retry_times=$(_math "$_request_retry_times" + 1) @@ -1895,9 +1896,9 @@ _send_signed_request() { fi if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then - _info "It seems the CA server is busy now, let's wait and retry." + _info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds." _CACHED_NONCE="" - _sleep 5 + _sleep $_sleep_retry_sec continue fi break From 86276ad17b50227b9b9d7f2d72abd7cdf22f19a8 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Wed, 1 Aug 2018 16:37:08 +0200 Subject: [PATCH 060/403] added hosting.de DNS Plugin * can be used with API of hosting.de * can also be used with ICANN registrar http.net * needs just API key and endpoint * support wildcard certificates --- dnsapi/dns_hostingde.sh | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 dnsapi/dns_hostingde.sh diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh new file mode 100644 index 00000000..a6abc428 --- /dev/null +++ b/dnsapi/dns_hostingde.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env sh + +# hosting.de API + +# Values to export: +# export HOSTINGDE_ENDPOINT='https://secure.hosting.de' +# export HOSTINGDE_APIKEY='xxxxx' + + +######## Public functions ##################### + +dns_hostingde_add() { + fulldomain="${1}" + txtvalue="${2}" + _debug "Calling: _hostingde_addRecord() '${fulldomain}' '${txtvalue}'" + _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_addRecord +} + +dns_hostingde_rm() { + fulldomain="${1}" + txtvalue="${2}" + _debug "Calling: _hostingde_removeRecord() '${fulldomain}' '${txtvalue}'" + _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_removeRecord +} + +#################### own Private functions below ################################## + +_hostingde_apiKey() { + HOSTINGDE_APIKEY="${HOSTINGDE_APIKEY:-$(_readaccountconf_mutable HOSTINGDE_APIKEY)}" + if [ -z "$HOSTINGDE_APIKEY" ] || [ -z "$HOSTINGDE_ENDPOINT" ]; then + HOSTINGDE_APIKEY="" + HOSTINGDE_ENDPOINT="" + _err "You haven't specified hosting.de API key or endpoint yet." + _err "Please create your key and try again." + return 1 + fi + + _saveaccountconf_mutable HOSTINGDE_APIKEY "$HOSTINGDE_APIKEY" + _saveaccountconf_mutable HOSTINGDE_ENDPOINT "$HOSTINGDE_ENDPOINT" +} + +_hostingde_getZoneConfig() { + _info "Getting ZoneConfig" + curZone="${fulldomain#*.}" + returnCode=1 + while _contains "${curZone}" "\\."; do + curData="{\"filter\":{\"field\":\"zoneName\",\"value\":\"${curZone}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}" + curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneConfigsFind")" + _debug "Calling zoneConfigsFind: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneConfigsFind'" + _debug "Result of zoneConfigsFind: '$curResult'" + if _contains "${curResult}" '"status": "error"'; then + if _contains "${curResult}" '"code": 10109'; then + _err "The API-Key is invalid or could not be found" + else + _err "UNKNOWN API ERROR" + fi + returnCode=1 + break; + fi + if _contains "${curResult}" '"totalEntries": 1'; then + _info "Retrieved zone data." + _debug "Zone data: '${curResult}'" + + # read ZoneConfigId for later update + zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + _debug "zoneConfigId '${zoneConfigId}'" + returnCode=0 + break + fi + curZone="${curZone#*.}" + done + if [ $returnCode -ne 0 ]; then + _info "ZoneEnd reached, Zone ${curZone} not found in hosting.de API" + fi + return $returnCode +} + +_hostingde_addRecord() { + _info "Adding record to zone" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" + curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" + _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" + _debug "Result of zoneUpdate: '$curResult'" + if _contains "${curResult}" '"status": "error"'; then + if _contains "${curResult}" '"code": 10109'; then + _err "The API-Key is invalid or could not be found" + else + _err "UNKNOWN API ERROR" + fi + return 1 + fi + return 0 +} + +_hostingde_removeRecord() { + _info "Removing record from zone" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" + curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" + _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" + _debug "Result of zoneUpdate: '$curResult'" + if _contains "${curResult}" '"status": "error"'; then + if _contains "${curResult}" '"code": 10109'; then + _err "The API-Key is invalid or could not be found" + else + _err "UNKNOWN API ERROR" + fi + return 1 + fi + return 0 +} From 5494e88e08f22400ed7fabc3c9f445eca85334e5 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Wed, 1 Aug 2018 17:00:22 +0200 Subject: [PATCH 061/403] making shfmt happy --- dnsapi/dns_hostingde.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index a6abc428..39bcfb63 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -6,7 +6,6 @@ # export HOSTINGDE_ENDPOINT='https://secure.hosting.de' # export HOSTINGDE_APIKEY='xxxxx' - ######## Public functions ##################### dns_hostingde_add() { @@ -55,7 +54,7 @@ _hostingde_getZoneConfig() { _err "UNKNOWN API ERROR" fi returnCode=1 - break; + break fi if _contains "${curResult}" '"totalEntries": 1'; then _info "Retrieved zone data." @@ -70,7 +69,7 @@ _hostingde_getZoneConfig() { curZone="${curZone#*.}" done if [ $returnCode -ne 0 ]; then - _info "ZoneEnd reached, Zone ${curZone} not found in hosting.de API" + _info "ZoneEnd reached, Zone ${curZone} not found in hosting.de API" fi return $returnCode } From 63134fafece3f9ffb5092b2d897e38366072d64d Mon Sep 17 00:00:00 2001 From: little-fat Date: Thu, 2 Aug 2018 20:57:27 +0800 Subject: [PATCH 062/403] Fix key leakage in SSH deploy log --- deploy/ssh.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/ssh.sh b/deploy/ssh.sh index a68da356..9cb0af9e 100644 --- a/deploy/ssh.sh +++ b/deploy/ssh.sh @@ -11,7 +11,7 @@ # # Only a username is required. All others are optional. # -# The following examples are for QNAP NAS running QTS 4.2 +# The following examples are for QNAP NAS running QTS 4.2 # export DEPLOY_SSH_CMD="" # defaults to ssh # export DEPLOY_SSH_USER="admin" # required # export DEPLOY_SSH_SERVER="qnap" # defaults to domain name @@ -101,7 +101,7 @@ ssh_deploy() { fi # CERTFILE is optional. - # If provided then private key will be copied or appended to provided filename. + # If provided then certificate will be copied or appended to provided filename. if [ -n "$DEPLOY_SSH_CERTFILE" ]; then Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE" _savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile" @@ -190,7 +190,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d _info "Backup directories erased after 180 days." fi - _debug "Remote commands to execute: $_cmdstr" + _secure_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 From 4162975f9f2db76fbc5fcfbdaa3bea5f0df6e9cc Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Thu, 2 Aug 2018 15:43:40 +0200 Subject: [PATCH 063/403] added hosting.de API to README's --- README.md | 1 + dnsapi/README.md | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/README.md b/README.md index c8bebc6f..614476a0 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,7 @@ You don't have to do anything manually! 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) 1. EUSERV.EU (https://www.euserv.eu) +1. hosting.de (https://www.hosting.de) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 1f394f92..bce0ffef 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -897,6 +897,29 @@ acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. Please report any issues to https://github.com/initit/acme.sh or to +## 48. Use hosting.de API + +Create an API key in your hosting.de account here: https://secure.hosting.de + +The key needs the following rights: +- DNS_ZONES_EDIT +- DNS_ZONES_LIST + +Set your API Key and endpoint: + +``` +export HOSTINGDE_APIKEY="xxx" +export HOSTINGDE_ENDPOINT="https://secure.hosting.de" +``` + +The plugin can also be used for the http.net API. http.net customers have to set endpoint to https://partner.http.net. + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_hostingde -d example.com -d *.example.com +``` + +The hosting.de API key and endpoint 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 ed95509a4f938737957c641a9c5257bda55a1540 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Thu, 2 Aug 2018 15:47:02 +0200 Subject: [PATCH 064/403] hosting.de API keys can contain special chars, so using simple quotes --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index bce0ffef..01192b13 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -908,8 +908,8 @@ The key needs the following rights: Set your API Key and endpoint: ``` -export HOSTINGDE_APIKEY="xxx" -export HOSTINGDE_ENDPOINT="https://secure.hosting.de" +export HOSTINGDE_APIKEY='xxx' +export HOSTINGDE_ENDPOINT='https://secure.hosting.de' ``` The plugin can also be used for the http.net API. http.net customers have to set endpoint to https://partner.http.net. From 4fbd21da5788ce48874b483aaa57700a4520ea7f Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Tue, 7 Aug 2018 13:35:08 +0200 Subject: [PATCH 065/403] Spelling --- dnsapi/dns_aws.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 8ce7c347..2ad3c819 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -29,7 +29,7 @@ dns_aws_add() { if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then 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 "You haven't specifed the aws route53 api key id and and api key secret yet." _err "Please create your key and try again. see $(__green $AWS_WIKI)" return 1 fi @@ -62,7 +62,7 @@ dns_aws_add() { fi if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then - _info "The txt record already exists, skip" + _info "The TXT record already exists. Skipping." return 0 fi @@ -71,7 +71,7 @@ dns_aws_add() { _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." + _info "TXT record updated successfully." return 0 fi @@ -99,7 +99,7 @@ dns_aws_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _info "Geting existing records for $fulldomain" + _info "Getting existing records for $fulldomain" if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then return 1 fi @@ -108,14 +108,14 @@ dns_aws_rm() { _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" + _debug "no records exist, 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." + _info "TXT record deleted successfully." return 0 fi @@ -163,7 +163,7 @@ _get_root() { _domain=$h return 0 fi - _err "Can not find domain id: $h" + _err "Can't find domain with id: $h" return 1 fi fi From 22cd408efbcbacb866987b866cdadc5c49f870e1 Mon Sep 17 00:00:00 2001 From: Hitoshi Date: Sun, 12 Aug 2018 18:15:20 +0800 Subject: [PATCH 066/403] add dns api support for dnspod.com --- README.md | 1 + dnsapi/README.md | 19 +++++- dnsapi/dns_dpi.sh | 161 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100755 dnsapi/dns_dpi.sh diff --git a/README.md b/README.md index c8bebc6f..e7c292cf 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,7 @@ You don't have to do anything manually! 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) 1. EUSERV.EU (https://www.euserv.eu) +1. DNSPod.com API (https://www.dnspod.com) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 1f394f92..3fa0ab38 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -897,6 +897,23 @@ acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. Please report any issues to https://github.com/initit/acme.sh or to + +## 48. Use DNSPod.com domain API to automatically issue cert + +First you need to get your API Key and ID by this [get-the-user-token](https://www.dnspod.com/docs/info.html#get-the-user-token). + +``` +export DPI_Id="1234" +export DPI_Key="sADDsdasdgdsf" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_dpi -d example.com -d www.example.com +``` + +The `DPI_Id` and `DPI_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. @@ -917,4 +934,4 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API -https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api \ No newline at end of file +https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/dnsapi/dns_dpi.sh b/dnsapi/dns_dpi.sh new file mode 100755 index 00000000..831150a9 --- /dev/null +++ b/dnsapi/dns_dpi.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env sh + +# Dnspod.com Domain api +# +#DPI_Id="1234" +# +#DPI_Key="sADDsdasdgdsf" + +REST_API="https://api.dnspod.com" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_dpi_add() { + fulldomain=$1 + txtvalue=$2 + + DPI_Id="${DPI_Id:-$(_readaccountconf_mutable DPI_Id)}" + DPI_Key="${DPI_Key:-$(_readaccountconf_mutable DPI_Key)}" + if [ -z "$DPI_Id" ] || [ -z "$DPI_Key" ]; then + DPI_Id="" + DPI_Key="" + _err "You don't specify dnspod api key and key id 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 DPI_Id "$DPI_Id" + _saveaccountconf_mutable DPI_Key "$DPI_Key" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + add_record "$_domain" "$_sub_domain" "$txtvalue" + +} + +#fulldomain txtvalue +dns_dpi_rm() { + fulldomain=$1 + txtvalue=$2 + + DPI_Id="${DPI_Id:-$(_readaccountconf_mutable DPI_Id)}" + DPI_Key="${DPI_Key:-$(_readaccountconf_mutable DPI_Key)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + if ! _rest POST "Record.List" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then + _err "Record.Lis error." + return 1 + fi + + if _contains "$response" 'No records'; then + _info "Don't need to remove." + return 0 + fi + + record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \") + _debug record_id "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id." + return 1 + fi + + if ! _rest POST "Record.Remove" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then + _err "Record.Remove error." + return 1 + fi + + _contains "$response" "Action completed successful" + +} + +#add the txt record. +#usage: root sub txtvalue +add_record() { + root=$1 + sub=$2 + txtvalue=$3 + fulldomain="$sub.$root" + + _info "Adding record" + + if ! _rest POST "Record.Create" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then + return 1 + fi + + _contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists" +} + +#################### 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) + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if ! _rest POST "Domain.Info" "user_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then + return 1 + fi + + if _contains "$response" "Action completed successful"; then + _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \") + _debug _domain_id "$_domain_id" + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _debug _sub_domain "$_sub_domain" + _domain="$h" + _debug _domain "$_domain" + return 0 + fi + return 1 + fi + p="$i" + i=$(_math "$i" + 1) + done + return 1 +} + +#Usage: method URI data +_rest() { + m="$1" + ep="$2" + data="$3" + _debug "$ep" + url="$REST_API/$ep" + + _debug url "$url" + + if [ "$m" = "GET" ]; then + response="$(_get "$url" | tr -d '\r')" + else + _debug2 data "$data" + response="$(_post "$data" "$url" | tr -d '\r')" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 7aeb113c62dee96e259229028ed349828d982dac Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 14 Aug 2018 09:53:13 +0200 Subject: [PATCH 067/403] createDomainKey: fix exitcode for creating new key when running acme.sh headless (without terminal) to create a new key createDomainKey returns a non-zero exit-code. explicitly returning zero avoids this. --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index 32219d9d..6eee183c 100755 --- a/acme.sh +++ b/acme.sh @@ -1327,6 +1327,7 @@ createDomainKey() { if _createkey "$_cdl" "$CERT_KEY_PATH"; then _savedomainconf Le_Keylength "$_cdl" _info "The domain key is here: $(__green $CERT_KEY_PATH)" + return 0 fi else if [ "$IS_RENEW" ]; then From 0a3ac1f5c3f1ac55ad210344a02ad79a4a9abd50 Mon Sep 17 00:00:00 2001 From: Janos Lenart Date: Fri, 25 May 2018 18:56:07 +0100 Subject: [PATCH 068/403] Added support for Google Cloud DNS API (dns_gcloud) --- README.md | 1 + dnsapi/README.md | 21 ++++++ dnsapi/dns_gcloud.sh | 167 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100755 dnsapi/dns_gcloud.sh diff --git a/README.md b/README.md index c8bebc6f..07fbc849 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,7 @@ You don't have to do anything manually! ### Currently acme.sh supports: +1. Google Cloud DNS API 1. CloudFlare.com API 1. DNSPod.cn API 1. CloudXNS.com API diff --git a/dnsapi/README.md b/dnsapi/README.md index 1f394f92..b5fff915 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -4,6 +4,27 @@ If your dns provider doesn't provide api access, you can use our dns alias mode: https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode +## 1. Use Google Cloud DNS API to automatically issue cert + +First you need to authenticate to gcloud. + +``` +gcloud init +``` + +**The `dns_gcloud` script uses the active gcloud configuration and credentials.** +There is no logic inside `dns_gcloud` to override the project and other settings. +If needed, create additional [gcloud configurations](https://cloud.google.com/sdk/gcloud/reference/topic/configurations). +You can change the configuration being used without *activating* it; simply set the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable. + +To issue a certificate you can: +``` +export CLOUDSDK_ACTIVE_CONFIG_NAME=default # see the note above +acme.sh --issue --dns dns_gcloud -d example.com -d '*.example.com' +``` + +`dns_gcloud` also supports [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode). + ## 1. Use CloudFlare domain API to automatically issue cert First you need to login to your CloudFlare account to get your API key. diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh new file mode 100755 index 00000000..5fbd2b60 --- /dev/null +++ b/dnsapi/dns_gcloud.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env sh + +# Author: Janos Lenart + +######## Public functions ##################### + +# Usage: dns_gcloud_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_gcloud_add() { + fulldomain=$1 + txtvalue=$2 + _info "Using gcloud" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + _dns_gcloud_find_zone || return $? + + # Add an extra RR + _dns_gcloud_start_tr || return $? + _dns_gcloud_get_rrdatas || return $? + echo "$rrdatas" | _dns_gcloud_remove_rrs || return $? + echo -e "$rrdatas\n\"$txtvalue\"" | grep -v '^$' | _dns_gcloud_add_rrs || return $? + _dns_gcloud_execute_tr || return $? + + _info "$fulldomain record added" +} + +# Usage: dns_gcloud_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Remove the txt record after validation. +dns_gcloud_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using gcloud" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + _dns_gcloud_find_zone || return $? + + # Remove one RR + _dns_gcloud_start_tr || return $? + _dns_gcloud_get_rrdatas || return $? + echo "$rrdatas" | _dns_gcloud_remove_rrs || return $? + echo "$rrdatas" | fgrep -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $? + _dns_gcloud_execute_tr || return $? + + _info "$fulldomain record added" +} + +#################### Private functions below ################################## + +_dns_gcloud_start_tr() { + if ! trd=`mktemp -d`; then + _err "_dns_gcloud_start_tr: failed to create temporary directory" + return 1 + fi + tr="$trd/tr.yaml" + _debug tr "$tr" + + if ! gcloud dns record-sets transaction start \ + --transaction-file="$tr" \ + --zone="$managedZone"; then + rm -r "$trd" + _err "_dns_gcloud_start_tr: failed to execute transaction" + return 1 + fi +} + +_dns_gcloud_execute_tr() { + if ! gcloud dns record-sets transaction execute \ + --transaction-file="$tr" \ + --zone="$managedZone"; then + _debug tr "`cat \"$tr\"`" + rm -r "$trd" + _err "_dns_gcloud_execute_tr: failed to execute transaction" + return 1 + fi + rm -r "$trd" + + for i in `seq 1 120`; do + if gcloud dns record-sets changes list \ + --zone=lenart \ + --filter='status != done' \ + | grep -q '.*'; then + _info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ..." + sleep 5 + else + return 0 + fi + done + + _err "_dns_gcloud_execute_tr: transaction is still pending after 10 minutes" + rm -r "$trd" + return 1 +} + +_dns_gcloud_remove_rrs() { + if ! xargs --no-run-if-empty gcloud dns record-sets transaction remove \ + --name="$fulldomain." \ + --ttl="$ttl" \ + --type=TXT \ + --zone="$managedZone" \ + --transaction-file="$tr"; then + _debug tr "`cat \"$tr\"`" + rm -r "$trd" + _err "_dns_gcloud_remove_rrs: failed to remove RRs" + return 1 + fi +} + +_dns_gcloud_add_rrs() { + ttl=60 + if ! xargs --no-run-if-empty gcloud dns record-sets transaction add \ + --name="$fulldomain." \ + --ttl="$ttl" \ + --type=TXT \ + --zone="$managedZone" \ + --transaction-file="$tr"; then + _debug tr "`cat \"$tr\"`" + rm -r "$trd" + _err "_dns_gcloud_add_rrs: failed to add RRs" + return 1 + fi +} + +_dns_gcloud_find_zone() { + # Prepare a filter that matches zones that are suiteable for this entry. + # For example, _acme-challenge.something.domain.com might need to go into something.domain.com or domain.com; + # this function finds the longest postfix that has a managed zone. + part="$fulldomain" + filter="dnsName=( " + while [ "$part" != "" ]; do + filter="$filter$part. " + part="`echo \"$part\" | sed 's/[^.]*\.*//'`" + done + filter="$filter)" + _debug filter "$filter" + + # List domains and find the longest match (in case of some levels of delegation) + if ! match=$(gcloud dns managed-zones list \ + --format="value(name, dnsName)" \ + --filter="$filter" \ + | while read dnsName name; do + echo -e "${#dnsName}\t$dnsName\t$name" + done \ + | sort -n -r | head -n1 | cut -f2,3 | grep '.*'); then + _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" + return 1 + fi + + dnsName=$(echo "$match" | cut -f2) + _debug dnsName "$dnsName" + managedZone=$(echo "$match" | cut -f1) + _debug managedZone "$managedZone" +} + +_dns_gcloud_get_rrdatas() { + if ! rrdatas=$(gcloud dns record-sets list \ + --zone="$managedZone" \ + --name="$fulldomain." \ + --type=TXT \ + --format="value(ttl,rrdatas)"); then + _err "_dns_gcloud_get_rrdatas: Failed to list record-sets" + rm -r "$trd" + return 1 + fi + ttl=$(echo "$rrdatas" | cut -f1) + rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/","/"\n"/g') +} From 167758003c3f04f2b849f4e330490b2c40e24251 Mon Sep 17 00:00:00 2001 From: Janos Lenart Date: Fri, 25 May 2018 19:22:40 +0100 Subject: [PATCH 069/403] Fixed shfmt (dns_gcloud) --- dnsapi/dns_gcloud.sh | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index 5fbd2b60..92466181 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -18,7 +18,7 @@ dns_gcloud_add() { _dns_gcloud_start_tr || return $? _dns_gcloud_get_rrdatas || return $? echo "$rrdatas" | _dns_gcloud_remove_rrs || return $? - echo -e "$rrdatas\n\"$txtvalue\"" | grep -v '^$' | _dns_gcloud_add_rrs || return $? + printf "%s\n%s\n" "$rrdatas" "\"$txtvalue\"" | grep -v '^$' | _dns_gcloud_add_rrs || return $? _dns_gcloud_execute_tr || return $? _info "$fulldomain record added" @@ -39,7 +39,7 @@ dns_gcloud_rm() { _dns_gcloud_start_tr || return $? _dns_gcloud_get_rrdatas || return $? echo "$rrdatas" | _dns_gcloud_remove_rrs || return $? - echo "$rrdatas" | fgrep -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $? + echo "$rrdatas" | grep -F -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $? _dns_gcloud_execute_tr || return $? _info "$fulldomain record added" @@ -48,7 +48,7 @@ dns_gcloud_rm() { #################### Private functions below ################################## _dns_gcloud_start_tr() { - if ! trd=`mktemp -d`; then + if ! trd=$(mktemp -d); then _err "_dns_gcloud_start_tr: failed to create temporary directory" return 1 fi @@ -56,8 +56,8 @@ _dns_gcloud_start_tr() { _debug tr "$tr" if ! gcloud dns record-sets transaction start \ - --transaction-file="$tr" \ - --zone="$managedZone"; then + --transaction-file="$tr" \ + --zone="$managedZone"; then rm -r "$trd" _err "_dns_gcloud_start_tr: failed to execute transaction" return 1 @@ -66,22 +66,22 @@ _dns_gcloud_start_tr() { _dns_gcloud_execute_tr() { if ! gcloud dns record-sets transaction execute \ - --transaction-file="$tr" \ - --zone="$managedZone"; then - _debug tr "`cat \"$tr\"`" + --transaction-file="$tr" \ + --zone="$managedZone"; then + _debug tr "$(cat "$tr")" rm -r "$trd" _err "_dns_gcloud_execute_tr: failed to execute transaction" return 1 fi rm -r "$trd" - for i in `seq 1 120`; do + for i in $(seq 1 120); do if gcloud dns record-sets changes list \ - --zone=lenart \ - --filter='status != done' \ - | grep -q '.*'; then - _info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ..." - sleep 5 + --zone=lenart \ + --filter='status != done' \ + | grep -q '^.*'; then + _info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ($i/120)..." + sleep 5 else return 0 fi @@ -94,12 +94,12 @@ _dns_gcloud_execute_tr() { _dns_gcloud_remove_rrs() { if ! xargs --no-run-if-empty gcloud dns record-sets transaction remove \ - --name="$fulldomain." \ - --ttl="$ttl" \ - --type=TXT \ - --zone="$managedZone" \ - --transaction-file="$tr"; then - _debug tr "`cat \"$tr\"`" + --name="$fulldomain." \ + --ttl="$ttl" \ + --type=TXT \ + --zone="$managedZone" \ + --transaction-file="$tr"; then + _debug tr "$(cat "$tr")" rm -r "$trd" _err "_dns_gcloud_remove_rrs: failed to remove RRs" return 1 @@ -109,12 +109,12 @@ _dns_gcloud_remove_rrs() { _dns_gcloud_add_rrs() { ttl=60 if ! xargs --no-run-if-empty gcloud dns record-sets transaction add \ - --name="$fulldomain." \ - --ttl="$ttl" \ - --type=TXT \ - --zone="$managedZone" \ - --transaction-file="$tr"; then - _debug tr "`cat \"$tr\"`" + --name="$fulldomain." \ + --ttl="$ttl" \ + --type=TXT \ + --zone="$managedZone" \ + --transaction-file="$tr"; then + _debug tr "$(cat "$tr")" rm -r "$trd" _err "_dns_gcloud_add_rrs: failed to add RRs" return 1 @@ -129,19 +129,19 @@ _dns_gcloud_find_zone() { filter="dnsName=( " while [ "$part" != "" ]; do filter="$filter$part. " - part="`echo \"$part\" | sed 's/[^.]*\.*//'`" + part="$(echo "$part" | sed 's/[^.]*\.*//')" done filter="$filter)" _debug filter "$filter" # List domains and find the longest match (in case of some levels of delegation) if ! match=$(gcloud dns managed-zones list \ - --format="value(name, dnsName)" \ - --filter="$filter" \ - | while read dnsName name; do - echo -e "${#dnsName}\t$dnsName\t$name" - done \ - | sort -n -r | head -n1 | cut -f2,3 | grep '.*'); then + --format="value(name, dnsName)" \ + --filter="$filter" \ + | while read -r dnsName name; do + printf "%s\t%s\t%s\n" "${#dnsName}" "$dnsName" "$name" + done \ + | sort -n -r | head -n1 | cut -f2,3 | grep '^.*'); then _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" return 1 fi @@ -154,10 +154,10 @@ _dns_gcloud_find_zone() { _dns_gcloud_get_rrdatas() { if ! rrdatas=$(gcloud dns record-sets list \ - --zone="$managedZone" \ - --name="$fulldomain." \ - --type=TXT \ - --format="value(ttl,rrdatas)"); then + --zone="$managedZone" \ + --name="$fulldomain." \ + --type=TXT \ + --format="value(ttl,rrdatas)"); then _err "_dns_gcloud_get_rrdatas: Failed to list record-sets" rm -r "$trd" return 1 From 1d4dec551068bd5b5fefc2f2b9258204305dc37c Mon Sep 17 00:00:00 2001 From: Janos Lenart Date: Sat, 26 May 2018 12:48:55 +0100 Subject: [PATCH 070/403] Moved dns_gcloud to 47. --- README.md | 2 +- dnsapi/README.md | 46 ++++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 07fbc849..cf29d76a 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,6 @@ You don't have to do anything manually! ### Currently acme.sh supports: -1. Google Cloud DNS API 1. CloudFlare.com API 1. DNSPod.cn API 1. CloudXNS.com API @@ -322,6 +321,7 @@ You don't have to do anything manually! 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) 1. EUSERV.EU (https://www.euserv.eu) +1. Google Cloud DNS API And: diff --git a/dnsapi/README.md b/dnsapi/README.md index b5fff915..31c99e8e 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -4,27 +4,6 @@ If your dns provider doesn't provide api access, you can use our dns alias mode: https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode -## 1. Use Google Cloud DNS API to automatically issue cert - -First you need to authenticate to gcloud. - -``` -gcloud init -``` - -**The `dns_gcloud` script uses the active gcloud configuration and credentials.** -There is no logic inside `dns_gcloud` to override the project and other settings. -If needed, create additional [gcloud configurations](https://cloud.google.com/sdk/gcloud/reference/topic/configurations). -You can change the configuration being used without *activating* it; simply set the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable. - -To issue a certificate you can: -``` -export CLOUDSDK_ACTIVE_CONFIG_NAME=default # see the note above -acme.sh --issue --dns dns_gcloud -d example.com -d '*.example.com' -``` - -`dns_gcloud` also supports [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode). - ## 1. Use CloudFlare domain API to automatically issue cert First you need to login to your CloudFlare account to get your API key. @@ -897,6 +876,7 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com ``` The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. +<<<<<<< HEAD ## 47. Use Euserv.eu API First you need to login to your euserv.eu account and activate your API Administration (API Verwaltung). @@ -918,6 +898,28 @@ acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. Please report any issues to https://github.com/initit/acme.sh or to + +## 48. Use Google Cloud DNS API to automatically issue cert + +First you need to authenticate to gcloud. + +``` +gcloud init +``` + +**The `dns_gcloud` script uses the active gcloud configuration and credentials.** +There is no logic inside `dns_gcloud` to override the project and other settings. +If needed, create additional [gcloud configurations](https://cloud.google.com/sdk/gcloud/reference/topic/configurations). +You can change the configuration being used without *activating* it; simply set the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable. + +To issue a certificate you can: +``` +export CLOUDSDK_ACTIVE_CONFIG_NAME=default # see the note above +acme.sh --issue --dns dns_gcloud -d example.com -d '*.example.com' +``` + +`dns_gcloud` also supports [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode). + # Use custom API If your API is not supported yet, you can write your own DNS API. @@ -938,4 +940,4 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API -https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api \ No newline at end of file +https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api From 441f8f3ce83e10bbf69a30a4d25c821d65e174b1 Mon Sep 17 00:00:00 2001 From: Janos Lenart Date: Wed, 15 Aug 2018 12:01:43 +0100 Subject: [PATCH 071/403] Replied to PR comments --- dnsapi/dns_gcloud.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index 92466181..99fbf410 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -77,7 +77,7 @@ _dns_gcloud_execute_tr() { for i in $(seq 1 120); do if gcloud dns record-sets changes list \ - --zone=lenart \ + --zone="$managedZone" \ --filter='status != done' \ | grep -q '^.*'; then _info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ($i/120)..." @@ -141,7 +141,7 @@ _dns_gcloud_find_zone() { | while read -r dnsName name; do printf "%s\t%s\t%s\n" "${#dnsName}" "$dnsName" "$name" done \ - | sort -n -r | head -n1 | cut -f2,3 | grep '^.*'); then + | sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" return 1 fi From 9e96a9317235ce3c775a048db2a78ec6f418fe2c Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 15 Aug 2018 18:36:24 +0200 Subject: [PATCH 072/403] Updated README with Gitlab help --- deploy/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index 181989da..5c03ce6a 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -275,3 +275,24 @@ acme.sh --deploy -d haproxy.example.com --deploy-hook haproxy ``` The path for the PEM file will be stored with the domain configuration and will be available when renewing, so that deploy will happen automatically when renewed. + +## 11. Deploy your cert to Gitlab pages + +You must define the API key and the informations for the project and Gitlab page you are updating the certificate for. + +```sh +# The token can be created in your user settings under "Access Tokens" +export GITLAB_TOKEN="xxxxxxxxxxx" + +# The project ID is displayed on the home page of the project +export GITLAB_PROJECT_ID=12345678 + +# The domain must match the one defined for the Gitlab page, without "https://" +export GITLAB_DOMAIN="www.mydomain.com" +``` + +You can then deploy the certificate as follows + +```sh +acme.sh --deploy -d www.mydomain.com --deploy-hook gitlab +``` \ No newline at end of file From d06eea53ef08c68340fb48590779f48df98716f7 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 15 Aug 2018 18:36:34 +0200 Subject: [PATCH 073/403] Add deploy plugin for Gitlab pages --- deploy/gitlab.sh | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 deploy/gitlab.sh diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh new file mode 100644 index 00000000..5bc53e8e --- /dev/null +++ b/deploy/gitlab.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env sh + +# Script to deploy certificate to a Gitlab hosted page + +# The following variables exported from environment will be used. +# If not set then values previously saved in domain.conf file are used. + +# All the variables are required + +# export GITLAB_TOKEN="xxxxxxx" +# export GITLAB_PROJECT_ID=012345 +# export GITLAB_DOMAIN="mydomain.com" + +gitlab_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if [ -z "$GITLAB_TOKEN" ]; then + if [ -z "$Le_Deploy_gitlab_token" ]; then + _err "GITLAB_TOKEN not defined." + return 1 + fi + else + Le_Deploy_gitlab_token="$GITLAB_TOKEN" + _savedomainconf Le_Deploy_gitlab_token "$Le_Deploy_gitlab_token" + fi + + if [ -z "$GITLAB_PROJECT_ID" ]; then + if [ -z "$Le_Deploy_gitlab_project_id" ]; then + _err "GITLAB_PROJECT_ID not defined." + return 1 + fi + else + Le_Deploy_gitlab_project_id="$GITLAB_PROJECT_ID" + _savedomainconf Le_Deploy_gitlab_project_id "$Le_Deploy_gitlab_project_id" + fi + + if [ -z "$GITLAB_DOMAIN" ]; then + if [ -z "$Le_Deploy_gitlab_domain" ]; then + _err "GITLAB_DOMAIN not defined." + return 1 + fi + else + Le_Deploy_gitlab_domain="$GITLAB_DOMAIN" + _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" + fi + + curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain > /dev/null && exit 0 + + # Exit curl status code if curl didn't work + exit $? +} From 0575eb671a8506d69eb81946d45e385732c6e8a7 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 15 Aug 2018 18:44:24 +0200 Subject: [PATCH 074/403] Fix double quote around URL --- deploy/gitlab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 5bc53e8e..9502da74 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -54,7 +54,7 @@ gitlab_deploy() { _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" fi - curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain > /dev/null && exit 0 + curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" > /dev/null && exit 0 # Exit curl status code if curl didn't work exit $? From 6d8292cdd8fe98a5f3d61072f1d8a53f8ceb2768 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 15 Aug 2018 19:00:08 +0200 Subject: [PATCH 075/403] Syntax fix --- deploy/gitlab.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 9502da74..6c1d0f4c 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -32,7 +32,7 @@ gitlab_deploy() { else Le_Deploy_gitlab_token="$GITLAB_TOKEN" _savedomainconf Le_Deploy_gitlab_token "$Le_Deploy_gitlab_token" - fi + fi if [ -z "$GITLAB_PROJECT_ID" ]; then if [ -z "$Le_Deploy_gitlab_project_id" ]; then @@ -42,7 +42,7 @@ gitlab_deploy() { else Le_Deploy_gitlab_project_id="$GITLAB_PROJECT_ID" _savedomainconf Le_Deploy_gitlab_project_id "$Le_Deploy_gitlab_project_id" - fi + fi if [ -z "$GITLAB_DOMAIN" ]; then if [ -z "$Le_Deploy_gitlab_domain" ]; then @@ -52,9 +52,9 @@ gitlab_deploy() { else Le_Deploy_gitlab_domain="$GITLAB_DOMAIN" _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" - fi + fi - curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" > /dev/null && exit 0 + curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 # Exit curl status code if curl didn't work exit $? From 75dd0a770f060eccb13f7ec449a6cc1cf1fba006 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 15 Aug 2018 19:10:31 +0200 Subject: [PATCH 076/403] Fix Syntax --- deploy/gitlab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 6c1d0f4c..174b2269 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -54,7 +54,7 @@ gitlab_deploy() { _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" fi - curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 + curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 # Exit curl status code if curl didn't work exit $? From b401dbbf65f9f671f3c4e66bd4aa75c8abbdf133 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 15 Aug 2018 19:17:24 +0200 Subject: [PATCH 077/403] Fix Syntax --- deploy/gitlab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 174b2269..e0222be5 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -55,7 +55,7 @@ gitlab_deploy() { fi curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 - + # Exit curl status code if curl didn't work exit $? } From 8113548920c4b3fdeee4ecdc3959d40d48410fd7 Mon Sep 17 00:00:00 2001 From: Aarup Date: Tue, 21 Aug 2018 11:44:36 +0200 Subject: [PATCH 078/403] Update dns api to support v2 wildcard cert #1261 --- dnsapi/dns_unoeuro.sh | 62 ++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index a3803a21..8be15427 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -50,35 +50,18 @@ dns_unoeuro_add() { _err "Error" return 1 fi + _info "Adding record" - if ! _contains "$response" "$_sub_domain" >/dev/null; then - _info "Adding record" - - if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then - if _contains "$response" "\"status\": 200" >/dev/null; then - _info "Added, OK" - return 0 - else - _err "Add txt record error." - return 1 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(_math "$record_line_number" - 1) - record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") - _debug "record_id" "$record_id" - - _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" + if _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 "Updated, OK" + _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." } #fulldomain txtvalue @@ -122,23 +105,24 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain"; then _info "Don't need to remove." else - record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(_math "$record_line_number" - 1) - record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") - _debug "record_id" "$record_id" - - if [ -z "$record_id" ]; then - _err "Can not get record id to remove." - return 1 - fi + for record_line_number in $(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1); do + record_line_number=$(_math "$record_line_number" - 1) + _debug "record_line_number" "$record_line_number" + record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") + _debug "record_id" "$record_id" + + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi - if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then - _err "Delete record error." - return 1 - fi - _contains "$response" "\"status\": 200" + if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" "\"status\": 200" + done fi - } #################### Private functions below ################################## From b23718f3ad8b7a5defc0fd67bbcf20f1ec9d1613 Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Tue, 21 Aug 2018 11:01:47 +0200 Subject: [PATCH 079/403] Add support for additional Lexicon options --- dnsapi/dns_lexicon.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_lexicon.sh b/dnsapi/dns_lexicon.sh index ab180fb2..f6f54464 100755 --- a/dnsapi/dns_lexicon.sh +++ b/dnsapi/dns_lexicon.sh @@ -78,7 +78,11 @@ dns_lexicon_add() { domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999) - $lexicon_cmd "$PROVIDER" create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" + _secure_debug LEXICON_OPTS "$LEXICON_OPTS" + _savedomainconf LEXICON_OPTS "$LEXICON_OPTS" + + # shellcheck disable=SC2086 + $lexicon_cmd "$PROVIDER" $LEXICON_OPTS create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" } @@ -93,6 +97,7 @@ dns_lexicon_rm() { domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999) - $lexicon_cmd "$PROVIDER" delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" + # shellcheck disable=SC2086 + $lexicon_cmd "$PROVIDER" $LEXICON_OPTS delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" } From 8b6986ba18367103d1efe32fed9961ccae40ac3a Mon Sep 17 00:00:00 2001 From: Aarup Date: Tue, 21 Aug 2018 12:32:30 +0200 Subject: [PATCH 080/403] Fix file formatting --- dnsapi/dns_unoeuro.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 8be15427..9132f136 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -61,7 +61,6 @@ dns_unoeuro_add() { return 1 fi fi - _err "Add txt record error." } #fulldomain txtvalue @@ -121,7 +120,7 @@ dns_unoeuro_rm() { return 1 fi _contains "$response" "\"status\": 200" - done + done fi } From c205777542ea8acf4ca9f36e5a55dc22c76b9515 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Tue, 21 Aug 2018 16:18:00 +0200 Subject: [PATCH 081/403] Better integration with acme.sh utils --- deploy/gitlab.sh | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index e0222be5..a95983af 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env sh -x # Script to deploy certificate to a Gitlab hosted page @@ -54,8 +54,29 @@ gitlab_deploy() { _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" fi - curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 + #curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 + + string_fullchain=$( _url_encode < $_cfullchain ) + string_key=$( _url_encode < $_ckey ) + + body="certificate=$string_fullchain&key=$string_key" + + export _H1="PRIVATE-TOKEN: $Le_Deploy_gitlab_token" - # Exit curl status code if curl didn't work - exit $? + gitlab_url="https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" + + _response=$( _post "$body" "$gitlab_url" 0 PUT | _dbase64 "multiline" ) + + error_response="error" + + if test "${_response#*$error_response}" != "$_response"; then + _err "Error in deploying certificate:" + _err "$_response" + return 1 + fi + + _debug response "$_response" + _info "Certificate successfully deployed" + + return 0 } From f1b0dd7836021db95470cc1d2269182edf35d0e1 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Tue, 21 Aug 2018 16:22:08 +0200 Subject: [PATCH 082/403] Fix Syntax --- deploy/gitlab.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index a95983af..1ec617b1 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh -x +#!/usr/bin/env sh # Script to deploy certificate to a Gitlab hosted page @@ -56,19 +56,19 @@ gitlab_deploy() { #curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 - string_fullchain=$( _url_encode < $_cfullchain ) - string_key=$( _url_encode < $_ckey ) + string_fullchain=$(_url_encode < $_cfullchain) + string_key=$(_url_encode < $_ckey) body="certificate=$string_fullchain&key=$string_key" - + export _H1="PRIVATE-TOKEN: $Le_Deploy_gitlab_token" gitlab_url="https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" - - _response=$( _post "$body" "$gitlab_url" 0 PUT | _dbase64 "multiline" ) + + _response=$(_post "$body" "$gitlab_url" 0 PUT | _dbase64 "multiline") error_response="error" - + if test "${_response#*$error_response}" != "$_response"; then _err "Error in deploying certificate:" _err "$_response" From 5a326b82bdb8569cb6c7980a5fcca85ec2791048 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Tue, 21 Aug 2018 16:24:57 +0200 Subject: [PATCH 083/403] Fix Syntax --- deploy/gitlab.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 1ec617b1..0d41ab28 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -56,8 +56,8 @@ gitlab_deploy() { #curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 - string_fullchain=$(_url_encode < $_cfullchain) - string_key=$(_url_encode < $_ckey) + string_fullchain=$(_url_encode <$_cfullchain) + string_key=$(_url_encode <$_ckey) body="certificate=$string_fullchain&key=$string_key" From bbf2a15f27acbce9f9a375f13a592b0ecb14e468 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Tue, 21 Aug 2018 16:30:33 +0200 Subject: [PATCH 084/403] Fix Syntax --- deploy/gitlab.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 0d41ab28..ece31c94 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -54,8 +54,6 @@ gitlab_deploy() { _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" fi - #curl -s --fail --request PUT --header "PRIVATE-TOKEN: $Le_Deploy_gitlab_token" --form "certificate=@$_cfullchain" --form "key=@$_ckey" "https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain" >/dev/null && exit 0 - string_fullchain=$(_url_encode <$_cfullchain) string_key=$(_url_encode <$_ckey) From e3c7fc8077aeb84c386da549dac035de855cab6c Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Tue, 21 Aug 2018 16:35:39 +0200 Subject: [PATCH 085/403] Fix Syntax --- deploy/gitlab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index ece31c94..66bb4ebf 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -56,7 +56,7 @@ gitlab_deploy() { string_fullchain=$(_url_encode <$_cfullchain) string_key=$(_url_encode <$_ckey) - + body="certificate=$string_fullchain&key=$string_key" export _H1="PRIVATE-TOKEN: $Le_Deploy_gitlab_token" From 8d6443b25da55693d4ff716b6ce76e849ae17c4d Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Tue, 21 Aug 2018 16:41:45 +0200 Subject: [PATCH 086/403] Fix Syntax --- deploy/gitlab.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/gitlab.sh b/deploy/gitlab.sh index 66bb4ebf..ba2d3122 100644 --- a/deploy/gitlab.sh +++ b/deploy/gitlab.sh @@ -54,8 +54,8 @@ gitlab_deploy() { _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain" fi - string_fullchain=$(_url_encode <$_cfullchain) - string_key=$(_url_encode <$_ckey) + string_fullchain=$(_url_encode <"$_cfullchain") + string_key=$(_url_encode <"$_ckey") body="certificate=$string_fullchain&key=$string_key" From 840b3a34cba3602e49a14dded23e2664a31fc277 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 21 Aug 2018 21:47:40 +0200 Subject: [PATCH 087/403] changed some chars --- dnsapi/dns_netcup.sh | 214 +++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 573550ed..2dfbdabb 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -8,123 +8,123 @@ end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON" client="" dns_netcup_add() { - login - if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then - _err "No Credentials given" - return 1 - fi - _saveaccountconf_mutable NC_Apikey "$NC_Apikey" - _saveaccountconf_mutable NC_Apipw "$NC_Apipw" - _saveaccountconf_mutable NC_CID "$NC_CID" - fulldomain=$1 - txtvalue=$2 - domain="" - exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) - exit=$(_math "$exit" + 1) - i=$exit + login + if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then + _err "No Credentials given" + return 1 + fi + _saveaccountconf_mutable NC_Apikey "$NC_Apikey" + _saveaccountconf_mutable NC_Apipw "$NC_Apipw" + _saveaccountconf_mutable NC_CID "$NC_CID" + fulldomain=$1 + txtvalue=$2 + domain="" + exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) + exit=$(_math "$exit" + 1) + i=$exit - while - [ "$exit" -gt 0 ]; do - tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") - if [ "$(_math "$i" - "$exit")" -eq 0 ]; then - domain="$tmp" - else - domain="$tmp.$domain" - fi - if [ "$(_math "$i" - "$exit")" -ge 1 ]; then - msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") - _debug "$msg" - if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - else - break - fi - fi - fi - exit=$(_math "$exit" - 1) - done - logout + while + [ "$exit" -gt 0 ]; do + tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") + if [ "$(_math "$i" - "$exit")" -eq 0 ]; then + domain="$tmp" + else + domain="$tmp.$domain" + fi + if [ "$(_math "$i" - "$exit")" -ge 1 ]; then + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") + _debug "$msg" + if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + else + break + fi + fi + fi + exit=$(_math "$exit" - 1) + done + logout } dns_netcup_rm() { - login - fulldomain=$1 - txtvalue=$2 + login + fulldomain=$1 + txtvalue=$2 - domain="" - exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) - exit=$(_math "$exit" + 1) - i=$exit - rec="" + domain="" + exit=$(echo "$fulldomain" | tr -dc '.' | wc -c) + exit=$(_math "$exit" + 1) + i=$exit + rec="" - while - [ "$exit" -gt 0 ]; do - tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") - if [ "$(_math "$i" - "$exit")" -eq 0 ]; then - domain="$tmp" - else - domain="$tmp.$domain" - fi - if [ "$(_math "$i" - "$exit")" -ge 1 ]; then - msg=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$domain\"}}" "$end" "" "POST") - rec=$(echo "$msg" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') - _debug "$msg" - if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - else - break - fi - fi - fi - exit=$(_math "$exit" - 1) - done + while + [ "$exit" -gt 0 ]; do + tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") + if [ "$(_math "$i" - "$exit")" -eq 0 ]; then + domain="$tmp" + else + domain="$tmp.$domain" + fi + if [ "$(_math "$i" - "$exit")" -ge 1 ]; then + msg=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$domain\"}}" "$end" "" "POST") + rec=$(echo "$msg" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g') + _debug "$msg" + if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + else + break + fi + fi + fi + exit=$(_math "$exit" - 1) + done - ida=0000 - idv=0001 - ids=0000000000 - i=1 - while - [ "$i" -ne 0 ]; do - specrec=$(_getfield "$rec" "$i" ";") - idv="$ida" - ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g') - txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g') - i=$(_math "$i" + 1) - if [ "$txtvalue" = "$txtv" ]; then - i=0 - ids="$ida" - fi - if [ "$ida" = "$idv" ]; then - i=0 - fi - done - msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") - _debug "$msg" - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - fi - logout + ida=0000 + idv=0001 + ids=0000000000 + i=1 + while + [ "$i" -ne 0 ]; do + specrec=$(_getfield "$rec" "$i" ";") + idv="$ida" + ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g') + txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g') + i=$(_math "$i" + 1) + if [ "$txtvalue" = "$txtv" ]; then + i=0 + ids="$ida" + fi + if [ "$ida" = "$idv" ]; then + i=0 + fi + done + msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST") + _debug "$msg" + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + fi + logout } login() { - tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') - _debug "$tmp" - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - fi + tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") + sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') + _debug "$tmp" + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + fi } logout() { - tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - _debug "$tmp" - if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then - _err "$msg" - return 1 - fi + tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") + _debug "$tmp" + if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then + _err "$msg" + return 1 + fi } From 4fffb3c8161358b1bdf9e570bedba4fb3c010803 Mon Sep 17 00:00:00 2001 From: linux-insideDE <39219399+linux-insideDE@users.noreply.github.com> Date: Tue, 21 Aug 2018 21:55:44 +0200 Subject: [PATCH 088/403] make shfmt happy --- dnsapi/dns_netcup.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 2dfbdabb..2273eb7c 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -24,7 +24,8 @@ dns_netcup_add() { i=$exit while - [ "$exit" -gt 0 ]; do + [ "$exit" -gt 0 ] + do tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") if [ "$(_math "$i" - "$exit")" -eq 0 ]; then domain="$tmp" @@ -60,7 +61,8 @@ dns_netcup_rm() { rec="" while - [ "$exit" -gt 0 ]; do + [ "$exit" -gt 0 ] + do tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit") if [ "$(_math "$i" - "$exit")" -eq 0 ]; then domain="$tmp" @@ -88,7 +90,8 @@ dns_netcup_rm() { ids=0000000000 i=1 while - [ "$i" -ne 0 ]; do + [ "$i" -ne 0 ] + do specrec=$(_getfield "$rec" "$i" ";") idv="$ida" ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g') From 2e74df2583cf2a28a74251a8f0c25d5e55d1a170 Mon Sep 17 00:00:00 2001 From: KUDO Takashi Date: Mon, 30 Jul 2018 19:41:11 +0900 Subject: [PATCH 089/403] Add support ConoHa DNS API --- README.md | 1 + dnsapi/README.md | 19 +++- dnsapi/dns_conoha.sh | 255 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 1 deletion(-) create mode 100755 dnsapi/dns_conoha.sh diff --git a/README.md b/README.md index ada8273a..d247707e 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,7 @@ You don't have to do anything manually! 1. EUSERV.EU (https://www.euserv.eu) 1. DNSPod.com API (https://www.dnspod.com) 1. Google Cloud DNS API +1. ConoHa (https://www.conoha.jp) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 8322679c..15c5026a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -876,7 +876,6 @@ acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com ``` The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. -<<<<<<< HEAD ## 47. Use Euserv.eu API First you need to login to your euserv.eu account and activate your API Administration (API Verwaltung). @@ -936,6 +935,24 @@ acme.sh --issue --dns dns_gcloud -d example.com -d '*.example.com' `dns_gcloud` also supports [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode). +## 50. Use ConoHa API + +First you need to login to your ConoHa account to get your API credentials. + +``` +export CONOHA_Username="xxxxxx" +export CONOHA_Password="xxxxxx" +export CONOHA_TenantId="xxxxxx" +export CONOHA_IdentityServiceApi="https://identity.xxxx.conoha.io/v2.0" +``` + +To issue a cert: +``` +acme.sh --issue --dns dns_conoha -d example.com -d www.example.com +``` + +The `CONOHA_Username`, `CONOHA_Password`, `CONOHA_TenantId` and `CONOHA_IdentityServiceApi` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + ======= # Use custom API diff --git a/dnsapi/dns_conoha.sh b/dnsapi/dns_conoha.sh new file mode 100755 index 00000000..f9e4ac17 --- /dev/null +++ b/dnsapi/dns_conoha.sh @@ -0,0 +1,255 @@ +#!/usr/bin/env sh + +CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\." + +######## Public functions ##################### + +#Usage: dns_conoha_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_conoha_add() { + fulldomain=$1 + txtvalue=$2 + _info "Using conoha" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + _debug "Check uesrname and password" + CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}" + CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}" + CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}" + CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}" + if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then + CONOHA_Username="" + CONOHA_Password="" + CONOHA_TenantId="" + CONOHA_IdentityServiceApi="" + _err "You didn't specify a conoha api username and password yet." + _err "Please create the user and try again." + return 1 + fi + + _saveaccountconf_mutable CONOHA_Username "$CONOHA_Username" + _saveaccountconf_mutable CONOHA_Password "$CONOHA_Password" + _saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId" + _saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi" + + if set -- $(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId"); then + accesstoken=$1 + CONOHA_Api=$2 + else + return 1 + fi + #return 1 #XXX + + _debug "First detect the root zone" + if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + #return 1 #XXX + + _info "Adding record" + body="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"data\":\"$txtvalue\",\"ttl\":60}" + if _conoha_rest POST "$CONOHA_Api/v1/domains/$_domain_id/records" "$body" "$accesstoken"; then + if _contains "$response" '"data":"'"$txtvalue"'"'; 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_conoha_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using conoha" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + _debug "Check uesrname and password" + CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}" + CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}" + CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}" + CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}" + if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then + CONOHA_Username="" + CONOHA_Password="" + CONOHA_TenantId="" + CONOHA_IdentityServiceApi="" + _err "You didn't specify a conoha api username and password yet." + _err "Please create the user and try again." + return 1 + fi + + _saveaccountconf_mutable CONOHA_Username "$CONOHA_Username" + _saveaccountconf_mutable CONOHA_Password "$CONOHA_Password" + _saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId" + _saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi" + + if set -- $(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId"); then + accesstoken=$1 + CONOHA_Api=$2 + else + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + if ! _conoha_rest GET "$CONOHA_Api/v1/domains/$_domain_id/records" "" "$accesstoken"; then + _err "Error" + return 1 + fi + + record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' | + grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" | + _head_n 1 | cut -d : -f 2 | tr -d \") + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + _debug record_id "$record_id" + + _info "Removing the txt record" + if ! _conoha_rest DELETE "$CONOHA_Api/v1/domains/$_domain_id/records/$record_id" "" "$accesstoken"; then + _err "Delete record error." + return 1 + fi + + return 0 +} + +#################### Private functions below ################################## + +_conoha_rest() { + m="$1" + ep="$2" + data="$3" + accesstoken="$4" + + export _H1="Accept: application/json" + export _H2="Content-Type: application/json" + if [ -n "$accesstoken" ]; then + export _H3="X-Auth-Token: $accesstoken" + fi + + _debug "$ep" + if [ "$m" != "GET" ]; then + _secure_debug2 data "$data" + response="$(_post "$data" "$ep" "" "$m")" + else + response="$(_get "$ep")" + fi + _ret="$?" + _secure_debug2 response "$response" + if [ "$_ret" != "0" ]; then + _err "error $ep" + return 1 + fi + + response="$(printf "%s" "$response" | _normalizeJson)" + return 0 +} + +_conoha_get_accesstoken() { + ep="$1" + username="$2" + password="$3" + tenantId="$4" + + accesstoken="$(_readaccountconf_mutable conoha_accesstoken)" + expires="$(_readaccountconf_mutable conoha_tokenvalidto)" + CONOHA_Api="$(_readaccountconf_mutable conoha_dns_ep)" + + # can we reuse the access token? + if [ -n "$accesstoken" ] && [ -n "$expires" ] && [ -n "$CONOHA_Api" ]; then + utc_date="$(_utc_date | sed "s/ /T/")" + if expr "$utc_date" "<" "$expires" >/dev/null; then + # access token is still valid - reuse it + _debug "reusing access token" + printf "%s\n%s" "$accesstoken" "$CONOHA_Api" + return 0 + else + _debug "access token expired" + fi + fi + _debug "getting new access token" + + body="$(printf '{"auth":{"passwordCredentials":{"username":"%s","password":"%s"},"tenantId":"%s"}}' "$username" "$password" "$tenantId")" + if ! _conoha_rest POST "$ep" "$body" ""; then + _err error "$response" + return 1 + fi + accesstoken=$(printf "%s" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") + expires=$(printf "%s" "$response" | _egrep_o "\"expires\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2-4 | tr -d \" | tr -d Z) #expect UTC + if [ -z "$accesstoken" ] || [ -z "$expires" ]; then + _err "no acccess token received. Check your Conoha settings see $WIKI" + return 1 + fi + _saveaccountconf_mutable conoha_accesstoken "$accesstoken" + _saveaccountconf_mutable conoha_tokenvalidto "$expires" + + CONOHA_Api=$(printf "%s" "$response" | _egrep_o 'publicURL":"'"$CONOHA_DNS_EP_PREFIX_REGEXP"'[^"]*"' | _head_n 1 | cut -d : -f 2-3 | tr -d \") + if [ -z "$CONOHA_Api" ]; then + _err "failed to get conoha dns endpoint url" + return 1 + fi + _saveaccountconf_mutable conoha_dns_ep "$CONOHA_Api" + + printf "%s\n%s" "$accesstoken" "$CONOHA_Api" + return 0 +} + +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain="$1" + ep="$2" + accesstoken="$3" + 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 ! _conoha_rest GET "$ep/v1/domains?name=$h" "" "$accesstoken"; then + 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 72a7f932c65c4fd2c889fd3220081bb2b005cf34 Mon Sep 17 00:00:00 2001 From: KUDO Takashi Date: Mon, 30 Jul 2018 22:03:14 +0900 Subject: [PATCH 090/403] fix indent --- dnsapi/dns_conoha.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_conoha.sh b/dnsapi/dns_conoha.sh index f9e4ac17..c573d172 100755 --- a/dnsapi/dns_conoha.sh +++ b/dnsapi/dns_conoha.sh @@ -117,9 +117,9 @@ dns_conoha_rm() { return 1 fi - record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' | - grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" | - _head_n 1 | cut -d : -f 2 | tr -d \") + record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' \ + | grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" \ + | _head_n 1 | cut -d : -f 2 | tr -d \") if [ -z "$record_id" ]; then _err "Can not get record id to remove." return 1 @@ -147,7 +147,7 @@ _conoha_rest() { export _H2="Content-Type: application/json" if [ -n "$accesstoken" ]; then export _H3="X-Auth-Token: $accesstoken" - fi + fi _debug "$ep" if [ "$m" != "GET" ]; then From a35d27166941762aa819da21f6c7452b6e2dd178 Mon Sep 17 00:00:00 2001 From: KUDO Takashi Date: Mon, 30 Jul 2018 22:15:57 +0900 Subject: [PATCH 091/403] cleanup --- dnsapi/dns_conoha.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dnsapi/dns_conoha.sh b/dnsapi/dns_conoha.sh index c573d172..694665b7 100755 --- a/dnsapi/dns_conoha.sh +++ b/dnsapi/dns_conoha.sh @@ -38,7 +38,6 @@ dns_conoha_add() { else return 1 fi - #return 1 #XXX _debug "First detect the root zone" if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then @@ -48,7 +47,6 @@ dns_conoha_add() { _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - #return 1 #XXX _info "Adding record" body="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"data\":\"$txtvalue\",\"ttl\":60}" @@ -176,7 +174,7 @@ _conoha_get_accesstoken() { accesstoken="$(_readaccountconf_mutable conoha_accesstoken)" expires="$(_readaccountconf_mutable conoha_tokenvalidto)" CONOHA_Api="$(_readaccountconf_mutable conoha_dns_ep)" - + # can we reuse the access token? if [ -n "$accesstoken" ] && [ -n "$expires" ] && [ -n "$CONOHA_Api" ]; then utc_date="$(_utc_date | sed "s/ /T/")" From 73d04b976ee638479e9dff65da43450a17a7858b Mon Sep 17 00:00:00 2001 From: KUDO Takashi Date: Mon, 30 Jul 2018 22:50:47 +0900 Subject: [PATCH 092/403] avoid "SC2046: Quote this to prevent word splitting." Travis CI error. --- dnsapi/dns_conoha.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_conoha.sh b/dnsapi/dns_conoha.sh index 694665b7..d3bee130 100755 --- a/dnsapi/dns_conoha.sh +++ b/dnsapi/dns_conoha.sh @@ -32,9 +32,9 @@ dns_conoha_add() { _saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId" _saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi" - if set -- $(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId"); then - accesstoken=$1 - CONOHA_Api=$2 + if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then + accesstoken="$(printf "%s" "$token" | sed -n 1p)" + CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)" else return 1 fi @@ -93,9 +93,9 @@ dns_conoha_rm() { _saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId" _saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi" - if set -- $(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId"); then - accesstoken=$1 - CONOHA_Api=$2 + if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then + accesstoken="$(printf "%s" "$token" | sed -n 1p)" + CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)" else return 1 fi @@ -181,7 +181,7 @@ _conoha_get_accesstoken() { if expr "$utc_date" "<" "$expires" >/dev/null; then # access token is still valid - reuse it _debug "reusing access token" - printf "%s\n%s" "$accesstoken" "$CONOHA_Api" + printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api" return 0 else _debug "access token expired" @@ -210,7 +210,7 @@ _conoha_get_accesstoken() { fi _saveaccountconf_mutable conoha_dns_ep "$CONOHA_Api" - printf "%s\n%s" "$accesstoken" "$CONOHA_Api" + printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api" return 0 } From 68a290c34752c2aa0b913332467b7a5f2c001111 Mon Sep 17 00:00:00 2001 From: Yann Bizeul Date: Wed, 22 Aug 2018 19:08:33 +0200 Subject: [PATCH 093/403] revert dns_inwx.sh to dev --- dnsapi/dns_inwx.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index f4590cf8..cd5af91b 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -158,8 +158,7 @@ _inwx_login() { export _H1 #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71 - if _contains "$response" "code1000" \ - && _contains "$response" "tfaGOOGLE-AUTH"; then + if _contains "$response" "tfa"; then if [ -z "$INWX_Shared_Secret" ]; then _err "Mobile TAN detected." _err "Please define a shared secret." From 1756bbff84e204bef1edaa953d2ffb0c04c9008b Mon Sep 17 00:00:00 2001 From: Herman Sletteng Date: Tue, 15 May 2018 11:31:43 +0200 Subject: [PATCH 094/403] DNS plugin for Danish service gratisdns.dk Currently only supports primary domains. My use case does not involve secondary domains so I'm not sure how it behaves, and cannot test it. Might be as simple as turning all "primary"-references into a variable that's either "primary" or "secondary", and make an extra check for this in _get_domain... Cookie handling heavily inspired by freedns plugin, including caching the cookie in the config file, so we can rm without re-authenticating --- README.md | 1 + dnsapi/README.md | 20 ++++++ dnsapi/dns_gdnsdk.sh | 168 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100755 dnsapi/dns_gdnsdk.sh diff --git a/README.md b/README.md index 0ba5eeb1..904a4789 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,7 @@ You don't have to do anything manually! 1. Google Cloud DNS API 1. ConoHa (https://www.conoha.jp) 1. netcup DNS API (https://www.netcup.de) +1. GratisDNS.dk (https://gratisdns.dk) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 47862d6c..891417f3 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -970,6 +970,26 @@ acme.sh --issue --dns dns_netcup -d example.com -d www.example.com The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 52. Use GratisDNS.dk + +GratisDNS.dk (https://gratisdns.dj/) does not provide an API to update DNS records (other than IPv4 and IPv6 +dynamic DNS addresses). The acme.sh plugin therefore retrieves and updates domain TXT records by logging +into the GratisDNS website to read the HTML and posting updates as HTTP. The plugin needs to know your +userid and password for the GratisDNS website. + +```sh +export GDNSDK_Username="..." +export GDNSDK_Password="..." +``` +The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + + +Now you can issue a certificate. + +```sh +acme.sh --issue --dns dns_gdnsdk -d example.com -d *.example.com +``` + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_gdnsdk.sh b/dnsapi/dns_gdnsdk.sh new file mode 100755 index 00000000..05a4c9fc --- /dev/null +++ b/dnsapi/dns_gdnsdk.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env sh +#Author: Herman Sletteng +#Report Bugs here: https://github.com/loial/acme.sh +# +# +# Note, gratisdns requires a login first, so the script needs to handle +# temporary cookies. Since acme.sh _get/_post currently don't directly support +# cookies, I've defined wrapper functions _myget/_mypost to set the headers + +GDNSDK_API="https://admin.gratisdns.com" +######## Public functions ##################### +#Usage: dns_gdnsdk_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_gdnsdk_add() { + fulldomain=$1 + txtvalue=$2 + _info "Using gratisdns.dk" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + if ! _gratisdns_login; then + _err "Login failed!" + return 1 + fi + #finding domain zone + if ! _get_domain; then + _err "No matching root domain for $fulldomain found" + return 1 + fi + # adding entry + _info "Adding the entry" + _mypost "action=dns_primary_record_added_txt&user_domain=$_domain&name=$fulldomain&txtdata=$txtvalue&ttl=1" + if _successful_update; then return 0; fi + _err "Couldn't create entry!" + return 1 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_gdnsdk_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using gratisdns.dk" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + if ! _gratisdns_login; then + _err "Login failed!" + return 1 + fi + if ! _get_domain; then + _err "No matching root domain for $fulldomain found" + return 1 + fi + _findentry "$fulldomain" "$txtvalue" + if [ -z "$_id" ]; then + _info "Entry doesn't exist, nothing to delete" + return 0 + fi + _debug "Deleting record..." + _mypost "action=dns_primary_delete_txt&user_domain=$_domain&id=$_id" + # removing entry + + if _successful_update; then return 0; fi + _err "Couldn't delete entry!" + return 1 +} + +#################### Private functions below ################################## + +_checkcredentials() { + GDNSDK_Username="${GDNSDK_Username:-$(_readaccountconf_mutable GDNSDK_Username)}" + GDNSDK_Password="${GDNSDK_Password:-$(_readaccountconf_mutable GDNSDK_Password)}" + + if [ -z "$GDNSDK_Username" ] || [ -z "$GDNSDK_Password" ]; then + GDNSDK_Username="" + GDNSDK_Password="" + _err "You haven't specified gratisdns.dk username and password yet." + _err "Please add credentials and try again." + return 1 + fi + #save the credentials to the account conf file. + _saveaccountconf_mutable GDNSDK_Username "$GDNSDK_Username" + _saveaccountconf_mutable GDNSDK_Password "$GDNSDK_Password" + return 0 +} + +_checkcookie() { + GDNSDK_Cookie="${GDNSDK_Cookie:-$(_readaccountconf_mutable GDNSDK_Cookie)}" + if [ -z "$GDNSDK_Cookie" ]; then + _debug "No cached cookie found" + return 1 + fi + _myget "action=" + if (echo "$_result" | grep -q "logmeout"); then + _debug "Cached cookie still valid" + return 0 + fi + _debug "Cached cookie no longer valid" + GDNSDK_Cookie="" + _saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie" + return 1 +} + +_gratisdns_login() { + if ! _checkcredentials; then return 1; fi + + if _checkcookie; then + _debug "Already logged in" + return 0 + fi + _debug "Logging into GratisDNS with user $GDNSDK_Username" + + if ! _mypost "login=$GDNSDK_Username&password=$GDNSDK_Password&action=logmein"; then + _err "GratisDNS login failed for user $GDNSDK_Username bad RC from _post" + return 1 + fi + + GDNSDK_Cookie="$(grep -A 15 '302 Found' "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]*' | _head_n 1 | cut -d ' ' -f2)" + + if [ -z "$GDNSDK_Cookie" ]; then + _err "GratisDNS login failed for user $GDNSDK_Username. Check $HTTP_HEADER file" + return 1 + fi + export GDNSDK_Cookie + _saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie" + return 0 +} + +_myget() { + #Adds cookie to request + export _H1="Cookie: $GDNSDK_Cookie" + _result=$(_get "$GDNSDK_API?$1") +} +_mypost() { + #Adds cookie to request + export _H1="Cookie: $GDNSDK_Cookie" + _result=$(_post "$1" "$GDNSDK_API") +} + +_get_domain() { + _myget 'action=dns_primarydns' + _domains=$(echo "$_result" | grep -o -P ' domain="\K([[:alnum:].-_]+)') + if [ -z "$_domains" ]; then + _err "Primary domain list not found!" + return 1 + fi + for _domain in $_domains; do + if (_endswith "$fulldomain" "$_domain"); then + _debug "Root domain: $_domain" + return 0 + fi + done + return 1 +} + +_successful_update() { + if (echo "$_result" | grep -q 'table-success'); then return 0; fi + return 1 +} + +_findentry() { + #returns id of dns entry, if it exists + _myget "action=dns_primary_changeDNSsetup&user_domain=$_domain" + _id=$(echo "$_result" | grep -o -P "$1\s*$2.*?id=\K(\d*)") + if [ -n "$_id" ]; then + _debug "Entry found with _id=$_id" + return 0 + fi + return 1 +} From 1f25b4a8a94ad14999fd19b87a29ea3d4383c237 Mon Sep 17 00:00:00 2001 From: Herman Sletteng Date: Fri, 24 Aug 2018 00:18:04 +0200 Subject: [PATCH 095/403] Replacing "grep -o -P" with "_egrep_o" and sed --- dnsapi/dns_gdnsdk.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_gdnsdk.sh b/dnsapi/dns_gdnsdk.sh index 05a4c9fc..7dc7894a 100755 --- a/dnsapi/dns_gdnsdk.sh +++ b/dnsapi/dns_gdnsdk.sh @@ -137,7 +137,7 @@ _mypost() { _get_domain() { _myget 'action=dns_primarydns' - _domains=$(echo "$_result" | grep -o -P ' domain="\K([[:alnum:].-_]+)') + _domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:].-_]+' | sed 's/^.*"//') if [ -z "$_domains" ]; then _err "Primary domain list not found!" return 1 @@ -159,7 +159,7 @@ _successful_update() { _findentry() { #returns id of dns entry, if it exists _myget "action=dns_primary_changeDNSsetup&user_domain=$_domain" - _id=$(echo "$_result" | grep -o -P "$1\s*$2.*?id=\K(\d*)") + _id=$(echo "$_result" | _egrep_o "$1\s*$2[^?]*[^&]*&id=[^&]*" | sed 's/^.*=//') if [ -n "$_id" ]; then _debug "Entry found with _id=$_id" return 0 From 12c900ea7d4f4da4de856611f5955bad23e8db25 Mon Sep 17 00:00:00 2001 From: Herman Sletteng Date: Wed, 29 Aug 2018 00:44:34 +0200 Subject: [PATCH 096/403] Gratisdns.dk: Fix typo in url, also added note recommending --dnssleep 300 --- dnsapi/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 891417f3..c8207b97 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -972,7 +972,7 @@ The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.con ## 52. Use GratisDNS.dk -GratisDNS.dk (https://gratisdns.dj/) does not provide an API to update DNS records (other than IPv4 and IPv6 +GratisDNS.dk (https://gratisdns.dk/) does not provide an API to update DNS records (other than IPv4 and IPv6 dynamic DNS addresses). The acme.sh plugin therefore retrieves and updates domain TXT records by logging into the GratisDNS website to read the HTML and posting updates as HTTP. The plugin needs to know your userid and password for the GratisDNS website. @@ -986,8 +986,11 @@ The username and password will be saved in `~/.acme.sh/account.conf` and will be Now you can issue a certificate. +Note: It usually takes a few minutes (usually 3-4 minutes) before the changes propagates to gratisdns.dk nameservers (ns3.gratisdns.dk often are slow), +and in rare cases I have seen over 5 minutes before google DNS catches it. Therefor a DNS sleep of at least 300 seconds are recommended- + ```sh -acme.sh --issue --dns dns_gdnsdk -d example.com -d *.example.com +acme.sh --issue --dns dns_gdnsdk --dnssleep 300 -d example.com -d *.example.com ``` # Use custom API From 0d03309c2f17bd5ed8e73a1425c956b4cc422a24 Mon Sep 17 00:00:00 2001 From: LLeny Date: Sun, 2 Sep 2018 21:25:44 +0800 Subject: [PATCH 097/403] Namecheap initial --- dnsapi/dns_namecheap.sh | 233 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100755 dnsapi/dns_namecheap.sh diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh new file mode 100755 index 00000000..67aa3acb --- /dev/null +++ b/dnsapi/dns_namecheap.sh @@ -0,0 +1,233 @@ +#!/usr/bin/env sh + +# Namecheap API +# https://www.namecheap.com/support/api/intro.aspx +# +# Requires Namecheap API key set in NAMECHEAP_API_KEY and NAMECHEAP_USERNAME set as environment variable +# +######## Public functions ##################### + +NAMECHEAP_API="https://api.sandbox.namecheap.com/xml.response" + +#Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_namecheap_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _namecheap_check_config; then + _err "$error" + return 1 + fi + + _namecheap_set_publicip + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + _debug domain "$_domain" + _debug sub_domain "$_sub_domain" + + _set_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue" +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_namecheap_rm() { + fulldomain=$1 + txtvalue=$2 + + _namecheap_set_publicip + + if ! _namecheap_check_config; then + _err "$error" + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + _debug domain "$_domain" + _debug sub_domain "$_sub_domain" + + _del_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue" + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + + if ! _namecheap_post "namecheap.domains.getList"; then + _err "$error" + 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" "$h"; then + _debug "$h not found" + else + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$h" + return 0 + fi + p="$i" + i=$(_math "$i" + 1) + done + return 1 +} + +_namecheap_set_publicip() { + _publicip="$(_get https://ifconfig.co/ip)" +} + +_namecheap_post() { + command=$1 + data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}" + + response="$(_post "$data" "$NAMECHEAP_API" "" "POST")" + _debug2 response "$response" + + if _contains "$response" "Status=\"ERROR\"" >/dev/null; then + error=$(echo "$response" | _egrep_o ">.*<\\/Error>" | cut -d '<' -f 1 | tr -d '>') + _err "error $error" + return 1 + fi + + return 0 +} + + +_namecheap_parse_host() { + _host=$1 + +#HostID UniqueID of the host records +#Name The domain or subdomain for which host record is set +#Type The type of host record that is set +#Address The value that is set for the host record (IP address for A record, URL for URL redirects, etc.) +#MXPref MXPreference number +#TTL TTL value for the host record + + _debug _host "$_host" + + _hostid=$(echo "$_host" | _egrep_o 'HostId=".*"' | cut -d '"' -f 2) + _hostname=$(echo "$_host" | _egrep_o 'Name=".*"' | cut -d '"' -f 2) + _hosttype=$(echo "$_host" | _egrep_o 'Type=".*"' | cut -d '"' -f 2) + _hostaddress=$(echo "$_host" | _egrep_o 'Address=".*"' | cut -d '"' -f 2) + _hostmxpref=$(echo "$_host" | _egrep_o 'MXPref=".*"' | cut -d '"' -f 2) + _hostttl=$(echo "$_host" | _egrep_o 'TTL=".*"' | cut -d '"' -f 2) + + _debug hostid "$_hostid" + _debug hostname "$_hostname" + _debug hosttype "$_hosttype" + _debug hostaddress "$_hostaddress" + _debug hostmxpref "$_hostmxpref" + _debug hostttl "$_hostttl" + +} + +_namecheap_check_config() { + + if [ -z "$NAMECHEAP_API_KEY" ]; then + _err "No API key specified for Namecheap API." + _err "Create your key and export it as NAMECHEAP_API_KEY" + return 1 + fi + + if [ -z "$NAMECHEAP_USERNAME" ]; then + _err "No username key specified for Namecheap API." + _err "Create your key and export it as NAMECHEAP_USERNAME" + return 1 + fi + + _saveaccountconf NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY" + _saveaccountconf NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME" + + return 0 +} + +_set_namecheap_TXT() { + subdomain=$2 + txt=$3 + tld=$(echo "$1" | cut -d '.' -f 2) + sld=$(echo "$1" | cut -d '.' -f 1) + request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" + + if ! _namecheap_post "$request"; then + _err "$error" + return 1 + fi + + hosts=$(echo "$response" | _egrep_o '') + _debug hosts "$hosts" + + if [ -z "$hosts" ]; then + _error "Hosts not found" + return 1 + fi + + i=0 + found=0 + + while read host; do + + if _contains "$host" " Date: Wed, 5 Sep 2018 21:29:42 +0800 Subject: [PATCH 098/403] WIP --- dnsapi/dns_namecheap.sh | 78 +++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 67aa3acb..89aeddd7 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -19,7 +19,9 @@ dns_namecheap_add() { return 1 fi - _namecheap_set_publicip + if ! _namecheap_set_publicip; then + return 1 + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -40,8 +42,10 @@ dns_namecheap_add() { dns_namecheap_rm() { fulldomain=$1 txtvalue=$2 - - _namecheap_set_publicip + + if ! _namecheap_set_publicip; then + return 1 + fi if ! _namecheap_check_config; then _err "$error" @@ -102,7 +106,35 @@ _get_root() { } _namecheap_set_publicip() { - _publicip="$(_get https://ifconfig.co/ip)" + + if [ -z "$NAMECHEAP_SOURCEIP" ]; then + _err "No Source IP specified for Namecheap API." + _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP" + return 1 + else + _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP" + _debug sourceip "$NAMECHEAP_SOURCEIP" + + ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') + addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*') + + _debug2 ip "$ip" + _debug2 addr "$addr" + + if [ -n "$ip" ]; then + _publicip="$ip" + elif [ -n "$addr" ]; then + _publicip=$(_get "$addr") + else + _err "No Source IP specified for Namecheap API." + _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP" + return 1 + fi + fi + + _debug publicip "$_publicip" + + return 0 } _namecheap_post() { @@ -124,14 +156,6 @@ _namecheap_post() { _namecheap_parse_host() { _host=$1 - -#HostID UniqueID of the host records -#Name The domain or subdomain for which host record is set -#Type The type of host record that is set -#Address The value that is set for the host record (IP address for A record, URL for URL redirects, etc.) -#MXPref MXPreference number -#TTL TTL value for the host record - _debug _host "$_host" _hostid=$(echo "$_host" | _egrep_o 'HostId=".*"' | cut -d '"' -f 2) @@ -190,38 +214,35 @@ _set_namecheap_TXT() { return 1 fi - i=0 + _namecheap_reset_hostList found=0 - while read host; do + while read -r host; do if _contains "$host" " Date: Fri, 7 Sep 2018 20:52:10 +0800 Subject: [PATCH 099/403] Usage --- dnsapi/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index 891417f3..48b0489f 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -990,6 +990,27 @@ Now you can issue a certificate. acme.sh --issue --dns dns_gdnsdk -d example.com -d *.example.com ``` +## 53. Use Namecheap + +You will need your namecheap username, API KEY (https://www.namecheap.com/support/api/intro.aspx) and your external IP address (or an URL to get it), this IP will need to be whitelisted at Namecheap. +Due to Namecheap's AP limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. + +```sh +export NAMECHEAP_USERNAME="..." +export NAMECHEAP_API_KEY="..." +export NAMECHEAP_SOURCEIP="..." +``` + +NAMECHEAP_SOURCEIP can either be an IP address (e.g. 145.34.23.54) or an URL to provide it (e.g. https://ifconfig.co/ip). + +The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +Now you can issue a certificate. + +```sh +acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com +``` + # Use custom API If your API is not supported yet, you can write your own DNS API. From dc0dd6588c7172892e87c91af57efda1fffad447 Mon Sep 17 00:00:00 2001 From: LLeny Date: Fri, 7 Sep 2018 20:52:34 +0800 Subject: [PATCH 100/403] Support list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 904a4789..b9a5cc59 100644 --- a/README.md +++ b/README.md @@ -326,6 +326,7 @@ You don't have to do anything manually! 1. ConoHa (https://www.conoha.jp) 1. netcup DNS API (https://www.netcup.de) 1. GratisDNS.dk (https://gratisdns.dk) +1. Namecheap API (https://www.namecheap.com/) And: From 8868783476809bf647fbd0c9efbba866306fe660 Mon Sep 17 00:00:00 2001 From: LLeny Date: Fri, 7 Sep 2018 20:52:49 +0800 Subject: [PATCH 101/403] Staging --- dnsapi/dns_namecheap.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 89aeddd7..73ed8650 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -7,7 +7,11 @@ # ######## Public functions ##################### -NAMECHEAP_API="https://api.sandbox.namecheap.com/xml.response" +if [ "$STAGE" -eq 1 ]; then + NAMECHEAP_API="https://api.sandbox.namecheap.com/xml.response" +else + NAMECHEAP_API="https://api.namecheap.com/xml.response" +fi #Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_namecheap_add() { From b859dd660c5b6c718fd71c595d3a1c1eb1e8bedd Mon Sep 17 00:00:00 2001 From: LLeny Date: Fri, 7 Sep 2018 20:53:21 +0800 Subject: [PATCH 102/403] dns_rm support --- dnsapi/dns_namecheap.sh | 80 +++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 73ed8650..0bf49e5f 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -129,7 +129,7 @@ _namecheap_set_publicip() { _publicip="$ip" elif [ -n "$addr" ]; then _publicip=$(_get "$addr") - else + else _err "No Source IP specified for Namecheap API." _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP" return 1 @@ -162,12 +162,12 @@ _namecheap_parse_host() { _host=$1 _debug _host "$_host" - _hostid=$(echo "$_host" | _egrep_o 'HostId=".*"' | cut -d '"' -f 2) - _hostname=$(echo "$_host" | _egrep_o 'Name=".*"' | cut -d '"' -f 2) - _hosttype=$(echo "$_host" | _egrep_o 'Type=".*"' | cut -d '"' -f 2) - _hostaddress=$(echo "$_host" | _egrep_o 'Address=".*"' | cut -d '"' -f 2) - _hostmxpref=$(echo "$_host" | _egrep_o 'MXPref=".*"' | cut -d '"' -f 2) - _hostttl=$(echo "$_host" | _egrep_o 'TTL=".*"' | cut -d '"' -f 2) + _hostid=$(echo "$_host" | _egrep_o '\sHostId="[^"]*' | cut -d '"' -f 2) + _hostname=$(echo "$_host" | _egrep_o '\sName="[^"]*' | cut -d '"' -f 2) + _hosttype=$(echo "$_host" | _egrep_o '\sType="[^"]*' | cut -d '"' -f 2) + _hostaddress=$(echo "$_host" | _egrep_o '\sAddress="[^"]*' | cut -d '"' -f 2) + _hostmxpref=$(echo "$_host" | _egrep_o '\sMXPref="[^"]*' | cut -d '"' -f 2) + _hostttl=$(echo "$_host" | _egrep_o '\sTTL="[^"]*' | cut -d '"' -f 2) _debug hostid "$_hostid" _debug hostname "$_hostname" @@ -210,7 +210,7 @@ _set_namecheap_TXT() { return 1 fi - hosts=$(echo "$response" | _egrep_o '') + hosts=$(echo "$response" | _egrep_o ']*') _debug hosts "$hosts" if [ -z "$hosts" ]; then @@ -219,29 +219,72 @@ _set_namecheap_TXT() { fi _namecheap_reset_hostList - found=0 while read -r host; do - if _contains "$host" "]*') + _debug hosts "$hosts" + + if [ -z "$hosts" ]; then + _error "Hosts not found" + return 1 + fi + + _namecheap_reset_hostList + + found=0 - if [ "$_hosttype" = "TXT" ] && [ "$_hostname" = "$subdomain" ]; then - _namecheap_add_host "$_hostname" "$_hosttype" "$txt" "$_hostmxpref" "$_hostttl" - found=1 + while read -r host; do + if _contains "$host" " Date: Sat, 8 Sep 2018 07:05:44 +0800 Subject: [PATCH 103/403] README fixes --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 48b0489f..1421cc23 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -993,7 +993,7 @@ acme.sh --issue --dns dns_gdnsdk -d example.com -d *.example.com ## 53. Use Namecheap You will need your namecheap username, API KEY (https://www.namecheap.com/support/api/intro.aspx) and your external IP address (or an URL to get it), this IP will need to be whitelisted at Namecheap. -Due to Namecheap's AP limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. +Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. ```sh export NAMECHEAP_USERNAME="..." @@ -1001,7 +1001,7 @@ export NAMECHEAP_API_KEY="..." export NAMECHEAP_SOURCEIP="..." ``` -NAMECHEAP_SOURCEIP can either be an IP address (e.g. 145.34.23.54) or an URL to provide it (e.g. https://ifconfig.co/ip). +NAMECHEAP_SOURCEIP can either be an IP address or an URL to provide it (e.g. https://ifconfig.co/ip). The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. From 30ee00ff50fca9345110c69c0cd4b9827f96f65d Mon Sep 17 00:00:00 2001 From: LLeny Date: Sat, 8 Sep 2018 07:06:16 +0800 Subject: [PATCH 104/403] RM TXT check --- dnsapi/dns_namecheap.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 0bf49e5f..9cf6fb1b 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -270,8 +270,7 @@ _del_namecheap_TXT() { while read -r host; do if _contains "$host" " Date: Sat, 8 Sep 2018 07:06:35 +0800 Subject: [PATCH 105/403] NC API warning --- dnsapi/dns_namecheap.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 9cf6fb1b..a3686088 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -3,8 +3,9 @@ # Namecheap API # https://www.namecheap.com/support/api/intro.aspx # -# Requires Namecheap API key set in NAMECHEAP_API_KEY and NAMECHEAP_USERNAME set as environment variable -# +# Requires Namecheap API key set in NAMECHEAP_API_KEY, NAMECHEAP_SOURCEIP and NAMECHEAP_USERNAME set as environment variable +# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. + ######## Public functions ##################### if [ "$STAGE" -eq 1 ]; then From 697e694de692b04531db2bc7e309c1afbe5e2616 Mon Sep 17 00:00:00 2001 From: LLeny Date: Sat, 8 Sep 2018 07:28:56 +0800 Subject: [PATCH 106/403] Indentation --- dnsapi/dns_namecheap.sh | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index a3686088..9ace134f 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -20,12 +20,12 @@ dns_namecheap_add() { txtvalue=$2 if ! _namecheap_check_config; then - _err "$error" - return 1 + _err "$error" + return 1 fi if ! _namecheap_set_publicip; then - return 1 + return 1 fi _debug "First detect the root zone" @@ -49,12 +49,12 @@ dns_namecheap_rm() { txtvalue=$2 if ! _namecheap_set_publicip; then - return 1 + return 1 fi if ! _namecheap_check_config; then - _err "$error" - return 1 + _err "$error" + return 1 fi _debug "First detect the root zone" @@ -81,8 +81,8 @@ _get_root() { domain=$1 if ! _namecheap_post "namecheap.domains.getList"; then - _err "$error" - return 1 + _err "$error" + return 1 fi i=2 @@ -117,7 +117,7 @@ _namecheap_set_publicip() { _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP" return 1 else - _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP" + _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP" _debug sourceip "$NAMECHEAP_SOURCEIP" ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') @@ -207,16 +207,16 @@ _set_namecheap_TXT() { request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" if ! _namecheap_post "$request"; then - _err "$error" - return 1 + _err "$error" + return 1 fi hosts=$(echo "$response" | _egrep_o ']*') _debug hosts "$hosts" if [ -z "$hosts" ]; then - _error "Hosts not found" - return 1 + _error "Hosts not found" + return 1 fi _namecheap_reset_hostList @@ -237,8 +237,8 @@ EOT request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}" if ! _namecheap_post "$request"; then - _err "$error" - return 1 + _err "$error" + return 1 fi return 0 @@ -252,16 +252,16 @@ _del_namecheap_TXT() { request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" if ! _namecheap_post "$request"; then - _err "$error" - return 1 + _err "$error" + return 1 fi hosts=$(echo "$response" | _egrep_o ']*') _debug hosts "$hosts" if [ -z "$hosts" ]; then - _error "Hosts not found" - return 1 + _error "Hosts not found" + return 1 fi _namecheap_reset_hostList @@ -271,9 +271,9 @@ _del_namecheap_TXT() { while read -r host; do if _contains "$host" " Date: Sat, 8 Sep 2018 08:06:35 +0800 Subject: [PATCH 107/403] shfmt --- dnsapi/dns_namecheap.sh | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 9ace134f..7089c2d0 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -47,7 +47,7 @@ dns_namecheap_add() { dns_namecheap_rm() { fulldomain=$1 txtvalue=$2 - + if ! _namecheap_set_publicip; then return 1 fi @@ -69,7 +69,6 @@ dns_namecheap_rm() { _debug sub_domain "$_sub_domain" _del_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue" - } #################### Private functions below ################################## @@ -89,7 +88,7 @@ _get_root() { p=1 while true; do - + h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" if [ -z "$h" ]; then @@ -111,7 +110,7 @@ _get_root() { } _namecheap_set_publicip() { - + if [ -z "$NAMECHEAP_SOURCEIP" ]; then _err "No Source IP specified for Namecheap API." _err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP" @@ -119,13 +118,13 @@ _namecheap_set_publicip() { else _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP" _debug sourceip "$NAMECHEAP_SOURCEIP" - + ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*') - + _debug2 ip "$ip" _debug2 addr "$addr" - + if [ -n "$ip" ]; then _publicip="$ip" elif [ -n "$addr" ]; then @@ -136,16 +135,16 @@ _namecheap_set_publicip() { return 1 fi fi - + _debug publicip "$_publicip" - + return 0 } _namecheap_post() { command=$1 data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}" - + response="$(_post "$data" "$NAMECHEAP_API" "" "POST")" _debug2 response "$response" @@ -158,7 +157,6 @@ _namecheap_post() { return 0 } - _namecheap_parse_host() { _host=$1 _debug _host "$_host" @@ -176,7 +174,6 @@ _namecheap_parse_host() { _debug hostaddress "$_hostaddress" _debug hostmxpref "$_hostmxpref" _debug hostttl "$_hostttl" - } _namecheap_check_config() { @@ -273,7 +270,7 @@ _del_namecheap_TXT() { _namecheap_parse_host "$host" if [ "$_hosttype" = "TXT" ] && [ "$_hostname" = "$subdomain" ] && [ "$_hostaddress" = "$txt" ]; then _debug "TXT entry found" - found=1 + found=1 else _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl" fi From 80b40c02b453538191f66d6d44aefbf7aed4b850 Mon Sep 17 00:00:00 2001 From: Christian Brandel Date: Mon, 10 Sep 2018 01:24:20 +0200 Subject: [PATCH 108/403] use perl instead of iconv, if iconv is not available --- deploy/fritzbox.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index 943b198d..21ea6cfd 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -28,8 +28,10 @@ fritzbox_deploy() { _debug _cfullchain "$_cfullchain" if ! _exists iconv; then - _err "iconv not found" - return 1 + if ! _exists perl; then + _err "iconv or perl not found" + return 1 + fi fi _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" @@ -61,7 +63,11 @@ fritzbox_deploy() { _info "Log in to the FRITZ!Box" _fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" - _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" + if _exists iconv; then + _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" + else + _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | md5sum | awk '{print $1}')" + fi _fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then From 5b7cac100220f4d6354d400d655c2e87d9023999 Mon Sep 17 00:00:00 2001 From: Tom Blauwendraat Date: Wed, 12 Sep 2018 05:34:56 +0200 Subject: [PATCH 109/403] [FIX] Delete all occurrences of TXT key, dont fail if there is more than one, [FIX] Respect pagination on domain listing, before only the first page was loaded --- dnsapi/dns_dgon.sh | 153 ++++++++++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 64 deletions(-) diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh index 5d38ef76..e92c7809 100755 --- a/dnsapi/dns_dgon.sh +++ b/dnsapi/dns_dgon.sh @@ -104,47 +104,59 @@ dns_dgon_rm() { ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}} GURL="https://api.digitalocean.com/v2/domains/$_domain/records" - ## while we dont have a record ID we keep going - while [ -z "$record" ]; do + ## Get all the matching records + while [ true ]; do ## 1) get the URL ## the create request - get ## args: URL, [onlyheader, timeout] 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*\"*[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\=[0-9]+")" - if [ -z "$nextpage" ]; then - _err "no record and no nextpage in digital ocean DNS removal" - return 1 - fi - _debug2 nextpage "$nextpage" - GURL="$nextpage" + + ## check response + if [ "$?" != "0" ]; then + _err "error in domain_list response: $domain_list" + return 1 fi - ## we break out of the loop when we have a record - done + _debug2 domain_list "$domain_list" - ## we found the record - rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")" - _debug rec_id "$rec_id" + ## 2) find records + ## check for what we are looking for: "type":"A","name":"$_sub_domain" + record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" - ## delete the record - ## delete URL for removing the one we dont want - DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id" + if [ ! -z "$record" ]; then + + ## we found records + rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")" + _debug rec_ids "$rec_ids" + if [ ! -z "$rec_ids" ]; then + echo "$rec_ids" | while IFS= read -r rec_id ; do + ## delete the record + ## delete URL for removing the one we dont want + DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id" + + ## the create request - delete + ## args: BODY, URL, [need64, httpmethod] + response="$(_post "" "$DURL" "" "DELETE")" + + ## check response (sort of) + if [ "$?" != "0" ]; then + _err "error in remove response: $response" + return 1 + fi + _debug2 response "$response" + + done + fi + fi - ## the create request - delete - ## args: BODY, URL, [need64, httpmethod] - response="$(_post "" "$DURL" "" "DELETE")" + ## 3) find the next page + nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")" + if [ -z "$nextpage" ]; then + break + fi + _debug2 nextpage "$nextpage" + GURL="$nextpage" - ## check response (sort of) - if [ "$?" != "0" ]; then - _err "error in remove response: $response" - return 1 - fi - _debug2 response "$response" + done ## finished correctly return 0 @@ -178,44 +190,57 @@ _get_base_domain() { export _H2="Authorization: Bearer $DO_API_KEY" _debug DO_API_KEY "$DO_API_KEY" ## get URL for the list of domains - ## havent seen this request paginated, tested with 18 domains (more requires manual requests with DO) + ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}} DOMURL="https://api.digitalocean.com/v2/domains" - ## get the domain list (DO gives basically a full XFER!) - domain_list="$(_get "$DOMURL")" + ## while we dont have a matching domain we keep going + while [ -z "$found" ]; do + ## get the domain list (current page) + domain_list="$(_get "$DOMURL")" - ## check response - if [ "$?" != "0" ]; then - _err "error in domain_list response: $domain_list" - return 1 - fi - _debug2 domain_list "$domain_list" - - ## for each shortening of our $fulldomain, check if it exists in the $domain_list - ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge" - i=2 - while [ $i -gt 0 ]; do - ## get next longest domain - _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM") - ## check we got something back from our cut (or are we at the end) - if [ -z "$_domain" ]; then - ## we got to the end of the domain - invalid domain - _err "domain not found in DigitalOcean account" + ## check response + if [ "$?" != "0" ]; then + _err "error in domain_list response: $domain_list" return 1 fi - ## we got part of a domain back - grep it out - found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")" - ## check if it exists - if [ ! -z "$found" ]; then - ## exists - exit loop returning the parts - sub_point=$(_math $i - 1) - _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point") - _debug _domain "$_domain" - _debug _sub_domain "$_sub_domain" - return 0 + _debug2 domain_list "$domain_list" + + ## for each shortening of our $fulldomain, check if it exists in the $domain_list + ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge" + i=2 + while [ $i -gt 0 ]; do + ## get next longest domain + _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM") + ## check we got something back from our cut (or are we at the end) + if [ -z "$_domain" ]; then + break + fi + ## we got part of a domain back - grep it out + found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")" + ## check if it exists + if [ ! -z "$found" ]; then + ## exists - exit loop returning the parts + sub_point=$(_math $i - 1) + _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point") + _debug _domain "$_domain" + _debug _sub_domain "$_sub_domain" + return 0 + fi + ## increment cut point $i + i=$(_math $i + 1) + done + + if [ -z "$found" ]; 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\=[0-9]+")" + if [ -z "$nextpage" ]; then + _err "no record and no nextpage in digital ocean DNS removal" + return 1 + fi + _debug2 nextpage "$nextpage" + DOMURL="$nextpage" fi - ## increment cut point $i - i=$(_math $i + 1) + done ## we went through the entire domain zone list and dint find one that matched From 4a18c45e4f34bcc05ecc3891af9ac72f5df2b68f Mon Sep 17 00:00:00 2001 From: Tom Blauwendraat Date: Wed, 12 Sep 2018 05:46:51 +0200 Subject: [PATCH 110/403] fixup! [FIX] Delete all occurrences of TXT key, dont fail if there is more than one, [FIX] Respect pagination on domain listing, before only the first page was loaded --- dnsapi/dns_dgon.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh index e92c7809..24e1a9f2 100755 --- a/dnsapi/dns_dgon.sh +++ b/dnsapi/dns_dgon.sh @@ -105,7 +105,7 @@ dns_dgon_rm() { GURL="https://api.digitalocean.com/v2/domains/$_domain/records" ## Get all the matching records - while [ true ]; do + while true; do ## 1) get the URL ## the create request - get ## args: URL, [onlyheader, timeout] @@ -128,7 +128,7 @@ dns_dgon_rm() { rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")" _debug rec_ids "$rec_ids" if [ ! -z "$rec_ids" ]; then - echo "$rec_ids" | while IFS= read -r rec_id ; do + echo "$rec_ids" | while IFS= read -r rec_id; do ## delete the record ## delete URL for removing the one we dont want DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id" From 332263073222754fa6fe5066b1a38e06d831276b Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 25 Sep 2018 23:42:04 +0800 Subject: [PATCH 111/403] minor, debug msg --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 32219d9d..db2953e2 100755 --- a/acme.sh +++ b/acme.sh @@ -1809,14 +1809,14 @@ _send_signed_request() { if [ -z "$_CACHED_NONCE" ]; then _headers="" if [ "$ACME_NEW_NONCE" ]; then - _debug2 "Get nonce. ACME_NEW_NONCE" "$ACME_NEW_NONCE" + _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE" if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then _headers="$(cat "$HTTP_HEADER")" fi fi if [ -z "$_headers" ]; then - _debug2 "Get nonce. ACME_DIRECTORY" "$ACME_DIRECTORY" + _debug2 "Get nonce with GET. ACME_DIRECTORY" "$ACME_DIRECTORY" nonceurl="$ACME_DIRECTORY" _headers="$(_get "$nonceurl" "onlyheader")" fi From 31d9ba7e02777cfd1492f2cfeea2db1bd78b9867 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Fri, 28 Sep 2018 08:45:18 +0800 Subject: [PATCH 112/403] Change default for reload --- deploy/haproxy.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index f6e3716f..0318c23c 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -17,6 +17,9 @@ # export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" # # OPTIONAL: Reload command used post deploy +# This defaults to be a no-op (ie "true"). +# It is strongly recommended to set this something that makes sense +# for your distro. # # export DEPLOY_HAPROXY_ISSUER="no" # @@ -249,7 +252,7 @@ haproxy_deploy() { # An OCSP file was already present but certificate did not have OCSP extension if [ -f "${_ocsp}" ]; then _err "OCSP was not requested but .ocsp file exists." - # Should remove the file at this step, although HAProxy just ignores it in this case + # Could remove the file at this step, although HAProxy just ignores it in this case # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file" fi fi From 0a4e61c1dd421f1e36eb9945891c1e1a0ac2d848 Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Fri, 28 Sep 2018 08:46:39 +0800 Subject: [PATCH 113/403] Readme update --- deploy/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 7b058c4d..8cefeffa 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -267,13 +267,13 @@ export DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy You may optionally specify the file name where you want the concatenated key and certificate chain written. The value shown below will be used as the default if you don't set this environment variable. ```sh -export DEPLOY_HAPROXY_PEM_PATH=$domain +export DEPLOY_HAPROXY_PEM_NAME=$domain ``` You may optionally define the command to reload HAProxy. The value shown below will be used as the default if you don't set this environment variable. ```sh -export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" +export DEPLOY_HAPROXY_RELOAD="true" ``` You may optionally specify that the issuer certificate is transferred to "${DEPLOY_HAPROXY_PEM}.issuer". This is a requirement to support OCSP stapling in HAProxy. The value shown below will be used as the default if you don't set this environment variable. From 454c90820d56db2d62d0315d4232eefecbc2cc8a Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Fri, 28 Sep 2018 08:57:13 +0800 Subject: [PATCH 114/403] Actually set reload default --- deploy/haproxy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 0318c23c..2479aebd 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -7,7 +7,7 @@ # export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem" # # Defines the name of the PEM file. -# Defaults to "domain.pem" +# Defaults to ".pem" # # export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy" # @@ -52,7 +52,7 @@ haproxy_deploy() { DEPLOY_HAPROXY_PEM_NAME_DEFAULT="${_cdomain}.pem" DEPLOY_HAPROXY_BUNDLE_DEFAULT="no" DEPLOY_HAPROXY_ISSUER_DEFAULT="no" - DEPLOY_HAPROXY_RELOAD_DEFAULT="systemctl reload haproxy" + DEPLOY_HAPROXY_RELOAD_DEFAULT="true" if [ -f "${DOMAIN_CONF}" ]; then # shellcheck disable=SC1090 From 4c1f70af4b27781a3f5055328f704a268fb8a5d4 Mon Sep 17 00:00:00 2001 From: evoadmin Date: Tue, 2 Oct 2018 10:43:25 +0300 Subject: [PATCH 115/403] Update dns_he.sh If you have a password with special char it will fail at Remove record --- dnsapi/dns_he.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index da4a1b81..df00c746 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -92,7 +92,9 @@ dns_he_rm() { return 1 fi # Remove the record - body="email=${HE_Username}&pass=${HE_Password}" + username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)" + password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)" + body="email=${username_encoded}&pass=${password_encoded}" body="$body&menu=edit_zone" body="$body&hosted_dns_zoneid=$_zone_id" body="$body&hosted_dns_recordid=$_record_id" From 475e6e28eb1eb998f37adcfa09f22faa869a7d9a Mon Sep 17 00:00:00 2001 From: Ne-Lexa Date: Fri, 12 Oct 2018 19:04:18 +0300 Subject: [PATCH 116/403] Added dns api support for internet.bs --- README.md | 1 + dnsapi/dns_internetbs.sh | 173 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100755 dnsapi/dns_internetbs.sh diff --git a/README.md b/README.md index b9a5cc59..b0c2d02a 100644 --- a/README.md +++ b/README.md @@ -327,6 +327,7 @@ You don't have to do anything manually! 1. netcup DNS API (https://www.netcup.de) 1. GratisDNS.dk (https://gratisdns.dk) 1. Namecheap API (https://www.namecheap.com/) +1. Internet.bs API (https://internetbs.net/) And: diff --git a/dnsapi/dns_internetbs.sh b/dnsapi/dns_internetbs.sh new file mode 100755 index 00000000..bf227e3a --- /dev/null +++ b/dnsapi/dns_internetbs.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env sh + +#This is the Internet.BS api wrapper for acme.sh +# +#Author: Ne-Lexa +#Report Bugs here: https://github.com/Ne-Lexa/acme.sh + +#INTERNETBS_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" +#INTERNETBS_API_PASSWORD="sdfsdfsdfljlbjkljlkjsdfoiwje" +INTERNETBS_API_URL="https://api.internet.bs" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_internetbs_add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$INTERNETBS_API_KEY" ] || [ -z "$INTERNETBS_API_PASSWORD" ]; then + INTERNETBS_API_KEY="" + INTERNETBS_API_PASSWORD="" + _err "You didn't specify the INTERNET.BS api key and password yet." + _err "Please create you key and try again." + return 1 + fi + + _saveaccountconf INTERNETBS_API_KEY "$INTERNETBS_API_KEY" + _saveaccountconf INTERNETBS_API_PASSWORD "$INTERNETBS_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" + + # https://testapi.internet.bs/Domain/DnsRecord/Add?ApiKey=testapi&Password=testpass&FullRecordName=w3.test-api-domain7.net&Type=CNAME&Value=www.internet.bs%&ResponseFormat=json + if _internetbs_rest POST "Domain/DnsRecord/Add" "FullRecordName=${_sub_domain}.${_domain}&Type=TXT&Value=${txtvalue}&ResponseFormat=json"; then + if ! _contains "$response" "\"status\":\"SUCCESS\""; then + _err "ERROR add TXT record" + _err "$response" + _clearaccountconf INTERNETBS_API_KEY + _clearaccountconf INTERNETBS_API_PASSWORD + return 1 + fi + + _info "txt record add success." + return 0 + fi + + return 1 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_internetbs_rm() { + fulldomain=$1 + txtvalue=$2 + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + # https://testapi.internet.bs/Domain/DnsRecord/List?ApiKey=testapi&Password=testpass&Domain=test-api-domain7.net&FilterType=CNAME&ResponseFormat=json + _internetbs_rest POST "Domain/DnsRecord/List" "Domain=$_domain&FilterType=TXT&ResponseFormat=json" + + if ! _contains "$response" "\"status\":\"SUCCESS\""; then + _err "ERROR list dns records" + _err "$response" + _clearaccountconf INTERNETBS_API_KEY + _clearaccountconf INTERNETBS_API_PASSWORD + return 1 + fi + + if _contains "$response" "\name\":\"${_sub_domain}.${_domain}\""; then + _info "txt record find." + + # https://testapi.internet.bs/Domain/DnsRecord/Remove?ApiKey=testapi&Password=testpass&FullRecordName=www.test-api-domain7.net&Type=cname&ResponseFormat=json + _internetbs_rest POST "Domain/DnsRecord/Remove" "FullRecordName=${_sub_domain}.${_domain}&Type=TXT&ResponseFormat=json" + + if ! _contains "$response" "\"status\":\"SUCCESS\""; then + _err "ERROR remove dns record" + _err "$response" + _clearaccountconf INTERNETBS_API_KEY + _clearaccountconf INTERNETBS_API_PASSWORD + return 1 + fi + + _info "txt record deleted success." + return 0 + fi + + return 1 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=12345 +_get_root() { + domain=$1 + i=2 + p=1 + + # https://testapi.internet.bs/Domain/List?ApiKey=testapi&Password=testpass&CompactList=yes&ResponseFormat=json + if _internetbs_rest POST "Domain/List" "CompactList=yes&ResponseFormat=json"; then + + if ! _contains "$response" "\"status\":\"SUCCESS\""; then + _err "ERROR fetch domain list" + _err "$response" + _clearaccountconf INTERNETBS_API_KEY + _clearaccountconf INTERNETBS_API_PASSWORD + return 1 + fi + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f ${i}-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if _contains "$response" "\"$h\""; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-${p}) + _domain=${h} + return 0 + fi + + p=i + i=$(_math "$i" + 1) + done + fi + return 1 +} + +#Usage: method URI data +_internetbs_rest() { + m="$1" + ep="$2" + data="$3" + url="${INTERNETBS_API_URL}/${ep}" + + _debug url "$url" + + apiKey="$(printf "%s" "${INTERNETBS_API_KEY}" | _url_encode)" + password="$(printf "%s" "${INTERNETBS_API_PASSWORD}" | _url_encode)" + + if [ "$m" = "GET" ]; then + response="$(_get "${url}?ApiKey=${apiKey}&Password=${password}&${data}" | tr -d '\r')" + else + _debug2 data "$data" + response="$(_post "$data" "${url}?ApiKey=${apiKey}&Password=${password}" | tr -d '\r')" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + + _debug2 response "$response" + return 0 +} From fdb9d93b1211b673ddf9589f367343256ba677a3 Mon Sep 17 00:00:00 2001 From: Ne-Lexa Date: Fri, 12 Oct 2018 19:27:41 +0300 Subject: [PATCH 117/403] formatted --- dnsapi/dns_internetbs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_internetbs.sh b/dnsapi/dns_internetbs.sh index bf227e3a..ba170836 100755 --- a/dnsapi/dns_internetbs.sh +++ b/dnsapi/dns_internetbs.sh @@ -69,7 +69,7 @@ dns_internetbs_rm() { _debug _domain "$_domain" _debug "Getting txt records" - # https://testapi.internet.bs/Domain/DnsRecord/List?ApiKey=testapi&Password=testpass&Domain=test-api-domain7.net&FilterType=CNAME&ResponseFormat=json + # https://testapi.internet.bs/Domain/DnsRecord/List?ApiKey=testapi&Password=testpass&Domain=test-api-domain7.net&FilterType=CNAME&ResponseFormat=json _internetbs_rest POST "Domain/DnsRecord/List" "Domain=$_domain&FilterType=TXT&ResponseFormat=json" if ! _contains "$response" "\"status\":\"SUCCESS\""; then From 9f6f721a133a30ea135d33ea004d30ffbac31de8 Mon Sep 17 00:00:00 2001 From: Ephen Date: Mon, 15 Oct 2018 17:11:25 +0800 Subject: [PATCH 118/403] cloudxns.net cloudxns.net is the main domain. --- dnsapi/dns_cx.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index f2d3eadb..d07d8e0c 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -# Cloudxns.com Domain api +# CloudXNS Domain api # #CX_Key="1234" # @@ -19,7 +19,7 @@ dns_cx_add() { if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then CX_Key="" CX_Secret="" - _err "You don't specify cloudxns.com api key or secret yet." + _err "You don't specify cloudxns.net api key or secret yet." _err "Please create you key and try again." return 1 fi From a63dc75b43414d0d553cf89ee3a82e613420739d Mon Sep 17 00:00:00 2001 From: Ne-Lexa Date: Mon, 15 Oct 2018 18:20:26 +0300 Subject: [PATCH 119/403] Added documentation on using dns api internet.bs --- dnsapi/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index 2cecfa5a..0f867580 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1014,6 +1014,22 @@ Now you can issue a certificate. acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com ``` +## 54. Use Internet.bs + +First you need to create/obtain API credentials on your Internet.bs (https://internetbs.net) account. Go to the "Get my API Key" section in the "My Domains" section. + +``` +export INTERNETBS_API_KEY="..." +export INTERNETBS_API_PASSWORD="..." +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_internetbs -d example.com -d www.example.com +``` + +The `INTERNETBS_API_KEY` and `INTERNETBS_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. From fd536d373ebd65eecf8ce8e2f760ef186e5ce74f Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 18 Oct 2018 17:12:06 +0100 Subject: [PATCH 120/403] Skip aliases of already verified domains. When issuing a two-domain certificate using a different alias for each domain, if the first domain is already verified, verification for the second domain would be attempted (unsuccessfully) using the alias of the first domain. Increment the alias index when skipping verified domains so that the correct alias will be used for subsequent domains. --- acme.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acme.sh b/acme.sh index ee238274..8ae6600f 100755 --- a/acme.sh +++ b/acme.sh @@ -2925,6 +2925,7 @@ _clearupdns() { _debug txt "$txt" if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then _debug "$d is already verified, skip $vtype." + _alias_index="$(_math "$_alias_index" + 1)" continue fi @@ -3775,6 +3776,7 @@ $_authorizations_map" _debug d "$d" if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then _debug "$d is already verified, skip $vtype." + _alias_index="$(_math "$_alias_index" + 1)" continue fi From 26421684dc0c7c0cc79ea36f728d44de2fa382f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 26 Jul 2018 19:59:15 +0200 Subject: [PATCH 121/403] Fix inwx account without Mobile TAN --- dnsapi/dns_inwx.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index cd5af91b..f4590cf8 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -158,7 +158,8 @@ _inwx_login() { export _H1 #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71 - if _contains "$response" "tfa"; then + if _contains "$response" "code1000" \ + && _contains "$response" "tfaGOOGLE-AUTH"; then if [ -z "$INWX_Shared_Secret" ]; then _err "Mobile TAN detected." _err "Please define a shared secret." From 46b3a9158c80ab5f1c76437ed1fb9fad2c13e96a Mon Sep 17 00:00:00 2001 From: LLeny <5269958+LLeny@users.noreply.github.com> Date: Sun, 21 Oct 2018 18:17:23 +0800 Subject: [PATCH 122/403] Fixes Neilpang/acme.sh#1888 --- dnsapi/dns_namecheap.sh | 64 +++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 7089c2d0..a6651be6 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -199,9 +199,12 @@ _namecheap_check_config() { _set_namecheap_TXT() { subdomain=$2 txt=$3 - tld=$(echo "$1" | cut -d '.' -f 2) - sld=$(echo "$1" | cut -d '.' -f 1) - request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" + + if ! _namecheap_set_tld_sld "$1"; then + return 1 + fi + + request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}" if ! _namecheap_post "$request"; then _err "$error" @@ -231,7 +234,7 @@ EOT _debug hostrequestfinal "$_hostrequest" - request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}" + request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}" if ! _namecheap_post "$request"; then _err "$error" @@ -244,9 +247,12 @@ EOT _del_namecheap_TXT() { subdomain=$2 txt=$3 - tld=$(echo "$1" | cut -d '.' -f 2) - sld=$(echo "$1" | cut -d '.' -f 1) - request="namecheap.domains.dns.getHosts&SLD=$sld&TLD=$tld" + + if ! _namecheap_set_tld_sld "$1"; then + return 1 + fi + + request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}" if ! _namecheap_post "$request"; then _err "$error" @@ -286,7 +292,7 @@ EOT _debug hostrequestfinal "$_hostrequest" - request="namecheap.domains.dns.setHosts&SLD=${sld}&TLD=${tld}${_hostrequest}" + request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}" if ! _namecheap_post "$request"; then _err "$error" @@ -306,3 +312,45 @@ _namecheap_add_host() { _hostindex=$(_math "$_hostindex" + 1) _hostrequest=$(printf '%s&HostName%d=%s&RecordType%d=%s&Address%d=%s&MXPref%d=%d&TTL%d=%d' "$_hostrequest" "$_hostindex" "$1" "$_hostindex" "$2" "$_hostindex" "$3" "$_hostindex" "$4" "$_hostindex" "$5") } + +_namecheap_set_tld_sld() { + domain=$1 + _tld="" + _sld="" + + i=2 + + while true; do + + _tld=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug tld "$_tld" + + if [ -z "$_tld" ]; then + _debug "invalid tld" + return 1 + fi + + j=$(_math "$i" - 1) + + _sld=$(printf "%s" "$domain" | cut -d . -f 1-"$j") + _debug sld "$_sld" + + if [ -z "$_sld" ]; then + _debug "invalid sld" + return 1 + fi + + request="namecheap.domains.dns.getHosts&SLD=$_sld&TLD=$_tld" + + if ! _namecheap_post "$request"; then + _debug "sld($_sld)/tld($_tld) not found" + else + _debug "sld($_sld)/tld($_tld) found" + return 0 + fi + + i=$(_math "$i" + 1) + + done + +} From a894b7cc9b374d5588b346a9afde1282b5d067d3 Mon Sep 17 00:00:00 2001 From: hebbet Date: Wed, 24 Oct 2018 16:33:02 +0200 Subject: [PATCH 123/403] add link to cloudflare profil for api key --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 2cecfa5a..71ba53b8 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -6,7 +6,7 @@ https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode ## 1. Use CloudFlare domain API to automatically issue cert -First you need to login to your CloudFlare account to get your API key. +First you need to login to your CloudFlare account to get your [API key](https://dash.cloudflare.com/profile). ``` export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" From a207199879a21c58433273f7461bf4a464d7a8f9 Mon Sep 17 00:00:00 2001 From: Ne-Lexa Date: Mon, 29 Oct 2018 15:18:43 +0300 Subject: [PATCH 124/403] fixed _get_root() function --- dnsapi/dns_internetbs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_internetbs.sh b/dnsapi/dns_internetbs.sh index ba170836..05a1adae 100755 --- a/dnsapi/dns_internetbs.sh +++ b/dnsapi/dns_internetbs.sh @@ -137,7 +137,7 @@ _get_root() { return 0 fi - p=i + p=${i} i=$(_math "$i" + 1) done fi From 9672c6b885a100a9c83ce1651591271a5f3e2b2a Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 30 Oct 2018 22:14:49 +0800 Subject: [PATCH 125/403] fix https://github.com/Neilpang/acme.sh/issues/1905 --- acme.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index ee238274..b59332b9 100755 --- a/acme.sh +++ b/acme.sh @@ -4602,7 +4602,8 @@ deploy() { _initpath "$_d" "$_isEcc" if [ ! -d "$DOMAIN_PATH" ]; then - _err "Domain is not valid:'$_d'" + _err "The domain '$_d' is not a cert name. You must use the cert name to specify the cert to install." + _err "Can not find path:'$DOMAIN_PATH'" return 1 fi @@ -4629,7 +4630,8 @@ installcert() { _initpath "$_main_domain" "$_isEcc" if [ ! -d "$DOMAIN_PATH" ]; then - _err "Domain is not valid:'$_main_domain'" + _err "The domain '$_main_domain' is not a cert name. You must use the cert name to specify the cert to install." + _err "Can not find path:'$DOMAIN_PATH'" return 1 fi From 7903fcb48c3b90bced87b187a05c538ec7c74fe8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 30 Oct 2018 22:50:44 +0800 Subject: [PATCH 126/403] fix typo --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index b59332b9..7944d5df 100755 --- a/acme.sh +++ b/acme.sh @@ -5476,7 +5476,7 @@ Parameters: --log-level 1|2 Specifies the log level, default is 1. --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug. - These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert: + These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert: --cert-file After issue/renew, the cert will be copied to this path. --key-file After issue/renew, the key will be copied to this path. From 12956679e73e615882fc556518fba00c2d07baf4 Mon Sep 17 00:00:00 2001 From: Joakim Lemb Date: Mon, 5 Nov 2018 14:52:26 +0100 Subject: [PATCH 127/403] Added top URI parameter --- dnsapi/dns_azure.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index c6893a0c..ae8aa1ca 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -316,7 +316,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?\$top=500&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 4b581f37203a8dea1c7c1ef5d25322fed49bb3e8 Mon Sep 17 00:00:00 2001 From: pavelaks Date: Sat, 10 Nov 2018 12:10:06 +0300 Subject: [PATCH 128/403] Update README.md Fix VSCALE example (add export before VSCALE_API_KEY) --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 2cecfa5a..9413925b 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -454,7 +454,7 @@ The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account. First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/). ``` -VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" +export VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" ``` Ok, let's issue a cert now: From 552710ac2a2e5dcb85d31f3d3d4ed5c2a0dbd5ec Mon Sep 17 00:00:00 2001 From: nakermann1973 <35577878+nakermann1973@users.noreply.github.com> Date: Tue, 13 Nov 2018 10:15:38 +0100 Subject: [PATCH 129/403] Add missing bind-tools package The bind_tools package is required for dns_nsupdate to work --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 5a64c720..c1a2199b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ RUN apk update -f \ && apk --no-cache add -f \ openssl \ coreutils \ + bind-tools \ curl \ socat \ && rm -rf /var/cache/apk/* From 5fee82ce39e70a301cece87185762f415489b258 Mon Sep 17 00:00:00 2001 From: Thomas Rohlik Date: Mon, 19 Nov 2018 16:09:32 +0100 Subject: [PATCH 130/403] Fix dot Very important commit :1st_place_medal: --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index ee238274..0930effd 100755 --- a/acme.sh +++ b/acme.sh @@ -5485,7 +5485,7 @@ Parameters: --server SERVER ACME Directory Resource URI. (default: https://acme-v01.api.letsencrypt.org/directory) --accountconf Specifies a customized account config file. - --home Specifies the home dir for $PROJECT_NAME . + --home Specifies the home dir for $PROJECT_NAME. --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. From 137dc1eac0bdc6f664c7fbc3aae9b1cce4c58a85 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 23 Nov 2018 22:53:02 +0800 Subject: [PATCH 131/403] fix https://github.com/Neilpang/acme.sh/issues/1912 --- acme.sh | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/acme.sh b/acme.sh index c7138c1b..502b6950 100755 --- a/acme.sh +++ b/acme.sh @@ -124,23 +124,19 @@ if [ -t 1 ]; then fi __green() { - if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" -o "${ACME_FORCE_COLOR}" = "1" ]; then - printf '\033[1;31;32m' + if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then + printf '\033[1;31;32m%b\033[0m' "$1" + return fi printf -- "%b" "$1" - if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" -o "${ACME_FORCE_COLOR}" = "1" ]; then - printf '\033[0m' - fi } __red() { - if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" -o "${ACME_FORCE_COLOR}" = "1" ]; then - printf '\033[1;31;40m' + if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then + printf '\033[1;31;40m%b\033[0m' "$1" + return fi printf -- "%b" "$1" - if [ "$__INTERACTIVE${ACME_NO_COLOR}" = "1" -o "${ACME_FORCE_COLOR}" = "1" ]; then - printf '\033[0m' - fi } _printargs() { From a6f2110141011c950c6709b083331f0dd933de9b Mon Sep 17 00:00:00 2001 From: epgdatacapbon Date: Sat, 24 Nov 2018 01:58:46 +0900 Subject: [PATCH 132/403] Add DNS API support for MyDNS.JP --- dnsapi/README.md | 16 ++++ dnsapi/dns_mydnsjp.sh | 210 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 dnsapi/dns_mydnsjp.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 2cecfa5a..7362eb2c 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1014,6 +1014,22 @@ Now you can issue a certificate. acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com ``` +## 54. Use MyDNS.JP API + +First, register to MyDNS.JP and get MasterID and Password. + +``` +export MYDNSJP_MasterID=MasterID +export MYDNSJP_Password=Password +``` + +To issue a certificate: + +``` +acme.sh --issue --dns dns_mydnsjp -d example.com -d www.example.com +``` +The `MYDNSJP_MasterID` and `MYDNSJP_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_mydnsjp.sh b/dnsapi/dns_mydnsjp.sh new file mode 100644 index 00000000..d421329c --- /dev/null +++ b/dnsapi/dns_mydnsjp.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env sh + +#Here is a api script for MyDNS.JP. +#This file name is "dns_mydnsjp.sh" +#So, here must be a method dns_mydnsjp_add() +#Which will be called by acme.sh to add the txt record to your api system. +#returns 0 means success, otherwise error. +# +#Author: epgdatacapbon +#Report Bugs here: https://github.com/epgdatacapbon/acme.sh +# +######## Public functions ##################### + +# Export MyDNS.JP MasterID and Password in following variables... +# MYDNSJP_MasterID=MasterID +# MYDNSJP_Password=Password + +MYDNSJP_API="http://www.mydns.jp" + +#Usage: dns_mydnsjp_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_mydnsjp_add() { + fulldomain=$1 + txtvalue=$2 + + _info "Using mydnsjp" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + # Load the credentials from the account conf file + MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}" + MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}" + if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then + MYDNSJP_MasterID="" + MYDNSJP_Password="" + _err "You don't specify mydnsjp api MasterID and Password yet." + _err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again." + return 1 + fi + + # Save the credentials to the account conf file + _saveaccountconf_mutable MYDNSJP_MasterID "$MYDNSJP_MasterID" + _saveaccountconf_mutable MYDNSJP_Password "$MYDNSJP_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 _mydnsjp_api "REGIST" "$_domain" "$txtvalue"; 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_mydnsjp_rm() { + fulldomain=$1 + txtvalue=$2 + + _info "Removing TXT record" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + # Load the credentials from the account conf file + MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}" + MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}" + if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then + MYDNSJP_MasterID="" + MYDNSJP_Password="" + _err "You don't specify mydnsjp api MasterID and Password yet." + _err "Please export as MYDNSJP_MasterID / MYDNSJP_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 _mydnsjp_api "DELETE" "$_domain" "$txtvalue"; then + if printf -- "%s" "$response" | grep "OK." >/dev/null; then + _info "Deleted, OK" + return 0 + else + _err "Delete txt record error." + return 1 + fi + fi + _err "Delete txt record error." + + return 1 +} + +#################### 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 + + # Get the root domain + _mydnsjp_retrieve_domain + if [ "$?" != "0" ]; then + # not valid + return 1 + fi + + while true; do + _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) + + if [ -z "$_domain" ]; then + # not valid + return 1 + fi + + if [ "$_domain" = "$_root_domain" ]; then + _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +# Retrieve the root domain +# returns 0 success +_mydnsjp_retrieve_domain() { + _debug "Login to MyDNS.JP" + + response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")" + cookie="$(grep '^Set-Cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)" + + # If cookies is not empty then logon successful + if [ -z "$cookie" ]; then + _err "Fail to get a cookie." + return 1 + fi + + _debug "Retrieve DOMAIN INFO page" + + export _H1="Cookie:${cookie}" + + response="$(_get "$MYDNSJP_API/?MENU=300")" + + if [ "$?" != "0" ]; then + _err "Fail to retrieve DOMAIN INFO." + return 1 + fi + + _root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/') + + # Logout + response="$(_get "$MYDNSJP_API/?MENU=090")" + + _debug _root_domain "$_root_domain" + + if [ -z "$_root_domain" ]; then + _err "Fail to get the root domain." + return 1 + fi + + return 0 +} + +_mydnsjp_api() { + cmd=$1 + domain=$2 + txtvalue=$3 + + # Base64 encode the credentials + credentials=$(printf "%s:%s" "$MYDNSJP_MasterID" "$MYDNSJP_Password" | _base64) + + # Construct the HTTP Authorization header + export _H1="Content-Type: application/x-www-form-urlencoded" + export _H2="Authorization: Basic ${credentials}" + + response="$(_post "CERTBOT_DOMAIN=$domain&CERTBOT_VALIDATION=$txtvalue&EDIT_CMD=$cmd" "$MYDNSJP_API/directedit.html")" + + if [ "$?" != "0" ]; then + _err "error $domain" + return 1 + fi + + _debug2 response "$response" + + return 0 +} From 14ad5955b58a48720da0b70bb902900029f9b3f8 Mon Sep 17 00:00:00 2001 From: epgdatacapbon Date: Sat, 24 Nov 2018 01:58:46 +0900 Subject: [PATCH 133/403] Add DNS API support for MyDNS.JP --- dnsapi/README.md | 16 ++++ dnsapi/dns_mydnsjp.sh | 210 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 dnsapi/dns_mydnsjp.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 02e8fd82..f126568b 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1014,6 +1014,22 @@ Now you can issue a certificate. acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com ``` +## 54. Use MyDNS.JP API + +First, register to MyDNS.JP and get MasterID and Password. + +``` +export MYDNSJP_MasterID=MasterID +export MYDNSJP_Password=Password +``` + +To issue a certificate: + +``` +acme.sh --issue --dns dns_mydnsjp -d example.com -d www.example.com +``` +The `MYDNSJP_MasterID` and `MYDNSJP_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_mydnsjp.sh b/dnsapi/dns_mydnsjp.sh new file mode 100644 index 00000000..d421329c --- /dev/null +++ b/dnsapi/dns_mydnsjp.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env sh + +#Here is a api script for MyDNS.JP. +#This file name is "dns_mydnsjp.sh" +#So, here must be a method dns_mydnsjp_add() +#Which will be called by acme.sh to add the txt record to your api system. +#returns 0 means success, otherwise error. +# +#Author: epgdatacapbon +#Report Bugs here: https://github.com/epgdatacapbon/acme.sh +# +######## Public functions ##################### + +# Export MyDNS.JP MasterID and Password in following variables... +# MYDNSJP_MasterID=MasterID +# MYDNSJP_Password=Password + +MYDNSJP_API="http://www.mydns.jp" + +#Usage: dns_mydnsjp_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_mydnsjp_add() { + fulldomain=$1 + txtvalue=$2 + + _info "Using mydnsjp" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + # Load the credentials from the account conf file + MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}" + MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}" + if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then + MYDNSJP_MasterID="" + MYDNSJP_Password="" + _err "You don't specify mydnsjp api MasterID and Password yet." + _err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again." + return 1 + fi + + # Save the credentials to the account conf file + _saveaccountconf_mutable MYDNSJP_MasterID "$MYDNSJP_MasterID" + _saveaccountconf_mutable MYDNSJP_Password "$MYDNSJP_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 _mydnsjp_api "REGIST" "$_domain" "$txtvalue"; 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_mydnsjp_rm() { + fulldomain=$1 + txtvalue=$2 + + _info "Removing TXT record" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + # Load the credentials from the account conf file + MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}" + MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}" + if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then + MYDNSJP_MasterID="" + MYDNSJP_Password="" + _err "You don't specify mydnsjp api MasterID and Password yet." + _err "Please export as MYDNSJP_MasterID / MYDNSJP_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 _mydnsjp_api "DELETE" "$_domain" "$txtvalue"; then + if printf -- "%s" "$response" | grep "OK." >/dev/null; then + _info "Deleted, OK" + return 0 + else + _err "Delete txt record error." + return 1 + fi + fi + _err "Delete txt record error." + + return 1 +} + +#################### 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 + + # Get the root domain + _mydnsjp_retrieve_domain + if [ "$?" != "0" ]; then + # not valid + return 1 + fi + + while true; do + _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) + + if [ -z "$_domain" ]; then + # not valid + return 1 + fi + + if [ "$_domain" = "$_root_domain" ]; then + _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +# Retrieve the root domain +# returns 0 success +_mydnsjp_retrieve_domain() { + _debug "Login to MyDNS.JP" + + response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")" + cookie="$(grep '^Set-Cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)" + + # If cookies is not empty then logon successful + if [ -z "$cookie" ]; then + _err "Fail to get a cookie." + return 1 + fi + + _debug "Retrieve DOMAIN INFO page" + + export _H1="Cookie:${cookie}" + + response="$(_get "$MYDNSJP_API/?MENU=300")" + + if [ "$?" != "0" ]; then + _err "Fail to retrieve DOMAIN INFO." + return 1 + fi + + _root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/') + + # Logout + response="$(_get "$MYDNSJP_API/?MENU=090")" + + _debug _root_domain "$_root_domain" + + if [ -z "$_root_domain" ]; then + _err "Fail to get the root domain." + return 1 + fi + + return 0 +} + +_mydnsjp_api() { + cmd=$1 + domain=$2 + txtvalue=$3 + + # Base64 encode the credentials + credentials=$(printf "%s:%s" "$MYDNSJP_MasterID" "$MYDNSJP_Password" | _base64) + + # Construct the HTTP Authorization header + export _H1="Content-Type: application/x-www-form-urlencoded" + export _H2="Authorization: Basic ${credentials}" + + response="$(_post "CERTBOT_DOMAIN=$domain&CERTBOT_VALIDATION=$txtvalue&EDIT_CMD=$cmd" "$MYDNSJP_API/directedit.html")" + + if [ "$?" != "0" ]; then + _err "error $domain" + return 1 + fi + + _debug2 response "$response" + + return 0 +} From d55c64c83891e06fef9301c3bbbad54b36de27b2 Mon Sep 17 00:00:00 2001 From: epgdatacapbon Date: Sat, 24 Nov 2018 14:07:50 +0900 Subject: [PATCH 134/403] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b9a5cc59..e0f601d3 100644 --- a/README.md +++ b/README.md @@ -327,6 +327,7 @@ You don't have to do anything manually! 1. netcup DNS API (https://www.netcup.de) 1. GratisDNS.dk (https://gratisdns.dk) 1. Namecheap API (https://www.namecheap.com/) +1. MyDNS.JP API (https://www.mydns.jp/) And: From be5085f2052321f0fa1887bd7be7b3ca0de52aa2 Mon Sep 17 00:00:00 2001 From: epgdatacapbon Date: Sun, 25 Nov 2018 18:14:52 +0900 Subject: [PATCH 135/403] Increase security using https for MyDNS.JP API --- dnsapi/dns_mydnsjp.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 dnsapi/dns_mydnsjp.sh diff --git a/dnsapi/dns_mydnsjp.sh b/dnsapi/dns_mydnsjp.sh old mode 100644 new mode 100755 index d421329c..aab2aabf --- a/dnsapi/dns_mydnsjp.sh +++ b/dnsapi/dns_mydnsjp.sh @@ -15,7 +15,7 @@ # MYDNSJP_MasterID=MasterID # MYDNSJP_Password=Password -MYDNSJP_API="http://www.mydns.jp" +MYDNSJP_API="https://www.mydns.jp" #Usage: dns_mydnsjp_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_mydnsjp_add() { @@ -151,7 +151,7 @@ _mydnsjp_retrieve_domain() { _debug "Login to MyDNS.JP" response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")" - cookie="$(grep '^Set-Cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)" + cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)" # If cookies is not empty then logon successful if [ -z "$cookie" ]; then From 7917aa2a7c7012d2d0ee2e5d14924dbee801a3b5 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 2 Dec 2018 19:37:35 +0800 Subject: [PATCH 136/403] fix https://github.com/Neilpang/acme.sh/issues/1941 cache dns zones response --- dnsapi/dns_cf.sh | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 3595b9b0..cbebb03a 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -34,6 +34,9 @@ dns_cf_add() { _saveaccountconf_mutable CF_Key "$CF_Key" _saveaccountconf_mutable CF_Email "$CF_Email" + _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '@.' '__')" + _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -58,9 +61,12 @@ dns_cf_add() { # 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 + if _contains "$response" "$fulldomain"; then _info "Added, OK" return 0 + elif _contains "$response" "The record already exists"; then + _info "Already exists, OK" + return 0 else _err "Add txt record error." return 1 @@ -99,11 +105,16 @@ dns_cf_rm() { return 1 fi + _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '@.' '__')" + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then + _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" _err "invalid domain" return 1 fi + _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" + _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" @@ -143,6 +154,21 @@ dns_cf_rm() { # _domain=domain.com # _domain_id=sdjkglgdfewsdfg _get_root() { + + _cf_zones="$(_readdomainconf $_DOMAIN_CF_ZONES_CACHE_NAME_)" + _debug2 "_cf_zones" "$_cf_zones" + if [ -z "$_cf_zones" ]; then + _debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ is none, so get it." + if ! _cf_rest GET "zones"; then + return 1 + fi + _cf_zones="$response" + _savedomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" "$(echo "$_cf_zones" | _base64)" + else + _debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ found" + _cf_zones="$(echo "$_cf_zones" | _dbase64)" + fi + domain=$1 i=2 p=1 @@ -154,12 +180,8 @@ _get_root() { return 1 fi - if ! _cf_rest GET "zones?name=$h"; then - 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 _contains "$_cf_zones" "\"name\":\"$h\"" >/dev/null; then + _domain_id=$(printf "%s\n" "$_cf_zones" | _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 From 9a27b389765ac6d7a256333e9e1f6fe3c4b92e08 Mon Sep 17 00:00:00 2001 From: "Aaron W. Swenson" Date: Sun, 2 Dec 2018 11:18:41 -0500 Subject: [PATCH 137/403] Update Linode API to v4 Linode API has made breaking changes that are resolved by this update. No user action is required. Additionally, related README.md entry updated to include new cloud manager interface. --- dnsapi/README.md | 13 +++++++++++-- dnsapi/dns_linode.sh | 39 ++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index bb0e89d0..5642aa4e 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -264,9 +264,18 @@ when needed. ## 14. Use Linode domain API First you need to login to your Linode account to get your API Key. -[https://manager.linode.com/profile/api](https://manager.linode.com/profile/api) -Then add an API key with label *ACME* and copy the new key. + * [Classic Manager](https://manager.linode.com/profile/api) + + Under "Add an API key", Give the new key a "Label" (we recommend *ACME*), + set the expiry to never, "Create API Key", and copy the new key into the `LINODE_API_KEY` command + below. + + * [Cloud Manager](https://cloud.linode.com/profile/tokens) + + Click on "Add a Personal Access Token". Give the new key a "Label" (we + recommend *ACME*), give it Read/Write access to "Domains". "Submit", and + copy the new key into the `LINODE_API_KEY` command below. ```sh export LINODE_API_KEY="..." diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index ead5b164..d03e6c47 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -2,7 +2,7 @@ #Author: Philipp Grosswiler -LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action=" +LINODE_API_URL="https://api.linode.com/v4/domains" ######## Public functions ##################### @@ -27,10 +27,14 @@ dns_linode_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue" + _payload="{ + \"type\": \"TXT\", + \"name\": \"$_sub_domain\", + \"target\": \"$txtvalue\" + }" - if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then - _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) + if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then + _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) _debug _resource_id "$_resource_id" if [ -z "$_resource_id" ]; then @@ -65,25 +69,21 @@ dns_linode_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _parameters="&DomainID=$_domain_id" - - if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then + if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" - resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" + resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then - _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) if [ "$_resource_id" ]; then _debug _resource_id "$_resource_id" - _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id" - - if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then - _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) - _debug _resource_id "$_resource_id" + if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then + # On 200/OK, empty set is returned. Check for error, if any. + _error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1) - if [ -z "$_resource_id" ]; then - _err "Error deleting the domain resource." + if [ -n "$_error_response" ]; then + _err "Error deleting the domain resource: $_error_response" return 1 fi @@ -127,7 +127,7 @@ _get_root() { i=2 p=1 - if _rest GET "domain.list"; then + if _rest GET; then response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -137,9 +137,9 @@ _get_root() { return 1 fi - hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")" + hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")" if [ "$hostedzone" ]; then - _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _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 @@ -165,6 +165,7 @@ _rest() { export _H1="Accept: application/json" export _H2="Content-Type: application/json" + export _H3="Authorization: Bearer $LINODE_API_KEY" if [ "$mtd" != "GET" ]; then # both POST and DELETE. From 598becf6197d750615f9f62a881f4c383c2f35b2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 3 Dec 2018 20:31:20 +0800 Subject: [PATCH 138/403] minor, fix format --- dnsapi/dns_cf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index cbebb03a..944956ad 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -155,7 +155,7 @@ dns_cf_rm() { # _domain_id=sdjkglgdfewsdfg _get_root() { - _cf_zones="$(_readdomainconf $_DOMAIN_CF_ZONES_CACHE_NAME_)" + _cf_zones="$(_readdomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_")" _debug2 "_cf_zones" "$_cf_zones" if [ -z "$_cf_zones" ]; then _debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ is none, so get it." From fb08b53f0b4c43db21bc33b804c031c9cc8ae975 Mon Sep 17 00:00:00 2001 From: Adrian Almenar Date: Mon, 3 Dec 2018 18:42:33 +0100 Subject: [PATCH 139/403] Add Neodigit.net DNS API --- README.md | 3 +- dnsapi/README.md | 13 +++ dnsapi/dns_neodigit.sh | 181 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_neodigit.sh diff --git a/README.md b/README.md index c4ea5c6e..6dcc8eaf 100644 --- a/README.md +++ b/README.md @@ -329,8 +329,9 @@ You don't have to do anything manually! 1. Namecheap API (https://www.namecheap.com/) 1. MyDNS.JP API (https://www.mydns.jp/) 1. hosting.de (https://www.hosting.de) +1. Neodigit.net API (https://www.neodigit.net) -And: +And: **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.)** diff --git a/dnsapi/README.md b/dnsapi/README.md index bb0e89d0..b5f4d2cc 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1054,6 +1054,19 @@ acme.sh --issue --dns dns_hostingde -d example.com -d *.example.com The hosting.de API key and endpoint will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 56. Use Neodigit.net API + +``` +export NEODIGIT_API_TOKEN="eXJxTkdUVUZmcHQ3QWJackQ4ZGlMejRDSklRYmo5VG5zcFFKK2thYnE0WnVnNnMy" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_neodigit -d example.com -d www.example.com +``` + +Neodigit API Token will be saved in `~/.acme.sh/account.conf` and will be used when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_neodigit.sh b/dnsapi/dns_neodigit.sh new file mode 100644 index 00000000..9835613d --- /dev/null +++ b/dnsapi/dns_neodigit.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env sh + +# +# NEODIGIT_API_TOKEN="jasdfhklsjadhflnhsausdfas" + +# This is Neodigit.net api wrapper for acme.sh +# +# Author: Adrian Almenar +# Report Bugs here: https://github.com/tecnocratica/acme.sh +# +NEODIGIT_API_URL="https://api.neodigit.net/v1" +# +######## Public functions ##################### + +# Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_neodigit_add() { + fulldomain=$1 + txtvalue=$2 + + NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" + if [ -z "$NEODIGIT_API_TOKEN" ]; then + NEODIGIT_API_TOKEN="" + _err "You haven't specified a Token api key." + _err "Please create the key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable NEODIGIT_API_TOKEN "$NEODIGIT_API_TOKEN" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + _debug domain "$_domain" + _debug sub_domain "$_sub_domain" + + _debug "Getting txt records" + _neo_rest GET "dns/zones/${_domain_id}/records?type=TXT&name=$fulldomain" + + _debug _code "$_code" + + if [ "$_code" != "200" ]; then + _err "error retrieving data!" + return 1 + fi + + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + _debug domain "$_domain" + _debug sub_domain "$_sub_domain" + + _info "Adding record" + if _neo_rest POST "dns/zones/$_domain_id/records" "{\"record\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":60}}"; then + if printf -- "%s" "$response" | grep "$_sub_domain" >/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 txtvalue +dns_neodigit_rm() { + fulldomain=$1 + txtvalue=$2 + + NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" + if [ -z "$NEODIGIT_API_TOKEN" ]; then + NEODIGIT_API_TOKEN="" + _err "You haven't specified a Token api key." + _err "Please create the key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable NEODIGIT_API_TOKEN "$NEODIGIT_API_TOKEN" + + _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" + _neo_rest GET "dns/zones/${_domain_id}/records?type=TXT&name=$fulldomain&content=$txtvalue" + + if [ "$_code" != "200" ]; then + _err "error retrieving data!" + return 1 + fi + + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1) + _debug "record_id" "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if ! _neo_rest DELETE "dns/zones/$_domain_id/records/$record_id"; then + _err "Delete record error." + return 1 + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=dasfdsafsadg5ythd +_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 ! _neo_rest GET "dns/zones?name=$h"; then + return 1 + fi + + _debug p "$p" + + if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1) + 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 +} + +_neo_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + export _H1="X-TCPanel-Token: $NEODIGIT_API_TOKEN" + export _H2="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$NEODIGIT_API_URL/$ep" "" "$m")" + else + response="$(_get "$NEODIGIT_API_URL/$ep")" + fi + + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From f4ad42bb842e410bd86500cf5bb936d8c3f1e989 Mon Sep 17 00:00:00 2001 From: Adrian Almenar Date: Tue, 4 Dec 2018 14:33:00 +0100 Subject: [PATCH 140/403] Changes requested on commit review --- dnsapi/dns_neodigit.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_neodigit.sh b/dnsapi/dns_neodigit.sh index 9835613d..d87845ad 100644 --- a/dnsapi/dns_neodigit.sh +++ b/dnsapi/dns_neodigit.sh @@ -17,7 +17,7 @@ dns_neodigit_add() { fulldomain=$1 txtvalue=$2 - NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" + NEODIGIT_API_TOKEN="$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" if [ -z "$NEODIGIT_API_TOKEN" ]; then NEODIGIT_API_TOKEN="" _err "You haven't specified a Token api key." @@ -73,7 +73,7 @@ dns_neodigit_rm() { fulldomain=$1 txtvalue=$2 - NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" + NEODIGIT_API_TOKEN="$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" if [ -z "$NEODIGIT_API_TOKEN" ]; then NEODIGIT_API_TOKEN="" _err "You haven't specified a Token api key." @@ -102,7 +102,7 @@ dns_neodigit_rm() { return 1 fi - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1) + record_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1) _debug "record_id" "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id to remove." @@ -140,7 +140,7 @@ _get_root() { _debug p "$p" if _contains "$response" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1) + _domain_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1) if [ "$_domain_id" ]; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain=$h From 842f030355ee9013b6ba6861d347ef15a4e546e3 Mon Sep 17 00:00:00 2001 From: Adrian Almenar Date: Tue, 4 Dec 2018 14:36:28 +0100 Subject: [PATCH 141/403] Revert change --- dnsapi/dns_neodigit.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_neodigit.sh b/dnsapi/dns_neodigit.sh index d87845ad..64ea8786 100644 --- a/dnsapi/dns_neodigit.sh +++ b/dnsapi/dns_neodigit.sh @@ -17,7 +17,7 @@ dns_neodigit_add() { fulldomain=$1 txtvalue=$2 - NEODIGIT_API_TOKEN="$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" + NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" if [ -z "$NEODIGIT_API_TOKEN" ]; then NEODIGIT_API_TOKEN="" _err "You haven't specified a Token api key." @@ -73,7 +73,7 @@ dns_neodigit_rm() { fulldomain=$1 txtvalue=$2 - NEODIGIT_API_TOKEN="$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" + NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}" if [ -z "$NEODIGIT_API_TOKEN" ]; then NEODIGIT_API_TOKEN="" _err "You haven't specified a Token api key." From 7ff525468f0fda2c29e3aea51e8cc425a1204acc Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Wed, 5 Dec 2018 03:01:50 +0800 Subject: [PATCH 142/403] Arch Linux package acme.sh is now in [community] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4ea5c6e..d8069405 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) - [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) - [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) -- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) +- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh) - [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/) From 9841063df9dd40120d32ed8185defaee9ef8a8e6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 6 Dec 2018 22:05:26 +0800 Subject: [PATCH 143/403] fix nginx mode --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 392079fb..ff241e6e 100755 --- a/acme.sh +++ b/acme.sh @@ -2839,7 +2839,7 @@ _isRealNginxConf() { _skip_ssl=1 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 + if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl")" ]; then _debug2 "$_listen_i is ssl" else _debug2 "$_listen_i is plain text" From c84466b1319f5648b420b5670cf15d686df4646a Mon Sep 17 00:00:00 2001 From: Sergey Pashinin Date: Mon, 10 Dec 2018 16:55:21 +0300 Subject: [PATCH 144/403] Write certs in Vault for Fabio load balancer --- deploy/vault_cli.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deploy/vault_cli.sh b/deploy/vault_cli.sh index 79c25aa2..b93fdd51 100644 --- a/deploy/vault_cli.sh +++ b/deploy/vault_cli.sh @@ -49,9 +49,13 @@ vault_cli_deploy() { 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}/chain.pem" value=@"$_cca" || return 1 - $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1 + if [ -n "$FABIO" ]; then + $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1 + else + $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}/chain.pem" value=@"$_cca" || return 1 + $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1 + fi } From 9f067d7f56eba9c1b301686d2a89419d9e993ea1 Mon Sep 17 00:00:00 2001 From: Sergey Pashinin Date: Mon, 10 Dec 2018 18:17:18 +0300 Subject: [PATCH 145/403] Deploy to Hashicorp Vault docs --- deploy/README.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index 5c03ce6a..cec7d773 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -295,4 +295,40 @@ You can then deploy the certificate as follows ```sh acme.sh --deploy -d www.mydomain.com --deploy-hook gitlab -``` \ No newline at end of file +``` + +## 12. Deploy your cert to Hashicorp Vault + +```sh +export VAULT_PREFIX="acme" +``` + +You can then deploy the certificate as follows + +```sh +acme.sh --deploy -d www.mydomain.com --deploy-hook vault_cli +``` + +Your certs will be saved in Vault using this structure: + +```sh +vault write "${VAULT_PREFIX}/${domain}/cert.pem" value=@"..." +vault write "${VAULT_PREFIX}/${domain}/cert.key" value=@"..." +vault write "${VAULT_PREFIX}/${domain}/chain.pem" value=@"..." +vault write "${VAULT_PREFIX}/${domain}/fullchain.pem" value=@"..." +``` + +You might be using Fabio load balancer (which can get certs from +Vault). It needs a bit different structure of your certs in Vault. It +gets certs only from keys that were saved in `prefix/domain`, like this: + +```bash +vault write /www.domain.com cert=@cert.pem key=@key.pem +``` + +If you want to save certs in Vault this way just set "FABIO" env +variable to anything (ex: "1") before running `acme.sh`: + +```sh +export FABIO="1" +``` From a43545c6ea9f79df2acfa1bc10db8f44e803b197 Mon Sep 17 00:00:00 2001 From: neil Date: Tue, 11 Dec 2018 19:11:56 +0800 Subject: [PATCH 146/403] fix https://github.com/Neilpang/acme.sh/issues/1959 --- dnsapi/dns_cf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 944956ad..f50ab498 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -181,7 +181,7 @@ _get_root() { fi if _contains "$_cf_zones" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(printf "%s\n" "$_cf_zones" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _domain_id=$(echo "$_cf_zones" | tr '{' "\n" | grep "\"name\":\"$h\"" | _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 From 5431d051685bc6c0d7cd1341dc5111892715ee19 Mon Sep 17 00:00:00 2001 From: "Daniel F. Dickinson" Date: Thu, 13 Dec 2018 01:22:44 -0500 Subject: [PATCH 147/403] dnsapi nsupdate: Add nsupdate debug option When debug is enabled, also use nsupdate's debug logging so that the user can see potential issues with the nsupdate transaction. Signed-off-by: Daniel F. Dickinson --- dnsapi/dns_nsupdate.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_nsupdate.sh b/dnsapi/dns_nsupdate.sh index 555f4d29..609785e0 100755 --- a/dnsapi/dns_nsupdate.sh +++ b/dnsapi/dns_nsupdate.sh @@ -14,7 +14,9 @@ dns_nsupdate_add() { _saveaccountconf NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}" _saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}" _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\"" - nsupdate -k "${NSUPDATE_KEY}" < Date: Thu, 13 Dec 2018 01:23:53 -0500 Subject: [PATCH 148/403] dnsapi: Add option to set zone for nsupdate Some DNS servers for which dns_nsupdate.sh is applicable (such as dyn.com's 'Standard DNS' TSIG update mechanism), require that the zone be set during the nsupdate transaction. Therefore we add a new environment variable NSUPDATE_ZONE which is used to set the zone for the DNS TSIG transaction. Signed-off-by: Daniel F. Dickinson --- dnsapi/README.md | 6 +++++- dnsapi/dns_nsupdate.sh | 23 +++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index afe1f7f4..30c1be53 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -146,13 +146,17 @@ Finally, make the DNS server and update Key available to `acme.sh` export NSUPDATE_SERVER="dns.example.com" export NSUPDATE_KEY="/path/to/your/nsupdate.key" ``` +and optionally (depending on DNS server) +``` +export NSUPDATE_ZONE="example.com" +``` Ok, let's issue a cert now: ``` acme.sh --issue --dns dns_nsupdate -d example.com -d www.example.com ``` -The `NSUPDATE_SERVER` and `NSUPDATE_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The `NSUPDATE_SERVER`, `NSUPDATE_KEY`, and `NSUPDATE_ZONE` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. ## 8. Use LuaDNS domain API diff --git a/dnsapi/dns_nsupdate.sh b/dnsapi/dns_nsupdate.sh index 609785e0..8b479f98 100755 --- a/dnsapi/dns_nsupdate.sh +++ b/dnsapi/dns_nsupdate.sh @@ -13,14 +13,24 @@ dns_nsupdate_add() { _saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}" _saveaccountconf NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}" _saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}" + _saveaccountconf NSUPDATE_ZONE "${NSUPDATE_ZONE}" _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\"" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" - nsupdate -k "${NSUPDATE_KEY}" $nsdebug < Date: Sun, 16 Dec 2018 21:10:22 +0800 Subject: [PATCH 149/403] add more debug info https://github.com/Neilpang/acme.sh/issues/1932 --- acme.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index ff241e6e..5cfcb382 100755 --- a/acme.sh +++ b/acme.sh @@ -1516,7 +1516,8 @@ _calcjwk() { JWK_HEADERPLACE_PART1='{"nonce": "' JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"' else - _err "Only RSA or EC key is supported." + _err "Only RSA or EC key is supported. keyfile=$keyfile" + _debug2 "$(cat "$keyfile")" return 1 fi From 2b9ebd666280cc7832bce31d0b282df7f4d276d7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 17 Dec 2018 23:02:02 +0800 Subject: [PATCH 150/403] fix showcsr https://github.com/Neilpang/acme.sh/issues/1968 --- acme.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 5cfcb382..6264da01 100755 --- a/acme.sh +++ b/acme.sh @@ -1134,12 +1134,17 @@ _readSubjectAltNamesFromCSR() { if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then _debug "AltNames contains subject" - _dnsAltnames="$(printf "%s" "$_dnsAltnames," | sed "s/DNS:$_csrsubj,//g")" + _excapedAlgnames="$(echo "$_dnsAltnames" | tr '*' '#')" + _debug _excapedAlgnames "$_excapedAlgnames" + _escapedSubject="$(echo "$_csrsubj" | tr '*' '#')" + _debug _escapedSubject "$_escapedSubject" + _dnsAltnames="$(echo "$_excapedAlgnames," | sed "s/DNS:$_escapedSubject,//g" | tr '#' '*' | sed "s/,\$//g")" + _debug _dnsAltnames "$_dnsAltnames" else _debug "AltNames doesn't contain subject" fi - printf "%s" "$_dnsAltnames" | sed "s/DNS://g" + echo "$_dnsAltnames" | sed "s/DNS://g" } #_csrfile From 08681f4a8b9ea066b00e5f890b47bfbfe3fb5b3e Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 18 Dec 2018 19:28:38 +0800 Subject: [PATCH 151/403] support tls-alpn-01 https://github.com/Neilpang/acme.sh/issues/1675#issuecomment-447857756 --- acme.sh | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 6264da01..efd64467 100755 --- a/acme.sh +++ b/acme.sh @@ -37,6 +37,7 @@ VTYPE_HTTP="http-01" VTYPE_DNS="dns-01" VTYPE_TLS="tls-sni-01" VTYPE_TLS2="tls-sni-02" +VTYPE_ALPN="tls-alpn-01" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -48,6 +49,7 @@ NO_VALUE="no" W_TLS="tls" W_DNS="dns" +W_ALPN="alpn" DNS_ALIAS_PREFIX="=" MODE_STATELESS="stateless" @@ -1046,7 +1048,7 @@ _idn() { fi } -#_createcsr cn san_list keyfile csrfile conf +#_createcsr cn san_list keyfile csrfile conf acmeValidationv1 _createcsr() { _debug _createcsr domain="$1" @@ -1054,6 +1056,7 @@ _createcsr() { csrkey="$3" csr="$4" csrconf="$5" + acmeValidationv1="$6" _debug2 domain "$domain" _debug2 domainlist "$domainlist" _debug2 csrkey "$csrkey" @@ -1062,7 +1065,9 @@ _createcsr() { printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf" - if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then + if [ "$acmeValidationv1" ]; then + printf -- "\nsubjectAltName=DNS:$domainlist" >>"$csrconf" + elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf" @@ -1084,6 +1089,10 @@ _createcsr() { printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf" fi + if [ "$acmeValidationv1" ]; then + printf "\n1.3.6.1.5.5.7.1.30.1=critical,DER:04:20:${acmeValidationv1}" >> "${csrconf}" + fi + _csr_cn="$(_idn "$domain")" _debug2 _csr_cn "$_csr_cn" if _contains "$(uname -a)" "MINGW"; then @@ -2107,7 +2116,7 @@ _sleep() { fi } -# _starttlsserver san_a san_b port content _ncaddr +# _starttlsserver san_a san_b port content _ncaddr acmeValidationv1 _starttlsserver() { _info "Starting tls server." san_a="$1" @@ -2115,10 +2124,12 @@ _starttlsserver() { port="$3" content="$4" opaddr="$5" + acmeValidationv1="$6" _debug san_a "$san_a" _debug san_b "$san_b" _debug port "$port" + _debug acmeValidationv1 "$acmeValidationv1" #create key TLS_KEY if ! _createkey "2048" "$TLS_KEY"; then @@ -2131,7 +2142,7 @@ _starttlsserver() { if [ "$san_b" ]; then alt="$alt,$san_b" fi - if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF"; then + if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$acmeValidationv1"; then _err "Create tls validation csr error." return 1 fi @@ -2157,6 +2168,10 @@ _starttlsserver() { __S_OPENSSL="$__S_OPENSSL -6" fi + if [ "$acmeValidationv1" ]; then + __S_OPENSSL="$__S_OPENSSL -alpn acme-tls/1" + fi + _debug "$__S_OPENSSL" if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then $__S_OPENSSL -tlsextdebug & @@ -3067,8 +3082,8 @@ _on_before_issue() { _savedomainconf "Le_HTTPPort" "$Le_HTTPPort" fi _checkport="$Le_HTTPPort" - elif [ "$_currentRoot" = "$W_TLS" ]; then - _info "Standalone tls mode." + elif [ "$_currentRoot" = "$W_TLS" ] || [ "$_currentRoot" = "$W_ALPN" ]; then + _info "Standalone tls/alpn mode." if [ -z "$Le_TLSPort" ]; then Le_TLSPort=443 else @@ -3694,6 +3709,10 @@ $_authorizations_map" fi fi + if [ "$_currentRoot" = "$W_ALPN" ]; then + vtype="$VTYPE_ALPN" + fi + if [ "$ACME_VERSION" = "2" ]; then response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")" _debug2 "response" "$response" @@ -4007,6 +4026,16 @@ $_authorizations_map" _on_issue_err "$_post_hook" "$vlist" return 1 fi + elif [ "$vtype" = "$VTYPE_ALPN" ]; then + acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")" + _debug acmevalidationv1 "$acmevalidationv1" + if ! _starttlsserver "$d" "" "$Le_TLSPort" "$keyauthorization" "$_ncaddr" "$acmevalidationv1"; then + _err "Start tls server error." + _clearupwebbroot "$_currentRoot" "$removelevel" "$token" + _clearup + _on_issue_err "$_post_hook" "$vlist" + return 1 + fi fi if ! __trigger_validation "$uri" "$keyauthorization"; then @@ -5469,6 +5498,7 @@ Parameters: --output-insecure Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for secure. --webroot, -w /path/to/webroot Specifies the web root folder for web root mode. --standalone Use standalone mode. + --alpn Use standalone alpn mode. --stateless Use stateless mode, see: $_STATELESS_WIKI --apache Use apache mode. --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api. @@ -5823,6 +5853,14 @@ _process() { _webroot="$_webroot,$wvalue" fi ;; + --alpn) + wvalue="$W_ALPN" + if [ -z "$_webroot" ]; then + _webroot="$wvalue" + else + _webroot="$_webroot,$wvalue" + fi + ;; --stateless) wvalue="$MODE_STATELESS" if [ -z "$_webroot" ]; then From 79a0a66f1f2f547464bec4ee7ab876a664a2ed78 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 18 Dec 2018 20:18:18 +0800 Subject: [PATCH 152/403] support --tlsport --- acme.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/acme.sh b/acme.sh index efd64467..5ce97f88 100755 --- a/acme.sh +++ b/acme.sh @@ -5529,6 +5529,7 @@ 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. @@ -5985,6 +5986,11 @@ _process() { Le_HTTPPort="$_httpport" shift ;; + --tlsport) + _tlsport="$2" + Le_TLSPort="$_tlsport" + shift + ;; --listraw) _listraw="raw" ;; From f99ca918db8e4587ec1437c6815a32a8de49c42f Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 18 Dec 2018 20:33:33 +0800 Subject: [PATCH 153/403] fix format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 5ce97f88..acda4bee 100755 --- a/acme.sh +++ b/acme.sh @@ -1090,7 +1090,7 @@ _createcsr() { fi if [ "$acmeValidationv1" ]; then - printf "\n1.3.6.1.5.5.7.1.30.1=critical,DER:04:20:${acmeValidationv1}" >> "${csrconf}" + printf "\n1.3.6.1.5.5.7.1.30.1=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}" fi _csr_cn="$(_idn "$domain")" From 67d3e8d04968e8beaaa463639cd5989edf5fd9b7 Mon Sep 17 00:00:00 2001 From: Ivru Date: Thu, 20 Dec 2018 16:07:05 +0100 Subject: [PATCH 154/403] Add Exoscape API support for DNSAPI --- README.md | 1 + dnsapi/README.md | 18 +++++ dnsapi/dns_exoscale.sh | 170 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100755 dnsapi/dns_exoscale.sh diff --git a/README.md b/README.md index 033711f3..6a1cf3a4 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,7 @@ You don't have to do anything manually! 1. MyDNS.JP API (https://www.mydns.jp/) 1. hosting.de (https://www.hosting.de) 1. Neodigit.net API (https://www.neodigit.net) +1. Exoscale.com API (https://www.exoscale.com/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index afe1f7f4..89cf3975 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1076,6 +1076,24 @@ acme.sh --issue --dns dns_neodigit -d example.com -d www.example.com Neodigit API Token will be saved in `~/.acme.sh/account.conf` and will be used when needed. +## 57. Use Exoscale API + +Create an API key and secret key in the Exoscale account section + +Set your API and secret key: + +``` +export EXOSCALE_API_KEY='xxx' +export EXOSCALE_SECRET_KEY='xxx' +``` + +Now, let's issue a cert: +``` +acme.sh --issue --dns dns_netcup -d example.com -d www.example.com +``` + +The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_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_exoscale.sh b/dnsapi/dns_exoscale.sh new file mode 100755 index 00000000..f9ffff24 --- /dev/null +++ b/dnsapi/dns_exoscale.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env sh + +EXOSCALE_API=https://api.exoscale.com/dns/v1 + + +######## Public functions ##################### + +# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Used to add txt record +dns_exoscale_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _checkAuth; 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" + + _info "Adding record" + if _exoscale_rest POST "domains/$_domain_id/records" "{\"record\":{\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}}" "$_domain_token"; then + if _contains "$response" "$txtvalue"; then + _info "Added, OK" + return 0 + fi + fi + _err "Add txt record error." + return 1 + +} + +# Usage: fulldomain txtvalue +# Used to remove the txt record after validation +dns_exoscale_rm() { + fulldomain=$1 + txtvalue=$2 + + if ! _checkAuth; 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" + _exoscale_rest GET "domains/${_domain_id}/records?type=TXT&name=$_sub_domain" "" "$_domain_token" + if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then + _record_id=$(echo "$response" | tr '{' "\n" | grep "\"content\":\"$txtvalue\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \") + fi + + if [ -z "$_record_id" ] ; then + _err "Can not get record id to remove." + return 1 + fi + + _debug "Deleting record $_record_id" + + if ! _exoscale_rest DELETE "domains/$_domain_id/records/$_record_id" "" "$_domain_token"; then + _err "Delete record error." + return 1 + fi + + return 0 +} + + +#################### Private functions below ################################## + +_checkAuth() { + EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}" + EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}" + + if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then + EXOSCALE_API_KEY="" + EXOSCALE_SECRET_KEY="" + _err "You don't specify Exoscale application key and application secret yet." + _err "Please create you key and try again." + return 1 + fi + + _saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY" + _saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY" + + return 0 +} + +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +# _domain_token=sdjkglgdfewsdfg +_get_root() { + + if ! _exoscale_rest GET "domains"; then + return 1 + fi + + 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 _contains "$response" "\"name\":\"$h\"" >/dev/null; then + _domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \") + _domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") + if [ "$_domain_token" ] && [ "$_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 +} + +# returns response +_exoscale_rest() { + method=$1 + path="$2" + data="$3" + token="$4" + request_url="$EXOSCALE_API/$path" + _debug "$path" + + export _H1="Accept: application/json" + + if [ "$token" ]; then + export _H2="X-DNS-Domain-Token: $token" + else + export _H2="X-DNS-Token: $EXOSCALE_API_KEY:$EXOSCALE_SECRET_KEY" + fi + + if [ "$data" ] || [ "$method" = "DELETE" ]; then + _H3="Content-Type: application/json" + _debug data "$data" + response="$(_post "$data" "$request_url" "" "$method")" + else + response="$(_get "$request_url" "" "" "$method")" + fi + + if [ "$?" != "0" ]; then + _err "error $request_url" + return 1 + fi + _debug2 response "$response" + return 0 +} From eea9aaf9404d6f62c722e96c2d064bcab21347b4 Mon Sep 17 00:00:00 2001 From: Ivru Date: Thu, 20 Dec 2018 16:24:08 +0100 Subject: [PATCH 155/403] Fix typos --- dnsapi/dns_exoscale.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_exoscale.sh b/dnsapi/dns_exoscale.sh index f9ffff24..a837b79d 100755 --- a/dnsapi/dns_exoscale.sh +++ b/dnsapi/dns_exoscale.sh @@ -61,7 +61,7 @@ dns_exoscale_rm() { _record_id=$(echo "$response" | tr '{' "\n" | grep "\"content\":\"$txtvalue\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \") fi - if [ -z "$_record_id" ] ; then + if [ -z "$_record_id" ]; then _err "Can not get record id to remove." return 1 fi @@ -123,7 +123,7 @@ _get_root() { if _contains "$response" "\"name\":\"$h\"" >/dev/null; then _domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \") _domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") - if [ "$_domain_token" ] && [ "$_domain_id" ] ; then + if [ "$_domain_token" ] && [ "$_domain_id" ]; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain=$h return 0 From 8e43b86f06d244ed6635054cb768c45e86f16bdd Mon Sep 17 00:00:00 2001 From: Ivru Date: Thu, 20 Dec 2018 16:30:02 +0100 Subject: [PATCH 156/403] Export header _H3 --- dnsapi/dns_exoscale.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_exoscale.sh b/dnsapi/dns_exoscale.sh index a837b79d..bb433ec3 100755 --- a/dnsapi/dns_exoscale.sh +++ b/dnsapi/dns_exoscale.sh @@ -154,7 +154,7 @@ _exoscale_rest() { fi if [ "$data" ] || [ "$method" = "DELETE" ]; then - _H3="Content-Type: application/json" + export _H3="Content-Type: application/json" _debug data "$data" response="$(_post "$data" "$request_url" "" "$method")" else From 405173a0b4176d8858f7d31aaf570a27cafb1a90 Mon Sep 17 00:00:00 2001 From: Ivru Date: Thu, 20 Dec 2018 16:37:11 +0100 Subject: [PATCH 157/403] Remove extraneous blank lines --- dnsapi/dns_exoscale.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/dnsapi/dns_exoscale.sh b/dnsapi/dns_exoscale.sh index bb433ec3..ccf05fc5 100755 --- a/dnsapi/dns_exoscale.sh +++ b/dnsapi/dns_exoscale.sh @@ -2,7 +2,6 @@ EXOSCALE_API=https://api.exoscale.com/dns/v1 - ######## Public functions ##################### # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" @@ -76,7 +75,6 @@ dns_exoscale_rm() { return 0 } - #################### Private functions below ################################## _checkAuth() { From 9a473640fb2341a5c9e56ffcb645200c1725392c Mon Sep 17 00:00:00 2001 From: "Aaron W. Swenson" Date: Thu, 20 Dec 2018 11:00:10 -0500 Subject: [PATCH 158/403] Revert "Update Linode API to v4" This reverts commit 9a27b389765ac6d7a256333e9e1f6fe3c4b92e08. Turns out, the Cloud Manager is not backward compatible, nor is the Classic Manager forward compatible. --- dnsapi/README.md | 13 ++----------- dnsapi/dns_linode.sh | 39 +++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 30c1be53..603bd72a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -268,18 +268,9 @@ when needed. ## 14. Use Linode domain API First you need to login to your Linode account to get your API Key. +[https://manager.linode.com/profile/api](https://manager.linode.com/profile/api) - * [Classic Manager](https://manager.linode.com/profile/api) - - Under "Add an API key", Give the new key a "Label" (we recommend *ACME*), - set the expiry to never, "Create API Key", and copy the new key into the `LINODE_API_KEY` command - below. - - * [Cloud Manager](https://cloud.linode.com/profile/tokens) - - Click on "Add a Personal Access Token". Give the new key a "Label" (we - recommend *ACME*), give it Read/Write access to "Domains". "Submit", and - copy the new key into the `LINODE_API_KEY` command below. +Then add an API key with label *ACME* and copy the new key. ```sh export LINODE_API_KEY="..." diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index d03e6c47..ead5b164 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -2,7 +2,7 @@ #Author: Philipp Grosswiler -LINODE_API_URL="https://api.linode.com/v4/domains" +LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action=" ######## Public functions ##################### @@ -27,14 +27,10 @@ dns_linode_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _payload="{ - \"type\": \"TXT\", - \"name\": \"$_sub_domain\", - \"target\": \"$txtvalue\" - }" + _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue" - if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then - _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) + if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then + _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) _debug _resource_id "$_resource_id" if [ -z "$_resource_id" ]; then @@ -69,21 +65,25 @@ dns_linode_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then + _parameters="&DomainID=$_domain_id" + + if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" - resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")" + resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then - _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) if [ "$_resource_id" ]; then _debug _resource_id "$_resource_id" - if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then - # On 200/OK, empty set is returned. Check for error, if any. - _error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1) + _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id" + + if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then + _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) + _debug _resource_id "$_resource_id" - if [ -n "$_error_response" ]; then - _err "Error deleting the domain resource: $_error_response" + if [ -z "$_resource_id" ]; then + _err "Error deleting the domain resource." return 1 fi @@ -127,7 +127,7 @@ _get_root() { i=2 p=1 - if _rest GET; then + if _rest GET "domain.list"; then response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -137,9 +137,9 @@ _get_root() { return 1 fi - hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")" + hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")" if [ "$hostedzone" ]; then - _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _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 @@ -165,7 +165,6 @@ _rest() { export _H1="Accept: application/json" export _H2="Content-Type: application/json" - export _H3="Authorization: Bearer $LINODE_API_KEY" if [ "$mtd" != "GET" ]; then # both POST and DELETE. From c8c1140f15d6c96d83bd3734fcc1604e0caa408e Mon Sep 17 00:00:00 2001 From: "Aaron W. Swenson" Date: Thu, 20 Dec 2018 11:01:34 -0500 Subject: [PATCH 159/403] Linode API v4 Redo The Cloud and Classic Manager work with different APIs, and so require a separate module, which we introduce here. The README has also been modified to state that the two are separate and incompatible, and provides instructions on using either. --- dnsapi/README.md | 46 +++++++++- dnsapi/dns_linode_v4.sh | 185 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 4 deletions(-) create mode 100755 dnsapi/dns_linode_v4.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 603bd72a..df6db111 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -267,16 +267,26 @@ when needed. ## 14. Use Linode domain API +The tokens created in the classic manager and cloud manager are incompatible +with one another. While the classic manager makes an all or nothing API, the +newer cloud manager interface promises to produce API keys with a finer +permission system. However, either way works just fine. + +### Classic Manager ### + +Classic Manager: https://manager.linode.com/profile/api + First you need to login to your Linode account to get your API Key. -[https://manager.linode.com/profile/api](https://manager.linode.com/profile/api) -Then add an API key with label *ACME* and copy the new key. +Then add an API key with label *ACME* and copy the new key into the following +command. ```sh export LINODE_API_KEY="..." ``` -Due to the reload time of any changes in the DNS records, we have to use the `dnssleep` option to wait at least 15 minutes for the changes to take effect. +Due to the reload time of any changes in the DNS records, we have to use the +`dnssleep` option to wait at least 15 minutes for the changes to take effect. Ok, let's issue a cert now: @@ -284,7 +294,35 @@ Ok, let's issue a cert now: acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com ``` -The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be +reused when needed. + +### Cloud Manager ### + +Cloud Manager: https://cloud.linode.com/profile/tokens + +First you need to login to your Linode account to get your API Key. + + 1. Click on "Add a Personal Access Token". + 2. Give the new key a "Label" (we recommend *ACME*) + 3. Give it Read/Write access to "Domains" + 4. "Submit" and copy the new key into the `LINODE_V4_API_KEY` command below. + +```sh +export LINODE_V4_API_KEY="..." +``` + +Due to the reload time of any changes in the DNS records, we have to use the +`dnssleep` option to wait at least 15 minutes for the changes to take effect. + +Ok, let's issue a cert now: + +```sh +acme.sh --issue --dns dns_linode_v4 --dnssleep 900 -d example.com -d www.example.com +``` + +The `LINODE_V4_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be +reused when needed. ## 15. Use FreeDNS diff --git a/dnsapi/dns_linode_v4.sh b/dnsapi/dns_linode_v4.sh new file mode 100755 index 00000000..dfa1a651 --- /dev/null +++ b/dnsapi/dns_linode_v4.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +#Original Author: Philipp Grosswiler +#v4 Update Author: Aaron W. Swenson + +LINODE_V4_API_URL="https://api.linode.com/v4/domains" + +######## Public functions ##################### + +#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_linode_add() { + fulldomain="${1}" + txtvalue="${2}" + + if ! _Linode_API; then + return 1 + fi + + _info "Using Linode" + _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "Domain does not exist." + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _payload="{ + \"type\": \"TXT\", + \"name\": \"$_sub_domain\", + \"target\": \"$txtvalue\" + }" + + if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then + _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) + _debug _resource_id "$_resource_id" + + if [ -z "$_resource_id" ]; then + _err "Error adding the domain resource." + return 1 + fi + + _info "Domain resource successfully added." + return 0 + fi + + return 1 +} + +#Usage: dns_linode_rm _acme-challenge.www.domain.com +dns_linode_rm() { + fulldomain="${1}" + + if ! _Linode_API; then + return 1 + fi + + _info "Using Linode" + _debug "Calling: dns_linode_rm() '${fulldomain}'" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "Domain does not exist." + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then + response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" + + resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")" + if [ "$resource" ]; then + _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + if [ "$_resource_id" ]; then + _debug _resource_id "$_resource_id" + + if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then + # On 200/OK, empty set is returned. Check for error, if any. + _error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1) + + if [ -n "$_error_response" ]; then + _err "Error deleting the domain resource: $_error_response" + return 1 + fi + + _info "Domain resource successfully deleted." + return 0 + fi + fi + + return 1 + fi + + return 0 + fi + + return 1 +} + +#################### Private functions below ################################## + +_Linode_API() { + if [ -z "$LINODE_V4_API_KEY" ]; then + LINODE_V4_API_KEY="" + + _err "You didn't specify the Linode v4 API key yet." + _err "Please create your key and try again." + + return 1 + fi + + _saveaccountconf LINODE_V4_API_KEY "$LINODE_V4_API_KEY" +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=12345 +_get_root() { + domain=$1 + i=2 + p=1 + + if _rest GET; then + response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")" + if [ "$hostedzone" ]; then + _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _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 + fi + return 1 +} + +#method method action data +_rest() { + mtd="$1" + ep="$2" + data="$3" + + _debug mtd "$mtd" + _debug ep "$ep" + + export _H1="Accept: application/json" + export _H2="Content-Type: application/json" + export _H3="Authorization: Bearer $LINODE_V4_API_KEY" + + if [ "$mtd" != "GET" ]; then + # both POST and DELETE. + _debug data "$data" + response="$(_post "$data" "$LINODE_V4_API_URL$ep" "" "$mtd")" + else + response="$(_get "$LINODE_V4_API_URL$ep$data")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 0b363a5c98d56df8c45a689ccc55087484f64306 Mon Sep 17 00:00:00 2001 From: Ne-Lexa Date: Mon, 24 Dec 2018 13:33:25 +0300 Subject: [PATCH 160/403] removed the _clearaccountconf() call for erroneous requests --- dnsapi/dns_internetbs.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dnsapi/dns_internetbs.sh b/dnsapi/dns_internetbs.sh index 05a1adae..d25c8324 100755 --- a/dnsapi/dns_internetbs.sh +++ b/dnsapi/dns_internetbs.sh @@ -41,8 +41,6 @@ dns_internetbs_add() { if ! _contains "$response" "\"status\":\"SUCCESS\""; then _err "ERROR add TXT record" _err "$response" - _clearaccountconf INTERNETBS_API_KEY - _clearaccountconf INTERNETBS_API_PASSWORD return 1 fi @@ -75,8 +73,6 @@ dns_internetbs_rm() { if ! _contains "$response" "\"status\":\"SUCCESS\""; then _err "ERROR list dns records" _err "$response" - _clearaccountconf INTERNETBS_API_KEY - _clearaccountconf INTERNETBS_API_PASSWORD return 1 fi @@ -89,8 +85,6 @@ dns_internetbs_rm() { if ! _contains "$response" "\"status\":\"SUCCESS\""; then _err "ERROR remove dns record" _err "$response" - _clearaccountconf INTERNETBS_API_KEY - _clearaccountconf INTERNETBS_API_PASSWORD return 1 fi @@ -118,8 +112,6 @@ _get_root() { if ! _contains "$response" "\"status\":\"SUCCESS\""; then _err "ERROR fetch domain list" _err "$response" - _clearaccountconf INTERNETBS_API_KEY - _clearaccountconf INTERNETBS_API_PASSWORD return 1 fi From b7b94e38ac7c3183d3c6d0bd8709dd9d8fdd589d Mon Sep 17 00:00:00 2001 From: Ne-Lexa Date: Mon, 24 Dec 2018 14:59:14 +0300 Subject: [PATCH 161/403] support change account conf from env --- dnsapi/dns_internetbs.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_internetbs.sh b/dnsapi/dns_internetbs.sh index d25c8324..ae6b9e1e 100755 --- a/dnsapi/dns_internetbs.sh +++ b/dnsapi/dns_internetbs.sh @@ -7,6 +7,7 @@ #INTERNETBS_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" #INTERNETBS_API_PASSWORD="sdfsdfsdfljlbjkljlkjsdfoiwje" + INTERNETBS_API_URL="https://api.internet.bs" ######## Public functions ##################### @@ -16,6 +17,9 @@ dns_internetbs_add() { fulldomain=$1 txtvalue=$2 + INTERNETBS_API_KEY="${INTERNETBS_API_KEY:-$(_readaccountconf_mutable INTERNETBS_API_KEY)}" + INTERNETBS_API_PASSWORD="${INTERNETBS_API_PASSWORD:-$(_readaccountconf_mutable INTERNETBS_API_PASSWORD)}" + if [ -z "$INTERNETBS_API_KEY" ] || [ -z "$INTERNETBS_API_PASSWORD" ]; then INTERNETBS_API_KEY="" INTERNETBS_API_PASSWORD="" @@ -24,8 +28,8 @@ dns_internetbs_add() { return 1 fi - _saveaccountconf INTERNETBS_API_KEY "$INTERNETBS_API_KEY" - _saveaccountconf INTERNETBS_API_PASSWORD "$INTERNETBS_API_PASSWORD" + _saveaccountconf_mutable INTERNETBS_API_KEY "$INTERNETBS_API_KEY" + _saveaccountconf_mutable INTERNETBS_API_PASSWORD "$INTERNETBS_API_PASSWORD" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -57,6 +61,17 @@ dns_internetbs_rm() { fulldomain=$1 txtvalue=$2 + INTERNETBS_API_KEY="${INTERNETBS_API_KEY:-$(_readaccountconf_mutable INTERNETBS_API_KEY)}" + INTERNETBS_API_PASSWORD="${INTERNETBS_API_PASSWORD:-$(_readaccountconf_mutable INTERNETBS_API_PASSWORD)}" + + if [ -z "$INTERNETBS_API_KEY" ] || [ -z "$INTERNETBS_API_PASSWORD" ]; then + INTERNETBS_API_KEY="" + INTERNETBS_API_PASSWORD="" + _err "You didn't specify the INTERNET.BS api key and password 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" From 2671af13cdfb592f56c5de1ffdbe438f79db72d8 Mon Sep 17 00:00:00 2001 From: Ketil Date: Thu, 27 Dec 2018 15:17:19 +0100 Subject: [PATCH 162/403] Bugfix for allowing '+' character in CloudFlare email --- dnsapi/dns_cf.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index f50ab498..021094d8 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -34,7 +34,7 @@ dns_cf_add() { _saveaccountconf_mutable CF_Key "$CF_Key" _saveaccountconf_mutable CF_Email "$CF_Email" - _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '@.' '__')" + _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '+@.' '___')" _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" _debug "First detect the root zone" @@ -105,7 +105,7 @@ dns_cf_rm() { return 1 fi - _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '@.' '__')" + _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '+@.' '___')" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then From 9756adb9336daf7fdfd63ce83dde75aa269708d8 Mon Sep 17 00:00:00 2001 From: Ketil Date: Thu, 27 Dec 2018 15:45:19 +0100 Subject: [PATCH 163/403] Fixed spelling of 'tigger' to 'Trigger'. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index acda4bee..c80d8aa3 100755 --- a/acme.sh +++ b/acme.sh @@ -3443,7 +3443,7 @@ __get_domain_new_authz() { #uri keyAuthorization __trigger_validation() { - _debug2 "tigger domain validation." + _debug2 "Trigger domain validation." _t_url="$1" _debug2 _t_url "$_t_url" _t_key_authz="$2" From 920cab6f1206abde5e8ced352bdd3908a2950534 Mon Sep 17 00:00:00 2001 From: Ketil Date: Thu, 27 Dec 2018 16:06:41 +0100 Subject: [PATCH 164/403] Added fixes to also use BuyPass Go ACME server --- acme.sh | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/acme.sh b/acme.sh index acda4bee..aa2adc6c 100755 --- a/acme.sh +++ b/acme.sh @@ -13,6 +13,8 @@ _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" +BUYPASS_CA="https://api.buypass.no/acme/directory" + LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" @@ -1820,9 +1822,12 @@ _send_signed_request() { _debug3 _request_retry_times "$_request_retry_times" if [ -z "$_CACHED_NONCE" ]; then _headers="" - if [ "$ACME_NEW_NONCE" ]; then - _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE" + if [ "$ACME_NEW_NONCE" ] || [ "$BUYPASS" ]; then nonceurl="$ACME_NEW_NONCE" + if [ "$BUYPASS" ]; then + nonceurl=$url + fi + _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$nonceurl" if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then _headers="$(cat "$HTTP_HEADER")" fi @@ -1875,7 +1880,7 @@ _send_signed_request() { sig="$(printf "%s" "$_sig_t" | _url_replace)" _debug3 sig "$sig" - if [ "$ACME_VERSION" = "2" ]; then + if [ "$ACME_VERSION" = "2" ] || [ "$BUYPASS" ]; then body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" else body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" @@ -2330,6 +2335,13 @@ _initAPI() { fi export ACME_AGREEMENT + BUYPASS=$(echo "$_api_server" | _egrep_o 'buypass') + if [ "$BUYPASS" ]; then + BUYPASS=1 + fi + export BUYPASS + _debug "BUYPASS" "$BUYPASS" + _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ" _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER" @@ -3448,10 +3460,16 @@ __trigger_validation() { _debug2 _t_url "$_t_url" _t_key_authz="$2" _debug2 _t_key_authz "$_t_key_authz" + _t_vtype="$3" + _debug2 _t_vtype "$_t_vtype" 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\"}" + if [ "$BUYPASS" ]; then + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}" + else + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + fi fi } @@ -4038,7 +4056,7 @@ $_authorizations_map" fi fi - if ! __trigger_validation "$uri" "$keyauthorization"; then + if ! __trigger_validation "$uri" "$keyauthorization" "$vtype"; then _err "$d:Can not get challenge: $response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup @@ -4047,14 +4065,18 @@ $_authorizations_map" fi if [ "$code" ] && [ "$code" != '202' ]; then - if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then + if [ "$BUYPASS" ] && [ "$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 + 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 fi From 8bd12ed040c65fcc5b4919c1f81835c579da6314 Mon Sep 17 00:00:00 2001 From: Ketil Date: Fri, 28 Dec 2018 09:22:31 +0100 Subject: [PATCH 165/403] Rewrite to remove BuyPass spesific fixes and adapt ACME v1 --- acme.sh | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/acme.sh b/acme.sh index aa2adc6c..0ec08511 100755 --- a/acme.sh +++ b/acme.sh @@ -1822,12 +1822,9 @@ _send_signed_request() { _debug3 _request_retry_times "$_request_retry_times" if [ -z "$_CACHED_NONCE" ]; then _headers="" - if [ "$ACME_NEW_NONCE" ] || [ "$BUYPASS" ]; then + if [ "$ACME_NEW_NONCE" ]; then + _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE" - if [ "$BUYPASS" ]; then - nonceurl=$url - fi - _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$nonceurl" if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then _headers="$(cat "$HTTP_HEADER")" fi @@ -1880,11 +1877,7 @@ _send_signed_request() { sig="$(printf "%s" "$_sig_t" | _url_replace)" _debug3 sig "$sig" - if [ "$ACME_VERSION" = "2" ] || [ "$BUYPASS" ]; then - body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" - else - body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" - fi + body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" _debug3 body "$body" response="$(_post "$body" "$url" "$needbase64" "POST" "$__request_conent_type")" @@ -2335,13 +2328,6 @@ _initAPI() { fi export ACME_AGREEMENT - BUYPASS=$(echo "$_api_server" | _egrep_o 'buypass') - if [ "$BUYPASS" ]; then - BUYPASS=1 - fi - export BUYPASS - _debug "BUYPASS" "$BUYPASS" - _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ" _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER" @@ -3455,7 +3441,7 @@ __get_domain_new_authz() { #uri keyAuthorization __trigger_validation() { - _debug2 "tigger domain validation." + _debug2 "Trigger domain validation." _t_url="$1" _debug2 _t_url "$_t_url" _t_key_authz="$2" @@ -3465,11 +3451,7 @@ __trigger_validation() { if [ "$ACME_VERSION" = "2" ]; then _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" else - if [ "$BUYPASS" ]; then - _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}" - else - _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" - fi + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}" fi } @@ -4065,18 +4047,14 @@ $_authorizations_map" fi if [ "$code" ] && [ "$code" != '202' ]; then - if [ "$BUYPASS" ] && [ "$code" = '200' ]; then + if [ "$code" = '200' ]; then _debug "trigger validation code: $code" else - 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 + _err "$d:Challenge error: $response" + _clearupwebbroot "$_currentRoot" "$removelevel" "$token" + _clearup + _on_issue_err "$_post_hook" "$vlist" + return 1 fi fi From 65a2f789dc415c518d16d5c50e9d0d28919eb5ff Mon Sep 17 00:00:00 2001 From: Ketil Date: Fri, 28 Dec 2018 13:26:20 +0100 Subject: [PATCH 166/403] Removing BUYPASS_CA variable --- acme.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0ec08511..422383b6 100755 --- a/acme.sh +++ b/acme.sh @@ -13,8 +13,6 @@ _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" -BUYPASS_CA="https://api.buypass.no/acme/directory" - LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" From b32071ad049e3235adebd87f64b1899bbc3d21f2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 28 Dec 2018 22:12:54 +0800 Subject: [PATCH 167/403] remove unused code --- acme.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 422383b6..dd718352 100755 --- a/acme.sh +++ b/acme.sh @@ -36,7 +36,6 @@ _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_ALPN="tls-alpn-01" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -3700,11 +3699,7 @@ $_authorizations_map" fi if [ "$_currentRoot" = "$W_TLS" ]; then - if [ "$ACME_VERSION" = "2" ]; then - vtype="$VTYPE_TLS2" - else - vtype="$VTYPE_TLS" - fi + vtype="$VTYPE_TLS" fi if [ "$_currentRoot" = "$W_ALPN" ]; then From 7ba9a5972dd904b1b5834e7ef1a6a3323d1e29aa Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 28 Dec 2018 22:45:40 +0800 Subject: [PATCH 168/403] revert fix for https://github.com/Neilpang/acme.sh/issues/1941 1. fix https://github.com/Neilpang/acme.sh/issues/1977 2. The cache is too long to as a line to save in the conf --- dnsapi/dns_cf.sh | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 021094d8..202385fe 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -34,9 +34,6 @@ dns_cf_add() { _saveaccountconf_mutable CF_Key "$CF_Key" _saveaccountconf_mutable CF_Email "$CF_Email" - _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '+@.' '___')" - _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" - _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -105,16 +102,11 @@ dns_cf_rm() { return 1 fi - _DOMAIN_CF_ZONES_CACHE_NAME_="$(echo "${CF_Email}_CF_ZONES_" | tr '+@.' '___')" - _debug "First detect the root zone" if ! _get_root "$fulldomain"; then - _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" _err "invalid domain" return 1 fi - _cleardomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" - _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" @@ -154,21 +146,6 @@ dns_cf_rm() { # _domain=domain.com # _domain_id=sdjkglgdfewsdfg _get_root() { - - _cf_zones="$(_readdomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_")" - _debug2 "_cf_zones" "$_cf_zones" - if [ -z "$_cf_zones" ]; then - _debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ is none, so get it." - if ! _cf_rest GET "zones"; then - return 1 - fi - _cf_zones="$response" - _savedomainconf "$_DOMAIN_CF_ZONES_CACHE_NAME_" "$(echo "$_cf_zones" | _base64)" - else - _debug "$_DOMAIN_CF_ZONES_CACHE_NAME_ found" - _cf_zones="$(echo "$_cf_zones" | _dbase64)" - fi - domain=$1 i=2 p=1 @@ -180,8 +157,12 @@ _get_root() { return 1 fi - if _contains "$_cf_zones" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(echo "$_cf_zones" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "^\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") + if ! _cf_rest GET "zones?name=$h"; then + return 1 + fi + + if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + _domain_id=$(echo "$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 From ec67a1b2c12c4fa1bdb28feb870853652b7bbe07 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 28 Dec 2018 22:52:40 +0800 Subject: [PATCH 169/403] Do not limit the renew days to 60, it's just a default value. buypass support 180 days. --- acme.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index dd718352..aae16f07 100755 --- a/acme.sh +++ b/acme.sh @@ -40,7 +40,7 @@ VTYPE_ALPN="tls-alpn-01" LOCAL_ANY_ADDRESS="0.0.0.0" -MAX_RENEW=60 +DEFAULT_RENEW=60 DEFAULT_DNS_SLEEP=120 @@ -4267,8 +4267,8 @@ $_authorizations_map" Le_CertCreateTimeStr=$(date -u) _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr" - if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ]; then - Le_RenewalDays="$MAX_RENEW" + if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then + Le_RenewalDays="$DEFAULT_RENEW" else _savedomainconf "Le_RenewalDays" "$Le_RenewalDays" fi @@ -5520,7 +5520,7 @@ Parameters: --useragent Specifies the user agent string. it will be saved for future use too. --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. + --days Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_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. From c4094c68ee44cd9651fb1effe38ce4a1c97b1cee Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 28 Dec 2018 23:04:40 +0800 Subject: [PATCH 170/403] Support BuyPass.com CA --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6a1cf3a4..50d2cfbe 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,10 @@ For all build statuses, check our [weekly build project](https://github.com/Neil https://github.com/Neilpang/acmetest +# Supported CA + +- Letsencrypt.org CA(default) +- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA) # Supported modes From c9baca79109de3d55a6ced143b01e4ef7a07729a Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 28 Dec 2018 23:12:16 +0800 Subject: [PATCH 171/403] add tls-alpn mode --- README.md | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 50d2cfbe..2d31c678 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ https://github.com/Neilpang/acmetest - Webroot mode - Standalone mode +- Standalone tls-alpn mode - Apache mode - Nginx mode - DNS mode @@ -225,8 +226,20 @@ 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 ssl server to issue cert -# 5. Use Apache mode +**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))** + +Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. + +```bash +acme.sh --issue --alpn -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 **(requires you to be root/sudoer, since it is required to interact with Apache server)** @@ -246,7 +259,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 -# 6. Use Nginx mode +# 7. Use Nginx mode **(requires you to be root/sudoer, since it is required to interact with Nginx server)** @@ -270,7 +283,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 -# 7. 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. @@ -348,7 +361,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) -# 8. Use DNS manual mode: +# 9. Use DNS manual mode: See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. @@ -384,7 +397,7 @@ Ok, it's done. **Please use dns api mode instead.** -# 9. Issue ECC certificates +# 10. Issue ECC certificates `Let's Encrypt` can now issue **ECDSA** certificates. @@ -416,7 +429,7 @@ Valid values are: -# 10. Issue Wildcard certificates +# 11. Issue Wildcard certificates It's simple, just give a wildcard domain as the `-d` parameter. @@ -426,7 +439,7 @@ acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf -# 11. How to renew the certs +# 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. @@ -443,7 +456,7 @@ 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 the following to remove the cert from the renewal list: @@ -456,7 +469,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. -# 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. @@ -481,25 +494,25 @@ 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 -# 17. License & Others +# 18. License & Others License is GPLv3 @@ -508,7 +521,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 29a5311ae0cb82799d404efb8dc1b4c3cc7ff14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20R=C3=B6ttsches?= Date: Fri, 28 Dec 2018 00:45:51 +0200 Subject: [PATCH 172/403] [dnsapi] Support adding / removing multiple TXT values for Gandi Gandi supports setting multiple entries by setting multiple array items for the rrset_values field in their API. Modify the dns_gandi_livedns.sh script so that it checks for existing entries, appends new ones if needed, and removes existing ones individually. This enabled wildcard certificate support on Gandi. Fixes the dns_gandi_livedns part of #1261. Tested for creating a multidomain, multiple wild-card certificate on Gandi and using a test script executing only the dns_gandi_livedns_add and dns_gandi_livedns_rm functions. --- dnsapi/dns_gandi_livedns.sh | 62 ++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) mode change 100755 => 100644 dnsapi/dns_gandi_livedns.sh diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh old mode 100755 new mode 100644 index 7a21aba6..cdda4775 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -7,6 +7,7 @@ # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable # #Author: Frédéric Crozat +# Dominik Röttsches #Report Bugs here: https://github.com/fcrozat/acme.sh # ######## Public functions ##################### @@ -36,9 +37,7 @@ dns_gandi_livedns_add() { _debug domain "$_domain" _debug sub_domain "$_sub_domain" - _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \ - && _contains "$response" '{"message": "DNS Record Created"}' \ - && _info "Add $(__green "success")" + _dns_gandi_append_record "$_domain" "$_sub_domain" "$txtvalue" } #Usage: fulldomain txtvalue @@ -56,9 +55,23 @@ dns_gandi_livedns_rm() { _debug fulldomain "$fulldomain" _debug domain "$_domain" _debug sub_domain "$_sub_domain" + _debug txtvalue "$txtvalue" - _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" "" - + if ! _dns_gandi_existing_rrset_values "$_domain" "$_sub_domain"; then + return 1 + fi + _new_rrset_values=$(echo "$_rrset_values" | sed "s/...$txtvalue...//g") + # Cleanup dangling commata. + _new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, ,/ ,/g") + _new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, *\]/\]/g") + _new_rrset_values=$(echo "$_new_rrset_values" | sed "s/\[ *,/\[/g") + _debug "New rrset_values" "$_new_rrset_values" + + _gandi_livedns_rest PUT \ + "domains/$_domain/records/$_sub_domain/TXT" \ + "{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" \ + && _contains "$response" '{"message": "DNS Record Created"}' \ + && _info "Removing record $(__green "success")" } #################### Private functions below ################################## @@ -98,6 +111,45 @@ _get_root() { return 1 } +_dns_gandi_append_record() { + domain=$1 + sub_domain=$2 + txtvalue=$3 + + if _dns_gandi_existing_rrset_values "$domain" "$sub_domain"; then + _debug "Appending new value" + _rrset_values=$(echo "$_rrset_values" | sed "s/\"]/\",\"$txtvalue\"]/") + else + _debug "Creating new record" "$_rrset_values" + _rrset_values="[\"$txtvalue\"]" + fi + _debug new_rrset_values "$_rrset_values" + _gandi_livedns_rest PUT "domains/$_domain/records/$sub_domain/TXT" \ + "{\"rrset_ttl\": 300, \"rrset_values\": $_rrset_values}" \ + && _contains "$response" '{"message": "DNS Record Created"}' \ + && _info "Adding record $(__green "success")" +} + +_dns_gandi_existing_rrset_values() { + domain=$1 + sub_domain=$2 + if ! _gandi_livedns_rest GET "domains/$domain/records/$sub_domain"; then + return 1 + fi + if ! _contains "$response" '"rrset_type": "TXT"'; then + _debug "Does not have a _acme-challenge TXT record yet." + return 1 + fi + if _contains "$response" '"rrset_values": \[\]'; then + _debug "Empty rrset_values for TXT record, no previous TXT record." + return 1 + fi + _debug "Already has TXT record." + _rrset_values=$(echo "$response" | _egrep_o 'rrset_values.*\[.*\]' \ + | _egrep_o '\[".*\"]') + return 0 +} + _gandi_livedns_rest() { m=$1 ep="$2" From 68c5c366f4bc0ab421bd2004e40718b7561c984f Mon Sep 17 00:00:00 2001 From: Ben Edmunds Date: Sun, 30 Dec 2018 03:13:23 +0000 Subject: [PATCH 173/403] dnsapi update dynu for api v2 --- dnsapi/dns_dynu.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh index 17a1cdb0..506ef53e 100644 --- a/dnsapi/dns_dynu.sh +++ b/dnsapi/dns_dynu.sh @@ -10,7 +10,7 @@ Dynu_Token="" # #Endpoint -Dynu_EndPoint="https://api.dynu.com/v1" +Dynu_EndPoint="https://api.dynu.com/v2" # #Author: Dynu Systems, Inc. #Report Bugs here: https://github.com/shar0119/acme.sh @@ -51,11 +51,11 @@ dns_dynu_add() { _debug _domain_name "$_domain_name" _info "Creating TXT record." - if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then + if ! _dynu_rest POST "dns/$dnsId/record" "{\"domainId\":\"$dnsId\",\"nodeName\":\"$_node\",\"recordType\":\"TXT\",\"textData\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then return 1 fi - if ! _contains "$response" "text_data"; then + if ! _contains "$response" "200"; then _err "Could not add TXT record." return 1 fi @@ -132,11 +132,12 @@ _get_root() { return 1 fi - if ! _dynu_rest GET "dns/get/$h"; then + if ! _dynu_rest GET "dns/getroot/$h"; then return 1 fi - if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then + dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2) _domain_name=$h _node=$(printf "%s" "$domain" | cut -d . -f 1-$p) return 0 @@ -152,7 +153,7 @@ _get_recordid() { fulldomain=$1 txtvalue=$2 - if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then + if ! _dynu_rest GET "dns/$dnsId/record"; then return 1 fi @@ -161,19 +162,18 @@ _get_recordid() { return 0 fi - _dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | _egrep_o ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) - + _dns_record_id=$(printf "%s" "$response" | sed -e 's/[^{]*\({[^}]*}\)[^{]*/\1\n/g' | grep "\"textData\":\"$txtvalue\"" | sed -e 's/.*"id":\([^,]*\).*/\1/') return 0 } _delete_txt_record() { _dns_record_id=$1 - if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then + if ! _dynu_rest DELETE "dns/$dnsId/record/$_dns_record_id"; then return 1 fi - if ! _contains "$response" "true"; then + if ! _contains "$response" "200"; then return 1 fi @@ -189,7 +189,7 @@ _dynu_rest() { export _H1="Authorization: Bearer $Dynu_Token" export _H2="Content-Type: application/json" - if [ "$data" ]; then + if [ "$data" ] || [ "$m" = "DELETE" ]; then _debug data "$data" response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" else @@ -216,8 +216,8 @@ _dynu_authentication() { _err "Authentication failed." return 1 fi - if _contains "$response" "accessToken"; then - Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2) + if _contains "$response" "access_token"; then + Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2) fi if _contains "$Dynu_Token" "null"; then Dynu_Token="" From cd4f29135b8f66d5dc8a34833712ad90eceefe9f Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Wed, 2 Jan 2019 16:44:11 +0100 Subject: [PATCH 174/403] waiting for API zoneStatus active --- dnsapi/dns_hostingde.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 39bcfb63..317ebeda 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -74,8 +74,26 @@ _hostingde_getZoneConfig() { return $returnCode } +_hostingde_getZoneStatus() { + _debug "Checking Zone status" + curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":\"${zoneConfigId}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}" + curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind")" + _debug "Calling zonesFind '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind'" + _debug "Result of zonesFind '$curResult'" + zoneStatus=$(echo "${curResult}" | grep -v success | _egrep_o '"status":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + _debug "zoneStatus '${zoneStatus}'" + return 0 +} + _hostingde_addRecord() { _info "Adding record to zone" + _hostingde_getZoneStatus + _debug "Result of zoneStatus: '${zoneStatus}'" + while [ "${zoneStatus}" != "active" ]; do + sleep 5 + _hostingde_getZoneStatus + _debug "Result of zoneStatus: '${zoneStatus}'" + done curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" @@ -93,6 +111,13 @@ _hostingde_addRecord() { _hostingde_removeRecord() { _info "Removing record from zone" + _hostingde_getZoneStatus + _debug "Result of zoneStatus: '$zoneStatus'" + while [ "$zoneStatus" != "active" ]; do + sleep 5 + _hostingde_getZoneStatus + _debug "Result of zoneStatus: '$zoneStatus'" + done curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" From ecf7dded07144d81aba47addee52eae0c072dbd0 Mon Sep 17 00:00:00 2001 From: Ivru Date: Thu, 3 Jan 2019 08:39:51 +0100 Subject: [PATCH 175/403] Fix typo --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 201deeec..9358eb4b 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1122,7 +1122,7 @@ export EXOSCALE_SECRET_KEY='xxx' Now, let's issue a cert: ``` -acme.sh --issue --dns dns_netcup -d example.com -d www.example.com +acme.sh --issue --dns dns_exoscale -d example.com -d www.example.com ``` The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. From 089823785e1aa4b1cb4d3b4725bab2920a97ff02 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Thu, 3 Jan 2019 10:32:59 +0100 Subject: [PATCH 176/403] Using _sleep() instead of sleep --- dnsapi/dns_hostingde.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 317ebeda..74a472d2 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -90,7 +90,7 @@ _hostingde_addRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" while [ "${zoneStatus}" != "active" ]; do - sleep 5 + _sleep 5 _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" done @@ -114,7 +114,7 @@ _hostingde_removeRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" while [ "$zoneStatus" != "active" ]; do - sleep 5 + _sleep 5 _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" done From 40f0238bb79a14ed884a520b24cb5cbcdbd3aa08 Mon Sep 17 00:00:00 2001 From: Marton Szucs Date: Thu, 3 Jan 2019 11:32:43 +0100 Subject: [PATCH 177/403] fix dns_loopia wildcard certificate Checks if a subdomain already exists before creating one. The loopia API clears all records for a subdomain when adding it again. Adding TXT-records instead of updating the existing record when using the add method. Wildcard certificates require multiple TXT-records for the same subdomain. Now you can create wildcard certificates using: `acme.sh --issue -d example.com -d '*.example.com' --dns dns_loopia` Double quoting variables --- dnsapi/dns_loopia.sh | 77 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh index 5d761187..ece5ef8c 100644 --- a/dnsapi/dns_loopia.sh +++ b/dnsapi/dns_loopia.sh @@ -38,8 +38,8 @@ dns_loopia_add() { _info "Adding record" - _loopia_add_record "$_domain" "$_sub_domain" - _loopia_update_record "$_domain" "$_sub_domain" "$txtvalue" + _loopia_add_sub_domain "$_domain" "$_sub_domain" + _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue" } @@ -96,6 +96,37 @@ dns_loopia_rm() { #################### Private functions below ################################## +_loopia_get_records() { + domain=$1 + sub_domain=$2 + + xml_content=$(printf ' + + getZoneRecords + + + %s + + + %s + + + %s + + + %s + + + ' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain") + + response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" + if ! _contains "$response" ""; then + _err "Error" + return 1 + fi + return 0 +} + _get_root() { domain=$1 _debug "get root" @@ -137,14 +168,14 @@ _get_root() { } -_loopia_update_record() { +_loopia_add_record() { domain=$1 sub_domain=$2 txtval=$3 xml_content=$(printf ' - updateZoneRecord + addZoneRecord %s @@ -176,10 +207,6 @@ _loopia_update_record() { rdata %s - - record_id - 0 - @@ -194,10 +221,42 @@ _loopia_update_record() { return 0 } -_loopia_add_record() { +_sub_domain_exists() { domain=$1 sub_domain=$2 + xml_content=$(printf ' + + getSubdomains + + + %s + + + %s + + + %s + + + ' $LOOPIA_User $LOOPIA_Password "$domain") + + response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" + + if _contains "$response" "$sub_domain"; then + return 0 + fi + return 1 +} + +_loopia_add_sub_domain() { + domain=$1 + sub_domain=$2 + + if _sub_domain_exists "$domain" "$sub_domain"; then + return 0 + fi + xml_content=$(printf ' addSubdomain From 68d9aad3a26eacb0d57d9c5fe66f59571bddab69 Mon Sep 17 00:00:00 2001 From: hebbet Date: Thu, 3 Jan 2019 14:19:55 +0100 Subject: [PATCH 178/403] add link to profile of Cloudflare follow-up for #1893 --- 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 202385fe..532199f3 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 didn't specify a cloudflare api key and email yet." - _err "Please create the key and try again." + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." return 1 fi @@ -97,8 +97,8 @@ dns_cf_rm() { if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then CF_Key="" CF_Email="" - _err "You didn't specify a cloudflare api key and email yet." - _err "Please create the key and try again." + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." return 1 fi From ad613e2437f0cb4cb476af6c61c5975cf1212db8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 4 Jan 2019 22:40:59 +0800 Subject: [PATCH 179/403] fix alpn oid. https://github.com/Neilpang/acme.sh/issues/2005 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index aae16f07..8b7fa70b 100755 --- a/acme.sh +++ b/acme.sh @@ -1089,7 +1089,7 @@ _createcsr() { fi if [ "$acmeValidationv1" ]; then - printf "\n1.3.6.1.5.5.7.1.30.1=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}" + printf "\n1.3.6.1.5.5.7.1.31=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}" fi _csr_cn="$(_idn "$domain")" From 0483d841e385626e6dc45bc24c8d88a3d5277b62 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 6 Jan 2019 21:05:33 +0800 Subject: [PATCH 180/403] Support Post as Get (#2009) * Support POST as GET https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380 * fix PAG, The newline '\n' in response is removed by _send_signed_request(), to keep it, we just use needbase64 * fix PAG, the cert is muti line * fix format * PAG is only for v2 --- acme.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 8b7fa70b..872529f7 100755 --- a/acme.sh +++ b/acme.sh @@ -3651,7 +3651,7 @@ issue() { _authorizations_map="" for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do _debug2 "_authz_url" "$_authz_url" - if ! response="$(_get "$_authz_url")"; then + if ! _send_signed_request "$_authz_url"; then _err "get to authz error." _err "_authorizations_seg" "$_authorizations_seg" _err "_authz_url" "$_authz_url" @@ -4069,7 +4069,11 @@ $_authorizations_map" _debug "sleep 2 secs to verify" sleep 2 _debug "checking" - response="$(_get "$uri")" + if [ "$ACME_VERSION" = "2" ]; then + _send_signed_request "$uri" + else + response="$(_get "$uri")" + fi if [ "$?" != "0" ]; then _err "$d:Verify error:$response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" @@ -4145,13 +4149,16 @@ $_authorizations_map" fi Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if ! _get "$Le_LinkCert" >"$CERT_PATH"; then + _tempSignedResponse="$response" + if ! _send_signed_request "$Le_LinkCert" "" "needbase64"; then _err "Sign failed, can not download cert:$Le_LinkCert." _err "$response" _on_issue_err "$_post_hook" return 1 fi + echo "$response" | _dbase64 "multiline" >"$CERT_PATH" + if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then _debug "Found cert chain" cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH" @@ -4161,6 +4168,7 @@ $_authorizations_map" _end_n="$(_math $_end_n + 1)" sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH" fi + response="$_tempSignedResponse" else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then _err "Sign failed. $response" @@ -4231,7 +4239,8 @@ $_authorizations_map" 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 + if _send_signed_request "$Le_LinkIssuer"; then + echo "$response" >"$CA_CERT_PATH" break fi else @@ -4957,7 +4966,7 @@ _deactivate() { authzUri="$_authorizations_seg" _debug2 "authzUri" "$authzUri" - if ! response="$(_get "$authzUri")"; then + if ! _send_signed_request "$authzUri"; then _err "get to authz error." _err "_authorizations_seg" "$_authorizations_seg" _err "authzUri" "$authzUri" From 72ce37704bbaa04d71623c50b545ef1357e981b3 Mon Sep 17 00:00:00 2001 From: Mike Barnes Date: Tue, 8 Jan 2019 15:49:09 +1100 Subject: [PATCH 181/403] Native PointHQ support --- dnsapi/README.md | 19 ++++- dnsapi/dns_pointhq.sh | 164 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 dnsapi/dns_pointhq.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 9358eb4b..f1bf05e4 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1,12 +1,12 @@ # How to use DNS API -If your dns provider doesn't provide api access, you can use our dns alias mode: +If your dns provider doesn't provide api access, you can use our dns alias mode: https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode ## 1. Use CloudFlare domain API to automatically issue cert -First you need to login to your CloudFlare account to get your [API key](https://dash.cloudflare.com/profile). +First you need to login to your CloudFlare account to get your [API key](https://dash.cloudflare.com/profile). ``` export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" @@ -891,7 +891,7 @@ acme.sh --issue --dns dns_loopia -d example.com -d *.example.com The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. ## 45. Use ACME DNS API -ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. +ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. https://github.com/joohoi/acme-dns ``` @@ -1056,6 +1056,7 @@ Now you can issue a certificate. acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com ``` +<<<<<<< HEAD ## 54. Use MyDNS.JP API First, register to MyDNS.JP and get MasterID and Password. @@ -1127,6 +1128,18 @@ acme.sh --issue --dns dns_exoscale -d example.com -d www.example.com The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 58. Using PointHQ API to issue certs + +Log into [PointHQ account management](https://app.pointhq.com/profile) and copy the API key from the page there. + +```export PointHQ_Key="apikeystringgoeshere" +exportPointHQ_Email="accountemail@yourdomain.com" +``` + +You can then issue certs by using: +```acme.sh --issue --dns dns_pointhq -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_pointhq.sh b/dnsapi/dns_pointhq.sh new file mode 100644 index 00000000..62313109 --- /dev/null +++ b/dnsapi/dns_pointhq.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env sh + +# +#PointHQ_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# +#PointHQ_Email="xxxx@sss.com" + +PointHQ_Api="https://api.pointhq.com" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_pointhq_add() { + fulldomain=$1 + txtvalue=$2 + + PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}" + PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}" + if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then + PointHQ_Key="" + PointHQ_Email="" + _err "You didn't specify a PointHQ API key and email yet." + _err "Please create the key and try again." + return 1 + fi + + if ! _contains "$PointHQ_Email" "@"; then + _err "It seems that the PointHQ_Email=$PointHQ_Email 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 PointHQ_Key "$PointHQ_Key" + _saveaccountconf_mutable PointHQ_Email "$PointHQ_Email" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _info "Adding record" + if _pointhq_rest POST "zones/$_domain/records" "{\"zone_record\": {\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":3600}}"; 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." + return 1 +} + +#fulldomain txtvalue +dns_pointhq_rm() { + fulldomain=$1 + txtvalue=$2 + + PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}" + PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}" + if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then + PointHQ_Key="" + PointHQ_Email="" + _err "You didn't specify a PointHQ API key and email yet." + _err "Please create the 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 _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _pointhq_rest GET "zones/${_domain}/records?record_type=TXT&name=$_sub_domain" + + if ! printf "%s" "$response" | grep "^\[" >/dev/null; then + _err "Error" + return 1 + fi + + if [ "$response" = "[]" ]; then + _info "No records to remove." + else + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | tr -d \" | head -n 1) + _debug "record_id" "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if ! _pointhq_rest DELETE "zones/$_domain/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" '"status":"OK"' + 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 + 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 ! _pointhq_rest GET "zones"; then + return 1 + fi + + if _contains "$response" "\"name\":\"$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 + return 1 +} + +_pointhq_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + _pointhq_auth=$(printf "%s:%s" "$PointHQ_Email" "$PointHQ_Key" | _base64) + + export _H1="Authorization: Basic $_pointhq_auth" + export _H2="Content-Type: application/json" + export _H3="Accept: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$PointHQ_Api/$ep" "" "$m")" + else + response="$(_get "$PointHQ_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From cd3ef8fa5ac946975704d6bb910b2ddf07314377 Mon Sep 17 00:00:00 2001 From: Mike Barnes Date: Tue, 8 Jan 2019 15:53:53 +1100 Subject: [PATCH 182/403] Correct edits to README.md this time --- dnsapi/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index f1bf05e4..fc65748e 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1,12 +1,12 @@ # How to use DNS API -If your dns provider doesn't provide api access, you can use our dns alias mode: +If your dns provider doesn't provide api access, you can use our dns alias mode: https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode ## 1. Use CloudFlare domain API to automatically issue cert -First you need to login to your CloudFlare account to get your [API key](https://dash.cloudflare.com/profile). +First you need to login to your CloudFlare account to get your [API key](https://dash.cloudflare.com/profile). ``` export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" @@ -891,7 +891,7 @@ acme.sh --issue --dns dns_loopia -d example.com -d *.example.com The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. ## 45. Use ACME DNS API -ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. +ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. https://github.com/joohoi/acme-dns ``` @@ -1056,7 +1056,6 @@ Now you can issue a certificate. acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com ``` -<<<<<<< HEAD ## 54. Use MyDNS.JP API First, register to MyDNS.JP and get MasterID and Password. From 3099c799b25b4eca5de668326b3be297eed12903 Mon Sep 17 00:00:00 2001 From: Mike Barnes Date: Wed, 9 Jan 2019 10:24:28 +1100 Subject: [PATCH 183/403] Added PointHQ to supported API list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2d31c678..41ffb694 100644 --- a/README.md +++ b/README.md @@ -348,6 +348,7 @@ You don't have to do anything manually! 1. hosting.de (https://www.hosting.de) 1. Neodigit.net API (https://www.neodigit.net) 1. Exoscale.com API (https://www.exoscale.com/) +1. PointDNS API (https://pointhq.com/) And: From dd068467def09b070dc2e7dcf158cf69d52c5d74 Mon Sep 17 00:00:00 2001 From: Fabio Kruger Date: Thu, 10 Jan 2019 19:33:25 +0100 Subject: [PATCH 184/403] 2020 Added a space to improve log readability --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 872529f7..ff472867 100755 --- a/acme.sh +++ b/acme.sh @@ -3901,7 +3901,7 @@ $_authorizations_map" continue fi - _info "Verifying:$d" + _info "Verifying: $d" _debug "d" "$d" _debug "keyauthorization" "$keyauthorization" _debug "uri" "$uri" From e19809d5b510ebd466f1abfd9f8ec4feadae3d92 Mon Sep 17 00:00:00 2001 From: shonenada Date: Fri, 11 Jan 2019 18:17:38 +0800 Subject: [PATCH 185/403] Add deployment for qiniu cdn Upload certificate and privkey to Qiniu's CDN service with https://developer.qiniu.com/fusion/api/4248/certificate --- acme.sh | 2 +- deploy/qiniu.sh | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 deploy/qiniu.sh diff --git a/acme.sh b/acme.sh index 872529f7..86b555a3 100755 --- a/acme.sh +++ b/acme.sh @@ -1580,7 +1580,7 @@ _inithttp() { fi if [ -z "$_ACME_CURL" ] && _exists "curl"; then - _ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER " + _ACME_CURL="curl -L --dump-header $HTTP_HEADER " if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then _CURL_DUMP="$(_mktemp)" _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP " diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh new file mode 100644 index 00000000..4fa66ee6 --- /dev/null +++ b/deploy/qiniu.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env sh + +# Script to create certificate to qiniu.com +# +# This deployment required following variables +# export QINIU_AK="QINIUACCESSKEY" +# export QINIU_SK="QINIUSECRETKEY" + +QINIU_API_BASE="https://api.qiniu.com" + +qiniu_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if [ -z "$QINIU_AK" ]; then + if [ -z "$Le_Deploy_Qiniu_AK" ]; then + _err "QINIU_AK is not defined." + return 1 + fi + else + Le_Deploy_Qiniu_AK="$QINIU_AK" + _savedomainconf Le_Deploy_Qiniu_AK "$Le_Deploy_Qiniu_AK" + fi + + if [ -z "$QINIU_SK" ]; then + if [ -z "$Le_Deploy_Qiniu_SK" ]; then + _err "QINIU_SK is not defined." + return 1 + fi + else + Le_Deploy_Qiniu_SK="$QINIU_SK" + _savedomainconf Le_Deploy_Qiniu_SK "$Le_Deploy_Qiniu_SK" + fi + + string_fullchain=$(awk '{printf "%s\\n", $0}' "$_cfullchain") + string_key=$(awk '{printf "%s\\n", $0}' "$_ckey") + + body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\""$string_fullchain"\",\"pri\":\"$string_key\"}" + + create_ssl_url="$QINIU_API_BASE/sslcert" + + ACCESSTOKEN="$(_make_sslcreate_access_token)" + export _H1="Authorization: QBox $ACCESSTOKEN" + + _response=$(_post "$body" "$create_ssl_url" 0 "POST" "application/json" | _dbase64 "multiline") + + success_response="certID" + if test "${_response#*$success_response}" == "$_response"; then + _err "Error in deploying certificate:" + _err "$_response" + return 1 + fi + + _debug response "$_response" + _info "Certificate successfully deployed" + + return 0 +} + +_make_sslcreate_access_token() { + _data="/sslcert\\n" + _token="$(printf "$_data" | openssl sha1 -hmac $Le_Deploy_Qiniu_SK -binary | openssl base64 -e)" + echo "$Le_Deploy_Qiniu_AK:$_token" +} From 3bc6628227dcebc1b788ed0676d51b992ee202cc Mon Sep 17 00:00:00 2001 From: shonenada Date: Fri, 11 Jan 2019 19:19:07 +0800 Subject: [PATCH 186/403] Update Qiniu's domain settings after uploading certificate --- acme.sh | 2 +- deploy/qiniu.sh | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/acme.sh b/acme.sh index 86b555a3..872529f7 100755 --- a/acme.sh +++ b/acme.sh @@ -1580,7 +1580,7 @@ _inithttp() { fi if [ -z "$_ACME_CURL" ] && _exists "curl"; then - _ACME_CURL="curl -L --dump-header $HTTP_HEADER " + _ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER " if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then _CURL_DUMP="$(_mktemp)" _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP " diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index 4fa66ee6..070b7f69 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -44,30 +44,53 @@ qiniu_deploy() { string_fullchain=$(awk '{printf "%s\\n", $0}' "$_cfullchain") string_key=$(awk '{printf "%s\\n", $0}' "$_ckey") - body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\""$string_fullchain"\",\"pri\":\"$string_key\"}" + sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\""$string_fullchain"\",\"pri\":\"$string_key\"}" create_ssl_url="$QINIU_API_BASE/sslcert" - ACCESSTOKEN="$(_make_sslcreate_access_token)" - export _H1="Authorization: QBox $ACCESSTOKEN" + sslcert_access_token="$(_make_sslcreate_access_token "/sslcert\\n")" + _debug sslcert_access_token "$sslcert_access_token" + export _H1="Authorization: QBox $sslcert_access_token" - _response=$(_post "$body" "$create_ssl_url" 0 "POST" "application/json" | _dbase64 "multiline") + sslcert_response=$(_post "$sslcerl_body" "$create_ssl_url" 0 "POST" "application/json" | _dbase64 "multiline") success_response="certID" - if test "${_response#*$success_response}" == "$_response"; then - _err "Error in deploying certificate:" - _err "$_response" + if test "${sslcert_response#*$success_response}" == "$sslcert_response"; then + _err "Error in creating certificate:" + _err "$sslcert_response" return 1 fi - _debug response "$_response" + _debug sslcert_response "$sslcert_response" + _info "Certificate successfully uploaded, updating domain $_cdomain" + + _certId=$(printf "%s" $sslcert_response | sed -e "s/^.*certID\":\"//" -e "s/\"\}$//") + _debug certId "$_certId" + + update_path="/domain/$_cdomain/httpsconf" + update_url="$QINIU_API_BASE$update_path" + update_body="{\"certid\":\""$_certId"\",\"forceHttps\":true}" + + update_access_token="$(_make_sslcreate_access_token "$update_path\\n")" + _debug update_access_token "$update_access_token" + export _H1="Authorization: QBox $update_access_token" + update_response=$(_post "$update_body" "$update_url" 0 "PUT" "application/json" | _dbase64 "multiline") + + err_response="error" + if test "${update_response#*$err_response}" != "$update_response"; then + _err "Error in updating domain:" + _err "$update_response" + return 1 + fi + + _debug update_response "$update_response" _info "Certificate successfully deployed" return 0 } _make_sslcreate_access_token() { - _data="/sslcert\\n" + _data="$1" _token="$(printf "$_data" | openssl sha1 -hmac $Le_Deploy_Qiniu_SK -binary | openssl base64 -e)" echo "$Le_Deploy_Qiniu_AK:$_token" } From d2a60f3ca42aead53dedbb7afa841919ac75fc83 Mon Sep 17 00:00:00 2001 From: shonenada Date: Sat, 12 Jan 2019 15:54:42 +0800 Subject: [PATCH 187/403] lint code --- deploy/qiniu.sh | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index 070b7f69..4f578b27 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -41,18 +41,16 @@ qiniu_deploy() { _savedomainconf Le_Deploy_Qiniu_SK "$Le_Deploy_Qiniu_SK" fi + ## upload certificate string_fullchain=$(awk '{printf "%s\\n", $0}' "$_cfullchain") string_key=$(awk '{printf "%s\\n", $0}' "$_ckey") - sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\""$string_fullchain"\",\"pri\":\"$string_key\"}" - - create_ssl_url="$QINIU_API_BASE/sslcert" - - sslcert_access_token="$(_make_sslcreate_access_token "/sslcert\\n")" + sslcert_path="/sslcert" + sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" + sslcert_access_token="$(_make_sslcreate_access_token "$sslcert_path")" _debug sslcert_access_token "$sslcert_access_token" export _H1="Authorization: QBox $sslcert_access_token" - - sslcert_response=$(_post "$sslcerl_body" "$create_ssl_url" 0 "POST" "application/json" | _dbase64 "multiline") + sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline") success_response="certID" if test "${sslcert_response#*$success_response}" == "$sslcert_response"; then @@ -64,21 +62,21 @@ qiniu_deploy() { _debug sslcert_response "$sslcert_response" _info "Certificate successfully uploaded, updating domain $_cdomain" - _certId=$(printf "%s" $sslcert_response | sed -e "s/^.*certID\":\"//" -e "s/\"\}$//") + ## extract certId + _certId=$(printf "%s" "$sslcert_response" | sed -e "s/^.*certID\":\"//" -e "s/\"\}$//") _debug certId "$_certId" + ## update domain ssl config update_path="/domain/$_cdomain/httpsconf" - update_url="$QINIU_API_BASE$update_path" - update_body="{\"certid\":\""$_certId"\",\"forceHttps\":true}" - - update_access_token="$(_make_sslcreate_access_token "$update_path\\n")" + update_body="{\"certid\":\"$_certId\",\"forceHttps\":true}" + update_access_token="$(_make_sslcreate_access_token "$update_path")" _debug update_access_token "$update_access_token" export _H1="Authorization: QBox $update_access_token" - update_response=$(_post "$update_body" "$update_url" 0 "PUT" "application/json" | _dbase64 "multiline") + update_response=$(_post "$update_body" "$QINIU_API_BASE$update_body" 0 "PUT" "application/json" | _dbase64 "multiline") err_response="error" if test "${update_response#*$err_response}" != "$update_response"; then - _err "Error in updating domain:" + _err "Error in updating domain httpsconf:" _err "$update_response" return 1 fi @@ -90,7 +88,7 @@ qiniu_deploy() { } _make_sslcreate_access_token() { - _data="$1" - _token="$(printf "$_data" | openssl sha1 -hmac $Le_Deploy_Qiniu_SK -binary | openssl base64 -e)" + _data="$1\\n" + _token="$(printf "%s" "$_data" | openssl sha1 -hmac "$Le_Deploy_Qiniu_SK" -binary | openssl base64 -e)" echo "$Le_Deploy_Qiniu_AK:$_token" } From 4ec39ab707069e8fe87eccffc289dc0432afcc53 Mon Sep 17 00:00:00 2001 From: shonenada Date: Sat, 12 Jan 2019 19:58:57 +0800 Subject: [PATCH 188/403] replace with functions defined in acme.sh --- deploy/qiniu.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index 4f578b27..c2306c51 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -72,7 +72,7 @@ qiniu_deploy() { update_access_token="$(_make_sslcreate_access_token "$update_path")" _debug update_access_token "$update_access_token" export _H1="Authorization: QBox $update_access_token" - update_response=$(_post "$update_body" "$QINIU_API_BASE$update_body" 0 "PUT" "application/json" | _dbase64 "multiline") + update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline") err_response="error" if test "${update_response#*$err_response}" != "$update_response"; then @@ -88,7 +88,6 @@ qiniu_deploy() { } _make_sslcreate_access_token() { - _data="$1\\n" - _token="$(printf "%s" "$_data" | openssl sha1 -hmac "$Le_Deploy_Qiniu_SK" -binary | openssl base64 -e)" + _token="$(printf "%s\\n" "$1" | _hmac "sha1" "$(printf "%s" "$Le_Deploy_Qiniu_SK" | _hex_dump | tr -d " ")" | _base64)" echo "$Le_Deploy_Qiniu_AK:$_token" } From 82b11da4caf356f418e12ca5c5fd047bbe21d37e Mon Sep 17 00:00:00 2001 From: shonenada Date: Sat, 12 Jan 2019 21:07:22 +0800 Subject: [PATCH 189/403] replace `awk` with `sed` and `tr` --- deploy/qiniu.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index c2306c51..559e59cf 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -42,8 +42,8 @@ qiniu_deploy() { fi ## upload certificate - string_fullchain=$(awk '{printf "%s\\n", $0}' "$_cfullchain") - string_key=$(awk '{printf "%s\\n", $0}' "$_ckey") + string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n') + string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n') sslcert_path="/sslcert" sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" @@ -63,12 +63,12 @@ qiniu_deploy() { _info "Certificate successfully uploaded, updating domain $_cdomain" ## extract certId - _certId=$(printf "%s" "$sslcert_response" | sed -e "s/^.*certID\":\"//" -e "s/\"\}$//") + _certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\":\s*\"[^\"]*\"" | cut -d : -f 2)" _debug certId "$_certId" ## update domain ssl config update_path="/domain/$_cdomain/httpsconf" - update_body="{\"certid\":\"$_certId\",\"forceHttps\":true}" + update_body="{\"certid\":$_certId,\"forceHttps\":true}" update_access_token="$(_make_sslcreate_access_token "$update_path")" _debug update_access_token "$update_access_token" export _H1="Authorization: QBox $update_access_token" From 0cd6afde6f586a3791952f07a3fe99c5f7e45ed3 Mon Sep 17 00:00:00 2001 From: shonenada Date: Sat, 12 Jan 2019 21:15:16 +0800 Subject: [PATCH 190/403] Add guidance to deploying cert to qiniu.com --- deploy/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index cec7d773..68c1a272 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -332,3 +332,19 @@ variable to anything (ex: "1") before running `acme.sh`: ```sh export FABIO="1" ``` + +## 13. Deploy your certificate to Qiniu.com + +You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key before deploying +your certificate. + +```sh +$ export QINIU_AK="foo" +$ export QINIU_SK="bar" +``` + +then you can deploy certificate by following command: + +```sh +$ acme.sh --deploy -d example.com --deploy-hook qiniu +``` From 96efc8c7f025b6b862c9502abf44434fcd6b0693 Mon Sep 17 00:00:00 2001 From: shonenada Date: Sat, 12 Jan 2019 23:11:19 +0800 Subject: [PATCH 191/403] lint codes --- deploy/qiniu.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index 559e59cf..e0be60fe 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -52,8 +52,7 @@ qiniu_deploy() { export _H1="Authorization: QBox $sslcert_access_token" sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline") - success_response="certID" - if test "${sslcert_response#*$success_response}" == "$sslcert_response"; then + if ! _contains "$sslcert_response" "certID"; then _err "Error in creating certificate:" _err "$sslcert_response" return 1 @@ -63,7 +62,7 @@ qiniu_deploy() { _info "Certificate successfully uploaded, updating domain $_cdomain" ## extract certId - _certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\":\s*\"[^\"]*\"" | cut -d : -f 2)" + _certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\": *\"[^\"]*\"" | cut -d : -f 2)" _debug certId "$_certId" ## update domain ssl config @@ -74,8 +73,7 @@ qiniu_deploy() { export _H1="Authorization: QBox $update_access_token" update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline") - err_response="error" - if test "${update_response#*$err_response}" != "$update_response"; then + if _contains "$update_response" "error"; then _err "Error in updating domain httpsconf:" _err "$update_response" return 1 @@ -88,6 +86,6 @@ qiniu_deploy() { } _make_sslcreate_access_token() { - _token="$(printf "%s\\n" "$1" | _hmac "sha1" "$(printf "%s" "$Le_Deploy_Qiniu_SK" | _hex_dump | tr -d " ")" | _base64)" + _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$Le_Deploy_Qiniu_SK" | _hex_dump | tr -d " ")" | _base64)" echo "$Le_Deploy_Qiniu_AK:$_token" } From 3c6b707353007d476e758932dfca1e7125e534b1 Mon Sep 17 00:00:00 2001 From: shonenada Date: Sun, 13 Jan 2019 12:23:15 +0800 Subject: [PATCH 192/403] add `QINIU_CDN_DOMAIN` for wildcard certificate --- deploy/README.md | 14 ++++++++++++-- deploy/qiniu.sh | 6 ++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 68c1a272..fa2d7189 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -335,8 +335,9 @@ export FABIO="1" ## 13. Deploy your certificate to Qiniu.com -You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key before deploying -your certificate. +You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key +before deploying your certificate, and please ensure you have enabled HTTPS for +your domain name. You can enable it in https://portal.qiniu.com/cdn/domain. ```sh $ export QINIU_AK="foo" @@ -348,3 +349,12 @@ then you can deploy certificate by following command: ```sh $ acme.sh --deploy -d example.com --deploy-hook qiniu ``` + +(Optional), If you are using wildcard certificate, +you may need export `QINIU_CDN_DOMAIN` to specify which domain +you want to update: + +```sh +$ export QINIU_CDN_DOMAIN="cdn.example.com" +$ acme.sh --deploy -d example.com --deploy-hook qiniu +``` diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index e0be60fe..dac1866d 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -5,6 +5,7 @@ # This deployment required following variables # export QINIU_AK="QINIUACCESSKEY" # export QINIU_SK="QINIUSECRETKEY" +# export QINIU_CDN_DOMAIN="cdn.example.com" QINIU_API_BASE="https://api.qiniu.com" @@ -14,6 +15,7 @@ qiniu_deploy() { _ccert="$3" _cca="$4" _cfullchain="$5" + _cdndomain="${QINIU_CDN_DOMAIN:-$_cdomain}" _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" @@ -46,7 +48,7 @@ qiniu_deploy() { string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n') sslcert_path="/sslcert" - sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdomain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" + sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdndomain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" sslcert_access_token="$(_make_sslcreate_access_token "$sslcert_path")" _debug sslcert_access_token "$sslcert_access_token" export _H1="Authorization: QBox $sslcert_access_token" @@ -66,7 +68,7 @@ qiniu_deploy() { _debug certId "$_certId" ## update domain ssl config - update_path="/domain/$_cdomain/httpsconf" + update_path="/domain/$_cdndomain/httpsconf" update_body="{\"certid\":$_certId,\"forceHttps\":true}" update_access_token="$(_make_sslcreate_access_token "$update_path")" _debug update_access_token "$update_access_token" From 4c1fa9c2422143b164448b6e7c327bb293f808a8 Mon Sep 17 00:00:00 2001 From: shonenada Date: Mon, 14 Jan 2019 22:19:00 +0800 Subject: [PATCH 193/403] save CDN Domain with `_savedomainconf` --- deploy/qiniu.sh | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index dac1866d..a97dfbbf 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -15,7 +15,6 @@ qiniu_deploy() { _ccert="$3" _cca="$4" _cfullchain="$5" - _cdndomain="${QINIU_CDN_DOMAIN:-$_cdomain}" _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" @@ -43,13 +42,20 @@ qiniu_deploy() { _savedomainconf Le_Deploy_Qiniu_SK "$Le_Deploy_Qiniu_SK" fi + Le_Deploy_Qiniu_Cdn_Domain="${QINIU_CDN_DOMAIN:-$(_readdomainconf Le_Deploy_Qiniu_Cdn_Domain)}" + if [ -z "$Le_Deploy_Qiniu_Cdn_Domain" ]; then + Le_Deploy_Qiniu_Cdn_Domain="$_cdomain" + fi + + _savedomainconf Le_Deploy_Qiniu_Cdn_Domain "$Le_Deploy_Qiniu_Cdn_Domain" + ## upload certificate string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n') string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n') sslcert_path="/sslcert" - sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$_cdndomain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" - sslcert_access_token="$(_make_sslcreate_access_token "$sslcert_path")" + sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$Le_Deploy_Qiniu_Cdn_Domain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" + sslcert_access_token="$(_make_access_token "$sslcert_path")" _debug sslcert_access_token "$sslcert_access_token" export _H1="Authorization: QBox $sslcert_access_token" sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline") @@ -68,9 +74,9 @@ qiniu_deploy() { _debug certId "$_certId" ## update domain ssl config - update_path="/domain/$_cdndomain/httpsconf" - update_body="{\"certid\":$_certId,\"forceHttps\":true}" - update_access_token="$(_make_sslcreate_access_token "$update_path")" + update_path="/domain/$Le_Deploy_Qiniu_Cdn_Domain/httpsconf" + update_body="{\"certid\":$_certId,\"forceHttps\":false}" + update_access_token="$(_make_access_token "$update_path")" _debug update_access_token "$update_access_token" export _H1="Authorization: QBox $update_access_token" update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline") @@ -87,7 +93,7 @@ qiniu_deploy() { return 0 } -_make_sslcreate_access_token() { +_make_access_token() { _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$Le_Deploy_Qiniu_SK" | _hex_dump | tr -d " ")" | _base64)" echo "$Le_Deploy_Qiniu_AK:$_token" } From 10ba2cd312fb346a9af32a808ddbfa1d55b83879 Mon Sep 17 00:00:00 2001 From: jim-p Date: Thu, 10 Jan 2019 16:06:46 -0500 Subject: [PATCH 194/403] Use a literal space instead of an escaped space. Fixes #2022 --- dnsapi/dns_namecheap.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index a6651be6..2f401bd9 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -161,12 +161,12 @@ _namecheap_parse_host() { _host=$1 _debug _host "$_host" - _hostid=$(echo "$_host" | _egrep_o '\sHostId="[^"]*' | cut -d '"' -f 2) - _hostname=$(echo "$_host" | _egrep_o '\sName="[^"]*' | cut -d '"' -f 2) - _hosttype=$(echo "$_host" | _egrep_o '\sType="[^"]*' | cut -d '"' -f 2) - _hostaddress=$(echo "$_host" | _egrep_o '\sAddress="[^"]*' | cut -d '"' -f 2) - _hostmxpref=$(echo "$_host" | _egrep_o '\sMXPref="[^"]*' | cut -d '"' -f 2) - _hostttl=$(echo "$_host" | _egrep_o '\sTTL="[^"]*' | cut -d '"' -f 2) + _hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2) + _hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2) + _hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2) + _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2) + _hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2) + _hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2) _debug hostid "$_hostid" _debug hostname "$_hostname" From afdb9a63ffa4540dd8d3097492b8940b21b50907 Mon Sep 17 00:00:00 2001 From: shonenada Date: Sat, 19 Jan 2019 23:58:55 +0800 Subject: [PATCH 195/403] chore: replece `Le_Deploy_Qiniu_*` with `QINIU_*` --- deploy/qiniu.sh | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index a97dfbbf..aadda53b 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -23,38 +23,31 @@ qiniu_deploy() { _debug _cfullchain "$_cfullchain" if [ -z "$QINIU_AK" ]; then - if [ -z "$Le_Deploy_Qiniu_AK" ]; then - _err "QINIU_AK is not defined." - return 1 - fi + _err "QINIU_AK is not defined." + return 1 else - Le_Deploy_Qiniu_AK="$QINIU_AK" - _savedomainconf Le_Deploy_Qiniu_AK "$Le_Deploy_Qiniu_AK" + _savedomainconf QINIU_AK "$QINIU_AK" fi if [ -z "$QINIU_SK" ]; then - if [ -z "$Le_Deploy_Qiniu_SK" ]; then - _err "QINIU_SK is not defined." - return 1 - fi + _err "QINIU_SK is not defined." + return 1 else - Le_Deploy_Qiniu_SK="$QINIU_SK" - _savedomainconf Le_Deploy_Qiniu_SK "$Le_Deploy_Qiniu_SK" + _savedomainconf QINIU_SK "$QINIU_SK" fi - Le_Deploy_Qiniu_Cdn_Domain="${QINIU_CDN_DOMAIN:-$(_readdomainconf Le_Deploy_Qiniu_Cdn_Domain)}" - if [ -z "$Le_Deploy_Qiniu_Cdn_Domain" ]; then - Le_Deploy_Qiniu_Cdn_Domain="$_cdomain" + if [ -z "$QINIU_CDN_DOMAIN" ]; then + QINIU_CDN_DOMAIN="$_cdomain" fi - _savedomainconf Le_Deploy_Qiniu_Cdn_Domain "$Le_Deploy_Qiniu_Cdn_Domain" + _savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN" ## upload certificate string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n') string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n') sslcert_path="/sslcert" - sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$Le_Deploy_Qiniu_Cdn_Domain\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" + sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$QINIU_CDN_DOMAIN\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}" sslcert_access_token="$(_make_access_token "$sslcert_path")" _debug sslcert_access_token "$sslcert_access_token" export _H1="Authorization: QBox $sslcert_access_token" @@ -74,7 +67,7 @@ qiniu_deploy() { _debug certId "$_certId" ## update domain ssl config - update_path="/domain/$Le_Deploy_Qiniu_Cdn_Domain/httpsconf" + update_path="/domain/$QINIU_CDN_DOMAIN/httpsconf" update_body="{\"certid\":$_certId,\"forceHttps\":false}" update_access_token="$(_make_access_token "$update_path")" _debug update_access_token "$update_access_token" @@ -94,6 +87,6 @@ qiniu_deploy() { } _make_access_token() { - _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$Le_Deploy_Qiniu_SK" | _hex_dump | tr -d " ")" | _base64)" - echo "$Le_Deploy_Qiniu_AK:$_token" + _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64)" + echo "$QINIU_AK:$_token" } From dd6fa4af0075248585202eab93ac89f51b652c0e Mon Sep 17 00:00:00 2001 From: shonenada Date: Sun, 20 Jan 2019 23:58:10 +0800 Subject: [PATCH 196/403] Save `QINIU_CDN_DOMAIN` only when defined --- deploy/qiniu.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index aadda53b..8410caa9 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -36,12 +36,12 @@ qiniu_deploy() { _savedomainconf QINIU_SK "$QINIU_SK" fi - if [ -z "$QINIU_CDN_DOMAIN" ]; then - QINIU_CDN_DOMAIN="$_cdomain" + if [ "$QINIU_CDN_DOMAIN" ]; then + _savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN" + else + QINIU_CDN_DOMAIN="$_cdomain" fi - _savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN" - ## upload certificate string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n') string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n') From e8eec2cb41b34bf52fff36647a4861bec23fa653 Mon Sep 17 00:00:00 2001 From: shonenada Date: Mon, 21 Jan 2019 00:08:23 +0800 Subject: [PATCH 197/403] add chinese readme --- deploy/README.md | 2 ++ deploy/README_zh.md | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 deploy/README_zh.md diff --git a/deploy/README.md b/deploy/README.md index fa2d7189..a9e28e9e 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -335,6 +335,8 @@ export FABIO="1" ## 13. Deploy your certificate to Qiniu.com +[中文文档](https://github.com/Neilpang/acme.sh/blob/master/deploy/README_zh.md#13-%E9%83%A8%E7%BD%B2%E5%88%B0%E4%B8%83%E7%89%9B%E5%9F%9F%E5%90%8D%E8%AF%81%E4%B9%A6%E6%9C%8D%E5%8A%A1) + You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key before deploying your certificate, and please ensure you have enabled HTTPS for your domain name. You can enable it in https://portal.qiniu.com/cdn/domain. diff --git a/deploy/README_zh.md b/deploy/README_zh.md new file mode 100644 index 00000000..4132dcea --- /dev/null +++ b/deploy/README_zh.md @@ -0,0 +1,24 @@ +# Deploy 使用文档 + +## 13. 部署到七牛域名证书服务 + +使用 acme.sh 部署到七牛之前,需要确保部署的域名已打开 HTTPS 功能,您可以访问[融合 CDN - 域名管理](https://portal.qiniu.com/cdn/domain) 设置。 +另外还需要先导出 AK/SK 环境变量,您可以访问[密钥管理](https://portal.qiniu.com/user/key) 获得。 + +```sh +$ export QINIU_AK="foo" +$ export QINIU_SK="bar" +``` + +完成准备工作之后,您就可以通过下面的命令开始部署 SSL 证书到七牛上: + +```sh +$ acme.sh --deploy -d example.com --deploy-hook qiniu +``` + +假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名: + +```sh +$ export QINIU_CDN_DOMAIN="cdn.example.com" +$ acme.sh --deploy -d example.com --deploy-hook qiniu +``` From c445e70cffe326219b8d597867ba28ae85523ba1 Mon Sep 17 00:00:00 2001 From: shonenada Date: Mon, 21 Jan 2019 14:33:15 +0800 Subject: [PATCH 198/403] fix indent --- deploy/qiniu.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index 8410caa9..158b8dbf 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -37,9 +37,9 @@ qiniu_deploy() { fi if [ "$QINIU_CDN_DOMAIN" ]; then - _savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN" + _savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN" else - QINIU_CDN_DOMAIN="$_cdomain" + QINIU_CDN_DOMAIN="$_cdomain" fi ## upload certificate From a4a53e1355503efa8a550f954f59cc3a2b763935 Mon Sep 17 00:00:00 2001 From: shonenada Date: Mon, 21 Jan 2019 17:31:21 +0800 Subject: [PATCH 199/403] Move docs into README.md from README_zh.md --- deploy/README.md | 23 ++++++++++++++++++++++- deploy/README_zh.md | 24 ------------------------ 2 files changed, 22 insertions(+), 25 deletions(-) delete mode 100644 deploy/README_zh.md diff --git a/deploy/README.md b/deploy/README.md index a9e28e9e..091e9feb 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -335,7 +335,28 @@ export FABIO="1" ## 13. Deploy your certificate to Qiniu.com -[中文文档](https://github.com/Neilpang/acme.sh/blob/master/deploy/README_zh.md#13-%E9%83%A8%E7%BD%B2%E5%88%B0%E4%B8%83%E7%89%9B%E5%9F%9F%E5%90%8D%E8%AF%81%E4%B9%A6%E6%9C%8D%E5%8A%A1) +使用 acme.sh 部署到七牛之前,需要确保部署的域名已打开 HTTPS 功能,您可以访问[融合 CDN - 域名管理](https://portal.qiniu.com/cdn/domain) 设置。 +另外还需要先导出 AK/SK 环境变量,您可以访问[密钥管理](https://portal.qiniu.com/user/key) 获得。 + +```sh +$ export QINIU_AK="foo" +$ export QINIU_SK="bar" +``` + +完成准备工作之后,您就可以通过下面的命令开始部署 SSL 证书到七牛上: + +```sh +$ acme.sh --deploy -d example.com --deploy-hook qiniu +``` + +假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名: + +```sh +$ export QINIU_CDN_DOMAIN="cdn.example.com" +$ acme.sh --deploy -d example.com --deploy-hook qiniu +``` + +### English version You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key before deploying your certificate, and please ensure you have enabled HTTPS for diff --git a/deploy/README_zh.md b/deploy/README_zh.md deleted file mode 100644 index 4132dcea..00000000 --- a/deploy/README_zh.md +++ /dev/null @@ -1,24 +0,0 @@ -# Deploy 使用文档 - -## 13. 部署到七牛域名证书服务 - -使用 acme.sh 部署到七牛之前,需要确保部署的域名已打开 HTTPS 功能,您可以访问[融合 CDN - 域名管理](https://portal.qiniu.com/cdn/domain) 设置。 -另外还需要先导出 AK/SK 环境变量,您可以访问[密钥管理](https://portal.qiniu.com/user/key) 获得。 - -```sh -$ export QINIU_AK="foo" -$ export QINIU_SK="bar" -``` - -完成准备工作之后,您就可以通过下面的命令开始部署 SSL 证书到七牛上: - -```sh -$ acme.sh --deploy -d example.com --deploy-hook qiniu -``` - -假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名: - -```sh -$ export QINIU_CDN_DOMAIN="cdn.example.com" -$ acme.sh --deploy -d example.com --deploy-hook qiniu -``` From 56d70e4ea74d2c1274326ec0ac9c93657d9b4e94 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Mon, 21 Jan 2019 15:02:09 +0100 Subject: [PATCH 200/403] Update to latest API Changes --- dnsapi/dns_hostingde.sh | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 74a472d2..7c185119 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -59,9 +59,22 @@ _hostingde_getZoneConfig() { if _contains "${curResult}" '"totalEntries": 1'; then _info "Retrieved zone data." _debug "Zone data: '${curResult}'" - - # read ZoneConfigId for later update zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + zoneConfigName=$(echo "${curResult}" | _egrep_o '"name":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + zoneConfigType=$(echo "${curResult}" | grep -v "FindZoneConfigsResult" | _egrep_o '"type":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + zoneConfigExpire=$(echo "${curResult}" | _egrep_o '"expire":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) + zoneConfigNegativeTtl=$(echo "${curResult}" | _egrep_o '"negativeTtl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) + zoneConfigRefresh=$(echo "${curResult}" | _egrep_o '"refresh":.*' | cut -d ':' -f 2 | cut -d '"' -f 2| cut -d ',' -f 1) + zoneConfigRetry=$(echo "${curResult}" | _egrep_o '"retry":.*' | cut -d ':' -f 2 | cut -d '"' -f 2| cut -d ',' -f 1) + zoneConfigTtl=$(echo "${curResult}" | _egrep_o '"ttl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2| cut -d ',' -f 1) + zoneConfigDnsServerGroupId=$(echo "${curResult}" | _egrep_o '"dnsServerGroupId":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + zoneConfigEmailAddress=$(echo "${curResult}" | _egrep_o '"emailAddress":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + zoneConfigDnsSecMode=$(echo "${curResult}" | _egrep_o '"dnsSecMode":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + if [ $zoneConfigType != "NATIVE" ]; then + _err "Zone is not native" + returnCode=1 + break + fi _debug "zoneConfigId '${zoneConfigId}'" returnCode=0 break @@ -94,7 +107,7 @@ _hostingde_addRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\",\"name\":\"${zoneConfigName}\",\"type\":\"${zoneConfigType}\",\"dnsServerGroupId\":\"${zoneConfigDnsServerGroupId}\",\"dnsSecMode\":\"${zoneConfigDnsSecMode}\",\"emailAddress\":\"${zoneConfigEmailAddress}\",\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" @@ -118,7 +131,7 @@ _hostingde_removeRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\"},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\",\"name\":\"${zoneConfigName}\",\"type\":\"${zoneConfigType}\",\"dnsServerGroupId\":\"${zoneConfigDnsServerGroupId}\",\"dnsSecMode\":\"${zoneConfigDnsSecMode}\",\"emailAddress\":\"${zoneConfigEmailAddress}\",\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" From b15c1ffedcd1d627e6339396179e7691ca83938e Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 21 Jan 2019 22:09:13 +0800 Subject: [PATCH 201/403] clean TXT records when error happens. https://github.com/Neilpang/acme.sh/issues/2037 --- acme.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index ff472867..697a14a4 100755 --- a/acme.sh +++ b/acme.sh @@ -2921,7 +2921,10 @@ _clearup() { _clearupdns() { _debug "_clearupdns" - if [ "$dnsadded" != 1 ] || [ -z "$vlist" ]; then + _debug "dnsadded" "$dnsadded" + _debug "vlist" "$vlist" + #dnsadded is "0" or "1" means dns-01 method was used for at least one domain + if [ -z "$dnsadded" ] || [ -z "$vlist" ]; then _debug "skip dns." return fi From 545f23551f09082799f02a159ed6ade2aa4eb36f Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 21 Jan 2019 22:25:23 +0800 Subject: [PATCH 202/403] trigger validation before cleanup fix https://github.com/Neilpang/acme.sh/issues/2037 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 697a14a4..d651c4f5 100755 --- a/acme.sh +++ b/acme.sh @@ -3857,8 +3857,8 @@ $_authorizations_map" ) if [ "$?" != "0" ]; then - _clearup _on_issue_err "$_post_hook" "$vlist" + _clearup return 1 fi dnsadded='1' @@ -3869,8 +3869,8 @@ $_authorizations_map" _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 re-run with --renew." - _clearup _on_issue_err "$_post_hook" + _clearup return 1 fi From 2dc50e66330c291fafee95d1b8d7183e413df647 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Mon, 21 Jan 2019 15:45:32 +0100 Subject: [PATCH 203/403] making shfmt happy --- dnsapi/dns_hostingde.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 7c185119..7f29d629 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -64,9 +64,9 @@ _hostingde_getZoneConfig() { zoneConfigType=$(echo "${curResult}" | grep -v "FindZoneConfigsResult" | _egrep_o '"type":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigExpire=$(echo "${curResult}" | _egrep_o '"expire":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) zoneConfigNegativeTtl=$(echo "${curResult}" | _egrep_o '"negativeTtl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) - zoneConfigRefresh=$(echo "${curResult}" | _egrep_o '"refresh":.*' | cut -d ':' -f 2 | cut -d '"' -f 2| cut -d ',' -f 1) - zoneConfigRetry=$(echo "${curResult}" | _egrep_o '"retry":.*' | cut -d ':' -f 2 | cut -d '"' -f 2| cut -d ',' -f 1) - zoneConfigTtl=$(echo "${curResult}" | _egrep_o '"ttl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2| cut -d ',' -f 1) + zoneConfigRefresh=$(echo "${curResult}" | _egrep_o '"refresh":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) + zoneConfigRetry=$(echo "${curResult}" | _egrep_o '"retry":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) + zoneConfigTtl=$(echo "${curResult}" | _egrep_o '"ttl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) zoneConfigDnsServerGroupId=$(echo "${curResult}" | _egrep_o '"dnsServerGroupId":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigEmailAddress=$(echo "${curResult}" | _egrep_o '"emailAddress":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigDnsSecMode=$(echo "${curResult}" | _egrep_o '"dnsSecMode":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) From b0775f7a580ac534ad2443cd9308fab5b339749f Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Mon, 21 Jan 2019 16:32:45 +0100 Subject: [PATCH 204/403] making shftm really happy --- dnsapi/dns_hostingde.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 7f29d629..b61acb7a 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -70,7 +70,7 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _egrep_o '"dnsServerGroupId":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigEmailAddress=$(echo "${curResult}" | _egrep_o '"emailAddress":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) zoneConfigDnsSecMode=$(echo "${curResult}" | _egrep_o '"dnsSecMode":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - if [ $zoneConfigType != "NATIVE" ]; then + if [ "${zoneConfigType}" != "NATIVE" ]; then _err "Zone is not native" returnCode=1 break From 572adbaad2c8531870753c18c2b313c70faa9a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A1la?= Date: Mon, 31 Dec 2018 20:05:08 +0100 Subject: [PATCH 205/403] Add support for Active24.cz --- README.md | 1 + dnsapi/README.md | 18 ++++++ dnsapi/dns_active24.sh | 141 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100755 dnsapi/dns_active24.sh diff --git a/README.md b/README.md index 41ffb694..70abcc6c 100644 --- a/README.md +++ b/README.md @@ -349,6 +349,7 @@ You don't have to do anything manually! 1. Neodigit.net API (https://www.neodigit.net) 1. Exoscale.com API (https://www.exoscale.com/) 1. PointDNS API (https://pointhq.com/) +1. Active24.cz API (https://www.active24.cz/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index fc65748e..0a9c4925 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1139,6 +1139,24 @@ You can then issue certs by using: ```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com ``` +## 59. Use Active24 API + +Create an API token in the Active24 account section, documentation on https://faq.active24.com/cz/790131-REST-API-rozhran%C3%AD. + +Set your API token: + +``` +export ACTIVE24_Token='xxx' +``` + +Now, let's issue a cert, set `dnssleep` for propagation new DNS record: +``` +acme.sh --issue --dns dns_active24 -d example.com -d www.example.com --dnssleep 1000 +``` + +The `ACTIVE24_Token` 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_active24.sh b/dnsapi/dns_active24.sh new file mode 100755 index 00000000..90ffaf68 --- /dev/null +++ b/dnsapi/dns_active24.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env sh + +#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje" + +ACTIVE24_Api="https://api.active24.com" + +######## Public functions ##################### + +# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Used to add txt record +dns_active24_add() { + fulldomain=$1 + txtvalue=$2 + + _active24_init + + _info "Adding txt record" + if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then + if _contains "$response" "errors"; then + _err "Add txt record error." + return 1 + else + _info "Added, OK" + return 0 + fi + fi + _err "Add txt record error." + return 1 +} + +# Usage: fulldomain txtvalue +# Used to remove the txt record after validation +dns_active24_rm() { + fulldomain=$1 + txtvalue=$2 + + _active24_init + + _debug "Getting txt records" + _active24_rest GET "dns/$_domain/records/v1" + + if _contains "$response" "errors"; then + _err "Error" + return 1 + fi + + hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-) + + for hash_id in $hash_ids; do + _debug "Removing hash_id" "$hash_id" + if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then + if _contains "$response" "errors"; then + _err "Unable to remove txt record." + return 1 + else + _info "Removed txt record." + return 0 + fi + fi + done + + _err "No txt records found." + return 1 +} + +#################### 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 ! _active24_rest GET "dns/domains/v1"; 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" "\"$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 + return 1 +} + +_active24_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + export _H1="Authorization: Bearer $ACTIVE24_Token" + + if [ "$m" != "GET" ]; then + _debug "data" "$data" + response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")" + else + response="$(_get "$ACTIVE24_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} + +_active24_init() { + ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}" + if [ -z "$ACTIVE24_Token" ]; then + ACTIVE24_Token="" + _err "You didn't specify a Active24 api token yet." + _err "Please create the token and try again." + return 1 + fi + + _saveaccountconf_mutable ACTIVE24_Token "ACTIVE24_Token" + + _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" +} From 127532c226149b991814bd696b1362b079fa8c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=A4del?= Date: Thu, 24 Jan 2019 16:53:03 +0100 Subject: [PATCH 206/403] Added dns_doapi.sh --- dnsapi/dns_doapi.sh | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 dnsapi/dns_doapi.sh diff --git a/dnsapi/dns_doapi.sh b/dnsapi/dns_doapi.sh new file mode 100755 index 00000000..f3d56b68 --- /dev/null +++ b/dnsapi/dns_doapi.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +# Official Let's Encrypt API for do.de / Domain-Offensive +# +# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers +# This API is also available to private customers/individuals +# +# Provide the required LetsEncrypt token like this: +# DO_LETOKEN="FmD408PdqT1E269gUK57" + +DO_API="https://www.do.de/api/letsencrypt" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_doapi_add() { + fulldomain=$1 + txtvalue=$2 + + DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}" + if [ -z "$DO_LETOKEN" ]; then + DO_LETOKEN="" + _err "You didn't configure a do.de API token yet." + _err "Please set DO_LETOKEN and try again." + return 1 + fi + _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" + + _info "Adding TXT record to ${_domain} as ${fulldomain}" + response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&value=${txtvalue}")" + if _contains "${response}" 'success'; then + return 0 + fi + _err "Could not create resource record, check logs" + _err $response + return 1 +} + +dns_doapi_rm() { + fulldomain=$1 + + DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}" + if [ -z "$DO_LETOKEN" ]; then + DO_LETOKEN="" + _err "You didn't configure a do.de API token yet." + _err "Please set DO_LETOKEN and try again." + return 1 + fi + _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" + + _info "Deleting resource record $fulldomain" + response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&action=delete")" + if _contains "${response}" 'success'; then + return 0 + fi + _err "Could not delete resource record, check logs" + _err $response + return 1 +} From ddf77f10e95e6daf22b99a7e5986912d58ff4b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=A4del?= Date: Thu, 24 Jan 2019 16:59:36 +0100 Subject: [PATCH 207/403] Cleaned up dns_doapi.sh --- dnsapi/dns_doapi.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_doapi.sh b/dnsapi/dns_doapi.sh index f3d56b68..fa2b7d33 100755 --- a/dnsapi/dns_doapi.sh +++ b/dnsapi/dns_doapi.sh @@ -26,13 +26,13 @@ dns_doapi_add() { fi _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" - _info "Adding TXT record to ${_domain} as ${fulldomain}" + _info "Adding TXT record to ${fulldomain}" response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&value=${txtvalue}")" if _contains "${response}" 'success'; then return 0 fi _err "Could not create resource record, check logs" - _err $response + _err "${response}" return 1 } @@ -54,6 +54,6 @@ dns_doapi_rm() { return 0 fi _err "Could not delete resource record, check logs" - _err $response + _err "${response}" return 1 } From 5f9b57d3004831cbd0e4455c0676633c56b5f708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=A4del?= Date: Thu, 24 Jan 2019 17:00:37 +0100 Subject: [PATCH 208/403] Cleaned up dns_doapi.sh --- dnsapi/dns_doapi.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_doapi.sh b/dnsapi/dns_doapi.sh index fa2b7d33..135f0b03 100755 --- a/dnsapi/dns_doapi.sh +++ b/dnsapi/dns_doapi.sh @@ -24,7 +24,7 @@ dns_doapi_add() { _err "Please set DO_LETOKEN and try again." return 1 fi - _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" + _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" _info "Adding TXT record to ${fulldomain}" response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&value=${txtvalue}")" @@ -46,7 +46,7 @@ dns_doapi_rm() { _err "Please set DO_LETOKEN and try again." return 1 fi - _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" + _saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN" _info "Deleting resource record $fulldomain" response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&action=delete")" From e2f1338f941dfca192ebc51c1cb65bb09bf8f6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=A4del?= Date: Thu, 24 Jan 2019 17:05:01 +0100 Subject: [PATCH 209/403] Added documentation --- dnsapi/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index fc65748e..e0532bcd 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1139,6 +1139,22 @@ You can then issue certs by using: ```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com ``` +## 59. Use do.de API + +Create an API token in your do.de account. + +Set your API token: +``` +export DO_LETOKEN='FmD408PdqT1E269gUK57' +``` + +To issue a certificate run: +``` +acme.sh --issue --dns dns_doapi -d example.com -d *.example.com +``` + +The API token 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 c1ec2afeca11ad8afdb8feec5e6e3bba1bf113da Mon Sep 17 00:00:00 2001 From: Grant Millar Date: Fri, 25 Jan 2019 09:27:30 +0000 Subject: [PATCH 210/403] Changed records per page to 5000 --- dnsapi/dns_dnsimple.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dnsimple.sh b/dnsapi/dns_dnsimple.sh index b2cba584..d831eb2b 100644 --- a/dnsapi/dns_dnsimple.sh +++ b/dnsapi/dns_dnsimple.sh @@ -152,7 +152,7 @@ _get_records() { sub_domain=$3 _debug "fetching txt records" - _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100&sort=id:desc" + _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=5000&sort=id:desc" if ! _contains "$response" "\"id\":"; then _err "failed to retrieve records" From 75fe022f96dce0baeaef6f8fe9c206f8ab094d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=A4del?= Date: Fri, 25 Jan 2019 15:26:41 +0100 Subject: [PATCH 211/403] Changed order in readme, added do.de --- README.md | 1 + dnsapi/README.md | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 70abcc6c..90a648d5 100644 --- a/README.md +++ b/README.md @@ -350,6 +350,7 @@ You don't have to do anything manually! 1. Exoscale.com API (https://www.exoscale.com/) 1. PointDNS API (https://pointhq.com/) 1. Active24.cz API (https://www.active24.cz/) +1. do.de API (https://www.do.de/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index dc20ac9c..4f9b4100 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1139,38 +1139,38 @@ You can then issue certs by using: ```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com ``` -## 59. Use do.de API +## 59. Use Active24 API -Create an API token in your do.de account. +Create an API token in the Active24 account section, documentation on https://faq.active24.com/cz/790131-REST-API-rozhran%C3%AD. Set your API token: + ``` -export DO_LETOKEN='FmD408PdqT1E269gUK57' +export ACTIVE24_Token='xxx' ``` -To issue a certificate run: +Now, let's issue a cert, set `dnssleep` for propagation new DNS record: ``` -acme.sh --issue --dns dns_doapi -d example.com -d *.example.com +acme.sh --issue --dns dns_active24 -d example.com -d www.example.com --dnssleep 1000 ``` -The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The `ACTIVE24_Token` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -## 60. Use Active24 API +## 60. Use do.de API -Create an API token in the Active24 account section, documentation on https://faq.active24.com/cz/790131-REST-API-rozhran%C3%AD. +Create an API token in your do.de account. Set your API token: - ``` -export ACTIVE24_Token='xxx' +export DO_LETOKEN='FmD408PdqT1E269gUK57' ``` -Now, let's issue a cert, set `dnssleep` for propagation new DNS record: +To issue a certificate run: ``` -acme.sh --issue --dns dns_active24 -d example.com -d www.example.com --dnssleep 1000 +acme.sh --issue --dns dns_doapi -d example.com -d *.example.com ``` -The `ACTIVE24_Token` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed. # Use custom API From 36335984629d07d7c048981cea921bce60e37dbf Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 25 Jan 2019 22:39:22 +0800 Subject: [PATCH 212/403] Lets start 2.8.1 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index d651c4f5..85c17f16 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.0 +VER=2.8.1 PROJECT_NAME="acme.sh" From 43ff787b04b56fb1b8cf73857351c8fbad1c382e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 26 Jan 2019 18:32:11 +0800 Subject: [PATCH 213/403] remove tls-sni --- acme.sh | 44 ++------------------------------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/acme.sh b/acme.sh index 85c17f16..cfdf5714 100755 --- a/acme.sh +++ b/acme.sh @@ -35,7 +35,6 @@ _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org" VTYPE_HTTP="http-01" VTYPE_DNS="dns-01" -VTYPE_TLS="tls-sni-01" VTYPE_ALPN="tls-alpn-01" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -46,7 +45,6 @@ DEFAULT_DNS_SLEEP=120 NO_VALUE="no" -W_TLS="tls" W_DNS="dns" W_ALPN="alpn" DNS_ALIAS_PREFIX="=" @@ -3080,8 +3078,8 @@ _on_before_issue() { _savedomainconf "Le_HTTPPort" "$Le_HTTPPort" fi _checkport="$Le_HTTPPort" - elif [ "$_currentRoot" = "$W_TLS" ] || [ "$_currentRoot" = "$W_ALPN" ]; then - _info "Standalone tls/alpn mode." + elif [ "$_currentRoot" = "$W_ALPN" ]; then + _info "Standalone alpn mode." if [ -z "$Le_TLSPort" ]; then Le_TLSPort=443 else @@ -3701,10 +3699,6 @@ $_authorizations_map" vtype="$VTYPE_DNS" fi - if [ "$_currentRoot" = "$W_TLS" ]; then - vtype="$VTYPE_TLS" - fi - if [ "$_currentRoot" = "$W_ALPN" ]; then vtype="$VTYPE_ALPN" fi @@ -3988,40 +3982,6 @@ $_authorizations_map" fi fi - - elif [ "$vtype" = "$VTYPE_TLS" ]; then - #create A - #_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )" - #_debug2 _hash_A "$_hash_A" - #_x="$(echo $_hash_A | cut -c 1-32)" - #_debug2 _x "$_x" - #_y="$(echo $_hash_A | cut -c 33-64)" - #_debug2 _y "$_y" - #_SAN_A="$_x.$_y.token.acme.invalid" - #_debug2 _SAN_A "$_SAN_A" - - #create B - _hash_B="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")" - _debug2 _hash_B "$_hash_B" - _x="$(echo "$_hash_B" | cut -c 1-32)" - _debug2 _x "$_x" - _y="$(echo "$_hash_B" | cut -c 33-64)" - _debug2 _y "$_y" - - #_SAN_B="$_x.$_y.ka.acme.invalid" - - _SAN_B="$_x.$_y.acme.invalid" - _debug2 _SAN_B "$_SAN_B" - - _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")" - _ncIndex="$(_math "$_ncIndex" + 1)" - if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then - _err "Start tls server error." - _clearupwebbroot "$_currentRoot" "$removelevel" "$token" - _clearup - _on_issue_err "$_post_hook" "$vlist" - return 1 - fi elif [ "$vtype" = "$VTYPE_ALPN" ]; then acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")" _debug acmevalidationv1 "$acmevalidationv1" From cc6159b39b5305778e1f437fcbe2673a3012cb13 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 26 Jan 2019 19:15:13 +0800 Subject: [PATCH 214/403] urlencode the existing txt record value fix https://github.com/Neilpang/acme.sh/issues/2052 --- dnsapi/dns_namecheap.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 2f401bd9..27eda3ad 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -3,16 +3,15 @@ # Namecheap API # https://www.namecheap.com/support/api/intro.aspx # -# Requires Namecheap API key set in NAMECHEAP_API_KEY, NAMECHEAP_SOURCEIP and NAMECHEAP_USERNAME set as environment variable +# Requires Namecheap API key set in +#NAMECHEAP_API_KEY, +#NAMECHEAP_USERNAME, +#NAMECHEAP_SOURCEIP # Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. ######## Public functions ##################### -if [ "$STAGE" -eq 1 ]; then - NAMECHEAP_API="https://api.sandbox.namecheap.com/xml.response" -else - NAMECHEAP_API="https://api.namecheap.com/xml.response" -fi +NAMECHEAP_API="https://api.namecheap.com/xml.response" #Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_namecheap_add() { @@ -144,7 +143,7 @@ _namecheap_set_publicip() { _namecheap_post() { command=$1 data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}" - + _debug2 "_namecheap_post data" "$data" response="$(_post "$data" "$NAMECHEAP_API" "" "POST")" _debug2 response "$response" @@ -224,6 +223,12 @@ _set_namecheap_TXT() { while read -r host; do if _contains "$host" " Date: Sat, 26 Jan 2019 20:27:53 +0800 Subject: [PATCH 215/403] fix rm method to urlencode the existing txt records. fix https://github.com/Neilpang/acme.sh/issues/2052 --- dnsapi/dns_namecheap.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 27eda3ad..fbf93c32 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -283,6 +283,7 @@ _del_namecheap_TXT() { _debug "TXT entry found" found=1 else + _hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)" _namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl" fi fi From a96464680361dde97e1388b81de275756612ba83 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 28 Jan 2019 19:11:45 +0800 Subject: [PATCH 216/403] fix https://github.com/Neilpang/acme.sh/issues/1364#issuecomment-458035330 --- dnsapi/dns_dp.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index 3cc720aa..6bbf149e 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -63,7 +63,7 @@ dns_dp_rm() { return 0 fi - record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \") + record_id=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2) _debug record_id "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id." From 227547f8263a87d9241a8f0a5de84ded1c6aa3d3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 30 Jan 2019 20:13:23 +0800 Subject: [PATCH 217/403] fix https://github.com/Neilpang/acme.sh/pull/1979 --- dnsapi/dns_linode_v4.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_linode_v4.sh b/dnsapi/dns_linode_v4.sh index dfa1a651..c9a83c77 100755 --- a/dnsapi/dns_linode_v4.sh +++ b/dnsapi/dns_linode_v4.sh @@ -8,7 +8,7 @@ LINODE_V4_API_URL="https://api.linode.com/v4/domains" ######## Public functions ##################### #Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_linode_add() { +dns_linode_v4_add() { fulldomain="${1}" txtvalue="${2}" @@ -51,7 +51,7 @@ dns_linode_add() { } #Usage: dns_linode_rm _acme-challenge.www.domain.com -dns_linode_rm() { +dns_linode_v4_rm() { fulldomain="${1}" if ! _Linode_API; then From 759b75ca482db36b6862dc5ba181c4230893deb7 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Mon, 4 Feb 2019 11:27:04 +0100 Subject: [PATCH 218/403] better parsing of json responses fixes an error if customer does not have access to dns-groups --- dnsapi/dns_hostingde.sh | 50 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index b61acb7a..4a7a2141 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -13,6 +13,7 @@ dns_hostingde_add() { txtvalue="${2}" _debug "Calling: _hostingde_addRecord() '${fulldomain}' '${txtvalue}'" _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_addRecord + return $? } dns_hostingde_rm() { @@ -20,6 +21,7 @@ dns_hostingde_rm() { txtvalue="${2}" _debug "Calling: _hostingde_removeRecord() '${fulldomain}' '${txtvalue}'" _hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_removeRecord + return $? } #################### own Private functions below ################################## @@ -38,6 +40,18 @@ _hostingde_apiKey() { _saveaccountconf_mutable HOSTINGDE_ENDPOINT "$HOSTINGDE_ENDPOINT" } +_hostingde_parse() { + find="${1}" + if [ "${2}" ]; then + notfind="${2}" + fi + if [ "${notfind}" ]; then + _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d '[:space:]' + else + _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d '[:space:]' + fi +} + _hostingde_getZoneConfig() { _info "Getting ZoneConfig" curZone="${fulldomain#*.}" @@ -59,18 +73,18 @@ _hostingde_getZoneConfig() { if _contains "${curResult}" '"totalEntries": 1'; then _info "Retrieved zone data." _debug "Zone data: '${curResult}'" - zoneConfigId=$(echo "${curResult}" | _egrep_o '"id":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - zoneConfigName=$(echo "${curResult}" | _egrep_o '"name":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - zoneConfigType=$(echo "${curResult}" | grep -v "FindZoneConfigsResult" | _egrep_o '"type":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - zoneConfigExpire=$(echo "${curResult}" | _egrep_o '"expire":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) - zoneConfigNegativeTtl=$(echo "${curResult}" | _egrep_o '"negativeTtl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) - zoneConfigRefresh=$(echo "${curResult}" | _egrep_o '"refresh":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) - zoneConfigRetry=$(echo "${curResult}" | _egrep_o '"retry":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) - zoneConfigTtl=$(echo "${curResult}" | _egrep_o '"ttl":.*' | cut -d ':' -f 2 | cut -d '"' -f 2 | cut -d ',' -f 1) - zoneConfigDnsServerGroupId=$(echo "${curResult}" | _egrep_o '"dnsServerGroupId":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - zoneConfigEmailAddress=$(echo "${curResult}" | _egrep_o '"emailAddress":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - zoneConfigDnsSecMode=$(echo "${curResult}" | _egrep_o '"dnsSecMode":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) - if [ "${zoneConfigType}" != "NATIVE" ]; then + zoneConfigId=$(echo "${curResult}" | _hostingde_parse "id") + zoneConfigName=$(echo "${curResult}" | _hostingde_parse "name") + zoneConfigType=$(echo "${curResult}" | _hostingde_parse "type" "FindZoneConfigsResult") + zoneConfigExpire=$(echo "${curResult}" | _hostingde_parse "expire") + zoneConfigNegativeTtl=$(echo "${curResult}" | _hostingde_parse "negativeTtl") + zoneConfigRefresh=$(echo "${curResult}" | _hostingde_parse "refresh") + zoneConfigRetry=$(echo "${curResult}" | _hostingde_parse "retry") + zoneConfigTtl=$(echo "${curResult}" | _hostingde_parse "ttl") + zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") + zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") + zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") + if [ ${zoneConfigType} != "\"NATIVE\"" ]; then _err "Zone is not native" returnCode=1 break @@ -89,11 +103,11 @@ _hostingde_getZoneConfig() { _hostingde_getZoneStatus() { _debug "Checking Zone status" - curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":\"${zoneConfigId}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}" + curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":${zoneConfigId}},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind")" _debug "Calling zonesFind '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind'" _debug "Result of zonesFind '$curResult'" - zoneStatus=$(echo "${curResult}" | grep -v success | _egrep_o '"status":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + zoneStatus=$(echo "${curResult}" | _hostingde_parse "status" "success") _debug "zoneStatus '${zoneStatus}'" return 0 } @@ -102,12 +116,12 @@ _hostingde_addRecord() { _info "Adding record to zone" _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" - while [ "${zoneStatus}" != "active" ]; do + while [ "${zoneStatus}" != "\"active\"" ]; do _sleep 5 _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\",\"name\":\"${zoneConfigName}\",\"type\":\"${zoneConfigType}\",\"dnsServerGroupId\":\"${zoneConfigDnsServerGroupId}\",\"dnsSecMode\":\"${zoneConfigDnsSecMode}\",\"emailAddress\":\"${zoneConfigEmailAddress}\",\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" @@ -126,12 +140,12 @@ _hostingde_removeRecord() { _info "Removing record from zone" _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" - while [ "$zoneStatus" != "active" ]; do + while [ "$zoneStatus" != "\"active\"" ]; do _sleep 5 _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":\"${zoneConfigId}\",\"name\":\"${zoneConfigName}\",\"type\":\"${zoneConfigType}\",\"dnsServerGroupId\":\"${zoneConfigDnsServerGroupId}\",\"dnsSecMode\":\"${zoneConfigDnsSecMode}\",\"emailAddress\":\"${zoneConfigEmailAddress}\",\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" From 4eda39a31d7a87ff3d741f39477206fa33554110 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Mon, 4 Feb 2019 15:40:45 +0100 Subject: [PATCH 219/403] making shellcheck happy --- dnsapi/dns_hostingde.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 4a7a2141..56eeec78 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -84,7 +84,7 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") - if [ ${zoneConfigType} != "\"NATIVE\"" ]; then + if [ "${zoneConfigType}" != "\"NATIVE\"" ]; then _err "Zone is not native" returnCode=1 break From 1167cdcaec8e21dfd68bd2300412a733658b258d Mon Sep 17 00:00:00 2001 From: Sebastiaan Hoogeveen Date: Tue, 5 Feb 2019 16:32:41 +0100 Subject: [PATCH 220/403] Added DNS API support for NederHost (https://www.nederhost.nl/) --- dnsapi/dns_nederhost.sh | 133 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100755 dnsapi/dns_nederhost.sh diff --git a/dnsapi/dns_nederhost.sh b/dnsapi/dns_nederhost.sh new file mode 100755 index 00000000..32357f83 --- /dev/null +++ b/dnsapi/dns_nederhost.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env sh + +#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghjk" + +NederHost_Api="https://api.nederhost.nl/dns/v1" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_nederhost_add() { + fulldomain=$1 + txtvalue=$2 + + NederHost_Key="${NederHost_Key:-$(_readaccountconf_mutable NederHost_Key)}" + if [ -z "$NederHost_Key" ]; then + NederHost_Key="" + _err "You didn't specify a NederHost api key." + _err "You can get yours from https://www.nederhost.nl/mijn_nederhost" + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable NederHost_Key "$NederHost_Key" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _info "Adding record" + if _nederhost_rest PATCH "zones/$_domain/records/$fulldomain/TXT" "[{\"content\":\"$txtvalue\",\"ttl\":60}]"; then + if _contains "$response" "$fulldomain"; then + _info "Added, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + return 1 + +} + +#fulldomain txtvalue +dns_nederhost_rm() { + fulldomain=$1 + txtvalue=$2 + + NederHost_Key="${NederHost_Key:-$(_readaccountconf_mutable NederHost_Key)}" + if [ -z "$NederHost_Key" ]; then + NederHost_Key="" + _err "You didn't specify a NederHost api key." + _err "You can get yours from https://www.nederhost.nl/mijn_nederhost" + 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 "Removing txt record" + _nederhost_rest DELETE "zones/${_domain}/records/$fulldomain/TXT?content=$txtvalue" + +} + +#################### 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 + while true; do + _domain=$(printf "%s" "$domain" | cut -d . -f $i-100) + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _debug _domain "$_domain" + if [ -z "$_domain" ]; then + #not valid + return 1 + fi + + if _nederhost_rest GET "zones/${_domain}"; then + if [ "${_code}" == "204" ]; then + return 0; + fi + else + return 1; + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_nederhost_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + export _H1="Authorization: Bearer $NederHost_Key" + export _H2="Content-Type: application/json" + + :>$HTTP_HEADER + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")" + else + response="$(_get "$NederHost_Api/$ep")" + fi + + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" + _debug "http response code $_code" + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From b3e3e080a9a18c005384942de6616fe076e8a8d4 Mon Sep 17 00:00:00 2001 From: Sebastiaan Hoogeveen Date: Tue, 5 Feb 2019 16:37:08 +0100 Subject: [PATCH 221/403] Cleaned up some of the comments from shellcheck. --- dnsapi/dns_nederhost.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_nederhost.sh b/dnsapi/dns_nederhost.sh index 32357f83..0058c848 100755 --- a/dnsapi/dns_nederhost.sh +++ b/dnsapi/dns_nederhost.sh @@ -91,7 +91,7 @@ _get_root() { fi if _nederhost_rest GET "zones/${_domain}"; then - if [ "${_code}" == "204" ]; then + if [ "${_code}" = "204" ]; then return 0; fi else @@ -112,7 +112,7 @@ _nederhost_rest() { export _H1="Authorization: Bearer $NederHost_Key" export _H2="Content-Type: application/json" - :>$HTTP_HEADER + :>"$HTTP_HEADER" if [ "$m" != "GET" ]; then _debug data "$data" From 44dcb0d0a9b0b22f88d3d7942b1aac9004a273b7 Mon Sep 17 00:00:00 2001 From: Sebastiaan Hoogeveen Date: Wed, 6 Feb 2019 11:46:47 +0100 Subject: [PATCH 222/403] Make Travis happy; fixed formatting of return statements. --- dnsapi/dns_nederhost.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_nederhost.sh b/dnsapi/dns_nederhost.sh index 0058c848..84c5ecd1 100755 --- a/dnsapi/dns_nederhost.sh +++ b/dnsapi/dns_nederhost.sh @@ -92,10 +92,10 @@ _get_root() { if _nederhost_rest GET "zones/${_domain}"; then if [ "${_code}" = "204" ]; then - return 0; + return 0 fi else - return 1; + return 1 fi p=$i i=$(_math "$i" + 1) From b7e92dbcedf358a2234ed6567662b71bb5ee2953 Mon Sep 17 00:00:00 2001 From: Sebastiaan Hoogeveen Date: Wed, 6 Feb 2019 14:27:26 +0100 Subject: [PATCH 223/403] Documentation update. --- README.md | 1 + dnsapi/README.md | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/README.md b/README.md index 90a648d5..4bde4ea4 100644 --- a/README.md +++ b/README.md @@ -351,6 +351,7 @@ You don't have to do anything manually! 1. PointDNS API (https://pointhq.com/) 1. Active24.cz API (https://www.active24.cz/) 1. do.de API (https://www.do.de/) +1. NederHost API (https://www.nederhost.nl/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 4f9b4100..a2bf0c18 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1172,6 +1172,20 @@ acme.sh --issue --dns dns_doapi -d example.com -d *.example.com The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 61. Use NederHost API + +Create an API token in Mijn NederHost. + +Set your API key: +``` +export NederHost_Key='xxx' +``` + +To issue a certificate run: +``` +acme.sh --issue --dns dns_nederhost -d example.com -d *.example.com +``` + # Use custom API If your API is not supported yet, you can write your own DNS API. From 84d80e93bcd9dcef9183658a5af4fc47efa8758f Mon Sep 17 00:00:00 2001 From: Frank Laszlo Date: Wed, 6 Feb 2019 10:42:11 -0500 Subject: [PATCH 224/403] Add support for Thermo, Nexcess, and Futurehosting DNS APIs --- README.md | 9 +- dnsapi/README.md | 62 +++++++++++++- dnsapi/dns_nw.sh | 211 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 dnsapi/dns_nw.sh diff --git a/README.md b/README.md index 90a648d5..65b83e71 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,7 @@ 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. +**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.** @@ -277,7 +277,7 @@ So, the config is not changed. acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` -**This nginx mode is only to issue the cert, it will not change your nginx 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 nginx server, don't worry.** @@ -351,6 +351,9 @@ You don't have to do anything manually! 1. PointDNS API (https://pointhq.com/) 1. Active24.cz API (https://www.active24.cz/) 1. do.de API (https://www.do.de/) +1. Nexcess API (https://www.nexcess.net) +1. Thermo.io API (https://www.thermo.io) +1. Futurehosting API (https://www.futurehosting.com) And: @@ -528,5 +531,5 @@ Please Star and Fork me. Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) - + [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) diff --git a/dnsapi/README.md b/dnsapi/README.md index 4f9b4100..a9b78ef8 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1,6 +1,6 @@ # How to use DNS API -If your dns provider doesn't provide api access, you can use our dns alias mode: +If your dns provider doesn't provide api access, you can use our dns alias mode: https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode @@ -891,7 +891,7 @@ acme.sh --issue --dns dns_loopia -d example.com -d *.example.com The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. ## 45. Use ACME DNS API -ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. +ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. https://github.com/joohoi/acme-dns ``` @@ -1011,7 +1011,6 @@ acme.sh --issue --dns dns_netcup -d example.com -d www.example.com ``` The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - ## 52. Use GratisDNS.dk GratisDNS.dk (https://gratisdns.dk/) does not provide an API to update DNS records (other than IPv4 and IPv6 @@ -1172,6 +1171,63 @@ acme.sh --issue --dns dns_doapi -d example.com -d *.example.com The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 61. Use Nexcess API + +First, you'll need to login to the [Nexcess.net Client Portal](https://portal.nexcess.net) and [generate a new API token](https://portal.nexcess.net/api-token). + +Once you have a token, set it in your systems environment: + +``` +export NW_API_TOKEN="YOUR_TOKEN_HERE" +export NW_API_ENDPOINT="https://portal.nexcess.net" +``` + +Finally, we'll issue the certificate: (Nexcess DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`) + +``` +acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 +``` + +The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +## 62. Use Thermo.io API + +First, you'll need to login to the [Thermo.io Client Portal](https://core.thermo.io) and [generate a new API token](https://core.thermo.io/api-token). + +Once you have a token, set it in your systems environment: + +``` +export NW_API_TOKEN="YOUR_TOKEN_HERE" +export NW_API_ENDPOINT="https://core.thermo.io" +``` + +Finally, we'll issue the certificate: (Thermo DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`) + +``` +acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 +``` + +The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +## 63. Use Futurehosting API + +First, you'll need to login to the [Futurehosting Client Portal](https://my.futurehosting.com) and [generate a new API token](https://my.futurehosting.com/api-token). + +Once you have a token, set it in your systems environment: + +``` +export NW_API_TOKEN="YOUR_TOKEN_HERE" +export NW_API_ENDPOINT="https://my.futurehosting.com" +``` + +Finally, we'll issue the certificate: (Futurehosting DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`) + +``` +acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 +``` + +The `NW_API_TOKEN` and `NW_API_ENDPOINT` 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_nw.sh b/dnsapi/dns_nw.sh new file mode 100644 index 00000000..c57d27c2 --- /dev/null +++ b/dnsapi/dns_nw.sh @@ -0,0 +1,211 @@ +#!/usr/bin/env sh +######################################################################## +# NocWorx script for acme.sh +# +# Handles DNS Updates for the Following vendors: +# - Nexcess.net +# - Thermo.io +# - Futurehosting.com +# +# Environment variables: +# +# - NW_API_TOKEN (Your API Token) +# - NW_API_ENDPOINT (One of the following listed below) +# +# Endpoints: +# - https://portal.nexcess.net (default) +# - https://core.thermo.io +# - https://my.futurehosting.com +# +# Note: If you do not have an API token, one can be generated at one +# of the following URLs: +# - https://portal.nexcess.net/api-token +# - https://core.thermo.io/api-token +# - https://my.futurehosting.com/api-token +# +# Author: Frank Laszlo + +NW_API_VERSION="0" + +# dns_nw_add() - Add TXT record +# Usage: dns_nw_add _acme-challenge.subdomain.domain.com "XyZ123..." +dns_nw_add() { + host="${1}" + txtvalue="${2}" + + _debug host "${host}" + _debug txtvalue "${txtvalue}" + + if ! _check_nw_api_creds; then + return 1 + fi + + _info "Using NocWorx (${NW_API_ENDPOINT})" + _debug "Calling: dns_nw_add() '${host}' '${txtvalue}'" + + _debug "Detecting root zone" + if ! _get_root "${host}"; then + _err "Zone for domain does not exist." + return 1 + fi + _debug _zone_id "${_zone_id}" + _debug _sub_domain "${_sub_domain}" + _debug _domain "${_domain}" + + _post_data="{\"zone_id\": \"${_zone_id}\", \"type\": \"TXT\", \"host\": \"${host}\", \"target\": \"${txtvalue}\", \"ttl\": \"300\"}" + + if _rest POST "dns-record" "${_post_data}" && [ -n "${response}" ]; then + _record_id=$(printf "%s\n" "${response}" | _egrep_o "\"record_id\": *[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1) + _debug _record_id "${_record_id}" + + if [ -z "$_record_id" ]; then + _err "Error adding the TXT record." + return 1 + fi + + _info "TXT record successfully added." + return 0 + fi + + return 1 +} + +# dns_nw_rm() - Remove TXT record +# Usage: dns_nw_rm _acme-challenge.subdomain.domain.com "XyZ123..." +dns_nw_rm() { + host="${1}" + txtvalue="${2}" + + _debug host "${host}" + _debug txtvalue "${txtvalue}" + + if ! _check_nw_api_creds; then + return 1 + fi + + _info "Using NocWorx (${NW_API_ENDPOINT})" + _debug "Calling: dns_nw_rm() '${host}'" + + _debug "Detecting root zone" + if ! _get_root "${host}"; then + _err "Zone for domain does not exist." + return 1 + fi + _debug _zone_id "${_zone_id}" + _debug _sub_domain "${_sub_domain}" + _debug _domain "${_domain}" + + _parameters="?zone_id=${_zone_id}" + + if _rest GET "dns-record" "${_parameters}" && [ -n "${response}" ]; then + response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"record_id":/|"record_id":/g' | sed 's/|/&{/g' | tr "|" "\n")" + _debug response "${response}" + + record="$(echo "${response}" | _egrep_o "{.*\"host\": *\"${_sub_domain}\", *\"target\": *\"${txtvalue}\".*}")" + _debug record "${record}" + + if [ "${record}" ]; then + _record_id=$(printf "%s\n" "${record}" | _egrep_o "\"record_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + if [ "${_record_id}" ]; then + _debug _record_id "${_record_id}" + + _rest DELETE "dns-record/${_record_id}" + + _info "TXT record successfully deleted." + return 0 + fi + + return 1 + fi + + return 0 + fi + + return 1 +} + +_check_nw_api_creds() { + NW_API_TOKEN="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}" + NW_API_ENDPOINT="${NW_API_ENDPOINT:-$(_readaccountconf_mutable NW_API_ENDPOINT)}" + + if [ -z "${NW_API_ENDPOINT}" ]; then + NW_API_ENDPOINT="https://portal.nexcess.net" + fi + + if [ -z "${NW_API_TOKEN}" ]; then + _err "You have not defined your NW_API_TOKEN." + _err "Please create your token and try again." + _err "If you need to generate a new token, please visit one of the following URLs:" + _err " - https://portal.nexcess.net/api-token" + _err " - https://core.thermo.io/api-token" + _err " - https://my.futurehosting.com/api-token" + + return 1 + fi + + _saveaccountconf_mutable NW_API_TOKEN "${NW_API_TOKEN}" + _saveaccountconf_mutable NW_API_ENDPOINT "${NW_API_ENDPOINT}" +} + +_get_root() { + domain="${1}" + i=2 + p=1 + + if _rest GET "dns-zone"; then + response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"zone_id":/|"zone_id":/g' | sed 's/|/&{/g' | tr "|" "\n")" + + _debug response "${response}" + while true; do + h=$(printf "%s" "${domain}" | cut -d . -f $i-100) + _debug h "${h}" + if [ -z "${h}" ]; then + #not valid + return 1 + fi + + hostedzone="$(echo "${response}" | _egrep_o "{.*\"domain\": *\"${h}\".*}")" + if [ "${hostedzone}" ]; then + _zone_id=$(printf "%s\n" "${hostedzone}" | _egrep_o "\"zone_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) + if [ "${_zone_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 + fi + return 1 +} + +_rest() { + method="${1}" + ep="/${2}" + data="${3}" + + _debug method "${method}" + _debug ep "${ep}" + + export _H1="Accept: application/json" + export _H2="Content-Type: application/json" + export _H3="Api-Version: ${NW_API_VERSION}" + export _H4="User-Agent: NW-ACME-CLIENT" + export _H5="Authorization: Bearer ${NW_API_TOKEN}" + + if [ "${method}" != "GET" ]; then + _debug data "${data}" + response="$(_post "${data}" "${NW_API_ENDPOINT}${ep}" "" "${method}")" + else + response="$(_get "${NW_API_ENDPOINT}${ep}${data}")" + fi + + if [ "${?}" != "0" ]; then + _err "error ${ep}" + return 1 + fi + _debug2 response "${response}" + return 0 +} From ebc90f6ab831ab2f35e3c7411bcba41a366583d2 Mon Sep 17 00:00:00 2001 From: Simon Wydooghe Date: Wed, 6 Feb 2019 17:42:50 +0100 Subject: [PATCH 225/403] Set NS1 DNS record TTL to 0 Default of a zone might be high, which is annoying when testing with the ACME staging API. I think setting the TTL to 0 makes sense as acme.sh is the only one checking this, so having an always up to date response seems desirable. --- dnsapi/dns_nsone.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_nsone.sh b/dnsapi/dns_nsone.sh index 00e186d2..9a998341 100644 --- a/dnsapi/dns_nsone.sh +++ b/dnsapi/dns_nsone.sh @@ -46,7 +46,7 @@ dns_nsone_add() { if [ "$count" = "0" ]; then _info "Adding record" - if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; then + if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\",\"ttl\":0}"; then if _contains "$response" "$fulldomain"; then _info "Added" #todo: check if the record takes effect @@ -62,7 +62,7 @@ dns_nsone_add() { prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1) _debug "prev_txt" "$prev_txt" - _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}" + _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\",\"ttl\":0}" if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then _info "Updated!" #todo: check if the record takes effect From 2cf01c23a2b1f09a317e58aa99f8c9fbedb7146d Mon Sep 17 00:00:00 2001 From: Christian Burmeister Date: Sat, 9 Feb 2019 19:38:32 +0100 Subject: [PATCH 226/403] Update Dockerfile --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c1a2199b..68385d7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.6 +FROM alpine:3.9 RUN apk update -f \ && apk --no-cache add -f \ @@ -7,6 +7,7 @@ RUN apk update -f \ bind-tools \ curl \ socat \ + tzdata \ && rm -rf /var/cache/apk/* ENV LE_CONFIG_HOME /acme.sh From 1fa026b9c7315128f60f6a1e9137f44aa01d60bf Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Mon, 11 Feb 2019 11:47:48 +0100 Subject: [PATCH 227/403] using ' ' instead of '[:space:]' for tr --- dnsapi/dns_hostingde.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 56eeec78..50aa142f 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -46,9 +46,9 @@ _hostingde_parse() { notfind="${2}" fi if [ "${notfind}" ]; then - _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d '[:space:]' + _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d ' ' else - _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d '[:space:]' + _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d ' ' fi } From d30b441ede3d2907c0645cf4dea78d740c2a6f08 Mon Sep 17 00:00:00 2001 From: Tom Cocca Date: Wed, 2 Jan 2019 00:18:25 -0600 Subject: [PATCH 228/403] Rackspace Cloud DNS Support Rackspace Cloud DNS This commit is based on the original pull request by tcocca https://github.com/Neilpang/acme.sh/pull/1297 Addtional cleanup was provided by senseisimple in https://github.com/Neilpang/acme.sh/pull/1999 This pull request has squashed the changes for review, fixed a minor (but breaking) problem with the field ordering in the response, and added documenation per the API guide. Co-Author: Chris Co-Author: Ian Wienand --- README.md | 1 + dnsapi/README.md | 15 +++ dnsapi/dns_rackspace.sh | 207 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 dnsapi/dns_rackspace.sh diff --git a/README.md b/README.md index 65b83e71..793df06a 100644 --- a/README.md +++ b/README.md @@ -354,6 +354,7 @@ You don't have to do anything manually! 1. Nexcess API (https://www.nexcess.net) 1. Thermo.io API (https://www.thermo.io) 1. Futurehosting API (https://www.futurehosting.com) +1. Rackspace Cloud DNS (https://www.rackspace.com) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index a9b78ef8..c136ed35 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1228,6 +1228,21 @@ acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 64. Use Rackspace API + +Set username and API key, which is available under "My Profile & Settings" + +``` +export RACKSPACE_Username='username' +export RACKSPACE_Apikey='xxx' +``` + +Now, let's issue a cert: + +``` +acme.sh --issue --dns dns_rackspace -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_rackspace.sh b/dnsapi/dns_rackspace.sh new file mode 100644 index 00000000..3939fd81 --- /dev/null +++ b/dnsapi/dns_rackspace.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env sh +# +# +#RACKSPACE_Username="" +# +#RACKSPACE_Apikey="" + +RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0" + +# 20190213 - The name & id fields swapped in the API response; fix sed +# 20190101 - Duplicating file for new pull request to dev branch +# Original - tcocca:rackspace_dnsapi https://github.com/Neilpang/acme.sh/pull/1297 + +######## Public functions ##################### +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_rackspace_add() { + fulldomain="$1" + _debug fulldomain="$fulldomain" + txtvalue="$2" + _debug txtvalue="$txtvalue" + _rackspace_check_auth || return 1 + _rackspace_check_rootzone || return 1 + _info "Creating TXT record." + if ! _rackspace_rest POST "$RACKSPACE_Tenant/domains/$_domain_id/records" "{\"records\":[{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":300}]}"; then + return 1 + fi + _debug2 response "$response" + if ! _contains "$response" "$txtvalue" >/dev/null; then + _err "Could not add TXT record." + return 1 + fi + return 0 +} + +#fulldomain txtvalue +dns_rackspace_rm() { + fulldomain=$1 + _debug fulldomain="$fulldomain" + txtvalue=$2 + _debug txtvalue="$txtvalue" + _rackspace_check_auth || return 1 + _rackspace_check_rootzone || return 1 + _info "Checking for TXT record." + if ! _get_recordid "$_domain_id" "$fulldomain" "$txtvalue"; then + _err "Could not get TXT record id." + return 1 + fi + if [ "$_dns_record_id" = "" ]; then + _err "TXT record not found." + return 1 + fi + _info "Removing TXT record." + if ! _delete_txt_record "$_domain_id" "$_dns_record_id"; then + _err "Could not remove TXT record $_dns_record_id." + 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_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 + if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains"; then + return 1 + fi + _debug2 response "$response" + if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + # Response looks like: + # {"ttl":300,"accountId":12345,"id":1111111,"name":"example.com","emailAddress": ... + _domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\([^,]*\),\"name\":\"$h\",.*/\1/p") + _debug2 domain_id "$_domain_id" + if [ -n "$_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 +} + +_get_recordid() { + domainid="$1" + fulldomain="$2" + txtvalue="$3" + if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains/$domainid/records?name=$fulldomain&type=TXT"; then + return 1 + fi + _debug response "$response" + if ! _contains "$response" "$txtvalue"; then + _dns_record_id=0 + return 0 + fi + _dns_record_id=$(echo "$response" | tr '{' "\n" | grep "\"data\":\"$txtvalue\"" | sed -n 's/^.*"id":"\([^"]*\)".*/\1/p') + _debug _dns_record_id "$_dns_record_id" + return 0 +} + +_delete_txt_record() { + domainid="$1" + _dns_record_id="$2" + if ! _rackspace_rest DELETE "$RACKSPACE_Tenant/domains/$domainid/records?id=$_dns_record_id"; then + return 1 + fi + _debug response "$response" + if ! _contains "$response" "RUNNING"; then + return 1 + fi + return 0 +} + +_rackspace_rest() { + m="$1" + ep="$2" + data="$3" + _debug ep "$ep" + export _H1="Accept: application/json" + export _H2="X-Auth-Token: $RACKSPACE_Token" + export _H3="X-Project-Id: $RACKSPACE_Tenant" + export _H4="Content-Type: application/json" + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$RACKSPACE_Endpoint/$ep" "" "$m")" + retcode=$? + else + _info "Getting $RACKSPACE_Endpoint/$ep" + response="$(_get "$RACKSPACE_Endpoint/$ep")" + retcode=$? + fi + + if [ "$retcode" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} + +_rackspace_authorization() { + export _H1="Content-Type: application/json" + data="{\"auth\":{\"RAX-KSKEY:apiKeyCredentials\":{\"username\":\"$RACKSPACE_Username\",\"apiKey\":\"$RACKSPACE_Apikey\"}}}" + _debug data "$data" + response="$(_post "$data" "https://identity.api.rackspacecloud.com/v2.0/tokens" "" "POST")" + retcode=$? + _debug2 response "$response" + if [ "$retcode" != "0" ]; then + _err "Authentication failed." + return 1 + fi + if _contains "$response" "token"; then + RACKSPACE_Token="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)",".*/\1/p')" + RACKSPACE_Tenant="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)"}.*/\1/p')" + _debug RACKSPACE_Token "$RACKSPACE_Token" + _debug RACKSPACE_Tenant "$RACKSPACE_Tenant" + fi + return 0 +} + +_rackspace_check_auth() { + # retrieve the rackspace creds + RACKSPACE_Username="${RACKSPACE_Username:-$(_readaccountconf_mutable RACKSPACE_Username)}" + RACKSPACE_Apikey="${RACKSPACE_Apikey:-$(_readaccountconf_mutable RACKSPACE_Apikey)}" + # check their vals for null + if [ -z "$RACKSPACE_Username" ] || [ -z "$RACKSPACE_Apikey" ]; then + RACKSPACE_Username="" + RACKSPACE_Apikey="" + _err "You didn't specify a Rackspace username and api key." + _err "Please set those values and try again." + return 1 + fi + # save the username and api key to the account conf file. + _saveaccountconf_mutable RACKSPACE_Username "$RACKSPACE_Username" + _saveaccountconf_mutable RACKSPACE_Apikey "$RACKSPACE_Apikey" + if [ -z "$RACKSPACE_Token" ]; then + _info "Getting authorization token." + if ! _rackspace_authorization; then + _err "Can not get token." + fi + fi +} + +_rackspace_check_rootzone() { + _debug "First detect the root zone" + if ! _get_root_zone "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" +} From ec5fad433c3cdfb8b9d64ed8197ed445297adc1c Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Wed, 13 Feb 2019 23:33:54 +0100 Subject: [PATCH 229/403] Add online.net DNS API --- README.md | 1 + dnsapi/README.md | 16 ++++ dnsapi/dns_online.sh | 214 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100755 dnsapi/dns_online.sh diff --git a/README.md b/README.md index 793df06a..8d749dcc 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,7 @@ You don't have to do anything manually! 1. Thermo.io API (https://www.thermo.io) 1. Futurehosting API (https://www.futurehosting.com) 1. Rackspace Cloud DNS (https://www.rackspace.com) +1. Online.net API (https://online.net/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index c136ed35..f022cab0 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1243,6 +1243,22 @@ Now, let's issue a cert: acme.sh --issue --dns dns_rackspace -d example.com -d www.example.com ``` +## 65. Use Online API + +First, you'll need to retrive your API key, which is available under https://console.online.net/en/api/access + +``` +export ONLINE_API_KEY='xxx' +``` + +To issue a cert run: + +``` +acme.sh --issue --dns dns_online -d example.com -d www.example.com +``` + +`ONLINE_API_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_online.sh b/dnsapi/dns_online.sh new file mode 100755 index 00000000..02d07dcd --- /dev/null +++ b/dnsapi/dns_online.sh @@ -0,0 +1,214 @@ +#!/usr/bin/env sh + +# Online API +# https://console.online.net/en/api/ +# +# Requires Online API key set in ONLINE_API_KEY + +######## Public functions ##################### + +ONLINE_API="https://api.online.net/api/v1" + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_online_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _online_check_config; 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 _real_dns_version "$_real_dns_version" + + _info "Creating temporary zone version" + _online_create_temporary_zone_version + _info "Enabling temporary zone version" + _online_enable_zone "$_temporary_dns_version" + + _info "Adding record" + _online_create_TXT_record "$_real_dns_version" "$_sub_domain" "$txtvalue" + _info "Disabling temporary version" + _online_enable_zone "$_real_dns_version" + _info "Destroying temporary version" + _online_destroy_zone "$_temporary_dns_version" + + _info "Record added." + return 0 +} + +#fulldomain +dns_online_rm() { + fulldomain=$1 + txtvalue=$2 + + if ! _online_check_config; 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 _real_dns_version "$_real_dns_version" + + _debug "Getting txt records" + if ! _online_rest GET "domain/$_domain/version/active"; then + return 1 + fi + + rid=$(echo "$response" | _egrep_o "\"id\":[0-9]+,\"name\":\"$_sub_domain\",\"data\":\"\\\u0022$txtvalue\\\u0022\"" | cut -d ':' -f 2 | cut -d ',' -f 1) + _debug rid "$rid" + if [ -z "$rid" ]; then + return 1 + fi + + _info "Creating temporary zone version" + _online_create_temporary_zone_version + _info "Enabling temporary zone version" + _online_enable_zone "$_temporary_dns_version" + + _info "Removing DNS record" + _online_rest DELETE "domain/$_domain/version/$_real_dns_version/zone/$rid" + _info "Disabling temporary version" + _online_enable_zone "$_real_dns_version" + _info "Destroying temporary version" + _online_destroy_zone "$_temporary_dns_version" + + return 0 +} + +#################### Private functions below ################################## + +_online_check_config() { + + if [ -z "$ONLINE_API_KEY" ]; then + _err "No API key specified for Online API." + _err "Create your key and export it as ONLINE_API_KEY" + return 1 + fi + + _saveaccountconf ONLINE_API_KEY "$ONLINE_API_KEY" + + return 0 +} + +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + if [ -z "$h" ]; then + #not valid + return 1 + fi + if ! _online_rest GET "domain/$h/version/active"; then + _err "Unable to retrive DNS zone matching this domain" + return 1 + fi + + if ! _contains "$response" "Domain not found" >/dev/null; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$h" + _real_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +# this function create a temporary zone version +# as online.net does not allow updating an active version +_online_create_temporary_zone_version() { + + _online_rest POST "domain/$_domain/version" "name=acme.sh" + if [ "$?" != "0" ]; then + return 1 + fi + + _temporary_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2) + + # Creating a dummy record in this temporary version, because online.net doesn't accept enabling an empty version + _online_create_TXT_record "$_temporary_dns_version" "dummy.acme.sh" "dummy" + + return 0 +} + +_online_destroy_zone() { + version_id=$1 + _online_rest DELETE "domain/$_domain/version/$version_id" + + if [ "$?" != "0" ]; then + return 1 + fi + return 0 +} + +_online_enable_zone() { + version_id=$1 + _online_rest PATCH "domain/$_domain/version/$version_id/enable" + + if [ "$?" != "0" ]; then + return 1 + fi + return 0 +} + +_online_create_TXT_record() { + version=$1 + txt_name=$2 + txt_value=$3 + + _online_rest POST "domain/$_domain/version/$version/zone" "type=TXT&name=$txt_name&data=%22$txt_value%22&ttl=60&priority=0" + + # Note : the normal, expected response SHOULD be "Unknown method". + # this happens because the API HTTP response contains a Location: header, that redirect + # to an unknown online.net endpoint. + if [ "$?" != "0" ] || _contains "$response" "Unknown method"; then + return 0 + else + _err "error $response" + return 1 + fi +} + +_online_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + _online_url="$ONLINE_API/$ep" + _debug2 _online_url "$_online_url" + export _H1="Authorization: Bearer $ONLINE_API_KEY" + export _H2="X-Pretty-JSON: 1" + if [ "$data" ] || [ "$m" = "PATCH" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then + _debug data "$data" + response="$(_post "$data" "$_online_url" "" "$m")" + else + response="$(_get "$_online_url")" + fi + if [ "$?" != "0" ] || _contains "$response" "invalid_grant" || _contains "$response" "Method not allowed"; then + _err "error $response" + return 1 + fi + _debug2 response "$response" + return 0 +} From 02f6d4cb66c3837490295379a59c67936dcb0b90 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 07:56:13 +0000 Subject: [PATCH 230/403] use read/saveconf_mutable, not readconf from OVH --- dnsapi/dns_online.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index 02d07dcd..c6ee485b 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -92,14 +92,18 @@ dns_online_rm() { #################### Private functions below ################################## _online_check_config() { - + ONLINE_API_KEY="${CF_Key:-$(_readaccountconf_mutable ONLINE_API_KEY)}" if [ -z "$ONLINE_API_KEY" ]; then _err "No API key specified for Online API." _err "Create your key and export it as ONLINE_API_KEY" return 1 fi + if [ ! _online_rest GET "domain/" ]; then + _err "Invalid API key specified for Online API." + return 1 + fi - _saveaccountconf ONLINE_API_KEY "$ONLINE_API_KEY" + _saveaccountconf_mutable ONLINE_API_KEY "$ONLINE_API_KEY" return 0 } From 5c94147603b4d9c3d903c01344bde9751095eddc Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 08:08:10 +0000 Subject: [PATCH 231/403] use read/saveconf_mutable, not readconf from OVH --- dnsapi/dns_online.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index c6ee485b..ee00685b 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -92,13 +92,13 @@ dns_online_rm() { #################### Private functions below ################################## _online_check_config() { - ONLINE_API_KEY="${CF_Key:-$(_readaccountconf_mutable ONLINE_API_KEY)}" + ONLINE_API_KEY="${ONLINE_API_KEY:-$(_readaccountconf_mutable ONLINE_API_KEY)}" if [ -z "$ONLINE_API_KEY" ]; then _err "No API key specified for Online API." _err "Create your key and export it as ONLINE_API_KEY" return 1 fi - if [ ! _online_rest GET "domain/" ]; then + if ! _online_rest GET "domain/"; then _err "Invalid API key specified for Online API." return 1 fi From 841513501a69aab5ae9ec98a9c383df65f1fb8f6 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 07:58:43 +0000 Subject: [PATCH 232/403] update get_root --- dnsapi/dns_online.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index ee00685b..8c5a046b 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -122,10 +122,8 @@ _get_root() { #not valid return 1 fi - if ! _online_rest GET "domain/$h/version/active"; then - _err "Unable to retrive DNS zone matching this domain" - return 1 - fi + + _online_rest GET "domain/$h/version/active" if ! _contains "$response" "Domain not found" >/dev/null; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) @@ -136,7 +134,8 @@ _get_root() { p=$i i=$(_math "$i" + 1) done - return 1 + _err "Unable to retrive DNS zone matching this domain" + return 1 } # this function create a temporary zone version From 9ace7db216cdce631475e3df1eb66e2d14f92489 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 08:03:13 +0000 Subject: [PATCH 233/403] simplify online_rest --- dnsapi/dns_online.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index 8c5a046b..8831f9af 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -202,7 +202,7 @@ _online_rest() { _debug2 _online_url "$_online_url" export _H1="Authorization: Bearer $ONLINE_API_KEY" export _H2="X-Pretty-JSON: 1" - if [ "$data" ] || [ "$m" = "PATCH" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then + if [ "$data" ] || [ "$m" != "GET" ]; then _debug data "$data" response="$(_post "$data" "$_online_url" "" "$m")" else From 63ea3e8d277e8868bcbf5f6a2242a0028a26bb5d Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 08:29:00 +0000 Subject: [PATCH 234/403] acme.sh does not follow Location: headers when using wget --- dnsapi/dns_online.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index 8831f9af..6f4c40d6 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -185,7 +185,7 @@ _online_create_TXT_record() { # Note : the normal, expected response SHOULD be "Unknown method". # this happens because the API HTTP response contains a Location: header, that redirect # to an unknown online.net endpoint. - if [ "$?" != "0" ] || _contains "$response" "Unknown method"; then + if [ "$?" != "0" ] || _contains "$response" "Unknown method" || _contains "$response" "\$ref"; then return 0 else _err "error $response" From 1ad6742dbc0e0bc9df869afbcbc67959d91452a0 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 08:43:07 +0000 Subject: [PATCH 235/403] fix travis --- dnsapi/dns_online.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index 6f4c40d6..0d1fca2a 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -134,7 +134,7 @@ _get_root() { p=$i i=$(_math "$i" + 1) done - _err "Unable to retrive DNS zone matching this domain" + _err "Unable to retrive DNS zone matching this domain" return 1 } From ec6569fbea21bb9eef2397cdcfb66b202cea9671 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 15 Feb 2019 08:56:09 +0000 Subject: [PATCH 236/403] fix travis --- dnsapi/dns_online.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_online.sh b/dnsapi/dns_online.sh index 0d1fca2a..9158c268 100755 --- a/dnsapi/dns_online.sh +++ b/dnsapi/dns_online.sh @@ -135,7 +135,7 @@ _get_root() { i=$(_math "$i" + 1) done _err "Unable to retrive DNS zone matching this domain" - return 1 + return 1 } # this function create a temporary zone version From f2acdd27fd0f8d0407058ad05b12137197d99afc Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 17 Feb 2019 14:19:14 +0800 Subject: [PATCH 237/403] fix tr err for Mac --- acme.sh | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/acme.sh b/acme.sh index cfdf5714..82c5e502 100755 --- a/acme.sh +++ b/acme.sh @@ -1882,29 +1882,34 @@ _send_signed_request() { _err "Can not post to $url" return 1 fi - _debug2 original "$response" - response="$(echo "$response" | _normalizeJson)" responseHeaders="$(cat "$HTTP_HEADER")" - _debug2 responseHeaders "$responseHeaders" - _debug2 response "$response" + code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" _debug code "$code" - _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" - - _body="$response" - if [ "$needbase64" ]; then - _body="$(echo "$_body" | _dbase64 | tr -d '\0')" - _debug3 _body "$_body" + _debug2 original "$response" + if echo "$responseHeaders" | grep -i "Content-Type: application/json" >/dev/null 2>&1; then + response="$(echo "$response" | _normalizeJson)" fi + _debug2 response "$response" - if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then - _info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds." - _CACHED_NONCE="" - _sleep $_sleep_retry_sec - continue + _CACHED_NONCE="$(echo "$responseHeaders" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" + + if ! _startswith "$code" "2"; then + _body="$response" + if [ "$needbase64" ]; then + _body="$(echo "$_body" | _dbase64 multiline)" + _debug3 _body "$_body" + fi + + if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then + _info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds." + _CACHED_NONCE="" + _sleep $_sleep_retry_sec + continue + fi fi break done @@ -4113,14 +4118,14 @@ $_authorizations_map" Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" _tempSignedResponse="$response" - if ! _send_signed_request "$Le_LinkCert" "" "needbase64"; then + if ! _send_signed_request "$Le_LinkCert"; then _err "Sign failed, can not download cert:$Le_LinkCert." _err "$response" _on_issue_err "$_post_hook" return 1 fi - echo "$response" | _dbase64 "multiline" >"$CERT_PATH" + echo "$response" >"$CERT_PATH" if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then _debug "Found cert chain" From a0ec5b18e79bfa21f22634806e80d0659105b35a Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 17 Feb 2019 14:26:27 +0800 Subject: [PATCH 238/403] fx format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 82c5e502..7b094e94 100755 --- a/acme.sh +++ b/acme.sh @@ -1897,7 +1897,7 @@ _send_signed_request() { _CACHED_NONCE="$(echo "$responseHeaders" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" - if ! _startswith "$code" "2"; then + if ! _startswith "$code" "2"; then _body="$response" if [ "$needbase64" ]; then _body="$(echo "$_body" | _dbase64 multiline)" From 9ff6d6e7b5bcaf41ccae97ee29d06223cda67455 Mon Sep 17 00:00:00 2001 From: dsc Date: Sun, 17 Feb 2019 23:20:17 +0100 Subject: [PATCH 239/403] initial commit --- dnsapi/dns_one.sh | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 dnsapi/dns_one.sh diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh new file mode 100644 index 00000000..185669ce --- /dev/null +++ b/dnsapi/dns_one.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env sh +# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- + +# one.com ui wrapper for acme.sh +# Author: github: @diseq +# Created: 2019-02-17 +# +# export ONECOM_USER="username" +# export ONECOM_PASSWORD="password" +# +# Usage: +# acme.sh --issue --dns dns_one -d example.com +# +# only single domain supported atm + +dns_one_add() { + mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) + mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + txtvalue=$2 + + # get credentials + ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" + ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" + if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then + ONECOM_USER="" + ONECOM_PASSWORD="" + _err "You didn't specify a one.com username and password yet." + _err "Please create the key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable ONECOM_USER "$ONECOM_USER" + _saveaccountconf_mutable ONECOM_PASSWORD "$ONECOM_PASSWORD" + + + # Login with user and password + postdata="loginDomain=true" + postdata="$postdata&displayUsername=$ONECOM_USER" + postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&targetDomain=$mydomain" + postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&loginTarget=" + + #_debug postdata "$postdata" + + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST")" + #_debug response "$response" + + JSESSIONID="$(grep "JSESSIONID=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + _debug jsessionid "$JSESSIONID" + + export _H1="Cookie: ${JSESSIONID}" + + + # get entries + response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" + _debug response "$response" + + + CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" + export _H2="Cookie: ${CSRF_G_TOKEN}" + + + # Update the IP address for domain entry + postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}" + _debug postdata "$postdata" + response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records" "" "POST" "application/json")" + response="$(echo "$response" | _normalizeJson)" + _debug response "$response" + + id=$(printf -- "%s" "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") + + if [ -z "$id" ]; then + _err "Add txt record error." + return 1 + else + _info "Added, OK ($id)" + return 0 + fi + +} + +dns_one_rm() { + mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) + mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + txtvalue=$2 + + # get credentials + ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" + ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" + if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then + ONECOM_USER="" + ONECOM_PASSWORD="" + _err "You didn't specify a one.com username and password yet." + _err "Please create the key and try again." + return 1 + fi + + + # Login with user and password + postdata="loginDomain=true" + postdata="$postdata&displayUsername=$ONECOM_USER" + postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&targetDomain=$mydomain" + postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&loginTarget=" + + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST")" + + JSESSIONID="$(grep "JSESSIONID=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + _debug jsessionid "$JSESSIONID" + + export _H1="Cookie: ${JSESSIONID}" + + + # get entries + response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" + response="$(echo "$response" | _normalizeJson)" + _debug response "$response" + + CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" + export _H2="Cookie: ${CSRF_G_TOKEN}" + + id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}.*/\1/p") + + if [ -z "$id" ]; then + _err "Txt record not found." + return 1 + fi + + # delete entry + response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records/$id" "" "DELETE" "application/json")" + response="$(echo "$response" | _normalizeJson)" + _debug response "$response" + + if [ "$response" = '{"result":null,"metadata":null}' ]; + then + _info "Removed, OK" + return 0 + else + _err "Removing txt record error." + return 1 + fi + +} \ No newline at end of file From 97147b594b185786ef1d69ce0d85b70a91f0ccc9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 18 Feb 2019 20:57:13 +0800 Subject: [PATCH 240/403] fix https://github.com/Neilpang/acme.sh/issues/2096 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 7b094e94..5c093e4c 100755 --- a/acme.sh +++ b/acme.sh @@ -1188,7 +1188,7 @@ _ss() { if _exists "netstat"; then _debug "Using: netstat" - if netstat -h 2>&1 | grep "\-p proto" >/dev/null; then + if netstat -help 2>&1 | grep "\-p proto" >/dev/null; then #for windows version netstat tool netstat -an -p tcp | grep "LISTENING" | grep ":$_port " else From b5ca9bbab2a73f11b9336d2ffe10a07add142130 Mon Sep 17 00:00:00 2001 From: neil Date: Tue, 19 Feb 2019 21:39:06 +0800 Subject: [PATCH 241/403] Doh (#2100) support doh to poll dns status fix https://github.com/Neilpang/acme.sh/issues/2015 --- acme.sh | 192 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 48 deletions(-) diff --git a/acme.sh b/acme.sh index 23bc4f6b..93112a1a 100755 --- a/acme.sh +++ b/acme.sh @@ -2929,42 +2929,38 @@ _clearup() { _clearupdns() { _debug "_clearupdns" - _debug "dnsadded" "$dnsadded" - _debug "vlist" "$vlist" - #dnsadded is "0" or "1" means dns-01 method was used for at least one domain - if [ -z "$dnsadded" ] || [ -z "$vlist" ]; then + _debug "dns_entries" "$dns_entries" + + if [ -z "$dns_entries" ]; then _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) - vtype=$(echo "$ventry" | cut -d "$sep" -f 4) - _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) - txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" - _debug txt "$txt" - if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then - _debug "$d is already verified, skip $vtype." - _alias_index="$(_math "$_alias_index" + 1)" - continue - fi - if [ "$vtype" != "$VTYPE_DNS" ]; then - _debug "Skip $d for $vtype" - continue + for entry in $dns_entries; do + d=$(_getfield "$entry" 1) + txtdomain=$(_getfield "$entry" 2) + aliasDomain=$(_getfield "$entry" 3) + txt=$(_getfield "$entry" 5) + d_api=$(_getfield "$entry" 6) + _debug "d" "$d" + _debug "txtdomain" "$txtdomain" + _debug "aliasDomain" "$aliasDomain" + _debug "txt" "$txt" + _debug "d_api" "$d_api" + if [ "$d_api" = "$txt" ]; then + d_api="" fi - d_api="$(_findHook "$d" dnsapi "$_currentRoot")" - _debug d_api "$d_api" - if [ -z "$d_api" ]; then _info "Not Found domain api file: $d_api" continue fi + if [ "$aliasDomain" ]; then + txtdomain="$aliasDomain" + fi + ( if ! . "$d_api"; then _err "Load file $d_api error. Please check your api file and try again." @@ -2977,24 +2973,6 @@ _clearupdns() { return 1 fi - _dns_root_d="$d" - if _startswith "$_dns_root_d" "*."; then - _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" - fi - - _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")" - _alias_index="$(_math "$_alias_index" + 1)" - _debug "_d_alias" "$_d_alias" - if [ "$_d_alias" ]; then - 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 - if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" return 1 @@ -3463,6 +3441,113 @@ __trigger_validation() { fi } +#endpoint domain type +_ns_lookup() { + _ns_ep="$1" + _ns_domain="$2" + _ns_type="$3" + _debug2 "_ns_ep" "$_ns_ep" + _debug2 "_ns_domain" "$_ns_domain" + _debug2 "_ns_type" "$_ns_type" + + response="$(_H1="accept: application/dns-json" _get "$_ns_ep?name=$_ns_domain&type=$_ns_type")" + _ret=$? + _debug2 "response" "$response" + if [ "$_ret" != "0" ]; then + return $_ret + fi + _answers="$(echo "$response" | tr '{}' '<>' | _egrep_o '"Answer":\[[^]]*]' | tr '<>' '\n\n')" + _debug2 "_answers" "$_answers" + echo "$_answers" +} + +#domain, type +_ns_lookup_cf() { + _cf_ld="$1" + _cf_ld_type="$2" + _cf_ep="https://cloudflare-dns.com/dns-query" + _ns_lookup "$_cf_ep" "$_cf_ld" "$_cf_ld_type" +} + +#domain, type +_ns_purge_cf() { + _cf_d="$1" + _cf_d_type="$2" + _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d" + _cf_purl="https://1.1.1.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" + response="$(_post "" "$_cf_purl")" + _debug2 response "$response" +} + +#txtdomain, alias, txt +__check_txt() { + _c_txtdomain="$1" + _c_aliasdomain="$2" + _c_txt="$3" + _debug "_c_txtdomain" "$_c_txtdomain" + _debug "_c_aliasdomain" "$_c_aliasdomain" + _debug "_c_txt" "$_c_txt" + _answers="$(_ns_lookup_cf "$_c_aliasdomain" TXT)" + _contains "$_answers" "$_c_txt" + +} + +#txtdomain +__purge_txt() { + _p_txtdomain="$1" + _debug _p_txtdomain "$_p_txtdomain" + _ns_purge_cf "$_p_txtdomain" "TXT" +} + +#wait and check each dns entries +_check_dns_entries() { + _success_txt="," + _end_time="$(_time)" + _end_time="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes. + + while [ "$(_time)" -le "$_end_time" ]; do + _left="" + for entry in $dns_entries; do + d=$(_getfield "$entry" 1) + txtdomain=$(_getfield "$entry" 2) + aliasDomain=$(_getfield "$entry" 3) + txt=$(_getfield "$entry" 5) + d_api=$(_getfield "$entry" 6) + _debug "d" "$d" + _debug "txtdomain" "$txtdomain" + _debug "aliasDomain" "$aliasDomain" + _debug "txt" "$txt" + _debug "d_api" "$d_api" + _info "Checking $d for $aliasDomain" + if _contains "$_success_txt" ",$txt,"; then + _info "Already success, continue next one." + continue + fi + + if __check_txt "$txtdomain" "$aliasDomain" "$txt"; then + _info "Domain $d '$aliasDomain' success." + _success_txt="$_success_txt,$txt," + continue + fi + _left=1 + _info "Not valid yet, let's wait 10 seconds and check next one." + _sleep 10 + __purge_txt "$txtdomain" + if [ "$txtdomain" != "$aliasDomain" ]; then + __purge_txt "$aliasDomain" + fi + done + if [ "$_left" ]; then + _info "Let's wait 10 seconds and check again". + _sleep 10 + else + _info "All success, let's return" + break + fi + done + +} + #webroot, domain domainlist keylength issue() { if [ -z "$2" ]; then @@ -3786,6 +3871,7 @@ $_authorizations_map" done _debug vlist "$vlist" #add entry + dns_entries="" dnsadded="" ventries=$(echo "$vlist" | tr "$dvsep" ' ') _alias_index=1 @@ -3816,8 +3902,10 @@ $_authorizations_map" else txtdomain="_acme-challenge.$_d_alias" fi + dns_entries="${dns_entries}${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot" else txtdomain="_acme-challenge.$_dns_root_d" + dns_entries="${dns_entries}${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot" fi _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" @@ -3826,7 +3914,9 @@ $_authorizations_map" d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" _debug d_api "$d_api" - + dns_entries="$dns_entries$dvsep$txt${dvsep}$d_api +" + _debug2 "$dns_entries" if [ "$d_api" ]; then _info "Found domain api file: $d_api" else @@ -3880,15 +3970,21 @@ $_authorizations_map" fi - if [ "$dnsadded" = '1' ]; then + if [ "$dns_entries" ]; then if [ -z "$Le_DNSSleep" ]; then - Le_DNSSleep="$DEFAULT_DNS_SLEEP" + _info "Let's check each dns records now. Sleep 20 seconds first." + _sleep 20 + if ! _check_dns_entries; then + _err "check dns error." + _on_issue_err "$_post_hook" + _clearup + return 1 + fi else _savedomainconf "Le_DNSSleep" "$Le_DNSSleep" + _info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect" + _sleep "$Le_DNSSleep" fi - - _info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect" - _sleep "$Le_DNSSleep" fi NGINX_RESTORE_VLIST="" From 16a0f40ac27b85180b55a383f8ceebf3a7cc342f Mon Sep 17 00:00:00 2001 From: Marcin Konicki Date: Wed, 20 Feb 2019 02:40:36 +0100 Subject: [PATCH 242/403] Support for MyDevil.net (#2076) support mydevil --- README.md | 1 + deploy/README.md | 10 +++++ deploy/mydevil.sh | 59 ++++++++++++++++++++++++++ dnsapi/README.md | 20 +++++++++ dnsapi/dns_mydevil.sh | 97 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100755 deploy/mydevil.sh create mode 100755 dnsapi/dns_mydevil.sh diff --git a/README.md b/README.md index 8d749dcc..f79b8602 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,7 @@ You don't have to do anything manually! 1. Futurehosting API (https://www.futurehosting.com) 1. Rackspace Cloud DNS (https://www.rackspace.com) 1. Online.net API (https://online.net/) +1. MyDevil.net (https://www.mydevil.net/) And: diff --git a/deploy/README.md b/deploy/README.md index 091e9feb..f290756a 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -381,3 +381,13 @@ you want to update: $ export QINIU_CDN_DOMAIN="cdn.example.com" $ acme.sh --deploy -d example.com --deploy-hook qiniu ``` + +## 14. Deploy your cert on MyDevil.net + +Once you have acme.sh installed and certificate issued (see info in [DNS API](../dnsapi/README.md#61-use-mydevilnet)), you can install it by following command: + +```sh +acme.sh --deploy --deploy-hook mydevil -d example.com +``` + +That will remove old certificate and install new one. diff --git a/deploy/mydevil.sh b/deploy/mydevil.sh new file mode 100755 index 00000000..bd9868aa --- /dev/null +++ b/deploy/mydevil.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +# MyDevil.net API (2019-02-03) +# +# MyDevil.net already supports automatic Let's Encrypt certificates, +# except for wildcard domains. +# +# This script depends on `devil` command that MyDevil.net provides, +# which means that it works only on server side. +# +# Author: Marcin Konicki +# +######## Public functions ##################### + +# Usage: mydevil_deploy domain keyfile certfile cafile fullchain +mydevil_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + ip="" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if ! _exists "devil"; then + _err "Could not find 'devil' command." + return 1 + fi + + ip=$(mydevil_get_ip "$_cdomain") + if [ -z "$ip" ]; then + _err "Could not find IP for domain $_cdomain." + return 1 + fi + + # Delete old certificate first + _info "Removing old certificate for $_cdomain at $ip" + devil ssl www del "$ip" "$_cdomain" + + # Add new certificate + _info "Adding new certificate for $_cdomain at $ip" + devil ssl www add "$ip" "$_cfullchain" "$_ckey" "$_cdomain" || return 1 + + return 0 +} + +#################### Private functions below ################################## + +# Usage: ip=$(mydevil_get_ip domain.com) +# echo $ip +mydevil_get_ip() { + devil dns list "$1" | cut -w -s -f 3,7 | grep "^A$(printf '\t')" | cut -w -s -f 2 || return 1 + return 0 +} diff --git a/dnsapi/README.md b/dnsapi/README.md index f022cab0..9f176c0d 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1259,6 +1259,26 @@ acme.sh --issue --dns dns_online -d example.com -d www.example.com `ONLINE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 66. Use MyDevil.net + +Make sure that you can execute own binaries: + +```sh +devil binexec on +``` + +Install acme.sh, or simply `git clone` it into some directory on your MyDevil host account (in which case you should link to it from your `~/bin` directory). + +If you're not using private IP and depend on default IP provided by host, you may want to edit `crontab` too, and make sure that `acme.sh --cron` is run also after reboot (you can find out how to do that on their wiki pages). + +To issue a new certificate, run: + +```sh +acme.sh --issue --dns dns_mydevil -d example.com -d *.example.com +``` + +After certificate is ready, you can install it with [deploy command](../deploy/README.md#14-deploy-your-cert-on-mydevilnet). + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_mydevil.sh b/dnsapi/dns_mydevil.sh new file mode 100755 index 00000000..2f398959 --- /dev/null +++ b/dnsapi/dns_mydevil.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env sh + +# MyDevil.net API (2019-02-03) +# +# MyDevil.net already supports automatic Let's Encrypt certificates, +# except for wildcard domains. +# +# This script depends on `devil` command that MyDevil.net provides, +# which means that it works only on server side. +# +# Author: Marcin Konicki +# +######## Public functions ##################### + +#Usage: dns_mydevil_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_mydevil_add() { + fulldomain=$1 + txtvalue=$2 + domain="" + + if ! _exists "devil"; then + _err "Could not find 'devil' command." + return 1 + fi + + _info "Using mydevil" + + domain=$(mydevil_get_domain "$fulldomain") + if [ -z "$domain" ]; then + _err "Invalid domain name: could not find root domain of $fulldomain." + return 1 + fi + + # No need to check if record name exists, `devil` always adds new record. + # In worst case scenario, we end up with multiple identical records. + + _info "Adding $fulldomain record for domain $domain" + if devil dns add "$domain" "$fulldomain" TXT "$txtvalue"; then + _info "Successfully added TXT record, ready for validation." + return 0 + else + _err "Unable to add DNS record." + return 1 + fi +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_mydevil_rm() { + fulldomain=$1 + txtvalue=$2 + domain="" + + if ! _exists "devil"; then + _err "Could not find 'devil' command." + return 1 + fi + + _info "Using mydevil" + + domain=$(mydevil_get_domain "$fulldomain") + if [ -z "$domain" ]; then + _err "Invalid domain name: could not find root domain of $fulldomain." + return 1 + fi + + # catch one or more numbers + num='[0-9][0-9]*' + # catch one or more whitespace + w=$(printf '[\t ][\t ]*') + # catch anything, except newline + any='.*' + # filter to make sure we do not delete other records + validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$" + for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do + _info "Removing record $id from domain $domain" + devil dns del "$domain" "$id" || _err "Could not remove DNS record." + done +} + +#################### Private functions below ################################## + +# Usage: domain=$(mydevil_get_domain "_acme-challenge.www.domain.com" || _err "Invalid domain name") +# echo $domain +mydevil_get_domain() { + fulldomain=$1 + domain="" + + for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do + if _endswith "$fulldomain" "$domain"; then + printf -- "%s" "$domain" + return 0 + fi + done + + return 1 +} From 0bb746ba39d2e1cc5fdf732422050f77fb28e513 Mon Sep 17 00:00:00 2001 From: diseq Date: Wed, 20 Feb 2019 09:44:25 +0100 Subject: [PATCH 243/403] Update dns_one.sh --- dnsapi/dns_one.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 185669ce..521b034c 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -143,4 +143,4 @@ dns_one_rm() { return 1 fi -} \ No newline at end of file +} From 81ba629b5684e75e450345ae6024987ce8d80a90 Mon Sep 17 00:00:00 2001 From: diseq Date: Wed, 20 Feb 2019 11:27:49 +0100 Subject: [PATCH 244/403] allow set-cookie as well as Set-Cookie --- dnsapi/dns_one.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 521b034c..5dc002d5 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -41,13 +41,12 @@ dns_one_add() { postdata="$postdata&targetDomain=$mydomain" postdata="$postdata&password1=$ONECOM_PASSWORD" postdata="$postdata&loginTarget=" - #_debug postdata "$postdata" - response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST")" + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" #_debug response "$response" - JSESSIONID="$(grep "JSESSIONID=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + JSESSIONID="$(grep "JSESSIONID" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" _debug jsessionid "$JSESSIONID" export _H1="Cookie: ${JSESSIONID}" @@ -106,9 +105,10 @@ dns_one_rm() { postdata="$postdata&password1=$ONECOM_PASSWORD" postdata="$postdata&loginTarget=" - response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST")" + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" + #_debug response "$response" - JSESSIONID="$(grep "JSESSIONID=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + JSESSIONID="$(grep "JSESSIONID" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" _debug jsessionid "$JSESSIONID" export _H1="Cookie: ${JSESSIONID}" From 0499d2b5c4bef6bd105ff64f1bc5df419fd4ab9a Mon Sep 17 00:00:00 2001 From: diseq Date: Wed, 20 Feb 2019 11:51:06 +0100 Subject: [PATCH 245/403] remove line break --- dnsapi/dns_one.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 5dc002d5..1bc30ab7 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -134,8 +134,7 @@ dns_one_rm() { response="$(echo "$response" | _normalizeJson)" _debug response "$response" - if [ "$response" = '{"result":null,"metadata":null}' ]; - then + if [ "$response" = '{"result":null,"metadata":null}' ]; then _info "Removed, OK" return 0 else From ed3f2646f0d9188de9cf9b1efe2d6c612ce624ea Mon Sep 17 00:00:00 2001 From: diseq Date: Wed, 20 Feb 2019 11:54:48 +0100 Subject: [PATCH 246/403] fix format --- dnsapi/dns_one.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 1bc30ab7..d3ad670f 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -135,11 +135,11 @@ dns_one_rm() { _debug response "$response" if [ "$response" = '{"result":null,"metadata":null}' ]; then - _info "Removed, OK" - return 0 - else - _err "Removing txt record error." - return 1 + _info "Removed, OK" + return 0 + else + _err "Removing txt record error." + return 1 fi } From 472ed721a38312c8bc53b3cfd7764c2ccc8c75ef Mon Sep 17 00:00:00 2001 From: diseq Date: Wed, 20 Feb 2019 21:51:59 +0100 Subject: [PATCH 247/403] fix format --- dnsapi/dns_one.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index d3ad670f..c99c9c97 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -33,7 +33,6 @@ dns_one_add() { _saveaccountconf_mutable ONECOM_USER "$ONECOM_USER" _saveaccountconf_mutable ONECOM_PASSWORD "$ONECOM_PASSWORD" - # Login with user and password postdata="loginDomain=true" postdata="$postdata&displayUsername=$ONECOM_USER" @@ -51,16 +50,13 @@ dns_one_add() { export _H1="Cookie: ${JSESSIONID}" - # get entries response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" _debug response "$response" - CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" export _H2="Cookie: ${CSRF_G_TOKEN}" - # Update the IP address for domain entry postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}" _debug postdata "$postdata" @@ -96,7 +92,6 @@ dns_one_rm() { return 1 fi - # Login with user and password postdata="loginDomain=true" postdata="$postdata&displayUsername=$ONECOM_USER" @@ -113,7 +108,6 @@ dns_one_rm() { export _H1="Cookie: ${JSESSIONID}" - # get entries response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" response="$(echo "$response" | _normalizeJson)" From 23b4c9c667d6aab198cf4f633b9ccc1b05b66640 Mon Sep 17 00:00:00 2001 From: dsc Date: Thu, 21 Feb 2019 08:43:09 +0100 Subject: [PATCH 248/403] add docs for one.com --- dnsapi/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index f022cab0..cb8ac574 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1280,3 +1280,19 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api + +## 66. Use one.com domain API to automatically issue cert + +Use your one.com credentials as you would login into the control panel. + +``` +export ONECOM_USER="sdfsdfsdfljlbjkljlkjsdfoiwje" +export ONECOM_PASSWORD="xxxx@sss.com" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_one -d example.com -d www.example.com +``` + +The `ONECOM_USER` and `ONECOM_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. From ec54074392561f3f697b489fb278445aee34ada5 Mon Sep 17 00:00:00 2001 From: Timothy Nelson Date: Mon, 25 Feb 2019 05:19:36 -0600 Subject: [PATCH 249/403] Fix verification for namecheap domains not *owned* by the calling user (#2106) --- dnsapi/dns_namecheap.sh | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index fbf93c32..6553deb6 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -76,6 +76,22 @@ dns_namecheap_rm() { # _sub_domain=_acme-challenge.www # _domain=domain.com _get_root() { + fulldomain=$1 + + if ! _get_root_by_getList "$fulldomain"; then + _debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api." + # The above "getList" api will only return hosts *owned* by the calling user. However, if the calling + # user is not the owner, but still has administrative rights, we must query the getHosts api directly. + # See this comment and the official namecheap response: http://disq.us/p/1q6v9x9 + if ! _get_root_by_getHosts "$fulldomain"; then + return 1 + fi + fi + + return 0 +} + +_get_root_by_getList() { domain=$1 if ! _namecheap_post "namecheap.domains.getList"; then @@ -94,6 +110,10 @@ _get_root() { #not valid return 1 fi + if ! _contains "$h" "\\."; then + #not valid + return 1 + fi if ! _contains "$response" "$h"; then _debug "$h not found" @@ -108,6 +128,31 @@ _get_root() { return 1 } +_get_root_by_getHosts() { + i=100 + p=99 + + while [ $p -ne 0 ]; do + + h=$(printf "%s" "$1" | cut -d . -f $i-100) + if [ -n "$h" ]; then + if _contains "$h" "\\."; then + _debug h "$h" + if _namecheap_set_tld_sld "$h"; then + _sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p) + _domain="$h" + return 0 + else + _debug "$h not found" + fi + fi + fi + i="$p" + p=$(_math "$p" - 1) + done + return 1 +} + _namecheap_set_publicip() { if [ -z "$NAMECHEAP_SOURCEIP" ]; then From e7f7e96d589ca757ab91744a97893f83d615c481 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 27 Feb 2019 20:36:13 +0800 Subject: [PATCH 250/403] Peb (#2126) * support pebble * support async finalize order --- acme.sh | 88 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/acme.sh b/acme.sh index 93112a1a..8ee22479 100755 --- a/acme.sh +++ b/acme.sh @@ -1827,23 +1827,29 @@ _send_signed_request() { nonceurl="$ACME_NEW_NONCE" if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then _headers="$(cat "$HTTP_HEADER")" + _debug2 _headers "$_headers" + _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" fi fi - if [ -z "$_headers" ]; then + if [ -z "$_CACHED_NONCE" ]; then _debug2 "Get nonce with GET. ACME_DIRECTORY" "$ACME_DIRECTORY" nonceurl="$ACME_DIRECTORY" _headers="$(_get "$nonceurl" "onlyheader")" + _debug2 _headers "$_headers" + _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" fi - + if [ -z "$_CACHED_NONCE" ] && [ "$ACME_NEW_NONCE" ]; then + _debug2 "Get nonce with GET. ACME_NEW_NONCE" "$ACME_NEW_NONCE" + nonceurl="$ACME_NEW_NONCE" + _headers="$(_get "$nonceurl" "onlyheader")" + _debug2 _headers "$_headers" + _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" + fi + _debug2 _CACHED_NONCE "$_CACHED_NONCE" if [ "$?" != "0" ]; then _err "Can not connect to $nonceurl to get nonce." return 1 fi - - _debug2 _headers "$_headers" - - _CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" - _debug2 _CACHED_NONCE "$_CACHED_NONCE" else _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE" fi @@ -2060,6 +2066,7 @@ _clearcaconf() { _startserver() { content="$1" ncaddr="$2" + _debug "content" "$content" _debug "ncaddr" "$ncaddr" _debug "startserver: $$" @@ -2086,8 +2093,14 @@ _startserver() { SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi + _content_len="$(printf "%s" "$content" | wc -c)" + _debug _content_len "$_content_len" _debug "_NC" "$_NC $SOCAT_OPTIONS" - $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; \ +echo 'HTTP/1.0 200 OK'; \ +echo 'Content-Length\: $_content_len'; \ +echo ''; \ +printf '$content';" & serverproc="$!" } @@ -3062,6 +3075,7 @@ _on_before_issue() { _info "Standalone mode." if [ -z "$Le_HTTPPort" ]; then Le_HTTPPort=80 + _cleardomainconf "Le_HTTPPort" else _savedomainconf "Le_HTTPPort" "$Le_HTTPPort" fi @@ -3269,7 +3283,7 @@ _regAccount() { fi _debug2 responseHeaders "$responseHeaders" - _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _accUri="$(echo "$responseHeaders" | grep -i "^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." @@ -3435,7 +3449,7 @@ __trigger_validation() { _t_vtype="$3" _debug2 _t_vtype "$_t_vtype" if [ "$ACME_VERSION" = "2" ]; then - _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" + _send_signed_request "$_t_url" "{}" else _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}" fi @@ -4205,20 +4219,66 @@ $_authorizations_map" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" if [ "$ACME_VERSION" = "2" ]; then + _info "Lets finalize the order, Le_OrderFinalize: $Le_OrderFinalize" if ! _send_signed_request "${Le_OrderFinalize}" "{\"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." + _err "Sign failed, finalize 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)" + Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" + if [ -z "$Le_LinkOrder" ]; then + _err "Sign error, can not get order link location header" + _err "responseHeaders" "$responseHeaders" + _on_issue_err "$_post_hook" + return 1 + fi + _savedomainconf "Le_LinkOrder" "$Le_LinkOrder" + + _link_cert_retry=0 + _MAX_CERT_RETRY=5 + while [ -z "$Le_LinkCert" ] && [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do + if _contains "$response" "\"status\":\"valid\""; then + _debug "Order status is valid." + Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug Le_LinkCert "$Le_LinkCert" + if [ -z "$Le_LinkCert" ]; then + _err "Sign error, can not find Le_LinkCert" + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + break + elif _contains "$response" "\"processing\""; then + _info "Order status is processing, lets sleep and retry." + _sleep 2 + else + _err "Sign error, wrong status" + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + if ! _send_signed_request "$Le_LinkOrder"; then + _err "Sign failed, can not post to Le_LinkOrder cert:$Le_LinkOrder." + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + _link_cert_retry="$(_math $_link_cert_retry + 1)" + done - _tempSignedResponse="$response" + if [ -z "$Le_LinkCert" ]; then + _err "Sign failed, can not get Le_LinkCert, retry time limit." + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + _info "Download cert, Le_LinkCert: $Le_LinkCert" if ! _send_signed_request "$Le_LinkCert"; then _err "Sign failed, can not download cert:$Le_LinkCert." _err "$response" @@ -4237,7 +4297,7 @@ $_authorizations_map" _end_n="$(_math $_end_n + 1)" sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH" fi - response="$_tempSignedResponse" + else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then _err "Sign failed. $response" From 81f0189d2342069ca74bd942f2d3592c1054232b Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 27 Feb 2019 20:40:10 +0800 Subject: [PATCH 251/403] add Pebble --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f79b8602..f68eb002 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ https://github.com/Neilpang/acmetest - Letsencrypt.org CA(default) - [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA) +- [Pebble strict Mode](https://github.com/letsencrypt/pebble) # Supported modes From 693d692a472e9298c3bf3ee71ffc7d3328451887 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 27 Feb 2019 20:41:50 +0800 Subject: [PATCH 252/403] sync (#2127) * Support for MyDevil.net (#2076) support mydevil * Fix verification for namecheap domains not *owned* by the calling user (#2106) * Peb (#2126) * support pebble * support async finalize order * add Pebble --- README.md | 2 + acme.sh | 88 +++++++++++++++++++++++++++++++------ deploy/README.md | 10 +++++ deploy/mydevil.sh | 59 +++++++++++++++++++++++++ dnsapi/README.md | 20 +++++++++ dnsapi/dns_mydevil.sh | 97 +++++++++++++++++++++++++++++++++++++++++ dnsapi/dns_namecheap.sh | 45 +++++++++++++++++++ 7 files changed, 307 insertions(+), 14 deletions(-) create mode 100755 deploy/mydevil.sh create mode 100755 dnsapi/dns_mydevil.sh diff --git a/README.md b/README.md index 8d749dcc..f68eb002 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ https://github.com/Neilpang/acmetest - Letsencrypt.org CA(default) - [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA) +- [Pebble strict Mode](https://github.com/letsencrypt/pebble) # Supported modes @@ -356,6 +357,7 @@ You don't have to do anything manually! 1. Futurehosting API (https://www.futurehosting.com) 1. Rackspace Cloud DNS (https://www.rackspace.com) 1. Online.net API (https://online.net/) +1. MyDevil.net (https://www.mydevil.net/) And: diff --git a/acme.sh b/acme.sh index 93112a1a..8ee22479 100755 --- a/acme.sh +++ b/acme.sh @@ -1827,23 +1827,29 @@ _send_signed_request() { nonceurl="$ACME_NEW_NONCE" if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then _headers="$(cat "$HTTP_HEADER")" + _debug2 _headers "$_headers" + _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" fi fi - if [ -z "$_headers" ]; then + if [ -z "$_CACHED_NONCE" ]; then _debug2 "Get nonce with GET. ACME_DIRECTORY" "$ACME_DIRECTORY" nonceurl="$ACME_DIRECTORY" _headers="$(_get "$nonceurl" "onlyheader")" + _debug2 _headers "$_headers" + _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" fi - + if [ -z "$_CACHED_NONCE" ] && [ "$ACME_NEW_NONCE" ]; then + _debug2 "Get nonce with GET. ACME_NEW_NONCE" "$ACME_NEW_NONCE" + nonceurl="$ACME_NEW_NONCE" + _headers="$(_get "$nonceurl" "onlyheader")" + _debug2 _headers "$_headers" + _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" + fi + _debug2 _CACHED_NONCE "$_CACHED_NONCE" if [ "$?" != "0" ]; then _err "Can not connect to $nonceurl to get nonce." return 1 fi - - _debug2 _headers "$_headers" - - _CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" - _debug2 _CACHED_NONCE "$_CACHED_NONCE" else _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE" fi @@ -2060,6 +2066,7 @@ _clearcaconf() { _startserver() { content="$1" ncaddr="$2" + _debug "content" "$content" _debug "ncaddr" "$ncaddr" _debug "startserver: $$" @@ -2086,8 +2093,14 @@ _startserver() { SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi + _content_len="$(printf "%s" "$content" | wc -c)" + _debug _content_len "$_content_len" _debug "_NC" "$_NC $SOCAT_OPTIONS" - $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; \ +echo 'HTTP/1.0 200 OK'; \ +echo 'Content-Length\: $_content_len'; \ +echo ''; \ +printf '$content';" & serverproc="$!" } @@ -3062,6 +3075,7 @@ _on_before_issue() { _info "Standalone mode." if [ -z "$Le_HTTPPort" ]; then Le_HTTPPort=80 + _cleardomainconf "Le_HTTPPort" else _savedomainconf "Le_HTTPPort" "$Le_HTTPPort" fi @@ -3269,7 +3283,7 @@ _regAccount() { fi _debug2 responseHeaders "$responseHeaders" - _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _accUri="$(echo "$responseHeaders" | grep -i "^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." @@ -3435,7 +3449,7 @@ __trigger_validation() { _t_vtype="$3" _debug2 _t_vtype "$_t_vtype" if [ "$ACME_VERSION" = "2" ]; then - _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" + _send_signed_request "$_t_url" "{}" else _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}" fi @@ -4205,20 +4219,66 @@ $_authorizations_map" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" if [ "$ACME_VERSION" = "2" ]; then + _info "Lets finalize the order, Le_OrderFinalize: $Le_OrderFinalize" if ! _send_signed_request "${Le_OrderFinalize}" "{\"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." + _err "Sign failed, finalize 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)" + Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" + if [ -z "$Le_LinkOrder" ]; then + _err "Sign error, can not get order link location header" + _err "responseHeaders" "$responseHeaders" + _on_issue_err "$_post_hook" + return 1 + fi + _savedomainconf "Le_LinkOrder" "$Le_LinkOrder" + + _link_cert_retry=0 + _MAX_CERT_RETRY=5 + while [ -z "$Le_LinkCert" ] && [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do + if _contains "$response" "\"status\":\"valid\""; then + _debug "Order status is valid." + Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug Le_LinkCert "$Le_LinkCert" + if [ -z "$Le_LinkCert" ]; then + _err "Sign error, can not find Le_LinkCert" + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + break + elif _contains "$response" "\"processing\""; then + _info "Order status is processing, lets sleep and retry." + _sleep 2 + else + _err "Sign error, wrong status" + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + if ! _send_signed_request "$Le_LinkOrder"; then + _err "Sign failed, can not post to Le_LinkOrder cert:$Le_LinkOrder." + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + _link_cert_retry="$(_math $_link_cert_retry + 1)" + done - _tempSignedResponse="$response" + if [ -z "$Le_LinkCert" ]; then + _err "Sign failed, can not get Le_LinkCert, retry time limit." + _err "$response" + _on_issue_err "$_post_hook" + return 1 + fi + _info "Download cert, Le_LinkCert: $Le_LinkCert" if ! _send_signed_request "$Le_LinkCert"; then _err "Sign failed, can not download cert:$Le_LinkCert." _err "$response" @@ -4237,7 +4297,7 @@ $_authorizations_map" _end_n="$(_math $_end_n + 1)" sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH" fi - response="$_tempSignedResponse" + else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then _err "Sign failed. $response" diff --git a/deploy/README.md b/deploy/README.md index 091e9feb..f290756a 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -381,3 +381,13 @@ you want to update: $ export QINIU_CDN_DOMAIN="cdn.example.com" $ acme.sh --deploy -d example.com --deploy-hook qiniu ``` + +## 14. Deploy your cert on MyDevil.net + +Once you have acme.sh installed and certificate issued (see info in [DNS API](../dnsapi/README.md#61-use-mydevilnet)), you can install it by following command: + +```sh +acme.sh --deploy --deploy-hook mydevil -d example.com +``` + +That will remove old certificate and install new one. diff --git a/deploy/mydevil.sh b/deploy/mydevil.sh new file mode 100755 index 00000000..bd9868aa --- /dev/null +++ b/deploy/mydevil.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +# MyDevil.net API (2019-02-03) +# +# MyDevil.net already supports automatic Let's Encrypt certificates, +# except for wildcard domains. +# +# This script depends on `devil` command that MyDevil.net provides, +# which means that it works only on server side. +# +# Author: Marcin Konicki +# +######## Public functions ##################### + +# Usage: mydevil_deploy domain keyfile certfile cafile fullchain +mydevil_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + ip="" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if ! _exists "devil"; then + _err "Could not find 'devil' command." + return 1 + fi + + ip=$(mydevil_get_ip "$_cdomain") + if [ -z "$ip" ]; then + _err "Could not find IP for domain $_cdomain." + return 1 + fi + + # Delete old certificate first + _info "Removing old certificate for $_cdomain at $ip" + devil ssl www del "$ip" "$_cdomain" + + # Add new certificate + _info "Adding new certificate for $_cdomain at $ip" + devil ssl www add "$ip" "$_cfullchain" "$_ckey" "$_cdomain" || return 1 + + return 0 +} + +#################### Private functions below ################################## + +# Usage: ip=$(mydevil_get_ip domain.com) +# echo $ip +mydevil_get_ip() { + devil dns list "$1" | cut -w -s -f 3,7 | grep "^A$(printf '\t')" | cut -w -s -f 2 || return 1 + return 0 +} diff --git a/dnsapi/README.md b/dnsapi/README.md index f022cab0..9f176c0d 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1259,6 +1259,26 @@ acme.sh --issue --dns dns_online -d example.com -d www.example.com `ONLINE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 66. Use MyDevil.net + +Make sure that you can execute own binaries: + +```sh +devil binexec on +``` + +Install acme.sh, or simply `git clone` it into some directory on your MyDevil host account (in which case you should link to it from your `~/bin` directory). + +If you're not using private IP and depend on default IP provided by host, you may want to edit `crontab` too, and make sure that `acme.sh --cron` is run also after reboot (you can find out how to do that on their wiki pages). + +To issue a new certificate, run: + +```sh +acme.sh --issue --dns dns_mydevil -d example.com -d *.example.com +``` + +After certificate is ready, you can install it with [deploy command](../deploy/README.md#14-deploy-your-cert-on-mydevilnet). + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_mydevil.sh b/dnsapi/dns_mydevil.sh new file mode 100755 index 00000000..2f398959 --- /dev/null +++ b/dnsapi/dns_mydevil.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env sh + +# MyDevil.net API (2019-02-03) +# +# MyDevil.net already supports automatic Let's Encrypt certificates, +# except for wildcard domains. +# +# This script depends on `devil` command that MyDevil.net provides, +# which means that it works only on server side. +# +# Author: Marcin Konicki +# +######## Public functions ##################### + +#Usage: dns_mydevil_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_mydevil_add() { + fulldomain=$1 + txtvalue=$2 + domain="" + + if ! _exists "devil"; then + _err "Could not find 'devil' command." + return 1 + fi + + _info "Using mydevil" + + domain=$(mydevil_get_domain "$fulldomain") + if [ -z "$domain" ]; then + _err "Invalid domain name: could not find root domain of $fulldomain." + return 1 + fi + + # No need to check if record name exists, `devil` always adds new record. + # In worst case scenario, we end up with multiple identical records. + + _info "Adding $fulldomain record for domain $domain" + if devil dns add "$domain" "$fulldomain" TXT "$txtvalue"; then + _info "Successfully added TXT record, ready for validation." + return 0 + else + _err "Unable to add DNS record." + return 1 + fi +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_mydevil_rm() { + fulldomain=$1 + txtvalue=$2 + domain="" + + if ! _exists "devil"; then + _err "Could not find 'devil' command." + return 1 + fi + + _info "Using mydevil" + + domain=$(mydevil_get_domain "$fulldomain") + if [ -z "$domain" ]; then + _err "Invalid domain name: could not find root domain of $fulldomain." + return 1 + fi + + # catch one or more numbers + num='[0-9][0-9]*' + # catch one or more whitespace + w=$(printf '[\t ][\t ]*') + # catch anything, except newline + any='.*' + # filter to make sure we do not delete other records + validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$" + for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do + _info "Removing record $id from domain $domain" + devil dns del "$domain" "$id" || _err "Could not remove DNS record." + done +} + +#################### Private functions below ################################## + +# Usage: domain=$(mydevil_get_domain "_acme-challenge.www.domain.com" || _err "Invalid domain name") +# echo $domain +mydevil_get_domain() { + fulldomain=$1 + domain="" + + for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do + if _endswith "$fulldomain" "$domain"; then + printf -- "%s" "$domain" + return 0 + fi + done + + return 1 +} diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index fbf93c32..6553deb6 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -76,6 +76,22 @@ dns_namecheap_rm() { # _sub_domain=_acme-challenge.www # _domain=domain.com _get_root() { + fulldomain=$1 + + if ! _get_root_by_getList "$fulldomain"; then + _debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api." + # The above "getList" api will only return hosts *owned* by the calling user. However, if the calling + # user is not the owner, but still has administrative rights, we must query the getHosts api directly. + # See this comment and the official namecheap response: http://disq.us/p/1q6v9x9 + if ! _get_root_by_getHosts "$fulldomain"; then + return 1 + fi + fi + + return 0 +} + +_get_root_by_getList() { domain=$1 if ! _namecheap_post "namecheap.domains.getList"; then @@ -94,6 +110,10 @@ _get_root() { #not valid return 1 fi + if ! _contains "$h" "\\."; then + #not valid + return 1 + fi if ! _contains "$response" "$h"; then _debug "$h not found" @@ -108,6 +128,31 @@ _get_root() { return 1 } +_get_root_by_getHosts() { + i=100 + p=99 + + while [ $p -ne 0 ]; do + + h=$(printf "%s" "$1" | cut -d . -f $i-100) + if [ -n "$h" ]; then + if _contains "$h" "\\."; then + _debug h "$h" + if _namecheap_set_tld_sld "$h"; then + _sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p) + _domain="$h" + return 0 + else + _debug "$h not found" + fi + fi + fi + i="$p" + p=$(_math "$p" - 1) + done + return 1 +} + _namecheap_set_publicip() { if [ -z "$NAMECHEAP_SOURCEIP" ]; then From af5f7a77796ff03e82bf554675816962d523fe28 Mon Sep 17 00:00:00 2001 From: tianji Date: Thu, 28 Feb 2019 23:43:58 +0800 Subject: [PATCH 253/403] fix deploy/qiniu.sh base64 According to the doc (https://developer.qiniu.com/kodo/manual/1231/appendix#1), we should use URL-safe base64 instead of plain base64 for token calculation. --- deploy/qiniu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index 158b8dbf..e46e6fb3 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -87,6 +87,6 @@ qiniu_deploy() { } _make_access_token() { - _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64)" + _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64 | tr -- '+/' '-_')" echo "$QINIU_AK:$_token" } From 22e7b4c91184201225a8dbe52d5cb20efb90e860 Mon Sep 17 00:00:00 2001 From: tianji Date: Thu, 28 Feb 2019 23:51:43 +0800 Subject: [PATCH 254/403] fix doc of qiniu deploy script A leading dot should be included when updating wildcard domains. --- deploy/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index f290756a..44d53225 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -349,10 +349,10 @@ $ export QINIU_SK="bar" $ acme.sh --deploy -d example.com --deploy-hook qiniu ``` -假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名: +假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名(请注意泛域名前的点): ```sh -$ export QINIU_CDN_DOMAIN="cdn.example.com" +$ export QINIU_CDN_DOMAIN=".cdn.example.com" $ acme.sh --deploy -d example.com --deploy-hook qiniu ``` @@ -375,10 +375,10 @@ $ acme.sh --deploy -d example.com --deploy-hook qiniu (Optional), If you are using wildcard certificate, you may need export `QINIU_CDN_DOMAIN` to specify which domain -you want to update: +you want to update (please note the leading dot): ```sh -$ export QINIU_CDN_DOMAIN="cdn.example.com" +$ export QINIU_CDN_DOMAIN=".cdn.example.com" $ acme.sh --deploy -d example.com --deploy-hook qiniu ``` From b3f6129718bf0e7b7f352344b7149c725cf1576b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Mar 2019 20:44:08 +0800 Subject: [PATCH 255/403] fix https://github.com/Neilpang/acme.sh/issues/2122 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 8ee22479..005b1333 100755 --- a/acme.sh +++ b/acme.sh @@ -4886,7 +4886,7 @@ _installcert() { export CERT_KEY_PATH export CA_CERT_PATH export CERT_FULLCHAIN_PATH - export Le_Domain + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_reload_cmd" ); then _info "$(__green "Reload success")" From 86fbb5952e2fad1065836f89502ca34aad7f78a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20H=C3=A5land?= Date: Sat, 2 Mar 2019 16:39:41 +0100 Subject: [PATCH 256/403] Use env sh --- deploy/routeros.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/routeros.sh b/deploy/routeros.sh index d590bc9a..d0d15c5c 100644 --- a/deploy/routeros.sh +++ b/deploy/routeros.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh #Here is a script to deploy cert to routeros router. From 7690f73e815a0b3af86fdf2901cc27519a1b0b33 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 5 Mar 2019 21:05:10 +0800 Subject: [PATCH 257/403] base64 encode reloadcmd. fix https://github.com/Neilpang/acme.sh/issues/2134 --- acme.sh | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/acme.sh b/acme.sh index 005b1333..6c42d7ee 100755 --- a/acme.sh +++ b/acme.sh @@ -66,6 +66,9 @@ END_CERT="-----END CERTIFICATE-----" CONTENT_TYPE_JSON="application/jose+json" RENEW_SKIP=2 +B64CONF_START="__ACME_BASE64__START_" +B64CONF_END="__ACME_BASE64__END_" + ECC_SEP="_" ECC_SUFFIX="${ECC_SEP}ecc" @@ -1964,12 +1967,16 @@ _setopt() { _debug3 "$(grep -n "^$__opt$__sep" "$__conf")" } -#_save_conf file key value +#_save_conf file key value base64encode #save to conf _save_conf() { _s_c_f="$1" _sdkey="$2" _sdvalue="$3" + _b64encode="$4" + if [ "$_b64encode" ]; then + _sdvalue="${B64CONF_START}$(printf "%s" "${_sdvalue}" | _base64)${B64CONF_END}" + fi if [ "$_s_c_f" ]; then _setopt "$_s_c_f" "$_sdkey" "=" "'$_sdvalue'" else @@ -1994,19 +2001,20 @@ _read_conf() { _r_c_f="$1" _sdkey="$2" if [ -f "$_r_c_f" ]; then - ( - eval "$(grep "^$_sdkey *=" "$_r_c_f")" - eval "printf \"%s\" \"\$$_sdkey\"" - ) + _sdv="$(grep "^$_sdkey *=" "$_r_c_f" | cut -d = -f 2-1000 | tr -d "'")" + if _startswith "$_sdv" "${B64CONF_START}" && _endswith "$_sdv" "${B64CONF_END}"; then + _sdv="$(echo "$_sdv" | sed "s/${B64CONF_START}//" | sed "s/${B64CONF_END}//" | _dbase64)" + fi + printf "%s" "$_sdv" else _debug "config file is empty, can not read $_sdkey" fi } -#_savedomainconf key value +#_savedomainconf key value base64encode #save to domain.conf _savedomainconf() { - _save_conf "$DOMAIN_CONF" "$1" "$2" + _save_conf "$DOMAIN_CONF" "$@" } #_cleardomainconf key @@ -2019,14 +2027,14 @@ _readdomainconf() { _read_conf "$DOMAIN_CONF" "$1" } -#_saveaccountconf key value +#_saveaccountconf key value base64encode _saveaccountconf() { - _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2" + _save_conf "$ACCOUNT_CONF_PATH" "$@" } -#key value +#key value base64encode _saveaccountconf_mutable() { - _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2" + _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2" "$3" #remove later _clearaccountconf "$1" } @@ -4455,7 +4463,7 @@ $_authorizations_map" _savedomainconf "Le_RealCertPath" "$_real_cert" _savedomainconf "Le_RealCACertPath" "$_real_ca" _savedomainconf "Le_RealKeyPath" "$_real_key" - _savedomainconf "Le_ReloadCmd" "$_reload_cmd" + _savedomainconf "Le_ReloadCmd" "$_reload_cmd" "base64" _savedomainconf "Le_RealFullChainPath" "$_real_fullchain" if ! _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"; then return 1 @@ -4522,6 +4530,7 @@ renew() { fi IS_RENEW="1" + Le_ReloadCmd="$(_readdomainconf Le_ReloadCmd)" 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 @@ -4802,7 +4811,7 @@ installcert() { _savedomainconf "Le_RealCertPath" "$_real_cert" _savedomainconf "Le_RealCACertPath" "$_real_ca" _savedomainconf "Le_RealKeyPath" "$_real_key" - _savedomainconf "Le_ReloadCmd" "$_reload_cmd" + _savedomainconf "Le_ReloadCmd" "$_reload_cmd" "base64" _savedomainconf "Le_RealFullChainPath" "$_real_fullchain" _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd" From dfca8c09e046ee157516b6f05dadf4d5240ba2fa Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 5 Mar 2019 21:22:03 +0800 Subject: [PATCH 258/403] fix format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 6c42d7ee..c63f2945 100755 --- a/acme.sh +++ b/acme.sh @@ -2001,7 +2001,7 @@ _read_conf() { _r_c_f="$1" _sdkey="$2" if [ -f "$_r_c_f" ]; then - _sdv="$(grep "^$_sdkey *=" "$_r_c_f" | cut -d = -f 2-1000 | tr -d "'")" + _sdv="$(grep "^$_sdkey *=" "$_r_c_f" | cut -d = -f 2-1000 | tr -d "'")" if _startswith "$_sdv" "${B64CONF_START}" && _endswith "$_sdv" "${B64CONF_END}"; then _sdv="$(echo "$_sdv" | sed "s/${B64CONF_START}//" | sed "s/${B64CONF_END}//" | _dbase64)" fi From c7257bcf464d09096b9543e42fef12094fcdf18b Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 5 Mar 2019 21:44:34 +0800 Subject: [PATCH 259/403] base64 hooks, fix https://github.com/Neilpang/acme.sh/issues/1969 --- acme.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index c63f2945..408f09cd 100755 --- a/acme.sh +++ b/acme.sh @@ -3650,9 +3650,9 @@ issue() { _savedomainconf "Le_Alt" "$_alt_domains" _savedomainconf "Le_Webroot" "$_web_roots" - _savedomainconf "Le_PreHook" "$_pre_hook" - _savedomainconf "Le_PostHook" "$_post_hook" - _savedomainconf "Le_RenewHook" "$_renew_hook" + _savedomainconf "Le_PreHook" "$_pre_hook" "base64" + _savedomainconf "Le_PostHook" "$_post_hook" "base64" + _savedomainconf "Le_RenewHook" "$_renew_hook" "base64" if [ "$_local_addr" ]; then _savedomainconf "Le_LocalAddress" "$_local_addr" @@ -4531,6 +4531,9 @@ renew() { IS_RENEW="1" Le_ReloadCmd="$(_readdomainconf Le_ReloadCmd)" + Le_PreHook="$(_readdomainconf Le_PreHook)" + Le_PostHook="$(_readdomainconf Le_PostHook)" + Le_RenewHook="$(_readdomainconf Le_RenewHook)" 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 From a3d8b9935ab7eb6656d63f95c69ae0423c747cfa Mon Sep 17 00:00:00 2001 From: neil Date: Fri, 8 Mar 2019 14:31:11 +0800 Subject: [PATCH 260/403] fix https://github.com/Neilpang/acme.sh/issues/2141 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 408f09cd..d81812fe 100755 --- a/acme.sh +++ b/acme.sh @@ -4250,7 +4250,7 @@ $_authorizations_map" _link_cert_retry=0 _MAX_CERT_RETRY=5 - while [ -z "$Le_LinkCert" ] && [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do + while [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do if _contains "$response" "\"status\":\"valid\""; then _debug "Order status is valid." Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" From 110a41d18def8f8305952600c07240e72aba7a67 Mon Sep 17 00:00:00 2001 From: 5ll <5ll@users.noreply.github.com> Date: Fri, 8 Mar 2019 10:33:09 +0100 Subject: [PATCH 261/403] initial commit DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/) --- dnsapi/dns_cn.sh | 158 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 dnsapi/dns_cn.sh diff --git a/dnsapi/dns_cn.sh b/dnsapi/dns_cn.sh new file mode 100644 index 00000000..b35f81cb --- /dev/null +++ b/dnsapi/dns_cn.sh @@ -0,0 +1,158 @@ +#!/usr/bin/env sh + +# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/). +# created by 5ll and francis + +CN_API="https://beta.api.core-networks.de" + +######## Public functions ##################### + +dns_cn_add(){ + fulldomain=$1 + txtvalue=$2 + + if ! _cn_login; then + _err "login failed" + return 1 + fi + + _debug "First detect the root zone" + if ! _cn_get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug "_sub_domain $_sub_domain" + _debug "_domain $_domain" + + _info "Adding record" + curData="{\"name\":\"$_sub_domain\",\"ttl\":120,\"type\":\"TXT\",\"data\":\"$txtvalue\"}" + curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/")" + + _debug "curData $curData" + _debug "curResult $curResult" + + if _contains "$curResult" ""; then + _info "Added, OK" + + if ! _cn_commit; then + _err "commiting changes failed" + return 1 + fi + return 0 + + else + _err "Add txt record error." + _debug "curData is $curData" + _debug "curResult is $curResult" + _err "error adding text record, response was $curResult" + return 1 + fi +} + +dns_cn_rm(){ + fulldomain=$1 + txtvalue=$2 + + if ! _cn_login; then + _err "login failed" + return 1 + fi + + _debug "First detect the root zone" + if ! _cn_get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _info "Deleting record" + curData="{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\"}" + curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/delete")" + _debug curData is "$curData" + + _info "commiting changes" + if ! _cn_commit; then + _err "commiting changes failed" + return 1 + fi + + _info "Deletet txt record" + return 0 +} + + +################### Private functions below ################################## +_cn_login() { + CN_User="${CN_User:-$(_readaccountconf_mutable CN_User)}" + CN_Password="${CN_Password:-$(_readaccountconf_mutable CN_Password)}" + if [ -z "$CN_User" ] || [ -z "$CN_Password" ]; then + CN_User="" + CN_Password="" + _err "You must export variables: CN_User and CN_Password" + return 1 + fi + + #save the config variables to the account conf file. + _saveaccountconf_mutable CN_User "$CN_User" + _saveaccountconf_mutable CN_Password "$CN_Password" + + _info "Getting an AUTH-Token" + curData="{\"login\":\"${CN_User}\",\"password\":\"${CN_Password}\"}" + curResult="$(_post "${curData}" "${CN_API}/auth/token")" + _debug "Calling _CN_login: '${curData}' '${CN_API}/auth/token'" + + if _contains "${curResult}" '"token":"'; then + authToken=$(echo "${curResult}" | cut -d ":" -f2 | cut -d "," -f1 | sed 's/^.\(.*\).$/\1/') + export _H1="Authorization: Bearer $authToken" + _info "Successfully acquired AUTH-Token" + _debug "AUTH-Token: '${authToken}'" + _debug "_H1 '${_H1}'" + else + _err "Couldn't acquire an AUTH-Token" + return 1 + fi +} + +# Commit changes +_cn_commit(){ + _info "Commiting changes" + _post "" "${CN_API}/dnszones/$h/records/commit" +} + +_cn_get_root(){ + domain=$1 + i=2 + p=1 + while true; do + + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + _debug _H1 "${_H1}" + + if [ -z "$h" ]; then + #not valid + return 1 + fi + + _cn_zonelist="$(_get ${CN_API}/dnszones/)" + _debug _cn_zonelist "${_cn_zonelist}" + + if [ "$?" != "0" ]; then + _err "something went wrong while getting the zone list" + return 1 + fi + + if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + else + _debug "Zonelist does not contain domain - iterating " + fi + p=$i + i=$(_math "$i" + 1) + + done + _err "Zonelist does not contain domain - exiting" + return 1 +} From 1d5967d143ddedddb8831be9e09583c406fd7c16 Mon Sep 17 00:00:00 2001 From: 5ll <5ll@users.noreply.github.com> Date: Fri, 8 Mar 2019 10:45:36 +0100 Subject: [PATCH 262/403] Updated README with Core-Networks support --- dnsapi/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index 9f176c0d..23620c4a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1300,3 +1300,22 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api + + +## Use Core-Networks API to automatically issue cert + +First you need to login to your Core-Networks account to to set up an API-User. +Then export username and password to use these credentials. + +``` +export CN_User="user" +export CN_Password="passowrd" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_cn -d example.com -d www.example.com +``` + +The `CN_User` and `CN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + From 3d5c75420a517eb199dcd4fb572856e77f1cc549 Mon Sep 17 00:00:00 2001 From: 5ll <5ll@users.noreply.github.com> Date: Fri, 8 Mar 2019 10:46:35 +0100 Subject: [PATCH 263/403] Changed Order --- dnsapi/README.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 23620c4a..33d724c7 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1279,6 +1279,26 @@ acme.sh --issue --dns dns_mydevil -d example.com -d *.example.com After certificate is ready, you can install it with [deploy command](../deploy/README.md#14-deploy-your-cert-on-mydevilnet). +## 67. Use Core-Networks API to automatically issue cert + +First you need to login to your Core-Networks account to to set up an API-User. +Then export username and password to use these credentials. + +``` +export CN_User="user" +export CN_Password="passowrd" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_cn -d example.com -d www.example.com +``` + +The `CN_User` and `CN_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. @@ -1302,20 +1322,3 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api -## Use Core-Networks API to automatically issue cert - -First you need to login to your Core-Networks account to to set up an API-User. -Then export username and password to use these credentials. - -``` -export CN_User="user" -export CN_Password="passowrd" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_cn -d example.com -d www.example.com -``` - -The `CN_User` and `CN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - From 30d0ac0784311d0f55c1737bb035242f58349c0e Mon Sep 17 00:00:00 2001 From: 5ll <5ll@users.noreply.github.com> Date: Fri, 8 Mar 2019 10:48:06 +0100 Subject: [PATCH 264/403] Updated README with Core-Networks support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f68eb002..d0d526d0 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ You don't have to do anything manually! 1. Rackspace Cloud DNS (https://www.rackspace.com) 1. Online.net API (https://online.net/) 1. MyDevil.net (https://www.mydevil.net/) - +1. Core-Networks.de (https://core-networks.de) And: **lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api From f5850d0c08bb72c1453043482ac5dd365df1e66b Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 8 Mar 2019 22:20:56 +0800 Subject: [PATCH 265/403] fix format --- dnsapi/dns_cn.sh | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/dnsapi/dns_cn.sh b/dnsapi/dns_cn.sh index b35f81cb..e90d7e60 100644 --- a/dnsapi/dns_cn.sh +++ b/dnsapi/dns_cn.sh @@ -7,7 +7,7 @@ CN_API="https://beta.api.core-networks.de" ######## Public functions ##################### -dns_cn_add(){ +dns_cn_add() { fulldomain=$1 txtvalue=$2 @@ -21,17 +21,17 @@ dns_cn_add(){ _err "invalid domain" return 1 fi - + _debug "_sub_domain $_sub_domain" _debug "_domain $_domain" - + _info "Adding record" curData="{\"name\":\"$_sub_domain\",\"ttl\":120,\"type\":\"TXT\",\"data\":\"$txtvalue\"}" curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/")" _debug "curData $curData" _debug "curResult $curResult" - + if _contains "$curResult" ""; then _info "Added, OK" @@ -40,7 +40,7 @@ dns_cn_add(){ return 1 fi return 0 - + else _err "Add txt record error." _debug "curData is $curData" @@ -50,7 +50,7 @@ dns_cn_add(){ fi } -dns_cn_rm(){ +dns_cn_rm() { fulldomain=$1 txtvalue=$2 @@ -64,14 +64,14 @@ dns_cn_rm(){ _err "invalid domain" return 1 fi - + _info "Deleting record" curData="{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\"}" curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/delete")" _debug curData is "$curData" _info "commiting changes" - if ! _cn_commit; then + if ! _cn_commit; then _err "commiting changes failed" return 1 fi @@ -80,7 +80,6 @@ dns_cn_rm(){ return 0 } - ################### Private functions below ################################## _cn_login() { CN_User="${CN_User:-$(_readaccountconf_mutable CN_User)}" @@ -100,7 +99,7 @@ _cn_login() { curData="{\"login\":\"${CN_User}\",\"password\":\"${CN_Password}\"}" curResult="$(_post "${curData}" "${CN_API}/auth/token")" _debug "Calling _CN_login: '${curData}' '${CN_API}/auth/token'" - + if _contains "${curResult}" '"token":"'; then authToken=$(echo "${curResult}" | cut -d ":" -f2 | cut -d "," -f1 | sed 's/^.\(.*\).$/\1/') export _H1="Authorization: Bearer $authToken" @@ -114,12 +113,12 @@ _cn_login() { } # Commit changes -_cn_commit(){ +_cn_commit() { _info "Commiting changes" _post "" "${CN_API}/dnszones/$h/records/commit" } -_cn_get_root(){ +_cn_get_root() { domain=$1 i=2 p=1 From 04eaf7f1751149ae0af7c29728996004cf6e1de2 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Fri, 22 Feb 2019 15:10:39 +0100 Subject: [PATCH 266/403] Add OpenProvider support --- README.md | 1 + dnsapi/README.md | 17 +++ dnsapi/dns_openprovider.sh | 244 +++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100755 dnsapi/dns_openprovider.sh diff --git a/README.md b/README.md index f79b8602..e7ad4cfb 100644 --- a/README.md +++ b/README.md @@ -357,6 +357,7 @@ You don't have to do anything manually! 1. Rackspace Cloud DNS (https://www.rackspace.com) 1. Online.net API (https://online.net/) 1. MyDevil.net (https://www.mydevil.net/) +1. OpenProvider API (https://www.openprovider.com/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 9f176c0d..3cbfe19a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1279,6 +1279,23 @@ acme.sh --issue --dns dns_mydevil -d example.com -d *.example.com After certificate is ready, you can install it with [deploy command](../deploy/README.md#14-deploy-your-cert-on-mydevilnet). +## 67. Use OpenProvider API + +First, you need to enable API access and retrieve your password hash on https://rcp.openprovider.eu/account/dashboard.php + +``` +export OPENPROVIDER_USER='username' +export OPENPROVIDER_PASSWORDHASH='xxx' +``` + +To issue a cert run: + +``` +acme.sh --issue --dns dns_openprovider -d example.com -d www.example.com +``` + +`OPENPROVIDER_USER` and `OPENPROVIDER_PASSWORDHASH` 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_openprovider.sh b/dnsapi/dns_openprovider.sh new file mode 100755 index 00000000..3d66dfe4 --- /dev/null +++ b/dnsapi/dns_openprovider.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env sh + +# This is the OpenProvider API wrapper for acme.sh +# +# Author: Sylvia van Os +# Report Bugs here: https://github.com/Neilpang/acme.sh/issues/2104 +# +# export OPENPROVIDER_USER="username" +# export OPENPROVIDER_PASSWORDHASH="hashed_password" +# +# Usage: +# acme.sh --issue --dns dns_openprovider -d example.com + +OPENPROVIDER_API="https://api.openprovider.eu/" +#OPENPROVIDER_API="https://api.cte.openprovider.eu/" # Test API + +######## Public functions ##################### + +#Usage: dns_openprovider_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_openprovider_add() { + fulldomain="$1" + txtvalue="$2" + + OPENPROVIDER_USER="${OPENPROVIDER_USER:-$(_readaccountconf_mutable OPENPROVIDER_USER)}" + OPENPROVIDER_PASSWORDHASH="${OPENPROVIDER_PASSWORDHASH:-$(_readaccountconf_mutable OPENPROVIDER_PASSWORDHASH)}" + + if [ -z "$OPENPROVIDER_USER" ] || [ -z "$OPENPROVIDER_PASSWORDHASH" ]; then + _err "You didn't specify the openprovider user and/or password hash." + return 1 + fi + + # save the username and password to the account conf file. + _saveaccountconf_mutable OPENPROVIDER_USER "$OPENPROVIDER_USER" + _saveaccountconf_mutable OPENPROVIDER_PASSWORDHASH "$OPENPROVIDER_PASSWORDHASH" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _domain_name "$_domain_name" + _debug _domain_extension "$_domain_extension" + + _debug "Getting current records" + existing_items="" + results_retrieved=0 + while true; do + _openprovider_request "$(printf '%s.%s%s' "$_domain_name" "$_domain_extension" "$results_retrieved")" + + items="$response" + while true; do + item="$(printf '%s' "$items" | _egrep_o '.*<\/openXML>' | sed -n -E 's/.*(.*<\/item>).*/\1/p')" + _debug existing_items "$existing_items" + _debug results_retrieved "$results_retrieved" + _debug item "$item" + + if [ -z "$item" ]; then + break + fi + + items="$(printf '%s' "$items" | sed "s$item")" + + results_retrieved=$((results_retrieved + 1)) + new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)\.$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + if [ -z "$new_item" ]; then + # Base record + new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + fi + + if [ -z "$(printf '%s' "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then + _debug "not an allowed record type, skipping" "$new_item" + continue + fi + + existing_items="$(printf '%s%s' "$existing_items" "$new_item")" + done + + total="$(printf '%s' "$response" | _egrep_o '.*?<\/total>' | sed -n -E 's/.*(.*)<\/total>.*/\1/p')" + + _debug total "$total" + if [ "$results_retrieved" -eq "$total" ]; then + break + fi + done + + _debug "Creating acme record" + acme_record="$(printf '%s' "$fulldomain" | sed -e "s/.$_domain_name.$_domain_extension$//")" + _openprovider_request "$(printf '%s%smaster%s%sTXT%s86400' "$_domain_name" "$_domain_extension" "$existing_items" "$acme_record" "$txtvalue")" + + return 0 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_openprovider_rm() { + fulldomain="$1" + txtvalue="$2" + + OPENPROVIDER_USER="${OPENPROVIDER_USER:-$(_readaccountconf_mutable OPENPROVIDER_USER)}" + OPENPROVIDER_PASSWORDHASH="${OPENPROVIDER_PASSWORDHASH:-$(_readaccountconf_mutable OPENPROVIDER_PASSWORDHASH)}" + + if [ -z "$OPENPROVIDER_USER" ] || [ -z "$OPENPROVIDER_PASSWORDHASH" ]; then + _err "You didn't specify the openprovider user and/or password hash." + return 1 + fi + + # save the username and password to the account conf file. + _saveaccountconf_mutable OPENPROVIDER_USER "$OPENPROVIDER_USER" + _saveaccountconf_mutable OPENPROVIDER_PASSWORDHASH "$OPENPROVIDER_PASSWORDHASH" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _domain_name "$_domain_name" + _debug _domain_extension "$_domain_extension" + + _debug "Getting current records" + existing_items="" + results_retrieved=0 + while true; do + _openprovider_request "$(printf '%s.%s%s' "$_domain_name" "$_domain_extension" "$results_retrieved")" + + # Remove acme records from items + items="$response" + while true; do + item="$(printf '%s' "$items" | _egrep_o '.*<\/openXML>' | sed -n -E 's/.*(.*<\/item>).*/\1/p')" + _debug existing_items "$existing_items" + _debug results_retrieved "$results_retrieved" + _debug item "$item" + + if [ -z "$item" ]; then + break + fi + + items="$(printf '%s' "$items" | sed "s$item")" + + results_retrieved=$((results_retrieved + 1)) + if ! printf '%s' "$item" | grep -v "$fulldomain"; then + _debug "acme record, skipping" "$item" + continue + fi + + new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)\.$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + + if [ -z "$new_item" ]; then + # Base record + new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + fi + + if [ -z "$(printf '%s' "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then + _debug "not an allowed record type, skipping" "$new_item" + continue + fi + + existing_items="$(printf '%s%s' "$existing_items" "$new_item")" + done + + total="$(printf '%s' "$response" | _egrep_o '.*?<\/total>' | sed -n -E 's/.*(.*)<\/total>.*/\1/p')" + + _debug total "$total" + + if [ "$results_retrieved" -eq "$total" ]; then + break + fi + done + + _debug "Removing acme record" + _openprovider_request "$(printf '%s%smaster%s' "$_domain_name" "$_domain_extension" "$existing_items")" + + return 0 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _domain_name=domain +# _domain_extension=com +_get_root() { + domain=$1 + i=2 + + results_retrieved=0 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + _openprovider_request "$(printf '%s%s' "$(printf "%s" "$h" | cut -d . -f 1)" "$results_retrieved")" + + items="$response" + while true; do + item="$(printf '%s' "$items" | _egrep_o '.*<\/openXML>' | sed -n -E 's/.*(.*<\/domain>).*/\1/p')" + _debug existing_items "$existing_items" + _debug results_retrieved "$results_retrieved" + _debug item "$item" + + if [ -z "$item" ]; then + break + fi + + items="$(printf '%s' "$items" | sed "s$item")" + + results_retrieved=$((results_retrieved + 1)) + + _domain_name="$(printf "%s" "$item" | sed -n -E 's/.*.*(.*)<\/name>.*<\/domain>.*/\1/p')" + _domain_extension="$(printf "%s" "$item" | sed -n -E 's/.*.*(.*)<\/extension>.*<\/domain>.*/\1/p')" + _debug _domain_name "$_domain_name" + _debug _domain_extension "$_domain_extension" + if [ "$(printf "%s.%s" "$_domain_name" "$_domain_extension")" = "$h" ]; then + return 0 + fi + done + + total="$(printf '%s' "$response" | _egrep_o '.*?<\/total>' | sed -n -E 's/.*(.*)<\/total>.*/\1/p')" + + _debug total "$total" + + if [ "$results_retrieved" -eq "$total" ]; then + results_retrieved=0 + i=$(_math "$i" + 1) + fi + done + return 1 +} + +_openprovider_request() { + request_xml=$1 + + xml_prefix=$(printf '') + xml_content=$(printf '%s%s%s' "$OPENPROVIDER_USER" "$OPENPROVIDER_PASSWORDHASH" "$request_xml") + response="$(_post "$(printf "%s%s" "$xml_prefix" "$xml_content" | tr -d '\n')" "$OPENPROVIDER_API" "" "POST" "application/xml")" + _debug response "$response" + if ! _contains "$response" "0.*"; then + _err "API request failed." + return 1 + fi +} From 725addafda8c3ffbad2b0feb2ff03b4ff518abb9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 9 Mar 2019 09:13:49 +0800 Subject: [PATCH 267/403] fix format --- dnsapi/dns_cn.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cn.sh b/dnsapi/dns_cn.sh index e90d7e60..38d1f4aa 100644 --- a/dnsapi/dns_cn.sh +++ b/dnsapi/dns_cn.sh @@ -69,7 +69,7 @@ dns_cn_rm() { curData="{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\"}" curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/delete")" _debug curData is "$curData" - + _info "commiting changes" if ! _cn_commit; then _err "commiting changes failed" @@ -132,7 +132,7 @@ _cn_get_root() { #not valid return 1 fi - + _cn_zonelist="$(_get ${CN_API}/dnszones/)" _debug _cn_zonelist "${_cn_zonelist}" From 53c018824862934939ac91c8e06c558c15d469a4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 11 Mar 2019 21:30:24 +0800 Subject: [PATCH 268/403] fix https://github.com/Neilpang/acme.sh/issues/2150 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index d81812fe..a31cf085 100755 --- a/acme.sh +++ b/acme.sh @@ -1974,7 +1974,7 @@ _save_conf() { _sdkey="$2" _sdvalue="$3" _b64encode="$4" - if [ "$_b64encode" ]; then + if [ "$_sdvalue" ] && [ "$_b64encode" ]; then _sdvalue="${B64CONF_START}$(printf "%s" "${_sdvalue}" | _base64)${B64CONF_END}" fi if [ "$_s_c_f" ]; then From f2add8de94e0ad9646f86dba3ea8666c9e39b348 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 12 Mar 2019 21:16:15 +0800 Subject: [PATCH 269/403] use acme v2 as default --- acme.sh | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index a31cf085..b3ccf9ee 100755 --- a/acme.sh +++ b/acme.sh @@ -19,8 +19,8 @@ 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_CA=$LETSENCRYPT_CA_V2 +DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V2 DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -3665,8 +3665,12 @@ issue() { _cleardomainconf "Le_ChallengeAlias" fi - Le_API="$ACME_DIRECTORY" - _savedomainconf "Le_API" "$Le_API" + if [ "$ACME_DIRECTORY" != "$DEFAULT_CA" ]; then + Le_API="$ACME_DIRECTORY" + _savedomainconf "Le_API" "$Le_API" + else + _cleardomainconf Le_API + fi if [ "$_alt_domains" = "$NO_VALUE" ]; then _alt_domains="" @@ -4500,6 +4504,16 @@ renew() { . "$DOMAIN_CONF" _debug Le_API "$Le_API" + + if [ "$Le_API" = "$LETSENCRYPT_CA_V1" ]; then + _cleardomainconf Le_API + Le_API="$DEFAULT_CA" + fi + if [ "$Le_API" = "$LETSENCRYPT_STAGING_CA_V1" ]; then + _cleardomainconf Le_API + Le_API="$DEFAULT_STAGING_CA" + fi + if [ "$Le_API" ]; then if [ "$_OLD_CA_HOST" = "$Le_API" ]; then export Le_API="$DEFAULT_CA" From db6db6a4e964befb0575030f1703d4e0a37db36a Mon Sep 17 00:00:00 2001 From: Sebastiaan Hoogeveen Date: Tue, 12 Mar 2019 14:36:42 +0100 Subject: [PATCH 270/403] Removed overwriting of the HTTP header file before sending a request. --- dnsapi/dns_nederhost.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/dnsapi/dns_nederhost.sh b/dnsapi/dns_nederhost.sh index 84c5ecd1..0954ab65 100755 --- a/dnsapi/dns_nederhost.sh +++ b/dnsapi/dns_nederhost.sh @@ -112,8 +112,6 @@ _nederhost_rest() { export _H1="Authorization: Bearer $NederHost_Key" export _H2="Content-Type: application/json" - :>"$HTTP_HEADER" - if [ "$m" != "GET" ]; then _debug data "$data" response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")" From 77f96b386e9d60380a1dcfc80577c126eadba49c Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 13 Mar 2019 20:42:02 +0800 Subject: [PATCH 271/403] support Windows scheduler. fix https://github.com/Neilpang/acme.sh/issues/2145 --- acme.sh | 130 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/acme.sh b/acme.sh index b3ccf9ee..ceacd574 100755 --- a/acme.sh +++ b/acme.sh @@ -9,6 +9,9 @@ PROJECT_ENTRY="acme.sh" PROJECT="https://github.com/Neilpang/$PROJECT_NAME" DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME" + +_WINDOWS_SCHEDULER_NAME="$PROJECT_NAME.cron" + _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" @@ -4923,35 +4926,108 @@ _installcert() { } +__read_password() { + unset _pp + prompt="Enter Password:" + while IFS= read -p "$prompt" -r -s -n 1 char + do + if [ "$char" = $'\0' ]; then + break + fi + prompt='*' + _pp="$_pp$char" + done + echo "$_pp" +} + +_install_win_taskscheduler() { + _lesh="$1" + _centry="$2" + _randomminute="$3" + if ! _exists cygpath; then + _err "cygpath not found" + return 1 + fi + if ! _exists schtasks; then + _err "schtasks.exe is not found, are you on Windows?" + return 1 + fi + _winbash="$(cygpath -w $(which bash))" + _debug _winbash "$_winbash" + if [ -z "$_winbash" ]; then + _err "can not find bash path" + return 1 + fi + _myname="$(whoami)" + _debug "_myname" "$_myname" + if [ -z "$_myname" ]; then + _err "can not find my user name" + return 1 + fi + _debug "_lesh" "$_lesh" + + _info "To install scheduler task in your Windows account, you must input your windows password." + _info "$PROJECT_NAME doesn't save your password." + _info "Please input your Windows password for: $(__green "$_myname")" + _password="$(__read_password)" + #SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'" >/dev/null + echo SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "\"$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'\"" | cmd.exe >/dev/null + echo + +} + +_uninstall_win_taskscheduler() { + if ! _exists schtasks; then + _err "schtasks.exe is not found, are you on Windows?" + return 1 + fi + if ! echo SCHTASKS /query /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null; then + _debug "scheduler $_WINDOWS_SCHEDULER_NAME is not found." + else + _info "Removing $_WINDOWS_SCHEDULER_NAME" + echo SCHTASKS /delete /f /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null + fi +} + #confighome installcronjob() { _c_home="$1" _initpath _CRONTAB="crontab" + if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then + lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" + else + _err "Can not install cronjob, $PROJECT_ENTRY not found." + return 1 + fi + if [ "$_c_home" ]; then + _c_entry="--config-home \"$_c_home\" " + fi + _t=$(_time) + random_minute=$(_math $_t % 60) + if ! _exists "$_CRONTAB" && _exists "fcrontab"; then _CRONTAB="fcrontab" fi + if ! _exists "$_CRONTAB"; then + if _exists cygpath && _exists schtasks.exe; then + _info "It seems you are on Windows, let's install Windows scheduler task." + if _install_win_taskscheduler "$lesh" "$_c_entry" "$random_minute"; then + _info "Install Windows scheduler task success." + return 0 + else + _err "Install Windows scheduler task failed." + return 1 + fi + fi _err "crontab/fcrontab doesn't exist, so, we can not install cron jobs." _err "All your certs will not be renewed automatically." _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." return 1 fi - _info "Installing cron job" if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then - if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then - lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" - else - _err "Can not install cronjob, $PROJECT_ENTRY not found." - return 1 - fi - - if [ "$_c_home" ]; then - _c_entry="--config-home \"$_c_home\" " - fi - _t=$(_time) - random_minute=$(_math $_t % 60) if _exists uname && uname -a | grep SunOS >/dev/null; then $_CRONTAB -l | { cat @@ -4979,6 +5055,16 @@ uninstallcronjob() { fi if ! _exists "$_CRONTAB"; then + if _exists cygpath && _exists schtasks.exe; then + _info "It seems you are on Windows, let's uninstall Windows scheduler task." + if _uninstall_win_taskscheduler; then + _info "Uninstall Windows scheduler task success." + return 0 + else + _err "Uninstall Windows scheduler task failed." + return 1 + fi + fi return fi _info "Removing cron job" @@ -5306,13 +5392,17 @@ _precheck() { if [ -z "$_nocron" ]; then if ! _exists "crontab" && ! _exists "fcrontab"; then - _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." - _err "We need to set cron job to renew the certs automatically." - _err "Otherwise, your certs will not be able to be renewed automatically." - if [ -z "$FORCE" ]; then - _err "Please add '--force' and try install again to go without crontab." - _err "./$PROJECT_ENTRY --install --force" - return 1 + if _exists cygpath && _exists schtasks.exe; then + _info "It seems you are on Windows, we will install Windows scheduler task." + else + _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." + _err "We need to set cron job to renew the certs automatically." + _err "Otherwise, your certs will not be able to be renewed automatically." + if [ -z "$FORCE" ]; then + _err "Please add '--force' and try install again to go without crontab." + _err "./$PROJECT_ENTRY --install --force" + return 1 + fi fi fi fi From 0b04a7f17f467db477fca6588d8d078e2ac017f0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 13 Mar 2019 20:49:26 +0800 Subject: [PATCH 272/403] fix format --- acme.sh | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index ceacd574..127b6ab3 100755 --- a/acme.sh +++ b/acme.sh @@ -4929,13 +4929,12 @@ _installcert() { __read_password() { unset _pp prompt="Enter Password:" - while IFS= read -p "$prompt" -r -s -n 1 char - do - if [ "$char" = $'\0' ]; then - break - fi - prompt='*' - _pp="$_pp$char" + while IFS= read -p "$prompt" -r -s -n 1 char; do + if [ "$char" = $'\0' ]; then + break + fi + prompt='*' + _pp="$_pp$char" done echo "$_pp" } @@ -4985,7 +4984,7 @@ _uninstall_win_taskscheduler() { _debug "scheduler $_WINDOWS_SCHEDULER_NAME is not found." else _info "Removing $_WINDOWS_SCHEDULER_NAME" - echo SCHTASKS /delete /f /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null + echo SCHTASKS /delete /f /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null fi } From 4ebad10557d3f05de6b4bddb8c173a2030af4145 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 13 Mar 2019 21:11:59 +0800 Subject: [PATCH 273/403] fix format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 127b6ab3..1887bc90 100755 --- a/acme.sh +++ b/acme.sh @@ -4931,7 +4931,7 @@ __read_password() { prompt="Enter Password:" while IFS= read -p "$prompt" -r -s -n 1 char; do if [ "$char" = $'\0' ]; then - break + break fi prompt='*' _pp="$_pp$char" From 532e79c7d0d8dcfcf514195c0d8b2873aa4717ee Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Wed, 13 Mar 2019 14:14:40 +0100 Subject: [PATCH 274/403] Fix reading endpoint --- dnsapi/dns_hostingde.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 50aa142f..1819e639 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -28,6 +28,7 @@ dns_hostingde_rm() { _hostingde_apiKey() { HOSTINGDE_APIKEY="${HOSTINGDE_APIKEY:-$(_readaccountconf_mutable HOSTINGDE_APIKEY)}" + HOSTINGDE_ENDPOINT="${HOSTINGDE_ENDPOINT:-$(_readaccountconf_mutable HOSTINGDE_ENDPOINT)}" if [ -z "$HOSTINGDE_APIKEY" ] || [ -z "$HOSTINGDE_ENDPOINT" ]; then HOSTINGDE_APIKEY="" HOSTINGDE_ENDPOINT="" From 0f00862e5efd76cf998fe2b7511432c4893a249f Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 13 Mar 2019 21:28:30 +0800 Subject: [PATCH 275/403] support windows scheduler (#2158) * support Windows scheduler. fix https://github.com/Neilpang/acme.sh/issues/2145 --- acme.sh | 129 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 109 insertions(+), 20 deletions(-) diff --git a/acme.sh b/acme.sh index b3ccf9ee..1887bc90 100755 --- a/acme.sh +++ b/acme.sh @@ -9,6 +9,9 @@ PROJECT_ENTRY="acme.sh" PROJECT="https://github.com/Neilpang/$PROJECT_NAME" DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME" + +_WINDOWS_SCHEDULER_NAME="$PROJECT_NAME.cron" + _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" @@ -4923,35 +4926,107 @@ _installcert() { } +__read_password() { + unset _pp + prompt="Enter Password:" + while IFS= read -p "$prompt" -r -s -n 1 char; do + if [ "$char" = $'\0' ]; then + break + fi + prompt='*' + _pp="$_pp$char" + done + echo "$_pp" +} + +_install_win_taskscheduler() { + _lesh="$1" + _centry="$2" + _randomminute="$3" + if ! _exists cygpath; then + _err "cygpath not found" + return 1 + fi + if ! _exists schtasks; then + _err "schtasks.exe is not found, are you on Windows?" + return 1 + fi + _winbash="$(cygpath -w $(which bash))" + _debug _winbash "$_winbash" + if [ -z "$_winbash" ]; then + _err "can not find bash path" + return 1 + fi + _myname="$(whoami)" + _debug "_myname" "$_myname" + if [ -z "$_myname" ]; then + _err "can not find my user name" + return 1 + fi + _debug "_lesh" "$_lesh" + + _info "To install scheduler task in your Windows account, you must input your windows password." + _info "$PROJECT_NAME doesn't save your password." + _info "Please input your Windows password for: $(__green "$_myname")" + _password="$(__read_password)" + #SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'" >/dev/null + echo SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "\"$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'\"" | cmd.exe >/dev/null + echo + +} + +_uninstall_win_taskscheduler() { + if ! _exists schtasks; then + _err "schtasks.exe is not found, are you on Windows?" + return 1 + fi + if ! echo SCHTASKS /query /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null; then + _debug "scheduler $_WINDOWS_SCHEDULER_NAME is not found." + else + _info "Removing $_WINDOWS_SCHEDULER_NAME" + echo SCHTASKS /delete /f /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null + fi +} + #confighome installcronjob() { _c_home="$1" _initpath _CRONTAB="crontab" + if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then + lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" + else + _err "Can not install cronjob, $PROJECT_ENTRY not found." + return 1 + fi + if [ "$_c_home" ]; then + _c_entry="--config-home \"$_c_home\" " + fi + _t=$(_time) + random_minute=$(_math $_t % 60) + if ! _exists "$_CRONTAB" && _exists "fcrontab"; then _CRONTAB="fcrontab" fi + if ! _exists "$_CRONTAB"; then + if _exists cygpath && _exists schtasks.exe; then + _info "It seems you are on Windows, let's install Windows scheduler task." + if _install_win_taskscheduler "$lesh" "$_c_entry" "$random_minute"; then + _info "Install Windows scheduler task success." + return 0 + else + _err "Install Windows scheduler task failed." + return 1 + fi + fi _err "crontab/fcrontab doesn't exist, so, we can not install cron jobs." _err "All your certs will not be renewed automatically." _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." return 1 fi - _info "Installing cron job" if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then - if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then - lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" - else - _err "Can not install cronjob, $PROJECT_ENTRY not found." - return 1 - fi - - if [ "$_c_home" ]; then - _c_entry="--config-home \"$_c_home\" " - fi - _t=$(_time) - random_minute=$(_math $_t % 60) if _exists uname && uname -a | grep SunOS >/dev/null; then $_CRONTAB -l | { cat @@ -4979,6 +5054,16 @@ uninstallcronjob() { fi if ! _exists "$_CRONTAB"; then + if _exists cygpath && _exists schtasks.exe; then + _info "It seems you are on Windows, let's uninstall Windows scheduler task." + if _uninstall_win_taskscheduler; then + _info "Uninstall Windows scheduler task success." + return 0 + else + _err "Uninstall Windows scheduler task failed." + return 1 + fi + fi return fi _info "Removing cron job" @@ -5306,13 +5391,17 @@ _precheck() { if [ -z "$_nocron" ]; then if ! _exists "crontab" && ! _exists "fcrontab"; then - _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." - _err "We need to set cron job to renew the certs automatically." - _err "Otherwise, your certs will not be able to be renewed automatically." - if [ -z "$FORCE" ]; then - _err "Please add '--force' and try install again to go without crontab." - _err "./$PROJECT_ENTRY --install --force" - return 1 + if _exists cygpath && _exists schtasks.exe; then + _info "It seems you are on Windows, we will install Windows scheduler task." + else + _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." + _err "We need to set cron job to renew the certs automatically." + _err "Otherwise, your certs will not be able to be renewed automatically." + if [ -z "$FORCE" ]; then + _err "Please add '--force' and try install again to go without crontab." + _err "./$PROJECT_ENTRY --install --force" + return 1 + fi fi fi fi From 709d82e7641916da2ad9e9035472e4f5f622dd0d Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 13 Mar 2019 21:32:10 +0800 Subject: [PATCH 276/403] sync sync --- dnsapi/dns_hostingde.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 50aa142f..1819e639 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -28,6 +28,7 @@ dns_hostingde_rm() { _hostingde_apiKey() { HOSTINGDE_APIKEY="${HOSTINGDE_APIKEY:-$(_readaccountconf_mutable HOSTINGDE_APIKEY)}" + HOSTINGDE_ENDPOINT="${HOSTINGDE_ENDPOINT:-$(_readaccountconf_mutable HOSTINGDE_ENDPOINT)}" if [ -z "$HOSTINGDE_APIKEY" ] || [ -z "$HOSTINGDE_ENDPOINT" ]; then HOSTINGDE_APIKEY="" HOSTINGDE_ENDPOINT="" From 5048c6c22a19ae31006c3d5fa6a15733045fd4f8 Mon Sep 17 00:00:00 2001 From: tambetliiv <35329231+tambetliiv@users.noreply.github.com> Date: Thu, 14 Mar 2019 14:20:39 +0200 Subject: [PATCH 277/403] Add zone.ee (zone.eu) DNS API (#2151) * add zone.ee (zone.eu) dns api --- README.md | 1 + dnsapi/README.md | 16 +++++ dnsapi/dns_zone.sh | 149 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100755 dnsapi/dns_zone.sh diff --git a/README.md b/README.md index fae0bbf5..50466ad7 100644 --- a/README.md +++ b/README.md @@ -360,6 +360,7 @@ You don't have to do anything manually! 1. MyDevil.net (https://www.mydevil.net/) 1. Core-Networks.de (https://core-networks.de) 1. NederHost API (https://www.nederhost.nl/) +1. Zone.ee (zone.eu) API (https://api.zone.eu/v2) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 7ef1c306..de3148cf 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1310,6 +1310,22 @@ To issue a certificate run: acme.sh --issue --dns dns_nederhost -d example.com -d *.example.com ``` +## 69. Use Zone.ee DNS API + +First, you'll need to retrive your API key. Estonian insructions https://help.zone.eu/kb/zoneid-api-v2/ + +``` +export ZONE_Username=yourusername +export ZONE_Key=keygoeshere +``` + +To issue a cert run: + +``` +acme.sh --issue -d example.com -d www.example.com --dns dns_zone +``` + +`ZONE_Username` and `ZONE_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_zone.sh b/dnsapi/dns_zone.sh new file mode 100755 index 00000000..847e32cd --- /dev/null +++ b/dnsapi/dns_zone.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env sh + +# Zone.ee dns API +# https://help.zone.eu/kb/zoneid-api-v2/ +# required ZONE_Username and ZONE_Key + +ZONE_Api="https://api.zone.eu/v2" +######## Public functions ##################### + +#Usage: dns_zone_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_zone_add() { + fulldomain=$1 + txtvalue=$2 + _info "Using zone.ee dns api" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + ZONE_Username="${ZONE_Username:-$(_readaccountconf_mutable ZONE_Username)}" + ZONE_Key="${ZONE_Key:-$(_readaccountconf_mutable ZONE_Key)}" + if [ -z "$ZONE_Username" ] || [ -z "$ZONE_Key" ]; then + ZONE_Username="" + ZONE_Key="" + _err "Zone api key and username must be present." + return 1 + fi + _saveaccountconf_mutable ZONE_Username "$ZONE_Username" + _saveaccountconf_mutable ZONE_Key "$ZONE_Key" + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug "Adding txt record" + + if _zone_rest POST "dns/${_domain}/txt" "{\"name\": \"$fulldomain\", \"destination\": \"$txtvalue\"}"; then + if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then + _info "Added, OK" + return 0 + else + _err "Adding txt record error." + return 1 + fi + else + _err "Adding txt record error." + fi +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_zone_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using zone.ee dns api" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + ZONE_Username="${ZONE_Username:-$(_readaccountconf_mutable ZONE_Username)}" + ZONE_Key="${ZONE_Key:-$(_readaccountconf_mutable ZONE_Key)}" + if [ -z "$ZONE_Username" ] || [ -z "$ZONE_Key" ]; then + ZONE_Username="" + ZONE_Key="" + _err "Zone api key and username must be present." + return 1 + fi + _saveaccountconf_mutable ZONE_Username "$ZONE_Username" + _saveaccountconf_mutable ZONE_Key "$ZONE_Key" + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug "Getting txt records" + _debug _domain "$_domain" + + _zone_rest GET "dns/${_domain}/txt" + + if printf "%s" "$response" | grep \"error\" >/dev/null; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain\"" | wc -l) + _debug count "$count" + if [ "$count" = "0" ]; then + _info "Nothing to remove." + else + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\",\"resource_url\":\"[^\"]*\",\"name\":\"$fulldomain\"," | cut -d : -f2 | cut -d , -f1 | tr -d \" | _head_n 1) + if [ -z "$record_id" ]; then + _err "No id found to remove." + return 1 + fi + if ! _zone_rest DELETE "dns/${_domain}/txt/$record_id"; then + _err "Record deleting error." + return 1 + fi + _info "Record deleted" + return 0 + fi + +} + +#################### Private functions below ################################## + +_zone_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + realm="$(printf "%s" "$ZONE_Username:$ZONE_Key" | _base64)" + + export _H1="Authorization: Basic $realm" + export _H2="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$ZONE_Api/$ep" "" "$m")" + else + response="$(_get "$ZONE_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} + +_get_root() { + domain=$1 + i=2 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + return 1 + fi + if ! _zone_rest GET "dns/$h/a"; then + return 1 + fi + if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + _domain=$h + return 0 + fi + i=$(_math "$i" + 1) + done + return 0 +} From 46fbd7f1e1fc355c34c76b6457e6a6959b5a387b Mon Sep 17 00:00:00 2001 From: "Steven M. Miano" Date: Thu, 14 Mar 2019 08:41:13 -0400 Subject: [PATCH 278/403] support ultradns.com api (#2117) support ultradns.com api (#2117) --- README.md | 1 + dnsapi/README.md | 24 +++++++ dnsapi/dns_ultra.sh | 164 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 dnsapi/dns_ultra.sh diff --git a/README.md b/README.md index 50466ad7..68d1b57d 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,7 @@ You don't have to do anything manually! 1. Core-Networks.de (https://core-networks.de) 1. NederHost API (https://www.nederhost.nl/) 1. Zone.ee (zone.eu) API (https://api.zone.eu/v2) +1. UltraDNS API (https://portal.ultradns.com) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index de3148cf..3cce294a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1326,6 +1326,30 @@ acme.sh --issue -d example.com -d www.example.com --dns dns_zone ``` `ZONE_Username` and `ZONE_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +## 70. Use UltraDNS API + +UltraDNS is a paid for service that provides DNS, as well as Web and Mail forwarding (as well as reporting, auditing, and advanced tools). + +More information can be found here: https://www.security.neustar/lp/ultra20/index.html + +The REST API documentation for this service is found here: https://portal.ultradns.com/static/docs/REST-API_User_Guide.pdf + +Set your UltraDNS User name, and password; these would be the same you would use here: + +https://portal.ultradns.com/ - or if you create an API only user, that username and password would be better utilized. + +``` +export ULTRA_USR="abcd" +export ULTRA_PWD="efgh" + +To issue a cert run: + +acme.sh --issue --dns dns_ultra -d example.com -d www.example.com +``` + +`ULTRA_USR` and `ULTRA_PWD` will be saved in `~/.acme.sh/account.conf` and will be resued when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_ultra.sh b/dnsapi/dns_ultra.sh new file mode 100644 index 00000000..0100b3b7 --- /dev/null +++ b/dnsapi/dns_ultra.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env sh + +# +# ULTRA_USR="your_user_goes_here" +# +# ULTRA_PWD="some_password_goes_here" + +ULTRA_API="https://restapi.ultradns.com/v2/" + +#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt" +dns_ultra_add() { + fulldomain=$1 + txtvalue=$2 + export txtvalue + ULTRA_USR="${ULTRA_USR:-$(_readaccountconf_mutable ULTRA_USR)}" + ULTRA_PWD="${ULTRA_PWD:-$(_readaccountconf_mutable ULTRA_PWD)}" + if [ -z "$ULTRA_USR" ] || [ -z "$ULTRA_PWD" ]; then + ULTRA_USR="" + ULTRA_PWD="" + _err "You didn't specify an UltraDNS username and password yet" + return 1 + fi + # save the username and password to the account conf file. + _saveaccountconf_mutable ULTRA_USR "$ULTRA_USR" + _saveaccountconf_mutable ULTRA_PWD "$ULTRA_PWD" + _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" + _ultra_rest GET "zones/${_domain_id}/rrsets/TXT?q=value:${fulldomain}" + if printf "%s" "$response" | grep \"totalCount\" >/dev/null; then + _err "Error, it would appear that this record already exists. Please review existing TXT records for this domain." + return 1 + fi + + _info "Adding record" + if _ultra_rest POST "zones/$_domain_id/rrsets/TXT/${_sub_domain}" '{"ttl":300,"rdata":["'"${txtvalue}"'"]}'; then + if _contains "$response" "Successful"; then + _info "Added, OK" + return 0 + elif _contains "$response" "Resource Record of type 16 with these attributes already exists"; then + _info "Already exists, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + +} + +dns_ultra_rm() { + fulldomain=$1 + txtvalue=$2 + export txtvalue + ULTRA_USR="${ULTRA_USR:-$(_readaccountconf_mutable ULTRA_USR)}" + ULTRA_PWD="${ULTRA_PWD:-$(_readaccountconf_mutable ULTRA_PWD)}" + if [ -z "$ULTRA_USR" ] || [ -z "$ULTRA_PWD" ]; then + ULTRA_USR="" + ULTRA_PWD="" + _err "You didn't specify an UltraDNS username and password yet" + 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" + _ultra_rest GET "zones/${_domain_id}/rrsets?q=kind:RECORDS+owner:${_sub_domain}" + + if ! printf "%s" "$response" | grep \"resultInfo\" >/dev/null; then + _err "There was an error in obtaining the resource records for ${_domain_id}" + return 1 + fi + + count=$(echo "$response" | _egrep_o "\"returnedCount\":[^,]*" | cut -d: -f2 | cut -d'}' -f1) + _debug count "${count}" + if [ "${count}" = "" ]; then + _info "Text record is not present, will not delete anything." + else + if ! _ultra_rest DELETE "zones/$_domain_id/rrsets/TXT/${_sub_domain}" '{"ttl":300,"rdata":["'"${txtvalue}"'"]}'; then + _err "Deleting the record did not succeed, please verify/check." + return 1 + fi + _contains "$response" "" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + _debug response "$response" + if [ -z "$h" ]; then + #not valid + return 1 + fi + if ! _ultra_rest GET "zones"; then + return 1 + fi + if _contains "${response}" "${h}." >/dev/null; then + _domain_id=$(echo "$response" | _egrep_o "${h}") + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="${h}" + _debug sub_domain "${_sub_domain}" + _debug domain "${_domain}" + return 0 + fi + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_ultra_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + _debug TOKEN "${AUTH_TOKEN}" + + _ultra_login + export _H1="Content-Type: application/json" + export _H2="Authorization: Bearer ${AUTH_TOKEN}" + + if [ "$m" != "GET" ]; then + _debug data "${data}" + response="$(_post "${data}" "${ULTRA_API}"/"${ep}" "" "${m}")" + else + response="$(_get "$ULTRA_API/$ep")" + fi +} + +_ultra_login() { + export _H1="" + export _H2="" + AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_API}authorization/token" | cut -d, -f3 | cut -d\" -f4) + export AUTH_TOKEN +} From dbc44c08df9e06c5db45ee85e797185dea81fd6e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 13:38:17 +0800 Subject: [PATCH 279/403] fix for solaris --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 1887bc90..f4f51268 100755 --- a/acme.sh +++ b/acme.sh @@ -3751,7 +3751,7 @@ issue() { return 1 fi - Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug Le_OrderFinalize "$Le_OrderFinalize" if [ -z "$Le_OrderFinalize" ]; then _err "Create new order error. Le_OrderFinalize not found. $response" @@ -3763,7 +3763,7 @@ issue() { #for dns manual mode _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize" - _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _authorizations_seg="$(echo "$response" | _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." @@ -3849,7 +3849,7 @@ $_authorizations_map" thumbprint="$(__calc_account_thumbprint)" fi - entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" + entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then _err "Error, can not get domain token entry $d" @@ -3861,7 +3861,7 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi - token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" + token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" if [ -z "$token" ]; then @@ -3871,9 +3871,9 @@ $_authorizations_map" return 1 fi if [ "$ACME_VERSION" = "2" ]; then - uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" + uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" else - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + uri="$(echo "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" fi _debug uri "$uri" From d0d749074e35057aacc020448b700a3af7e4e63f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 14:00:15 +0800 Subject: [PATCH 280/403] fix for solaris --- acme.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index f4f51268..3c7bc6d0 100755 --- a/acme.sh +++ b/acme.sh @@ -4194,7 +4194,7 @@ $_authorizations_map" fi if [ "$status" = "invalid" ]; then - error="$(echo "$response" | tr -d "\r\n" | _egrep_o '"error":\{[^\}]*')" + error="$(echo "$response" | _egrep_o '"error":\{[^\}]*')" _debug2 error "$error" errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)" _debug2 errordetail "$errordetail" @@ -4260,7 +4260,7 @@ $_authorizations_map" while [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do if _contains "$response" "\"status\":\"valid\""; then _debug "Order status is valid." - Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + Le_LinkCert="$(echo "$response" | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" _debug Le_LinkCert "$Le_LinkCert" if [ -z "$Le_LinkCert" ]; then _err "Sign error, can not find Le_LinkCert" @@ -5195,7 +5195,7 @@ _deactivate() { _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 '"')" + _authorizations_seg="$(echo "$response" | _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." @@ -5241,16 +5241,16 @@ _deactivate() { fi _debug "Trigger validation." vtype="$VTYPE_DNS" - entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" + entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then _err "Error, can not get domain token $d" return 1 fi - token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" + token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" + uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" keyauthorization="$token.$thumbprint" @@ -5272,11 +5272,11 @@ _deactivate() { break fi - _vtype="$(printf "%s\n" "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')" + _vtype="$(echo "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')" _debug _vtype "$_vtype" _info "Found $_vtype" - uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" + uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then From 2ffd8637e10d3ed7178769d219a6a85a09d79c63 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 14:28:24 +0800 Subject: [PATCH 281/403] fix standalone content --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 3c7bc6d0..d9ea4ee5 100755 --- a/acme.sh +++ b/acme.sh @@ -2111,7 +2111,7 @@ _startserver() { echo 'HTTP/1.0 200 OK'; \ echo 'Content-Length\: $_content_len'; \ echo ''; \ -printf '$content';" & +printf -- '$content';" & serverproc="$!" } From 3f35006c264cf002a875b21a9cde97cc67ffccfa Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 14:35:33 +0800 Subject: [PATCH 282/403] fix error message --- dnsapi/dns_namecom.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 254952d6..a9a7ac51 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -130,6 +130,8 @@ _namecom_login() { if [ "$retcode" ]; then _info "Successfully logged in." else + _err "$response" + _err "Please add your ip to api whitelist" _err "Logging in failed." return 1 fi From 82b0ebb787ac87d1712a8cc3cc4982e030ed659a Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 14:53:02 +0800 Subject: [PATCH 283/403] minor, remove dns records only when it's added success --- acme.sh | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index d9ea4ee5..752b49bc 100755 --- a/acme.sh +++ b/acme.sh @@ -3931,21 +3931,21 @@ $_authorizations_map" else txtdomain="_acme-challenge.$_d_alias" fi - dns_entries="${dns_entries}${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot" + dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot" else txtdomain="_acme-challenge.$_dns_root_d" - dns_entries="${dns_entries}${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot" + dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot" fi + _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" - _debug d_api "$d_api" - dns_entries="$dns_entries$dvsep$txt${dvsep}$d_api -" - _debug2 "$dns_entries" + + dns_entry="$dns_entry$dvsep$txt${dvsep}$d_api" + _debug2 dns_entry "$dns_entry" if [ "$d_api" ]; then _info "Found domain api file: $d_api" else @@ -3984,6 +3984,9 @@ $_authorizations_map" _clearup return 1 fi + dns_entries="$dns_entries$dns_entry +" + _debug2 "$dns_entries" dnsadded='1' fi done From 2b36f4f57f1f9a484c5f6caf8fcff5472636a6a4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 15:07:34 +0800 Subject: [PATCH 284/403] update --- dnsapi/dns_namecom.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index a9a7ac51..769a2082 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -13,6 +13,8 @@ dns_namecom_add() { fulldomain=$1 txtvalue=$2 + Namecom_Username="${Namecom_Username:-$(_readaccountconf_mutable Namecom_Username)}" + Namecom_Token="${Namecom_Token:-$(_readaccountconf_mutable Namecom_Token)}" # First we need name.com credentials. if [ -z "$Namecom_Username" ]; then Namecom_Username="" @@ -29,8 +31,8 @@ dns_namecom_add() { fi # Save them in configuration. - _saveaccountconf Namecom_Username "$Namecom_Username" - _saveaccountconf Namecom_Token "$Namecom_Token" + _saveaccountconf_mutable Namecom_Username "$Namecom_Username" + _saveaccountconf_mutable Namecom_Token "$Namecom_Token" # Login in using API if ! _namecom_login; then @@ -46,7 +48,7 @@ dns_namecom_add() { # Add TXT record. _namecom_addtxt_json="{\"host\":\"$_sub_domain\",\"type\":\"TXT\",\"answer\":\"$txtvalue\",\"ttl\":\"300\"}" if _namecom_rest POST "domains/$_domain/records" "$_namecom_addtxt_json"; then - _retvalue=$(printf "%s\n" "$response" | _egrep_o "\"$_sub_domain\"") + _retvalue=$(echo "$response" | _egrep_o "\"$_sub_domain\"") if [ "$_retvalue" ]; then _info "Successfully added TXT record, ready for validation." return 0 @@ -63,6 +65,8 @@ dns_namecom_rm() { fulldomain=$1 txtvalue=$2 + Namecom_Username="${Namecom_Username:-$(_readaccountconf_mutable Namecom_Username)}" + Namecom_Token="${Namecom_Token:-$(_readaccountconf_mutable Namecom_Token)}" if ! _namecom_login; then return 1 fi @@ -75,7 +79,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\":\"TXT\",\"answer\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+) + _record_id=$(echo "$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." @@ -126,7 +130,7 @@ _namecom_login() { _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\"") + retcode=$(echo "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"") if [ "$retcode" ]; then _info "Successfully logged in." else From 653c77e852b879559c3daa338b9f4f247ac97ed2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 15:09:49 +0800 Subject: [PATCH 285/403] update --- dnsapi/dns_namecom.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh index 769a2082..0d5dd2c4 100755 --- a/dnsapi/dns_namecom.sh +++ b/dnsapi/dns_namecom.sh @@ -29,7 +29,8 @@ dns_namecom_add() { _err "Please specify that in your environment variable." return 1 fi - + _debug Namecom_Username "$Namecom_Username" + _secure_debug Namecom_Token "$Namecom_Token" # Save them in configuration. _saveaccountconf_mutable Namecom_Username "$Namecom_Username" _saveaccountconf_mutable Namecom_Token "$Namecom_Token" From c74d597c84342f14a3f5af9d7c6c2514383a1242 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Mar 2019 18:34:44 +0800 Subject: [PATCH 286/403] add debug info --- dnsapi/dns_netcup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 2273eb7c..d5d7c22e 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -8,6 +8,7 @@ end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON" client="" dns_netcup_add() { + _debug NC_Apikey "$NC_Apikey" login if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then _err "No Credentials given" From 7decce97180bd1431eb63c6ed027bbb2898bdff1 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 18 Mar 2019 14:48:01 +0100 Subject: [PATCH 287/403] Resolve comments on pull request --- dnsapi/dns_openprovider.sh | 60 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/dnsapi/dns_openprovider.sh b/dnsapi/dns_openprovider.sh index 3d66dfe4..a0171e4e 100755 --- a/dnsapi/dns_openprovider.sh +++ b/dnsapi/dns_openprovider.sh @@ -50,7 +50,7 @@ dns_openprovider_add() { items="$response" while true; do - item="$(printf '%s' "$items" | _egrep_o '.*<\/openXML>' | sed -n -E 's/.*(.*<\/item>).*/\1/p')" + item="$(echo "$items" | _egrep_o '.*<\/openXML>' | sed -n 's/.*\(.*<\/item>\).*/\1/p')" _debug existing_items "$existing_items" _debug results_retrieved "$results_retrieved" _debug item "$item" @@ -59,24 +59,24 @@ dns_openprovider_add() { break fi - items="$(printf '%s' "$items" | sed "s$item")" + items="$(echo "$items" | sed "s|${item}||")" - results_retrieved=$((results_retrieved + 1)) - new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)\.$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + results_retrieved="$(_math "$results_retrieved" + 1)" + new_item="$(echo "$item" | sed -n 's/.*.*\(\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p')" if [ -z "$new_item" ]; then # Base record - new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + new_item="$(echo "$item" | sed -n 's/.*.*\(\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p')" fi - if [ -z "$(printf '%s' "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then + if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then _debug "not an allowed record type, skipping" "$new_item" continue fi - existing_items="$(printf '%s%s' "$existing_items" "$new_item")" + existing_items="$existing_items$new_item" done - total="$(printf '%s' "$response" | _egrep_o '.*?<\/total>' | sed -n -E 's/.*(.*)<\/total>.*/\1/p')" + total="$(echo "$response" | _egrep_o '.*?<\/total>' | sed -n 's/.*\(.*\)<\/total>.*/\1/p')" _debug total "$total" if [ "$results_retrieved" -eq "$total" ]; then @@ -85,7 +85,7 @@ dns_openprovider_add() { done _debug "Creating acme record" - acme_record="$(printf '%s' "$fulldomain" | sed -e "s/.$_domain_name.$_domain_extension$//")" + acme_record="$(echo "$fulldomain" | sed -e "s/.$_domain_name.$_domain_extension$//")" _openprovider_request "$(printf '%s%smaster%s%sTXT%s86400' "$_domain_name" "$_domain_extension" "$existing_items" "$acme_record" "$txtvalue")" return 0 @@ -127,7 +127,7 @@ dns_openprovider_rm() { # Remove acme records from items items="$response" while true; do - item="$(printf '%s' "$items" | _egrep_o '.*<\/openXML>' | sed -n -E 's/.*(.*<\/item>).*/\1/p')" + item="$(echo "$items" | _egrep_o '.*<\/openXML>' | sed -n 's/.*\(.*<\/item>\).*/\1/p')" _debug existing_items "$existing_items" _debug results_retrieved "$results_retrieved" _debug item "$item" @@ -136,30 +136,30 @@ dns_openprovider_rm() { break fi - items="$(printf '%s' "$items" | sed "s$item")" + items="$(echo "$items" | sed "s|${item}||")" - results_retrieved=$((results_retrieved + 1)) - if ! printf '%s' "$item" | grep -v "$fulldomain"; then + results_retrieved="$(_math "$results_retrieved" + 1)" + if ! echo "$item" | grep -v "$fulldomain"; then _debug "acme record, skipping" "$item" continue fi - new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)\.$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + new_item="$(echo "$item" | sed -n 's/.*.*\(\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p')" if [ -z "$new_item" ]; then # Base record - new_item="$(printf '%s' "$item" | sed -n -E "s/.*.*((.*)$_domain_name\.$_domain_extension<\/name>.*(.*<\/type>).*(.*<\/value>).*(.*<\/prio>).*(.*<\/ttl>)).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p")" + new_item="$(echo "$item" | sed -n 's/.*.*\(\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/\2<\/name>\3\4\5\6<\/item>/p')" fi - if [ -z "$(printf '%s' "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then + if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then _debug "not an allowed record type, skipping" "$new_item" continue fi - existing_items="$(printf '%s%s' "$existing_items" "$new_item")" + existing_items="$existing_items$new_item" done - total="$(printf '%s' "$response" | _egrep_o '.*?<\/total>' | sed -n -E 's/.*(.*)<\/total>.*/\1/p')" + total="$(echo "$response" | _egrep_o '.*?<\/total>' | sed -n 's/.*\(.*\)<\/total>.*/\1/p')" _debug total "$total" @@ -185,18 +185,18 @@ _get_root() { results_retrieved=0 while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) + h=$(echo "$domain" | cut -d . -f $i-100) _debug h "$h" if [ -z "$h" ]; then #not valid return 1 fi - _openprovider_request "$(printf '%s%s' "$(printf "%s" "$h" | cut -d . -f 1)" "$results_retrieved")" + _openprovider_request "$(printf '%s%s' "$(echo "$h" | cut -d . -f 1)" "$results_retrieved")" items="$response" while true; do - item="$(printf '%s' "$items" | _egrep_o '.*<\/openXML>' | sed -n -E 's/.*(.*<\/domain>).*/\1/p')" + item="$(echo "$items" | _egrep_o '.*<\/openXML>' | sed -n 's/.*\(.*<\/domain>\).*/\1/p')" _debug existing_items "$existing_items" _debug results_retrieved "$results_retrieved" _debug item "$item" @@ -205,26 +205,26 @@ _get_root() { break fi - items="$(printf '%s' "$items" | sed "s$item")" + items="$(echo "$items" | sed "s|${item}||")" - results_retrieved=$((results_retrieved + 1)) + results_retrieved="$(_math "$results_retrieved" + 1)" - _domain_name="$(printf "%s" "$item" | sed -n -E 's/.*.*(.*)<\/name>.*<\/domain>.*/\1/p')" - _domain_extension="$(printf "%s" "$item" | sed -n -E 's/.*.*(.*)<\/extension>.*<\/domain>.*/\1/p')" + _domain_name="$(echo "$item" | sed -n 's/.*.*\(.*\)<\/name>.*<\/domain>.*/\1/p')" + _domain_extension="$(echo "$item" | sed -n 's/.*.*\(.*\)<\/extension>.*<\/domain>.*/\1/p')" _debug _domain_name "$_domain_name" _debug _domain_extension "$_domain_extension" - if [ "$(printf "%s.%s" "$_domain_name" "$_domain_extension")" = "$h" ]; then + if [ "$_domain_name.$_domain_extension" = "$h" ]; then return 0 fi done - total="$(printf '%s' "$response" | _egrep_o '.*?<\/total>' | sed -n -E 's/.*(.*)<\/total>.*/\1/p')" + total="$(echo "$response" | _egrep_o '.*?<\/total>' | sed -n 's/.*\(.*\)<\/total>.*/\1/p')" _debug total "$total" if [ "$results_retrieved" -eq "$total" ]; then results_retrieved=0 - i=$(_math "$i" + 1) + i="$(_math "$i" + 1)" fi done return 1 @@ -233,9 +233,9 @@ _get_root() { _openprovider_request() { request_xml=$1 - xml_prefix=$(printf '') + xml_prefix=$(echo '') xml_content=$(printf '%s%s%s' "$OPENPROVIDER_USER" "$OPENPROVIDER_PASSWORDHASH" "$request_xml") - response="$(_post "$(printf "%s%s" "$xml_prefix" "$xml_content" | tr -d '\n')" "$OPENPROVIDER_API" "" "POST" "application/xml")" + response="$(_post "$(echo "$xml_prefix$xml_content" | tr -d '\n')" "$OPENPROVIDER_API" "" "POST" "application/xml")" _debug response "$response" if ! _contains "$response" "0.*"; then _err "API request failed." From 71cfd874aeaa8282efb2f1200666dab5fa6fec68 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 18 Mar 2019 16:10:58 +0100 Subject: [PATCH 288/403] Fix SC2116 --- dnsapi/dns_openprovider.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_openprovider.sh b/dnsapi/dns_openprovider.sh index a0171e4e..1b1b760e 100755 --- a/dnsapi/dns_openprovider.sh +++ b/dnsapi/dns_openprovider.sh @@ -233,7 +233,7 @@ _get_root() { _openprovider_request() { request_xml=$1 - xml_prefix=$(echo '') + xml_prefix='' xml_content=$(printf '%s%s%s' "$OPENPROVIDER_USER" "$OPENPROVIDER_PASSWORDHASH" "$request_xml") response="$(_post "$(echo "$xml_prefix$xml_content" | tr -d '\n')" "$OPENPROVIDER_API" "" "POST" "application/xml")" _debug response "$response" From 7679df062c246e72c24ce2a57ca5d58cd02095ca Mon Sep 17 00:00:00 2001 From: Herman Sletteng Date: Tue, 19 Mar 2019 14:16:05 +0100 Subject: [PATCH 289/403] dns_gdnsdk: Fixed stupid regex error, want literal "-", not a range --- dnsapi/dns_gdnsdk.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_gdnsdk.sh b/dnsapi/dns_gdnsdk.sh index 7dc7894a..8c4962c0 100755 --- a/dnsapi/dns_gdnsdk.sh +++ b/dnsapi/dns_gdnsdk.sh @@ -137,7 +137,7 @@ _mypost() { _get_domain() { _myget 'action=dns_primarydns' - _domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:].-_]+' | sed 's/^.*"//') + _domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:]._-]+' | sed 's/^.*"//') if [ -z "$_domains" ]; then _err "Primary domain list not found!" return 1 From 34be7e99f01c716465f6f3701310ba81f6121d76 Mon Sep 17 00:00:00 2001 From: bz-heilig <35926736+bz-heilig@users.noreply.github.com> Date: Tue, 19 Mar 2019 15:04:37 +0100 Subject: [PATCH 290/403] Update README.md Added links for do.de API token creation and documentation of API. --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 3cce294a..a5779a30 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1157,7 +1157,7 @@ The `ACTIVE24_Token` will be saved in `~/.acme.sh/account.conf` and will be reus ## 60. Use do.de API -Create an API token in your do.de account. +Create an API token in your do.de account ([Create token here](https://www.do.de/account/letsencrypt/) | [Documentation](https://www.do.de/wiki/LetsEncrypt_-_Entwickler)). Set your API token: ``` From 307336cfc4ca136514423f43294a1768b727a2a7 Mon Sep 17 00:00:00 2001 From: Valentin Brandl Date: Tue, 19 Mar 2019 18:42:47 +0100 Subject: [PATCH 291/403] Add deploy hook for mailcow This hook will copy the key and certificate chain to the specified mailcow installation (as described in https://mailcow.github.io/mailcow-dockerized-docs/firststeps-ssl/#use-own-certificates) and restarts the containers, that are using the certificates. The hook has 2 parameters: * `DEPLOY_MAILCOW_PATH`: The path to the mailcow installation (required) * `DEPLOY_MAILCOW_RELOAD`: The reload command, defaults to `docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow` --- deploy/mailcow.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 deploy/mailcow.sh diff --git a/deploy/mailcow.sh b/deploy/mailcow.sh new file mode 100644 index 00000000..3b38fa85 --- /dev/null +++ b/deploy/mailcow.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env sh + +#Here is a script to deploy cert to mailcow. + +#returns 0 means success, otherwise error. + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +mailcow_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" + + _ssl_path="${DEPLOY_MAILCOW_PATH}/data/assets/ssl/" + if [ ! -d "$_ssl_path"; ] then + _err "Cannot find mailcow ssl path: $_ssl_path" + return 1 + fi + + _info "Copying key and cert" + _real_key="$_ssl_path/key.pem" + if ! cat "$_ckey" >"$_real_key"; then + _err "Error: write key file to: $_real_key" + return 1 + fi + + _real_fullchain="$_ssl_path/cert.pem" + if ! cat "$_cfullchain" >"$_real_fullchain"; then + _err "Error: write cert file to: $_real_fullchain" + return 1 + fi + + DEFAULT_MAILCOW_RELOAD="docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow" + _reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}" + + _info "Run reload: $_reload" + if eval "$_reload"; then + _info "Reload success!" + fi + return 0 + +} From b581a171f0a09870fcae71272ec6fe5b99c4df20 Mon Sep 17 00:00:00 2001 From: Valentin Brandl Date: Tue, 19 Mar 2019 18:43:07 +0100 Subject: [PATCH 292/403] Add documentation for mailcow deploy hook --- deploy/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index 44d53225..8cced4d8 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -391,3 +391,23 @@ acme.sh --deploy --deploy-hook mydevil -d example.com ``` That will remove old certificate and install new one. + +## 15. Deploy your cert to local mailcow server + +You can install your certificates to a local [mailcow](https://github.com/mailcow/mailcow-dockerized/) instance. The +deploy hook will copy the certificates and reload the containers, that use the certificates (`postfix-mailcow` +`dovecot-mailcow` and `nginx-mailcow`). + +```sh +$ export DEPLOY_MAILCOW_PATH="/path/to/mailcow" +$ acme.sh --deploy -d example.com --deploy-hook mailcow +``` + +The default command to restart is `docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow`, if you want a +custom restart command, specify it by setting `DEPLOY_MAILCOW_RELOAD`: + +```sh +$ export DEPLOY_MAILCOW_PATH="/path/to/mailcow" +$ export DEPLOY_MAILCOW_RELOAD="docker-compose restart" +$ acme.sh --deploy -d example.com --deploy-hook mailcow +``` From d643a2ff13ae642ca16ecc87c04a0c88bb8a63bb Mon Sep 17 00:00:00 2001 From: Valentin Brandl Date: Tue, 19 Mar 2019 19:09:25 +0100 Subject: [PATCH 293/403] Check if mailcow path is set and fix directory check --- deploy/mailcow.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/deploy/mailcow.sh b/deploy/mailcow.sh index 3b38fa85..bdba3e29 100644 --- a/deploy/mailcow.sh +++ b/deploy/mailcow.sh @@ -20,8 +20,15 @@ mailcow_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _ssl_path="${DEPLOY_MAILCOW_PATH}/data/assets/ssl/" - if [ ! -d "$_ssl_path"; ] then + _mailcow_path="${DEPLOY_MAILCOW_PATH}" + + if [ -z "$_mailcow_path" ]; then + _err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH." + return 1 + fi + + _ssl_path="${_mailcow_path}/data/assets/ssl/" + if [ ! -d "$_ssl_path" ]; then _err "Cannot find mailcow ssl path: $_ssl_path" return 1 fi @@ -39,7 +46,7 @@ mailcow_deploy() { return 1 fi - DEFAULT_MAILCOW_RELOAD="docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow" + DEFAULT_MAILCOW_RELOAD="cd ${_mailcow_path} && docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow" _reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}" _info "Run reload: $_reload" From d604166194491503a54b5c73be4fc1986fae9456 Mon Sep 17 00:00:00 2001 From: Valentin Brandl Date: Tue, 19 Mar 2019 19:15:31 +0100 Subject: [PATCH 294/403] Fix formatting --- deploy/mailcow.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/mailcow.sh b/deploy/mailcow.sh index bdba3e29..3a806e83 100644 --- a/deploy/mailcow.sh +++ b/deploy/mailcow.sh @@ -23,14 +23,14 @@ mailcow_deploy() { _mailcow_path="${DEPLOY_MAILCOW_PATH}" if [ -z "$_mailcow_path" ]; then - _err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH." - return 1 + _err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH." + return 1 fi _ssl_path="${_mailcow_path}/data/assets/ssl/" if [ ! -d "$_ssl_path" ]; then - _err "Cannot find mailcow ssl path: $_ssl_path" - return 1 + _err "Cannot find mailcow ssl path: $_ssl_path" + return 1 fi _info "Copying key and cert" From 228c835466b41448897c23c41350dc07a29fe9e1 Mon Sep 17 00:00:00 2001 From: temoffey Date: Wed, 20 Mar 2019 03:03:10 +0300 Subject: [PATCH 295/403] gcore_cdn_deploy --- deploy/README.md | 15 +++++ deploy/gcore_cdn.sh | 130 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 deploy/gcore_cdn.sh diff --git a/deploy/README.md b/deploy/README.md index 44d53225..e89add80 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -391,3 +391,18 @@ acme.sh --deploy --deploy-hook mydevil -d example.com ``` That will remove old certificate and install new one. + +## 15. Deploy the cert to G-Core CDN servise + +Deploy the cert to G-Core CDN servise (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). +Uses command line curl for send requests and jq for parse responses. + +Then you can deploy now: + +```sh +export DEPLOY_GCORE_CDN_USERNAME=myusername +export DEPLOY_GCORE_CDN_PASSWORD=mypassword +acme.sh --deploy -d example.com --deploy-hook gcore_cdn +``` + +Please note, need installed jq. diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh new file mode 100644 index 00000000..051226d9 --- /dev/null +++ b/deploy/gcore_cdn.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env sh + +# Here is the script to deploy the cert to G-Core CDN servise (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). +# Uses command line curl for send requests and jq for parse responses. +# Returns 0 when success. +# +# Written by temoffey +# Public domain, 2019 + +#export DEPLOY_GCORE_CDN_USERNAME=myusername +#export DEPLOY_GCORE_CDN_PASSWORD=mypassword + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain + +gcore_cdn_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" + + _fullchain=$(awk 1 ORS='\\n' "$_cfullchain") + _key=$(awk 1 ORS='\\n' "$_ckey") + + _debug _fullchain "$_fullchain" + _debug _key "$_key" + + if [ -z "$DEPLOY_GCORE_CDN_USERNAME" ]; then + if [ -z "$Le_Deploy_gcore_cdn_username" ]; then + _err "Please define the target username: export DEPLOY_GCORE_CDN_USERNAME=username" + return 1 + else + DEPLOY_GCORE_CDN_USERNAME="$Le_Deploy_gcore_cdn_username" + fi + else + _savedomainconf Le_Deploy_gcore_cdn_username "$DEPLOY_GCORE_CDN_USERNAME" + fi + + if [ -z "$DEPLOY_GCORE_CDN_PASSWORD" ]; then + if [ -z "$Le_Deploy_gcore_cdn_password" ]; then + _err "Please define the target password: export DEPLOY_GCORE_CDN_PASSWORD=password" + return 1 + else + DEPLOY_GCORE_CDN_PASSWORD="$Le_Deploy_gcore_cdn_password" + fi + else + _savedomainconf Le_Deploy_gcore_cdn_password "$DEPLOY_GCORE_CDN_PASSWORD" + fi + + if ! [ -x "$(command -v jq)" ]; then + _err "Please install the package jq: sudo apt-get install jq" + return 1 + fi + + _info "Get authorization token" + _request="{ \"username\": \"$DEPLOY_GCORE_CDN_USERNAME\", \"password\": \"$DEPLOY_GCORE_CDN_PASSWORD\" }" + _debug _request "$_request" + _response=$(curl -s -X POST https://api.gcdn.co/auth/signin -H "Content-Type:application/json" -d "$_request") + _debug _response "$_response" + _token=$(echo "$_response" | jq -r '.token') + _debug _token "$_token" + + if [ "$_token" == "null" ]; then + _err "Error G-Core Labs API authorization" + return 1 + fi + + _info "Find CDN resource with cname $_cdomain" + _response=$(curl -s -X GET https://api.gcdn.co/resources -H "Authorization:Token $_token") + _debug _response "$_response" + _resource=$(echo "$_response" | jq -r ".[] | select(.cname == \"$_cdomain\")") + _debug _resource "$_resource" + _resourceId=$(echo "$_resource" | jq -r '.id') + _sslDataOld=$(echo "$_resource" | jq -r '.sslData') + _originGroup=$(echo "$_resource" | jq -r '.originGroup') + _debug _resourceId "$_resourceId" + _debug _sslDataOld "$_sslDataOld" + _debug _originGroup "$_originGroup" + + if [ -z "$_resourceId" ] || [ "$_resourceId" == "null" ] || [ -z "$_originGroup" ] || [ "$_originGroup" == "null" ]; then + _err "Not found CDN resource with cname $_cdomain" + return 1 + fi + + _info "Add new SSL certificate" + _date=$(date "+%d.%m.%Y %H:%M:%S") + _request="{ \"name\": \"$_cdomain ($_date)\", \"sslCertificate\": \"$_fullchain\n\", \"sslPrivateKey\": \"$_key\n\" }" + _debug _request "$_request" + _response=$(curl -s -X POST https://api.gcdn.co/sslData -H "Content-Type:application/json" -H "Authorization:Token $_token" -d "$_request") + _debug _response "$_response" + _sslDataAdd=$(echo "$_response" | jq -r '.id') + _debug _sslDataAdd "$_sslDataAdd" + + if [ "$_sslDataAdd" == "null" ]; then + _err "Error new SSL certificate add" + return 1 + fi + + _info "Update CDN resource" + _request="{ \"originGroup\": $_originGroup, \"sslData\": $_sslDataAdd }" + _debug _request "$_request" + _response=$(curl -s -X PUT https://api.gcdn.co/resources/$_resourceId -H "Content-Type:application/json" -H "Authorization:Token $_token" -d "$_request") + _debug _response "$_response" + _sslDataNew=$(echo "$_response" | jq -r '.sslData') + _debug _sslDataNew "$_sslDataNew" + + if [ "$_sslDataNew" != "$_sslDataAdd" ]; then + _err "Error CDN resource update" + return 1 + fi + + if [ -z "$_sslDataOld" ] || [ "$_sslDataOld" = "null" ]; then + _info "Not found old SSL certificate" + else + _info "Delete old SSL certificate" + _response=$(curl -s -X DELETE https://api.gcdn.co/sslData/$_sslDataOld -H "Authorization:Token $_token") + _debug _response "$_response" + fi + + _info "Certificate successfully deployed" + return 0 +} \ No newline at end of file From 95cdb4b2bc606e1641850359e9bf55abce2d46f4 Mon Sep 17 00:00:00 2001 From: temoffey Date: Wed, 20 Mar 2019 14:02:11 +0300 Subject: [PATCH 296/403] fix syntax --- deploy/README.md | 4 ++-- deploy/gcore_cdn.sh | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index e89add80..76a6cc94 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -392,9 +392,9 @@ acme.sh --deploy --deploy-hook mydevil -d example.com That will remove old certificate and install new one. -## 15. Deploy the cert to G-Core CDN servise +## 15. Deploy the cert to G-Core CDN service -Deploy the cert to G-Core CDN servise (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). +Deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). Uses command line curl for send requests and jq for parse responses. Then you can deploy now: diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 051226d9..621d445b 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -# Here is the script to deploy the cert to G-Core CDN servise (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). +# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). # Uses command line curl for send requests and jq for parse responses. # Returns 0 when success. # @@ -37,22 +37,20 @@ gcore_cdn_deploy() { if [ -z "$Le_Deploy_gcore_cdn_username" ]; then _err "Please define the target username: export DEPLOY_GCORE_CDN_USERNAME=username" return 1 - else - DEPLOY_GCORE_CDN_USERNAME="$Le_Deploy_gcore_cdn_username" fi else - _savedomainconf Le_Deploy_gcore_cdn_username "$DEPLOY_GCORE_CDN_USERNAME" + Le_Deploy_gcore_cdn_username="$DEPLOY_GCORE_CDN_USERNAME" + _savedomainconf Le_Deploy_gcore_cdn_username "$Le_Deploy_gcore_cdn_username" fi if [ -z "$DEPLOY_GCORE_CDN_PASSWORD" ]; then if [ -z "$Le_Deploy_gcore_cdn_password" ]; then _err "Please define the target password: export DEPLOY_GCORE_CDN_PASSWORD=password" return 1 - else - DEPLOY_GCORE_CDN_PASSWORD="$Le_Deploy_gcore_cdn_password" fi else - _savedomainconf Le_Deploy_gcore_cdn_password "$DEPLOY_GCORE_CDN_PASSWORD" + Le_Deploy_gcore_cdn_password="$DEPLOY_GCORE_CDN_PASSWORD" + _savedomainconf Le_Deploy_gcore_cdn_password "$Le_Deploy_gcore_cdn_password" fi if ! [ -x "$(command -v jq)" ]; then @@ -61,14 +59,14 @@ gcore_cdn_deploy() { fi _info "Get authorization token" - _request="{ \"username\": \"$DEPLOY_GCORE_CDN_USERNAME\", \"password\": \"$DEPLOY_GCORE_CDN_PASSWORD\" }" + _request="{ \"username\": \"$Le_Deploy_gcore_cdn_username\", \"password\": \"$Le_Deploy_gcore_cdn_password\" }" _debug _request "$_request" _response=$(curl -s -X POST https://api.gcdn.co/auth/signin -H "Content-Type:application/json" -d "$_request") _debug _response "$_response" _token=$(echo "$_response" | jq -r '.token') _debug _token "$_token" - if [ "$_token" == "null" ]; then + if [ "$_token" = "null" ]; then _err "Error G-Core Labs API authorization" return 1 fi @@ -85,7 +83,7 @@ gcore_cdn_deploy() { _debug _sslDataOld "$_sslDataOld" _debug _originGroup "$_originGroup" - if [ -z "$_resourceId" ] || [ "$_resourceId" == "null" ] || [ -z "$_originGroup" ] || [ "$_originGroup" == "null" ]; then + if [ -z "$_resourceId" ] || [ "$_resourceId" = "null" ] || [ -z "$_originGroup" ] || [ "$_originGroup" = "null" ]; then _err "Not found CDN resource with cname $_cdomain" return 1 fi @@ -107,7 +105,7 @@ gcore_cdn_deploy() { _info "Update CDN resource" _request="{ \"originGroup\": $_originGroup, \"sslData\": $_sslDataAdd }" _debug _request "$_request" - _response=$(curl -s -X PUT https://api.gcdn.co/resources/$_resourceId -H "Content-Type:application/json" -H "Authorization:Token $_token" -d "$_request") + _response=$(curl -s -X PUT "https://api.gcdn.co/resources/$_resourceId" -H "Content-Type:application/json" -H "Authorization:Token $_token" -d "$_request") _debug _response "$_response" _sslDataNew=$(echo "$_response" | jq -r '.sslData') _debug _sslDataNew "$_sslDataNew" @@ -118,13 +116,13 @@ gcore_cdn_deploy() { fi if [ -z "$_sslDataOld" ] || [ "$_sslDataOld" = "null" ]; then - _info "Not found old SSL certificate" + _info "Not found old SSL certificate" else _info "Delete old SSL certificate" - _response=$(curl -s -X DELETE https://api.gcdn.co/sslData/$_sslDataOld -H "Authorization:Token $_token") + _response=$(curl -s -X DELETE "https://api.gcdn.co/sslData/$_sslDataOld" -H "Authorization:Token $_token") _debug _response "$_response" fi _info "Certificate successfully deployed" return 0 -} \ No newline at end of file +} From 89989adcadd31cbd162beff2ca7ab746c3928324 Mon Sep 17 00:00:00 2001 From: temoffey Date: Wed, 20 Mar 2019 14:05:18 +0300 Subject: [PATCH 297/403] fix syntax --- deploy/gcore_cdn.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 621d445b..18d137a6 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -65,7 +65,7 @@ gcore_cdn_deploy() { _debug _response "$_response" _token=$(echo "$_response" | jq -r '.token') _debug _token "$_token" - + if [ "$_token" = "null" ]; then _err "Error G-Core Labs API authorization" return 1 @@ -97,7 +97,7 @@ gcore_cdn_deploy() { _sslDataAdd=$(echo "$_response" | jq -r '.id') _debug _sslDataAdd "$_sslDataAdd" - if [ "$_sslDataAdd" == "null" ]; then + if [ "$_sslDataAdd" = "null" ]; then _err "Error new SSL certificate add" return 1 fi From fbdc5a0eb540865b52d4647ac5eda84ecaa9a0be Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 20 Mar 2019 22:52:40 +0800 Subject: [PATCH 298/403] fix https://github.com/Neilpang/acme.sh/issues/2179 --- acme.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 752b49bc..f47a5ebb 100755 --- a/acme.sh +++ b/acme.sh @@ -3750,7 +3750,8 @@ issue() { _on_issue_err "$_post_hook" return 1 fi - + Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" + _debug Le_LinkOrder "$Le_LinkOrder" Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug Le_OrderFinalize "$Le_OrderFinalize" if [ -z "$Le_OrderFinalize" ]; then @@ -4249,13 +4250,10 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi - Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" if [ -z "$Le_LinkOrder" ]; then - _err "Sign error, can not get order link location header" - _err "responseHeaders" "$responseHeaders" - _on_issue_err "$_post_hook" - return 1 + Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)" fi + _savedomainconf "Le_LinkOrder" "$Le_LinkOrder" _link_cert_retry=0 @@ -4281,6 +4279,14 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi + #the order is processing, so we are going to poll order status + if [ -z "$Le_LinkOrder" ]; then + _err "Sign error, can not get order link location header" + _err "responseHeaders" "$responseHeaders" + _on_issue_err "$_post_hook" + return 1 + fi + _info "Polling order status: $Le_LinkOrder" if ! _send_signed_request "$Le_LinkOrder"; then _err "Sign failed, can not post to Le_LinkOrder cert:$Le_LinkOrder." _err "$response" From 0629c2a086e390b5fc502ad5c0e6bdb8d11ac470 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 20 Mar 2019 23:01:24 +0800 Subject: [PATCH 299/403] move to wiki --- dnsapi/README.md | 1373 +--------------------------------------------- 1 file changed, 2 insertions(+), 1371 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index a5779a30..d9ab46fa 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -1,1375 +1,6 @@ # How to use DNS API +DNS api usage: -If your dns provider doesn't provide api access, you can use our dns alias mode: - -https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode - -## 1. Use CloudFlare domain API to automatically issue cert - -First you need to login to your CloudFlare account to get your [API key](https://dash.cloudflare.com/profile). - -``` -export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" -export CF_Email="xxxx@sss.com" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_cf -d example.com -d www.example.com -``` - -The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -## 2. Use DNSPod.cn domain API to automatically issue cert - -First you need to login to your DNSPod account to get your API Key and ID. - -``` -export DP_Id="1234" -export DP_Key="sADDsdasdgdsf" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_dp -d example.com -d www.example.com -``` - -The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -## 3. Use CloudXNS.com domain API to automatically issue cert - -First you need to login to your CloudXNS account to get your API Key and Secret. - -``` -export CX_Key="1234" -export CX_Secret="sADDsdasdgdsf" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_cx -d example.com -d www.example.com -``` - -The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -## 4. Use GoDaddy.com domain API to automatically issue cert - -First you need to login to your GoDaddy account to get your API Key and Secret. - -https://developer.godaddy.com/keys/ - -Please create a Production key, instead of a Test key. - -``` -export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" -export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_gd -d example.com -d www.example.com -``` - -The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -## 5. Use PowerDNS embedded API to automatically issue cert - -First you need to login to your PowerDNS account to enable the API and set your API-Token in the configuration. - -https://doc.powerdns.com/md/httpapi/README/ - -``` -export PDNS_Url="http://ns.example.com:8081" -export PDNS_ServerId="localhost" -export PDNS_Token="0123456789ABCDEF" -export PDNS_Ttl=60 -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_pdns -d example.com -d www.example.com -``` - -The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -## 6. Use OVH/kimsufi/soyoustart/runabove API to automatically issue cert - -https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api - - -## 7. Use nsupdate to automatically issue cert - -First, generate a key for updating the zone -``` -b=$(dnssec-keygen -a hmac-sha512 -b 512 -n USER -K /tmp foo) -cat > /etc/named/keys/update.key < /etc/knot/acme.key -``` - -Include this key in your knot configuration file. - -``` -include: /etc/knot/acme.key -``` - -Next, configure your zone to allow dynamic updates. - -Dynamic updates for the zone are allowed via proper ACL rule with the `update` action. For in-depth instructions, please see [Knot DNS's documentation](https://www.knot-dns.cz/documentation/). - -``` -acl: - - id: acme_acl - address: 192.168.1.0/24 - key: acme_key - action: update - -zone: - - domain: example.com - file: example.com.zone - acl: acme_acl -``` - -Finally, make the DNS server and TSIG Key available to `acme.sh` - -``` -export KNOT_SERVER="dns.example.com" -export KNOT_KEY=`grep \# /etc/knot/acme.key | cut -d' ' -f2` -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_knot -d example.com -d www.example.com -``` - -The `KNOT_SERVER` and `KNOT_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 20. Use DigitalOcean API (native) - -You need to obtain a read and write capable API key from your DigitalOcean account. See: https://www.digitalocean.com/help/api/ - -``` -export DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc" -``` - -Ok, let's issue a cert now: -``` -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/. 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. - -``` -# 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" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com -``` -The `CLOUDNS_AUTH_ID` and `CLOUDNS_AUTH_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 22. Use Infoblox API - -First you need to create/obtain API credentials on your Infoblox appliance. - -``` -export Infoblox_Creds="username:password" -export Infoblox_Server="ip or fqdn of infoblox appliance" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_infoblox -d example.com -d www.example.com -``` - -Note: This script will automatically create and delete the ephemeral txt record. -The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -## 23. Use VSCALE API - -First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/). - -``` -export VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_vscale -d example.com -d www.example.com -``` - -## 24. Use Dynu API - -First you need to create/obtain API credentials from your Dynu account. See: https://www.dynu.com/resources/api/documentation - -``` -export Dynu_ClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -export Dynu_Secret="yyyyyyyyyyyyyyyyyyyyyyyyy" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_dynu -d example.com -d www.example.com -``` - -The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 25. Use DNSimple API - -First you need to login to your DNSimple account and generate a new oauth token. - -https://dnsimple.com/a/{your account id}/account/access_tokens - -Note that this is an _account_ token and not a user token. The account token is -needed to infer the `account_id` used in requests. A user token will not be able -to determine the correct account to use. - -``` -export DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje" -``` - -To issue the cert just specify the `dns_dnsimple` API. - -``` -acme.sh --issue --dns dns_dnsimple -d example.com -``` - -The `DNSimple_OAUTH_TOKEN` will be saved in `~/.acme.sh/account.conf` and will -be reused when needed. - -If you have any issues with this integration please report them to -https://github.com/pho3nixf1re/acme.sh/issues. - -## 26. Use NS1.com API - -``` -export NS1_Key="fdmlfsdklmfdkmqsdfk" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_nsone -d example.com -d www.example.com -``` - -## 27. Use DuckDNS.org API - -``` -export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" -``` - -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 -``` - -For issues, please report to https://github.com/raidenii/acme.sh/issues. - -## 28. Use Name.com API - -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" -export Namecom_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -``` - -And now you can issue certs with: - -``` -acme.sh --issue --dns dns_namecom -d example.com -d www.example.com -``` - -For issues, please report to https://github.com/raidenii/acme.sh/issues. - -## 29. Use Dyn Managed DNS API to automatically issue cert - -First, login to your Dyn Managed DNS account: https://portal.dynect.net/login/ - -It is recommended to add a new user specific for API access. - -The minimum "Zones & Records Permissions" required are: -``` -RecordAdd -RecordUpdate -RecordDelete -RecordGet -ZoneGet -ZoneAddNode -ZoneRemoveNode -ZonePublish -``` - -Pass the API user credentials to the environment: -``` -export DYN_Customer="customer" -export DYN_Username="apiuser" -export DYN_Password="secret" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_dyn -d example.com -d www.example.com -``` - -The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 30. Use pdd.yandex.ru API - -``` -export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -``` - -Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/ -``` -acme.sh --issue --dns dns_yandex -d mydomain.example.org -``` - -For issues, please report to https://github.com/non7top/acme.sh/issues. - -## 31. Use Hurricane Electric - -Hurricane Electric (https://dns.he.net/) doesn't have an API so just set your login credentials like so: - -``` -export HE_Username="yourusername" -export HE_Password="password" -``` - -Then you can issue your certificate: - -``` -acme.sh --issue --dns dns_he -d example.com -d www.example.com -``` - -The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -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. - -## 33. Use INWX - -[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" -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. - -If your account is secured by mobile tan you have also defined the shared secret. - -``` -export INWX_Shared_Secret="shared secret" -``` - -You may need to re-enable the mobile tan to gain the shared secret. - -## 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. - -## 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 -``` - -## 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: - -``` -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. - -## 37. Use Azure DNS - -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" -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. - -## 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. - -## 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. - -## 40. 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. - -## 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. - -## 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. - -## 43. Use Zilore DNS API - -First, get your API key at https://my.zilore.com/account/api - -``` -export Zilore_Key="5dcad3a2-36cb-50e8-cb92-000002f9" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_zilore -d example.com -d *.example.com -``` - -The `Zilore_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 44. Use Loopia.se API -User must provide login credentials to the Loopia API. -The user needs the following permissions: - -- addSubdomain -- updateZoneRecord -- getDomains -- removeSubdomain - -Set the login credentials: -``` -export LOOPIA_User="user@loopiaapi" -export LOOPIA_Password="password" -``` - -And to issue a cert: -``` -acme.sh --issue --dns dns_loopia -d example.com -d *.example.com -``` - -The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -## 45. Use ACME DNS API - -ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely. -https://github.com/joohoi/acme-dns - -``` -export ACMEDNS_UPDATE_URL="https://auth.acme-dns.io/update" -export ACMEDNS_USERNAME="" -export ACMEDNS_PASSWORD="" -export ACMEDNS_SUBDOMAIN="" - -acme.sh --issue --dns dns_acmedns -d example.com -d www.example.com -``` - -The credentials will be saved in `~/.acme.sh/account.conf` and will -be reused when needed. -## 46. Use TELE3 API - -First you need to login to your TELE3 account to set your API-KEY. -https://www.tele3.cz/system-acme-api.html - -``` -export TELE3_Key="MS2I4uPPaI..." -export TELE3_Secret="kjhOIHGJKHg" - -acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com -``` - -The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed. - -## 47. Use Euserv.eu API - -First you need to login to your euserv.eu account and activate your API Administration (API Verwaltung). -[https://support.euserv.com](https://support.euserv.com) - -Once you've activate, login to your API Admin Interface and create an API account. -Please specify the scope (active groups: domain) and assign the allowed IPs. - -``` -export EUSERV_Username="99999.user123" -export EUSERV_Password="Asbe54gHde" -``` - -Ok, let's issue a cert now: (Be aware to use the `--insecure` flag, cause euserv.eu is still using self-signed certificates!) -``` -acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure -``` - -The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -Please report any issues to https://github.com/initit/acme.sh or to - -## 48. Use DNSPod.com domain API to automatically issue cert - -First you need to get your API Key and ID by this [get-the-user-token](https://www.dnspod.com/docs/info.html#get-the-user-token). - -``` -export DPI_Id="1234" -export DPI_Key="sADDsdasdgdsf" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_dpi -d example.com -d www.example.com -``` - -The `DPI_Id` and `DPI_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 49. Use Google Cloud DNS API to automatically issue cert - -First you need to authenticate to gcloud. - -``` -gcloud init -``` - -**The `dns_gcloud` script uses the active gcloud configuration and credentials.** -There is no logic inside `dns_gcloud` to override the project and other settings. -If needed, create additional [gcloud configurations](https://cloud.google.com/sdk/gcloud/reference/topic/configurations). -You can change the configuration being used without *activating* it; simply set the `CLOUDSDK_ACTIVE_CONFIG_NAME` environment variable. - -To issue a certificate you can: -``` -export CLOUDSDK_ACTIVE_CONFIG_NAME=default # see the note above -acme.sh --issue --dns dns_gcloud -d example.com -d '*.example.com' -``` - -`dns_gcloud` also supports [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode). - -## 50. Use ConoHa API - -First you need to login to your ConoHa account to get your API credentials. - -``` -export CONOHA_Username="xxxxxx" -export CONOHA_Password="xxxxxx" -export CONOHA_TenantId="xxxxxx" -export CONOHA_IdentityServiceApi="https://identity.xxxx.conoha.io/v2.0" -``` - -To issue a cert: -``` -acme.sh --issue --dns dns_conoha -d example.com -d www.example.com -``` - -The `CONOHA_Username`, `CONOHA_Password`, `CONOHA_TenantId` and `CONOHA_IdentityServiceApi` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 51. Use netcup DNS API to automatically issue cert - -First you need to login in your CCP account to get your API Key and API Password. -``` -export NC_Apikey="" -export NC_Apipw="" -export NC_CID="" -``` - -Now, let's issue a cert: -``` -acme.sh --issue --dns dns_netcup -d example.com -d www.example.com -``` - -The `NC_Apikey`,`NC_Apipw` and `NC_CID` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -## 52. Use GratisDNS.dk - -GratisDNS.dk (https://gratisdns.dk/) does not provide an API to update DNS records (other than IPv4 and IPv6 -dynamic DNS addresses). The acme.sh plugin therefore retrieves and updates domain TXT records by logging -into the GratisDNS website to read the HTML and posting updates as HTTP. The plugin needs to know your -userid and password for the GratisDNS website. - -```sh -export GDNSDK_Username="..." -export GDNSDK_Password="..." -``` -The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - - -Now you can issue a certificate. - -Note: It usually takes a few minutes (usually 3-4 minutes) before the changes propagates to gratisdns.dk nameservers (ns3.gratisdns.dk often are slow), -and in rare cases I have seen over 5 minutes before google DNS catches it. Therefor a DNS sleep of at least 300 seconds are recommended- - -```sh -acme.sh --issue --dns dns_gdnsdk --dnssleep 300 -d example.com -d *.example.com -``` - -## 53. Use Namecheap - -You will need your namecheap username, API KEY (https://www.namecheap.com/support/api/intro.aspx) and your external IP address (or an URL to get it), this IP will need to be whitelisted at Namecheap. -Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. - -```sh -export NAMECHEAP_USERNAME="..." -export NAMECHEAP_API_KEY="..." -export NAMECHEAP_SOURCEIP="..." -``` - -NAMECHEAP_SOURCEIP can either be an IP address or an URL to provide it (e.g. https://ifconfig.co/ip). - -The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -Now you can issue a certificate. - -```sh -acme.sh --issue --dns dns_namecheap -d example.com -d *.example.com -``` - -## 54. Use MyDNS.JP API - -First, register to MyDNS.JP and get MasterID and Password. - -``` -export MYDNSJP_MasterID=MasterID -export MYDNSJP_Password=Password -``` - -To issue a certificate: - -``` -acme.sh --issue --dns dns_mydnsjp -d example.com -d www.example.com -``` -The `MYDNSJP_MasterID` and `MYDNSJP_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 55. Use hosting.de API - -Create an API key in your hosting.de account here: https://secure.hosting.de - -The key needs the following rights: -- DNS_ZONES_EDIT -- DNS_ZONES_LIST - -Set your API Key and endpoint: - -``` -export HOSTINGDE_APIKEY='xxx' -export HOSTINGDE_ENDPOINT='https://secure.hosting.de' -``` - -The plugin can also be used for the http.net API. http.net customers have to set endpoint to https://partner.http.net. - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_hostingde -d example.com -d *.example.com -``` - -The hosting.de API key and endpoint will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 56. Use Neodigit.net API - -``` -export NEODIGIT_API_TOKEN="eXJxTkdUVUZmcHQ3QWJackQ4ZGlMejRDSklRYmo5VG5zcFFKK2thYnE0WnVnNnMy" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_neodigit -d example.com -d www.example.com -``` - -Neodigit API Token will be saved in `~/.acme.sh/account.conf` and will be used when needed. - -## 57. Use Exoscale API - -Create an API key and secret key in the Exoscale account section - -Set your API and secret key: - -``` -export EXOSCALE_API_KEY='xxx' -export EXOSCALE_SECRET_KEY='xxx' -``` - -Now, let's issue a cert: -``` -acme.sh --issue --dns dns_exoscale -d example.com -d www.example.com -``` - -The `EXOSCALE_API_KEY` and `EXOSCALE_SECRET_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 58. Using PointHQ API to issue certs - -Log into [PointHQ account management](https://app.pointhq.com/profile) and copy the API key from the page there. - -```export PointHQ_Key="apikeystringgoeshere" -exportPointHQ_Email="accountemail@yourdomain.com" -``` - -You can then issue certs by using: -```acme.sh --issue --dns dns_pointhq -d example.com -d www.example.com -``` - -## 59. Use Active24 API - -Create an API token in the Active24 account section, documentation on https://faq.active24.com/cz/790131-REST-API-rozhran%C3%AD. - -Set your API token: - -``` -export ACTIVE24_Token='xxx' -``` - -Now, let's issue a cert, set `dnssleep` for propagation new DNS record: -``` -acme.sh --issue --dns dns_active24 -d example.com -d www.example.com --dnssleep 1000 -``` - -The `ACTIVE24_Token` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 60. Use do.de API - -Create an API token in your do.de account ([Create token here](https://www.do.de/account/letsencrypt/) | [Documentation](https://www.do.de/wiki/LetsEncrypt_-_Entwickler)). - -Set your API token: -``` -export DO_LETOKEN='FmD408PdqT1E269gUK57' -``` - -To issue a certificate run: -``` -acme.sh --issue --dns dns_doapi -d example.com -d *.example.com -``` - -The API token will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 61. Use Nexcess API - -First, you'll need to login to the [Nexcess.net Client Portal](https://portal.nexcess.net) and [generate a new API token](https://portal.nexcess.net/api-token). - -Once you have a token, set it in your systems environment: - -``` -export NW_API_TOKEN="YOUR_TOKEN_HERE" -export NW_API_ENDPOINT="https://portal.nexcess.net" -``` - -Finally, we'll issue the certificate: (Nexcess DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`) - -``` -acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 -``` - -The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 62. Use Thermo.io API - -First, you'll need to login to the [Thermo.io Client Portal](https://core.thermo.io) and [generate a new API token](https://core.thermo.io/api-token). - -Once you have a token, set it in your systems environment: - -``` -export NW_API_TOKEN="YOUR_TOKEN_HERE" -export NW_API_ENDPOINT="https://core.thermo.io" -``` - -Finally, we'll issue the certificate: (Thermo DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`) - -``` -acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 -``` - -The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 63. Use Futurehosting API - -First, you'll need to login to the [Futurehosting Client Portal](https://my.futurehosting.com) and [generate a new API token](https://my.futurehosting.com/api-token). - -Once you have a token, set it in your systems environment: - -``` -export NW_API_TOKEN="YOUR_TOKEN_HERE" -export NW_API_ENDPOINT="https://my.futurehosting.com" -``` - -Finally, we'll issue the certificate: (Futurehosting DNS publishes at max every 15 minutes, we recommend setting a 900 second `--dnssleep`) - -``` -acme.sh --issue --dns dns_nw -d example.com --dnssleep 900 -``` - -The `NW_API_TOKEN` and `NW_API_ENDPOINT` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 64. Use Rackspace API - -Set username and API key, which is available under "My Profile & Settings" - -``` -export RACKSPACE_Username='username' -export RACKSPACE_Apikey='xxx' -``` - -Now, let's issue a cert: - -``` -acme.sh --issue --dns dns_rackspace -d example.com -d www.example.com -``` - -## 65. Use Online API - -First, you'll need to retrive your API key, which is available under https://console.online.net/en/api/access - -``` -export ONLINE_API_KEY='xxx' -``` - -To issue a cert run: - -``` -acme.sh --issue --dns dns_online -d example.com -d www.example.com -``` - -`ONLINE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 66. Use MyDevil.net - -Make sure that you can execute own binaries: - -```sh -devil binexec on -``` - -Install acme.sh, or simply `git clone` it into some directory on your MyDevil host account (in which case you should link to it from your `~/bin` directory). - -If you're not using private IP and depend on default IP provided by host, you may want to edit `crontab` too, and make sure that `acme.sh --cron` is run also after reboot (you can find out how to do that on their wiki pages). - -To issue a new certificate, run: - -```sh -acme.sh --issue --dns dns_mydevil -d example.com -d *.example.com -``` - -After certificate is ready, you can install it with [deploy command](../deploy/README.md#14-deploy-your-cert-on-mydevilnet). - -## 67. Use Core-Networks API to automatically issue cert - -First you need to login to your Core-Networks account to to set up an API-User. -Then export username and password to use these credentials. - -``` -export CN_User="user" -export CN_Password="passowrd" -``` - -Ok, let's issue a cert now: -``` -acme.sh --issue --dns dns_cn -d example.com -d www.example.com -``` - -The `CN_User` and `CN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 68. Use NederHost API - -Create an API token in Mijn NederHost. - -Set your API key: -``` -export NederHost_Key='xxx' -``` - -To issue a certificate run: -``` -acme.sh --issue --dns dns_nederhost -d example.com -d *.example.com -``` - -## 69. Use Zone.ee DNS API - -First, you'll need to retrive your API key. Estonian insructions https://help.zone.eu/kb/zoneid-api-v2/ - -``` -export ZONE_Username=yourusername -export ZONE_Key=keygoeshere -``` - -To issue a cert run: - -``` -acme.sh --issue -d example.com -d www.example.com --dns dns_zone -``` - -`ZONE_Username` and `ZONE_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. - -## 70. Use UltraDNS API - -UltraDNS is a paid for service that provides DNS, as well as Web and Mail forwarding (as well as reporting, auditing, and advanced tools). - -More information can be found here: https://www.security.neustar/lp/ultra20/index.html - -The REST API documentation for this service is found here: https://portal.ultradns.com/static/docs/REST-API_User_Guide.pdf - -Set your UltraDNS User name, and password; these would be the same you would use here: - -https://portal.ultradns.com/ - or if you create an API only user, that username and password would be better utilized. - -``` -export ULTRA_USR="abcd" -export ULTRA_PWD="efgh" - -To issue a cert run: - -acme.sh --issue --dns dns_ultra -d example.com -d www.example.com -``` - -`ULTRA_USR` and `ULTRA_PWD` will be saved in `~/.acme.sh/account.conf` and will be resued when needed. - -# Use custom API - -If your API is not supported yet, you can write your own DNS API. - -Let's assume you want to name it 'myapi': - -1. Create a bash script named `~/.acme.sh/dns_myapi.sh`, -2. In the script you must have a function named `dns_myapi_add()` which will be called by acme.sh to add the DNS records. -3. Then you can use your API to issue cert like this: - -``` -acme.sh --issue --dns dns_myapi -d example.com -d www.example.com -``` - -For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh) - -See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide - -# Use lexicon DNS API - -https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api +https://github.com/Neilpang/acme.sh/wiki/dnsapi From 61bcd67a5d34e0183b421c5d941fa602706e3394 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 20 Mar 2019 23:03:49 +0800 Subject: [PATCH 300/403] move to wiki --- deploy/README.md | 411 +---------------------------------------------- 1 file changed, 2 insertions(+), 409 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 8cced4d8..fc633ad7 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,413 +1,6 @@ # Using deploy api -Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). +deploy hook usage: -Here are the scripts to deploy the certs/key to the server/services. +https://github.com/Neilpang/acme.sh/wiki/deployhooks -## 1. Deploy the certs to your cpanel host - -If you want to deploy using cpanel UAPI see 7. - -(cpanel deploy hook is not finished yet, this is just an example.) - - - -Then you can deploy now: - -```sh -export DEPLOY_CPANEL_USER=myusername -export DEPLOY_CPANEL_PASSWORD=PASSWORD -acme.sh --deploy -d example.com --deploy-hook cpanel -``` - -## 2. Deploy ssl cert on kong proxy engine based on api - -Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). -Currently supports Kong-v0.10.x. - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook kong -``` - -## 3. Deploy the cert to remote server through SSH access - -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... - -```sh -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... -``` -export DEPLOY_SSH_USER=username -``` -Optional... -``` -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 -``` - -**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). - -**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). - -**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. - -**DEPLOY_SSH_KEYFILE** -Target filename for the private key issued by LetsEncrypt. - -**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. - -**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. - -**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. - -**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. - -**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 -user -```sh -~/.acme_ssh_deploy/[domain name]-backup-[timestamp] -``` -Any backups older than 180 days will be deleted when new certificates -are deployed. This defaults to "yes" set to "no" to disable backup. - -###Examples using SSH deploy -The following example illustrates deploying certificates to a QNAP NAS -(tested with QTS version 4.2.3) - -```sh -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 -``` -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 illustrates deploying certificates to a Unifi -Controller (tested with version 5.4.11). - -```sh -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 \ - -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 unifi.example.com --deploy-hook ssh -``` -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 finally -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 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 \ - /var/lib/unifi/unifi.example.com.p12 \ -&& service unifi restart -``` - -## 4. Deploy the cert to local vsftpd server - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd -``` - -The default vsftpd conf file is `/etc/vsftpd.conf`, if your vsftpd conf is not in the default location, you can specify one: - -```sh -export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf" - -acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd -``` - -The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one: - -```sh -export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" - -acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd -``` - -## 5. Deploy the cert to local exim4 server - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook exim4 -``` - -The default exim4 conf file is `/etc/exim/exim.conf`, if your exim4 conf is not in the default location, you can specify one: - -```sh -export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template" - -acme.sh --deploy -d ftp.example.com --deploy-hook exim4 -``` - -The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one: - -```sh -export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart" - -acme.sh --deploy -d ftp.example.com --deploy-hook exim4 -``` - -## 6. Deploy the cert to OSX Keychain - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook keychain -``` - -## 7. Deploy to cpanel host using UAPI - -This hook is using UAPI and works in cPanel & WHM version 56 or newer. -``` -acme.sh --deploy -d example.com --deploy-hook cpanel_uapi -``` -DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username. -```sh -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 separate certificate for each domain. - -## 8. Deploy the cert to your FRITZ!Box router - -You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables: -```sh -$ export DEPLOY_FRITZBOX_USERNAME=my_username -$ export DEPLOY_FRITZBOX_PASSWORD=the_password -$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com -``` - -After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this: - -```sh -acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox -``` - -## 9. Deploy the cert to strongswan - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook strongswan -``` - -## 10. Deploy the cert to HAProxy - -You must specify the path where you want the concatenated key and certificate chain written. -```sh -export DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy -``` - -You may optionally define the command to reload HAProxy. The value shown below will be used as the default if you don't set this environment variable. - -```sh -export DEPLOY_HAPROXY_RELOAD="/usr/sbin/service haproxy restart" -``` - -You can then deploy the certificate as follows -```sh -acme.sh --deploy -d haproxy.example.com --deploy-hook haproxy -``` - -The path for the PEM file will be stored with the domain configuration and will be available when renewing, so that deploy will happen automatically when renewed. - -## 11. Deploy your cert to Gitlab pages - -You must define the API key and the informations for the project and Gitlab page you are updating the certificate for. - -```sh -# The token can be created in your user settings under "Access Tokens" -export GITLAB_TOKEN="xxxxxxxxxxx" - -# The project ID is displayed on the home page of the project -export GITLAB_PROJECT_ID=12345678 - -# The domain must match the one defined for the Gitlab page, without "https://" -export GITLAB_DOMAIN="www.mydomain.com" -``` - -You can then deploy the certificate as follows - -```sh -acme.sh --deploy -d www.mydomain.com --deploy-hook gitlab -``` - -## 12. Deploy your cert to Hashicorp Vault - -```sh -export VAULT_PREFIX="acme" -``` - -You can then deploy the certificate as follows - -```sh -acme.sh --deploy -d www.mydomain.com --deploy-hook vault_cli -``` - -Your certs will be saved in Vault using this structure: - -```sh -vault write "${VAULT_PREFIX}/${domain}/cert.pem" value=@"..." -vault write "${VAULT_PREFIX}/${domain}/cert.key" value=@"..." -vault write "${VAULT_PREFIX}/${domain}/chain.pem" value=@"..." -vault write "${VAULT_PREFIX}/${domain}/fullchain.pem" value=@"..." -``` - -You might be using Fabio load balancer (which can get certs from -Vault). It needs a bit different structure of your certs in Vault. It -gets certs only from keys that were saved in `prefix/domain`, like this: - -```bash -vault write /www.domain.com cert=@cert.pem key=@key.pem -``` - -If you want to save certs in Vault this way just set "FABIO" env -variable to anything (ex: "1") before running `acme.sh`: - -```sh -export FABIO="1" -``` - -## 13. Deploy your certificate to Qiniu.com - -使用 acme.sh 部署到七牛之前,需要确保部署的域名已打开 HTTPS 功能,您可以访问[融合 CDN - 域名管理](https://portal.qiniu.com/cdn/domain) 设置。 -另外还需要先导出 AK/SK 环境变量,您可以访问[密钥管理](https://portal.qiniu.com/user/key) 获得。 - -```sh -$ export QINIU_AK="foo" -$ export QINIU_SK="bar" -``` - -完成准备工作之后,您就可以通过下面的命令开始部署 SSL 证书到七牛上: - -```sh -$ acme.sh --deploy -d example.com --deploy-hook qiniu -``` - -假如您部署的证书为泛域名证书,您还需要设置 `QINIU_CDN_DOMAIN` 变量,指定实际需要部署的域名(请注意泛域名前的点): - -```sh -$ export QINIU_CDN_DOMAIN=".cdn.example.com" -$ acme.sh --deploy -d example.com --deploy-hook qiniu -``` - -### English version - -You should create AccessKey/SecretKey pair in https://portal.qiniu.com/user/key -before deploying your certificate, and please ensure you have enabled HTTPS for -your domain name. You can enable it in https://portal.qiniu.com/cdn/domain. - -```sh -$ export QINIU_AK="foo" -$ export QINIU_SK="bar" -``` - -then you can deploy certificate by following command: - -```sh -$ acme.sh --deploy -d example.com --deploy-hook qiniu -``` - -(Optional), If you are using wildcard certificate, -you may need export `QINIU_CDN_DOMAIN` to specify which domain -you want to update (please note the leading dot): - -```sh -$ export QINIU_CDN_DOMAIN=".cdn.example.com" -$ acme.sh --deploy -d example.com --deploy-hook qiniu -``` - -## 14. Deploy your cert on MyDevil.net - -Once you have acme.sh installed and certificate issued (see info in [DNS API](../dnsapi/README.md#61-use-mydevilnet)), you can install it by following command: - -```sh -acme.sh --deploy --deploy-hook mydevil -d example.com -``` - -That will remove old certificate and install new one. - -## 15. Deploy your cert to local mailcow server - -You can install your certificates to a local [mailcow](https://github.com/mailcow/mailcow-dockerized/) instance. The -deploy hook will copy the certificates and reload the containers, that use the certificates (`postfix-mailcow` -`dovecot-mailcow` and `nginx-mailcow`). - -```sh -$ export DEPLOY_MAILCOW_PATH="/path/to/mailcow" -$ acme.sh --deploy -d example.com --deploy-hook mailcow -``` - -The default command to restart is `docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow`, if you want a -custom restart command, specify it by setting `DEPLOY_MAILCOW_RELOAD`: - -```sh -$ export DEPLOY_MAILCOW_PATH="/path/to/mailcow" -$ export DEPLOY_MAILCOW_RELOAD="docker-compose restart" -$ acme.sh --deploy -d example.com --deploy-hook mailcow -``` From 16b0704acc635a5e43033d199c3f7ba0208cfbaa Mon Sep 17 00:00:00 2001 From: temoffey Date: Wed, 20 Mar 2019 18:10:53 +0300 Subject: [PATCH 301/403] remove readme --- deploy/README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 76a6cc94..44d53225 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -391,18 +391,3 @@ acme.sh --deploy --deploy-hook mydevil -d example.com ``` That will remove old certificate and install new one. - -## 15. Deploy the cert to G-Core CDN service - -Deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). -Uses command line curl for send requests and jq for parse responses. - -Then you can deploy now: - -```sh -export DEPLOY_GCORE_CDN_USERNAME=myusername -export DEPLOY_GCORE_CDN_PASSWORD=mypassword -acme.sh --deploy -d example.com --deploy-hook gcore_cdn -``` - -Please note, need installed jq. From 236acbd6e8c45f68d0cecc648cd124796ae4427f Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 20 Mar 2019 23:11:13 +0800 Subject: [PATCH 302/403] move to wiki --- README.md | 88 +++---------------------------------------------------- 1 file changed, 4 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 68d1b57d..6682c556 100644 --- a/README.md +++ b/README.md @@ -290,90 +290,10 @@ If your DNS provider supports API access, we can use that API to automatically i You don't have to do anything manually! -### Currently acme.sh supports: - -1. CloudFlare.com API -1. DNSPod.cn API -1. CloudXNS.com API -1. GoDaddy.com API -1. PowerDNS.com API -1. OVH, kimsufi, soyoustart and runabove API -1. nsupdate API -1. LuaDNS.com API -1. DNSMadeEasy.com API -1. AWS Route 53 -1. aliyun.com(阿里云) API -1. ISPConfig 3.1 API -1. Alwaysdata.com API -1. Linode.com API -1. FreeDNS (https://freedns.afraid.org/) -1. cyon.ch -1. Domain-Offensive/Resellerinterface/Domainrobot API -1. Gandi LiveDNS API -1. Knot DNS API -1. DigitalOcean API (native) -1. ClouDNS.net API -1. Infoblox NIOS API (https://www.infoblox.com/) -1. VSCALE (https://vscale.io/) -1. Dynu API (https://www.dynu.com) -1. DNSimple API -1. NS1.com API -1. DuckDNS.org API -1. Name.com API -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/) -1. INWX (https://www.inwx.de/) -1. Servercow (https://servercow.de) -1. Namesilo (https://www.namesilo.com) -1. InternetX autoDNS API (https://internetx.com) -1. Azure DNS -1. selectel.com(selectel.ru) DNS API -1. zonomi.com DNS API -1. DreamHost.com API -1. DirectAdmin API -1. KingHost (https://www.kinghost.com.br/) -1. Zilore (https://zilore.com) -1. Loopia.se API -1. acme-dns (https://github.com/joohoi/acme-dns) -1. TELE3 (https://www.tele3.cz) -1. EUSERV.EU (https://www.euserv.eu) -1. DNSPod.com API (https://www.dnspod.com) -1. Google Cloud DNS API -1. ConoHa (https://www.conoha.jp) -1. netcup DNS API (https://www.netcup.de) -1. GratisDNS.dk (https://gratisdns.dk) -1. Namecheap API (https://www.namecheap.com/) -1. MyDNS.JP API (https://www.mydns.jp/) -1. hosting.de (https://www.hosting.de) -1. Neodigit.net API (https://www.neodigit.net) -1. Exoscale.com API (https://www.exoscale.com/) -1. PointDNS API (https://pointhq.com/) -1. Active24.cz API (https://www.active24.cz/) -1. do.de API (https://www.do.de/) -1. Nexcess API (https://www.nexcess.net) -1. Thermo.io API (https://www.thermo.io) -1. Futurehosting API (https://www.futurehosting.com) -1. Rackspace Cloud DNS (https://www.rackspace.com) -1. Online.net API (https://online.net/) -1. MyDevil.net (https://www.mydevil.net/) -1. Core-Networks.de (https://core-networks.de) -1. NederHost API (https://www.nederhost.nl/) -1. Zone.ee (zone.eu) API (https://api.zone.eu/v2) -1. UltraDNS API (https://portal.ultradns.com) - -And: - -**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. - -For more details: [How to use DNS API](dnsapi) +### Currently acme.sh supports most of the dns providers: + +https://github.com/Neilpang/acme.sh/wiki/dnsapi + # 9. Use DNS manual mode: From 3bb97b81ded2d7528cd432d2219df69af5de2870 Mon Sep 17 00:00:00 2001 From: James Qian Date: Thu, 14 Mar 2019 13:26:58 +0800 Subject: [PATCH 303/403] dnsapi: add deSEC.io api support Signed-off-by: James Qian --- dnsapi/dns_desec.sh | 204 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 dnsapi/dns_desec.sh diff --git a/dnsapi/dns_desec.sh b/dnsapi/dns_desec.sh new file mode 100644 index 00000000..6488b7fb --- /dev/null +++ b/dnsapi/dns_desec.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env sh +# +# deSEC.io Domain API +# +# Author: Zheng Qian +# +# deSEC API doc +# https://desec.readthedocs.io/en/latest/ + +REST_API="https://desec.io/api/v1/domains" + +######## Public functions ##################### + +#Usage: dns_desec_add _acme-challenge.foobar.dedyn.io "d41d8cd98f00b204e9800998ecf8427e" +dns_desec_add() { + fulldomain=$1 + txtvalue=$2 + _info "Using desec.io api" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}" + DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}" + + if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then + DEDYN_TOKEN="" + DEDYN_NAME="" + _err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet." + _err "Please create you key and try again." + _err "e.g." + _err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e" + _err "export DEDYN_NAME=foobar.dedyn.io" + return 1 + fi + #save the api token and name to the account conf file. + _saveaccountconf_mutable DEDYN_TOKEN "$DEDYN_TOKEN" + _saveaccountconf_mutable DEDYN_NAME "$DEDYN_NAME" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain" "$REST_API/"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + # Get existing TXT record + _debug "Getting txt records" + txtvalues="\"\\\"$txtvalue\\\"\"" + _desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/" + + if [ "$_code" = "200" ]; then + oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")" + _debug "existing TXT found" + _debug oldtxtvalues "$oldtxtvalues" + if [ -n "$oldtxtvalues" ]; then + for oldtxtvalue in $oldtxtvalues; do + txtvalues="$txtvalues, \"\\\"$oldtxtvalue\\\"\"" + done + fi + fi + _debug txtvalues "$txtvalues" + _info "Adding record" + body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]" + + if _desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"; then + if _contains "$response" "$txtvalue"; 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_desec_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using desec.io api" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}" + DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}" + + if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then + DEDYN_TOKEN="" + DEDYN_NAME="" + _err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet." + _err "Please create you key and try again." + _err "e.g." + _err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e" + _err "export DEDYN_NAME=foobar.dedyn.io" + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain" "$REST_API/"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + # Get existing TXT record + _debug "Getting txt records" + txtvalues="" + _desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/" + + if [ "$_code" = "200" ]; then + oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")" + _debug "existing TXT found" + _debug oldtxtvalues "$oldtxtvalues" + if [ -n "$oldtxtvalues" ]; then + for oldtxtvalue in $oldtxtvalues; do + if [ "$txtvalue" != "$oldtxtvalue" ]; then + txtvalues="$txtvalues, \"\\\"$oldtxtvalue\\\"\"" + fi + done + fi + fi + txtvalues="$(echo "$txtvalues" | cut -c3-)" + _debug txtvalues "$txtvalues" + + _info "Deleting record" + body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]" + _desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body" + if [ "$_code" = "200" ]; then + _info "Deleted, OK" + return 0 + fi + + _err "Delete txt record error." + return 1 +} + +#################### Private functions below ################################## + +_desec_rest() { + m="$1" + ep="$2" + data="$3" + + export _H1="Authorization: Token $DEDYN_TOKEN" + export _H2="Accept: application/json" + export _H3="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _secure_debug2 data "$data" + response="$(_post "$data" "$ep" "" "$m")" + else + response="$(_get "$ep")" + fi + _ret="$?" + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" + _debug "http response code $_code" + _secure_debug2 response "$response" + if [ "$_ret" != "0" ]; then + _err "error $ep" + return 1 + fi + + response="$(printf "%s" "$response" | _normalizeJson)" + return 0 +} + +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain="$1" + ep="$2" + 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 ! _desec_rest GET "$ep"; then + return 1 + fi + + if _contains "$response" "\"name\":\"$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 + return 1 +} From b8489464b3d9600d9f06f363c484256f97140d09 Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 03:41:26 +0300 Subject: [PATCH 304/403] remove use awk, jq, curl --- deploy/gcore_cdn.sh | 52 +++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 18d137a6..31f8db68 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -27,8 +27,8 @@ gcore_cdn_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _fullchain=$(awk 1 ORS='\\n' "$_cfullchain") - _key=$(awk 1 ORS='\\n' "$_ckey") + _fullchain=$(while read line; do printf "%s" "$line\n"; done < "$_cfullchain") + _key=$(while read line; do printf "%s" "$line\n"; done < "$_ckey") _debug _fullchain "$_fullchain" _debug _key "$_key" @@ -61,43 +61,57 @@ gcore_cdn_deploy() { _info "Get authorization token" _request="{ \"username\": \"$Le_Deploy_gcore_cdn_username\", \"password\": \"$Le_Deploy_gcore_cdn_password\" }" _debug _request "$_request" - _response=$(curl -s -X POST https://api.gcdn.co/auth/signin -H "Content-Type:application/json" -d "$_request") + _H1="Content-Type:application/json" + _response=$(_post "$_request" "https://api.gcdn.co/auth/signin") _debug _response "$_response" - _token=$(echo "$_response" | jq -r '.token') + _regex="\"token\":\"([^\"]+)\"" + _debug _regex "$_regex" + _token=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _token "$_token" - if [ "$_token" = "null" ]; then + if [ -z "$_token" ]; then _err "Error G-Core Labs API authorization" return 1 fi _info "Find CDN resource with cname $_cdomain" - _response=$(curl -s -X GET https://api.gcdn.co/resources -H "Authorization:Token $_token") + _H2="Authorization:Token $_token" + _response=$(_get "https://api.gcdn.co/resources") _debug _response "$_response" - _resource=$(echo "$_response" | jq -r ".[] | select(.cname == \"$_cdomain\")") + _regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})" + _debug _regex "$_regex" + _resource=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _resource "$_resource" - _resourceId=$(echo "$_resource" | jq -r '.id') - _sslDataOld=$(echo "$_resource" | jq -r '.sslData') - _originGroup=$(echo "$_resource" | jq -r '.originGroup') + _regex="\"id\":([0-9]+)" + _debug _regex "$_regex" + _resourceId=$(if [[ $_resource =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _resourceId "$_resourceId" + _regex="\"sslData\":([0-9]+|null)" + _debug _regex "$_regex" + _sslDataOld=$(if [[ $_resource =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _sslDataOld "$_sslDataOld" + _regex="\"originGroup\":([0-9]+)" + _debug _regex "$_regex" + _originGroup=$(if [[ $_resource =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _originGroup "$_originGroup" - if [ -z "$_resourceId" ] || [ "$_resourceId" = "null" ] || [ -z "$_originGroup" ] || [ "$_originGroup" = "null" ]; then + if [ -z "$_resourceId" ] || [ -z "$_originGroup" ]; then _err "Not found CDN resource with cname $_cdomain" return 1 fi _info "Add new SSL certificate" _date=$(date "+%d.%m.%Y %H:%M:%S") - _request="{ \"name\": \"$_cdomain ($_date)\", \"sslCertificate\": \"$_fullchain\n\", \"sslPrivateKey\": \"$_key\n\" }" + _request="{ \"name\": \"$_cdomain ($_date)\", \"sslCertificate\": \"$_fullchain\", \"sslPrivateKey\": \"$_key\" }" _debug _request "$_request" - _response=$(curl -s -X POST https://api.gcdn.co/sslData -H "Content-Type:application/json" -H "Authorization:Token $_token" -d "$_request") + _response=$(_post "$_request" "https://api.gcdn.co/sslData") _debug _response "$_response" - _sslDataAdd=$(echo "$_response" | jq -r '.id') + _regex="\"id\":([0-9]+)" + _debug _regex "$_regex" + _sslDataAdd=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _sslDataAdd "$_sslDataAdd" - if [ "$_sslDataAdd" = "null" ]; then + if [ -z "$_sslDataAdd" ]; then _err "Error new SSL certificate add" return 1 fi @@ -105,9 +119,11 @@ gcore_cdn_deploy() { _info "Update CDN resource" _request="{ \"originGroup\": $_originGroup, \"sslData\": $_sslDataAdd }" _debug _request "$_request" - _response=$(curl -s -X PUT "https://api.gcdn.co/resources/$_resourceId" -H "Content-Type:application/json" -H "Authorization:Token $_token" -d "$_request") + _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" - _sslDataNew=$(echo "$_response" | jq -r '.sslData') + _regex="\"sslData\":([0-9]+)" + _debug _regex "$_regex" + _sslDataNew=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) _debug _sslDataNew "$_sslDataNew" if [ "$_sslDataNew" != "$_sslDataAdd" ]; then @@ -119,7 +135,7 @@ gcore_cdn_deploy() { _info "Not found old SSL certificate" else _info "Delete old SSL certificate" - _response=$(curl -s -X DELETE "https://api.gcdn.co/sslData/$_sslDataOld" -H "Authorization:Token $_token") + _response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE") _debug _response "$_response" fi From d289b0b450a3c4c3a4645ddefb136560f9125deb Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 04:21:41 +0300 Subject: [PATCH 305/403] fix syntax --- deploy/gcore_cdn.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 31f8db68..439508c2 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -27,8 +27,8 @@ gcore_cdn_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _fullchain=$(while read line; do printf "%s" "$line\n"; done < "$_cfullchain") - _key=$(while read line; do printf "%s" "$line\n"; done < "$_ckey") + _fullchain=$(while read -r line; do printf "%s" "$line\n"; done <"$_cfullchain") + _key=$(while read -r line; do printf "%s" "$line\n"; done <"$_ckey") _debug _fullchain "$_fullchain" _debug _key "$_key" @@ -66,7 +66,7 @@ gcore_cdn_deploy() { _debug _response "$_response" _regex="\"token\":\"([^\"]+)\"" _debug _regex "$_regex" - _token=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _token=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _token "$_token" if [ -z "$_token" ]; then @@ -80,19 +80,19 @@ gcore_cdn_deploy() { _debug _response "$_response" _regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})" _debug _regex "$_regex" - _resource=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _resource=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _resource "$_resource" _regex="\"id\":([0-9]+)" _debug _regex "$_regex" - _resourceId=$(if [[ $_resource =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _resourceId=$(if [[ $_resource =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _resourceId "$_resourceId" _regex="\"sslData\":([0-9]+|null)" _debug _regex "$_regex" - _sslDataOld=$(if [[ $_resource =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _sslDataOld=$(if [[ $_resource =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _sslDataOld "$_sslDataOld" _regex="\"originGroup\":([0-9]+)" _debug _regex "$_regex" - _originGroup=$(if [[ $_resource =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _originGroup=$(if [[ $_resource =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _originGroup "$_originGroup" if [ -z "$_resourceId" ] || [ -z "$_originGroup" ]; then @@ -108,7 +108,7 @@ gcore_cdn_deploy() { _debug _response "$_response" _regex="\"id\":([0-9]+)" _debug _regex "$_regex" - _sslDataAdd=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _sslDataAdd=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _sslDataAdd "$_sslDataAdd" if [ -z "$_sslDataAdd" ]; then @@ -123,7 +123,7 @@ gcore_cdn_deploy() { _debug _response "$_response" _regex="\"sslData\":([0-9]+)" _debug _regex "$_regex" - _sslDataNew=$(if [[ $_response =~ $_regex ]]; then printf "${BASH_REMATCH[1]}"; fi) + _sslDataNew=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) _debug _sslDataNew "$_sslDataNew" if [ "$_sslDataNew" != "$_sslDataAdd" ]; then From 0ecb5a3fec0b14e410ac4cc6682eae7051651510 Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 04:31:58 +0300 Subject: [PATCH 306/403] fix syntax --- deploy/gcore_cdn.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 439508c2..2655cbc4 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -61,8 +61,7 @@ gcore_cdn_deploy() { _info "Get authorization token" _request="{ \"username\": \"$Le_Deploy_gcore_cdn_username\", \"password\": \"$Le_Deploy_gcore_cdn_password\" }" _debug _request "$_request" - _H1="Content-Type:application/json" - _response=$(_post "$_request" "https://api.gcdn.co/auth/signin") + _response=$(_H1="Content-Type:application/json" && _post "$_request" "https://api.gcdn.co/auth/signin") _debug _response "$_response" _regex="\"token\":\"([^\"]+)\"" _debug _regex "$_regex" @@ -75,8 +74,7 @@ gcore_cdn_deploy() { fi _info "Find CDN resource with cname $_cdomain" - _H2="Authorization:Token $_token" - _response=$(_get "https://api.gcdn.co/resources") + _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _get "https://api.gcdn.co/resources") _debug _response "$_response" _regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})" _debug _regex "$_regex" @@ -104,7 +102,7 @@ gcore_cdn_deploy() { _date=$(date "+%d.%m.%Y %H:%M:%S") _request="{ \"name\": \"$_cdomain ($_date)\", \"sslCertificate\": \"$_fullchain\", \"sslPrivateKey\": \"$_key\" }" _debug _request "$_request" - _response=$(_post "$_request" "https://api.gcdn.co/sslData") + _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _post "$_request" "https://api.gcdn.co/sslData") _debug _response "$_response" _regex="\"id\":([0-9]+)" _debug _regex "$_regex" @@ -119,7 +117,7 @@ gcore_cdn_deploy() { _info "Update CDN resource" _request="{ \"originGroup\": $_originGroup, \"sslData\": $_sslDataAdd }" _debug _request "$_request" - _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") + _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" _regex="\"sslData\":([0-9]+)" _debug _regex "$_regex" @@ -135,7 +133,7 @@ gcore_cdn_deploy() { _info "Not found old SSL certificate" else _info "Delete old SSL certificate" - _response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE") + _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE") _debug _response "$_response" fi From 8896642e2541265a4627792b922204129d6c9cca Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 20:01:39 +0300 Subject: [PATCH 307/403] fix syntax --- deploy/gcore_cdn.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 2655cbc4..f9ed6c7d 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -61,7 +61,8 @@ gcore_cdn_deploy() { _info "Get authorization token" _request="{ \"username\": \"$Le_Deploy_gcore_cdn_username\", \"password\": \"$Le_Deploy_gcore_cdn_password\" }" _debug _request "$_request" - _response=$(_H1="Content-Type:application/json" && _post "$_request" "https://api.gcdn.co/auth/signin") + export _H1="Content-Type:application/json" + _response=$(_post "$_request" "https://api.gcdn.co/auth/signin") _debug _response "$_response" _regex="\"token\":\"([^\"]+)\"" _debug _regex "$_regex" @@ -74,7 +75,8 @@ gcore_cdn_deploy() { fi _info "Find CDN resource with cname $_cdomain" - _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _get "https://api.gcdn.co/resources") + export _H2="Authorization:Token $_token" + _response=$(_get "https://api.gcdn.co/resources") _debug _response "$_response" _regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})" _debug _regex "$_regex" @@ -102,7 +104,7 @@ gcore_cdn_deploy() { _date=$(date "+%d.%m.%Y %H:%M:%S") _request="{ \"name\": \"$_cdomain ($_date)\", \"sslCertificate\": \"$_fullchain\", \"sslPrivateKey\": \"$_key\" }" _debug _request "$_request" - _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _post "$_request" "https://api.gcdn.co/sslData") + _response=$(_post "$_request" "https://api.gcdn.co/sslData") _debug _response "$_response" _regex="\"id\":([0-9]+)" _debug _regex "$_regex" @@ -117,7 +119,7 @@ gcore_cdn_deploy() { _info "Update CDN resource" _request="{ \"originGroup\": $_originGroup, \"sslData\": $_sslDataAdd }" _debug _request "$_request" - _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") + _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" _regex="\"sslData\":([0-9]+)" _debug _regex "$_regex" @@ -133,7 +135,7 @@ gcore_cdn_deploy() { _info "Not found old SSL certificate" else _info "Delete old SSL certificate" - _response=$(_H1="Content-Type:application/json" && _H2="Authorization:Token $_token" && _post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE") + _response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE") _debug _response "$_response" fi From 4b6e7e6c371a7945e2b3304ff7241bf05691d453 Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 20:02:59 +0300 Subject: [PATCH 308/403] remove use while, [[ ]], array --- deploy/gcore_cdn.sh | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index f9ed6c7d..f0cc43ec 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -27,8 +27,8 @@ gcore_cdn_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _fullchain=$(while read -r line; do printf "%s" "$line\n"; done <"$_cfullchain") - _key=$(while read -r line; do printf "%s" "$line\n"; done <"$_ckey") + _fullchain=$(cat "$_cfullchain" | tr '\n\r' '@#' | sed 's/@/\\n/g;s/#/\\r/g') + _key=$(cat "$_ckey" | tr '\n\r' '@#' | sed 's/@/\\n/g;s/#/\\r/g') _debug _fullchain "$_fullchain" _debug _key "$_key" @@ -59,14 +59,14 @@ gcore_cdn_deploy() { fi _info "Get authorization token" - _request="{ \"username\": \"$Le_Deploy_gcore_cdn_username\", \"password\": \"$Le_Deploy_gcore_cdn_password\" }" + _request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}" _debug _request "$_request" export _H1="Content-Type:application/json" _response=$(_post "$_request" "https://api.gcdn.co/auth/signin") _debug _response "$_response" - _regex="\"token\":\"([^\"]+)\"" + _regex=".*\"token\":\"\([-._0-9A-Za-z]*\)\".*$" _debug _regex "$_regex" - _token=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _token=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _token "$_token" if [ -z "$_token" ]; then @@ -79,20 +79,21 @@ gcore_cdn_deploy() { _response=$(_get "https://api.gcdn.co/resources") _debug _response "$_response" _regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})" + _regex="\"cname\":\"$_cdomain\"" _debug _regex "$_regex" - _resource=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _resource=$(echo "$_response" | sed 's/},{/},\n{/g' | grep -E "$_regex") _debug _resource "$_resource" - _regex="\"id\":([0-9]+)" + _regex=".*\"id\":\([0-9]*\),.*$" _debug _regex "$_regex" - _resourceId=$(if [[ $_resource =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _debug _resourceId "$_resourceId" - _regex="\"sslData\":([0-9]+|null)" + _regex=".*\"sslData\":\([0-9]*\)}.*$" _debug _regex "$_regex" - _sslDataOld=$(if [[ $_resource =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _debug _sslDataOld "$_sslDataOld" - _regex="\"originGroup\":([0-9]+)" + _regex=".*\"originGroup\":\([0-9]*\),.*$" _debug _regex "$_regex" - _originGroup=$(if [[ $_resource =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _debug _originGroup "$_originGroup" if [ -z "$_resourceId" ] || [ -z "$_originGroup" ]; then @@ -102,13 +103,13 @@ gcore_cdn_deploy() { _info "Add new SSL certificate" _date=$(date "+%d.%m.%Y %H:%M:%S") - _request="{ \"name\": \"$_cdomain ($_date)\", \"sslCertificate\": \"$_fullchain\", \"sslPrivateKey\": \"$_key\" }" + _request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}" _debug _request "$_request" _response=$(_post "$_request" "https://api.gcdn.co/sslData") _debug _response "$_response" - _regex="\"id\":([0-9]+)" + _regex=".*\"id\":\([0-9]*\),.*$" _debug _regex "$_regex" - _sslDataAdd=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _sslDataAdd "$_sslDataAdd" if [ -z "$_sslDataAdd" ]; then @@ -117,13 +118,13 @@ gcore_cdn_deploy() { fi _info "Update CDN resource" - _request="{ \"originGroup\": $_originGroup, \"sslData\": $_sslDataAdd }" + _request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}" _debug _request "$_request" _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" - _regex="\"sslData\":([0-9]+)" + _regex=".*\"sslData\":\([0-9]*\)}.*$" _debug _regex "$_regex" - _sslDataNew=$(if [[ $_response =~ $_regex ]]; then printf "%s" "${BASH_REMATCH[1]}"; fi) + _sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _sslDataNew "$_sslDataNew" if [ "$_sslDataNew" != "$_sslDataAdd" ]; then From bd1bb7a71bf79daa70db446995c9ca54517f57e2 Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 20:08:35 +0300 Subject: [PATCH 309/403] fix syntax --- deploy/gcore_cdn.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index f0cc43ec..40fbf480 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -27,8 +27,8 @@ gcore_cdn_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _fullchain=$(cat "$_cfullchain" | tr '\n\r' '@#' | sed 's/@/\\n/g;s/#/\\r/g') - _key=$(cat "$_ckey" | tr '\n\r' '@#' | sed 's/@/\\n/g;s/#/\\r/g') + _fullchain=$(tr '\n\r' '@#' <"$_cfullchain" | sed 's/@/\\n/g;s/#/\\r/g') + _key=$(tr '\n\r' '@#' <"$_ckey" | sed 's/@/\\n/g;s/#/\\r/g') _debug _fullchain "$_fullchain" _debug _key "$_key" From df9174577a503811973c5d185abc5f3000736a2a Mon Sep 17 00:00:00 2001 From: temoffey Date: Fri, 22 Mar 2019 23:00:47 +0300 Subject: [PATCH 310/403] remove check jq --- deploy/gcore_cdn.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 40fbf480..b38226f4 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -53,11 +53,6 @@ gcore_cdn_deploy() { _savedomainconf Le_Deploy_gcore_cdn_password "$Le_Deploy_gcore_cdn_password" fi - if ! [ -x "$(command -v jq)" ]; then - _err "Please install the package jq: sudo apt-get install jq" - return 1 - fi - _info "Get authorization token" _request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}" _debug _request "$_request" From 189a7766d4fc4684ba971147474a7dd3187a29dd Mon Sep 17 00:00:00 2001 From: Charles Surett Date: Fri, 22 Mar 2019 18:43:06 -0400 Subject: [PATCH 311/403] Made dns_dgon.sh use _lower_case Fixed private function which breaks on embedded systems before. --- dnsapi/dns_dgon.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dgon.sh b/dnsapi/dns_dgon.sh index 24e1a9f2..c176afd3 100755 --- a/dnsapi/dns_dgon.sh +++ b/dnsapi/dns_dgon.sh @@ -178,7 +178,7 @@ dns_dgon_rm() { ## _domain="domain.com" _get_base_domain() { # args - fulldomain="$(echo "$1" | tr '[:upper:]' '[:lower:]')" + fulldomain="$(echo "$1" | _lower_case)" _debug fulldomain "$fulldomain" # domain max legal length = 253 From bea52aa7435d57733be37fe45bd06938c8ffab0f Mon Sep 17 00:00:00 2001 From: temoffey Date: Sat, 23 Mar 2019 16:29:33 +0300 Subject: [PATCH 312/403] remove use grep -E --- deploy/gcore_cdn.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index b38226f4..56ca9afd 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -74,9 +74,9 @@ gcore_cdn_deploy() { _response=$(_get "https://api.gcdn.co/resources") _debug _response "$_response" _regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})" - _regex="\"cname\":\"$_cdomain\"" + _regex="^.*\"cname\":\"$_cdomain\".*$" _debug _regex "$_regex" - _resource=$(echo "$_response" | sed 's/},{/},\n{/g' | grep -E "$_regex") + _resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex") _debug _resource "$_resource" _regex=".*\"id\":\([0-9]*\),.*$" _debug _regex "$_regex" From 978ec91107db6adb140a33141577de2119db5822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Toki=C4=87?= Date: Tue, 5 Mar 2019 20:16:54 +0100 Subject: [PATCH 313/403] Extract configuration loading code to function --- dnsapi/dns_loopia.sh | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh index ece5ef8c..a66a443a 100644 --- a/dnsapi/dns_loopia.sh +++ b/dnsapi/dns_loopia.sh @@ -14,13 +14,7 @@ dns_loopia_add() { fulldomain=$1 txtvalue=$2 - LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}" - LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}" - if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then - LOOPIA_User="" - LOOPIA_Password="" - _err "You don't specify loopia user and password yet." - _err "Please create you key and try again." + if ! _loopia_load_config; then return 1 fi @@ -47,13 +41,7 @@ dns_loopia_rm() { fulldomain=$1 txtvalue=$2 - LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}" - LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}" - if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then - LOOPIA_User="" - LOOPIA_Password="" - _err "You don't specify LOOPIA user and password yet." - _err "Please create you key and try again." + if ! _loopia_load_config; then return 1 fi @@ -96,6 +84,23 @@ dns_loopia_rm() { #################### Private functions below ################################## +_loopia_load_config() { + LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}" + LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}" + + if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then + LOOPIA_User="" + LOOPIA_Password="" + + _err "You don't specify loopia user and password yet." + _err "Please create you key and try again." + + return 1 + fi + + return 0 +} + _loopia_get_records() { domain=$1 sub_domain=$2 From a7d614616997d6e109b30711c804d194d5a68fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Toki=C4=87?= Date: Tue, 5 Mar 2019 20:21:14 +0100 Subject: [PATCH 314/403] Extract configuration saving code to function --- dnsapi/dns_loopia.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh index a66a443a..92556e7e 100644 --- a/dnsapi/dns_loopia.sh +++ b/dnsapi/dns_loopia.sh @@ -18,9 +18,7 @@ dns_loopia_add() { return 1 fi - #save the api key and email to the account conf file. - _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User" - _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password" + _loopia_save_config _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -45,9 +43,7 @@ dns_loopia_rm() { return 1 fi - #save the api key and email to the account conf file. - _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User" - _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password" + _loopia_save_config _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -101,6 +97,11 @@ _loopia_load_config() { return 0 } +_loopia_save_config() { + _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User" + _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password" +} + _loopia_get_records() { domain=$1 sub_domain=$2 From 85be2b85fd9103efc391f0c08de73d59638aa7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Toki=C4=87?= Date: Tue, 5 Mar 2019 21:10:11 +0100 Subject: [PATCH 315/403] Fix error message language --- dnsapi/dns_loopia.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh index 92556e7e..e8d19d15 100644 --- a/dnsapi/dns_loopia.sh +++ b/dnsapi/dns_loopia.sh @@ -88,8 +88,8 @@ _loopia_load_config() { LOOPIA_User="" LOOPIA_Password="" - _err "You don't specify loopia user and password yet." - _err "Please create you key and try again." + _err "A valid Loopia API user and password not provided." + _err "Please provide a valid API user and try again." return 1 fi From 0daa225e2641da94d0045e4974b6af9ba91effc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Toki=C4=87?= Date: Tue, 5 Mar 2019 20:56:34 +0100 Subject: [PATCH 316/403] Make the Loopia API endpoint configurable Loopia provides hosting in several countries. Each hosting location has it's own API endpoint, such as "https://api.loopia./RPCSERV", where is one of: com, no, rs, se. The current LOOPIA_Api variable is hard-coded to ".se". This prevents using the Loopia DNS API on other hosting locations. This commit makes the LOOPIA_Api variable configurable and it falls back to ".se" TLD if LOOPIA_Api is not set. References: - https://www.loopia.com/api/authentication/ - https://www.loopia.no/api/authentication/ - https://www.loopia.rs/api/authentication/ - https://www.loopia.se/api/authentication/ --- dnsapi/dns_loopia.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh index e8d19d15..109507ca 100644 --- a/dnsapi/dns_loopia.sh +++ b/dnsapi/dns_loopia.sh @@ -4,8 +4,10 @@ #LOOPIA_User="username" # #LOOPIA_Password="password" +# +#LOOPIA_Api="https://api.loopia./RPCSERV" -LOOPIA_Api="https://api.loopia.se/RPCSERV" +LOOPIA_Api_Default="https://api.loopia.se/RPCSERV" ######## Public functions ##################### @@ -81,9 +83,14 @@ dns_loopia_rm() { #################### Private functions below ################################## _loopia_load_config() { + LOOPIA_Api="${LOOPIA_Api:-$(_readaccountconf_mutable LOOPIA_Api)}" LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}" LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}" + if [ -z "$LOOPIA_Api" ]; then + LOOPIA_Api="$LOOPIA_Api_Default" + fi + if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then LOOPIA_User="" LOOPIA_Password="" @@ -98,6 +105,9 @@ _loopia_load_config() { } _loopia_save_config() { + if [ "$LOOPIA_Api" != "$LOOPIA_Api_Default" ]; then + _saveaccountconf_mutable LOOPIA_Api "$LOOPIA_Api" + fi _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User" _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password" } From aec9c3c9a4820dbd05660bffc074c95f3aee77a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Toki=C4=87?= Date: Thu, 28 Mar 2019 16:34:13 +0100 Subject: [PATCH 317/403] Double quote unquoted variables Double quote unquoted variables to prevent globbing and word splitting. --- dnsapi/dns_loopia.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh index 109507ca..1316a274 100644 --- a/dnsapi/dns_loopia.sh +++ b/dnsapi/dns_loopia.sh @@ -70,7 +70,7 @@ dns_loopia_rm() { %s - ' $LOOPIA_User $LOOPIA_Password "$_domain" "$_sub_domain") + ' "$LOOPIA_User" "$LOOPIA_Password" "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")" From c2d0d4d28c86b3edeb2321f6ea98e9ffbd22fccf Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 31 Mar 2019 21:46:14 +0800 Subject: [PATCH 318/403] root domain as dns alias mode --- dnsapi/dns_cf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 532199f3..96731435 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -147,7 +147,7 @@ dns_cf_rm() { # _domain_id=sdjkglgdfewsdfg _get_root() { domain=$1 - i=2 + i=1 p=1 while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) From 6e917d156c0abcf7f3ebc0c8d008af44a8cba45f Mon Sep 17 00:00:00 2001 From: Gorbachev Date: Tue, 2 Apr 2019 18:05:52 +0300 Subject: [PATCH 319/403] Trim double quotes for email and key Currently dns_cf generates headers like this: 'X-Auth-Email: "sample@mail.com"'. Cloudflare API responses 400 BadRequest for quoted headers with message "Invalid format for X-Auth-Email header". --- dnsapi/dns_cf.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 532199f3..73089978 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -182,8 +182,11 @@ _cf_rest() { data="$3" _debug "$ep" - export _H1="X-Auth-Email: $CF_Email" - export _H2="X-Auth-Key: $CF_Key" + email_trimmed=$(echo $CF_Email | tr -d '"') + key_trimmed=$(echo $CF_Key | tr -d '"') + + export _H1="X-Auth-Email: $email_trimmed" + export _H2="X-Auth-Key: $key_trimmed" export _H3="Content-Type: application/json" if [ "$m" != "GET" ]; then From 987f95221c66abcd2a439102096d87510004de74 Mon Sep 17 00:00:00 2001 From: Kimmax Date: Tue, 2 Apr 2019 23:08:39 +0000 Subject: [PATCH 320/403] Added missing "templateValues" object to "zoneConfig" on "_hostingde_getZoneConfig" --- dnsapi/dns_hostingde.sh | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 1819e639..4cfe33fb 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -53,6 +53,18 @@ _hostingde_parse() { fi } +_hostingde_parse_no_strip_whitespace() { + find="${1}" + if [ "${2}" ]; then + notfind="${2}" + fi + if [ "${notfind}" ]; then + _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 + else + _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 + fi +} + _hostingde_getZoneConfig() { _info "Getting ZoneConfig" curZone="${fulldomain#*.}" @@ -85,6 +97,22 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") + zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse_object "templateValues") + + if [ "$zoneConfigTemplateValues" != "null" ]; then + _debug "Zone is tied to a template." + zoneConfigTemplateValuesTemplateId=$(echo "${curResult}" | _hostingde_parse "templateId") + zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateName") + zoneConfigTemplateValuesTemplateReplacementsIPv4=$(echo "${curResult}" | _hostingde_parse "ipv4Replacement") + zoneConfigTemplateValuesTemplateReplacementsIPv6=$(echo "${curResult}" | _hostingde_parse "ipv6Replacement") + zoneConfigTemplateValuesTemplateReplacementsMailIPv4=$(echo "${curResult}" | _hostingde_parse "mailIpv4Replacement") + zoneConfigTemplateValuesTemplateReplacementsMailIPv6=$(echo "${curResult}" | _hostingde_parse "mailIpv6Replacement") + zoneConfigTemplateValuesTemplateTieToTemplate=$(echo "${curResult}" | _hostingde_parse "tieToTemplate") + + zoneConfigTemplateValues="{\"templateId\":${zoneConfigTemplateValuesTemplateId},\"templateName\":${zoneConfigTemplateValuesTemplateName},\"templateReplacements\":{\"ipv4Replacement\":${zoneConfigTemplateValuesTemplateReplacementsIPv4},\"ipv6Replacement\":${zoneConfigTemplateValuesTemplateReplacementsIPv6},\"mailIpv4Replacement\":${zoneConfigTemplateValuesTemplateReplacementsMailIPv4},\"mailIpv6Replacement\":${zoneConfigTemplateValuesTemplateReplacementsMailIPv6}},\"tieToTemplate\":${zoneConfigTemplateValuesTemplateTieToTemplate}}" + _debug "Template values: '{$zoneConfigTemplateValues}'" + fi + if [ "${zoneConfigType}" != "\"NATIVE\"" ]; then _err "Zone is not native" returnCode=1 @@ -122,7 +150,7 @@ _hostingde_addRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '${zoneStatus}'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}},\"templateValues\":${zoneConfigTemplateValues}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" From 64e53927880732978cf3702b6afa792156ae4db3 Mon Sep 17 00:00:00 2001 From: Kimmax Date: Tue, 2 Apr 2019 23:29:58 +0000 Subject: [PATCH 321/403] Zone delete also needs new "templateValues" field --- dnsapi/dns_hostingde.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 4cfe33fb..1aa70394 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -97,7 +97,7 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") - zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse_object "templateValues") + zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateValues") if [ "$zoneConfigTemplateValues" != "null" ]; then _debug "Zone is tied to a template." @@ -174,7 +174,7 @@ _hostingde_removeRecord() { _hostingde_getZoneStatus _debug "Result of zoneStatus: '$zoneStatus'" done - curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" + curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}},\"templateValues\":${zoneConfigTemplateValues}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}" curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")" _debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'" _debug "Result of zoneUpdate: '$curResult'" From eda321954dfb31ecf18dbe41e0123dd329592cd1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Apr 2019 16:05:08 +0800 Subject: [PATCH 322/403] fix https://github.com/Neilpang/acme.sh/issues/2208 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index f47a5ebb..14b23de1 100755 --- a/acme.sh +++ b/acme.sh @@ -2004,7 +2004,7 @@ _read_conf() { _r_c_f="$1" _sdkey="$2" if [ -f "$_r_c_f" ]; then - _sdv="$(grep "^$_sdkey *=" "$_r_c_f" | cut -d = -f 2-1000 | tr -d "'")" + _sdv="$(eval "$(grep "^$_sdkey *=" "$_r_c_f")" ; eval "printf \"%s\" \"\$$_sdkey\"")" if _startswith "$_sdv" "${B64CONF_START}" && _endswith "$_sdv" "${B64CONF_END}"; then _sdv="$(echo "$_sdv" | sed "s/${B64CONF_START}//" | sed "s/${B64CONF_END}//" | _dbase64)" fi From c97e43dcd6b4d6447f69402d31522d4f02586813 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Apr 2019 16:45:58 +0800 Subject: [PATCH 323/403] fix format --- acme.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 14b23de1..7994cc22 100755 --- a/acme.sh +++ b/acme.sh @@ -2004,7 +2004,10 @@ _read_conf() { _r_c_f="$1" _sdkey="$2" if [ -f "$_r_c_f" ]; then - _sdv="$(eval "$(grep "^$_sdkey *=" "$_r_c_f")" ; eval "printf \"%s\" \"\$$_sdkey\"")" + _sdv="$( + eval "$(grep "^$_sdkey *=" "$_r_c_f")" + eval "printf \"%s\" \"\$$_sdkey\"" + )" if _startswith "$_sdv" "${B64CONF_START}" && _endswith "$_sdv" "${B64CONF_END}"; then _sdv="$(echo "$_sdv" | sed "s/${B64CONF_START}//" | sed "s/${B64CONF_END}//" | _dbase64)" fi From 0cfeee4ded53a34db628af4eefd4372e8a6e3d1b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Apr 2019 16:48:17 +0800 Subject: [PATCH 324/403] fix format --- acme.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 7994cc22..77f84a25 100755 --- a/acme.sh +++ b/acme.sh @@ -2004,9 +2004,9 @@ _read_conf() { _r_c_f="$1" _sdkey="$2" if [ -f "$_r_c_f" ]; then - _sdv="$( - eval "$(grep "^$_sdkey *=" "$_r_c_f")" - eval "printf \"%s\" \"\$$_sdkey\"" + _sdv="$( + eval "$(grep "^$_sdkey *=" "$_r_c_f")" + eval "printf \"%s\" \"\$$_sdkey\"" )" if _startswith "$_sdv" "${B64CONF_START}" && _endswith "$_sdv" "${B64CONF_END}"; then _sdv="$(echo "$_sdv" | sed "s/${B64CONF_START}//" | sed "s/${B64CONF_END}//" | _dbase64)" From 98d27c4a6a780ff333a99201166e899b017259ae Mon Sep 17 00:00:00 2001 From: Matthew R Chase Date: Sun, 7 Apr 2019 15:04:03 -0400 Subject: [PATCH 325/403] Fix most-specific zone match Most specific zone selected by deepest sub-domain (how many '.' in the domain) rather than seemingly irrelevant count of the number of characters within the zone. --- dnsapi/dns_gcloud.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index 99fbf410..87aceaad 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -134,12 +134,12 @@ _dns_gcloud_find_zone() { filter="$filter)" _debug filter "$filter" - # List domains and find the longest match (in case of some levels of delegation) + # List domains and find the zone with the deepest sub-domain (in case of some levels of delegation) if ! match=$(gcloud dns managed-zones list \ --format="value(name, dnsName)" \ --filter="$filter" \ | while read -r dnsName name; do - printf "%s\t%s\t%s\n" "${#dnsName}" "$dnsName" "$name" + printf "%s\t%s\t%s\n" "$(awk -F"." '{print NF-1}' <<< "$name")" "$dnsName" "$name" done \ | sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" From f23b0aacd79b249382f73029850e11b03107afb0 Mon Sep 17 00:00:00 2001 From: chasefox <49169974+chasefox@users.noreply.github.com> Date: Mon, 8 Apr 2019 07:11:08 -0400 Subject: [PATCH 326/403] Remove here string CI doesn't want here strings --- dnsapi/dns_gcloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index 87aceaad..9dfc5743 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -139,7 +139,7 @@ _dns_gcloud_find_zone() { --format="value(name, dnsName)" \ --filter="$filter" \ | while read -r dnsName name; do - printf "%s\t%s\t%s\n" "$(awk -F"." '{print NF-1}' <<< "$name")" "$dnsName" "$name" + printf "%s\t%s\t%s\n" "$(echo $name | awk -F"." '{print NF-1}')" "$dnsName" "$name" done \ | sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" From 2d72b25c4393716efabd4b241c729efa50794845 Mon Sep 17 00:00:00 2001 From: chasefox <49169974+chasefox@users.noreply.github.com> Date: Mon, 8 Apr 2019 07:44:41 -0400 Subject: [PATCH 327/403] CI wanted double-quote --- dnsapi/dns_gcloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index 9dfc5743..919223ab 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -139,7 +139,7 @@ _dns_gcloud_find_zone() { --format="value(name, dnsName)" \ --filter="$filter" \ | while read -r dnsName name; do - printf "%s\t%s\t%s\n" "$(echo $name | awk -F"." '{print NF-1}')" "$dnsName" "$name" + printf "%s\t%s\t%s\n" "$(echo "$name" | awk -F"." '{print NF-1}')" "$dnsName" "$name" done \ | sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" From 4aa488f48b68f5271ba90e6f04673f0f234d06d1 Mon Sep 17 00:00:00 2001 From: chasefox <49169974+chasefox@users.noreply.github.com> Date: Mon, 8 Apr 2019 07:51:39 -0400 Subject: [PATCH 328/403] Formatting - indentation I think this is what CI wants.... --- dnsapi/dns_gcloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index 919223ab..c2ead9a9 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -141,7 +141,7 @@ _dns_gcloud_find_zone() { | while read -r dnsName name; do printf "%s\t%s\t%s\n" "$(echo "$name" | awk -F"." '{print NF-1}')" "$dnsName" "$name" done \ - | sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then + | sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then _err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?" return 1 fi From 79e2f8a2e5ebd9d070903ca1e5294a1b24f03d06 Mon Sep 17 00:00:00 2001 From: dim0x69 Date: Wed, 17 Apr 2019 14:51:07 +0200 Subject: [PATCH 329/403] implement account update for acmev2 --- acme.sh | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 77f84a25..2fe9acb6 100755 --- a/acme.sh +++ b/acme.sh @@ -3218,11 +3218,6 @@ _on_issue_success() { } -updateaccount() { - _initpath - _regAccount -} - registeraccount() { _reg_length="$1" _initpath @@ -3320,6 +3315,61 @@ _regAccount() { _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" } +#implement updateaccount +updateaccount() { + _initpath + + if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then + _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" + mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH" + fi + + if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then + _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH" + mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH" + fi + + if [ ! -f "$ACCOUNT_KEY_PATH" ]; then + _err "Account key is not found at: $ACCOUNT_KEY_PATH" + return 1 + fi + + _accUri=$(_readcaconf "ACCOUNT_URL") + _debug _accUri "$_accUri" + + if [ -z "$_accUri" ]; then + _err "The account url is empty, please run '--update-account' first to update the account info first," + _err "Then try again." + return 1 + fi + + if ! _calcjwk "$ACCOUNT_KEY_PATH"; then + return 1 + fi + _initAPI + + if [ "$ACME_VERSION" = "2" ]; then + if [ "$ACCOUNT_EMAIL" ]; then + updjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"]}' + fi + else + # ACMEv1: Updates happen the same way a registration is done. + # https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-6.3 + _regAccount + return + fi + + # this part handles ACMEv2 account updates. + _send_signed_request "$_accUri" "$updjson" + + if [ "$code" = '200' ]; then + _info "account update success for $_accUri." + else + _info "Error. The account was not updated." + return 1 + fi +} + #Implement deactivate account deactivateaccount() { _initpath From 61556a54e2955d0ab913422489e0e9e90adcf885 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Fri, 19 Apr 2019 17:27:32 +0200 Subject: [PATCH 330/403] feat: add `--noprofile` option to `install` command --- acme.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 77f84a25..7adda616 100755 --- a/acme.sh +++ b/acme.sh @@ -5795,6 +5795,7 @@ Parameters: --ca-bundle Specifies the path to the CA certificate bundle to verify api server's certificate. --ca-path Specifies directory containing CA certificates in PEM format, used by wget or curl. --nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically. + --noprofile Only valid for '--install' command, which means: do not install aliases to user profile. --no-color Do not output color text. --force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails. --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR' @@ -5928,6 +5929,7 @@ _process() { _ca_bundle="" _ca_path="" _nocron="" + _noprofile="" _ecc="" _csr="" _pre_hook="" @@ -6272,6 +6274,9 @@ _process() { --nocron) _nocron="1" ;; + --noprofile) + _noprofile="1" + ;; --no-color) export ACME_NO_COLOR=1 ;; @@ -6430,7 +6435,7 @@ _process() { fi case "${_CMD}" in - install) install "$_nocron" "$_confighome" ;; + install) install "$_nocron" "$_confighome" "$_noprofile" ;; uninstall) uninstall "$_nocron" ;; upgrade) upgrade ;; issue) From 9c9fed749aef146c63afe5ff16e9b1115d13d0da Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 20 Apr 2019 12:49:51 +0800 Subject: [PATCH 331/403] fix https://github.com/Neilpang/acme.sh/issues/2225 make NSUPDATE_SERVER can be overwritten --- dnsapi/dns_nsupdate.sh | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_nsupdate.sh b/dnsapi/dns_nsupdate.sh index 8b479f98..dfb3672a 100755 --- a/dnsapi/dns_nsupdate.sh +++ b/dnsapi/dns_nsupdate.sh @@ -6,14 +6,22 @@ dns_nsupdate_add() { fulldomain=$1 txtvalue=$2 + NSUPDATE_SERVER="${NSUPDATE_SERVER:-$(_readaccountconf_mutable NSUPDATE_SERVER)}" + NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}" + NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}" + NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}" + _checkKeyFile || return 1 + + # save the dns server and key to the account conf file. + _saveaccountconf_mutable NSUPDATE_SERVER "${NSUPDATE_SERVER}" + _saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}" + _saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}" + _saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}" + [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" [ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53 - # save the dns server and key to the account conf file. - _saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}" - _saveaccountconf NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}" - _saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}" - _saveaccountconf NSUPDATE_ZONE "${NSUPDATE_ZONE}" + _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\"" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d" [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" @@ -42,6 +50,12 @@ EOF #Usage: dns_nsupdate_rm _acme-challenge.www.domain.com dns_nsupdate_rm() { fulldomain=$1 + + NSUPDATE_SERVER="${NSUPDATE_SERVER:-$(_readaccountconf_mutable NSUPDATE_SERVER)}" + NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}" + NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}" + NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}" + _checkKeyFile || return 1 [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" [ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53 From 4f1888d2ea755332b83f955a2dde1a52624bff40 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 21 Apr 2019 12:23:06 +0800 Subject: [PATCH 332/403] fix https://github.com/Neilpang/acme.sh/issues/2192 --- acme.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 77f84a25..ff6f8e4b 100755 --- a/acme.sh +++ b/acme.sh @@ -1312,13 +1312,19 @@ _create_account_key() { _initpath mkdir -p "$CA_DIR" - if [ -f "$ACCOUNT_KEY_PATH" ]; then + if [ -s "$ACCOUNT_KEY_PATH" ]; then _info "Account key exists, skip" - return + return 0 else #generate account key - _createkey "$length" "$ACCOUNT_KEY_PATH" - chmod 600 "$ACCOUNT_KEY_PATH" + if _createkey "$length" "$ACCOUNT_KEY_PATH"; then + chmod 600 "$ACCOUNT_KEY_PATH" + _info "Create account key ok." + return 0 + else + _err "Create account key error." + return 1 + fi fi } @@ -1341,11 +1347,14 @@ createDomainKey() { _initpath "$domain" "$_cdl" - if [ ! -f "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$IS_RENEW" ]) || [ "$Le_ForceNewDomainKey" = "1" ]; then + if [ ! -f "$CERT_KEY_PATH" ] || [ ! -s "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$IS_RENEW" ]) || [ "$Le_ForceNewDomainKey" = "1" ]; then if _createkey "$_cdl" "$CERT_KEY_PATH"; then _savedomainconf Le_Keylength "$_cdl" _info "The domain key is here: $(__green $CERT_KEY_PATH)" return 0 + else + _err "Can not domain key" + return 1 fi else if [ "$IS_RENEW" ]; then From e6df1828d9029f278a74798eac81f4a3c86a052d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 21 Apr 2019 12:37:26 +0800 Subject: [PATCH 333/403] fix https://github.com/Neilpang/acme.sh/issues/2192 --- acme.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index c004934f..9d5b6e54 100755 --- a/acme.sh +++ b/acme.sh @@ -1006,10 +1006,20 @@ _createkey() { if _isEccKey "$length"; then _debug "Using ec name: $eccname" - ${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null >"$f" + if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null)"; then + echo "$_opkey" >"$f" + else + _err "error ecc key name: $eccname" + return 1 + fi else _debug "Using RSA: $length" - ${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null >"$f" + if _opkey="$(${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null)"; then + echo "$_opkey" >"$f" + else + _err "error rsa key: $length" + return 1 + fi fi if [ "$?" != "0" ]; then From d1030eb0b2939a0748cd1208442bafdc1fd39a46 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Wed, 24 Apr 2019 14:03:54 +0200 Subject: [PATCH 334/403] Create DDNSS API based on the work of helbgd --- dnsapi/dns_ddnss.sh | 133 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 dnsapi/dns_ddnss.sh diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh new file mode 100644 index 00000000..f2e9947e --- /dev/null +++ b/dnsapi/dns_ddnss.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env sh + +#Created by RaidenII, to use DuckDNS's API to add/remove text records +#06/27/201 +#modified by helbgd @ 03/13/2018 to support ddnss.de +#modified by mod242 @ 04/24/2018 to support different ddnss domains +#Please note: the Wildcard Feature must be turned on for the Host record +#and the checkbox for TXT needs to be enabled + + +# Pass credentials before "acme.sh --issue --dns dns_ddnss ..." +# -- +# export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" +# -- +# + + +DDNSS_DNS_API="https://ddnss.de/upd.php" + +######## Public functions ##################### + +#Usage: dns_ddnss_add _acme-challenge.domain.ddnss.de "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ddnss_add() { + fulldomain=$1 + txtvalue=$2 + + DDNSS_Token="${DDNSS_Token:-$(_readaccountconf_mutable DDNSS_Token)}" + if [ -z "$DDNSS_Token" ]; then + _err "You must export variable: DDNSS_Token" + _err "The token for your DDNSS account is necessary." + _err "You can look it up in your DDNSS account." + return 1 + fi + + # Now save the credentials. + _saveaccountconf_mutable DDNSS_Token "$DDNSS_Token" + + # Unfortunately, DDNSS does not seems to support lookup domain through API + # So I assume your credentials (which are your domain and token) are correct + # If something goes wrong, we will get a KO response from DDNSS + + if ! _ddnss_get_domain; then + return 1 + fi + + # Now add the TXT record to DDNSS DNS + _info "Trying to add TXT record" + if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=1&txt=$txtvalue"; then + if [ "$response" = "Updated 1 hostname." ]; then + _info "TXT record has been successfully added to your DDNSS domain." + _info "Note that all subdomains under this domain uses the same TXT record." + return 0 + else + _err "Errors happened during adding the TXT record, response=$response" + return 1 + fi + else + _err "Errors happened during adding the TXT record." + return 1 + fi +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_ddnss_rm() { + fulldomain=$1 + txtvalue=$2 + + DDNSS_Token="${DDNSS_Token:-$(_readaccountconf_mutable DDNSS_Token)}" + if [ -z "$DDNSS_Token" ]; then + _err "You must export variable: DDNSS_Token" + _err "The token for your DDNSS account is necessary." + _err "You can look it up in your DDNSS account." + return 1 + fi + + if ! _ddnss_get_domain; then + return 1 + fi + + # Now remove the TXT record from DDNS DNS + _info "Trying to remove TXT record" + if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=1&txt=."; then + if [ "$response" = "Updated 1 hostname." ]; then + _info "TXT record has been successfully removed from your DDNSS domain." + return 0 + else + _err "Errors happened during removing the TXT record, response=$response" + return 1 + fi + else + _err "Errors happened during removing the TXT record." + return 1 + fi +} + +#################### Private functions below ################################## + +#fulldomain=_acme-challenge.domain.ddnss.de +#returns +# _ddnss_domain=domain +_ddnss_get_domain() { + + # We'll extract the domain/username from full domain + _ddnss_domain="$(echo "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.](ddnss|dyn-ip24|dyndns|dyn|dyndns1|home-webserver|myhome-server|dynip)\..*' | cut -d . -f 2-)" + + if [ -z "$_ddnss_domain" ]; then + _err "Error extracting the domain." + return 1 + fi + + return 0 +} + +#Usage: method URI +_ddnss_rest() { + method=$1 + param="$2" + _debug param "$param" + url="$DDNSS_DNS_API?$param" + _debug url "$url" + + # DDNSS uses GET to update domain info + if [ "$method" = "GET" ]; then + response="$(_get "$url" | sed -e :a -e 's/<[^>]*>//g;/ Date: Wed, 24 Apr 2019 14:57:48 +0200 Subject: [PATCH 335/403] Removed -e and changed tail to funktion --- dnsapi/dns_ddnss.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index f2e9947e..711936df 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -122,7 +122,7 @@ _ddnss_rest() { # DDNSS uses GET to update domain info if [ "$method" = "GET" ]; then - response="$(_get "$url" | sed -e :a -e 's/<[^>]*>//g;/]*>//g;/ Date: Wed, 24 Apr 2019 16:05:44 +0200 Subject: [PATCH 336/403] Update dns_ddnss.sh --- dnsapi/dns_ddnss.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index 711936df..f0cf04f2 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -122,7 +122,7 @@ _ddnss_rest() { # DDNSS uses GET to update domain info if [ "$method" = "GET" ]; then - response="$(_get "$url" | sed :a -e 's/<[^>]*>//g;/]*>//g;/ Date: Wed, 24 Apr 2019 16:15:01 +0200 Subject: [PATCH 337/403] Cleanup according to styleguide --- dnsapi/dns_ddnss.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index f0cf04f2..53665ad1 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -7,14 +7,12 @@ #Please note: the Wildcard Feature must be turned on for the Host record #and the checkbox for TXT needs to be enabled - # Pass credentials before "acme.sh --issue --dns dns_ddnss ..." # -- # export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" # -- # - DDNSS_DNS_API="https://ddnss.de/upd.php" ######## Public functions ##################### From 20af1ceb7d4cdadcc9fae50914159fff716bc7f9 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Wed, 24 Apr 2019 19:38:07 +0200 Subject: [PATCH 338/403] Cleanup comment --- dnsapi/dns_ddnss.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index 53665ad1..c38e6c7b 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -1,7 +1,6 @@ #!/usr/bin/env sh #Created by RaidenII, to use DuckDNS's API to add/remove text records -#06/27/201 #modified by helbgd @ 03/13/2018 to support ddnss.de #modified by mod242 @ 04/24/2018 to support different ddnss domains #Please note: the Wildcard Feature must be turned on for the Host record From 52f556412212040fbaeb8aa1cb6e5cb6a58a74c9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Apr 2019 20:58:13 +0800 Subject: [PATCH 339/403] fix image links --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 28859825..8d40d51a 100644 --- a/README.md +++ b/README.md @@ -45,25 +45,25 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) | NO | Status| Platform| |----|-------|---------| -|1|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu -|2|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/debian-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian -|3|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/centos-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS -|4|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/windows-cygwin.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included) -|5|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/freebsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD -|6|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/pfsense.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense -|7|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/opensuse-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE -|8|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/alpine-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl) -|9|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/base-archlinux.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux -|10|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/fedora-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora -|11|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/kalilinux-kali-linux-docker.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux -|12|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/oraclelinux-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux -|13|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/proxmox.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh +|1|[![](https://neilpang.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu +|2|[![](https://neilpang.github.io/acmetest/status/debian-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian +|3|[![](https://neilpang.github.io/acmetest/status/centos-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS +|4|[![](https://neilpang.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included) +|5|[![](https://neilpang.github.io/acmetest/status/freebsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD +|6|[![](https://neilpang.github.io/acmetest/status/pfsense.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense +|7|[![](https://neilpang.github.io/acmetest/status/opensuse-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE +|8|[![](https://neilpang.github.io/acmetest/status/alpine-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl) +|9|[![](https://neilpang.github.io/acmetest/status/base-archlinux.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux +|10|[![](https://neilpang.github.io/acmetest/status/fedora-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora +|11|[![](https://neilpang.github.io/acmetest/status/kalilinux-kali-linux-docker.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux +|12|[![](https://neilpang.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux +|13|[![](https://neilpang.github.io/acmetest/status/proxmox.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh |14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111 -|15|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/openbsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD -|16|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/mageia.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia +|15|[![](https://neilpang.github.io/acmetest/status/openbsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD +|16|[![](https://neilpang.github.io/acmetest/status/mageia.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia |17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) -|18|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris -|19|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux +|18|[![](https://neilpang.github.io/acmetest/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris +|19|[![](https://neilpang.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux |20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest): From bb703281a289531643cf47331a8fa829f81f5f3d Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Thu, 25 Apr 2019 16:18:52 +0200 Subject: [PATCH 340/403] Update dns_ddnss.sh --- dnsapi/dns_ddnss.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index c38e6c7b..dfe6dcb7 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -119,7 +119,7 @@ _ddnss_rest() { # DDNSS uses GET to update domain info if [ "$method" = "GET" ]; then - response="$(_get "$url" | sed :a -e 's/<[^>]*>//g;/]*>//g;/ Date: Fri, 26 Apr 2019 23:44:25 +0800 Subject: [PATCH 341/403] fix idn issues --- acme.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 9d5b6e54..5c9bf0c6 100755 --- a/acme.sh +++ b/acme.sh @@ -1084,11 +1084,12 @@ _createcsr() { printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf" if [ "$acmeValidationv1" ]; then + domainlist="$(_idn "$domainlist")" printf -- "\nsubjectAltName=DNS:$domainlist" >>"$csrconf" elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" - printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf" + printf -- "\nsubjectAltName=DNS:$(_idn $domain)" >>"$csrconf" else domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" @@ -3557,7 +3558,9 @@ _check_dns_entries() { for entry in $dns_entries; do d=$(_getfield "$entry" 1) txtdomain=$(_getfield "$entry" 2) + txtdomain=$(_idn $txtdomain) aliasDomain=$(_getfield "$entry" 3) + aliasDomain=$(_idn $aliasDomain) txt=$(_getfield "$entry" 5) d_api=$(_getfield "$entry" 6) _debug "d" "$d" @@ -3754,7 +3757,7 @@ issue() { if [ -z "$vlist" ]; then if [ "$ACME_VERSION" = "2" ]; then #make new order request - _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" + _identifiers="{\"type\":\"dns\",\"value\":\"$(_idn $_main_domain)\"}" _w_index=1 while true; do d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")" @@ -3851,7 +3854,7 @@ $_authorizations_map" fi if [ "$ACME_VERSION" = "2" ]; then - response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")" + response="$(echo "$_authorizations_map" | grep "^$(_idn $d)," | sed "s/$d,//")" _debug2 "response" "$response" if [ -z "$response" ]; then _err "get to authz error." From 47ff768b70aa8ab40ea7966b5a8fa90c4f2e6e2c Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Apr 2019 23:57:40 +0800 Subject: [PATCH 342/403] fix https://github.com/Neilpang/acme.sh/issues/2195 --- dnsapi/dns_cf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 96731435..78250842 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -58,7 +58,7 @@ dns_cf_add() { # 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 _contains "$response" "$fulldomain"; then + if _contains "$response" "$txtvalue"; then _info "Added, OK" return 0 elif _contains "$response" "The record already exists"; then From a7420ca3d4e0c73b315df3c746c78b10aaf7f74b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 27 Apr 2019 09:17:26 +0800 Subject: [PATCH 343/403] typo --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 6694aede..93ad83d6 100755 --- a/acme.sh +++ b/acme.sh @@ -1364,7 +1364,7 @@ createDomainKey() { _info "The domain key is here: $(__green $CERT_KEY_PATH)" return 0 else - _err "Can not domain key" + _err "Can not create domain key" return 1 fi else From 1b062ab929f4e3b62d72a61dbe77b17c0252d405 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Sun, 28 Apr 2019 15:58:08 +0200 Subject: [PATCH 344/403] Correct sed parsing error --- dnsapi/dns_ddnss.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ddnss.sh b/dnsapi/dns_ddnss.sh index dfe6dcb7..903b9619 100644 --- a/dnsapi/dns_ddnss.sh +++ b/dnsapi/dns_ddnss.sh @@ -119,7 +119,7 @@ _ddnss_rest() { # DDNSS uses GET to update domain info if [ "$method" = "GET" ]; then - response="$(_get "$url" | sed 's/<[^>]*>//g;/]*>//g' | _tail_n 1)" else _err "Unsupported method" return 1 From 5b1b5cc8f2c76c31859cf2047c230730707689d3 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Mon, 29 Apr 2019 10:43:16 +0200 Subject: [PATCH 345/403] Create dns_schlundtech.sh --- dnsapi/dns_schlundtech.sh | 261 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 dnsapi/dns_schlundtech.sh diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh new file mode 100644 index 00000000..202b3469 --- /dev/null +++ b/dnsapi/dns_schlundtech.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env sh +# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- + +# Schlundtech DNS API +# Author: mod242 +# Created: 2019-40-29 +# Completly based on the autoDNS xml api wrapper by auerswald@gmail.com +# +# export SCHLUNDTECH_USER="username" +# export SCHLUNDTECH_PASSWORD="password" +# +# Usage: +# acme.sh --issue --dns dns_autodns -d example.com + +AUTODNS_API="https://gateway.schlundtech.de" + +# Arguments: +# txtdomain +# txt +dns_schlundtech_add() { + fulldomain="$1" + txtvalue="$2" + + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" + + if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then + _err "You didn't specify schlundtech user and password." + return 1 + fi + + _saveaccountconf_mutable SCHLUNDTECH_USER "$SCHLUNDTECH_USER" + _saveaccountconf_mutable SCHLUNDTECH_PASSWORD "$SCHLUNDTECH_PASSWORD" + + _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_schlundtech_rm() { + fulldomain="$1" + txtvalue="$2" + + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" + + if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then + _err "You didn't specify schlundtech user and password." + 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 + 10 + " "$SCHLUNDTECH_USER" "$SCHLUNDTECH_PASSWORD" +} + +# 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 345d6c5687e48dae07494565d735a367f6faa7af Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Mon, 29 Apr 2019 10:44:23 +0200 Subject: [PATCH 346/403] Update dns_schlundtech.sh --- dnsapi/dns_schlundtech.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh index 202b3469..c5e7d630 100644 --- a/dnsapi/dns_schlundtech.sh +++ b/dnsapi/dns_schlundtech.sh @@ -21,8 +21,8 @@ dns_schlundtech_add() { fulldomain="$1" txtvalue="$2" - SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" - SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then _err "You didn't specify schlundtech user and password." From 9b68a3ef4acb7112119d01182965394cd653c761 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Mon, 29 Apr 2019 12:13:40 +0200 Subject: [PATCH 347/403] Update dns_schlundtech.sh --- dnsapi/dns_schlundtech.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh index c5e7d630..efb021ae 100644 --- a/dnsapi/dns_schlundtech.sh +++ b/dnsapi/dns_schlundtech.sh @@ -62,8 +62,8 @@ dns_schlundtech_rm() { fulldomain="$1" txtvalue="$2" - SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" - SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then _err "You didn't specify schlundtech user and password." From 175b56b43c05ed0ed2ec432e3e1ead2f12f78414 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Mon, 29 Apr 2019 12:18:05 +0200 Subject: [PATCH 348/403] Update dns_schlundtech.sh --- dnsapi/dns_schlundtech.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh index efb021ae..12408633 100644 --- a/dnsapi/dns_schlundtech.sh +++ b/dnsapi/dns_schlundtech.sh @@ -10,9 +10,9 @@ # export SCHLUNDTECH_PASSWORD="password" # # Usage: -# acme.sh --issue --dns dns_autodns -d example.com +# acme.sh --issue --dns dns_schlundtech -d example.com -AUTODNS_API="https://gateway.schlundtech.de" +SCHLUNDTECH_API="https://gateway.schlundtech.de" # Arguments: # txtdomain @@ -241,7 +241,7 @@ _autodns_api_call() { _debug request_data "$request_data" - autodns_response="$(_post "$request_data" "$AUTODNS_API")" + autodns_response="$(_post "$request_data" "$SCHLUNDTECH_API")" ret="$?" _debug autodns_response "$autodns_response" From d10f40f109d417ba9810eefd482662ba44fec208 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 29 Apr 2019 21:44:25 +0800 Subject: [PATCH 349/403] fix idn issue. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 93ad83d6..0ce42ff5 100755 --- a/acme.sh +++ b/acme.sh @@ -1032,7 +1032,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" ] } From a89d50d34ee8d20ca7365f6aa6d1e6465f2f626c Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 29 Apr 2019 21:52:22 +0800 Subject: [PATCH 350/403] use mutable --- dnsapi/dns_cx.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index d07d8e0c..c287d507 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -16,6 +16,8 @@ dns_cx_add() { fulldomain=$1 txtvalue=$2 + CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}" + CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}" if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then CX_Key="" CX_Secret="" @@ -27,8 +29,8 @@ dns_cx_add() { REST_API="$CX_Api" #save the api key and email to the account conf file. - _saveaccountconf CX_Key "$CX_Key" - _saveaccountconf CX_Secret "$CX_Secret" + _saveaccountconf_mutable CX_Key "$CX_Key" + _saveaccountconf_mutable CX_Secret "$CX_Secret" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -43,6 +45,8 @@ dns_cx_add() { dns_cx_rm() { fulldomain=$1 txtvalue=$2 + CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}" + CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}" REST_API="$CX_Api" if _get_root "$fulldomain"; then record_id="" From b7a04430913063b8801fba50a8647ae51aefabc3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 29 Apr 2019 22:11:25 +0800 Subject: [PATCH 351/403] lets start 2.8.2 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 0ce42ff5..153b953f 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.1 +VER=2.8.2 PROJECT_NAME="acme.sh" From b50e701caefed9fdde1bd2c388e7f3a0011ebb54 Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 29 Apr 2019 22:13:54 +0800 Subject: [PATCH 352/403] Add notification (#2241) * add cron notify * fix format * fix format --- acme.sh | 270 ++++++++++++++++++++++++++++++++++++++++++--- notify/mail.sh | 15 +++ notify/mailgun.sh | 123 +++++++++++++++++++++ notify/pop.sh | 15 +++ notify/sendgrid.sh | 56 ++++++++++ notify/smtp.sh | 15 +++ 6 files changed, 481 insertions(+), 13 deletions(-) create mode 100644 notify/mail.sh create mode 100644 notify/mailgun.sh create mode 100644 notify/pop.sh create mode 100644 notify/sendgrid.sh create mode 100644 notify/smtp.sh diff --git a/acme.sh b/acme.sh index 153b953f..dc110a0f 100755 --- a/acme.sh +++ b/acme.sh @@ -14,7 +14,11 @@ _WINDOWS_SCHEDULER_NAME="$PROJECT_NAME.cron" _SCRIPT_="$0" -_SUB_FOLDERS="dnsapi deploy" +_SUB_FOLDER_NOTIFY="notify" +_SUB_FOLDER_DNSAPI="dnsapi" +_SUB_FOLDER_DEPLOY="deploy" + +_SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY" LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" @@ -107,6 +111,18 @@ SYSLOG_LEVEL_DEFAULT=$SYSLOG_LEVEL_ERROR #none SYSLOG_LEVEL_NONE=0 +NOTIFY_LEVEL_DISABLE=0 +NOTIFY_LEVEL_ERROR=1 +NOTIFY_LEVEL_RENEW=2 +NOTIFY_LEVEL_SKIP=3 + +NOTIFY_LEVEL_DEFAULT=$NOTIFY_LEVEL_RENEW + +NOTIFY_MODE_BULK=0 +NOTIFY_MODE_CERT=1 + +NOTIFY_MODE_DEFAULT=$NOTIFY_MODE_BULK + _DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh" _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations" @@ -117,6 +133,8 @@ _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" +_NOTIFY_WIKI="https://github.com/Neilpang/acme.sh/wiki/notify" + _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" @@ -784,6 +802,13 @@ _url_encode() { done } +_json_encode() { + _j_str="$(sed 's/"/\\"/g' | sed "s/\r/\\r/g")" + _debug3 "_json_encode" + _debug3 "_j_str" "$_j_str" + echo "$_j_str" | _hex_dump | _lower_case | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n" +} + #options file _sed_i() { options="$1" @@ -3168,6 +3193,14 @@ _on_issue_err() { _err "See: $_DEBUG_WIKI" fi + if [ "$IN_CRON" ]; then + if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_ERROR ]; then + if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then + _send_notify "Renew $_main_domain error" "There is an error." "$NOTIFY_HOOK" 1 + fi + fi + fi + #run the post hook if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" @@ -3210,6 +3243,13 @@ _on_issue_success() { _chk_post_hook="$1" _chk_renew_hook="$2" _debug _on_issue_success + if [ "$IN_CRON" ]; then + if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_RENEW ]; then + if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then + _send_notify "Renew $_main_domain success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0 + fi + fi + fi #run the post hook if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" @@ -3467,9 +3507,9 @@ _findHook() { d_api="$_SCRIPT_HOME/$_hookcat/$_hookname" elif [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname.sh" ]; then d_api="$_SCRIPT_HOME/$_hookcat/$_hookname.sh" - elif [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ]; then + elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ]; then d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname" - elif [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ]; then + elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ]; then d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" elif [ -f "$LE_WORKING_DIR/$_hookname" ]; then d_api="$LE_WORKING_DIR/$_hookname" @@ -4017,7 +4057,7 @@ $_authorizations_map" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" - d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" + d_api="$(_findHook "$_dns_root_d" $_SUB_FOLDER_DNSAPI "$_currentRoot")" _debug d_api "$d_api" dns_entry="$dns_entry$dvsep$txt${dvsep}$d_api" @@ -4622,6 +4662,15 @@ renew() { if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then _info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")" _info "Add '$(__red '--force')' to force to renew." + + if [ "$IN_CRON" = "1" ]; then + if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_SKIP ]; then + if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then + _send_notify "Renew $Le_Domain skipped" "Good, the cert next renewal time is $Le_NextRenewTimeStr." "$NOTIFY_HOOK" "$RENEW_SKIP" + fi + fi + fi + return "$RENEW_SKIP" fi @@ -4657,7 +4706,9 @@ renewAll() { _stopRenewOnError="$1" _debug "_stopRenewOnError" "$_stopRenewOnError" _ret="0" - + _success_msg="" + _error_msg="" + _skipped_msg="" for di in "${CERT_HOME}"/*.*/; do _debug di "$di" if ! [ -d "$di" ]; then @@ -4678,15 +4729,49 @@ renewAll() { if [ "$rc" != "0" ]; then if [ "$rc" = "$RENEW_SKIP" ]; then _info "Skipped $d" - elif [ "$_stopRenewOnError" ]; then - _err "Error renew $d, stop now." - return "$rc" + _skipped_msg="${_skipped_msg} $d +" else - _ret="$rc" - _err "Error renew $d." + _error_msg="${_error_msg} $d +" + if [ "$_stopRenewOnError" ]; then + _err "Error renew $d, stop now." + _ret="$rc" + break + else + _ret="$rc" + _err "Error renew $d." + fi fi + else + _success_msg="${_success_msg} $d +" fi done + + if [ "$IN_CRON" = "1" ]; then + if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then + _msg_subject="Renew" + if [ "$_error_msg" ]; then + _msg_subject="${_msg_subject} Error" + fi + if [ "$_success_msg" ]; then + _msg_subject="${_msg_subject} Success" + fi + if [ "$_skipped_msg" ]; then + _msg_subject="${_msg_subject} Skipped" + fi + _msg_data="Error certs: +${_error_msg} +Success certs: +${_success_msg} +Skipped certs: +$_skipped_msg +" + _send_notify "$_msg_subject" "$_msg_data" "$NOTIFY_HOOK" 0 + fi + fi + return "$_ret" } @@ -4835,7 +4920,7 @@ _deploy() { _hooks="$2" for _d_api in $(echo "$_hooks" | tr ',' " "); do - _deployApi="$(_findHook "$_d" deploy "$_d_api")" + _deployApi="$(_findHook "$_d" $_SUB_FOLDER_DEPLOY "$_d_api")" if [ -z "$_deployApi" ]; then _err "The deploy hook $_d_api is not found." return 1 @@ -5785,6 +5870,113 @@ version() { echo "v$VER" } +# subject content hooks code +_send_notify() { + _nsubject="$1" + _ncontent="$2" + _nhooks="$3" + _nerror="$4" + + if [ "$NOTIFY_LEVEL" = "$NOTIFY_LEVEL_DISABLE" ]; then + _debug "The NOTIFY_LEVEL is $NOTIFY_LEVEL, disabled, just return." + return 0 + fi + + if [ -z "$_nhooks" ]; then + _debug "The NOTIFY_HOOK is empty, just return." + return 0 + fi + + _send_err=0 + for _n_hook in $(echo "$_nhooks" | tr ',' " "); do + _n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")" + _info "Found $_n_hook_file" + + if ! ( + if ! . "$_n_hook_file"; then + _err "Load file $_n_hook_file error. Please check your api file and try again." + return 1 + fi + + d_command="${_n_hook}_send" + if ! _exists "$d_command"; then + _err "It seems that your api file is not correct, it must have a function named: $d_command" + return 1 + fi + + if ! $d_command "$_nsubject" "$_ncontent" "$_nerror"; then + _err "Error send message by $d_command" + return 1 + fi + + return 0 + ); then + _err "Set $_n_hook_file error." + _send_err=1 + else + _info "$_n_hook $(__green Success)" + fi + done + return $_send_err + +} + +# hook +_set_notify_hook() { + _nhooks="$1" + + _test_subject="Hello, this is notification from $PROJECT_NAME" + _test_content="If you receive this email, your notification works." + + _send_notify "$_test_subject" "$_test_content" "$_nhooks" 0 + +} + +#[hook] [level] [mode] +setnotify() { + _nhook="$1" + _nlevel="$2" + _nmode="$3" + + _initpath + + if [ -z "$_nhook$_nlevel$_nmode" ]; then + _usage "Usage: $PROJECT_ENTRY --set-notify [--notify-hook mailgun] [--notify-level $NOTIFY_LEVEL_DEFAULT] [--notify-mode $NOTIFY_MODE_DEFAULT]" + _usage "$_NOTIFY_WIKI" + return 1 + fi + + if [ "$_nlevel" ]; then + _info "Set notify level to: $_nlevel" + export "NOTIFY_LEVEL=$_nlevel" + _saveaccountconf "NOTIFY_LEVEL" "$NOTIFY_LEVEL" + fi + + if [ "$_nmode" ]; then + _info "Set notify mode to: $_nmode" + export "NOTIFY_MODE=$_nmode" + _saveaccountconf "NOTIFY_MODE" "$NOTIFY_MODE" + fi + + if [ "$_nhook" ]; then + _info "Set notify hook to: $_nhook" + if [ "$_nhook" = "$NO_VALUE" ]; then + _info "Clear notify hook" + _clearaccountconf "NOTIFY_HOOK" + else + if _set_notify_hook "$_nhook"; then + export NOTIFY_HOOK="$_nhook" + _saveaccountconf "NOTIFY_HOOK" "$NOTIFY_HOOK" + return 0 + else + _err "Can not set notify hook to: $_nhook" + return 1 + fi + fi + fi + +} + showhelp() { _initpath version @@ -5817,6 +6009,8 @@ Commands: --create-domain-key Create an domain private key, professional use. --createCSR, -ccsr Create CSR , professional use. --deactivate Deactivate the domain authz, professional use. + --set-notify Set the cron notification hook, level or mode. + Parameters: --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc. @@ -5885,7 +6079,18 @@ Parameters: --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 --branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to. - " + + --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT. + 0: disabled, no notification will be sent. + 1: send notification only when there is an error. No news is good news. + 2: send notification when a cert is successfully renewed, or there is an error + 3: send notification when a cert is skipped, renewdd, or error + --notify-mode 0|1 Set notification mode. Default value is $NOTIFY_MODE_DEFAULT. + 0: Bulk mode. Send all the domain's notifications in one message(mail) + 1: Cert mode. Send a message for every single cert. + --notify-hook [hookname] Set the notify hook + +" } # nocron noprofile @@ -6019,6 +6224,9 @@ _process() { _syslog="" _use_wget="" _server="" + _notify_hook="" + _notify_level="" + _notify_mode="" while [ ${#} -gt 0 ]; do case "${1}" in @@ -6105,6 +6313,9 @@ _process() { --deactivate-account) _CMD="deactivateaccount" ;; + --set-notify) + _CMD="setnotify" + ;; --domain | -d) _dvalue="$2" @@ -6453,6 +6664,37 @@ _process() { export BRANCH="$2" shift ;; + --notify-hook) + _nhook="$2" + if _startswith "$_nhook" "-"; then + _err "'$_nhook' is not a hook name for '$1'" + return 1 + fi + if [ "$_notify_hook" ]; then + _notify_hook="$_notify_hook,$_nhook" + else + _notify_hook="$_nhook" + fi + shift + ;; + --notify-level) + _nlevel="$2" + if _startswith "$_nlevel" "-"; then + _err "'$_nlevel' is not a integer for '$1'" + return 1 + fi + _notify_level="$_nlevel" + shift + ;; + --notify-mode) + _nmode="$2" + if _startswith "$_nmode" "-"; then + _err "'$_nmode' is not a integer for '$1'" + return 1 + fi + _notify_mode="$_nmode" + shift + ;; *) _err "Unknown parameter : $1" return 1 @@ -6570,7 +6812,9 @@ _process() { createCSR) createCSR "$_domain" "$_altdomains" "$_ecc" ;; - + setnotify) + setnotify "$_notify_hook" "$_notify_level" "$_notify_mode" + ;; *) if [ "$_CMD" ]; then _err "Invalid command: $_CMD" diff --git a/notify/mail.sh b/notify/mail.sh new file mode 100644 index 00000000..3dfef0be --- /dev/null +++ b/notify/mail.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +# support local mail app + +mail_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + _err "Not implemented yet." + return 1 +} diff --git a/notify/mailgun.sh b/notify/mailgun.sh new file mode 100644 index 00000000..1689a0b9 --- /dev/null +++ b/notify/mailgun.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env sh + +#Support mailgun.com api + +#MAILGUN_API_KEY="xxxx" +#MAILGUN_TO="yyyy@gmail.com" + +#MAILGUN_REGION="us|eu" #optional, use "us" as default +#MAILGUN_API_DOMAIN="xxxxxx.com" #optional, use the default sandbox domain +#MAILGUN_FROM="xxx@xxxxx.com" #optional, use the default sendbox account + +_MAILGUN_BASE="https://api.mailgun.net/v3" + +# subject content statusCode +mailgun_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + MAILGUN_API_KEY="${MAILGUN_API_KEY:-$(_readaccountconf_mutable MAILGUN_API_KEY)}" + if [ -z "$MAILGUN_API_KEY" ]; then + MAILGUN_API_KEY="" + _err "You didn't specify a mailgun api key MAILGUN_API_KEY yet ." + _err "You can get yours from here https://mailgun.com" + return 1 + fi + _saveaccountconf_mutable MAILGUN_API_KEY "$MAILGUN_API_KEY" + + MAILGUN_REGION="${MAILGUN_REGION:-$(_readaccountconf_mutable MAILGUN_REGION)}" + if [ -z "$MAILGUN_REGION" ]; then + MAILGUN_REGION="" + _info "The MAILGUN_REGION is not set, so use the default us region." + _MAILGUN_BASE="https://api.mailgun.net/v3" + else + _saveaccountconf_mutable MAILGUN_REGION "$MAILGUN_REGION" + _MAILGUN_BASE="https://api.eu.mailgun.net/v3" + fi + + MAILGUN_TO="${MAILGUN_TO:-$(_readaccountconf_mutable MAILGUN_TO)}" + if [ -z "$MAILGUN_TO" ]; then + MAILGUN_TO="" + _err "You didn't specify an email to MAILGUN_TO receive messages." + return 1 + fi + _saveaccountconf_mutable MAILGUN_TO "$MAILGUN_TO" + + MAILGUN_API_DOMAIN="${MAILGUN_API_DOMAIN:-$(_readaccountconf_mutable MAILGUN_API_DOMAIN)}" + if [ -z "$MAILGUN_API_DOMAIN" ]; then + _info "The MAILGUN_API_DOMAIN is not set, try to get the default sending sandbox domain for you." + if ! _mailgun_rest GET "/domains"; then + _err "Can not get sandbox domain." + return 1 + fi + _sendboxDomain="$(echo "$response" | _egrep_o '"name": *"sandbox.*.mailgun.org"' | cut -d : -f 2 | tr -d '" ')" + _debug _sendboxDomain "$_sendboxDomain" + MAILGUN_API_DOMAIN="$_sendboxDomain" + if [ -z "$MAILGUN_API_DOMAIN" ]; then + _err "Can not get sandbox domain for MAILGUN_API_DOMAIN" + return 1 + fi + + _info "$(__green "When using sandbox domain, you must verify your email first.")" + #todo: add recepient + fi + if [ -z "$MAILGUN_API_DOMAIN" ]; then + _err "Can not get MAILGUN_API_DOMAIN" + return 1 + fi + _saveaccountconf_mutable MAILGUN_API_DOMAIN "$MAILGUN_API_DOMAIN" + + MAILGUN_FROM="${MAILGUN_FROM:-$(_readaccountconf_mutable MAILGUN_FROM)}" + if [ -z "$MAILGUN_FROM" ]; then + MAILGUN_FROM="$PROJECT_NAME@$MAILGUN_API_DOMAIN" + _info "The MAILGUN_FROM is not set, so use the default value: $MAILGUN_FROM" + else + _debug MAILGUN_FROM "$MAILGUN_FROM" + _saveaccountconf_mutable MAILGUN_FROM "$MAILGUN_FROM" + fi + + #send from url + _msg="/$MAILGUN_API_DOMAIN/messages?from=$(printf "%s" "$MAILGUN_FROM" | _url_encode)&to=$(printf "%s" "$MAILGUN_TO" | _url_encode)&subject=$(printf "%s" "$_subject" | _url_encode)&text=$(printf "%s" "$_content" | _url_encode)" + _debug "_msg" "$_msg" + _mailgun_rest POST "$_msg" + if _contains "$response" "Queued. Thank you."; then + _info "mailgun send success." + return 0 + else + _err "mailgun send error" + _err "$response" + return 1 + fi + +} + +# method uri data +_mailgun_rest() { + _method="$1" + _mguri="$2" + _mgdata="$3" + _debug _mguri "$_mguri" + _mgurl="$_MAILGUN_BASE$_mguri" + _debug _mgurl "$_mgurl" + + _auth="$(printf "%s" "api:$MAILGUN_API_KEY" | _base64)" + export _H1="Authorization: Basic $_auth" + export _H2="Content-Type: application/json" + + if [ "$_method" = "GET" ]; then + response="$(_get "$_mgurl")" + else + _debug _mgdata "$_mgdata" + response="$(_post "$_mgdata" "$_mgurl" "" "$_method")" + fi + if [ "$?" != "0" ]; then + _err "Error: $_mguri" + _err "$response" + return 1 + fi + _debug2 response "$response" + return 0 + +} diff --git a/notify/pop.sh b/notify/pop.sh new file mode 100644 index 00000000..f118d79b --- /dev/null +++ b/notify/pop.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +# support pop + +pop_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + _err "Not implemented yet." + return 1 +} diff --git a/notify/sendgrid.sh b/notify/sendgrid.sh new file mode 100644 index 00000000..5c5bfdba --- /dev/null +++ b/notify/sendgrid.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env sh + +#Support SENDGRID.com api + +#SENDGRID_API_KEY="" +#SENDGRID_TO="xxxx@xxx.com" +#SENDGRID_FROM="xxxx@cccc.com" + +sendgrid_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + SENDGRID_API_KEY="${SENDGRID_API_KEY:-$(_readaccountconf_mutable SENDGRID_API_KEY)}" + if [ -z "$SENDGRID_API_KEY" ]; then + SENDGRID_API_KEY="" + _err "You didn't specify a sendgrid api key SENDGRID_API_KEY yet ." + _err "You can get yours from here https://sendgrid.com" + return 1 + fi + _saveaccountconf_mutable SENDGRID_API_KEY "$SENDGRID_API_KEY" + + SENDGRID_TO="${SENDGRID_TO:-$(_readaccountconf_mutable SENDGRID_TO)}" + if [ -z "$SENDGRID_TO" ]; then + SENDGRID_TO="" + _err "You didn't specify an email to SENDGRID_TO receive messages." + return 1 + fi + _saveaccountconf_mutable SENDGRID_TO "$SENDGRID_TO" + + SENDGRID_FROM="${SENDGRID_FROM:-$(_readaccountconf_mutable SENDGRID_FROM)}" + if [ -z "$SENDGRID_FROM" ]; then + SENDGRID_FROM="" + _err "You didn't specify an email to SENDGRID_FROM receive messages." + return 1 + fi + _saveaccountconf_mutable SENDGRID_FROM "$SENDGRID_FROM" + + export _H1="Authorization: Bearer $SENDGRID_API_KEY" + export _H2="Content-Type: application/json" + + _content="$(echo "$_content" | _json_encode)" + _data="{\"personalizations\": [{\"to\": [{\"email\": \"$SENDGRID_TO\"}]}],\"from\": {\"email\": \"$SENDGRID_FROM\"},\"subject\": \"$_subject\",\"content\": [{\"type\": \"text/plain\", \"value\": \"$_content\"}]}" + response="" #just make shellcheck happy + if _post "$_data" "https://api.sendgrid.com/v3/mail/send"; then + if [ -z "$response" ]; then + _info "sendgrid send sccess." + return 0 + fi + fi + _err "sendgrid send error." + _err "$response" + return 1 + +} diff --git a/notify/smtp.sh b/notify/smtp.sh new file mode 100644 index 00000000..6aa37ca3 --- /dev/null +++ b/notify/smtp.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +# support smtp + +smtp_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + _err "Not implemented yet." + return 1 +} From d7be2c5b8a4eb49da47faea5ded8da66d30f98b6 Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Mon, 29 Apr 2019 16:17:24 +0200 Subject: [PATCH 353/403] Remove from Master Branch --- dnsapi/dns_schlundtech.sh | 261 -------------------------------------- 1 file changed, 261 deletions(-) delete mode 100644 dnsapi/dns_schlundtech.sh diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh deleted file mode 100644 index 12408633..00000000 --- a/dnsapi/dns_schlundtech.sh +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/env sh -# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- - -# Schlundtech DNS API -# Author: mod242 -# Created: 2019-40-29 -# Completly based on the autoDNS xml api wrapper by auerswald@gmail.com -# -# export SCHLUNDTECH_USER="username" -# export SCHLUNDTECH_PASSWORD="password" -# -# Usage: -# acme.sh --issue --dns dns_schlundtech -d example.com - -SCHLUNDTECH_API="https://gateway.schlundtech.de" - -# Arguments: -# txtdomain -# txt -dns_schlundtech_add() { - fulldomain="$1" - txtvalue="$2" - - SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" - SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" - - if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then - _err "You didn't specify schlundtech user and password." - return 1 - fi - - _saveaccountconf_mutable SCHLUNDTECH_USER "$SCHLUNDTECH_USER" - _saveaccountconf_mutable SCHLUNDTECH_PASSWORD "$SCHLUNDTECH_PASSWORD" - - _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_schlundtech_rm() { - fulldomain="$1" - txtvalue="$2" - - SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" - SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" - - if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then - _err "You didn't specify schlundtech user and password." - 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 - 10 - " "$SCHLUNDTECH_USER" "$SCHLUNDTECH_PASSWORD" -} - -# 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" "$SCHLUNDTECH_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 37ef0a0cb62cc70c0b4ef6158d21946576e56fac Mon Sep 17 00:00:00 2001 From: andrewheberle Date: Tue, 30 Apr 2019 15:32:36 +0800 Subject: [PATCH 354/403] Fix README.md confict --- deploy/README.md | 294 +---------------------------------------------- 1 file changed, 2 insertions(+), 292 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 8cefeffa..fc633ad7 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,296 +1,6 @@ # Using deploy api -Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). +deploy hook usage: -Here are the scripts to deploy the certs/key to the server/services. +https://github.com/Neilpang/acme.sh/wiki/deployhooks -## 1. Deploy the certs to your cpanel host - -If you want to deploy using cpanel UAPI see 7. - -(cpanel deploy hook is not finished yet, this is just an example.) - - - -Then you can deploy now: - -```sh -export DEPLOY_CPANEL_USER=myusername -export DEPLOY_CPANEL_PASSWORD=PASSWORD -acme.sh --deploy -d example.com --deploy-hook cpanel -``` - -## 2. Deploy ssl cert on kong proxy engine based on api - -Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). -Currently supports Kong-v0.10.x. - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook kong -``` - -## 3. Deploy the cert to remote server through SSH access - -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... - -```sh -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... -``` -export DEPLOY_SSH_USER=username -``` -Optional... -``` -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 -``` - -**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). - -**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). - -**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. - -**DEPLOY_SSH_KEYFILE** -Target filename for the private key issued by LetsEncrypt. - -**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. - -**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. - -**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. - -**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. - -**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 -user -```sh -~/.acme_ssh_deploy/[domain name]-backup-[timestamp] -``` -Any backups older than 180 days will be deleted when new certificates -are deployed. This defaults to "yes" set to "no" to disable backup. - -###Examples using SSH deploy -The following example illustrates deploying certificates to a QNAP NAS -(tested with QTS version 4.2.3) - -```sh -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 -``` -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 illustrates deploying certificates to a Unifi -Controller (tested with version 5.4.11). - -```sh -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 \ - -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 unifi.example.com --deploy-hook ssh -``` -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 finally -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 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 \ - /var/lib/unifi/unifi.example.com.p12 \ -&& service unifi restart -``` - -## 4. Deploy the cert to local vsftpd server - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd -``` - -The default vsftpd conf file is `/etc/vsftpd.conf`, if your vsftpd conf is not in the default location, you can specify one: - -```sh -export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf" - -acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd -``` - -The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one: - -```sh -export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" - -acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd -``` - -## 5. Deploy the cert to local exim4 server - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook exim4 -``` - -The default exim4 conf file is `/etc/exim/exim.conf`, if your exim4 conf is not in the default location, you can specify one: - -```sh -export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template" - -acme.sh --deploy -d ftp.example.com --deploy-hook exim4 -``` - -The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one: - -```sh -export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart" - -acme.sh --deploy -d ftp.example.com --deploy-hook exim4 -``` - -## 6. Deploy the cert to OSX Keychain - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook keychain -``` - -## 7. Deploy to cpanel host using UAPI - -This hook is using UAPI and works in cPanel & WHM version 56 or newer. -``` -acme.sh --deploy -d example.com --deploy-hook cpanel_uapi -``` -DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username. -```sh -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 separate certificate for each domain. - -## 8. Deploy the cert to your FRITZ!Box router - -You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables: -```sh -$ export DEPLOY_FRITZBOX_USERNAME=my_username -$ export DEPLOY_FRITZBOX_PASSWORD=the_password -$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com -``` - -After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this: - -```sh -acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox -``` - -## 9. Deploy the cert to strongswan - -```sh -acme.sh --deploy -d ftp.example.com --deploy-hook strongswan -``` - -## 10. Deploy the cert to HAProxy - -You may specify the directory where you want the concatenated key and certificate chain written. The value shown below will be used as the default if you don't set this environment variable. - -```sh -export DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy -``` - -You may optionally specify the file name where you want the concatenated key and certificate chain written. The value shown below will be used as the default if you don't set this environment variable. - -```sh -export DEPLOY_HAPROXY_PEM_NAME=$domain -``` - -You may optionally define the command to reload HAProxy. The value shown below will be used as the default if you don't set this environment variable. - -```sh -export DEPLOY_HAPROXY_RELOAD="true" -``` - -You may optionally specify that the issuer certificate is transferred to "${DEPLOY_HAPROXY_PEM}.issuer". This is a requirement to support OCSP stapling in HAProxy. The value shown below will be used as the default if you don't set this environment variable. - -```sh -export DEPLOY_HAPROXY_ISSUER="no" -``` - -You may optionally specify that you wish to support HAProxy's multi-cert bundle functionality. This allows serving of both RSA and ECC certificates on the same proxy. This adds a ".rsa" or ".ecc" suffix to the files generated (.pem, .ocsp and .issuer). The value shown below will be used as the default if you don't set this environment variable. - -```sh -export DEPLOY_HAPROXY_BUNDLE="no" -``` - -You can then deploy the certificate as follows -```sh -acme.sh --deploy -d haproxy.example.com --deploy-hook haproxy -``` - -The path for the PEM file will be stored with the domain configuration and will be available when renewing, so that deploy will happen automatically when renewed. From 388ff75260ea86e6f24f4326b5a0ba5e8e003d93 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 30 Apr 2019 20:43:10 +0800 Subject: [PATCH 355/403] --- Auto-Git Commit --- --- .gitignore | 1 + test.sh | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 .gitignore create mode 100644 test.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..427ec6be --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.autogit \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100644 index 00000000..d976bfe4 --- /dev/null +++ b/test.sh @@ -0,0 +1,8 @@ + + +_data='aaaaa +bbb"bb +ccccc +ddddd +' + From 522b7c51f74298a11beea2e20a9f8e69b31c76fe Mon Sep 17 00:00:00 2001 From: Jakub Filo Date: Wed, 1 May 2019 01:53:51 +0200 Subject: [PATCH 356/403] Adding NLnetLabs NSD API --- dnsapi/dns_nsd.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 dnsapi/dns_nsd.sh diff --git a/dnsapi/dns_nsd.sh b/dnsapi/dns_nsd.sh new file mode 100644 index 00000000..2c5b64ce --- /dev/null +++ b/dnsapi/dns_nsd.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env sh + +#Nsd_ZoneFile="/etc/nsd/zones/example.com.zone" +#Nsd_Command="sudo nsd-control reload" + +# args: fulldomain txtvalue +dns_nsd_add() +{ + fulldomain=$1 + txtvalue=$2 + ttlvalue=300 + + Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}" + Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}" + + # Arg checks + if [ -z "$Nsd_ZoneFile" ] || [ -z "$Nsd_Command" ]; then + Nsd_ZoneFile="" + Nsd_Command="" + _err "Specify ENV vars Nsd_ZoneFile and Nsd_Command" + return 1 + fi + + if [ ! -f "$Nsd_ZoneFile" ]; then + Nsd_ZoneFile="" + Nsd_Command="" + _err "No such file: $Nsd_ZoneFile" + return 1 + fi + + _savedomainconf Nsd_ZoneFile "$Nsd_ZoneFile" + _savedomainconf Nsd_Command "$Nsd_Command" + + echo "$fulldomain. $ttlvalue IN TXT \"$txtvalue\"" >> "$Nsd_ZoneFile" + _info "Added TXT record for $fulldomain" + _debug "Running $Nsd_Command" + if eval "$Nsd_Command"; then + _info "Successfully updated the zone" + return 0 + else + _err "Problem updating the zone" + return 1 + fi +} + +# args: fulldomain txtvalue +dns_nsd_rm() +{ + fulldomain=$1 + txtvalue=$2 + ttlvalue=300 + + Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}" + Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}" + + sed -i "/$fulldomain. $ttlvalue IN TXT \"$txtvalue\"/d" "$Nsd_ZoneFile" + _info "Removed TXT record for $fulldomain" + _debug "Running $Nsd_Command" + if eval "$Nsd_Command"; then + _info "Successfully reloaded NSD " + return 0 + else + _err "Problem reloading NSD" + return 1 + fi +} + From 63407041738634e16b761542bc1de163cfa8b7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=AF=D1=85=D0=B8=D0=BD?= Date: Wed, 1 May 2019 10:11:39 +0300 Subject: [PATCH 357/403] fixed line breaks for support api gcore_cdn (#2237) --- deploy/gcore_cdn.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index 56ca9afd..e0921bcb 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -27,8 +27,8 @@ gcore_cdn_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _fullchain=$(tr '\n\r' '@#' <"$_cfullchain" | sed 's/@/\\n/g;s/#/\\r/g') - _key=$(tr '\n\r' '@#' <"$_ckey" | sed 's/@/\\n/g;s/#/\\r/g') + _fullchain=$(tr '\r\n' '*#' <"$_cfullchain" | sed 's/*#/#/g;s/##/#/g;s/#/\\n/g') + _key=$(tr '\r\n' '*#' <"$_ckey" | sed 's/*#/#/g;s/#/\\n/g') _debug _fullchain "$_fullchain" _debug _key "$_key" From 040ca5320d4a409b9c5787940f47796443158cbe Mon Sep 17 00:00:00 2001 From: Jakub Filo Date: Wed, 1 May 2019 12:17:54 +0200 Subject: [PATCH 358/403] Fixed style to match upstream --- dnsapi/dns_nsd.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_nsd.sh b/dnsapi/dns_nsd.sh index 2c5b64ce..a7416708 100644 --- a/dnsapi/dns_nsd.sh +++ b/dnsapi/dns_nsd.sh @@ -4,8 +4,7 @@ #Nsd_Command="sudo nsd-control reload" # args: fulldomain txtvalue -dns_nsd_add() -{ +dns_nsd_add() { fulldomain=$1 txtvalue=$2 ttlvalue=300 @@ -31,7 +30,7 @@ dns_nsd_add() _savedomainconf Nsd_ZoneFile "$Nsd_ZoneFile" _savedomainconf Nsd_Command "$Nsd_Command" - echo "$fulldomain. $ttlvalue IN TXT \"$txtvalue\"" >> "$Nsd_ZoneFile" + echo "$fulldomain. $ttlvalue IN TXT \"$txtvalue\"" >>"$Nsd_ZoneFile" _info "Added TXT record for $fulldomain" _debug "Running $Nsd_Command" if eval "$Nsd_Command"; then @@ -44,8 +43,7 @@ dns_nsd_add() } # args: fulldomain txtvalue -dns_nsd_rm() -{ +dns_nsd_rm() { fulldomain=$1 txtvalue=$2 ttlvalue=300 From d1ef039e39fe246c0b7ec26eb656b6ee62f81648 Mon Sep 17 00:00:00 2001 From: Jakub Filo Date: Wed, 1 May 2019 12:25:46 +0200 Subject: [PATCH 359/403] Removed trailing line --- dnsapi/dns_nsd.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_nsd.sh b/dnsapi/dns_nsd.sh index a7416708..83cc4cac 100644 --- a/dnsapi/dns_nsd.sh +++ b/dnsapi/dns_nsd.sh @@ -62,4 +62,3 @@ dns_nsd_rm() { return 1 fi } - From 096ce1a20749ddd9e7738f5fd2a614c0d89002da Mon Sep 17 00:00:00 2001 From: mod242 <40213799+mod242@users.noreply.github.com> Date: Thu, 2 May 2019 12:18:16 +0200 Subject: [PATCH 360/403] Create DNS API for Schlundtech --- dnsapi/dns_schlundtech.sh | 261 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 dnsapi/dns_schlundtech.sh diff --git a/dnsapi/dns_schlundtech.sh b/dnsapi/dns_schlundtech.sh new file mode 100644 index 00000000..399c50e0 --- /dev/null +++ b/dnsapi/dns_schlundtech.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env sh +# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- + +# Schlundtech DNS API +# Author: mod242 +# Created: 2019-40-29 +# Completly based on the autoDNS xml api wrapper by auerswald@gmail.com +# +# export SCHLUNDTECH_USER="username" +# export SCHLUNDTECH_PASSWORD="password" +# +# Usage: +# acme.sh --issue --dns dns_schlundtech -d example.com + +SCHLUNDTECH_API="https://gateway.schlundtech.de" + +# Arguments: +# txtdomain +# txt +dns_schlundtech_add() { + fulldomain="$1" + txtvalue="$2" + + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" + + if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then + _err "You didn't specify schlundtech user and password." + return 1 + fi + + _saveaccountconf_mutable SCHLUNDTECH_USER "$SCHLUNDTECH_USER" + _saveaccountconf_mutable SCHLUNDTECH_PASSWORD "$SCHLUNDTECH_PASSWORD" + + _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_schlundtech_rm() { + fulldomain="$1" + txtvalue="$2" + + SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}" + SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}" + + if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then + _err "You didn't specify schlundtech user and password." + 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 + 10 + " "$SCHLUNDTECH_USER" "$SCHLUNDTECH_PASSWORD" +} + +# 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" "$SCHLUNDTECH_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 dac75a1dda7681df3e9cfae93675d93ceca7f574 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 3 May 2019 20:50:42 +0800 Subject: [PATCH 361/403] rename --- dnsapi/dns_one.sh | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index c99c9c97..94ac49c6 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -5,8 +5,8 @@ # Author: github: @diseq # Created: 2019-02-17 # -# export ONECOM_USER="username" -# export ONECOM_PASSWORD="password" +# export ONECOM_User="username" +# export ONECOM_Password="password" # # Usage: # acme.sh --issue --dns dns_one -d example.com @@ -19,26 +19,26 @@ dns_one_add() { txtvalue=$2 # get credentials - ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" - ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" - if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then - ONECOM_USER="" - ONECOM_PASSWORD="" + ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" + ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" + if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then + ONECOM_User="" + ONECOM_Password="" _err "You didn't specify a one.com username and password yet." _err "Please create the key and try again." return 1 fi #save the api key and email to the account conf file. - _saveaccountconf_mutable ONECOM_USER "$ONECOM_USER" - _saveaccountconf_mutable ONECOM_PASSWORD "$ONECOM_PASSWORD" + _saveaccountconf_mutable ONECOM_User "$ONECOM_User" + _saveaccountconf_mutable ONECOM_Password "$ONECOM_Password" # Login with user and password postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_USER" - postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&displayUsername=$ONECOM_User" + postdata="$postdata&username=$ONECOM_User" postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&password1=$ONECOM_Password" postdata="$postdata&loginTarget=" #_debug postdata "$postdata" @@ -64,7 +64,7 @@ dns_one_add() { response="$(echo "$response" | _normalizeJson)" _debug response "$response" - id=$(printf -- "%s" "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") + id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") if [ -z "$id" ]; then _err "Add txt record error." @@ -82,11 +82,11 @@ dns_one_rm() { txtvalue=$2 # get credentials - ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" - ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" - if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then - ONECOM_USER="" - ONECOM_PASSWORD="" + ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" + ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" + if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then + ONECOM_User="" + ONECOM_Password="" _err "You didn't specify a one.com username and password yet." _err "Please create the key and try again." return 1 @@ -94,10 +94,10 @@ dns_one_rm() { # Login with user and password postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_USER" - postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&displayUsername=$ONECOM_User" + postdata="$postdata&username=$ONECOM_User" postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&password1=$ONECOM_Password" postdata="$postdata&loginTarget=" response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" From 621d4745b4a65ea63658ad82c93aa0e185e80b07 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 10:18:42 +0800 Subject: [PATCH 362/403] fix idn --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index dc110a0f..727e35ed 100755 --- a/acme.sh +++ b/acme.sh @@ -3856,7 +3856,7 @@ issue() { if [ -z "$d" ]; then break fi - _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$$(_idn $d)\"}" done _debug2 _identifiers "$_identifiers" if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then From 6198e43fe69ca87c2f0eed639d2b8f098d11f039 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 10:21:15 +0800 Subject: [PATCH 363/403] fix idn --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 727e35ed..e84619fd 100755 --- a/acme.sh +++ b/acme.sh @@ -3856,7 +3856,7 @@ issue() { if [ -z "$d" ]; then break fi - _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$$(_idn $d)\"}" + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn $d)\"}" done _debug2 _identifiers "$_identifiers" if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then From a77f2fa4246ef4de4859924dbe563a67516608df Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 10:32:01 +0800 Subject: [PATCH 364/403] remove test file --- test.sh | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 test.sh diff --git a/test.sh b/test.sh deleted file mode 100644 index d976bfe4..00000000 --- a/test.sh +++ /dev/null @@ -1,8 +0,0 @@ - - -_data='aaaaa -bbb"bb -ccccc -ddddd -' - From 0f866510895e1130fcdc22cffcd5464e9e966841 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 10:43:39 +0800 Subject: [PATCH 365/403] fix idn --- .gitignore | 1 - acme.sh | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 427ec6be..00000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.autogit \ No newline at end of file diff --git a/acme.sh b/acme.sh index e84619fd..0397e5d2 100755 --- a/acme.sh +++ b/acme.sh @@ -1119,9 +1119,9 @@ _createcsr() { domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" + alt="DNS:$(_idn $domain),DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" else - alt="DNS:$domain,DNS:$domainlist" + alt="DNS:$(_idn $domain),DNS:$domainlist" fi #multi _info "Multi domain" "$alt" From acae0ac2a647c7b3c59b8e7bb4d41bfd40b89b73 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 10:59:00 +0800 Subject: [PATCH 366/403] fix RENEW_SKIP code --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0397e5d2..19af5b01 100755 --- a/acme.sh +++ b/acme.sh @@ -4622,7 +4622,7 @@ renew() { _info "$(__green "Renew: '$Le_Domain'")" if [ ! -f "$DOMAIN_CONF" ]; then _info "'$Le_Domain' is not a issued domain, skip." - return 0 + return $RENEW_SKIP fi if [ "$Le_RenewalDays" ]; then @@ -4676,7 +4676,7 @@ renew() { if [ "$IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then _info "Skip invalid cert for: $Le_Domain" - return 0 + return $RENEW_SKIP fi IS_RENEW="1" From 83768f0531432a3d0de05264240485871f2b8703 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 11:02:10 +0800 Subject: [PATCH 367/403] reduce info message --- notify/mailgun.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notify/mailgun.sh b/notify/mailgun.sh index 1689a0b9..7f5c914a 100644 --- a/notify/mailgun.sh +++ b/notify/mailgun.sh @@ -30,7 +30,7 @@ mailgun_send() { MAILGUN_REGION="${MAILGUN_REGION:-$(_readaccountconf_mutable MAILGUN_REGION)}" if [ -z "$MAILGUN_REGION" ]; then MAILGUN_REGION="" - _info "The MAILGUN_REGION is not set, so use the default us region." + _debug "The MAILGUN_REGION is not set, so use the default us region." _MAILGUN_BASE="https://api.mailgun.net/v3" else _saveaccountconf_mutable MAILGUN_REGION "$MAILGUN_REGION" @@ -83,7 +83,7 @@ mailgun_send() { _debug "_msg" "$_msg" _mailgun_rest POST "$_msg" if _contains "$response" "Queued. Thank you."; then - _info "mailgun send success." + _debug "mailgun send success." return 0 else _err "mailgun send error" From 5d468f7ca5cbf82982bc4d07e4a5762157e2c2c6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 11:06:25 +0800 Subject: [PATCH 368/403] add notifications --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8d40d51a..ab3412c1 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - DOES NOT require `root/sudoer` access. - Docker friendly - IPv6 support +- Cron job notifications for renewal or error etc. It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. @@ -432,20 +433,25 @@ acme.sh --upgrade --auto-upgrade 0 https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR -# 16. Under the Hood +# 16. Send notifications in cronjob + +https://github.com/Neilpang/acme.sh/wiki/notify + + +# 17. Under the Hood Speak ACME language using shell, directly to "Let's Encrypt". TODO: -# 17. Acknowledgments +# 18. Acknowledgments 1. Acme-tiny: https://github.com/diafygi/acme-tiny 2. ACME protocol: https://github.com/ietf-wg-acme/acme -# 18. License & Others +# 19. License & Others License is GPLv3 @@ -454,7 +460,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 +# 20. Donate Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) From 2b765fdedb84532052918a5524da5090604dbe18 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 4 May 2019 11:54:59 +0800 Subject: [PATCH 369/403] add set-notify --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 68385d7d..0e8b58d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ RUN for verb in help \ createCSR \ deactivate \ deactivate-account \ + set-notify \ ; do \ printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ ; done From b8f4fa359cea941397b6aa867efb57e082025eed Mon Sep 17 00:00:00 2001 From: Maarten den Braber Date: Mon, 6 May 2019 17:12:50 +0200 Subject: [PATCH 370/403] Add acmeproxy provider --- dnsapi/dns_acmeproxy.sh | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 dnsapi/dns_acmeproxy.sh diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh new file mode 100644 index 00000000..762f8652 --- /dev/null +++ b/dnsapi/dns_acmeproxy.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env sh + +## API integration by Jason Keller and Elijah Tenai +## +## Report any bugs via https://github.com/jasonkeller/acme.sh + +dns_acmeproxy_add() { + fulldomain="${1}" + txtvalue="${2}" + action="present" + + _debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'" + _acmeproxy_request $fulldomain $txtvalue $action +} + +dns_acmeproxy_rm() { + fulldomain="${1}" + txtvalue="${2}" + action="cleanup" + + _debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'" + _acmeproxy_request $fulldomain $txtvalue $action +} + +_acmeproxy_request() { + + ## Nothing to see here, just some housekeeping + fulldomain=$1 + txtvalue=$2 + action=$3 + + _info "Using acmeproxy" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + ACMEPROXY_ENDPOINT="${ACMEPROXY_ENDPOINT:-$(_readaccountconf_mutable ACMEPROXY_ENDPOINT)}" + ACMEPROXY_USERNAME="${ACMEPROXY_USERNAME:-$(_readaccountconf_mutable ACMEPROXY_USERNAME)}" + ACMEPROXY_PASSWORD="${ACMEPROXY_PASSWORD:-$(_readaccountconf_mutable ACMEPROXY_PASSWORD)}" + + ## Check for the endpoint + if [ -z "ACMEPROXY_ENDPOINT" ]; then + ACMEPROXY_ENDPOINT="" + _err "You didn't specify the endpoint" + _err "Please set them via 'export ACMEPROXY_ENDPOINT=https://ip:port' and try again." + return 1 + fi + + ## Check for the credentials + if [ -z "$ACMEPROXY_USERNAME" ] || [ -z "$ACMEPROXY_PASSWORD" ]; then + ACMEPROXY_USERNAME="" + ACMEPROXY_PASSWORD="" + _err "You didn't set username and password" + _err "Please set them via 'export ACMEPROXY_USERNAME=...' and 'export ACMEPROXY_PASSWORD=...' and try again." + return 1 + fi + + ## Save the credentials to the account file + _saveaccountconf_mutable ACMEPROXY_ENDPOINT "$ACMEPROXY_ENDPOINT" + _saveaccountconf_mutable ACMEPROXY_USERNAME "$ACMEPROXY_USERNAME" + _saveaccountconf_mutable ACMEPROXY_PASSWORD "$ACMEPROXY_PASSWORD" + + ## Base64 encode the credentials + credentials=$(printf "%b" "$ACMEPROXY_USERNAME:$ACMEPROXY_PASSWORD" | _base64) + + ## Construct the HTTP Authorization header + export _H1="Authorization: Basic $credentials" + export _H2="Accept: application/json" + export _H3="Content-Type: application/json" + + ## Add the challenge record to the acmeproxy grid member + response="$(_post "{\"fqdn\": \"$fulldomain.\", \"value\": \"$txtvalue\"}" "$ACMEPROXY_ENDPOINT/$action" "" "POST")" + + ## Let's see if we get something intelligible back from the unit + if echo "$response" | grep "\"$txtvalue\"" > /dev/null; then + _info "Successfully created the txt record" + return 0 + else + _err "Error encountered during record addition" + _err "$response" + return 1 + fi + +} + +#################### Private functions below ################################## From 68142c9835d77e9b564056460ff1116b1636395f Mon Sep 17 00:00:00 2001 From: Maarten den Braber Date: Mon, 6 May 2019 17:14:31 +0200 Subject: [PATCH 371/403] Update description --- dnsapi/dns_acmeproxy.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh index 762f8652..36bfc00c 100644 --- a/dnsapi/dns_acmeproxy.sh +++ b/dnsapi/dns_acmeproxy.sh @@ -1,8 +1,9 @@ #!/usr/bin/env sh -## API integration by Jason Keller and Elijah Tenai +## Acmeproxy DNS provider to be used with acmeproxy (http://github.com/mdbraber/acmeproxy) +## API integration by Maarten den Braber ## -## Report any bugs via https://github.com/jasonkeller/acme.sh +## Report any bugs via https://github.com/mdbraber/acme.sh dns_acmeproxy_add() { fulldomain="${1}" From c297aff99bd1abca6b0b554d7681bc073af45e33 Mon Sep 17 00:00:00 2001 From: Maarten den Braber Date: Mon, 6 May 2019 18:31:58 +0200 Subject: [PATCH 372/403] Improved logging description --- dnsapi/dns_acmeproxy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh index 36bfc00c..7f17ae6f 100644 --- a/dnsapi/dns_acmeproxy.sh +++ b/dnsapi/dns_acmeproxy.sh @@ -73,7 +73,7 @@ _acmeproxy_request() { ## Let's see if we get something intelligible back from the unit if echo "$response" | grep "\"$txtvalue\"" > /dev/null; then - _info "Successfully created the txt record" + _info "Successfully updated the txt record" return 0 else _err "Error encountered during record addition" From 585ef998d0ee5fe752484ddef13b92a3ce7dca88 Mon Sep 17 00:00:00 2001 From: Maarten den Braber Date: Tue, 7 May 2019 16:47:23 +0200 Subject: [PATCH 373/403] Fixed CI errors --- dnsapi/dns_acmeproxy.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh index 7f17ae6f..656e3104 100644 --- a/dnsapi/dns_acmeproxy.sh +++ b/dnsapi/dns_acmeproxy.sh @@ -11,7 +11,7 @@ dns_acmeproxy_add() { action="present" _debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'" - _acmeproxy_request $fulldomain $txtvalue $action + _acmeproxy_request "$fulldomain" "$txtvalue" "$action" } dns_acmeproxy_rm() { @@ -20,7 +20,7 @@ dns_acmeproxy_rm() { action="cleanup" _debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'" - _acmeproxy_request $fulldomain $txtvalue $action + _acmeproxy_request "$fulldomain" "$txtvalue" "$action" } _acmeproxy_request() { @@ -39,7 +39,7 @@ _acmeproxy_request() { ACMEPROXY_PASSWORD="${ACMEPROXY_PASSWORD:-$(_readaccountconf_mutable ACMEPROXY_PASSWORD)}" ## Check for the endpoint - if [ -z "ACMEPROXY_ENDPOINT" ]; then + if [ -z "$ACMEPROXY_ENDPOINT" ]; then ACMEPROXY_ENDPOINT="" _err "You didn't specify the endpoint" _err "Please set them via 'export ACMEPROXY_ENDPOINT=https://ip:port' and try again." @@ -72,7 +72,7 @@ _acmeproxy_request() { response="$(_post "{\"fqdn\": \"$fulldomain.\", \"value\": \"$txtvalue\"}" "$ACMEPROXY_ENDPOINT/$action" "" "POST")" ## Let's see if we get something intelligible back from the unit - if echo "$response" | grep "\"$txtvalue\"" > /dev/null; then + if echo "$response" | grep "\"$txtvalue\"" >/dev/null; then _info "Successfully updated the txt record" return 0 else From 11ecbd27be9fd92143c05ed22294d7059284419f Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 8 May 2019 22:07:27 +0800 Subject: [PATCH 374/403] fix punycode domain --- dnsapi/dns_cf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 6898eb1a..cd93189f 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -161,7 +161,7 @@ _get_root() { return 1 fi - if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then _domain_id=$(echo "$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) From 1a126b700feb18f780a826d943e92cdb3165ce37 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 8 May 2019 22:13:33 +0800 Subject: [PATCH 375/403] fix https://github.com/Neilpang/acme.sh/issues/2252 --- deploy/haproxy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 2479aebd..836c5182 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -179,7 +179,7 @@ haproxy_deploy() { return ${_ret} fi else - [ -f "${_issuer}" ] _err "Issuer file update not requested but .issuer file exists" + [ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists" fi # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option From f9e3a2132f1d957b0190243cc703a472eb20ee7a Mon Sep 17 00:00:00 2001 From: Maarten den Braber Date: Thu, 9 May 2019 21:14:26 +0200 Subject: [PATCH 376/403] Username/password no longer required --- dnsapi/dns_acmeproxy.sh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh index 656e3104..8ca3eb0a 100644 --- a/dnsapi/dns_acmeproxy.sh +++ b/dnsapi/dns_acmeproxy.sh @@ -46,15 +46,6 @@ _acmeproxy_request() { return 1 fi - ## Check for the credentials - if [ -z "$ACMEPROXY_USERNAME" ] || [ -z "$ACMEPROXY_PASSWORD" ]; then - ACMEPROXY_USERNAME="" - ACMEPROXY_PASSWORD="" - _err "You didn't set username and password" - _err "Please set them via 'export ACMEPROXY_USERNAME=...' and 'export ACMEPROXY_PASSWORD=...' and try again." - return 1 - fi - ## Save the credentials to the account file _saveaccountconf_mutable ACMEPROXY_ENDPOINT "$ACMEPROXY_ENDPOINT" _saveaccountconf_mutable ACMEPROXY_USERNAME "$ACMEPROXY_USERNAME" From d9ef8c17792f4dcf4bd40b9dae5890f47e23d087 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 00:25:36 +0200 Subject: [PATCH 377/403] add sendmail notify --- notify/sendmail.sh | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 notify/sendmail.sh diff --git a/notify/sendmail.sh b/notify/sendmail.sh new file mode 100644 index 00000000..3964e904 --- /dev/null +++ b/notify/sendmail.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +#Support sendmail + +#SENDMAIL_BIN="sendmail" +#SENDMAIL_FROM="yyyy@gmail.com" +#SENDMAIL_TO="yyyy@gmail.com" + +sendmail_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + SENDMAIL_BIN="${SENDMAIL_BIN:-$(_readaccountconf_mutable SENDMAIL_BIN)}" + if [ -z "$SENDMAIL_BIN" ]; then + SENDMAIL_BIN="sendmail" + _info "The SENDMAIL_BIN is not set, so use the default value: $SENDMAIL_BIN" + fi + if ! _exists "$SENDMAIL_BIN"; then + _err "Please install sendmail first." + return 1 + fi + _saveaccountconf_mutable SENDMAIL_BIN "$SENDMAIL_BIN" + + SENDMAIL_FROM="${SENDMAIL_FROM:-$(_readaccountconf_mutable SENDMAIL_FROM)}" + if [ -z "$SENDMAIL_FROM" ]; then + SENDMAIL_FROM="$USER@$HOSTNAME" + _info "The SENDMAIL_FROM is not set, so use the default value: $SENDMAIL_FROM" + fi + _saveaccountconf_mutable SENDMAIL_FROM "$SENDMAIL_FROM" + + SENDMAIL_TO="${SENDMAIL_TO:-$(_readaccountconf_mutable SENDMAIL_TO)}" + if [ -z "$SENDMAIL_TO" ]; then + SENDMAIL_TO="$(_readaccountconf ACCOUNT_EMAIL)" + _info "The SENDMAIL_TO is not set, so use the account email: $SENDMAIL_TO" + fi + _saveaccountconf_mutable SENDMAIL_TO "$SENDMAIL_TO" + + subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" + error=$( { echo "From: $SENDMAIL_FROM +To: $SENDMAIL_TO +Subject: $subject +Content-Type: text/plain; charset=utf-8 + +$_content +" | "$SENDMAIL_BIN" -f "$SENDMAIL_FROM" "$SENDMAIL_TO"; } 2>&1 ) + + if [ $? -ne 0 ]; then + _debug "sendmail send error." + _err "$error" + return 1 + fi + + _debug "sendmail send success." + return 0 +} From 773e1d4e059fce80bb92bd43f3c23ab5c764b2d0 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 00:34:46 +0200 Subject: [PATCH 378/403] use hostname function instead of HOSTNAME env variable --- notify/sendmail.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notify/sendmail.sh b/notify/sendmail.sh index 3964e904..162b5393 100644 --- a/notify/sendmail.sh +++ b/notify/sendmail.sh @@ -27,7 +27,7 @@ sendmail_send() { SENDMAIL_FROM="${SENDMAIL_FROM:-$(_readaccountconf_mutable SENDMAIL_FROM)}" if [ -z "$SENDMAIL_FROM" ]; then - SENDMAIL_FROM="$USER@$HOSTNAME" + SENDMAIL_FROM="$USER@$(hostname -f)" _info "The SENDMAIL_FROM is not set, so use the default value: $SENDMAIL_FROM" fi _saveaccountconf_mutable SENDMAIL_FROM "$SENDMAIL_FROM" From 4f0354860885807fe3c29321a4400cce0eb721e5 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 00:35:47 +0200 Subject: [PATCH 379/403] typos --- notify/sendmail.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notify/sendmail.sh b/notify/sendmail.sh index 162b5393..7c253e9c 100644 --- a/notify/sendmail.sh +++ b/notify/sendmail.sh @@ -40,13 +40,13 @@ sendmail_send() { _saveaccountconf_mutable SENDMAIL_TO "$SENDMAIL_TO" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" - error=$( { echo "From: $SENDMAIL_FROM + error=$({ echo "From: $SENDMAIL_FROM To: $SENDMAIL_TO Subject: $subject Content-Type: text/plain; charset=utf-8 $_content -" | "$SENDMAIL_BIN" -f "$SENDMAIL_FROM" "$SENDMAIL_TO"; } 2>&1 ) +" | "$SENDMAIL_BIN" -f "$SENDMAIL_FROM" "$SENDMAIL_TO"; } 2>&1) if [ $? -ne 0 ]; then _debug "sendmail send error." From a4b83895a37750801c5fbf250bc69386a896146e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 12 May 2019 15:34:58 +0800 Subject: [PATCH 380/403] fix https://github.com/Neilpang/acme.sh/issues/2258 --- notify/mailgun.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/notify/mailgun.sh b/notify/mailgun.sh index 7f5c914a..4b6ee3ba 100644 --- a/notify/mailgun.sh +++ b/notify/mailgun.sh @@ -9,7 +9,10 @@ #MAILGUN_API_DOMAIN="xxxxxx.com" #optional, use the default sandbox domain #MAILGUN_FROM="xxx@xxxxx.com" #optional, use the default sendbox account -_MAILGUN_BASE="https://api.mailgun.net/v3" +_MAILGUN_BASE_US="https://api.mailgun.net/v3" +_MAILGUN_BASE_EU="https://api.eu.mailgun.net/v3" + +_MAILGUN_BASE="$_MAILGUN_BASE_US" # subject content statusCode mailgun_send() { @@ -31,12 +34,17 @@ mailgun_send() { if [ -z "$MAILGUN_REGION" ]; then MAILGUN_REGION="" _debug "The MAILGUN_REGION is not set, so use the default us region." - _MAILGUN_BASE="https://api.mailgun.net/v3" + _MAILGUN_BASE="$_MAILGUN_BASE_US" else + MAILGUN_REGION="$(echo "$MAILGUN_REGION" | _lower_case)" _saveaccountconf_mutable MAILGUN_REGION "$MAILGUN_REGION" - _MAILGUN_BASE="https://api.eu.mailgun.net/v3" + if [ "$MAILGUN_REGION" = "us" ]; then + _MAILGUN_BASE="$_MAILGUN_BASE_US" + else + _MAILGUN_BASE="$_MAILGUN_BASE_EU" + fi fi - + _debug _MAILGUN_BASE "$_MAILGUN_BASE" MAILGUN_TO="${MAILGUN_TO:-$(_readaccountconf_mutable MAILGUN_TO)}" if [ -z "$MAILGUN_TO" ]; then MAILGUN_TO="" From f6f6d89e0683422338c55d2a6b8b3c97fe66b403 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 10:41:32 +0200 Subject: [PATCH 381/403] move sendmail notify to mail notify --- notify/mail.sh | 64 +++++++++++++++++++++++++++++++++++++++++++--- notify/sendmail.sh | 59 ------------------------------------------ 2 files changed, 61 insertions(+), 62 deletions(-) delete mode 100644 notify/sendmail.sh diff --git a/notify/mail.sh b/notify/mail.sh index 3dfef0be..949e8b5c 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -1,6 +1,9 @@ #!/usr/bin/env sh -# support local mail app +#Support local mail app + +#MAIL_FROM="yyyy@gmail.com" +#MAIL_TO="yyyy@gmail.com" mail_send() { _subject="$1" @@ -10,6 +13,61 @@ mail_send() { _debug "_content" "$_content" _debug "_statusCode" "$_statusCode" - _err "Not implemented yet." - return 1 + if _exists "sendmail"; then + _MAIL_BIN="sendmail" + elif _exists "mail"; then + _MAIL_BIN="mail" + else + _err "Please install mail or sendmail first." + return 1 + fi + + MAIL_FROM="${MAIL_FROM:-$(_readaccountconf_mutable MAIL_FROM)}" + if [ -z "$MAIL_FROM" ]; then + MAIL_FROM="$USER@$(hostname -f)" + _info "The MAIL_FROM is not set, so use the default value: $MAIL_FROM" + fi + _saveaccountconf_mutable MAIL_FROM "$MAIL_FROM" + + MAIL_TO="${MAIL_TO:-$(_readaccountconf_mutable MAIL_TO)}" + if [ -z "$MAIL_TO" ]; then + MAIL_TO="$(_readaccountconf ACCOUNT_EMAIL)" + _info "The MAIL_TO is not set, so use the account email: $MAIL_TO" + fi + _saveaccountconf_mutable MAIL_TO "$MAIL_TO" + + subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" + result=$({ _mail_body | _mail_send; } 2>&1) + + if [ $? -ne 0 ]; then + _debug "mail send error." + _err "$result" + return 1 + fi + + _debug "mail send success." + return 0 +} + +_mail_send() { + case "$_MAIL_BIN" in + sendmail) + sendmail -f "$MAIL_FROM" "$MAIL_TO" + ;; + mail) + mail -s "$subject" -a "From:$MAIL_FROM" -a "Content-Type:text/plain; charset=utf-8" "$MAIL_TO" + ;; + esac +} + +_mail_body() { + if [ "$_MAIL_BIN" = "sendmail" ]; then + echo "From: $MAIL_FROM" + echo "To: $MAIL_TO" + echo "Subject: =?UTF-8?B?$(echo "$_subject" | _base64)?=" + echo "Content-Type: text/plain; charset=utf-8" + echo + fi + + echo "$_content" } diff --git a/notify/sendmail.sh b/notify/sendmail.sh deleted file mode 100644 index 7c253e9c..00000000 --- a/notify/sendmail.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env sh - -#Support sendmail - -#SENDMAIL_BIN="sendmail" -#SENDMAIL_FROM="yyyy@gmail.com" -#SENDMAIL_TO="yyyy@gmail.com" - -sendmail_send() { - _subject="$1" - _content="$2" - _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped - _debug "_subject" "$_subject" - _debug "_content" "$_content" - _debug "_statusCode" "$_statusCode" - - SENDMAIL_BIN="${SENDMAIL_BIN:-$(_readaccountconf_mutable SENDMAIL_BIN)}" - if [ -z "$SENDMAIL_BIN" ]; then - SENDMAIL_BIN="sendmail" - _info "The SENDMAIL_BIN is not set, so use the default value: $SENDMAIL_BIN" - fi - if ! _exists "$SENDMAIL_BIN"; then - _err "Please install sendmail first." - return 1 - fi - _saveaccountconf_mutable SENDMAIL_BIN "$SENDMAIL_BIN" - - SENDMAIL_FROM="${SENDMAIL_FROM:-$(_readaccountconf_mutable SENDMAIL_FROM)}" - if [ -z "$SENDMAIL_FROM" ]; then - SENDMAIL_FROM="$USER@$(hostname -f)" - _info "The SENDMAIL_FROM is not set, so use the default value: $SENDMAIL_FROM" - fi - _saveaccountconf_mutable SENDMAIL_FROM "$SENDMAIL_FROM" - - SENDMAIL_TO="${SENDMAIL_TO:-$(_readaccountconf_mutable SENDMAIL_TO)}" - if [ -z "$SENDMAIL_TO" ]; then - SENDMAIL_TO="$(_readaccountconf ACCOUNT_EMAIL)" - _info "The SENDMAIL_TO is not set, so use the account email: $SENDMAIL_TO" - fi - _saveaccountconf_mutable SENDMAIL_TO "$SENDMAIL_TO" - - subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" - error=$({ echo "From: $SENDMAIL_FROM -To: $SENDMAIL_TO -Subject: $subject -Content-Type: text/plain; charset=utf-8 - -$_content -" | "$SENDMAIL_BIN" -f "$SENDMAIL_FROM" "$SENDMAIL_TO"; } 2>&1) - - if [ $? -ne 0 ]; then - _debug "sendmail send error." - _err "$error" - return 1 - fi - - _debug "sendmail send success." - return 0 -} From a89a62071b3728e1e6ad885fe3e9441cfb0b78bd Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 13:03:01 +0200 Subject: [PATCH 382/403] cleanup, lint --- notify/mail.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index 949e8b5c..6c6ef4e2 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -18,7 +18,7 @@ mail_send() { elif _exists "mail"; then _MAIL_BIN="mail" else - _err "Please install mail or sendmail first." + _err "Please install sendmail or mail first." return 1 fi @@ -36,6 +36,7 @@ mail_send() { fi _saveaccountconf_mutable MAIL_TO "$MAIL_TO" + contenttype="text/plain; charset=utf-8" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" result=$({ _mail_body | _mail_send; } 2>&1) @@ -52,10 +53,10 @@ mail_send() { _mail_send() { case "$_MAIL_BIN" in sendmail) - sendmail -f "$MAIL_FROM" "$MAIL_TO" + "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" ;; mail) - mail -s "$subject" -a "From:$MAIL_FROM" -a "Content-Type:text/plain; charset=utf-8" "$MAIL_TO" + "$_MAIL_BIN" -s "$subject" -a "From:$MAIL_FROM" -a "Content-Type:$contenttype" "$MAIL_TO" ;; esac } @@ -64,8 +65,8 @@ _mail_body() { if [ "$_MAIL_BIN" = "sendmail" ]; then echo "From: $MAIL_FROM" echo "To: $MAIL_TO" - echo "Subject: =?UTF-8?B?$(echo "$_subject" | _base64)?=" - echo "Content-Type: text/plain; charset=utf-8" + echo "Subject: $subject" + echo "Content-Type: $contenttype" echo fi From 10801bfb255a3dd0894ca8db1694ee019fcc4018 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 13:06:45 +0200 Subject: [PATCH 383/403] use mutt if installed --- notify/mail.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notify/mail.sh b/notify/mail.sh index 6c6ef4e2..cfd381fc 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -15,6 +15,8 @@ mail_send() { if _exists "sendmail"; then _MAIL_BIN="sendmail" + elif _exists "mutt"; then + _MAIL_BIN="mutt" elif _exists "mail"; then _MAIL_BIN="mail" else @@ -55,6 +57,9 @@ _mail_send() { sendmail) "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" ;; + mutt) + "$_MAIL_BIN" -s "$subject" -e "my_hdr From:$MAIL_FROM" -e "my_hdr Content-Type:$contenttype" "$MAIL_TO" + ;; mail) "$_MAIL_BIN" -s "$subject" -a "From:$MAIL_FROM" -a "Content-Type:$contenttype" "$MAIL_TO" ;; From f6ca92337b62801222ee643cfb9def25742153b9 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 20:24:02 +0200 Subject: [PATCH 384/403] remove unsupported options from mail and mutt command --- notify/mail.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index cfd381fc..cd926270 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -57,11 +57,8 @@ _mail_send() { sendmail) "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" ;; - mutt) - "$_MAIL_BIN" -s "$subject" -e "my_hdr From:$MAIL_FROM" -e "my_hdr Content-Type:$contenttype" "$MAIL_TO" - ;; - mail) - "$_MAIL_BIN" -s "$subject" -a "From:$MAIL_FROM" -a "Content-Type:$contenttype" "$MAIL_TO" + mutt|mail) + "$_MAIL_BIN" -s "$_subject" "$MAIL_TO" ;; esac } From 91c09dd0a076ff4d037dc3fc65a39d799fe4e903 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 20:26:31 +0200 Subject: [PATCH 385/403] ssmtp --- notify/mail.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index cd926270..6bb7520b 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -15,12 +15,14 @@ mail_send() { if _exists "sendmail"; then _MAIL_BIN="sendmail" + elif _exists "ssmtp"; then + _MAIL_BIN="ssmtp" elif _exists "mutt"; then _MAIL_BIN="mutt" elif _exists "mail"; then _MAIL_BIN="mail" else - _err "Please install sendmail or mail first." + _err "Please install sendmail, ssmtp, mutt or mail first." return 1 fi @@ -57,6 +59,9 @@ _mail_send() { sendmail) "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" ;; + ssmtp) + "$_MAIL_BIN" "$MAIL_TO" + ;; mutt|mail) "$_MAIL_BIN" -s "$_subject" "$MAIL_TO" ;; @@ -64,7 +69,7 @@ _mail_send() { } _mail_body() { - if [ "$_MAIL_BIN" = "sendmail" ]; then + if [ "$_MAIL_BIN" = "sendmail" ] || [ "$_MAIL_BIN" = "ssmtp" ]; then echo "From: $MAIL_FROM" echo "To: $MAIL_TO" echo "Subject: $subject" From d180f01b458d607559d28b853a2d42af2e42063a Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sun, 12 May 2019 22:28:37 +0200 Subject: [PATCH 386/403] typos --- notify/mail.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notify/mail.sh b/notify/mail.sh index 6bb7520b..034fc03c 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -62,7 +62,7 @@ _mail_send() { ssmtp) "$_MAIL_BIN" "$MAIL_TO" ;; - mutt|mail) + mutt | mail) "$_MAIL_BIN" -s "$_subject" "$MAIL_TO" ;; esac From 5e165819a1df9d6eef5f55bd6fd464f231cec570 Mon Sep 17 00:00:00 2001 From: Maarten den Braber Date: Mon, 13 May 2019 08:45:57 +0200 Subject: [PATCH 387/403] Update authentication logic / info --- dnsapi/dns_acmeproxy.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_acmeproxy.sh b/dnsapi/dns_acmeproxy.sh index 8ca3eb0a..d4a0e172 100644 --- a/dnsapi/dns_acmeproxy.sh +++ b/dnsapi/dns_acmeproxy.sh @@ -51,13 +51,19 @@ _acmeproxy_request() { _saveaccountconf_mutable ACMEPROXY_USERNAME "$ACMEPROXY_USERNAME" _saveaccountconf_mutable ACMEPROXY_PASSWORD "$ACMEPROXY_PASSWORD" - ## Base64 encode the credentials - credentials=$(printf "%b" "$ACMEPROXY_USERNAME:$ACMEPROXY_PASSWORD" | _base64) + if [ -z "$ACMEPROXY_USERNAME" ] || [ -z "$ACMEPROXY_PASSWORD" ]; then + _info "ACMEPROXY_USERNAME and/or ACMEPROXY_PASSWORD not set - using without client authentication! Make sure you're using server authentication (e.g. IP-based)" + export _H1="Accept: application/json" + export _H2="Content-Type: application/json" + else + ## Base64 encode the credentials + credentials=$(printf "%b" "$ACMEPROXY_USERNAME:$ACMEPROXY_PASSWORD" | _base64) - ## Construct the HTTP Authorization header - export _H1="Authorization: Basic $credentials" - export _H2="Accept: application/json" - export _H3="Content-Type: application/json" + ## Construct the HTTP Authorization header + export _H1="Authorization: Basic $credentials" + export _H2="Accept: application/json" + export _H3="Content-Type: application/json" + fi ## Add the challenge record to the acmeproxy grid member response="$(_post "{\"fqdn\": \"$fulldomain.\", \"value\": \"$txtvalue\"}" "$ACMEPROXY_ENDPOINT/$action" "" "POST")" From d509ef7581789697164a38e2bdd5ae12617dfb7c Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 16:06:24 +0200 Subject: [PATCH 388/403] make MAIL_FROM not required --- notify/mail.sh | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index 034fc03c..33ed8fe9 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -27,11 +27,14 @@ mail_send() { fi MAIL_FROM="${MAIL_FROM:-$(_readaccountconf_mutable MAIL_FROM)}" - if [ -z "$MAIL_FROM" ]; then - MAIL_FROM="$USER@$(hostname -f)" - _info "The MAIL_FROM is not set, so use the default value: $MAIL_FROM" + if [ -n "$MAIL_FROM" ]; then + if ! _contains "$MAIL_FROM" "@"; then + _err "It seems that the MAIL_FROM=$MAIL_FROM is not a valid email address." + return 1 + fi + + _saveaccountconf_mutable MAIL_FROM "$MAIL_FROM" fi - _saveaccountconf_mutable MAIL_FROM "$MAIL_FROM" MAIL_TO="${MAIL_TO:-$(_readaccountconf_mutable MAIL_TO)}" if [ -z "$MAIL_TO" ]; then @@ -57,7 +60,11 @@ mail_send() { _mail_send() { case "$_MAIL_BIN" in sendmail) - "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" + if [ -n "$MAIL_FROM" ]; then + "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" + else + "$_MAIL_BIN" "$MAIL_TO" + fi ;; ssmtp) "$_MAIL_BIN" "$MAIL_TO" @@ -70,7 +77,10 @@ _mail_send() { _mail_body() { if [ "$_MAIL_BIN" = "sendmail" ] || [ "$_MAIL_BIN" = "ssmtp" ]; then - echo "From: $MAIL_FROM" + if [ -n "$MAIL_FROM" ]; then + echo "From: $MAIL_FROM" + fi + echo "To: $MAIL_TO" echo "Subject: $subject" echo "Content-Type: $contenttype" From 0093dc3d32cf26e2815ddb476e3bd3398604c985 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 13 May 2019 23:30:31 +0800 Subject: [PATCH 389/403] fix https://github.com/Neilpang/acme.sh/issues/2256 --- acme.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 19af5b01..4c1e45d6 100755 --- a/acme.sh +++ b/acme.sh @@ -1114,14 +1114,14 @@ _createcsr() { elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" - printf -- "\nsubjectAltName=DNS:$(_idn $domain)" >>"$csrconf" + printf -- "\nsubjectAltName=DNS:$(_idn "$domain")" >>"$csrconf" else domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$(_idn $domain),DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" + alt="DNS:$(_idn "$domain"),DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" else - alt="DNS:$(_idn $domain),DNS:$domainlist" + alt="DNS:$(_idn "$domain"),DNS:$domainlist" fi #multi _info "Multi domain" "$alt" @@ -3648,9 +3648,9 @@ _check_dns_entries() { for entry in $dns_entries; do d=$(_getfield "$entry" 1) txtdomain=$(_getfield "$entry" 2) - txtdomain=$(_idn $txtdomain) + txtdomain=$(_idn "$txtdomain") aliasDomain=$(_getfield "$entry" 3) - aliasDomain=$(_idn $aliasDomain) + aliasDomain=$(_idn "$aliasDomain") txt=$(_getfield "$entry" 5) d_api=$(_getfield "$entry" 6) _debug "d" "$d" @@ -3847,7 +3847,7 @@ issue() { if [ -z "$vlist" ]; then if [ "$ACME_VERSION" = "2" ]; then #make new order request - _identifiers="{\"type\":\"dns\",\"value\":\"$(_idn $_main_domain)\"}" + _identifiers="{\"type\":\"dns\",\"value\":\"$(_idn "$_main_domain")\"}" _w_index=1 while true; do d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")" @@ -3856,7 +3856,7 @@ issue() { if [ -z "$d" ]; then break fi - _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn $d)\"}" + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn "$d")\"}" done _debug2 _identifiers "$_identifiers" if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then @@ -3944,7 +3944,7 @@ $_authorizations_map" fi if [ "$ACME_VERSION" = "2" ]; then - response="$(echo "$_authorizations_map" | grep "^$(_idn $d)," | sed "s/$d,//")" + response="$(echo "$_authorizations_map" | grep "^$(_idn "$d")," | sed "s/$d,//")" _debug2 "response" "$response" if [ -z "$response" ]; then _err "get to authz error." From 7b6ebc5c989350c113ee3516d47192dffaaac128 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 17:42:07 +0200 Subject: [PATCH 390/403] try to use ACCOUNT_MAIL if MAIL_FROM is not set --- notify/mail.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index 33ed8fe9..a1970c0a 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -37,11 +37,21 @@ mail_send() { fi MAIL_TO="${MAIL_TO:-$(_readaccountconf_mutable MAIL_TO)}" - if [ -z "$MAIL_TO" ]; then + if [ -n "$MAIL_TO" ]; then + if ! _contains "$MAIL_TO" "@"; then + _err "It seems that the MAIL_TO=$MAIL_TO is not a valid email address." + return 1 + fi + + _saveaccountconf_mutable MAIL_TO "$MAIL_TO" + else MAIL_TO="$(_readaccountconf ACCOUNT_EMAIL)" - _info "The MAIL_TO is not set, so use the account email: $MAIL_TO" + + if [ -z "$MAIL_TO" ]; then + _err "It seems that account email is empty." + return 1 + fi fi - _saveaccountconf_mutable MAIL_TO "$MAIL_TO" contenttype="text/plain; charset=utf-8" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" From e3052c8c57279f5732d849e63530efba90cd5af6 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 17:44:04 +0200 Subject: [PATCH 391/403] expose MAIL_BIN variable --- notify/mail.sh | 54 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index a1970c0a..44148d99 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -2,6 +2,7 @@ #Support local mail app +#MAIL_BIN="sendmail" #MAIL_FROM="yyyy@gmail.com" #MAIL_TO="yyyy@gmail.com" @@ -13,18 +14,18 @@ mail_send() { _debug "_content" "$_content" _debug "_statusCode" "$_statusCode" - if _exists "sendmail"; then - _MAIL_BIN="sendmail" - elif _exists "ssmtp"; then - _MAIL_BIN="ssmtp" - elif _exists "mutt"; then - _MAIL_BIN="mutt" - elif _exists "mail"; then - _MAIL_BIN="mail" - else - _err "Please install sendmail, ssmtp, mutt or mail first." + unset -f _MAIL_BIN _MAIL_BODY _MAIL_CMD + + MAIL_BIN="${MAIL_BIN:-$(_readaccountconf_mutable MAIL_BIN)}" + if [ -n "$MAIL_BIN" ] && ! _exists "$MAIL_BIN"; then + _err "It seems that the command $MAIL_BIN is not in path." return 1 fi + _MAIL_CMD=$(_mail_cmnd) + if [ -n "$MAIL_BIN" ]; then + _saveaccountconf_mutable MAIL_BIN "$MAIL_BIN" + fi + _MAIL_BODY=$(_mail_body) MAIL_FROM="${MAIL_FROM:-$(_readaccountconf_mutable MAIL_FROM)}" if [ -n "$MAIL_FROM" ]; then @@ -47,6 +48,8 @@ mail_send() { else MAIL_TO="$(_readaccountconf ACCOUNT_EMAIL)" + echo "MAIL_TO: $MAIL_TO" + if [ -z "$MAIL_TO" ]; then _err "It seems that account email is empty." return 1 @@ -55,7 +58,7 @@ mail_send() { contenttype="text/plain; charset=utf-8" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" - result=$({ _mail_body | _mail_send; } 2>&1) + result=$({ echo "$_MAIL_BODY" | eval "$_MAIL_CMD"; } 2>&1) if [ $? -ne 0 ]; then _debug "mail send error." @@ -67,20 +70,39 @@ mail_send() { return 0 } -_mail_send() { +_mail_cmnd() { + if [ -n "$MAIL_BIN" ]; then + _MAIL_BIN=$(basename "$MAIL_BIN") + elif _exists "sendmail"; then + _MAIL_BIN="sendmail" + elif _exists "ssmtp"; then + _MAIL_BIN="ssmtp" + elif _exists "mutt"; then + _MAIL_BIN="mutt" + elif _exists "mail"; then + _MAIL_BIN="mail" + else + _err "Please install sendmail, ssmtp, mutt or mail first." + return 1 + fi + case "$_MAIL_BIN" in sendmail) if [ -n "$MAIL_FROM" ]; then - "$_MAIL_BIN" -f "$MAIL_FROM" "$MAIL_TO" + echo "'$_MAIL_BIN' -f '$MAIL_FROM' '$MAIL_TO'" else - "$_MAIL_BIN" "$MAIL_TO" + echo "'$_MAIL_BIN' '$MAIL_TO'" fi ;; ssmtp) - "$_MAIL_BIN" "$MAIL_TO" + echo "'$_MAIL_BIN' '$MAIL_TO'" ;; mutt | mail) - "$_MAIL_BIN" -s "$_subject" "$MAIL_TO" + echo "'$_MAIL_BIN' -s '$_subject' '$MAIL_TO'" + ;; + *) + _err "Command $MAIL_BIN is not supported, use sendmail, ssmtp, mutt or mail." + return 1 ;; esac } From 30f2c2bd7781149e3a51368f1be1fcebd273d78c Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 18:11:44 +0200 Subject: [PATCH 392/403] prevent _MAIL_BIN modification --- notify/mail.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index 44148d99..3aa05366 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -72,7 +72,7 @@ mail_send() { _mail_cmnd() { if [ -n "$MAIL_BIN" ]; then - _MAIL_BIN=$(basename "$MAIL_BIN") + _MAIL_BIN="$MAIL_BIN" elif _exists "sendmail"; then _MAIL_BIN="sendmail" elif _exists "ssmtp"; then @@ -86,7 +86,7 @@ _mail_cmnd() { return 1 fi - case "$_MAIL_BIN" in + case $(basename "$_MAIL_BIN") in sendmail) if [ -n "$MAIL_FROM" ]; then echo "'$_MAIL_BIN' -f '$MAIL_FROM' '$MAIL_TO'" From 7625d662599f01fabbcfbd8c72034591e94743ce Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 18:58:28 +0200 Subject: [PATCH 393/403] wip --- notify/slack.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 notify/slack.sh diff --git a/notify/slack.sh b/notify/slack.sh new file mode 100644 index 00000000..00d38b2e --- /dev/null +++ b/notify/slack.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env sh + +#Support Slack webhooks + +#SLACK_WEBHOOK_URL="" + +slack_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + SLACK_WEBHOOK_URL="${SLACK_WEBHOOK_URL:-$(_readaccountconf_mutable SLACK_WEBHOOK_URL)}" + if [ -z "$SLACK_WEBHOOK_URL" ]; then + SLACK_WEBHOOK_URL="" + _err "You didn't specify a Slack webhook url SLACK_WEBHOOK_URL yet." + return 1 + fi + _saveaccountconf_mutable SLACK_WEBHOOK_URL "$SLACK_WEBHOOK_URL" + + export _H1="Content-Type: application/json" + + _content="$(echo "$_subject: $_content" | _json_encode)" + _data="{\"text\": \"$_content\"}" + +echo "$_content" +echo "$_data" + + if _post "$_data" "$SLACK_WEBHOOK_URL"; then + # shellcheck disable=SC2154 + if [ -z "$response" ]; then + _info "slack send sccess." + return 0 + fi + fi + _err "slack send error." + _err "$response" + return 1 + +} From fc5e3a0aec38c9108322489dd16ff72cec8b55b6 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 18:59:58 +0200 Subject: [PATCH 394/403] remove echo command --- notify/mail.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index 3aa05366..73f180df 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -47,9 +47,6 @@ mail_send() { _saveaccountconf_mutable MAIL_TO "$MAIL_TO" else MAIL_TO="$(_readaccountconf ACCOUNT_EMAIL)" - - echo "MAIL_TO: $MAIL_TO" - if [ -z "$MAIL_TO" ]; then _err "It seems that account email is empty." return 1 From 73bbe25d2692dd8e5e2a5985becaf69d416e8f34 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 13 May 2019 19:49:16 +0200 Subject: [PATCH 395/403] add slack notify --- notify/slack.sh | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/notify/slack.sh b/notify/slack.sh index 00d38b2e..cc1ed765 100644 --- a/notify/slack.sh +++ b/notify/slack.sh @@ -3,6 +3,8 @@ #Support Slack webhooks #SLACK_WEBHOOK_URL="" +#SLACK_CHANNEL="" +#SLACK_USERNAME="" slack_send() { _subject="$1" @@ -18,23 +20,36 @@ slack_send() { fi _saveaccountconf_mutable SLACK_WEBHOOK_URL "$SLACK_WEBHOOK_URL" - export _H1="Content-Type: application/json" + SLACK_CHANNEL="${SLACK_CHANNEL:-$(_readaccountconf_mutable SLACK_CHANNEL)}" + if [ -n "$SLACK_CHANNEL" ]; then + _saveaccountconf_mutable SLACK_CHANNEL "$SLACK_CHANNEL" + fi + + SLACK_USERNAME="${SLACK_USERNAME:-$(_readaccountconf_mutable SLACK_USERNAME)}" + if [ -n "$SLACK_USERNAME" ]; then + _saveaccountconf_mutable SLACK_USERNAME "$SLACK_USERNAME" + fi - _content="$(echo "$_subject: $_content" | _json_encode)" - _data="{\"text\": \"$_content\"}" + export _H1="Content-Type: application/json" -echo "$_content" -echo "$_data" + _content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)" + _data="{\"text\": \"$_content\", " + if [ -n "$SLACK_CHANNEL" ]; then + _data="$_data\"channel\": \"$SLACK_CHANNEL\", " + fi + if [ -n "$SLACK_USERNAME" ]; then + _data="$_data\"username\": \"$SLACK_USERNAME\", " + fi + _data="$_data\"mrkdwn\": \"true\"}" if _post "$_data" "$SLACK_WEBHOOK_URL"; then # shellcheck disable=SC2154 - if [ -z "$response" ]; then - _info "slack send sccess." + if [ "$response" = "ok" ]; then + _info "slack send success." return 0 fi fi _err "slack send error." _err "$response" return 1 - } From a180b95ccaeda85769e413ed6ce54dd0a6f54c9e Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 17 May 2019 20:16:26 +0800 Subject: [PATCH 396/403] add more debug info --- acme.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 4c1e45d6..60580f83 100755 --- a/acme.sh +++ b/acme.sh @@ -3044,11 +3044,12 @@ _clearupdns() { _err "It seems that your api file doesn't define $rmcommand" return 1 fi - + _info "Removing txt: $txt for domain: $txtdomain" if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" return 1 fi + _info "Removed: Success" ) done @@ -4063,7 +4064,7 @@ $_authorizations_map" dns_entry="$dns_entry$dvsep$txt${dvsep}$d_api" _debug2 dns_entry "$dns_entry" if [ "$d_api" ]; then - _info "Found domain api file: $d_api" + _debug "Found domain api file: $d_api" else if [ "$_currentRoot" != "$W_DNS" ]; then _err "Can not find dns api hook for: $_currentRoot" @@ -4088,11 +4089,12 @@ $_authorizations_map" _err "It seems that your api file is not correct, it must have a function named: $addcommand" return 1 fi - + _info "Adding txt value: $txt for domain: $txtdomain" if ! $addcommand "$txtdomain" "$txt"; then _err "Error add txt for domain:$txtdomain" return 1 fi + _info "The txt record is added: Success." ) if [ "$?" != "0" ]; then From ace947e6b3fce69ecd97881daaaf22917dafc9a6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 18 May 2019 21:00:39 +0800 Subject: [PATCH 397/403] add dns_durabledns.sh --- dnsapi/dns_durabledns.sh | 182 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 dnsapi/dns_durabledns.sh diff --git a/dnsapi/dns_durabledns.sh b/dnsapi/dns_durabledns.sh new file mode 100644 index 00000000..5de1eaaa --- /dev/null +++ b/dnsapi/dns_durabledns.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env sh + + +#DD_API_User="xxxxx" +#DD_API_Key="xxxxxx" + +_DD_BASE="https://durabledns.com/services/dns" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_durabledns_add() { + fulldomain=$1 + txtvalue=$2 + + DD_API_User="${DD_API_User:-$(_readaccountconf_mutable DD_API_User)}" + DD_API_Key="${DD_API_Key:-$(_readaccountconf_mutable DD_API_Key)}" + if [ -z "$DD_API_User" ] || [ -z "$DD_API_Key" ]; then + DD_API_User="" + DD_API_Key="" + _err "You didn't specify a durabledns api user or key yet." + _err "You can get yours from here https://durabledns.com/dashboard/index.php" + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable DD_API_User "$DD_API_User" + _saveaccountconf_mutable DD_API_Key "$DD_API_Key" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _dd_soap createRecord string zonename "$_domain." string name "$_sub_domain" string type "TXT" string data "$txtvalue" int aux 0 int ttl 10 string ddns_enabled N + _contains "$response" "createRecordResponse" +} + + +dns_durabledns_rm() { + fulldomain=$1 + txtvalue=$2 + + DD_API_User="${DD_API_User:-$(_readaccountconf_mutable DD_API_User)}" + DD_API_Key="${DD_API_Key:-$(_readaccountconf_mutable DD_API_Key)}" + if [ -z "$DD_API_User" ] || [ -z "$DD_API_Key" ]; then + DD_API_User="" + DD_API_Key="" + _err "You didn't specify a durabledns api user or key yet." + _err "You can get yours from here https://durabledns.com/dashboard/index.php" + 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 "Find record id" + if ! _dd_soap listRecords string zonename "$_domain."; then + _err "can not listRecords" + return 1 + fi + + subtxt="$(echo "$txtvalue" | cut -c 1-30)" + record="$(echo "$response" | sed 's//#/g' | tr '#' '\n' | grep ">$subtxt")" + _debug record "$record" + if [ -z "$record" ]; then + _err "can not find record for txtvalue" "$txtvalue" + _err "$response" + return 1 + fi + + recordid="$(echo "$record" | _egrep_o '[0-9]*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _debug recordid "$recordid" + if [ -z "$recordid" ]; then + _err "can not find record id" + return 1 + fi + + if ! _dd_soap deleteRecord string zonename "$_domain." int id "$recordid"; then + _err "delete error" + return 1 + fi + + _contains "$response" "Success" +} + +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + if ! _dd_soap "listZones"; then + return 1 + fi + + i=1 + 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" ">$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 + +} + + +#method +_dd_soap() { + _method="$1" + shift + _urn="${_method}wsdl" + # put the parameters to xml + body=" + $DD_API_User + $DD_API_Key + " + while [ "$1" ]; do + _t="$1" + shift + _k="$1" + shift + _v="$1" + shift + body="$body<$_k xsi:type=\"xsd:$_t\">$_v" + done + body="$body" + _debug2 "SOAP request ${body}" + + # build SOAP XML + _xml=' + + '"$body"' +' + + _debug2 _xml "$_xml" + # set SOAP headers + _action="SOAPAction: \"urn:$_urn#$_method\"" + _debug2 "_action" "$_action" + export _H1="$_action" + export _H2="Content-Type: text/xml; charset=utf-8" + + _url="$_DD_BASE/$_method.php" + _debug "_url" "$_url" + if ! response="$(_post "${_xml}" "${_url}")"; then + _err "Error <$1>" + return 1 + fi + _debug2 "response" "$response" + response="$(echo "$response" | tr -d "\r\n" | _egrep_o ":${_method}Response .*:${_method}Response><")" + _debug2 "response" "$response" + return 0 +} + + + From 9a7c9e8d989fb8f0cfea976fb7e5b14f1a7d4743 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sat, 18 May 2019 18:20:16 +0200 Subject: [PATCH 398/403] remove unset --- notify/mail.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index 73f180df..5ad8e883 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -14,8 +14,6 @@ mail_send() { _debug "_content" "$_content" _debug "_statusCode" "$_statusCode" - unset -f _MAIL_BIN _MAIL_BODY _MAIL_CMD - MAIL_BIN="${MAIL_BIN:-$(_readaccountconf_mutable MAIL_BIN)}" if [ -n "$MAIL_BIN" ] && ! _exists "$MAIL_BIN"; then _err "It seems that the command $MAIL_BIN is not in path." From d83c9da8308decbdbd9cbab5b551189423c2004a Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sat, 18 May 2019 18:21:19 +0200 Subject: [PATCH 399/403] add clearaccountconf MAIL_BIN --- notify/mail.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notify/mail.sh b/notify/mail.sh index 5ad8e883..dbecc3a5 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -22,6 +22,8 @@ mail_send() { _MAIL_CMD=$(_mail_cmnd) if [ -n "$MAIL_BIN" ]; then _saveaccountconf_mutable MAIL_BIN "$MAIL_BIN" + else + _clearaccountconf "MAIL_BIN" fi _MAIL_BODY=$(_mail_body) From cf4c603362acee171e2786476a0b02ff2e1f3619 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 19 May 2019 08:05:40 +0800 Subject: [PATCH 400/403] fix format --- dnsapi/dns_durabledns.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dnsapi/dns_durabledns.sh b/dnsapi/dns_durabledns.sh index 5de1eaaa..9a05eb32 100644 --- a/dnsapi/dns_durabledns.sh +++ b/dnsapi/dns_durabledns.sh @@ -1,6 +1,5 @@ #!/usr/bin/env sh - #DD_API_User="xxxxx" #DD_API_Key="xxxxxx" @@ -40,7 +39,6 @@ dns_durabledns_add() { _contains "$response" "createRecordResponse" } - dns_durabledns_rm() { fulldomain=$1 txtvalue=$2 @@ -125,7 +123,6 @@ _get_root() { } - #method _dd_soap() { _method="$1" @@ -177,6 +174,3 @@ xmlns:xsd="http://www.w3.org/2001/XMLSchema"> _debug2 "response" "$response" return 0 } - - - From f6d6658de7e5c9b762f944e22ce583c94168e9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A1la?= Date: Sun, 19 May 2019 11:47:19 +0200 Subject: [PATCH 401/403] Fix saving token for DNS Active24 --- dnsapi/dns_active24.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_active24.sh b/dnsapi/dns_active24.sh index 90ffaf68..862f734f 100755 --- a/dnsapi/dns_active24.sh +++ b/dnsapi/dns_active24.sh @@ -129,7 +129,7 @@ _active24_init() { return 1 fi - _saveaccountconf_mutable ACTIVE24_Token "ACTIVE24_Token" + _saveaccountconf_mutable ACTIVE24_Token "$ACTIVE24_Token" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then From 688fe131c93c23ecc2a1510034a7c973201d2356 Mon Sep 17 00:00:00 2001 From: mjthompson Date: Tue, 21 May 2019 18:21:54 +0800 Subject: [PATCH 402/403] Fix typo --- 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 2ad3c819..246f4774 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -49,7 +49,7 @@ dns_aws_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _info "Geting existing records for $fulldomain" + _info "Getting existing records for $fulldomain" if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then return 1 fi From 09bce5e6d6be6b97b3c843b815087874e3e44a21 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Tue, 28 May 2019 08:47:33 +0800 Subject: [PATCH 403/403] sync (#2297) * Create LICENSE.md * remove _hostingde_parse_no_strip_whitespace function as this breaks API requests * Fix sessionid parsing on BSD * Make travis happy. (SC2020) * fix for https://github.com/Neilpang/acme.sh/issues/2286 * Notify mail update (#2293) * feat: disable e-mail validation if MAIL_NOVALIDATE is set * fix: expose _MAIL_BIN variable * fix: call _mail_body and _mail_cmnd directly to make sure that all used variables are exposed * fix: update notify/mail.sh Co-Authored-By: Matej Mihevc * fix: remove useless echo, quote eval --- LICENSE.md | 674 ++++++++++++++++++++++++++++++++++++++++ acme.sh | 2 + dnsapi/dns_hostingde.sh | 16 +- dnsapi/dns_netcup.sh | 2 +- notify/mail.sh | 48 ++- 5 files changed, 712 insertions(+), 30 deletions(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/acme.sh b/acme.sh index 60580f83..eb2194aa 100755 --- a/acme.sh +++ b/acme.sh @@ -4338,6 +4338,8 @@ $_authorizations_map" if [ "$status" = "pending" ]; then _info "Pending" + elif [ "$status" = "processing" ]; then + _info "Processing" else _err "$d:Verify error:$response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 1aa70394..9e3e5664 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -53,18 +53,6 @@ _hostingde_parse() { fi } -_hostingde_parse_no_strip_whitespace() { - find="${1}" - if [ "${2}" ]; then - notfind="${2}" - fi - if [ "${notfind}" ]; then - _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 - else - _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 - fi -} - _hostingde_getZoneConfig() { _info "Getting ZoneConfig" curZone="${fulldomain#*.}" @@ -97,12 +85,12 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") - zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateValues") + zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse "templateValues") if [ "$zoneConfigTemplateValues" != "null" ]; then _debug "Zone is tied to a template." zoneConfigTemplateValuesTemplateId=$(echo "${curResult}" | _hostingde_parse "templateId") - zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateName") + zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse "templateName") zoneConfigTemplateValuesTemplateReplacementsIPv4=$(echo "${curResult}" | _hostingde_parse "ipv4Replacement") zoneConfigTemplateValuesTemplateReplacementsIPv6=$(echo "${curResult}" | _hostingde_parse "ipv6Replacement") zoneConfigTemplateValuesTemplateReplacementsMailIPv4=$(echo "${curResult}" | _hostingde_parse "mailIpv4Replacement") diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index d5d7c22e..d519e4f7 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -117,7 +117,7 @@ dns_netcup_rm() { login() { tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') + sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4) _debug "$tmp" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" diff --git a/notify/mail.sh b/notify/mail.sh index dbecc3a5..ec9aa0de 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -5,6 +5,7 @@ #MAIL_BIN="sendmail" #MAIL_FROM="yyyy@gmail.com" #MAIL_TO="yyyy@gmail.com" +#MAIL_NOVALIDATE="" mail_send() { _subject="$1" @@ -14,22 +15,28 @@ mail_send() { _debug "_content" "$_content" _debug "_statusCode" "$_statusCode" + MAIL_NOVALIDATE="${MAIL_NOVALIDATE:-$(_readaccountconf_mutable MAIL_NOVALIDATE)}" + if [ -n "$MAIL_NOVALIDATE" ]; then + _saveaccountconf_mutable MAIL_NOVALIDATE 1 + else + _clearaccountconf "MAIL_NOVALIDATE" + fi + MAIL_BIN="${MAIL_BIN:-$(_readaccountconf_mutable MAIL_BIN)}" if [ -n "$MAIL_BIN" ] && ! _exists "$MAIL_BIN"; then _err "It seems that the command $MAIL_BIN is not in path." return 1 fi - _MAIL_CMD=$(_mail_cmnd) + _MAIL_BIN=$(_mail_bin) if [ -n "$MAIL_BIN" ]; then _saveaccountconf_mutable MAIL_BIN "$MAIL_BIN" else _clearaccountconf "MAIL_BIN" fi - _MAIL_BODY=$(_mail_body) MAIL_FROM="${MAIL_FROM:-$(_readaccountconf_mutable MAIL_FROM)}" if [ -n "$MAIL_FROM" ]; then - if ! _contains "$MAIL_FROM" "@"; then + if ! _mail_valid "$MAIL_FROM"; then _err "It seems that the MAIL_FROM=$MAIL_FROM is not a valid email address." return 1 fi @@ -39,7 +46,7 @@ mail_send() { MAIL_TO="${MAIL_TO:-$(_readaccountconf_mutable MAIL_TO)}" if [ -n "$MAIL_TO" ]; then - if ! _contains "$MAIL_TO" "@"; then + if ! _mail_valid "$MAIL_TO"; then _err "It seems that the MAIL_TO=$MAIL_TO is not a valid email address." return 1 fi @@ -55,8 +62,9 @@ mail_send() { contenttype="text/plain; charset=utf-8" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" - result=$({ echo "$_MAIL_BODY" | eval "$_MAIL_CMD"; } 2>&1) + result=$({ _mail_body | eval "$(_mail_cmnd)"; } 2>&1) + # shellcheck disable=SC2181 if [ $? -ne 0 ]; then _debug "mail send error." _err "$result" @@ -67,7 +75,7 @@ mail_send() { return 0 } -_mail_cmnd() { +_mail_bin() { if [ -n "$MAIL_BIN" ]; then _MAIL_BIN="$MAIL_BIN" elif _exists "sendmail"; then @@ -83,6 +91,10 @@ _mail_cmnd() { return 1 fi + echo "$_MAIL_BIN" +} + +_mail_cmnd() { case $(basename "$_MAIL_BIN") in sendmail) if [ -n "$MAIL_FROM" ]; then @@ -105,16 +117,22 @@ _mail_cmnd() { } _mail_body() { - if [ "$_MAIL_BIN" = "sendmail" ] || [ "$_MAIL_BIN" = "ssmtp" ]; then - if [ -n "$MAIL_FROM" ]; then - echo "From: $MAIL_FROM" - fi + case $(basename "$_MAIL_BIN") in + sendmail | ssmtp) + if [ -n "$MAIL_FROM" ]; then + echo "From: $MAIL_FROM" + fi - echo "To: $MAIL_TO" - echo "Subject: $subject" - echo "Content-Type: $contenttype" - echo - fi + echo "To: $MAIL_TO" + echo "Subject: $subject" + echo "Content-Type: $contenttype" + echo + ;; + esac echo "$_content" } + +_mail_valid() { + [ -n "$MAIL_NOVALIDATE" ] || _contains "$1" "@" +}