From 212d0f24d8d264c30969d5f19bb61c5840ae5108 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Tue, 31 Oct 2017 22:35:15 -0700 Subject: [PATCH 001/156] [cloudns] Add support for sub user IDs --- dnsapi/README.md | 7 +++++-- dnsapi/dns_cloudns.sh | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index cff59c7..7aeef01 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -409,10 +409,13 @@ acme.sh --issue --dns dns_dgon -d example.com -d www.example.com ## 21. Use ClouDNS.net API -You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/ +You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/. For security reasons, it's recommended to use a sub user ID that only has access to the necessary zones, as a regular API user has access to your entire account. ``` -export CLOUDNS_AUTH_ID=XXXXX +# Use this for a sub auth ID +export CLOUDNS_SUB_AUTH_ID=XXXXX +# Use this for a regular auth ID +#export CLOUDNS_AUTH_ID=XXXXX export CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" ``` diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index b1861b2..c459551 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -97,17 +97,19 @@ _dns_cloudns_init_check() { fi CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}" + CLOUDNS_SUB_AUTH_ID="${CLOUDNS_SUB_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_SUB_AUTH_ID)}" CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}" - if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then + if [ -z "$CLOUDNS_AUTH_ID$CLOUDNS_SUB_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then CLOUDNS_AUTH_ID="" + CLOUDNS_SUB_AUTH_ID="" CLOUDNS_AUTH_PASSWORD="" _err "You don't specify cloudns api id and password yet." _err "Please create you id and password and try again." return 1 fi - if [ -z "$CLOUDNS_AUTH_ID" ]; then - _err "CLOUDNS_AUTH_ID is not configured" + if [ -z "$CLOUDNS_AUTH_ID" ] && [ -z "$CLOUDNS_SUB_AUTH_ID" ]; then + _err "CLOUDNS_AUTH_ID or CLOUDNS_SUB_AUTH_ID is not configured" return 1 fi @@ -125,6 +127,7 @@ _dns_cloudns_init_check() { #save the api id and password to the account conf file. _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _saveaccountconf_mutable CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID" _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" CLOUDNS_INIT_CHECK_COMPLETED=1 @@ -168,12 +171,19 @@ _dns_cloudns_http_api_call() { method=$1 _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID" _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" + if [ ! -z "$CLOUDNS_SUB_AUTH_ID" ]; then + auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID" + else + auth_user="auth-id=$CLOUDNS_AUTH_ID" + fi; + if [ -z "$2" ]; then - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" + data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD" else - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" + data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" fi response="$(_get "$CLOUDNS_API/$method?$data")" From bab4f691c5bade8a50a88ef715b834c8ec18cb1e Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 12 Nov 2017 18:38:30 -0800 Subject: [PATCH 002/156] Fix lint warning --- dnsapi/dns_cloudns.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index c459551..4a1ae64 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -4,6 +4,7 @@ # Repository: https://github.com/ClouDNS/acme.sh/ #CLOUDNS_AUTH_ID=XXXXX +#CLOUDNS_SUB_AUTH_ID=XXXXX #CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" CLOUDNS_API="https://api.cloudns.net" @@ -178,7 +179,7 @@ _dns_cloudns_http_api_call() { auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID" else auth_user="auth-id=$CLOUDNS_AUTH_ID" - fi; + fi if [ -z "$2" ]; then data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD" From f62457a24e2f286f309c245b09bd52631f8e00f9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 13 Nov 2017 20:54:29 +0800 Subject: [PATCH 003/156] fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b45cc46..dda8eb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: openssl version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true; export PATH="$_old_path"; - else sudo apt-get install socat; + else sudo apt-get -y update && sudo apt-get install -y socat; fi script: From ceafe389afb406b92f77392fd7fe2005cb6b750f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 26 Nov 2017 20:57:02 +0800 Subject: [PATCH 004/156] fix https://github.com/Neilpang/acme.sh/issues/1109 --- dnsapi/dns_freedns.sh | 77 ++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 53da411..ae77d06 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -53,6 +53,8 @@ dns_freedns_add() { i="$(_math "$i" - 1)" sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" + _debug top_domain "$top_domain" + _debug sub_domain "$sub_domain" # Sometimes FreeDNS does not return the subdomain page but rather # returns a page regarding becoming a premium member. This usually # happens after a period of inactivity. Immediately trying again @@ -61,7 +63,6 @@ dns_freedns_add() { attempts=2 while [ "$attempts" -gt "0" ]; do attempts="$(_math "$attempts" - 1)" - htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" if [ "$?" != "0" ]; then if [ "$using_cached_cookies" = "true" ]; then @@ -70,19 +71,11 @@ dns_freedns_add() { fi return 1 fi + _debug2 htmlpage "$htmlpage" + + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '
' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $top_domain)" + _debug2 subdomain_csv "$subdomain_csv" - # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo "$htmlpage" \ - | grep -i -e ']*>/\n/Ig' \ - | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ - | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ - | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ - | grep 'edit.php?' \ - | grep "$top_domain")" # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the top domain we are looking for. # So all we should be left with is CSV of table of subdomains we are @@ -90,30 +83,32 @@ dns_freedns_add() { # Now we have to read through this table and extract the data we need lines="$(echo "$subdomain_csv" | wc -l)" - nl=' -' i=0 found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" - tmp="$(echo "$line" | cut -d ',' -f 1)" - if [ $found = 0 ] && _startswith "$tmp" "$top_domain"; then + line="$(echo "$subdomain_csv" | sed -n ${i}p)" + _debug2 line "$line" + if [ $found = 0 ] && _contains "$line" "$top_domain"; then # this line will contain DNSdomainid for the top_domain - DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')" + DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" + _debug2 DNSdomainid "$DNSdomainid" found=1 else # lines contain DNS records for all subdomains - DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" - DNStype="$(echo "$line" | cut -d ',' -f 3)" + DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _debug2 DNSname "$DNSname" + DNStype="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNStype "$DNStype" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" + DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" # Now get current value for the TXT record. This method may # not produce accurate results as the value field is truncated # on this webpage. To get full value we would need to load # another page. However we don't really need this so long as # there is only one TXT record for the acme challenge subdomain. - DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" + DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNSvalue "$DNSvalue" if [ $found != 0 ]; then break # we are breaking out of the loop at the first match of DNS name @@ -169,8 +164,7 @@ dns_freedns_add() { return 0 else # Delete the old TXT record (with the wrong value) - _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid" - if [ "$?" = "0" ]; then + if _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"; then # And add in new TXT record with the value provided _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" fi @@ -210,18 +204,9 @@ dns_freedns_rm() { return 1 fi - # Now convert the tables in the HTML to CSV. This litte gem from - # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv - subdomain_csv="$(echo "$htmlpage" \ - | grep -i -e ']*>/\n/Ig' \ - | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ - | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ - | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ - | grep 'edit.php?' \ - | grep "$fulldomain")" + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $fulldomain)" + _debug2 subdomain_csv "$subdomain_csv" + # The above beauty ends with striping out rows that do not have an # href to edit.php and do not have the domain name we are looking for. # So all we should be left with is CSV of table of subdomains we are @@ -229,19 +214,21 @@ dns_freedns_rm() { # Now we have to read through this table and extract the data we need lines="$(echo "$subdomain_csv" | wc -l)" - nl=' -' i=0 found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")" - DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')" - DNStype="$(echo "$line" | cut -d ',' -f 3)" + line="$(echo "$subdomain_csv" | sed -n ${i}p)" + _debug2 line "$line" + DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _debug2 DNSname "$DNSname" + DNStype="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNStype "$DNStype" if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then - DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')" - DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" - _debug "DNSvalue: $DNSvalue" + DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)" + _debug2 DNSdataid "$DNSdataid" + DNSvalue="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)" + _debug2 DNSvalue "$DNSvalue" # if [ "$DNSvalue" = "$txtvalue" ]; then # Testing value match fails. Website is truncating the value # field. So for now we will assume that there is only one TXT From b615cce92d69c0de937a15538ef764a21fda33b5 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Dec 2017 19:54:33 +0800 Subject: [PATCH 005/156] fix https://github.com/Neilpang/acme.sh/issues/1127 --- Dockerfile | 2 +- acme.sh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 97626ba..b286673 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine +FROM alpine:3.6 RUN apk update -f \ && apk --no-cache add -f \ diff --git a/acme.sh b/acme.sh index 9c8d6d4..98f4067 100755 --- a/acme.sh +++ b/acme.sh @@ -463,8 +463,7 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then fi _h2b() { - if _exists xxd; then - xxd -r -p + if _exists xxd && xxd -r -p 2>/dev/null; then return fi From 529cbc037900506969e996e98903d7e60b211751 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Dec 2017 12:51:51 +0800 Subject: [PATCH 006/156] run ci in docker --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dda8eb1..ab5632d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,14 @@ language: shell sudo: required +dist: trusty os: - linux - osx +services: + - docker + env: global: - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 @@ -30,7 +34,6 @@ install: openssl version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true; export PATH="$_old_path"; - else sudo apt-get -y update && sudo apt-get install -y socat; fi script: @@ -44,7 +47,7 @@ script: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi - cd .. - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest - - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi + - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi From dcf8457f4d4b6d80236557fbc77ecd7060f56ec0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Dec 2017 13:16:37 +0800 Subject: [PATCH 007/156] fix format --- dnsapi/dns_freedns.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index ae77d06..afd8b79 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -73,7 +73,7 @@ dns_freedns_add() { fi _debug2 htmlpage "$htmlpage" - subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $top_domain)" + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")" _debug2 subdomain_csv "$subdomain_csv" # The above beauty ends with striping out rows that do not have an @@ -87,7 +87,7 @@ dns_freedns_add() { found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | sed -n ${i}p)" + line="$(echo "$subdomain_csv" | sed -n "${i}p")" _debug2 line "$line" if [ $found = 0 ] && _contains "$line" "$top_domain"; then # this line will contain DNSdomainid for the top_domain @@ -204,7 +204,7 @@ dns_freedns_rm() { return 1 fi - subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep $fulldomain)" + subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")" _debug2 subdomain_csv "$subdomain_csv" # The above beauty ends with striping out rows that do not have an @@ -218,7 +218,7 @@ dns_freedns_rm() { found=0 while [ "$i" -lt "$lines" ]; do i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | sed -n ${i}p)" + line="$(echo "$subdomain_csv" | sed -n "${i}p")" _debug2 line "$line" DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)" _debug2 DNSname "$DNSname" From 9eeebb147f9747a68e567ac40e202c51dc7aa4a3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Dec 2017 20:57:25 +0800 Subject: [PATCH 008/156] fix osx build --- .travis.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab5632d..b6b5742 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,20 +22,10 @@ addons: install: - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then - brew update && brew install openssl socat; - brew info openssl; - ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; - ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; - ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl; - _old_path="$PATH"; - echo "PATH=$PATH"; - export PATH=""; - export ACME_OPENSSL_BIN="/usr/local/openssl"; - openssl version 2>&1 || true; - $ACME_OPENSSL_BIN version 2>&1 || true; - export PATH="$_old_path"; + brew update && brew install socat; + export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ; fi - + script: - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" - command -V openssl && openssl version From 0ca3141088ead8a638c5067519e6c97216f699cf Mon Sep 17 00:00:00 2001 From: Aarup Date: Wed, 6 Dec 2017 11:50:54 +0100 Subject: [PATCH 009/156] Added support for UnoEuro api --- dnsapi/dns_unoeuro.sh | 194 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 dnsapi/dns_unoeuro.sh diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh new file mode 100644 index 0000000..985b441 --- /dev/null +++ b/dnsapi/dns_unoeuro.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env sh + +# +#Uno_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# +#Uno_User="UExxxxxx" + +Uno_Api="https://api.unoeuro.com/1/$Uno_User/$Uno_Key" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_unoeuro_add() { + fulldomain=$1 + txtvalue=$2 + + Uno_Key="${Uno_Key:-$(_readaccountconf_mutable Uno_Key)}" + Uno_User="${Uno_User:-$(_readaccountconf_mutable Uno_User)}" + if [ -z "$Uno_Key" ] || [ -z "$Uno_User" ]; then + Uno_Key="" + Uno_User="" + _err "You haven't specified a UnoEuro api key and account yet." + _err "Please create your key and try again." + return 1 + fi + + if ! _contains "$Uno_User" "UE"; then + _err "It seems that the Uno_User=$Uno_User is not a valid email address." + _err "Please check and retry." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable Uno_Key "$Uno_Key" + _saveaccountconf_mutable Uno_User "$Uno_User" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _uno_rest GET "my/products/$h/dns/records" + + if ! _contains "$response" "\"status\": 200" >/dev/null; then + _err "Error" + return 1 + fi + + if ! _contains "$response" "$_sub_domain" >/dev/null; then + _info "Adding record" + if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then + if _contains "$response" "\"status\": 200" >/dev/null; then + _info "Added, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + else + _info "Updating record" + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -1 | _egrep_o "[0-9]{1,}") + _debug "record_id" "$record_id" + + _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" + if _contains "$response" "\"status\": 200" >/dev/null; then + _info "Updated, OK" + return 0 + fi + _err "Update error" + return 1 + fi + +} + +#fulldomain txtvalue +dns_unoeuro_rm() { + fulldomain=$1 + txtvalue=$2 + + Uno_Key="${Uno_Key:-$(_readaccountconf_mutable Uno_Key)}" + Uno_User="${Uno_User:-$(_readaccountconf_mutable Uno_User)}" + if [ -z "$Uno_Key" ] || [ -z "$Uno_User" ]; then + Uno_Key="" + Uno_User="" + _err "You haven't specified a UnoEuro api key and account yet." + _err "Please create your key and try again." + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _uno_rest GET "my/products/$h/dns/records" + + if ! _contains "$response" "\"status\": 200" >/dev/null; then + _err "Error" + return 1 + fi + + if ! _contains "$response" "$_sub_domain" >/dev/null; then + _info "Don't need to remove." + else + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -1 | _egrep_o "[0-9]{1,}") + _debug "record_id" "$record_id" + + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + + if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" "\"status\": 200" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if ! _uno_rest GET "my/products/$h/dns/records"; then + return 1 + fi + + if _contains "$response" "\"status\": 200" >/dev/null; then + _domain_id=$h + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_uno_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + #export _H1="X-Auth-Email: $Uno_User" + #export _H2="X-Auth-Key: $Uno_Key" + export _H1="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$Uno_Api/$ep" "" "$m")" + else + response="$(_get "$Uno_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 70702e41e994ce98dac23b06bf59e2bbcdf4f560 Mon Sep 17 00:00:00 2001 From: Aarup Date: Wed, 6 Dec 2017 11:55:29 +0100 Subject: [PATCH 010/156] Use _head_n --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 985b441..4774187 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -65,7 +65,7 @@ dns_unoeuro_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" @@ -114,7 +114,7 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Don't need to remove." else - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | head -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" if [ -z "$record_id" ]; then From 78712245f70800f4bd4875732271b9e96af1c4f9 Mon Sep 17 00:00:00 2001 From: Aarup Date: Wed, 6 Dec 2017 12:13:40 +0100 Subject: [PATCH 011/156] Add UnoEuro to README --- README.md | 1 + dnsapi/README.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index 7d4353c..9e64e61 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,7 @@ You don't have to do anything manually! 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) 1. Hurricane Electric DNS service (https://dns.he.net) +1. UnoEuro API (https://www.unoeuro.com/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index cff59c7..5fdcd84 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -602,6 +602,22 @@ The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/accoun Please report any issues to https://github.com/angel333/acme.sh or to . +## 32. Use UnoEuro API to automatically issue cert + +First you need to login to your UnoEuro account to get your API key. + +``` +export Uno_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +export Uno_User="UExxxxxx" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_unoeuro -d example.com -d www.example.com +``` + +The `Uno_Key` and `Uno_User` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From 657334fb67e25b5503d02fc9abba2f5a2dbca410 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 11:47:01 +0100 Subject: [PATCH 012/156] Add support for inwx.de API --- README.md | 1 + dnsapi/README.md | 17 +++ dnsapi/dns_inwx.sh | 365 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100755 dnsapi/dns_inwx.sh diff --git a/README.md b/README.md index 7d4353c..e8f9464 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,7 @@ You don't have to do anything manually! 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) 1. Hurricane Electric DNS service (https://dns.he.net) +1. INWX (https://www.inwx.de/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index cff59c7..768f69a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -602,6 +602,23 @@ The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/accoun Please report any issues to https://github.com/angel333/acme.sh or to . +## 31. Use INWX + +INWX offers an xmlrpc api with your standard login credentials, set them like so: + +``` +export INWX_User="yourusername" +export INWX_Password="password" +``` + +Then you can issue your certificates with: + +``` +acme.sh --issue --dns dns_inwx -d example.com -d www.example.com +``` + +The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh new file mode 100755 index 0000000..db5b18c --- /dev/null +++ b/dnsapi/dns_inwx.sh @@ -0,0 +1,365 @@ +#!/usr/local/bin/bash + +# +#INWX_User="username" +# +#INWX_Password="password" + +INWX_Api="https://api.domrobot.com/xmlrpc/" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_inwx_add() { + fulldomain=$1 + txtvalue=$2 + + INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}" + INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}" + if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then + INWX_User="" + INWX_Password="" + _err "You don't specify inwx user and password yet." + _err "Please create you key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable INWX_User "$INWX_User" + _saveaccountconf_mutable INWX_Password "$INWX_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + + + printf -v xml_content ' + + nameserver.info + + + + + + domain + + %s + + + + type + + TXT + + + + name + + %s + + + + + + + ' $_domain $_sub_domain + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + _err "Error could net get txt records" + return 1 + fi + + if ! printf "%s" "$response" | grep "count" -q; then + _info "Adding record" + _inwx_add_record $_domain $_sub_domain $txtvalue + else + _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _info "Updating record" + _inwx_update_record $_record_id $txtvalue + fi + +} + +#fulldomain txtvalue +dns_inwx_rm() { + + fulldomain=$1 + txtvalue=$2 + + INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}" + INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}" + if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then + INWX_User="" + INWX_Password="" + _err "You don't specify inwx user and password yet." + _err "Please create you key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable INWX_User "$INWX_User" + _saveaccountconf_mutable INWX_Password "$INWX_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + + + printf -v xml_content ' + + nameserver.info + + + + + + domain + + %s + + + + type + + TXT + + + + name + + %s + + + + + + + ' $_domain $_sub_domain + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + _err "Error could not get txt records" + return 1 + fi + + if ! printf "%s" "$response" | grep "count" -q; then + _info "Do not need to delete record" + else + _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _info "Deleting record" + _inwx_delete_record $_record_id + fi + + +} + +#################### Private functions below ################################## + +_inwx_login() { + + printf -v xml_content ' + + account.login + + + + + + user + + %s + + + + pass + + %s + + + + + + + ' $INWX_User $INWX_Password + + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')" + +} + +_get_root() { + domain=$1 + _debug "get root" + + domain=$1 + i=2 + p=1 + + + export _H1=$(_inwx_login) + printf -v xml_content ' + + nameserver.list + ' + + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if _contains "$response" "$h"; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$h" + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 + +} + +_inwx_delete_record() { + record_id=$1 + printf -v xml_content ' + + nameserver.deleteRecord + + + + + + id + + %s + + + + + + + ' $record_id + + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then + _err "Error" + return 1 + fi + return 0 + + +} + +_inwx_update_record() { + record_id=$1 + txtval=$2 + printf -v xml_content ' + + nameserver.updateRecord + + + + + + content + + %s + + + + id + + %s + + + + + + + ' $txtval $record_id + + + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then + _err "Error" + return 1 + fi + return 0 + +} + +_inwx_add_record() { + + domain=$1 + sub_domain=$2 + txtval=$3 + + _debug domain: $domain + _debug value: $txtval + _debug subd: $sub_domain + + printf -v xml_content ' + + nameserver.createRecord + + + + + + domain + + %s + + + + type + + TXT + + + + content + + %s + + + + name + + %s + + + + + + + ' $domain $txtval $sub_domain + + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" + + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then + _err "Error" + return 1 + fi + return 0 +} From a8202d4b3780e04b3b68cc7196ee47871e63ee3d Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 12:05:03 +0100 Subject: [PATCH 013/156] Fix CI issues --- dnsapi/dns_inwx.sh | 64 +++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index db5b18c..9e2302b 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -35,11 +35,9 @@ dns_inwx_add() { fi _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - - printf -v xml_content ' + printf -v xml_content ' nameserver.info @@ -68,21 +66,21 @@ dns_inwx_add() { - ' $_domain $_sub_domain + ' "$_domain" "$_sub_domain" response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error could net get txt records" return 1 fi if ! printf "%s" "$response" | grep "count" -q; then _info "Adding record" - _inwx_add_record $_domain $_sub_domain $txtvalue + _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" else - _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') _info "Updating record" - _inwx_update_record $_record_id $txtvalue + _inwx_update_record "$_record_id" "$txtvalue" fi } @@ -117,8 +115,7 @@ dns_inwx_rm() { _debug "Getting txt records" - - printf -v xml_content ' + printf -v xml_content ' nameserver.info @@ -147,10 +144,10 @@ dns_inwx_rm() { - ' $_domain $_sub_domain + ' "$_domain" "$_sub_domain" response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - if ! printf "%s" "$response" | grep "Command completed successfully" > /dev/null; then + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error could not get txt records" return 1 fi @@ -158,12 +155,11 @@ dns_inwx_rm() { if ! printf "%s" "$response" | grep "count" -q; then _info "Do not need to delete record" else - _record_id=$(printf '%s' $response | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') _info "Deleting record" - _inwx_delete_record $_record_id + _inwx_delete_record "$_record_id" fi - } #################### Private functions below ################################## @@ -196,26 +192,26 @@ _inwx_login() { ' $INWX_User $INWX_Password response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - + printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')" } _get_root() { - domain=$1 + domain=$1 _debug "get root" domain=$1 i=2 p=1 - - export _H1=$(_inwx_login) + _H1=$(_inwx_login) + export _H1 printf -v xml_content ' nameserver.list ' - + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -256,17 +252,16 @@ _inwx_delete_record() { - ' $record_id - + ' "$record_id" + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error" return 1 fi return 0 - - + } _inwx_update_record() { @@ -295,17 +290,16 @@ _inwx_update_record() { - ' $txtval $record_id - - + ' "$txtval" "$record_id" + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error" return 1 fi return 0 - + } _inwx_add_record() { @@ -314,10 +308,6 @@ _inwx_add_record() { sub_domain=$2 txtval=$3 - _debug domain: $domain - _debug value: $txtval - _debug subd: $sub_domain - printf -v xml_content ' nameserver.createRecord @@ -353,10 +343,10 @@ _inwx_add_record() { - ' $domain $txtval $sub_domain - + ' "$domain" "$txtval" "$sub_domain" + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error" return 1 From ecba959dd99cda64d72c88c1d9c7d77894f72f80 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 13:07:05 +0100 Subject: [PATCH 014/156] Fix missed whitespaces --- dnsapi/dns_inwx.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 9e2302b..b02d1fb 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -155,7 +155,7 @@ dns_inwx_rm() { if ! printf "%s" "$response" | grep "count" -q; then _info "Do not need to delete record" else - _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') _info "Deleting record" _inwx_delete_record "$_record_id" fi @@ -253,7 +253,7 @@ _inwx_delete_record() { ' "$record_id" - + response="$(_post "$xml_content" "$INWX_Api" "" "POST")" if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then @@ -261,7 +261,7 @@ _inwx_delete_record() { return 1 fi return 0 - + } _inwx_update_record() { @@ -293,13 +293,13 @@ _inwx_update_record() { ' "$txtval" "$record_id" response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - + if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then _err "Error" return 1 fi return 0 - + } _inwx_add_record() { From a00169451fc21653763efefd0dc7938362a0e5fa Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 13:10:59 +0100 Subject: [PATCH 015/156] Change bash to sh to fit project requirements --- dnsapi/dns_inwx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index b02d1fb..78e101e 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -1,4 +1,4 @@ -#!/usr/local/bin/bash +#!/usr/local/bin/sh # #INWX_User="username" From 4a9f607d318a6a5e451f6962e44697c857580eea Mon Sep 17 00:00:00 2001 From: JAA Date: Thu, 7 Dec 2017 13:53:27 +0100 Subject: [PATCH 016/156] Cleanup --- dnsapi/dns_unoeuro.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 4774187..f48b8b9 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -5,7 +5,7 @@ # #Uno_User="UExxxxxx" -Uno_Api="https://api.unoeuro.com/1/$Uno_User/$Uno_Key" +Uno_Api="https://api.unoeuro.com/1" ######## Public functions ##################### @@ -25,7 +25,7 @@ dns_unoeuro_add() { fi if ! _contains "$Uno_User" "UE"; then - _err "It seems that the Uno_User=$Uno_User is not a valid email address." + _err "It seems that the Uno_User=$Uno_User is not a valid username." _err "Please check and retry." return 1 fi @@ -94,6 +94,12 @@ dns_unoeuro_rm() { return 1 fi + if ! _contains "$Uno_User" "UE"; then + _err "It seems that the Uno_User=$Uno_User is not a valid username." + _err "Please check and retry." + return 1 + fi + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -174,15 +180,13 @@ _uno_rest() { data="$3" _debug "$ep" - #export _H1="X-Auth-Email: $Uno_User" - #export _H2="X-Auth-Key: $Uno_Key" export _H1="Content-Type: application/json" if [ "$m" != "GET" ]; then _debug data "$data" - response="$(_post "$data" "$Uno_Api/$ep" "" "$m")" + response="$(_post "$data" "$Uno_Api/$Uno_User/$Uno_Key/$ep" "" "$m")" else - response="$(_get "$Uno_Api/$ep")" + response="$(_get "$Uno_Api/$Uno_User/$Uno_Key/$ep")" fi if [ "$?" != "0" ]; then From b91c0a0616f11f3475d21269883cf3107201581f Mon Sep 17 00:00:00 2001 From: JAA Date: Thu, 7 Dec 2017 13:53:40 +0100 Subject: [PATCH 017/156] Don't use grep -B --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index f48b8b9..d19e9eb 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -65,7 +65,7 @@ dns_unoeuro_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" @@ -120,7 +120,7 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Don't need to remove." else - record_id=$(echo "$response" | grep -B 1 "$_sub_domain" | _head_n -1 | _egrep_o "[0-9]{1,}") + record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" if [ -z "$record_id" ]; then From 9a1f769828c426b26589110b70f324bde4e6c7e2 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 14:18:54 +0100 Subject: [PATCH 018/156] Avoid usage of `sed -E` `grep -q` and `printf -v` --- dnsapi/dns_inwx.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 78e101e..2a2895f 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -37,7 +37,7 @@ dns_inwx_add() { _debug _domain "$_domain" _debug "Getting txt records" - printf -v xml_content ' + xml_content=$(printf ' nameserver.info @@ -66,7 +66,7 @@ dns_inwx_add() { - ' "$_domain" "$_sub_domain" + ' "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then @@ -74,11 +74,11 @@ dns_inwx_add() { return 1 fi - if ! printf "%s" "$response" | grep "count" -q; then + if ! printf "%s" "$response" | grep "count" >/dev/null; then _info "Adding record" _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" else - _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') _info "Updating record" _inwx_update_record "$_record_id" "$txtvalue" fi @@ -115,7 +115,7 @@ dns_inwx_rm() { _debug "Getting txt records" - printf -v xml_content ' + xml_content=$(printf ' nameserver.info @@ -144,7 +144,7 @@ dns_inwx_rm() { - ' "$_domain" "$_sub_domain" + ' "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then @@ -152,10 +152,10 @@ dns_inwx_rm() { return 1 fi - if ! printf "%s" "$response" | grep "count" -q; then + if ! printf "%s" "$response" | grep "count" >/dev/null; then _info "Do not need to delete record" else - _record_id=$(printf '%s' "$response" | sed -nE 's/.*(record){1}(.*)(id<\/name>)([0-9]+){1}.*/\4/p') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') _info "Deleting record" _inwx_delete_record "$_record_id" fi @@ -166,7 +166,7 @@ dns_inwx_rm() { _inwx_login() { - printf -v xml_content ' + xml_content=$(printf ' account.login @@ -189,7 +189,7 @@ _inwx_login() { - ' $INWX_User $INWX_Password + ' $INWX_User $INWX_Password) response="$(_post "$xml_content" "$INWX_Api" "" "POST")" @@ -207,7 +207,7 @@ _get_root() { _H1=$(_inwx_login) export _H1 - printf -v xml_content ' + xml_content=' nameserver.list ' @@ -235,7 +235,7 @@ _get_root() { _inwx_delete_record() { record_id=$1 - printf -v xml_content ' + xml_content=$(printf ' nameserver.deleteRecord @@ -252,7 +252,7 @@ _inwx_delete_record() { - ' "$record_id" + ' "$record_id") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" @@ -267,7 +267,7 @@ _inwx_delete_record() { _inwx_update_record() { record_id=$1 txtval=$2 - printf -v xml_content ' + xml_content=$(printf ' nameserver.updateRecord @@ -290,7 +290,7 @@ _inwx_update_record() { - ' "$txtval" "$record_id" + ' "$txtval" "$record_id") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" @@ -308,7 +308,7 @@ _inwx_add_record() { sub_domain=$2 txtval=$3 - printf -v xml_content ' + xml_content=$(printf ' nameserver.createRecord @@ -343,7 +343,7 @@ _inwx_add_record() { - ' "$domain" "$txtval" "$sub_domain" + ' "$domain" "$txtval" "$sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" From 5911594906f07aa2db4b7d3256388e7514f28b92 Mon Sep 17 00:00:00 2001 From: speedmann Date: Thu, 7 Dec 2017 14:21:37 +0100 Subject: [PATCH 019/156] Fix egrep invocation --- dnsapi/dns_inwx.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 2a2895f..ae20074 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -78,7 +78,7 @@ dns_inwx_add() { _info "Adding record" _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" else - _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+') _info "Updating record" _inwx_update_record "$_record_id" "$txtvalue" fi @@ -155,7 +155,7 @@ dns_inwx_rm() { if ! printf "%s" "$response" | grep "count" >/dev/null; then _info "Do not need to delete record" else - _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | egrep -o '[0-9]+') + _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+') _info "Deleting record" _inwx_delete_record "$_record_id" fi From f87890cb4b534feecd23dd2e7042e8601f15a3c3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 7 Dec 2017 21:32:17 +0800 Subject: [PATCH 020/156] v2.7.6 fix ECC 384 signature https://github.com/Neilpang/acme.sh/issues/1130 https://github.com/Neilpang/acme.sh/issues/990 --- acme.sh | 149 ++++++++++++++++++++++---------------------------------- 1 file changed, 57 insertions(+), 92 deletions(-) diff --git a/acme.sh b/acme.sh index 98f4067..0755f78 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.5 +VER=2.7.6 PROJECT_NAME="acme.sh" @@ -15,7 +15,7 @@ _SUB_FOLDERS="dnsapi deploy" _OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" -DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf" + DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -900,17 +900,11 @@ _sign() { fi _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile " - if [ "$alg" = "sha256" ]; then - _sign_openssl="$_sign_openssl -$alg" - else - _err "$alg is not supported yet" - return 1 - fi if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then - $_sign_openssl | _base64 + $_sign_openssl -$alg | _base64 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then - if ! _signedECText="$($_sign_openssl | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then + if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then _err "Sign failed: $_sign_openssl" _err "Key file: $keyfile" _err "Key content:$(wc -l <"$keyfile") lines" @@ -1433,7 +1427,11 @@ _calcjwk() { _debug "EC key" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" _debug3 crv "$crv" - + __ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2) + if [ "$__ECC_KEY_LEN" = "521" ]; then + __ECC_KEY_LEN=512 + fi + _debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN" if [ -z "$crv" ]; then _debug "Let's try ASN1 OID" crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")" @@ -1441,12 +1439,15 @@ _calcjwk() { case "${crv_oid}" in "prime256v1") crv="P-256" + __ECC_KEY_LEN=256 ;; "secp384r1") crv="P-384" + __ECC_KEY_LEN=384 ;; "secp521r1") crv="P-521" + __ECC_KEY_LEN=512 ;; *) _err "ECC oid : $crv_oid" @@ -1488,9 +1489,9 @@ _calcjwk() { jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}' _debug3 jwk "$jwk" - JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}' + JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' else _err "Only RSA or EC key is supported." return 1 @@ -2160,17 +2161,6 @@ _initAPI() { _api_server="${1:-$ACME_DIRECTORY}" _debug "_init api for server: $_api_server" - if [ "$_api_server" = "$DEFAULT_CA" ]; then - #just for performance, hardcode the default entry points - export ACME_KEY_CHANGE="https://acme-v01.api.letsencrypt.org/acme/key-change" - export ACME_NEW_AUTHZ="https://acme-v01.api.letsencrypt.org/acme/new-authz" - export ACME_NEW_ORDER="https://acme-v01.api.letsencrypt.org/acme/new-cert" - export ACME_NEW_ORDER_RES="new-cert" - export ACME_NEW_ACCOUNT="https://acme-v01.api.letsencrypt.org/acme/new-reg" - export ACME_NEW_ACCOUNT_RES="new-reg" - export ACME_REVOKE_CERT="https://acme-v01.api.letsencrypt.org/acme/revoke-cert" - fi - if [ -z "$ACME_NEW_ACCOUNT" ]; then response=$(_get "$_api_server") if [ "$?" != "0" ]; then @@ -2209,14 +2199,18 @@ _initAPI() { ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) export ACME_NEW_NONCE + + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) + export ACME_AGREEMENT - fi + _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" + _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ" + _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER" + _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" + _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" + _debug "ACME_AGREEMENT" "$ACME_AGREEMENT" - _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" - _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ" - _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER" - _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" - _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" + fi } #[domain] [keylength or isEcc flag] @@ -3058,7 +3052,7 @@ __calc_account_thumbprint() { _regAccount() { _initpath _reg_length="$1" - + _debug3 _regAccount "$_regAccount" mkdir -p "$CA_DIR" if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" @@ -3081,75 +3075,46 @@ _regAccount() { return 1 fi _initAPI - _updateTos="" _reg_res="$ACME_NEW_ACCOUNT_RES" - while true; do - _debug AGREEMENT "$AGREEMENT" + regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + fi - regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}' + if [ -z "$_updateTos" ]; then + _info "Registering account" - if [ "$ACCOUNT_EMAIL" ]; then - regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' - fi + if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then + _err "Register account Error: $response" + return 1 + fi - if [ -z "$_updateTos" ]; then - _info "Registering account" + if [ "$code" = "" ] || [ "$code" = '201' ]; then + echo "$response" >"$ACCOUNT_JSON_PATH" + _info "Registered" + elif [ "$code" = '409' ]; then + _info "Already registered" + else + _err "Register account Error: $response" + return 1 + fi - if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then - _err "Register account Error: $response" - return 1 - fi + _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _debug "_accUri" "$_accUri" + _savecaconf "ACCOUNT_URL" "$_accUri" - if [ "$code" = "" ] || [ "$code" = '201' ]; then - echo "$response" >"$ACCOUNT_JSON_PATH" - _info "Registered" - elif [ "$code" = '409' ]; then - _info "Already registered" - else - _err "Register account Error: $response" - return 1 - fi + echo "$response" >"$ACCOUNT_JSON_PATH" + CA_KEY_HASH="$(__calcAccountKeyHash)" + _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" + _savecaconf CA_KEY_HASH "$CA_KEY_HASH" - _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" - _debug "_accUri" "$_accUri" - _savecaconf "ACCOUNT_URL" "$_accUri" - _tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')" - _debug "_tos" "$_tos" - if [ -z "$_tos" ]; then - _debug "Use default tos: $DEFAULT_AGREEMENT" - _tos="$DEFAULT_AGREEMENT" - fi - if [ "$_tos" != "$AGREEMENT" ]; then - _updateTos=1 - AGREEMENT="$_tos" - _reg_res="reg" - continue - fi + if [ "$code" = '403' ]; then + _err "It seems that the account key is already deactivated, please use a new account key." + return 1 + fi - else - _debug "Update tos: $_tos" - if ! _send_signed_request "$_accUri" "$regjson"; then - _err "Update tos error." - return 1 - fi - if [ "$code" = '202' ]; then - _info "Update account tos info success." - echo "$response" >"$ACCOUNT_JSON_PATH" - CA_KEY_HASH="$(__calcAccountKeyHash)" - _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" - _savecaconf CA_KEY_HASH "$CA_KEY_HASH" - elif [ "$code" = '403' ]; then - _err "It seems that the account key is already deactivated, please use a new account key." - return 1 - else - _err "Update account error." - return 1 - fi - fi - ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" - _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" - return 0 - done + ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" + _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" } From db3043553cbfb9ca05f5a6807590603ee381a80a Mon Sep 17 00:00:00 2001 From: JAA Date: Thu, 7 Dec 2017 14:52:09 +0100 Subject: [PATCH 021/156] Also don't use sed --- dnsapi/dns_unoeuro.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index d19e9eb..6b582dd 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -53,6 +53,7 @@ dns_unoeuro_add() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Adding record" + if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then if _contains "$response" "\"status\": 200" >/dev/null; then _info "Added, OK" @@ -65,7 +66,9 @@ dns_unoeuro_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") + record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) + record_line_number=$(($record_line_number-1)) + record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}" @@ -76,7 +79,6 @@ dns_unoeuro_add() { _err "Update error" return 1 fi - } #fulldomain txtvalue @@ -120,7 +122,9 @@ dns_unoeuro_rm() { if ! _contains "$response" "$_sub_domain" >/dev/null; then _info "Don't need to remove." else - record_id=$(echo "$response" | sed -n -e "/$_sub_domain/{x;p;d;}" -e x | _head_n -1 | _egrep_o "[0-9]{1,}") + record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) + record_line_number=$(($record_line_number-1)) + record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" if [ -z "$record_id" ]; then From 1f635b90e76a5fb61a92f038a20a78b5381b1ea2 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 08:26:52 +0100 Subject: [PATCH 022/156] Try to fix build --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 6b582dd..8a1928d 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -67,7 +67,7 @@ dns_unoeuro_add() { else _info "Updating record" record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number-1)) + record_line_number=$(($record_line_number - 1)) record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" @@ -123,7 +123,7 @@ dns_unoeuro_rm() { _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number-1)) + record_line_number=$(($record_line_number - 1)) record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" From 3f1e6c128ff0d1af3fc1fc896f115821b55af885 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 08:31:24 +0100 Subject: [PATCH 023/156] Try again --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 8a1928d..be0fadb 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -67,7 +67,7 @@ dns_unoeuro_add() { else _info "Updating record" record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number - 1)) + record_line_number=$((record_line_number - 1)) record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" @@ -123,7 +123,7 @@ dns_unoeuro_rm() { _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$(($record_line_number - 1)) + record_line_number=$((record_line_number - 1)) record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" From ca7ebd933324db4c9ea4a383321413c6deb399f3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 8 Dec 2017 19:49:18 +0800 Subject: [PATCH 024/156] fix typo --- acme.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0755f78..2affeb9 100755 --- a/acme.sh +++ b/acme.sh @@ -3081,8 +3081,7 @@ _regAccount() { regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' fi - if [ -z "$_updateTos" ]; then - _info "Registering account" + _info "Registering account" if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then _err "Register account Error: $response" From 4249e13eb4089deb518d0e54e10b8bd24d2f5640 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 8 Dec 2017 19:54:25 +0800 Subject: [PATCH 025/156] fix format --- acme.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 2affeb9..88273cf 100755 --- a/acme.sh +++ b/acme.sh @@ -16,7 +16,6 @@ _SUB_FOLDERS="dnsapi deploy" _OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" - DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -2199,7 +2198,7 @@ _initAPI() { ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) export ACME_NEW_NONCE - + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) export ACME_AGREEMENT From dbc3ad130461a4c9dde8b2ce778ae1881960508f Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 12:59:02 +0100 Subject: [PATCH 026/156] use _math --- dnsapi/dns_unoeuro.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index be0fadb..1479d54 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -67,7 +67,7 @@ dns_unoeuro_add() { else _info "Updating record" record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$((record_line_number - 1)) + record_line_number=$(_math "$record_line_number" - 1) record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" @@ -123,7 +123,7 @@ dns_unoeuro_rm() { _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) - record_line_number=$((record_line_number - 1)) + record_line_number=$(_math "$record_line_number" - 1) record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}") _debug "record_id" "$record_id" From f763e1edd7c677e9d313288b8426223f86ca9e49 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 13:20:25 +0100 Subject: [PATCH 027/156] fix contains usage --- dnsapi/dns_unoeuro.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index 1479d54..f75de39 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -114,12 +114,12 @@ dns_unoeuro_rm() { _debug "Getting txt records" _uno_rest GET "my/products/$h/dns/records" - if ! _contains "$response" "\"status\": 200" >/dev/null; then + if ! _contains "$response" "\"status\": 200"; then _err "Error" return 1 fi - if ! _contains "$response" "$_sub_domain" >/dev/null; then + if ! _contains "$response" "$_sub_domain"; then _info "Don't need to remove." else record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) @@ -163,7 +163,7 @@ _get_root() { return 1 fi - if _contains "$response" "\"status\": 200" >/dev/null; then + if _contains "$response" "\"status\": 200"; then _domain_id=$h if [ "$_domain_id" ]; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) From f9b8d7a9d8316ad0e6b663572f552bb32a6236a4 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 13:22:17 +0100 Subject: [PATCH 028/156] renamed uno_user and uno_key --- dnsapi/dns_unoeuro.sh | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh index f75de39..a3803a2 100644 --- a/dnsapi/dns_unoeuro.sh +++ b/dnsapi/dns_unoeuro.sh @@ -1,9 +1,9 @@ #!/usr/bin/env sh # -#Uno_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" # -#Uno_User="UExxxxxx" +#UNO_User="UExxxxxx" Uno_Api="https://api.unoeuro.com/1" @@ -14,25 +14,25 @@ dns_unoeuro_add() { fulldomain=$1 txtvalue=$2 - Uno_Key="${Uno_Key:-$(_readaccountconf_mutable Uno_Key)}" - Uno_User="${Uno_User:-$(_readaccountconf_mutable Uno_User)}" - if [ -z "$Uno_Key" ] || [ -z "$Uno_User" ]; then - Uno_Key="" - Uno_User="" + UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}" + UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}" + if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then + UNO_Key="" + UNO_User="" _err "You haven't specified a UnoEuro api key and account yet." _err "Please create your key and try again." return 1 fi - if ! _contains "$Uno_User" "UE"; then - _err "It seems that the Uno_User=$Uno_User is not a valid username." + if ! _contains "$UNO_User" "UE"; then + _err "It seems that the UNO_User=$UNO_User is not a valid username." _err "Please check and retry." return 1 fi #save the api key and email to the account conf file. - _saveaccountconf_mutable Uno_Key "$Uno_Key" - _saveaccountconf_mutable Uno_User "$Uno_User" + _saveaccountconf_mutable UNO_Key "$UNO_Key" + _saveaccountconf_mutable UNO_User "$UNO_User" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -86,18 +86,18 @@ dns_unoeuro_rm() { fulldomain=$1 txtvalue=$2 - Uno_Key="${Uno_Key:-$(_readaccountconf_mutable Uno_Key)}" - Uno_User="${Uno_User:-$(_readaccountconf_mutable Uno_User)}" - if [ -z "$Uno_Key" ] || [ -z "$Uno_User" ]; then - Uno_Key="" - Uno_User="" + UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}" + UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}" + if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then + UNO_Key="" + UNO_User="" _err "You haven't specified a UnoEuro api key and account yet." _err "Please create your key and try again." return 1 fi - if ! _contains "$Uno_User" "UE"; then - _err "It seems that the Uno_User=$Uno_User is not a valid username." + if ! _contains "$UNO_User" "UE"; then + _err "It seems that the UNO_User=$UNO_User is not a valid username." _err "Please check and retry." return 1 fi @@ -188,9 +188,9 @@ _uno_rest() { if [ "$m" != "GET" ]; then _debug data "$data" - response="$(_post "$data" "$Uno_Api/$Uno_User/$Uno_Key/$ep" "" "$m")" + response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")" else - response="$(_get "$Uno_Api/$Uno_User/$Uno_Key/$ep")" + response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")" fi if [ "$?" != "0" ]; then From fb6e0658cf23a79cf4c16a3898fd57d281be0a32 Mon Sep 17 00:00:00 2001 From: Aarup Date: Fri, 8 Dec 2017 14:09:04 +0100 Subject: [PATCH 029/156] update README --- dnsapi/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 5fdcd84..8a486ae 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -607,8 +607,8 @@ Please report any issues to https://github.com/angel333/acme.sh or to Date: Sat, 9 Dec 2017 14:13:05 +0100 Subject: [PATCH 030/156] Fix shebang --- dnsapi/dns_inwx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index ae20074..74440bd 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -1,4 +1,4 @@ -#!/usr/local/bin/sh +#!/usr/bin/env sh # #INWX_User="username" From f7c346de0928fcc57eb0c8f7561fada69118022d Mon Sep 17 00:00:00 2001 From: speedmann Date: Sat, 9 Dec 2017 14:20:36 +0100 Subject: [PATCH 031/156] Fix order in README and add link to inwx.de and apidocs --- README.md | 2 +- dnsapi/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8420c02..eb12291 100644 --- a/README.md +++ b/README.md @@ -339,8 +339,8 @@ You don't have to do anything manually! 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) 1. Hurricane Electric DNS service (https://dns.he.net) -1. INWX (https://www.inwx.de/) 1. UnoEuro API (https://www.unoeuro.com/) +1. INWX (https://www.inwx.de/) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 11b6b3a..c16e759 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -620,7 +620,7 @@ The `UNO_Key` and `UNO_User` will be saved in `~/.acme.sh/account.conf` and will ## 33. Use INWX -INWX offers an xmlrpc api with your standard login credentials, set them like so: +[INWX](https://www.inwx.de/) offers an [xmlrpc api](https://www.inwx.de/de/help/apidoc) with your standard login credentials, set them like so: ``` export INWX_User="yourusername" From 8201458332ea5898177118097621dbac842ad64f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 9 Dec 2017 21:50:45 +0800 Subject: [PATCH 032/156] fix https://github.com/Neilpang/acme.sh/issues/1123 --- acme.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 88273cf..472975a 100755 --- a/acme.sh +++ b/acme.sh @@ -4308,7 +4308,12 @@ _installcert() { if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then cp "$_real_key" "$_backup_path/key.bak" fi - cat "$CERT_KEY_PATH" >"$_real_key" + if [ -f "$_real_key" ]; then + cat "$CERT_KEY_PATH" >"$_real_key" + else + cat "$CERT_KEY_PATH" >"$_real_key" + chmod 700 "$_real_key" + fi fi if [ "$_real_fullchain" ]; then From ae29929714b8cb8e4b7c612a354b56433f8ac47c Mon Sep 17 00:00:00 2001 From: Jens Hartlep Date: Sat, 9 Dec 2017 19:32:53 +0100 Subject: [PATCH 033/156] added dns api for servercow --- dnsapi/README.md | 16 ++++ dnsapi/dns_servercow.sh | 170 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100755 dnsapi/dns_servercow.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index c16e759..d357c05 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -635,6 +635,22 @@ acme.sh --issue --dns dns_inwx -d example.com -d www.example.com The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 34. User Servercow API v1 + +Create a new user from the servercow control center. Don't forget to activate **DNS API** for this user. + +``` +export SERVERCOW_API_Username=username +export SERVERCOW_API_Password=password +``` + +Now you cann issue a cert: + +``` +acme.sh --issue --dns dns_servercow -d example.com -d www.example.com +``` +Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh new file mode 100755 index 0000000..e704959 --- /dev/null +++ b/dnsapi/dns_servercow.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env sh + +########## +# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/Neilpang/acme.sh) +# +# Usage: +# export SERVERCOW_API_Username=username +# export SERVERCOW_API_Password=password +# acme.sh --issue -d example.com --dns dns_servercow +# +# Issues: +# Any issues / questions / suggestions can be posted here: +# https://github.com/jhartlep/servercow-dns-api/issues +# +# Author: Jens Hartlep +########## + +SERVERCOW_API="https://api.servercow.de/dns/v1/domains" + +# Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz" +dns_servercow_add() { + fulldomain=$1 + txtvalue=$2 + + _info "Using servercow" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" + SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" + if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then + SERVERCOW_API_Username="" + SERVERCOW_API_Password="" + _err "You don't specify servercow api username and password yet." + _err "Please create your username and password and try again." + return 1 + fi + + # save the credentials to the account conf file + _saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username" + _saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then + if printf -- "%s" "$response" | grep "ok" > /dev/null; then + _info "Added, OK" + return 0 + else + _err "add txt record error." + return 1 + fi + fi + _err "add txt record error." + + return 1 +} + +# Usage fulldomain txtvalue +# Remove the txt record after validation +dns_servercow_rm() { + fulldomain=$1 + txtvalue=$2 + + _info "Using servercow" + _debug fulldomain "$fulldomain" + _debug txtvalue "$fulldomain" + + SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" + SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" + if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then + SERVERCOW_API_Username="" + SERVERCOW_API_Password="" + _err "You don't specify servercow api username and password yet." + _err "Please create your username and password and try again." + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then + if printf -- "%s" "$response" | grep "ok" > /dev/null; then + _info "Deleted, OK" + _contains "$response" '"message":"ok"' + else + _err "delete txt record error." + return 1 + fi + fi + +} + +#################### Private functions below ################################## + +# _acme-challenge.www.domain.com +# returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + fulldomain=$1 + i=2 + p=1 + + while true; do + _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) + + _debug _domain "$_domain" + if [ -z "$_domain" ]; then + # not valid + return 1 + fi + + if ! _servercow_api GET "$_domain"; then + return 1 + fi + + if ! _contains "$response" '"error":"no such domain in user context"' > /dev/null; then + _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) + if [ -z "$_sub_domain" ]; then + # not valid + return 1 + fi + + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done; + + return 1 +} + +_servercow_api() { + method=$1 + domain=$2 + data="$3" + + export _H1="Content-Type: application/json" + export _H2="X-Auth-Username: $SERVERCOW_API_Username" + export _H3="X-Auth-Password: $SERVERCOW_API_Password" + + if [ "$method" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")" + else + response="$(_get "$SERVERCOW_API/$domain")" + fi + + if [ "$?" != "0" ]; then + _err "error $domain" + return 1 + fi + _debug2 response "$response" + return 0 +} From b140e2553bdfce40b99fb3ecd246ef3e64d33d42 Mon Sep 17 00:00:00 2001 From: Jens Hartlep Date: Sat, 9 Dec 2017 19:33:48 +0100 Subject: [PATCH 034/156] added Servercow to supported list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb12291..5574b67 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,7 @@ You don't have to do anything manually! 1. Hurricane Electric DNS service (https://dns.he.net) 1. UnoEuro API (https://www.unoeuro.com/) 1. INWX (https://www.inwx.de/) - +1. Servercow (https://servercow.de) And: From 488745f3783e767adf5421b20ff96fc82aae9a37 Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:05:05 +0100 Subject: [PATCH 035/156] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5574b67..0d94275 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,7 @@ You don't have to do anything manually! 1. INWX (https://www.inwx.de/) 1. Servercow (https://servercow.de) + And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api From 1c9b19833cf0ebd0087f171f9f3f5cf24fc96ecc Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:14:46 +0100 Subject: [PATCH 036/156] Update dns_servercow.sh replaced tab with space --- dnsapi/dns_servercow.sh | 260 ++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index e704959..3ffc393 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -19,88 +19,88 @@ SERVERCOW_API="https://api.servercow.de/dns/v1/domains" # Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz" dns_servercow_add() { - fulldomain=$1 - txtvalue=$2 - - _info "Using servercow" - _debug fulldomain "$fulldomain" - _debug txtvalue "$txtvalue" - - SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" - SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" - if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then - SERVERCOW_API_Username="" - SERVERCOW_API_Password="" - _err "You don't specify servercow api username and password yet." - _err "Please create your username and password and try again." - return 1 - fi - - # save the credentials to the account conf file - _saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username" - _saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password" - - _debug "First detect the root zone" - if ! _get_root "$fulldomain"; then - _err "invalid domain" - return 1 - fi - - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then - if printf -- "%s" "$response" | grep "ok" > /dev/null; then - _info "Added, OK" - return 0 - else - _err "add txt record error." - return 1 - fi - fi - _err "add txt record error." - - return 1 + fulldomain=$1 + txtvalue=$2 + + _info "Using servercow" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" + SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" + if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then + SERVERCOW_API_Username="" + SERVERCOW_API_Password="" + _err "You don't specify servercow api username and password yet." + _err "Please create your username and password and try again." + return 1 + fi + + # save the credentials to the account conf file + _saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username" + _saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then + if printf -- "%s" "$response" | grep "ok" > /dev/null; then + _info "Added, OK" + return 0 + else + _err "add txt record error." + return 1 + fi + fi + _err "add txt record error." + + return 1 } # Usage fulldomain txtvalue # Remove the txt record after validation dns_servercow_rm() { - fulldomain=$1 - txtvalue=$2 - - _info "Using servercow" - _debug fulldomain "$fulldomain" - _debug txtvalue "$fulldomain" - - SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" - SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" - if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then - SERVERCOW_API_Username="" - SERVERCOW_API_Password="" - _err "You don't specify servercow api username and password yet." - _err "Please create your username and password and try again." - return 1 - fi - - _debug "First detect the root zone" - if ! _get_root "$fulldomain"; then - _err "invalid domain" - return 1 - fi - - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then - if printf -- "%s" "$response" | grep "ok" > /dev/null; then - _info "Deleted, OK" - _contains "$response" '"message":"ok"' - else - _err "delete txt record error." - return 1 - fi - fi + fulldomain=$1 + txtvalue=$2 + + _info "Using servercow" + _debug fulldomain "$fulldomain" + _debug txtvalue "$fulldomain" + + SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}" + SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}" + if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then + SERVERCOW_API_Username="" + SERVERCOW_API_Password="" + _err "You don't specify servercow api username and password yet." + _err "Please create your username and password and try again." + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then + if printf -- "%s" "$response" | grep "ok" > /dev/null; then + _info "Deleted, OK" + _contains "$response" '"message":"ok"' + else + _err "delete txt record error." + return 1 + fi + fi } @@ -111,60 +111,60 @@ dns_servercow_rm() { # _sub_domain=_acme-challenge.www # _domain=domain.com _get_root() { - fulldomain=$1 - i=2 - p=1 - - while true; do - _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) - - _debug _domain "$_domain" - if [ -z "$_domain" ]; then - # not valid - return 1 - fi - - if ! _servercow_api GET "$_domain"; then - return 1 - fi - - if ! _contains "$response" '"error":"no such domain in user context"' > /dev/null; then - _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) - if [ -z "$_sub_domain" ]; then - # not valid - return 1 - fi - - return 0 - fi - - p=$i - i=$(_math "$i" + 1) - done; - - return 1 + fulldomain=$1 + i=2 + p=1 + + while true; do + _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) + + _debug _domain "$_domain" + if [ -z "$_domain" ]; then + # not valid + return 1 + fi + + if ! _servercow_api GET "$_domain"; then + return 1 + fi + + if ! _contains "$response" '"error":"no such domain in user context"' > /dev/null; then + _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) + if [ -z "$_sub_domain" ]; then + # not valid + return 1 + fi + + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done; + + return 1 } _servercow_api() { - method=$1 - domain=$2 - data="$3" - - export _H1="Content-Type: application/json" - export _H2="X-Auth-Username: $SERVERCOW_API_Username" - export _H3="X-Auth-Password: $SERVERCOW_API_Password" - - if [ "$method" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")" - else - response="$(_get "$SERVERCOW_API/$domain")" - fi - - if [ "$?" != "0" ]; then - _err "error $domain" - return 1 - fi - _debug2 response "$response" - return 0 + method=$1 + domain=$2 + data="$3" + + export _H1="Content-Type: application/json" + export _H2="X-Auth-Username: $SERVERCOW_API_Username" + export _H3="X-Auth-Password: $SERVERCOW_API_Password" + + if [ "$method" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")" + else + response="$(_get "$SERVERCOW_API/$domain")" + fi + + if [ "$?" != "0" ]; then + _err "error $domain" + return 1 + fi + _debug2 response "$response" + return 0 } From 8101aceab5dad0ac83c8446380d814d77f44fecf Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:18:05 +0100 Subject: [PATCH 037/156] Update dns_servercow.sh fixed remaining issues from shellcheck --- dnsapi/dns_servercow.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index 3ffc393..872ae58 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -50,7 +50,7 @@ dns_servercow_add() { _debug _domain "$_domain" if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then - if printf -- "%s" "$response" | grep "ok" > /dev/null; then + if printf -- "%s" "$response" | grep "ok" >/dev/null; then _info "Added, OK" return 0 else @@ -92,8 +92,8 @@ dns_servercow_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then - if printf -- "%s" "$response" | grep "ok" > /dev/null; then + if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then + if printf -- "%s" "$response" | grep "ok" >/dev/null; then _info "Deleted, OK" _contains "$response" '"message":"ok"' else @@ -140,7 +140,7 @@ _get_root() { p=$i i=$(_math "$i" + 1) - done; + done return 1 } From a95ccc7e4cecd7fa92144dc8af7bca5ffccb107a Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 9 Dec 2017 20:22:09 +0100 Subject: [PATCH 038/156] Update dns_servercow.sh ... didn't see this line in spellcheck ... :S --- dnsapi/dns_servercow.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh index 872ae58..be4e59d 100755 --- a/dnsapi/dns_servercow.sh +++ b/dnsapi/dns_servercow.sh @@ -128,7 +128,7 @@ _get_root() { return 1 fi - if ! _contains "$response" '"error":"no such domain in user context"' > /dev/null; then + if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p) if [ -z "$_sub_domain" ]; then # not valid From a582e7c2fb7c4ef85c324d69377f3e9644406eba Mon Sep 17 00:00:00 2001 From: sjau Date: Tue, 2 Jan 2018 15:05:26 +0100 Subject: [PATCH 039/156] dns_ispconfig.sh: remove unnecessary permission in api user --- dnsapi/dns_ispconfig.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_ispconfig.sh b/dnsapi/dns_ispconfig.sh index 6d1f34c..1e500ad 100755 --- a/dnsapi/dns_ispconfig.sh +++ b/dnsapi/dns_ispconfig.sh @@ -2,7 +2,6 @@ # ISPConfig 3.1 API # User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to: -# - DNS zone Functions # - DNS txt Functions # Report bugs to https://github.com/sjau/acme.sh From 8ea800205c2e5496b63e3244dc4849d629acc1ad Mon Sep 17 00:00:00 2001 From: hiska Date: Thu, 4 Jan 2018 19:01:57 +0900 Subject: [PATCH 040/156] support both debian and redhat --- deploy/strongswan.sh | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/deploy/strongswan.sh b/deploy/strongswan.sh index 2de18f8..f991d69 100644 --- a/deploy/strongswan.sh +++ b/deploy/strongswan.sh @@ -16,17 +16,38 @@ strongswan_deploy() { _cca="$4" _cfullchain="$5" + _info "Using strongswan" + + if [ -x /usr/sbin/ipsec ]; then + _ipsec=/usr/sbin/ipsec + elif [ -x /usr/sbin/strongswan ]; then + _ipsec=/usr/sbin/strongswan + else + _err "no strongswan or ipsec command is detected" + return 1 + fi + + _info _ipsec "$_ipsec" + + _confdir=$($_ipsec --confdir) + if [ $? -ne 0 ] || [ -z "$_confdir" ]; then + _err "no strongswan --confdir is detected" + return 1 + fi + + _info _confdir "$_confdir" + _debug _cdomain "$_cdomain" _debug _ckey "$_ckey" _debug _ccert "$_ccert" _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - cat "$_ckey" >"/etc/ipsec.d/private/$(basename "$_ckey")" - cat "$_ccert" >"/etc/ipsec.d/certs/$(basename "$_ccert")" - cat "$_cca" >"/etc/ipsec.d/cacerts/$(basename "$_cca")" - cat "$_cfullchain" >"/etc/ipsec.d/cacerts/$(basename "$_cfullchain")" + cat "$_ckey" >"${_confdir}/ipsec.d/private/$(basename "$_ckey")" + cat "$_ccert" >"${_confdir}/ipsec.d/certs/$(basename "$_ccert")" + cat "$_cca" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cca")" + cat "$_cfullchain" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cfullchain")" - ipsec reload + $_ipsec reload } From 6541492a5598eb4d27aa9c3cd06f8ba7317da41c Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 12:45:24 +0800 Subject: [PATCH 041/156] first version to support ACME v2 --- acme.sh | 308 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 239 insertions(+), 69 deletions(-) diff --git a/acme.sh b/acme.sh index 472975a..604bff0 100755 --- a/acme.sh +++ b/acme.sh @@ -13,8 +13,15 @@ _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" -_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" -DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" + +LETSENCRYPT_CA_V2="https://acme-v02.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/directory" + +DEFAULT_CA=$LETSENCRYPT_CA_V1 +DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 + DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -24,13 +31,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048 DEFAULT_OPENSSL_BIN="openssl" -STAGE_CA="https://acme-staging.api.letsencrypt.org/directory" +_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org" VTYPE_HTTP="http-01" VTYPE_DNS="dns-01" VTYPE_TLS="tls-sni-01" -#VTYPE_TLS2="tls-sni-02" +VTYPE_TLS2="tls-sni-02" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -1044,13 +1051,14 @@ _createcsr() { if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" + printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf" else domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" else - alt="DNS:$domainlist" + alt="DNS:$domain,DNS:$domainlist" fi #multi _info "Multi domain" "$alt" @@ -1421,7 +1429,7 @@ _calcjwk() { JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "RS256"' elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then _debug "EC key" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" @@ -1490,7 +1498,7 @@ _calcjwk() { JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"' else _err "Only RSA or EC key is supported." return 1 @@ -1580,7 +1588,7 @@ _inithttp() { # body url [needbase64] [POST|PUT] _post() { body="$1" - url="$2" + _post_url="$2" needbase64="$3" httpmethod="$4" @@ -1588,7 +1596,7 @@ _post() { httpmethod="POST" fi _debug $httpmethod - _debug "url" "$url" + _debug "_post_url" "$_post_url" _debug2 "body" "$body" _inithttp @@ -1600,9 +1608,9 @@ _post() { fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url")" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" fi _ret="$?" if [ "$_ret" != "0" ]; then @@ -1620,15 +1628,15 @@ _post() { _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" fi else if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi fi _ret="$?" @@ -1776,7 +1784,15 @@ _send_signed_request() { nonce="$_CACHED_NONCE" _debug2 nonce "$nonce" - protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2" + if [ "$ACME_VERSION" = "2" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' + fi + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + fi _debug3 protected "$protected" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)" @@ -1791,7 +1807,11 @@ _send_signed_request() { sig="$(printf "%s" "$_sig_t" | _url_replace)" _debug3 sig "$sig" - body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + if [ "$ACME_VERSION" = "2" ]; then + body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + else + body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + fi _debug3 body "$body" response="$(_post "$body" "$url" "$needbase64")" @@ -2170,9 +2190,15 @@ _initAPI() { _debug2 "response" "$response" ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_KEY_CHANGE" ]; then + ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_KEY_CHANGE ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_AUTHZ" ]; then + ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_AUTHZ ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3) @@ -2180,6 +2206,9 @@ _initAPI() { if [ -z "$ACME_NEW_ORDER" ]; then ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER_RES="new-order" + if [ -z "$ACME_NEW_ORDER" ]; then + ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3) + fi fi export ACME_NEW_ORDER export ACME_NEW_ORDER_RES @@ -2189,17 +2218,32 @@ _initAPI() { if [ -z "$ACME_NEW_ACCOUNT" ]; then ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ACCOUNT_RES="new-account" + if [ -z "$ACME_NEW_ACCOUNT" ]; then + ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3) + if [ "$ACME_NEW_ACCOUNT" ]; then + export ACME_VERSION=2 + fi + fi fi export ACME_NEW_ACCOUNT export ACME_NEW_ACCOUNT_RES ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_REVOKE_CERT" ]; then + ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_REVOKE_CERT ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_NONCE" ]; then + ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_NONCE ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_AGREEMENT" ]; then + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_AGREEMENT _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" @@ -2208,6 +2252,8 @@ _initAPI() { _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" _debug "ACME_AGREEMENT" "$ACME_AGREEMENT" + _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE" + _debug "ACME_VERSION" "$ACME_VERSION" fi } @@ -2236,7 +2282,7 @@ _initpath() { if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" else - ACME_DIRECTORY="$STAGE_CA" + ACME_DIRECTORY="$DEFAULT_STAGING_CA" _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY" fi fi @@ -2951,6 +2997,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err + _cleardomainconf "ORDER_FINALIZE" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3052,6 +3099,8 @@ _regAccount() { _initpath _reg_length="$1" _debug3 _regAccount "$_regAccount" + _initAPI + mkdir -p "$CA_DIR" if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" @@ -3073,11 +3122,18 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - _initAPI - _reg_res="$ACME_NEW_ACCOUNT_RES" - regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' - if [ "$ACCOUNT_EMAIL" ]; then - regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + + if [ "$ACME_VERSION" = "2" ]; then + regjson='{"termsOfServiceAgreed": true}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}' + fi + else + _reg_res="$ACME_NEW_ACCOUNT_RES" + regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + fi fi _info "Registering account" @@ -3100,8 +3156,8 @@ _regAccount() { _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _debug "_accUri" "$_accUri" _savecaconf "ACCOUNT_URL" "$_accUri" + export ACCOUNT_URL="$ACCOUNT_URL" - echo "$response" >"$ACCOUNT_JSON_PATH" CA_KEY_HASH="$(__calcAccountKeyHash)" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" _savecaconf CA_KEY_HASH "$CA_KEY_HASH" @@ -3113,7 +3169,6 @@ _regAccount() { ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" - } #Implement deactivate account @@ -3251,7 +3306,11 @@ __trigger_validation() { _debug2 _t_url "$_t_url" _t_key_authz="$2" _debug2 _t_key_authz "$_t_key_authz" - _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + if [ "$ACME_VERSION" = "2" ]; then + _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" + else + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + fi } #webroot, domain domainlist keylength @@ -3393,32 +3452,107 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then + if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + #make new order request + _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" + for d in $(echo "$_alt_domains" | tr ',' ' '); do + #todo: check wildcard ? + if [ "$d" ]; then + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" + fi + done + _debug2 _identifiers "$_identifiers" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Create new order error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug ORDER_FINALIZE "$ORDER_FINALIZE" + if [ -z "$ORDER_FINALIZE" ]; then + _err "ORDER_FINALIZE not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #for dns manual mode + _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + + _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #domain and authz map + _authorizations_map="" + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + _debug2 "_authz_url" "$_authz_url" + if ! response="$(_get "$_authz_url")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + _debug2 _d "$_d" + _authorizations_map="$_d,$response +$_authorizations_map" + done + _debug2 _authorizations_map "$_authorizations_map" + fi + alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ') - _index=1 + _index=0 _currentRoot="" for d in $alldomains; do _info "Getting webroot for domain" "$d" + _index=$(_math $_index + 1) _w="$(echo $_web_roots | cut -d , -f $_index)" _debug _w "$_w" if [ "$_w" ]; then _currentRoot="$_w" fi _debug "_currentRoot" "$_currentRoot" - _index=$(_math $_index + 1) vtype="$VTYPE_HTTP" + #todo, v2 wildcard force to use dns if _startswith "$_currentRoot" "dns"; then vtype="$VTYPE_DNS" fi if [ "$_currentRoot" = "$W_TLS" ]; then - vtype="$VTYPE_TLS" + if [ "$ACME_VERSION" = "2" ]; then + vtype="$VTYPE_TLS2" + else + vtype="$VTYPE_TLS" + fi fi - if ! __get_domain_new_authz "$d"; then - _clearup - _on_issue_err "$_post_hook" - return 1 + if [ "$ACME_VERSION" = "2" ]; then + response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")" + _debug2 "response" "$response" + if [ -z "$response" ]; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! __get_domain_new_authz "$d"; then + _clearup + _on_issue_err "$_post_hook" + return 1 + fi fi if [ -z "$thumbprint" ]; then @@ -3436,14 +3570,18 @@ issue() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + if [ "$ACME_VERSION" = "2" ]; then + uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" + else + uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + fi _debug uri "$uri" keyauthorization="$token.$thumbprint" _debug keyauthorization "$keyauthorization" if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then - _debug "$d is already verified, skip." + _debug "$d is already verified." keyauthorization="$STATE_VERIFIED" _debug keyauthorization "$keyauthorization" fi @@ -3685,12 +3823,16 @@ issue() { return 1 fi - if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then - _err "$d:Challenge error: $response" - _clearupwebbroot "$_currentRoot" "$removelevel" "$token" - _clearup - _on_issue_err "$_post_hook" "$vlist" - return 1 + if [ "$code" ] && [ "$code" != '202' ]; then + if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then + _debug "trigger validation code: $code" + else + _err "$d:Challenge error: $response" + _clearupwebbroot "$_currentRoot" "$removelevel" "$token" + _clearup + _on_issue_err "$_post_hook" "$vlist" + return 1 + fi fi waittimes=0 @@ -3773,18 +3915,32 @@ issue() { _info "Verify finished, start to sign." der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" - if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then - _err "Sign failed." - _on_issue_err "$_post_hook" - return 1 - fi - - _rcert="$response" - Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" - _debug "Le_LinkCert" "$Le_LinkCert" - _savedomainconf "Le_LinkCert" "$Le_LinkCert" + if [ "$ACME_VERSION" = "2" ]; then + if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + if [ "$code" != "200" ]; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if [ "$Le_LinkCert" ]; then + if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + _rcert="$response" + Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" echo "$BEGIN_CERT" >"$CERT_PATH" #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then @@ -3798,6 +3954,12 @@ issue() { fi echo "$END_CERT" >>"$CERT_PATH" + fi + + _debug "Le_LinkCert" "$Le_LinkCert" + _savedomainconf "Le_LinkCert" "$Le_LinkCert" + + if [ "$Le_LinkCert" ]; then _info "$(__green "Cert success.")" cat "$CERT_PATH" @@ -3825,29 +3987,37 @@ issue() { _cleardomainconf "Le_Vlist" Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - if ! _contains "$Le_LinkIssuer" ":"; then - _info "$(__red "Relative issuer link found.")" - Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" - fi - _debug Le_LinkIssuer "$Le_LinkIssuer" - _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" if [ "$Le_LinkIssuer" ]; then + if ! _contains "$Le_LinkIssuer" ":"; then + _info "$(__red "Relative issuer link found.")" + Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" + fi + _debug Le_LinkIssuer "$Le_LinkIssuer" + _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" + _link_issuer_retry=0 _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then - echo "$BEGIN_CERT" >"$CA_CERT_PATH" - _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" - echo "$END_CERT" >>"$CA_CERT_PATH" + + if [ "$ACME_VERSION" = "2" ]; then + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then + break + fi + else + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then + echo "$BEGIN_CERT" >"$CA_CERT_PATH" + _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" + echo "$END_CERT" >>"$CA_CERT_PATH" - _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" - cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" - _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" + _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" + cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" + _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" - rm -f "$CA_CERT_PATH.der" - break + rm -f "$CA_CERT_PATH.der" + break + fi fi _link_issuer_retry=$(_math $_link_issuer_retry + 1) _sleep "$_link_issuer_retry" @@ -3957,7 +4127,7 @@ renew() { _savedomainconf Le_API "$Le_API" fi if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then - export Le_API="$STAGE_CA" + export Le_API="$DEFAULT_STAGING_CA" _savedomainconf Le_API "$Le_API" fi export ACME_DIRECTORY="$Le_API" From 136aebc009cf9e32a9a16dead43378a1ba9effe1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 13:57:35 +0800 Subject: [PATCH 042/156] fix format --- acme.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 604bff0..890cdd8 100755 --- a/acme.sh +++ b/acme.sh @@ -22,7 +22,6 @@ LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/director DEFAULT_CA=$LETSENCRYPT_CA_V1 DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 - DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -3122,7 +3121,7 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - + if [ "$ACME_VERSION" = "2" ]; then regjson='{"termsOfServiceAgreed": true}' if [ "$ACCOUNT_EMAIL" ]; then @@ -3469,7 +3468,7 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug ORDER_FINALIZE "$ORDER_FINALIZE" if [ -z "$ORDER_FINALIZE" ]; then _err "ORDER_FINALIZE not found." @@ -3481,7 +3480,7 @@ issue() { #for dns manual mode _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" - _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" if [ -z "$_authorizations_seg" ]; then _err "_authorizations_seg not found." @@ -3492,7 +3491,7 @@ issue() { #domain and authz map _authorizations_map="" - for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do _debug2 "_authz_url" "$_authz_url" if ! response="$(_get "$_authz_url")"; then _err "get to authz error." @@ -3926,9 +3925,9 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi - Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + if ! _get "$Le_LinkCert" >"$CERT_PATH"; then _err "Sign failed, code is not 200." _on_issue_err "$_post_hook" return 1 @@ -4000,7 +3999,6 @@ $_authorizations_map" _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if [ "$ACME_VERSION" = "2" ]; then if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then break From 6ae3911972d6c67e4170cc90e941e733e21429fd Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 17:39:15 +0800 Subject: [PATCH 043/156] support ACME v2 wildcard cert --- acme.sh | 38 +++++++++++++++++++++++++++++------- dnsapi/dns_cf.sh | 51 +++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/acme.sh b/acme.sh index 890cdd8..1021dcd 100755 --- a/acme.sh +++ b/acme.sh @@ -997,7 +997,7 @@ _createkey() { _is_idn() { _is_idn_d="$1" _debug2 _is_idn_d "$_is_idn_d" - _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '.,-') + _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-') _debug2 _idn_temp "$_idn_temp" [ "$_idn_temp" ] } @@ -1055,7 +1055,7 @@ _createcsr() { domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" else alt="DNS:$domain,DNS:$domainlist" fi @@ -1663,7 +1663,7 @@ _get() { onlyheader="$2" t="$3" _debug url "$url" - _debug "timeout" "$t" + _debug "timeout=$t" _inithttp @@ -2277,6 +2277,11 @@ _initpath() { CA_HOME="$DEFAULT_CA_HOME" fi + if [ "$ACME_VERSION" = "2" ]; then + DEFAULT_CA="$LETSENCRYPT_CA_V2" + DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2" + fi + if [ -z "$ACME_DIRECTORY" ]; then if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" @@ -2863,7 +2868,11 @@ _clearupdns() { return 1 fi - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" @@ -3503,6 +3512,9 @@ issue() { response="$(echo "$response" | _normalizeJson)" _debug2 response "$response" _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + if _contains "$response" "\"wildcard\" *: *true"; then + _d="*.$_d" + fi _debug2 _d "$_d" _authorizations_map="$_d,$response $_authorizations_map" @@ -3600,7 +3612,7 @@ $_authorizations_map" keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) vtype=$(echo "$ventry" | cut -d "$sep" -f 4) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) - + _debug d "$d" if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then _debug "$d is already verified, skip $vtype." continue @@ -3608,12 +3620,16 @@ $_authorizations_map" if [ "$vtype" = "$VTYPE_DNS" ]; then dnsadded='0' - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" - d_api="$(_findHook "$d" dnsapi "$_currentRoot")" + d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" _debug d_api "$d_api" @@ -5476,8 +5492,16 @@ _process() { fi if [ -z "$_domain" ]; then + if _startswith "$_dvalue" "*."; then + _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." + return 1 + fi _domain="$_dvalue" else + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 57a2e88..abe7700 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,33 +51,36 @@ dns_cf_add() { return 1 fi - count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" - if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then - _info "Added, OK" - return 0 - else - _err "Add txt record error." - return 1 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - _debug "record_id" "$record_id" - - _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - if [ "$?" = "0" ]; then - _info "Updated, OK" +# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so +# we can not use updating anymore. +# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) +# _debug count "$count" +# if [ "$count" = "0" ]; then + _info "Adding record" + if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then + _info "Added, OK" return 0 + else + _err "Add txt record error." + return 1 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 +# else +# _info "Updating record" +# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) +# _debug "record_id" "$record_id" +# +# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" +# if [ "$?" = "0" ]; then +# _info "Updated, OK" +# return 0 +# fi +# _err "Update error" +# return 1 +# fi } From 4f3b3a273f67b0dcc6a6ab4055133298d8e21c9f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 19:42:29 +0800 Subject: [PATCH 044/156] fix format --- dnsapi/dns_cf.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index abe7700..68264a4 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,11 +51,11 @@ dns_cf_add() { return 1 fi -# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so -# we can not use updating anymore. -# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) -# _debug count "$count" -# if [ "$count" = "0" ]; then + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then _info "Adding record" if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then @@ -68,19 +68,19 @@ dns_cf_add() { fi _err "Add txt record error." return 1 -# else -# _info "Updating record" -# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) -# _debug "record_id" "$record_id" -# -# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" -# if [ "$?" = "0" ]; then -# _info "Updated, OK" -# return 0 -# fi -# _err "Update error" -# return 1 -# fi + # else + # _info "Updating record" + # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) + # _debug "record_id" "$record_id" + # + # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" + # if [ "$?" = "0" ]; then + # _info "Updated, OK" + # return 0 + # fi + # _err "Update error" + # return 1 + # fi } From 2823306810ab2574bc2ca5312a75e7b3f371bd6e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:33:27 +0800 Subject: [PATCH 045/156] fix ACME v2: deactivate/deactivate-account/revoke --- acme.sh | 103 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/acme.sh b/acme.sh index 1021dcd..5a5f033 100755 --- a/acme.sh +++ b/acme.sh @@ -1784,7 +1784,7 @@ _send_signed_request() { _debug2 nonce "$nonce" if [ "$ACME_VERSION" = "2" ]; then - if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' else protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "ORDER_FINALIZE" + _cleardomainconf "Le_OrderFinalize" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3212,7 +3212,12 @@ deactivateaccount() { fi _initAPI - if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}" + fi + if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate account success for $_accUri." _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,') elif [ "$code" = "403" ]; then @@ -3334,6 +3339,11 @@ issue() { _web_roots="$1" _main_domain="$2" _alt_domains="$3" + + if _startswith "$_main_domain" "*."; then + _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." + return 1 + fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -3460,7 +3470,7 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then - if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + if [ "$ACME_VERSION" = "2" ]; then #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do @@ -3477,17 +3487,17 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" - _debug ORDER_FINALIZE "$ORDER_FINALIZE" - if [ -z "$ORDER_FINALIZE" ]; then - _err "ORDER_FINALIZE not found." + Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug Le_OrderFinalize "$Le_OrderFinalize" + if [ -z "$Le_OrderFinalize" ]; then + _err "Le_OrderFinalize not found." _clearup _on_issue_err "$_post_hook" return 1 fi #for dns manual mode - _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize" _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" @@ -3931,7 +3941,7 @@ $_authorizations_map" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" if [ "$ACME_VERSION" = "2" ]; then - if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then _err "Sign failed." _on_issue_err "$_post_hook" return 1 @@ -4632,7 +4642,11 @@ revoke() { _initAPI - data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + if [ "$ACME_VERSION" = "2" ]; then + data="{\"certificate\": \"$cert\"}" + else + data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + fi uri="${ACME_REVOKE_CERT}" if [ -f "$CERT_KEY_PATH" ]; then @@ -4703,27 +4717,56 @@ _deactivate() { _d_type="$2" _initpath - if ! __get_domain_new_authz "$_d_domain"; then - _err "Can not get domain new authz token." - return 1 - fi + if [ "$ACME_VERSION" = "2" ]; then + _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Can not get domain new order." + return 1 + fi + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" - _debug "authzUri" "$authzUri" + authzUri="$_authorizations_seg" + _debug2 "authzUri" "$authzUri" + if ! response="$(_get "$authzUri")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - if [ "$code" ] && [ ! "$code" = '201' ]; then - _err "new-authz error: $response" - return 1 + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _URL_NAME="url" + else + if ! __get_domain_new_authz "$_d_domain"; then + _err "Can not get domain new authz token." + return 1 + fi + + authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _debug "authzUri" "$authzUri" + if [ "$code" ] && [ ! "$code" = '201' ]; then + _err "new-authz error: $response" + return 1 + fi + _URL_NAME="uri" fi - entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')" + entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")" if [ -z "$entries" ]; then _info "No valid entries found." if [ -z "$thumbprint" ]; then thumbprint="$(__calc_account_thumbprint)" fi _debug "Trigger validation." - vtype="$VTYPE_HTTP" + vtype="$VTYPE_DNS" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then @@ -4733,7 +4776,7 @@ _deactivate() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" keyauthorization="$token.$thumbprint" @@ -4759,7 +4802,7 @@ _deactivate() { _debug _vtype "$_vtype" _info "Found $_vtype" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then @@ -4769,7 +4812,13 @@ _deactivate() { _info "Deactivate: $_vtype" - if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}" + fi + + if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate: $_vtype success." else _err "Can not deactivate $_vtype." @@ -5492,10 +5541,6 @@ _process() { fi if [ -z "$_domain" ]; then - if _startswith "$_dvalue" "*."; then - _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." - return 1 - fi _domain="$_dvalue" else if _startswith "$_dvalue" "*."; then From 4a139934f696867f81cb135296c409a788a39ffd Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:50:57 +0800 Subject: [PATCH 046/156] fix dns manual mode. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 5a5f033..4433e81 100755 --- a/acme.sh +++ b/acme.sh @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "Le_OrderFinalize" + if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else From ee6f78805f2eff9c8c4cf969470662fca6bf7874 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 7 Jan 2018 12:12:40 +0800 Subject: [PATCH 047/156] update doc for v2 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0d94275..2845488 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. +- Support ACME v1 and ACME v2 +- Support ACME v2 wildcard certs - Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Bash, dash and sh compatible. - Simplest shell script for Let's Encrypt free certificate client. From c99d4948b7e3525f9d9ff1aed760d21284bbad2a Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 20:43:23 +0800 Subject: [PATCH 048/156] we should not use "updating" to support wildcard --- dnsapi/dns_ovh.sh | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index eaa90bd..03b75d9 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -137,48 +137,27 @@ dns_ovh_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" - - if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then - _info "Adding record" - if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then - if _contains "$response" "$txtvalue"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 - return 0 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1) - if [ -z "$record_id" ]; then - _err "Can not get record id." - return 1 - fi - _debug "record_id" "$record_id" - - if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then - if _contains "$response" "null"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Updated, sleeping 10 seconds" - sleep 10 - return 0 - fi + _info "Adding record" + if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then + if _contains "$response" "$txtvalue"; then + _ovh_rest POST "domain/zone/$_domain/refresh" + _debug "Refresh:$response" + _info "Added, sleeping 10 seconds" + sleep 10 + return 0 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 } #fulldomain dns_ovh_rm() { fulldomain=$1 + txtvalue=$2 + _debug "Getting txt records" + #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" } From 6d5e7826aebcce830636b8b34eb9b36295d52a44 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 21:36:48 +0800 Subject: [PATCH 049/156] support dns_ovh_rm() --- dnsapi/dns_ovh.sh | 56 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 03b75d9..96c2044 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,13 +78,8 @@ _ovh_get_api() { esac } -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_ovh_add() { - fulldomain=$1 - txtvalue=$2 +_initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" OVH_AS="" @@ -127,6 +122,19 @@ dns_ovh_add() { return 1 fi _info "Consumer key is ok." + return 0 +} + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ovh_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _initAuth; then + return 1 + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -156,9 +164,39 @@ dns_ovh_add() { dns_ovh_rm() { fulldomain=$1 txtvalue=$2 + + if ! _initAuth; then + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" _debug "Getting txt records" - #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" + if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then + return 1 + fi + for rid in $(echo "$response" | tr '[,]' ' '); do + _debug rid "$rid" + if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then + return 1 + fi + if _contains "$response" "\"target\":\"$txtvalue\""; then + _debug "Found txt id:$rid" + if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then + return 1 + fi + return 0 + fi + done + + return 1 } #################### Private functions below ################################## @@ -170,7 +208,7 @@ _ovh_authentication() { _H3="" _H4="" - _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' + _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" _debug3 response "$response" @@ -258,7 +296,7 @@ _ovh_rest() { export _H3="X-Ovh-Timestamp: $_ovh_t" export _H4="X-Ovh-Consumer: $OVH_CK" export _H5="Content-Type: application/json;charset=utf-8" - if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then + if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then _debug data "$data" response="$(_post "$data" "$_ovh_url" "" "$m")" else From 2befb5e7849c3dddbdc32633e5d5b11a64ef1795 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:04:03 +0800 Subject: [PATCH 050/156] fix ovh --- dnsapi/dns_ovh.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 96c2044..60ce189 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -114,8 +114,7 @@ _initAuth() { _info "Checking authentication" - response="$(_ovh_rest GET "domain")" - if _contains "$response" "INVALID_CREDENTIAL"; then + if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then _err "The consumer key is invalid: $OVH_CK" _err "Please retry to create a new one." _clearaccountconf OVH_CK @@ -150,8 +149,7 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 + _info "Added" return 0 fi fi @@ -303,8 +301,8 @@ _ovh_rest() { response="$(_get "$_ovh_url")" fi - if [ "$?" != "0" ]; then - _err "error $ep" + if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then + _err "error $response" return 1 fi _debug2 response "$response" From a8ae23d0a29d796ce5f465a7572538bb2c232cc9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:47:01 +0800 Subject: [PATCH 051/156] add more sleep for ovh --- dnsapi/dns_ovh.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 60ce189..de69bd9 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -149,7 +149,8 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added" + _info "Added, sleep 10 seconds." + _sleep 10 return 0 fi fi From 06a2e5fc82e5cf91922d921cb5490e04893afc61 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 23:05:55 +0800 Subject: [PATCH 052/156] fix format --- dnsapi/dns_ovh.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index de69bd9..6009473 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,7 +78,6 @@ _ovh_get_api() { esac } - _initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" @@ -181,7 +180,7 @@ dns_ovh_rm() { return 1 fi - for rid in $(echo "$response" | tr '[,]' ' '); do + for rid in $(echo "$response" | tr '][,' ' '); do _debug rid "$rid" if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then return 1 From eb207322d3ee34ddc2098c246f76fc980129fcc9 Mon Sep 17 00:00:00 2001 From: Meowthink Date: Sun, 14 Jan 2018 14:19:33 +0800 Subject: [PATCH 053/156] Add namesilo.com dns api support --- README.md | 1 + dnsapi/README.md | 15 +++++ dnsapi/dns_namesilo.sh | 137 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100755 dnsapi/dns_namesilo.sh diff --git a/README.md b/README.md index 0d94275..bd39a20 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,7 @@ You don't have to do anything manually! 1. UnoEuro API (https://www.unoeuro.com/) 1. INWX (https://www.inwx.de/) 1. Servercow (https://servercow.de) +1. Namesilo (https://www.namesilo.com) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index d357c05..ed36b97 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -651,6 +651,21 @@ acme.sh --issue --dns dns_servercow -d example.com -d www.example.com ``` Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 35. Use Namesilo.com API + +You'll need to generate an API key at https://www.namesilo.com/account_api.php +Optionally you may restrict the access to an IP range there. + +``` +export Namesilo_Key="xxxxxxxxxxxxxxxxxxxxxxxx" +``` + +And now you can issue certs with: + +``` +acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com +``` + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh new file mode 100755 index 0000000..dc1a4fd --- /dev/null +++ b/dnsapi/dns_namesilo.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env sh + +#Author: meowthink +#Created 01/14/2017 +#Utilize namesilo.com API to finish dns-01 verifications. + +Namesilo_API="https://www.namesilo.com/api" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_namesilo_add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$Namesilo_Key" ]; then + Namesilo_Key="" + _err "API token for namesilo.com is missing." + _err "Please specify that in your environment variable." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf Namesilo_Key "$Namesilo_Key" + + if ! _get_root "$fulldomain"; then + _err "Unable to find domain specified." + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug txtvalue "$txtvalue" + if _namesilo_rest GET "dnsAddRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrtype=TXT&rrhost=$_sub_domain&rrvalue=$txtvalue"; then + retcode=$(printf "%s\n" "$response" | _egrep_o "300") + if [ "$retcode" ]; then + _info "Successfully added TXT record, ready for validation." + return 0 + else + _err "Unable to add the DNS record." + return 1 + fi + fi +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_namesilo_rm() { + fulldomain=$1 + txtvalue=$2 + + if ! _get_root "$fulldomain"; then + _err "Unable to find domain specified." + return 1 + fi + + # Get the record id. + if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then + retcode=$(printf "%s\n" "$response" | _egrep_o "300") + if [ "$retcode" ]; then + _record_id=$(printf "%s\n" "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1) + _debug record_id "$_record_id" + _info "Successfully retrieved the record id for ACME challenge." + else + _err "Unable to retrieve the record id." + return 1 + fi + fi + + # Remove the DNS record using record id. + if _namesilo_rest GET "dnsDeleteRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrid=$_record_id"; then + retcode=$(printf "%s\n" "$response" | _egrep_o "300") + if [ "$retcode" ]; then + _info "Successfully removed the TXT record." + return 0 + else + _err "Unable to remove the DNS record." + return 1 + fi + fi +} + +#################### Private functions below ################################## + +# _acme-challenge.www.domain.com +# returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + i=2 + p=1 + + if ! _namesilo_rest GET "listDomains?version=1&type=xml&key=$Namesilo_Key"; then + return 1 + fi + + # Need to exclude the last field (tld) + numfields=$(echo "$domain" | _egrep_o "\." | wc -l) + while [ $i -le "$numfields" ]; do + host=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug host "$host" + if [ -z "$host" ]; then + return 1 + fi + + if _contains "$response" "$host"; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$host" + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_namesilo_rest() { + method=$1 + param=$2 + data=$3 + + if [ "$method" != "GET" ]; then + response="$(_post "$data" "$Namesilo_API/$param" "" "$method")" + else + response="$(_get "$Namesilo_API/$param")" + fi + + if [ "$?" != "0" ]; then + _err "error $param" + return 1 + fi + + _debug2 response "$response" + return 0 +} From cd2fe698bb627c4fb9a12a775f71672df1d07d6e Mon Sep 17 00:00:00 2001 From: AA Date: Mon, 15 Jan 2018 10:59:50 +0100 Subject: [PATCH 054/156] Create dns_autodns.sh --- dnsapi/dns_autodns.sh | 264 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 dnsapi/dns_autodns.sh diff --git a/dnsapi/dns_autodns.sh b/dnsapi/dns_autodns.sh new file mode 100644 index 0000000..9253448 --- /dev/null +++ b/dnsapi/dns_autodns.sh @@ -0,0 +1,264 @@ +#!/usr/bin/env sh +# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*- + +# This is the InternetX autoDNS xml api wrapper for acme.sh +# Author: auerswald@gmail.com +# Created: 2018-01-14 +# +# export AUTODNS_USER="username" +# export AUTODNS_PASSWORD="password" +# export AUTODNS_CONTEXT="context" +# +# Usage: +# acme.sh --issue --dns dns_autodns -d example.com + +AUTODNS_API="https://gateway.autodns.com" + +# Arguments: +# txtdomain +# txt +dns_autodns_add() { + fulldomain="$1" + txtvalue="$2" + + AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" + AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" + AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}" + + if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then + _err "You don't specify autodns user, password and context." + return 1 + fi + + _saveaccountconf_mutable AUTODNS_USER "$AUTODNS_USER" + _saveaccountconf_mutable AUTODNS_PASSWORD "$AUTODNS_PASSWORD" + _saveaccountconf_mutable AUTODNS_CONTEXT "$AUTODNS_CONTEXT" + + _debug "First detect the root zone" + + if ! _get_autodns_zone "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _zone "$_zone" + _debug _system_ns "$_system_ns" + + _info "Adding TXT record" + + autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")" + + if [ "$?" -eq "0" ]; then + _info "Added, OK" + return 0 + fi + + return 1 +} + +# Arguments: +# txtdomain +# txt +dns_autodns_rm() { + fulldomain="$1" + txtvalue="$2" + + AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}" + AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}" + AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}" + + if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then + _err "You don't specify autodns user, password and context." + return 1 + fi + + _debug "First detect the root zone" + + if ! _get_autodns_zone "$fulldomain"; then + _err "zone not found" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _zone "$_zone" + _debug _system_ns "$_system_ns" + + _info "Delete TXT record" + + autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")" + + if [ "$?" -eq "0" ]; then + _info "Deleted, OK" + return 0 + fi + + return 1 +} + +#################### Private functions below ################################## + +# Arguments: +# fulldomain +# Returns: +# _sub_domain=_acme-challenge.www +# _zone=domain.com +# _system_ns +_get_autodns_zone() { + domain="$1" + + i=2 + p=1 + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + + if [ -z "$h" ]; then + # not valid + return 1 + fi + + autodns_response="$(_autodns_zone_inquire "$h")" + + if [ "$?" -ne "0" ]; then + _err "invalid domain" + return 1 + fi + + if _contains "$autodns_response" "1" >/dev/null; then + _zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)" + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +_build_request_auth_xml() { + printf " + %s + %s + %s + " "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT" +} + +# Arguments: +# zone +_build_zone_inquire_xml() { + printf " + + %s + + 0205 + + 1 + 1 + + + name + eq + %s + + + " "$(_build_request_auth_xml)" "$1" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_build_zone_update_xml() { + printf " + + %s + + 0202001 + + + %s + 600 + TXT + %s + + + + %s + %s + + + " "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4" +} + +# Arguments: +# zone +_autodns_zone_inquire() { + request_data="$(_build_zone_inquire_xml "$1")" + autodns_response="$(_autodns_api_call "$request_data")" + ret="$?" + + printf "%s" "$autodns_response" + return "$ret" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_autodns_zone_update() { + request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")" + autodns_response="$(_autodns_api_call "$request_data")" + ret="$?" + + printf "%s" "$autodns_response" + return "$ret" +} + +# Arguments: +# zone +# subdomain +# txtvalue +# system_ns +_autodns_zone_cleanup() { + request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")" + # replace 'rr_add>' with 'rr_rem>' in request_data + request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')" + autodns_response="$(_autodns_api_call "$request_data")" + ret="$?" + + printf "%s" "$autodns_response" + return "$ret" +} + +# Arguments: +# request_data +_autodns_api_call() { + request_data="$1" + + _debug request_data "$request_data" + + autodns_response="$(_post "$request_data" "$AUTODNS_API")" + ret="$?" + + _debug autodns_response "$autodns_response" + + if [ "$ret" -ne "0" ]; then + _err "error" + return 1 + fi + + if _contains "$autodns_response" "success" >/dev/null; then + _info "success" + printf "%s" "$autodns_response" + return 0 + fi + + return 1 +} From a01da2fd92dafbf0a1e67dbe8ff45dce6a5be33b Mon Sep 17 00:00:00 2001 From: AA Date: Mon, 15 Jan 2018 11:00:44 +0100 Subject: [PATCH 055/156] Update README.md --- dnsapi/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index ed36b97..83ef4ea 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -666,6 +666,24 @@ And now you can issue certs with: acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com ``` +## 37. Use autoDNS (InternetX) + +[InternetX](https://www.internetx.com/) offers an [xml api](https://help.internetx.com/display/API/AutoDNS+XML-API) with your standard login credentials, set them like so: + +``` +export AUTODNS_USER="yourusername" +export AUTODNS_PASSWORD="password" +export AUTODNS_CONTEXT="context" +``` + +Then you can issue your certificates with: + +``` +acme.sh --issue --dns dns_autodns -d example.com -d www.example.com +``` + +The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From 775aae7082efab15175449d9183114a976c3a4d1 Mon Sep 17 00:00:00 2001 From: AA Date: Mon, 15 Jan 2018 11:15:26 +0100 Subject: [PATCH 056/156] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd39a20..a2c210e 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,7 @@ You don't have to do anything manually! 1. INWX (https://www.inwx.de/) 1. Servercow (https://servercow.de) 1. Namesilo (https://www.namesilo.com) - +1. InternetX autoDNS API (https://internetx.com) And: From 7e212c4d406e3d8669b23b89b2107ea1c2b6b390 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 19:48:57 +0800 Subject: [PATCH 057/156] typo --- acme.sh | 1 - dnsapi/dns_aws.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 4433e81..6c3f6e0 100755 --- a/acme.sh +++ b/acme.sh @@ -3474,7 +3474,6 @@ issue() { #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do - #todo: check wildcard ? if [ "$d" ]; then _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" fi diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 5a71651..8c1513d 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -23,7 +23,7 @@ dns_aws_add() { AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" _err "You don't specify aws route53 api key id and and api key secret yet." - _err "Please create you key and try again. see $(__green $AWS_WIKI)" + _err "Please create your key and try again. see $(__green $AWS_WIKI)" return 1 fi From e6cda79ee8411255d191f7781df42522bbf3aedc Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 21:55:40 +0800 Subject: [PATCH 058/156] support only one wildcard domain. fix https://github.com/Neilpang/acme.sh/issues/1188#issuecomment-357684744 --- acme.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 6c3f6e0..74b437c 100755 --- a/acme.sh +++ b/acme.sh @@ -3340,10 +3340,6 @@ issue() { _main_domain="$2" _alt_domains="$3" - if _startswith "$_main_domain" "*."; then - _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." - return 1 - fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -5539,13 +5535,13 @@ _process() { return 1 fi + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ -z "$_domain" ]; then _domain="$_dvalue" else - if _startswith "$_dvalue" "*."; then - _debug "Wildcard domain" - export ACME_VERSION=2 - fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else From 3164b5ab1307321548aca49c9e97e117d72d9e8f Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 16 Jan 2018 20:55:07 +0800 Subject: [PATCH 059/156] fix https://github.com/Neilpang/acme.sh/issues/1189 --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 74b437c..59a7d96 100755 --- a/acme.sh +++ b/acme.sh @@ -2259,6 +2259,8 @@ _initAPI() { #[domain] [keylength or isEcc flag] _initpath() { + domain="$1" + _ilength="$2" __initHome @@ -2346,13 +2348,10 @@ _initpath() { ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" fi - if [ -z "$1" ]; then + if [ -z "$domain" ]; then return 0 fi - domain="$1" - _ilength="$2" - if [ -z "$DOMAIN_PATH" ]; then domainhome="$CERT_HOME/$domain" domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX" @@ -4234,8 +4233,6 @@ signcsr() { return 1 fi - _initpath - _csrsubj=$(_readSubjectFromCSR "$_csrfile") if [ "$?" != "0" ]; then _err "Can not read subject from csr: $_csrfile" @@ -4272,6 +4269,9 @@ signcsr() { return 1 fi + if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then + export ACME_VERSION=2 + fi _initpath "$_csrsubj" "$_csrkeylength" mkdir -p "$DOMAIN_PATH" From c1151b0d459c4e9cfac45c8dd2cfc70cedeecfff Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 12:45:24 +0800 Subject: [PATCH 060/156] first version to support ACME v2 --- acme.sh | 308 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 239 insertions(+), 69 deletions(-) diff --git a/acme.sh b/acme.sh index 472975a..604bff0 100755 --- a/acme.sh +++ b/acme.sh @@ -13,8 +13,15 @@ _SCRIPT_="$0" _SUB_FOLDERS="dnsapi deploy" -_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" -DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" + +LETSENCRYPT_CA_V2="https://acme-v02.api.letsencrypt.org/directory" +LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/directory" + +DEFAULT_CA=$LETSENCRYPT_CA_V1 +DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 + DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -24,13 +31,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048 DEFAULT_OPENSSL_BIN="openssl" -STAGE_CA="https://acme-staging.api.letsencrypt.org/directory" +_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org" VTYPE_HTTP="http-01" VTYPE_DNS="dns-01" VTYPE_TLS="tls-sni-01" -#VTYPE_TLS2="tls-sni-02" +VTYPE_TLS2="tls-sni-02" LOCAL_ANY_ADDRESS="0.0.0.0" @@ -1044,13 +1051,14 @@ _createcsr() { if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then #single domain _info "Single domain" "$domain" + printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf" else domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" else - alt="DNS:$domainlist" + alt="DNS:$domain,DNS:$domainlist" fi #multi _info "Multi domain" "$alt" @@ -1421,7 +1429,7 @@ _calcjwk() { JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "RS256"' elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then _debug "EC key" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" @@ -1490,7 +1498,7 @@ _calcjwk() { JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' JWK_HEADERPLACE_PART1='{"nonce": "' - JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}' + JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"' else _err "Only RSA or EC key is supported." return 1 @@ -1580,7 +1588,7 @@ _inithttp() { # body url [needbase64] [POST|PUT] _post() { body="$1" - url="$2" + _post_url="$2" needbase64="$3" httpmethod="$4" @@ -1588,7 +1596,7 @@ _post() { httpmethod="POST" fi _debug $httpmethod - _debug "url" "$url" + _debug "_post_url" "$_post_url" _debug2 "body" "$body" _inithttp @@ -1600,9 +1608,9 @@ _post() { fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url")" + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" fi _ret="$?" if [ "$_ret" != "0" ]; then @@ -1620,15 +1628,15 @@ _post() { _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" fi else if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")" + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi fi _ret="$?" @@ -1776,7 +1784,15 @@ _send_signed_request() { nonce="$_CACHED_NONCE" _debug2 nonce "$nonce" - protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2" + if [ "$ACME_VERSION" = "2" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' + fi + else + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' + fi _debug3 protected "$protected" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)" @@ -1791,7 +1807,11 @@ _send_signed_request() { sig="$(printf "%s" "$_sig_t" | _url_replace)" _debug3 sig "$sig" - body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + if [ "$ACME_VERSION" = "2" ]; then + body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + else + body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" + fi _debug3 body "$body" response="$(_post "$body" "$url" "$needbase64")" @@ -2170,9 +2190,15 @@ _initAPI() { _debug2 "response" "$response" ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_KEY_CHANGE" ]; then + ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_KEY_CHANGE ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_AUTHZ" ]; then + ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_AUTHZ ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3) @@ -2180,6 +2206,9 @@ _initAPI() { if [ -z "$ACME_NEW_ORDER" ]; then ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER_RES="new-order" + if [ -z "$ACME_NEW_ORDER" ]; then + ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3) + fi fi export ACME_NEW_ORDER export ACME_NEW_ORDER_RES @@ -2189,17 +2218,32 @@ _initAPI() { if [ -z "$ACME_NEW_ACCOUNT" ]; then ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ACCOUNT_RES="new-account" + if [ -z "$ACME_NEW_ACCOUNT" ]; then + ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3) + if [ "$ACME_NEW_ACCOUNT" ]; then + export ACME_VERSION=2 + fi + fi fi export ACME_NEW_ACCOUNT export ACME_NEW_ACCOUNT_RES ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_REVOKE_CERT" ]; then + ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_REVOKE_CERT ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_NEW_NONCE" ]; then + ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_NEW_NONCE ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3) + if [ -z "$ACME_AGREEMENT" ]; then + ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3) + fi export ACME_AGREEMENT _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" @@ -2208,6 +2252,8 @@ _initAPI() { _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT" _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" _debug "ACME_AGREEMENT" "$ACME_AGREEMENT" + _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE" + _debug "ACME_VERSION" "$ACME_VERSION" fi } @@ -2236,7 +2282,7 @@ _initpath() { if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" else - ACME_DIRECTORY="$STAGE_CA" + ACME_DIRECTORY="$DEFAULT_STAGING_CA" _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY" fi fi @@ -2951,6 +2997,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err + _cleardomainconf "ORDER_FINALIZE" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3052,6 +3099,8 @@ _regAccount() { _initpath _reg_length="$1" _debug3 _regAccount "$_regAccount" + _initAPI + mkdir -p "$CA_DIR" if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH" @@ -3073,11 +3122,18 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - _initAPI - _reg_res="$ACME_NEW_ACCOUNT_RES" - regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' - if [ "$ACCOUNT_EMAIL" ]; then - regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + + if [ "$ACME_VERSION" = "2" ]; then + regjson='{"termsOfServiceAgreed": true}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}' + fi + else + _reg_res="$ACME_NEW_ACCOUNT_RES" + regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + if [ "$ACCOUNT_EMAIL" ]; then + regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}' + fi fi _info "Registering account" @@ -3100,8 +3156,8 @@ _regAccount() { _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _debug "_accUri" "$_accUri" _savecaconf "ACCOUNT_URL" "$_accUri" + export ACCOUNT_URL="$ACCOUNT_URL" - echo "$response" >"$ACCOUNT_JSON_PATH" CA_KEY_HASH="$(__calcAccountKeyHash)" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH" _savecaconf CA_KEY_HASH "$CA_KEY_HASH" @@ -3113,7 +3169,6 @@ _regAccount() { ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)" _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT" - } #Implement deactivate account @@ -3251,7 +3306,11 @@ __trigger_validation() { _debug2 _t_url "$_t_url" _t_key_authz="$2" _debug2 _t_key_authz "$_t_key_authz" - _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + if [ "$ACME_VERSION" = "2" ]; then + _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}" + else + _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" + fi } #webroot, domain domainlist keylength @@ -3393,32 +3452,107 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then + if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + #make new order request + _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" + for d in $(echo "$_alt_domains" | tr ',' ' '); do + #todo: check wildcard ? + if [ "$d" ]; then + _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" + fi + done + _debug2 _identifiers "$_identifiers" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Create new order error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug ORDER_FINALIZE "$ORDER_FINALIZE" + if [ -z "$ORDER_FINALIZE" ]; then + _err "ORDER_FINALIZE not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #for dns manual mode + _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + + _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + #domain and authz map + _authorizations_map="" + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + _debug2 "_authz_url" "$_authz_url" + if ! response="$(_get "$_authz_url")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + _debug2 _d "$_d" + _authorizations_map="$_d,$response +$_authorizations_map" + done + _debug2 _authorizations_map "$_authorizations_map" + fi + alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ') - _index=1 + _index=0 _currentRoot="" for d in $alldomains; do _info "Getting webroot for domain" "$d" + _index=$(_math $_index + 1) _w="$(echo $_web_roots | cut -d , -f $_index)" _debug _w "$_w" if [ "$_w" ]; then _currentRoot="$_w" fi _debug "_currentRoot" "$_currentRoot" - _index=$(_math $_index + 1) vtype="$VTYPE_HTTP" + #todo, v2 wildcard force to use dns if _startswith "$_currentRoot" "dns"; then vtype="$VTYPE_DNS" fi if [ "$_currentRoot" = "$W_TLS" ]; then - vtype="$VTYPE_TLS" + if [ "$ACME_VERSION" = "2" ]; then + vtype="$VTYPE_TLS2" + else + vtype="$VTYPE_TLS" + fi fi - if ! __get_domain_new_authz "$d"; then - _clearup - _on_issue_err "$_post_hook" - return 1 + if [ "$ACME_VERSION" = "2" ]; then + response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")" + _debug2 "response" "$response" + if [ -z "$response" ]; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! __get_domain_new_authz "$d"; then + _clearup + _on_issue_err "$_post_hook" + return 1 + fi fi if [ -z "$thumbprint" ]; then @@ -3436,14 +3570,18 @@ issue() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + if [ "$ACME_VERSION" = "2" ]; then + uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" + else + uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" + fi _debug uri "$uri" keyauthorization="$token.$thumbprint" _debug keyauthorization "$keyauthorization" if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then - _debug "$d is already verified, skip." + _debug "$d is already verified." keyauthorization="$STATE_VERIFIED" _debug keyauthorization "$keyauthorization" fi @@ -3685,12 +3823,16 @@ issue() { return 1 fi - if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then - _err "$d:Challenge error: $response" - _clearupwebbroot "$_currentRoot" "$removelevel" "$token" - _clearup - _on_issue_err "$_post_hook" "$vlist" - return 1 + if [ "$code" ] && [ "$code" != '202' ]; then + if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then + _debug "trigger validation code: $code" + else + _err "$d:Challenge error: $response" + _clearupwebbroot "$_currentRoot" "$removelevel" "$token" + _clearup + _on_issue_err "$_post_hook" "$vlist" + return 1 + fi fi waittimes=0 @@ -3773,18 +3915,32 @@ issue() { _info "Verify finished, start to sign." der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" - if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then - _err "Sign failed." - _on_issue_err "$_post_hook" - return 1 - fi - - _rcert="$response" - Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" - _debug "Le_LinkCert" "$Le_LinkCert" - _savedomainconf "Le_LinkCert" "$Le_LinkCert" + if [ "$ACME_VERSION" = "2" ]; then + if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + if [ "$code" != "200" ]; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if [ "$Le_LinkCert" ]; then + if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + _err "Sign failed, code is not 200." + _on_issue_err "$_post_hook" + return 1 + fi + else + if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then + _err "Sign failed." + _on_issue_err "$_post_hook" + return 1 + fi + _rcert="$response" + Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" echo "$BEGIN_CERT" >"$CERT_PATH" #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then @@ -3798,6 +3954,12 @@ issue() { fi echo "$END_CERT" >>"$CERT_PATH" + fi + + _debug "Le_LinkCert" "$Le_LinkCert" + _savedomainconf "Le_LinkCert" "$Le_LinkCert" + + if [ "$Le_LinkCert" ]; then _info "$(__green "Cert success.")" cat "$CERT_PATH" @@ -3825,29 +3987,37 @@ issue() { _cleardomainconf "Le_Vlist" Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - if ! _contains "$Le_LinkIssuer" ":"; then - _info "$(__red "Relative issuer link found.")" - Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" - fi - _debug Le_LinkIssuer "$Le_LinkIssuer" - _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" if [ "$Le_LinkIssuer" ]; then + if ! _contains "$Le_LinkIssuer" ":"; then + _info "$(__red "Relative issuer link found.")" + Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" + fi + _debug Le_LinkIssuer "$Le_LinkIssuer" + _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" + _link_issuer_retry=0 _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then - echo "$BEGIN_CERT" >"$CA_CERT_PATH" - _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" - echo "$END_CERT" >>"$CA_CERT_PATH" + + if [ "$ACME_VERSION" = "2" ]; then + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then + break + fi + else + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then + echo "$BEGIN_CERT" >"$CA_CERT_PATH" + _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" + echo "$END_CERT" >>"$CA_CERT_PATH" - _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" - cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" - _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" + _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" + cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" + _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" - rm -f "$CA_CERT_PATH.der" - break + rm -f "$CA_CERT_PATH.der" + break + fi fi _link_issuer_retry=$(_math $_link_issuer_retry + 1) _sleep "$_link_issuer_retry" @@ -3957,7 +4127,7 @@ renew() { _savedomainconf Le_API "$Le_API" fi if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then - export Le_API="$STAGE_CA" + export Le_API="$DEFAULT_STAGING_CA" _savedomainconf Le_API "$Le_API" fi export ACME_DIRECTORY="$Le_API" From f8d22c486e243704b58a40b377a1b15dc8366d3e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 13:57:35 +0800 Subject: [PATCH 061/156] fix format --- acme.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 604bff0..890cdd8 100755 --- a/acme.sh +++ b/acme.sh @@ -22,7 +22,6 @@ LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/director DEFAULT_CA=$LETSENCRYPT_CA_V1 DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1 - DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_ACCOUNT_EMAIL="" @@ -3122,7 +3121,7 @@ _regAccount() { if ! _calcjwk "$ACCOUNT_KEY_PATH"; then return 1 fi - + if [ "$ACME_VERSION" = "2" ]; then regjson='{"termsOfServiceAgreed": true}' if [ "$ACCOUNT_EMAIL" ]; then @@ -3469,7 +3468,7 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response"| tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug ORDER_FINALIZE "$ORDER_FINALIZE" if [ -z "$ORDER_FINALIZE" ]; then _err "ORDER_FINALIZE not found." @@ -3481,7 +3480,7 @@ issue() { #for dns manual mode _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" - _authorizations_seg="$(echo "$response"| tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" if [ -z "$_authorizations_seg" ]; then _err "_authorizations_seg not found." @@ -3492,7 +3491,7 @@ issue() { #domain and authz map _authorizations_map="" - for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' ' ); do + for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do _debug2 "_authz_url" "$_authz_url" if ! response="$(_get "$_authz_url")"; then _err "get to authz error." @@ -3926,9 +3925,9 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi - Le_LinkCert="$(echo "$response"| tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" + Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)" - if ! _get "$Le_LinkCert" > "$CERT_PATH"; then + if ! _get "$Le_LinkCert" >"$CERT_PATH"; then _err "Sign failed, code is not 200." _on_issue_err "$_post_hook" return 1 @@ -4000,7 +3999,6 @@ $_authorizations_map" _MAX_ISSUER_RETRY=5 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do _debug _link_issuer_retry "$_link_issuer_retry" - if [ "$ACME_VERSION" = "2" ]; then if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then break From 72f54ca6c13c33943aafbacaee03423907f5e738 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 17:39:15 +0800 Subject: [PATCH 062/156] support ACME v2 wildcard cert --- acme.sh | 38 +++++++++++++++++++++++++++++------- dnsapi/dns_cf.sh | 51 +++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/acme.sh b/acme.sh index 890cdd8..1021dcd 100755 --- a/acme.sh +++ b/acme.sh @@ -997,7 +997,7 @@ _createkey() { _is_idn() { _is_idn_d="$1" _debug2 _is_idn_d "$_is_idn_d" - _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '.,-') + _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-') _debug2 _idn_temp "$_idn_temp" [ "$_idn_temp" ] } @@ -1055,7 +1055,7 @@ _createcsr() { domainlist="$(_idn "$domainlist")" _debug2 domainlist "$domainlist" if _contains "$domainlist" ","; then - alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")" + alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" else alt="DNS:$domain,DNS:$domainlist" fi @@ -1663,7 +1663,7 @@ _get() { onlyheader="$2" t="$3" _debug url "$url" - _debug "timeout" "$t" + _debug "timeout=$t" _inithttp @@ -2277,6 +2277,11 @@ _initpath() { CA_HOME="$DEFAULT_CA_HOME" fi + if [ "$ACME_VERSION" = "2" ]; then + DEFAULT_CA="$LETSENCRYPT_CA_V2" + DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2" + fi + if [ -z "$ACME_DIRECTORY" ]; then if [ -z "$STAGE" ]; then ACME_DIRECTORY="$DEFAULT_CA" @@ -2863,7 +2868,11 @@ _clearupdns() { return 1 fi - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" if ! $rmcommand "$txtdomain" "$txt"; then _err "Error removing txt for domain:$txtdomain" @@ -3503,6 +3512,9 @@ issue() { response="$(echo "$response" | _normalizeJson)" _debug2 response "$response" _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" + if _contains "$response" "\"wildcard\" *: *true"; then + _d="*.$_d" + fi _debug2 _d "$_d" _authorizations_map="$_d,$response $_authorizations_map" @@ -3600,7 +3612,7 @@ $_authorizations_map" keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) vtype=$(echo "$ventry" | cut -d "$sep" -f 4) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) - + _debug d "$d" if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then _debug "$d is already verified, skip $vtype." continue @@ -3608,12 +3620,16 @@ $_authorizations_map" if [ "$vtype" = "$VTYPE_DNS" ]; then dnsadded='0' - txtdomain="_acme-challenge.$d" + _dns_root_d="$d" + if _startswith "$_dns_root_d" "*."; then + _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" + fi + txtdomain="_acme-challenge.$_dns_root_d" _debug txtdomain "$txtdomain" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" _debug txt "$txt" - d_api="$(_findHook "$d" dnsapi "$_currentRoot")" + d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")" _debug d_api "$d_api" @@ -5476,8 +5492,16 @@ _process() { fi if [ -z "$_domain" ]; then + if _startswith "$_dvalue" "*."; then + _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." + return 1 + fi _domain="$_dvalue" else + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index 57a2e88..abe7700 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,33 +51,36 @@ dns_cf_add() { return 1 fi - count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" - if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then - _info "Added, OK" - return 0 - else - _err "Add txt record error." - return 1 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - _debug "record_id" "$record_id" - - _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - if [ "$?" = "0" ]; then - _info "Updated, OK" +# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so +# we can not use updating anymore. +# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) +# _debug count "$count" +# if [ "$count" = "0" ]; then + _info "Adding record" + if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then + _info "Added, OK" return 0 + else + _err "Add txt record error." + return 1 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 +# else +# _info "Updating record" +# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) +# _debug "record_id" "$record_id" +# +# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" +# if [ "$?" = "0" ]; then +# _info "Updated, OK" +# return 0 +# fi +# _err "Update error" +# return 1 +# fi } From 506c41cb157bf45fc27d5930458688eb2ff666b4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 19:42:29 +0800 Subject: [PATCH 063/156] fix format --- dnsapi/dns_cf.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index abe7700..68264a4 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -51,11 +51,11 @@ dns_cf_add() { return 1 fi -# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so -# we can not use updating anymore. -# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) -# _debug count "$count" -# if [ "$count" = "0" ]; then + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then _info "Adding record" if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then @@ -68,19 +68,19 @@ dns_cf_add() { fi _err "Add txt record error." return 1 -# else -# _info "Updating record" -# record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) -# _debug "record_id" "$record_id" -# -# _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" -# if [ "$?" = "0" ]; then -# _info "Updated, OK" -# return 0 -# fi -# _err "Update error" -# return 1 -# fi + # else + # _info "Updating record" + # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) + # _debug "record_id" "$record_id" + # + # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" + # if [ "$?" = "0" ]; then + # _info "Updated, OK" + # return 0 + # fi + # _err "Update error" + # return 1 + # fi } From d2cde379ad310767fc84e2f6f92599b9b6d8c458 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:33:27 +0800 Subject: [PATCH 064/156] fix ACME v2: deactivate/deactivate-account/revoke --- acme.sh | 103 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/acme.sh b/acme.sh index 1021dcd..5a5f033 100755 --- a/acme.sh +++ b/acme.sh @@ -1784,7 +1784,7 @@ _send_signed_request() { _debug2 nonce "$nonce" if [ "$ACME_VERSION" = "2" ]; then - if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then + if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' else protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "ORDER_FINALIZE" + _cleardomainconf "Le_OrderFinalize" if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else @@ -3212,7 +3212,12 @@ deactivateaccount() { fi _initAPI - if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}" + fi + if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate account success for $_accUri." _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,') elif [ "$code" = "403" ]; then @@ -3334,6 +3339,11 @@ issue() { _web_roots="$1" _main_domain="$2" _alt_domains="$3" + + if _startswith "$_main_domain" "*."; then + _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." + return 1 + fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -3460,7 +3470,7 @@ issue() { sep='#' dvsep=',' if [ -z "$vlist" ]; then - if [ "$ACME_VERSION" = "2" ] && [ -z "$ORDER_FINALIZE" ]; then + if [ "$ACME_VERSION" = "2" ]; then #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do @@ -3477,17 +3487,17 @@ issue() { return 1 fi - ORDER_FINALIZE="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" - _debug ORDER_FINALIZE "$ORDER_FINALIZE" - if [ -z "$ORDER_FINALIZE" ]; then - _err "ORDER_FINALIZE not found." + Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" + _debug Le_OrderFinalize "$Le_OrderFinalize" + if [ -z "$Le_OrderFinalize" ]; then + _err "Le_OrderFinalize not found." _clearup _on_issue_err "$_post_hook" return 1 fi #for dns manual mode - _savedomainconf "ORDER_FINALIZE" "$ORDER_FINALIZE" + _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize" _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" _debug2 _authorizations_seg "$_authorizations_seg" @@ -3931,7 +3941,7 @@ $_authorizations_map" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" if [ "$ACME_VERSION" = "2" ]; then - if ! _send_signed_request "${ORDER_FINALIZE}" "{\"csr\": \"$der\"}"; then + if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then _err "Sign failed." _on_issue_err "$_post_hook" return 1 @@ -4632,7 +4642,11 @@ revoke() { _initAPI - data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + if [ "$ACME_VERSION" = "2" ]; then + data="{\"certificate\": \"$cert\"}" + else + data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" + fi uri="${ACME_REVOKE_CERT}" if [ -f "$CERT_KEY_PATH" ]; then @@ -4703,27 +4717,56 @@ _deactivate() { _d_type="$2" _initpath - if ! __get_domain_new_authz "$_d_domain"; then - _err "Can not get domain new authz token." - return 1 - fi + if [ "$ACME_VERSION" = "2" ]; then + _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}" + if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then + _err "Can not get domain new order." + return 1 + fi + _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')" + _debug2 _authorizations_seg "$_authorizations_seg" + if [ -z "$_authorizations_seg" ]; then + _err "_authorizations_seg not found." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" - _debug "authzUri" "$authzUri" + authzUri="$_authorizations_seg" + _debug2 "authzUri" "$authzUri" + if ! response="$(_get "$authzUri")"; then + _err "get to authz error." + _clearup + _on_issue_err "$_post_hook" + return 1 + fi - if [ "$code" ] && [ ! "$code" = '201' ]; then - _err "new-authz error: $response" - return 1 + response="$(echo "$response" | _normalizeJson)" + _debug2 response "$response" + _URL_NAME="url" + else + if ! __get_domain_new_authz "$_d_domain"; then + _err "Can not get domain new authz token." + return 1 + fi + + authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" + _debug "authzUri" "$authzUri" + if [ "$code" ] && [ ! "$code" = '201' ]; then + _err "new-authz error: $response" + return 1 + fi + _URL_NAME="uri" fi - entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')" + entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")" if [ -z "$entries" ]; then _info "No valid entries found." if [ -z "$thumbprint" ]; then thumbprint="$(__calc_account_thumbprint)" fi _debug "Trigger validation." - vtype="$VTYPE_HTTP" + vtype="$VTYPE_DNS" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then @@ -4733,7 +4776,7 @@ _deactivate() { token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" keyauthorization="$token.$thumbprint" @@ -4759,7 +4802,7 @@ _deactivate() { _debug _vtype "$_vtype" _info "Found $_vtype" - uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" _debug uri "$uri" if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then @@ -4769,7 +4812,13 @@ _deactivate() { _info "Deactivate: $_vtype" - if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then + if [ "$ACME_VERSION" = "2" ]; then + _djson="{\"status\":\"deactivated\"}" + else + _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}" + fi + + if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then _info "Deactivate: $_vtype success." else _err "Can not deactivate $_vtype." @@ -5492,10 +5541,6 @@ _process() { fi if [ -z "$_domain" ]; then - if _startswith "$_dvalue" "*."; then - _err "The first domain can not be wildcard, '$_dvalue' is a wildcard domain." - return 1 - fi _domain="$_dvalue" else if _startswith "$_dvalue" "*."; then From cd8fc35968b6009d349284a3acb24f10d531e50f Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 6 Jan 2018 21:50:57 +0800 Subject: [PATCH 065/156] fix dns manual mode. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 5a5f033..4433e81 100755 --- a/acme.sh +++ b/acme.sh @@ -3005,7 +3005,7 @@ _on_issue_err() { _chk_post_hook="$1" _chk_vlist="$2" _debug _on_issue_err - _cleardomainconf "Le_OrderFinalize" + if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" else From 79a2bed640430d68da691a956ef64229de954c1a Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 7 Jan 2018 12:12:40 +0800 Subject: [PATCH 066/156] update doc for v2 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a2c210e..ea509e2 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. +- Support ACME v1 and ACME v2 +- Support ACME v2 wildcard certs - Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Bash, dash and sh compatible. - Simplest shell script for Let's Encrypt free certificate client. From ea25492c2823a4971b3d2eb28fa0dcd2b5105db9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 20:43:23 +0800 Subject: [PATCH 067/156] we should not use "updating" to support wildcard --- dnsapi/dns_ovh.sh | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index eaa90bd..03b75d9 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -137,48 +137,27 @@ dns_ovh_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" - - if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then - _info "Adding record" - if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then - if _contains "$response" "$txtvalue"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 - return 0 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1) - if [ -z "$record_id" ]; then - _err "Can not get record id." - return 1 - fi - _debug "record_id" "$record_id" - - if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then - if _contains "$response" "null"; then - _ovh_rest POST "domain/zone/$_domain/refresh" - _debug "Refresh:$response" - _info "Updated, sleeping 10 seconds" - sleep 10 - return 0 - fi + _info "Adding record" + if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then + if _contains "$response" "$txtvalue"; then + _ovh_rest POST "domain/zone/$_domain/refresh" + _debug "Refresh:$response" + _info "Added, sleeping 10 seconds" + sleep 10 + return 0 fi - _err "Update error" - return 1 fi + _err "Add txt record error." + return 1 } #fulldomain dns_ovh_rm() { fulldomain=$1 + txtvalue=$2 + _debug "Getting txt records" + #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" } From be186bd39ba8f0fdd971ca23e12138b81d5e95c4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 21:36:48 +0800 Subject: [PATCH 068/156] support dns_ovh_rm() --- dnsapi/dns_ovh.sh | 56 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 03b75d9..96c2044 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,13 +78,8 @@ _ovh_get_api() { esac } -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_ovh_add() { - fulldomain=$1 - txtvalue=$2 +_initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" OVH_AS="" @@ -127,6 +122,19 @@ dns_ovh_add() { return 1 fi _info "Consumer key is ok." + return 0 +} + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_ovh_add() { + fulldomain=$1 + txtvalue=$2 + + if ! _initAuth; then + return 1 + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -156,9 +164,39 @@ dns_ovh_add() { dns_ovh_rm() { fulldomain=$1 txtvalue=$2 + + if ! _initAuth; then + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" _debug "Getting txt records" - #_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain" + if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then + return 1 + fi + for rid in $(echo "$response" | tr '[,]' ' '); do + _debug rid "$rid" + if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then + return 1 + fi + if _contains "$response" "\"target\":\"$txtvalue\""; then + _debug "Found txt id:$rid" + if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then + return 1 + fi + return 0 + fi + done + + return 1 } #################### Private functions below ################################## @@ -170,7 +208,7 @@ _ovh_authentication() { _H3="" _H4="" - _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' + _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" _debug3 response "$response" @@ -258,7 +296,7 @@ _ovh_rest() { export _H3="X-Ovh-Timestamp: $_ovh_t" export _H4="X-Ovh-Consumer: $OVH_CK" export _H5="Content-Type: application/json;charset=utf-8" - if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then + if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then _debug data "$data" response="$(_post "$data" "$_ovh_url" "" "$m")" else From f823f170e64a641639fdf2c97a8c6a334f3264b7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:04:03 +0800 Subject: [PATCH 069/156] fix ovh --- dnsapi/dns_ovh.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 96c2044..60ce189 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -114,8 +114,7 @@ _initAuth() { _info "Checking authentication" - response="$(_ovh_rest GET "domain")" - if _contains "$response" "INVALID_CREDENTIAL"; then + if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then _err "The consumer key is invalid: $OVH_CK" _err "Please retry to create a new one." _clearaccountconf OVH_CK @@ -150,8 +149,7 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added, sleeping 10 seconds" - sleep 10 + _info "Added" return 0 fi fi @@ -303,8 +301,8 @@ _ovh_rest() { response="$(_get "$_ovh_url")" fi - if [ "$?" != "0" ]; then - _err "error $ep" + if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then + _err "error $response" return 1 fi _debug2 response "$response" From 01cc2e13d81cbcda3485c8256a591230defe2626 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 22:47:01 +0800 Subject: [PATCH 070/156] add more sleep for ovh --- dnsapi/dns_ovh.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 60ce189..de69bd9 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -149,7 +149,8 @@ dns_ovh_add() { if _contains "$response" "$txtvalue"; then _ovh_rest POST "domain/zone/$_domain/refresh" _debug "Refresh:$response" - _info "Added" + _info "Added, sleep 10 seconds." + _sleep 10 return 0 fi fi From 0170c20e9a7d9632f8c83417c73c56fe0d985c51 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 9 Jan 2018 23:05:55 +0800 Subject: [PATCH 071/156] fix format --- dnsapi/dns_ovh.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index de69bd9..6009473 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -78,7 +78,6 @@ _ovh_get_api() { esac } - _initAuth() { if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" @@ -181,7 +180,7 @@ dns_ovh_rm() { return 1 fi - for rid in $(echo "$response" | tr '[,]' ' '); do + for rid in $(echo "$response" | tr '][,' ' '); do _debug rid "$rid" if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then return 1 From 60814ecfe10427238a8bcb39caa778ed520f0f7f Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 19:48:57 +0800 Subject: [PATCH 072/156] typo --- acme.sh | 1 - dnsapi/dns_aws.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 4433e81..6c3f6e0 100755 --- a/acme.sh +++ b/acme.sh @@ -3474,7 +3474,6 @@ issue() { #make new order request _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}" for d in $(echo "$_alt_domains" | tr ',' ' '); do - #todo: check wildcard ? if [ "$d" ]; then _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}" fi diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 5a71651..8c1513d 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -23,7 +23,7 @@ dns_aws_add() { AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" _err "You don't specify aws route53 api key id and and api key secret yet." - _err "Please create you key and try again. see $(__green $AWS_WIKI)" + _err "Please create your key and try again. see $(__green $AWS_WIKI)" return 1 fi From 9e9f839d96053cae020aab3817cdb94d9622df18 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 15 Jan 2018 21:55:40 +0800 Subject: [PATCH 073/156] support only one wildcard domain. fix https://github.com/Neilpang/acme.sh/issues/1188#issuecomment-357684744 --- acme.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 6c3f6e0..74b437c 100755 --- a/acme.sh +++ b/acme.sh @@ -3340,10 +3340,6 @@ issue() { _main_domain="$2" _alt_domains="$3" - if _startswith "$_main_domain" "*."; then - _err "The first domain can not be wildcard, '$_main_domain' is a wildcard domain." - return 1 - fi if _contains "$_main_domain" ","; then _main_domain=$(echo "$2,$3" | cut -d , -f 1) _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") @@ -5539,13 +5535,13 @@ _process() { return 1 fi + if _startswith "$_dvalue" "*."; then + _debug "Wildcard domain" + export ACME_VERSION=2 + fi if [ -z "$_domain" ]; then _domain="$_dvalue" else - if _startswith "$_dvalue" "*."; then - _debug "Wildcard domain" - export ACME_VERSION=2 - fi if [ "$_altdomains" = "$NO_VALUE" ]; then _altdomains="$_dvalue" else From cd9fb3b63581a2f4dadca299c847b365528ea718 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 16 Jan 2018 20:55:07 +0800 Subject: [PATCH 074/156] fix https://github.com/Neilpang/acme.sh/issues/1189 --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 74b437c..59a7d96 100755 --- a/acme.sh +++ b/acme.sh @@ -2259,6 +2259,8 @@ _initAPI() { #[domain] [keylength or isEcc flag] _initpath() { + domain="$1" + _ilength="$2" __initHome @@ -2346,13 +2348,10 @@ _initpath() { ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" fi - if [ -z "$1" ]; then + if [ -z "$domain" ]; then return 0 fi - domain="$1" - _ilength="$2" - if [ -z "$DOMAIN_PATH" ]; then domainhome="$CERT_HOME/$domain" domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX" @@ -4234,8 +4233,6 @@ signcsr() { return 1 fi - _initpath - _csrsubj=$(_readSubjectFromCSR "$_csrfile") if [ "$?" != "0" ]; then _err "Can not read subject from csr: $_csrfile" @@ -4272,6 +4269,9 @@ signcsr() { return 1 fi + if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then + export ACME_VERSION=2 + fi _initpath "$_csrsubj" "$_csrkeylength" mkdir -p "$DOMAIN_PATH" From 78d1cfb4648dcf6fbb5ae30074f95e6a517057c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20B=C3=B6hnke?= Date: Wed, 17 Jan 2018 19:21:14 +0100 Subject: [PATCH 075/156] fix bug in the --ca-bundle param of passing -f to _readlink When _readlink is called the -f param must not be passed. _readlink (with leading underscore) is a wrapper around readlink (without leading underscore). _readlink already passes -f to readlink, that's why it must not be passed to _readlink. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 472975a..2d24551 100755 --- a/acme.sh +++ b/acme.sh @@ -5510,7 +5510,7 @@ _process() { HTTPS_INSECURE="1" ;; --ca-bundle) - _ca_bundle="$(_readlink -f "$2")" + _ca_bundle="$(_readlink "$2")" CA_BUNDLE="$_ca_bundle" shift ;; From 6ba4f8b54cbc5019ce0b4da537975d10d39c0251 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 18 Jan 2018 21:04:06 +0800 Subject: [PATCH 076/156] fix https://github.com/Neilpang/acme.sh/issues/1204 --- dnsapi/dns_aws.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 5a71651..450e42d 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -19,6 +19,8 @@ dns_aws_add() { fulldomain=$1 txtvalue=$2 + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" @@ -27,10 +29,9 @@ dns_aws_add() { return 1 fi - if [ -z "$AWS_SESSION_TOKEN" ]; then - _saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" - _saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" - fi + #save for future use + _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" + _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -56,6 +57,8 @@ dns_aws_rm() { fulldomain=$1 txtvalue=$2 + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" From 37f39c0870ce3f381a586f331ee5fe41cc3d89b3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 19 Jan 2018 22:41:42 +0800 Subject: [PATCH 077/156] minor --- acme.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 2d24551..bd46052 100755 --- a/acme.sh +++ b/acme.sh @@ -2548,10 +2548,7 @@ _setNginx() { _d="$1" _croot="$2" _thumbpt="$3" - if ! _exists "nginx"; then - _err "nginx command is not found." - return 1 - fi + FOUND_REAL_NGINX_CONF="" FOUND_REAL_NGINX_CONF_LN="" BACKUP_NGINX_CONF="" @@ -2561,6 +2558,10 @@ _setNginx() { if [ -z "$_start_f" ]; then _debug "find start conf from nginx command" if [ -z "$NGINX_CONF" ]; then + if ! _exists "nginx"; then + _err "nginx command is not found." + return 1 + fi NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")" _debug NGINX_CONF "$NGINX_CONF" NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)" @@ -2605,6 +2606,10 @@ _setNginx() { return 1 fi + if ! _exists "nginx"; then + _err "nginx command is not found." + return 1 + fi _info "Check the nginx conf before setting up." if ! _exec "nginx -t" >/dev/null; then _exec_err From 258cf20c92d3ea541283cf3eff0ecc0b07305ae7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 19 Jan 2018 22:48:06 +0800 Subject: [PATCH 078/156] minor --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 83ef4ea..cf88462 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -666,7 +666,7 @@ And now you can issue certs with: acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com ``` -## 37. Use autoDNS (InternetX) +## 36. Use autoDNS (InternetX) [InternetX](https://www.internetx.com/) offers an [xml api](https://help.internetx.com/display/API/AutoDNS+XML-API) with your standard login credentials, set them like so: From 04a609b51f38f1db8792d57dbc0af0eadaf108fe Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 21 Jan 2018 20:20:54 +0800 Subject: [PATCH 079/156] fix https://github.com/Neilpang/acme.sh/issues/1209 --- acme.sh | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index bd46052..920f15f 100755 --- a/acme.sh +++ b/acme.sh @@ -2702,7 +2702,7 @@ _isRealNginxConf() { for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do _debug _fln "$_fln" if [ "$_fln" ]; then - _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1) + _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *" | grep -v server_name | _tail_n 1) _debug "_start" "$_start" _start_n=$(echo "$_start" | cut -d : -f 1) _start_nn=$(_math $_start_n + 1) @@ -2711,8 +2711,8 @@ _isRealNginxConf() { _left="$(sed -n "${_start_nn},99999p" "$2")" _debug2 _left "$_left" - if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then - _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1) + if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" >/dev/null; then + _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | _head_n 1) _debug "_end" "$_end" _end_n=$(echo "$_end" | cut -d : -f 1) _debug "_end_n" "$_end_n" @@ -2723,8 +2723,20 @@ _isRealNginxConf() { _debug "_seg_n" "$_seg_n" - if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \ - || [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then + _skip_ssl=1 + for _listen_i in $(echo "$_seg_n" | grep "^ *listen" | tr -d " "); do + if [ "$_listen_i" ]; then + if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl[ |;]")" ]; then + _debug2 "$_listen_i is ssl" + else + _debug2 "$_listen_i is plain text" + _skip_ssl="" + break; + fi + fi + done + + if [ "$_skip_ssl" = "1" ]; then _debug "ssl on, skip" else FOUND_REAL_NGINX_CONF_LN=$_fln From c05eb0b1b2b859a1e20f82d0ee2593f6840f13a4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 23 Jan 2018 19:42:57 +0800 Subject: [PATCH 080/156] fx format --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 920f15f..77ab439 100755 --- a/acme.sh +++ b/acme.sh @@ -2731,8 +2731,8 @@ _isRealNginxConf() { else _debug2 "$_listen_i is plain text" _skip_ssl="" - break; - fi + break + fi fi done From 9134b6ea989fb0f390ecf680e62f867cae3063a9 Mon Sep 17 00:00:00 2001 From: Herve Couplet Date: Tue, 23 Jan 2018 17:53:40 +0100 Subject: [PATCH 081/156] if local-address is present, bind to that ip instead of 0.0.0.0 --- acme.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 77ab439..c3cba1e 100755 --- a/acme.sh +++ b/acme.sh @@ -1984,9 +1984,15 @@ _startserver() { _NC="$_NC -6" fi + SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork + + #Adding bind to local-address + if [ "$_local_address" ]; then + $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${_local_address}" + fi + _debug "_NC" "$_NC" - #todo listen address - $_NC TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From e90f3b84c1a1694f7f5c13a3ebfabc2da246a78d Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 23 Jan 2018 18:24:08 +0100 Subject: [PATCH 082/156] Add support for Azure DNS Adding support for Azure DNS See https://docs.microsoft.com/en-us/azure/dns/ --- dnsapi/dns_azure.sh | 241 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 dnsapi/dns_azure.sh diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh new file mode 100644 index 0000000..4099774 --- /dev/null +++ b/dnsapi/dns_azure.sh @@ -0,0 +1,241 @@ +#!/usr/bin/env sh + + +######## Public functions ##################### + + # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Used to add txt record +# +# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate +# +dns_azure_add() +{ + fulldomain=$1 + txtvalue=$2 + + AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" + AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" + AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" + AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" + + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Subscription ID " + return 1 + fi + + if [ -z "$AZUREDNS_TENANTID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify then Azure Tenant ID " + return 1 + fi + + if [ -z "$AZUREDNS_APPID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure App ID" + return 1 + fi + + if [ -z "$AZUREDNS_CLIENTSECRET" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Client Secret" + return 1 + fi + #save account details to account conf file. + _saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID" + _saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID" + _saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID" + _saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET" + + + accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") + + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + acmeRecordURI="https://management.azure.com$(printf '%s' $_domain_id |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug $acmeRecordURI + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" + _debug $response +} + +# Usage: fulldomain txtvalue +# Used to remove the txt record after validation +# +# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete +# +dns_azure_rm() +{ + fulldomain=$1 + txtvalue=$2 + + AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" + AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" + AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" + AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" + + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Subscription ID " + return 1 + fi + + if [ -z "$AZUREDNS_TENANTID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Tenant ID " + return 1 + fi + + if [ -z "$AZUREDNS_APPID" ] ;then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure App ID" + return 1 + fi + + if [ -z "$AZUREDNS_CLIENTSECRET" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify Azure Client Secret" + return 1 + fi + + accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") + + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + acmeRecordURI="https://management.azure.com$(printf '%s' $_domain_id |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug $acmeRecordURI + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" + _debug $response +} + +################### Private functions below ################################## + +_azure_rest() { + m=$1 + ep="$2" + data="$3" + accesstoken="$4" + + _debug "$ep" + + export _H1="authorization: Bearer $accesstoken" + export _H2="accept: application/json" + export _H3="Content-Type: application/json" + _H1="authorization: Bearer $accesstoken" + _H2="accept: application/json" + _H3="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$ep" "" "$m")" + else + response="$(_get "$ep")" + fi + _debug2 response "$response" + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + return 0 +} + +## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token +_azure_getaccess_token() { + TENANTID=$1 + clientID=$2 + clientSecret=$3 + + export _H1="accept: application/json" + export _H2="Content-Type: application/x-www-form-urlencoded" + export _H3="" + + body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" $clientID | _url_encode)&client_secret=$(printf "%s" $clientSecret| _url_encode)&grant_type=client_credentials" + _debug data "$body" + response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" + accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + + if [ "$?" != "0" ]; then + _err "error $response" + return 1 + fi + printf $accesstoken + _debug2 response "$response" + return 0 +} + +_get_root() { + domain=$1 + subscriptionId=$2 + accesstoken=$3 + i=2 + p=1 + + ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list + ## returns up to 100 zones in one response therefore handling more results is not not implemented + ## (ZoneListResult with continuation token for the next page of results) + ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways + ## + _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" $accesstoken + + # Find matching domain name is Json response + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 "Checking domain: $h" + if [ -z "$h" ]; then + #not valid + _err "Invalid domain" + return 1 + fi + + if _contains "$response" "\"name\":\"$h\"" >/dev/null; then + _domain_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + From b6fc8398cf03a0c7c6b173f6f61704c09047c578 Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 23 Jan 2018 18:25:52 +0100 Subject: [PATCH 083/156] Usage instructions for Azure DNS --- dnsapi/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index cf88462..76b0cdf 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -684,6 +684,25 @@ acme.sh --issue --dns dns_autodns -d example.com -d www.example.com The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 37. Use Azure DNS + +You have to create a service principal first. See: https://github.com/Neilpang/acme.sh/wiki/How-to-use-AzureDns-API + +``` +export AZUREDNS_SUBSCRIPTIONID="12345678-9abc-def0-1234-567890abcdef" +export AZUREDNS_TENANTID="11111111-2222-3333-4444-555555555555" +export AZUREDNS_APPID="3b5033b5-7a66-43a5-b3b9-a36b9e7c25ed" +export AZUREDNS_CLIENTSECRET="1b0224ef-34d4-5af9-110f-77f527d561bd" +``` + +Then you can issue your certificates with: + +``` +acme.sh --issue --dns dns_azure -d example.com -d www.example.com +``` + +`AZUREDNS_SUBSCRIPTIONID`, `AZUREDNS_TENANTID`,`AZUREDNS_APPID` and `AZUREDNS_CLIENTSECRET` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. From c82cd90ed6f64a982acfc45ce94db518c7603cad Mon Sep 17 00:00:00 2001 From: martgras Date: Tue, 23 Jan 2018 18:46:59 +0100 Subject: [PATCH 084/156] Relative link to Azure DNS wiki page --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 76b0cdf..1148ea7 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -686,7 +686,7 @@ The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be sa ## 37. Use Azure DNS -You have to create a service principal first. See: https://github.com/Neilpang/acme.sh/wiki/How-to-use-AzureDns-API +You have to create a service principal first. See:[How to use Azure DNS](../../../wiki/How-to-use-Azure-DNS) ``` export AZUREDNS_SUBSCRIPTIONID="12345678-9abc-def0-1234-567890abcdef" From 3fdbbafcb5eb10a55f195c90d77f31a9aa342850 Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 24 Jan 2018 09:59:05 +0100 Subject: [PATCH 085/156] fix getroot with multiple dns zones --- dnsapi/dns_azure.sh | 96 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 4099774..583a37d 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -1,56 +1,55 @@ #!/usr/bin/env sh - ######## Public functions ##################### - # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" # Used to add txt record # # Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate # -dns_azure_add() -{ +dns_azure_add() +{ fulldomain=$1 txtvalue=$2 - + AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" - + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Subscription ID " + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Subscription ID " return 1 fi if [ -z "$AZUREDNS_TENANTID" ] ; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify then Azure Tenant ID " + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify then Azure Tenant ID " return 1 fi if [ -z "$AZUREDNS_APPID" ] ; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure App ID" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure App ID" return 1 fi if [ -z "$AZUREDNS_CLIENTSECRET" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Client Secret" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Client Secret" return 1 fi #save account details to account conf file. @@ -61,15 +60,15 @@ dns_azure_add() accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then _err "invalid domain" return 1 fi _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - + _debug _domain "$_domain" + acmeRecordURI="https://management.azure.com$(printf '%s' $_domain_id |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug $acmeRecordURI body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" @@ -82,62 +81,62 @@ dns_azure_add() # # Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete # -dns_azure_rm() -{ +dns_azure_rm() +{ fulldomain=$1 txtvalue=$2 - + AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" - + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Subscription ID " + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Subscription ID " return 1 fi if [ -z "$AZUREDNS_TENANTID" ] ; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Tenant ID " + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Tenant ID " return 1 fi if [ -z "$AZUREDNS_APPID" ] ;then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure App ID" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure App ID" return 1 fi if [ -z "$AZUREDNS_CLIENTSECRET" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify Azure Client Secret" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify Azure Client Secret" return 1 fi accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then _err "invalid domain" return 1 fi _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - + _debug _domain "$_domain" + acmeRecordURI="https://management.azure.com$(printf '%s' $_domain_id |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug $acmeRecordURI body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" @@ -152,7 +151,7 @@ _azure_rest() { ep="$2" data="$3" accesstoken="$4" - + _debug "$ep" export _H1="authorization: Bearer $accesstoken" @@ -181,16 +180,16 @@ _azure_getaccess_token() { TENANTID=$1 clientID=$2 clientSecret=$3 - + export _H1="accept: application/json" export _H2="Content-Type: application/x-www-form-urlencoded" export _H3="" - + body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" $clientID | _url_encode)&client_secret=$(printf "%s" $clientSecret| _url_encode)&grant_type=client_credentials" _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") - + if [ "$?" != "0" ]; then _err "error $response" return 1 @@ -208,11 +207,11 @@ _get_root() { p=1 ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list - ## returns up to 100 zones in one response therefore handling more results is not not implemented + ## returns up to 100 zones in one response therefore handling more results is not not implemented ## (ZoneListResult with continuation token for the next page of results) - ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways + ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways ## - _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" $accesstoken + _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" $accesstoken # Find matching domain name is Json response while true; do @@ -225,7 +224,7 @@ _get_root() { fi if _contains "$response" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _domain_id=$(printf "%s\n" "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") if [ "$_domain_id" ]; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain=$h @@ -238,4 +237,3 @@ _get_root() { done return 1 } - From f7d4698ef038b245ad6163bf604d167ceb51eb8b Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 24 Jan 2018 13:39:38 +0100 Subject: [PATCH 086/156] improve error checking --- dnsapi/dns_azure.sh | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 583a37d..24b13bb 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -73,7 +73,11 @@ dns_azure_add() _debug $acmeRecordURI body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" - _debug $response + if [ "$_code" = "200" ] || [ "$code" = '201' ]; then + _info "validation record added" + else + _err "error adding validation record ($_code)" + fi } # Usage: fulldomain txtvalue @@ -141,7 +145,11 @@ dns_azure_rm() _debug $acmeRecordURI body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" - _debug $response + if [ "$_code" = "200" ] || [ "$code" = '204' ]; then + _info "validation record removed" + else + _err "error removing validation record ($_code)" + fi } ################### Private functions below ################################## @@ -168,6 +176,10 @@ _azure_rest() { response="$(_get "$ep")" fi _debug2 response "$response" + + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + _debug2 "http response code $_code" + if [ "$?" != "0" ]; then _err "error $ep" return 1 @@ -189,13 +201,18 @@ _azure_getaccess_token() { _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") - + _debug2 "response $response" + + if [ -Z "$accesstoken" ] ; then + _err "no acccess token received" + return 1 + fi if [ "$?" != "0" ]; then _err "error $response" return 1 fi printf $accesstoken - _debug2 response "$response" + return 0 } From d51c383866a7fb029479e99b28a86c1ff4b9d9aa Mon Sep 17 00:00:00 2001 From: martgras Date: Wed, 24 Jan 2018 15:08:06 +0100 Subject: [PATCH 087/156] remove unused code and fix error handling --- dnsapi/dns_azure.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 24b13bb..1841b07 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -77,6 +77,7 @@ dns_azure_add() _info "validation record added" else _err "error adding validation record ($_code)" + return 1 fi } @@ -149,6 +150,7 @@ dns_azure_rm() _info "validation record removed" else _err "error removing validation record ($_code)" + return 1 fi } @@ -160,15 +162,11 @@ _azure_rest() { data="$3" accesstoken="$4" - _debug "$ep" - export _H1="authorization: Bearer $accesstoken" export _H2="accept: application/json" export _H3="Content-Type: application/json" - _H1="authorization: Bearer $accesstoken" - _H2="accept: application/json" - _H3="Content-Type: application/json" - + + _debug "$ep" if [ "$m" != "GET" ]; then _debug data "$data" response="$(_post "$data" "$ep" "" "$m")" @@ -195,7 +193,6 @@ _azure_getaccess_token() { export _H1="accept: application/json" export _H2="Content-Type: application/x-www-form-urlencoded" - export _H3="" body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" $clientID | _url_encode)&client_secret=$(printf "%s" $clientSecret| _url_encode)&grant_type=client_credentials" _debug data "$body" @@ -203,7 +200,7 @@ _azure_getaccess_token() { accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" - if [ -Z "$accesstoken" ] ; then + if [ -z "$accesstoken" ] ; then _err "no acccess token received" return 1 fi From c7b8debb6eb1e378b63b2df151fcadeae660adf2 Mon Sep 17 00:00:00 2001 From: martgras Date: Thu, 25 Jan 2018 07:01:39 +0100 Subject: [PATCH 088/156] fix travis issues --- dnsapi/dns_azure.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 1841b07..b7ebe54 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -69,11 +69,11 @@ dns_azure_add() _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - acmeRecordURI="https://management.azure.com$(printf '%s' $_domain_id |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" - _debug $acmeRecordURI + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" - if [ "$_code" = "200" ] || [ "$code" = '201' ]; then + if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then _info "validation record added" else _err "error adding validation record ($_code)" @@ -142,8 +142,8 @@ dns_azure_rm() _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - acmeRecordURI="https://management.azure.com$(printf '%s' $_domain_id |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" - _debug $acmeRecordURI + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" if [ "$_code" = "200" ] || [ "$code" = '204' ]; then @@ -194,7 +194,7 @@ _azure_getaccess_token() { export _H1="accept: application/json" export _H2="Content-Type: application/x-www-form-urlencoded" - body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" $clientID | _url_encode)&client_secret=$(printf "%s" $clientSecret| _url_encode)&grant_type=client_credentials" + body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret"| _url_encode)&grant_type=client_credentials" _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") @@ -208,7 +208,7 @@ _azure_getaccess_token() { _err "error $response" return 1 fi - printf $accesstoken + printf "$accesstoken" return 0 } @@ -225,7 +225,7 @@ _get_root() { ## (ZoneListResult with continuation token for the next page of results) ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways ## - _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" $accesstoken + _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" "$accesstoken" # Find matching domain name is Json response while true; do From 00781dd4e1f0be3c9e83872ca9fc7a53bb0370bd Mon Sep 17 00:00:00 2001 From: martgras Date: Thu, 25 Jan 2018 07:06:42 +0100 Subject: [PATCH 089/156] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a2c210e..8e1b22d 100644 --- a/README.md +++ b/README.md @@ -344,6 +344,7 @@ You don't have to do anything manually! 1. Servercow (https://servercow.de) 1. Namesilo (https://www.namesilo.com) 1. InternetX autoDNS API (https://internetx.com) +1. Azure DNS And: From 441c26dd3286bb326523e4d56ea5d303c2571a65 Mon Sep 17 00:00:00 2001 From: martgras Date: Thu, 25 Jan 2018 07:40:35 +0100 Subject: [PATCH 090/156] Update dns_azure.sh --- dnsapi/dns_azure.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index b7ebe54..3c7e4de 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -146,7 +146,7 @@ dns_azure_rm() _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" - if [ "$_code" = "200" ] || [ "$code" = '204' ]; then + if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then _info "validation record removed" else _err "error removing validation record ($_code)" @@ -208,7 +208,7 @@ _azure_getaccess_token() { _err "error $response" return 1 fi - printf "$accesstoken" + printf "%s" "$accesstoken" return 0 } From d1067c60bf93011eb74695ab34fc9cce52fdd0b1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Jan 2018 21:08:20 +0800 Subject: [PATCH 091/156] fix https://github.com/Neilpang/acme.sh/issues/1193#issuecomment-360025170 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 77ab439..48297c7 100755 --- a/acme.sh +++ b/acme.sh @@ -2724,7 +2724,7 @@ _isRealNginxConf() { _debug "_seg_n" "$_seg_n" _skip_ssl=1 - for _listen_i in $(echo "$_seg_n" | grep "^ *listen" | tr -d " "); do + for _listen_i in $(echo "$_seg_n" | tr "\t" ' ' | grep "^ *listen" | tr -d " "); do if [ "$_listen_i" ]; then if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl[ |;]")" ]; then _debug2 "$_listen_i is ssl" From 91607bb2a1e207fa693d3c09dfafee15dd8a912e Mon Sep 17 00:00:00 2001 From: neil Date: Thu, 25 Jan 2018 22:58:11 +0800 Subject: [PATCH 092/156] Update dns_azure.sh --- dnsapi/dns_azure.sh | 406 ++++++++++++++++++++++---------------------- 1 file changed, 201 insertions(+), 205 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 3c7e4de..104a81a 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -7,78 +7,76 @@ # # Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate # -dns_azure_add() -{ - fulldomain=$1 - txtvalue=$2 - - AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" - AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" - AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" - AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" - - if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Subscription ID " - return 1 - fi +dns_azure_add() { + fulldomain=$1 + txtvalue=$2 + + AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" + AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" + AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" + AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" + + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Subscription ID " + return 1 + fi - if [ -z "$AZUREDNS_TENANTID" ] ; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" + if [ -z "$AZUREDNS_TENANTID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" AZUREDNS_CLIENTSECRET="" _err "You didn't specify then Azure Tenant ID " return 1 - fi - - if [ -z "$AZUREDNS_APPID" ] ; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure App ID" - return 1 - fi - - if [ -z "$AZUREDNS_CLIENTSECRET" ]; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Client Secret" - return 1 - fi - #save account details to account conf file. - _saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID" - _saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID" - _saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID" - _saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET" - + fi + + if [ -z "$AZUREDNS_APPID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure App ID" + return 1 + fi + + if [ -z "$AZUREDNS_CLIENTSECRET" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Client Secret" + return 1 + fi + #save account details to account conf file. + _saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID" + _saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID" + _saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID" + _saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET" - accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") + accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug "$acmeRecordURI" + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" + if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then + _info "validation record added" + else + _err "error adding validation record ($_code)" return 1 - fi - _debug _domain_id "$_domain_id" - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" - _debug "$acmeRecordURI" - body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" - _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" - if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then - _info "validation record added" - else - _err "error adding validation record ($_code)" - return 1 - fi + fi } # Usage: fulldomain txtvalue @@ -86,156 +84,154 @@ dns_azure_add() # # Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete # -dns_azure_rm() -{ - fulldomain=$1 - txtvalue=$2 - - AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" - AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" - AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" - AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" - - if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Subscription ID " - return 1 - fi - - if [ -z "$AZUREDNS_TENANTID" ] ; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure Tenant ID " - return 1 - fi - - if [ -z "$AZUREDNS_APPID" ] ;then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify the Azure App ID" - return 1 - fi - - if [ -z "$AZUREDNS_CLIENTSECRET" ]; then - AZUREDNS_SUBSCRIPTIONID="" - AZUREDNS_TENANTID="" - AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify Azure Client Secret" - return 1 - fi +dns_azure_rm() { + fulldomain=$1 + txtvalue=$2 + + AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}" + AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" + AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" + AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" + + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Subscription ID " + return 1 + fi + + if [ -z "$AZUREDNS_TENANTID" ] ; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure Tenant ID " + return 1 + fi + + if [ -z "$AZUREDNS_APPID" ] ;then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify the Azure App ID" + return 1 + fi + + if [ -z "$AZUREDNS_CLIENTSECRET" ]; then + AZUREDNS_SUBSCRIPTIONID="" + AZUREDNS_TENANTID="" + AZUREDNS_APPID="" + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify Azure Client Secret" + return 1 + fi - accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") + accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + _debug "$acmeRecordURI" + body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" + _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" + if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then + _info "validation record removed" + else + _err "error removing validation record ($_code)" return 1 - fi - _debug _domain_id "$_domain_id" - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" - _debug "$acmeRecordURI" - body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" - _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" - if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then - _info "validation record removed" - else - _err "error removing validation record ($_code)" - return 1 - fi + fi } ################### Private functions below ################################## _azure_rest() { - m=$1 - ep="$2" - data="$3" - accesstoken="$4" - - export _H1="authorization: Bearer $accesstoken" - export _H2="accept: application/json" - export _H3="Content-Type: application/json" - - _debug "$ep" - if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$ep" "" "$m")" - else - response="$(_get "$ep")" - fi - _debug2 response "$response" - - _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" - _debug2 "http response code $_code" - - if [ "$?" != "0" ]; then - _err "error $ep" - return 1 - fi - return 0 + m=$1 + ep="$2" + data="$3" + accesstoken="$4" + + export _H1="authorization: Bearer $accesstoken" + export _H2="accept: application/json" + export _H3="Content-Type: application/json" + + _debug "$ep" + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$ep" "" "$m")" + else + response="$(_get "$ep")" + fi + _debug2 response "$response" + + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + _debug2 "http response code $_code" + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + return 0 } ## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token _azure_getaccess_token() { - TENANTID=$1 - clientID=$2 - clientSecret=$3 - - export _H1="accept: application/json" - export _H2="Content-Type: application/x-www-form-urlencoded" - - body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret"| _url_encode)&grant_type=client_credentials" - _debug data "$body" - response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" - accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") - _debug2 "response $response" - - if [ -z "$accesstoken" ] ; then - _err "no acccess token received" - return 1 - fi - if [ "$?" != "0" ]; then - _err "error $response" - return 1 - fi - printf "%s" "$accesstoken" - - return 0 + TENANTID=$1 + clientID=$2 + clientSecret=$3 + + export _H1="accept: application/json" + export _H2="Content-Type: application/x-www-form-urlencoded" + + body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret"| _url_encode)&grant_type=client_credentials" + _debug data "$body" + response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" + accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _debug2 "response $response" + + if [ -z "$accesstoken" ] ; then + _err "no acccess token received" + return 1 + fi + if [ "$?" != "0" ]; then + _err "error $response" + return 1 + fi + printf "%s" "$accesstoken" + return 0 } _get_root() { - domain=$1 - subscriptionId=$2 - accesstoken=$3 - i=2 - p=1 - - ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list - ## returns up to 100 zones in one response therefore handling more results is not not implemented - ## (ZoneListResult with continuation token for the next page of results) - ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways - ## - _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" "$accesstoken" - - # Find matching domain name is Json response - while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) - _debug2 "Checking domain: $h" - if [ -z "$h" ]; then - #not valid - _err "Invalid domain" - return 1 - fi + domain=$1 + subscriptionId=$2 + accesstoken=$3 + i=2 + p=1 + + ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list + ## returns up to 100 zones in one response therefore handling more results is not not implemented + ## (ZoneListResult with continuation token for the next page of results) + ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways + ## + _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" "$accesstoken" + + # Find matching domain name is Json response + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 "Checking domain: $h" + if [ -z "$h" ]; then + #not valid + _err "Invalid domain" + return 1 + fi if _contains "$response" "\"name\":\"$h\"" >/dev/null; then _domain_id=$(printf "%s\n" "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") @@ -245,9 +241,9 @@ _get_root() { return 0 fi return 1 - fi - p=$i - i=$(_math "$i" + 1) - done - return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 } From e4b24d20acb68b741422d3e078fbf572d1d86f36 Mon Sep 17 00:00:00 2001 From: neil Date: Thu, 25 Jan 2018 23:08:56 +0800 Subject: [PATCH 093/156] Update dns_azure.sh --- dnsapi/dns_azure.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 104a81a..9952a16 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -29,9 +29,9 @@ dns_azure_add() { AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" - AZUREDNS_CLIENTSECRET="" - _err "You didn't specify then Azure Tenant ID " - return 1 + AZUREDNS_CLIENTSECRET="" + _err "You didn't specify then Azure Tenant ID " + return 1 fi if [ -z "$AZUREDNS_APPID" ] ; then @@ -194,7 +194,7 @@ _azure_getaccess_token() { body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret"| _url_encode)&grant_type=client_credentials" _debug data "$body" response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" - accesstoken=$(printf "%s\n" "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" if [ -z "$accesstoken" ] ; then @@ -234,7 +234,7 @@ _get_root() { fi if _contains "$response" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(printf "%s\n" "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _domain_id=$(echo "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") if [ "$_domain_id" ]; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain=$h From a4fc802d1b84c49c3357aa2765dead14ec4320e7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Jan 2018 23:18:37 +0800 Subject: [PATCH 094/156] Add selectel.com(selectel.ru) DNS API. fix https://github.com/Neilpang/acme.sh/issues/1220 --- README.md | 1 + dnsapi/README.md | 17 +++++ dnsapi/dns_selectel.sh | 161 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 dnsapi/dns_selectel.sh diff --git a/README.md b/README.md index 8e1b22d..ad3befc 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,7 @@ You don't have to do anything manually! 1. Namesilo (https://www.namesilo.com) 1. InternetX autoDNS API (https://internetx.com) 1. Azure DNS +1. selectel.com(selectel.ru) DNS API And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 1148ea7..32eca13 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -703,6 +703,23 @@ acme.sh --issue --dns dns_azure -d example.com -d www.example.com `AZUREDNS_SUBSCRIPTIONID`, `AZUREDNS_TENANTID`,`AZUREDNS_APPID` and `AZUREDNS_CLIENTSECRET` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 38. Use selectel.com(selectel.ru) domain API to automatically issue cert + +First you need to login to your account to get your API key from: https://my.selectel.ru/profile/apikeys. + +```sh +export SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" + +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_selectel -d example.com -d www.example.com +``` + +The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh new file mode 100644 index 0000000..8dd9b35 --- /dev/null +++ b/dnsapi/dns_selectel.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env sh + +# +#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# + +SL_Api="https://api.selectel.ru/domains/v1" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_selectel_add() { + fulldomain=$1 + txtvalue=$2 + + SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" + + if [ -z "$SL_Key" ]; then + SL_Key="" + _err "You don't specify selectel.ru api key yet." + _err "Please create you key and try again." + return 1 + fi + + #save the api key to the account conf file. + _saveaccountconf_mutable SL_Key "$SL_Key" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _info "Adding record" + if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then + if _contains "$response" "$txtvalue"; then + _info "Added, OK" + return 0 + fi + fi + _err "Add txt record error." + return 1 +} + +#fulldomain txtvalue +dns_selectel_rm() { + fulldomain=$1 + txtvalue=$2 + + SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}" + + if [ -z "$SL_Key" ]; then + SL_Key="" + _err "You don't specify slectel api key yet." + _err "Please create you key and try again." + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _sl_rest GET "/${_domain_id}/records/" + + if ! _contains "$response" "$txtvalue"; then + _err "Txt record not found" + return 1 + fi + + _record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")" + _debug2 "_record_seg" "$_record_seg" + if [ -z "$_record_seg" ]; then + _err "can not find _record_seg" + return 1 + fi + + _record_id="$(echo "$_record_seg" | tr ",}" "\n\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)" + _debug2 "_record_id" "$_record_id" + if [ -z "$_record_id" ]; then + _err "can not find _record_id" + return 1 + fi + + if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then + _err "Delete record error." + return 1 + fi + return 0; +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + + if ! _sl_rest GET "/"; then + return 1 + fi + + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if _contains "$response" "\"name\": \"$h\","; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + _debug "Getting domain id for $h" + if ! _sl_rest GET "/$h"; then + return 1 + fi + _domain_id="$(echo "$response" | tr ",}" "\n\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_sl_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + export _H1="X-Token: $SL_Key" + export _H2="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$SL_Api/$ep" "" "$m")" + else + response="$(_get "$SL_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 03140865f031dfe2d9ee9bb3c3017b23d52f8971 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 25 Jan 2018 23:25:50 +0800 Subject: [PATCH 095/156] fix for existing record --- dnsapi/dns_selectel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh index 8dd9b35..c5e4078 100644 --- a/dnsapi/dns_selectel.sh +++ b/dnsapi/dns_selectel.sh @@ -36,7 +36,7 @@ dns_selectel_add() { _info "Adding record" if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then - if _contains "$response" "$txtvalue"; then + if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then _info "Added, OK" return 0 fi From 72fe7396d6d2779b03b40ec0d7c76fc137423f12 Mon Sep 17 00:00:00 2001 From: martgras Date: Fri, 26 Jan 2018 07:53:47 +0100 Subject: [PATCH 096/156] spelling mistake in error message --- dnsapi/dns_azure.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 9952a16..6e53131 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -30,7 +30,7 @@ dns_azure_add() { AZUREDNS_TENANTID="" AZUREDNS_APPID="" AZUREDNS_CLIENTSECRET="" - _err "You didn't specify then Azure Tenant ID " + _err "You didn't specify the Azure Tenant ID " return 1 fi @@ -125,7 +125,7 @@ dns_azure_rm() { AZUREDNS_TENANTID="" AZUREDNS_APPID="" AZUREDNS_CLIENTSECRET="" - _err "You didn't specify Azure Client Secret" + _err "You didn't specify the Azure Client Secret" return 1 fi From dd171ca44a2ed193c5dd9e21c1aedc6cfe251fde Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Jan 2018 20:14:51 +0800 Subject: [PATCH 097/156] fix format --- dnsapi/dns_azure.sh | 48 +++++++++++++++++++++--------------------- dnsapi/dns_selectel.sh | 4 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 6e53131..2c39a00 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -15,7 +15,7 @@ dns_azure_add() { AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}" AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}" AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}" - + if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" @@ -25,7 +25,7 @@ dns_azure_add() { return 1 fi - if [ -z "$AZUREDNS_TENANTID" ] ; then + if [ -z "$AZUREDNS_TENANTID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -34,7 +34,7 @@ dns_azure_add() { return 1 fi - if [ -z "$AZUREDNS_APPID" ] ; then + if [ -z "$AZUREDNS_APPID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -59,15 +59,15 @@ dns_azure_add() { accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" - return 1 + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then + _err "invalid domain" + return 1 fi _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken" @@ -76,7 +76,7 @@ dns_azure_add() { else _err "error adding validation record ($_code)" return 1 - fi + fi } # Usage: fulldomain txtvalue @@ -102,7 +102,7 @@ dns_azure_rm() { return 1 fi - if [ -z "$AZUREDNS_TENANTID" ] ; then + if [ -z "$AZUREDNS_TENANTID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -111,7 +111,7 @@ dns_azure_rm() { return 1 fi - if [ -z "$AZUREDNS_APPID" ] ;then + if [ -z "$AZUREDNS_APPID" ];then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -131,15 +131,15 @@ dns_azure_rm() { accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET") - if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then - _err "invalid domain" - return 1 + if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then + _err "invalid domain" + return 1 fi _debug _domain_id "$_domain_id" _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" |sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" + acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01" _debug "$acmeRecordURI" body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}" _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken" @@ -165,10 +165,10 @@ _azure_rest() { _debug "$ep" if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$ep" "" "$m")" + _debug data "$data" + response="$(_post "$data" "$ep" "" "$m")" else - response="$(_get "$ep")" + response="$(_get "$ep")" fi _debug2 response "$response" @@ -176,8 +176,8 @@ _azure_rest() { _debug2 "http response code $_code" if [ "$?" != "0" ]; then - _err "error $ep" - return 1 + _err "error $ep" + return 1 fi return 0 } @@ -191,15 +191,15 @@ _azure_getaccess_token() { export _H1="accept: application/json" export _H2="Content-Type: application/x-www-form-urlencoded" - body="resource=$(printf "%s" 'https://management.core.windows.net/'| _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret"| _url_encode)&grant_type=client_credentials" + body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials" _debug data "$body" - response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST" )" - accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") + response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST")" + accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" - if [ -z "$accesstoken" ] ; then + if [ -z "$accesstoken" ]; then _err "no acccess token received" - return 1 + return 1 fi if [ "$?" != "0" ]; then _err "error $response" diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh index c5e4078..460f89c 100644 --- a/dnsapi/dns_selectel.sh +++ b/dnsapi/dns_selectel.sh @@ -42,7 +42,7 @@ dns_selectel_add() { fi fi _err "Add txt record error." - return 1 + return 1 } #fulldomain txtvalue @@ -94,7 +94,7 @@ dns_selectel_rm() { _err "Delete record error." return 1 fi - return 0; + return 0 } #################### Private functions below ################################## From 8c88757451b2ca2bb61814180442b4606555fe16 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Jan 2018 20:39:41 +0800 Subject: [PATCH 098/156] fix format --- dnsapi/dns_azure.sh | 8 ++++---- dnsapi/dns_selectel.sh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index 2c39a00..0834ede 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -111,7 +111,7 @@ dns_azure_rm() { return 1 fi - if [ -z "$AZUREDNS_APPID" ];then + if [ -z "$AZUREDNS_APPID" ]; then AZUREDNS_SUBSCRIPTIONID="" AZUREDNS_TENANTID="" AZUREDNS_APPID="" @@ -162,7 +162,7 @@ _azure_rest() { export _H1="authorization: Bearer $accesstoken" export _H2="accept: application/json" export _H3="Content-Type: application/json" - + _debug "$ep" if [ "$m" != "GET" ]; then _debug data "$data" @@ -173,7 +173,7 @@ _azure_rest() { _debug2 response "$response" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" - _debug2 "http response code $_code" + _debug2 "http response code $_code" if [ "$?" != "0" ]; then _err "error $ep" @@ -197,7 +197,7 @@ _azure_getaccess_token() { accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \") _debug2 "response $response" - if [ -z "$accesstoken" ]; then + if [ -z "$accesstoken" ]; then _err "no acccess token received" return 1 fi diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh index 460f89c..94252d8 100644 --- a/dnsapi/dns_selectel.sh +++ b/dnsapi/dns_selectel.sh @@ -83,7 +83,7 @@ dns_selectel_rm() { return 1 fi - _record_id="$(echo "$_record_seg" | tr ",}" "\n\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)" + _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)" _debug2 "_record_id" "$_record_id" if [ -z "$_record_id" ]; then _err "can not find _record_id" @@ -127,7 +127,7 @@ _get_root() { if ! _sl_rest GET "/$h"; then return 1 fi - _domain_id="$(echo "$response" | tr ",}" "\n\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" + _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)" return 0 fi p=$i From 1c35f46b457fa65612ed7eaa7a9236d6f1f0c3c9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 26 Jan 2018 21:37:30 +0800 Subject: [PATCH 099/156] fix https://github.com/Neilpang/acme.sh/issues/1212 --- acme.sh | 82 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/acme.sh b/acme.sh index 9bdb66a..f64955d 100755 --- a/acme.sh +++ b/acme.sh @@ -3969,6 +3969,16 @@ $_authorizations_map" _on_issue_err "$_post_hook" return 1 fi + + if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then + _debug "Found cert chain" + cat "$CERT_PATH" > "$CERT_FULLCHAIN_PATH" + _end_n="$(grep -n -- "$END_CERT" "$CERT_FULLCHAIN_PATH" | _head_n 1 | cut -d : -f 1)" + _debug _end_n "$_end_n" + sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" > "$CERT_PATH" + _end_n="$(_math $_end_n + 1)" + sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" > "$CA_CERT_PATH" + fi else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then _err "Sign failed." @@ -4022,47 +4032,49 @@ $_authorizations_map" _cleardomainconf "Le_Vlist" - Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - - if [ "$Le_LinkIssuer" ]; then - if ! _contains "$Le_LinkIssuer" ":"; then - _info "$(__red "Relative issuer link found.")" - Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" - fi - _debug Le_LinkIssuer "$Le_LinkIssuer" - _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" - - _link_issuer_retry=0 - _MAX_ISSUER_RETRY=5 - while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do - _debug _link_issuer_retry "$_link_issuer_retry" - if [ "$ACME_VERSION" = "2" ]; then - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then - break - fi - else - if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then - echo "$BEGIN_CERT" >"$CA_CERT_PATH" - _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" - echo "$END_CERT" >>"$CA_CERT_PATH" + if [ "$ACME_VERSION" = "2" ]; then + _debug "v2 chain." + else + Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') - _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" - cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" - _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" + if [ "$Le_LinkIssuer" ]; then + if ! _contains "$Le_LinkIssuer" ":"; then + _info "$(__red "Relative issuer link found.")" + Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" + fi + _debug Le_LinkIssuer "$Le_LinkIssuer" + _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" - rm -f "$CA_CERT_PATH.der" - break + _link_issuer_retry=0 + _MAX_ISSUER_RETRY=5 + while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do + _debug _link_issuer_retry "$_link_issuer_retry" + if [ "$ACME_VERSION" = "2" ]; then + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then + break + fi + else + if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then + echo "$BEGIN_CERT" >"$CA_CERT_PATH" + _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" + echo "$END_CERT" >>"$CA_CERT_PATH" + cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" + rm -f "$CA_CERT_PATH.der" + break + fi fi + _link_issuer_retry=$(_math $_link_issuer_retry + 1) + _sleep "$_link_issuer_retry" + done + if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then + _err "Max retry for issuer ca cert is reached." fi - _link_issuer_retry=$(_math $_link_issuer_retry + 1) - _sleep "$_link_issuer_retry" - done - if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then - _err "Max retry for issuer ca cert is reached." + else + _debug "No Le_LinkIssuer header found." fi - else - _debug "No Le_LinkIssuer header found." fi + [ -f "$CA_CERT_PATH" ] && _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" + [ -f "$CERT_FULLCHAIN_PATH" ] && _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" Le_CertCreateTime=$(_time) _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime" From 120cde169bd5c3b815994b6190ea830b3bdb7d70 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 27 Jan 2018 17:20:38 +0800 Subject: [PATCH 100/156] fix format --- acme.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index f64955d..2438fd1 100755 --- a/acme.sh +++ b/acme.sh @@ -3972,12 +3972,12 @@ $_authorizations_map" if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then _debug "Found cert chain" - cat "$CERT_PATH" > "$CERT_FULLCHAIN_PATH" + cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH" _end_n="$(grep -n -- "$END_CERT" "$CERT_FULLCHAIN_PATH" | _head_n 1 | cut -d : -f 1)" _debug _end_n "$_end_n" - sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" > "$CERT_PATH" + sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" >"$CERT_PATH" _end_n="$(_math $_end_n + 1)" - sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" > "$CA_CERT_PATH" + sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH" fi else if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then From a4964b90730621e0f6f0b8f4bdc4266b6a9383ff Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Thu, 1 Feb 2018 18:06:26 +0100 Subject: [PATCH 101/156] Add Readme-section about cert removal (#1137) --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ad3befc..9b42fc7 100644 --- a/README.md +++ b/README.md @@ -409,7 +409,18 @@ acme.sh --renew -d example.com --force --ecc ``` -# 12. How to upgrade `acme.sh` +# 12. How to stop cert renewal + +To stop renewal of a cert, you can execute: + +``` +acme.sh --remove -d example.com [--ecc] +``` + +or remove the respective directory (e.g. `~/.acme.sh/example.com`). + + +# 13. How to upgrade `acme.sh` acme.sh is in constant development, so it's strongly recommended to use the latest code. @@ -434,26 +445,26 @@ acme.sh --upgrade --auto-upgrade 0 ``` -# 13. Issue a cert from an existing CSR +# 14. Issue a cert from an existing CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR -# 14. Under the Hood +# 15. Under the Hood Speak ACME language using shell, directly to "Let's Encrypt". TODO: -# 15. Acknowledgments +# 16. Acknowledgments 1. Acme-tiny: https://github.com/diafygi/acme-tiny 2. ACME protocol: https://github.com/ietf-wg-acme/acme 3. Certbot: https://github.com/certbot/certbot -# 16. License & Others +# 17. License & Others License is GPLv3 @@ -462,7 +473,7 @@ Please Star and Fork me. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. -# 17. Donate +# 18. Donate Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) From 47b49f1be93db798529ab6275aa77645a4fc7c86 Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Thu, 1 Feb 2018 18:07:29 +0100 Subject: [PATCH 102/156] Slightly improved --help output, fixes #1137 . --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 48297c7..dbcc872 100755 --- a/acme.sh +++ b/acme.sh @@ -5017,7 +5017,7 @@ Commands: --renew, -r Renew a cert. --renew-all Renew all the certs. --revoke Revoke a cert. - --remove Remove the cert from $PROJECT + --remove Remove the cert from list of certs known to $PROJECT_NAME. --list List all the certs. --showcsr Show the content of a csr. --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job. From a51f109930f34ecdbdc306d30e6ae05d7460bd7d Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 5 Feb 2018 20:28:50 +0800 Subject: [PATCH 103/156] fix https://github.com/Neilpang/acme.sh/issues/1234 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 522d9c5..4b80c12 100755 --- a/acme.sh +++ b/acme.sh @@ -5152,7 +5152,7 @@ install() { #Modify shebang if _exists bash; then _info "Good, bash is found, so change the shebang to use bash as preferred." - _shebang='#!'"$(env bash -c "command -v bash")" + _shebang='#!'"$(bash -c "command -v bash")" _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang" for subf in $_SUB_FOLDERS; do if [ -d "$LE_WORKING_DIR/$subf" ]; then From e27dfbb0bbafa277dd766f8bc2dcb527ffb5e369 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 5 Feb 2018 21:19:48 +0800 Subject: [PATCH 104/156] update doc --- README.md | 135 +++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 9ef1352..c66b7f6 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Ok, you are ready to issue certs now. Show help message: -``` +```sh root@v1:~# acme.sh -h ``` @@ -166,16 +166,16 @@ You must have at least one domain there. You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. -Generated/issued certs will be placed in `~/.acme.sh/example.com/` +The certs will be placed in `~/.acme.sh/example.com/` -The issued cert will be renewed automatically every **60** days. +The certs will be renewed automatically every **60** days. More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 3. Install the issued cert to Apache/Nginx etc. +# 3. Install the cert to Apache/Nginx etc. -After you issue a cert, you probably want to install/copy the cert to your Apache/Nginx or other servers. +After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers. You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future. **Apache** example: @@ -197,9 +197,9 @@ acme.sh --install-cert -d example.com \ Only the domain is required, all the other parameters are optional. -The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission. +The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission. -Install/copy the issued cert/key to the production Apache or Nginx path. +Install/copy the cert/key to the production Apache or Nginx path. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. @@ -242,7 +242,7 @@ Particularly, if you are running an Apache server, you should use Apache mode in Just set string "apache" as the second argument and it will force use of apache plugin automatically. -``` +```sh acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com ``` @@ -262,47 +262,13 @@ It will configure nginx server automatically to verify the domain and then resto So, the config is not changed. -``` +```sh acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# 8. Use DNS mode: - -Support the `dns-01` challenge. - -```bash -acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com -``` - -You should get an output like below: - -``` -Add the following txt record: -Domain:_acme-challenge.example.com -Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c - -Add the following txt record: -Domain:_acme-challenge.www.example.com -Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - -Please add those txt records to the domains. Waiting for the dns to take effect. -``` - -Then just rerun with `renew` argument: - -```bash -acme.sh --renew -d example.com -``` - -Ok, it's finished. - -**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** - -**Please use dns api mode instead.** - -# 9. Automatic DNS API integration +# 8. Automatic DNS API integration If your DNS provider supports API access, we can use that API to automatically issue the certs. @@ -362,6 +328,39 @@ If your DNS provider is not on the supported list above, you can write your own For more details: [How to use DNS API](dnsapi) +# 9. Use DNS manual mode: + +If your dns provider doesn't support any api access, you will have to add the txt record by your hand. + +```bash +acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com +``` + +You should get an output like below: + +```sh +Add the following txt record: +Domain:_acme-challenge.example.com +Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c + +Add the following txt record: +Domain:_acme-challenge.www.example.com +Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +Please add those txt records to the domains. Waiting for the dns to take effect. +``` + +Then just rerun with `renew` argument: + +```bash +acme.sh --renew -d example.com +``` + +Ok, it's done. + +**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** + +**Please use dns api mode instead.** # 10. Issue ECC certificates @@ -394,47 +393,60 @@ Valid values are: 3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** -# 11. How to renew the issued certs -No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. +# 11. Issue Wildcard certificates -However, you can also force to renew any cert: +It's simple, just give a wildcard domain as the `-d` parameter. +```sh +acme.sh --issue -d example.com -d *.example.com --dns dns_cf ``` + + + +# 12. How to renew the certs + +No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. + +However, you can also force to renew a cert: + +```sh acme.sh --renew -d example.com --force ``` or, for ECC cert: -``` +```sh acme.sh --renew -d example.com --force --ecc ``` -# 12. How to stop cert renewal +# 13. How to stop cert renewal -To stop renewal of a cert, you can execute: +To stop renewal of a cert, you can execute the following to remove the cert from the renewal list: -``` +```sh acme.sh --remove -d example.com [--ecc] ``` -or remove the respective directory (e.g. `~/.acme.sh/example.com`). +The cert/key file is not removed from the disk. +You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. -# 13. How to upgrade `acme.sh` + +# 14. How to upgrade `acme.sh` acme.sh is in constant development, so it's strongly recommended to use the latest code. You can update acme.sh to the latest code: -``` +```sh acme.sh --upgrade ``` You can also enable auto upgrade: -``` +```sh acme.sh --upgrade --auto-upgrade ``` @@ -442,31 +454,30 @@ Then **acme.sh** will be kept up to date automatically. Disable auto upgrade: -``` +```sh acme.sh --upgrade --auto-upgrade 0 ``` -# 14. Issue a cert from an existing CSR +# 15. Issue a cert from an existing CSR https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR -# 15. Under the Hood +# 16. Under the Hood Speak ACME language using shell, directly to "Let's Encrypt". TODO: -# 16. Acknowledgments +# 17. Acknowledgments 1. Acme-tiny: https://github.com/diafygi/acme-tiny 2. ACME protocol: https://github.com/ietf-wg-acme/acme -3. Certbot: https://github.com/certbot/certbot -# 17. License & Others +# 18. License & Others License is GPLv3 @@ -475,7 +486,7 @@ Please Star and Fork me. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. -# 18. Donate +# 19. Donate Your donation makes **acme.sh** better: 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) From 7df20e5049e5e6d48ee5ae359d898876cef5dfb5 Mon Sep 17 00:00:00 2001 From: jim-p Date: Mon, 5 Feb 2018 16:37:57 -0500 Subject: [PATCH 105/156] When registering, consider a 200 final response code as an account that already exists. Fixes #1242 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4b80c12..266a2cc 100755 --- a/acme.sh +++ b/acme.sh @@ -3170,7 +3170,7 @@ _regAccount() { if [ "$code" = "" ] || [ "$code" = '201' ]; then echo "$response" >"$ACCOUNT_JSON_PATH" _info "Registered" - elif [ "$code" = '409' ]; then + elif [ "$code" = '409' ] || [ "$code" = '200' ]; then _info "Already registered" else _err "Register account Error: $response" From 6b798b01a80ad0b44d664f62e467676ab31398bd Mon Sep 17 00:00:00 2001 From: jim-p Date: Mon, 5 Feb 2018 16:38:42 -0500 Subject: [PATCH 106/156] Add braces around ACCOUNT_URL when forming the protected= line. Fixes #1243 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4b80c12..c778e2e 100755 --- a/acme.sh +++ b/acme.sh @@ -1787,7 +1787,7 @@ _send_signed_request() { if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' else - protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"$ACCOUNT_URL\""'}' + protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}' fi else protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' From 584fb2904b6b63cf13d71ba20fa184ade62730a0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 6 Feb 2018 21:23:08 +0800 Subject: [PATCH 107/156] fix https://github.com/Neilpang/acme.sh/issues/1191 https://github.com/Neilpang/acme.sh/issues/1246 --- acme.sh | 14 ++++++++++++- dnsapi/dns_inwx.sh | 50 +++------------------------------------------- 2 files changed, 16 insertions(+), 48 deletions(-) diff --git a/acme.sh b/acme.sh index 928a31f..2a0a7ff 100755 --- a/acme.sh +++ b/acme.sh @@ -3594,7 +3594,7 @@ $_authorizations_map" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" _debug entry "$entry" if [ -z "$entry" ]; then - _err "Error, can not get domain token $d" + _err "Error, can not get domain token entry $d" _clearup _on_issue_err "$_post_hook" return 1 @@ -3602,6 +3602,12 @@ $_authorizations_map" token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" _debug token "$token" + if [ -z "$token" ]; then + _err "Error, can not get domain token $entry" + _clearup + _on_issue_err "$_post_hook" + return 1 + fi if [ "$ACME_VERSION" = "2" ]; then uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" else @@ -3609,6 +3615,12 @@ $_authorizations_map" fi _debug uri "$uri" + if [ -z "$uri" ]; then + _err "Error, can not get domain uri. $entry" + _clearup + _on_issue_err "$_post_hook" + return 1 + fi keyauthorization="$token.$thumbprint" _debug keyauthorization "$keyauthorization" diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh index 74440bd..5dfba7d 100755 --- a/dnsapi/dns_inwx.sh +++ b/dnsapi/dns_inwx.sh @@ -35,53 +35,9 @@ dns_inwx_add() { fi _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - - xml_content=$(printf ' - - nameserver.info - - - - - - domain - - %s - - - - type - - TXT - - - - name - - %s - - - - - - - ' "$_domain" "$_sub_domain") - response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - - if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then - _err "Error could net get txt records" - return 1 - fi - if ! printf "%s" "$response" | grep "count" >/dev/null; then - _info "Adding record" - _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" - else - _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+') - _info "Updating record" - _inwx_update_record "$_record_id" "$txtvalue" - fi + _info "Adding record" + _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue" } @@ -147,7 +103,7 @@ dns_inwx_rm() { ' "$_domain" "$_sub_domain") response="$(_post "$xml_content" "$INWX_Api" "" "POST")" - if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then + if ! _contains "$response" "Command completed successfully"; then _err "Error could not get txt records" return 1 fi From 52b945164cb8d13bd0557d0e2e6177ded2a65369 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 20:54:05 +0800 Subject: [PATCH 108/156] fix https://github.com/Neilpang/acme.sh/issues/1247 --- dnsapi/dns_yandex.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index eb60d5a..a0d8a02 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -18,7 +18,6 @@ dns_yandex_add() { curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" - curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" curUri="https://pddimp.yandex.ru/api2/admin/dns/add" curResult="$(_post "${curData}" "${curUri}")" @@ -36,7 +35,6 @@ dns_yandex_rm() { curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" - curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" @@ -61,7 +59,7 @@ _PDD_get_domain() { __last=1 fi - __all_domains="$__all_domains $(echo "$res1" | sed -e "s@,@\n@g" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" + __all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" __page=$(_math $__page + 1) done @@ -72,6 +70,8 @@ _PDD_get_domain() { _debug "finding zone for domain $__t" for d in $__all_domains; do if [ "$d" = "$__t" ]; then + p=$(_math $k - 1) + curSubdomain="$(echo "$fulldomain" | cut -d . -f 1-$p)" echo "$__t" return fi @@ -98,7 +98,6 @@ pdd_get_record_id() { curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" - curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}" | _normalizeJson)" From 694af4aeb108386e9b41d0baa319fb9bfc111976 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 21:12:49 +0800 Subject: [PATCH 109/156] fix https://github.com/Neilpang/acme.sh/issues/1234 --- acme.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 2a0a7ff..0874edc 100755 --- a/acme.sh +++ b/acme.sh @@ -5163,8 +5163,14 @@ install() { if [ -z "$NO_DETECT_SH" ]; then #Modify shebang if _exists bash; then + _bash_path="$(bash -c "command -v bash 2>/dev/null")" + if [ -z "$_bash_path" ]; then + _bash_path="$(bash -c 'echo $SHELL')" + fi + fi + if [ "$_bash_path" ]; then _info "Good, bash is found, so change the shebang to use bash as preferred." - _shebang='#!'"$(bash -c "command -v bash")" + _shebang='#!'"$_bash_path" _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang" for subf in $_SUB_FOLDERS; do if [ -d "$LE_WORKING_DIR/$subf" ]; then From 3e3161c747a60e6782179a42affac11b073518bd Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 22:18:04 +0800 Subject: [PATCH 110/156] fix format --- dnsapi/dns_yandex.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index a0d8a02..7ebb15d 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -71,7 +71,7 @@ _PDD_get_domain() { for d in $__all_domains; do if [ "$d" = "$__t" ]; then p=$(_math $k - 1) - curSubdomain="$(echo "$fulldomain" | cut -d . -f 1-$p)" + curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")" echo "$__t" return fi From 78915896d5eed0eba9ffd65c2403315f7285452f Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 7 Feb 2018 23:49:08 +0800 Subject: [PATCH 111/156] minor, fix error message --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0874edc..a21b24e 100755 --- a/acme.sh +++ b/acme.sh @@ -3321,7 +3321,7 @@ __get_domain_new_authz() { _err "new-authz retry reach the max $_Max_new_authz_retry_times times." fi - if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then + if [ "$code" ] && [ "$code" != '201' ]; then _err "new-authz error: $response" return 1 fi @@ -3501,7 +3501,7 @@ issue() { Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" _debug Le_OrderFinalize "$Le_OrderFinalize" if [ -z "$Le_OrderFinalize" ]; then - _err "Le_OrderFinalize not found." + _err "Create new order error. Le_OrderFinalize not found. $response" _clearup _on_issue_err "$_post_hook" return 1 From da0bd5a9dc414c3b934371145fb8c1ce4073c661 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 09:03:55 +0800 Subject: [PATCH 112/156] begin 2.7.7 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index a21b24e..907ab30 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.6 +VER=2.7.7 PROJECT_NAME="acme.sh" From 6ca5f3d8f6164cb17cc4670dd3f7a6983ad76e21 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 23:23:31 +0800 Subject: [PATCH 113/156] support Zonomi.com dns api: https://github.com/Neilpang/acme.sh/issues/1255 --- README.md | 2 +- acme.sh | 3 ++ dnsapi/README.md | 22 +++++++++++ dnsapi/dns_zonomi.sh | 87 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_zonomi.sh diff --git a/README.md b/README.md index c66b7f6..d6b2c55 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,7 @@ You don't have to do anything manually! 1. InternetX autoDNS API (https://internetx.com) 1. Azure DNS 1. selectel.com(selectel.ru) DNS API - +1. zonomi.com DNS API And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/acme.sh b/acme.sh index 907ab30..ebddf81 100755 --- a/acme.sh +++ b/acme.sh @@ -1561,6 +1561,9 @@ _inithttp() { _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE " fi + if _contains "$(curl --help 2>&1)" "--globoff"; then + _ACME_CURL="$_ACME_CURL -g " + fi fi if [ -z "$_ACME_WGET" ] && _exists "wget"; then diff --git a/dnsapi/README.md b/dnsapi/README.md index 32eca13..5ded260 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -719,6 +719,28 @@ acme.sh --issue --dns dns_selectel -d example.com -d www.example.com The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 39. Use zonomi.com domain API to automatically issue cert + +First you need to login to your account to find your API key from: http://zonomi.com/app/dns/dyndns.jsp + +Your will find your api key in the example urls: + +```sh +https://zonomi.com/app/dns/dyndns.jsp?host=example.com&api_key=1063364558943540954358668888888888 +``` + +```sh +export ZM_Key="1063364558943540954358668888888888" + +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_zonomi -d example.com -d www.example.com +``` + +The `ZM_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + # Use custom API diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh new file mode 100644 index 0000000..e3a5f44 --- /dev/null +++ b/dnsapi/dns_zonomi.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env sh + +# +#ZM_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# +#https://zonomi.com dns api + +ZM_Api="https://zonomi.com/app/dns/dyndns.jsp" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_zonomi_add() { + fulldomain=$1 + txtvalue=$2 + + ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}" + + if [ -z "$ZM_Key" ]; then + ZM_Key="" + _err "You don't specify zonomi api key yet." + _err "Please create your key and try again." + return 1 + fi + + #save the api key to the account conf file. + _saveaccountconf_mutable ZM_Key "$ZM_Key" + + _info "Get existing txt records for $fulldomain" + if ! _zm_request "action=QUERY&name=$fulldomain"; then + _err "error" + return 1 + fi + + if _contains "$response" "' | tr "<" "\n" | grep record | grep 'type="TXT"' | cut -d '"' -f 6); do + _debug2 t "$t" + _qstr="$_qstr&action[$_qindex]=SET&type[$_qindex]=TXT&name[$_qindex]=$fulldomain&value[$_qindex]=$t" + _qindex="$(_math "$_qindex" + 1)" + done + _zm_request "$_qstr" + else + _debug "Just add record" + _zm_request "action=SET&type=TXT&name=$fulldomain&value=$txtvalue" + fi + +} + +#fulldomain txtvalue +dns_zonomi_rm() { + fulldomain=$1 + txtvalue=$2 + + ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}" + if [ -z "$ZM_Key" ]; then + ZM_Key="" + _err "You don't specify zonomi api key yet." + _err "Please create your key and try again." + return 1 + fi + + _zm_request "action=DELETE&type=TXT&name=$fulldomain" + +} + +#################### Private functions below ################################## +#qstr +_zm_request() { + qstr="$1" + + _debug2 "action" "$action" + _debug2 "qstr" "$qstr" + + _zm_url="$ZM_Api?api_key=$ZM_Key&$qstr" + _debug2 "_zm_url" "$_zm_url" + response="$(_get "$_zm_url")" + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + _contains "$response" "OK:" +} From 0159277dbf8c73e7c1fce21fcbba92ef991b46bf Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 10 Feb 2018 23:24:43 +0800 Subject: [PATCH 114/156] fix format --- dnsapi/dns_zonomi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh index e3a5f44..efc9b1f 100644 --- a/dnsapi/dns_zonomi.sh +++ b/dnsapi/dns_zonomi.sh @@ -31,7 +31,7 @@ dns_zonomi_add() { _err "error" return 1 fi - + if _contains "$response" " Date: Sat, 10 Feb 2018 23:34:34 +0800 Subject: [PATCH 115/156] fix format --- dnsapi/dns_zonomi.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh index efc9b1f..52a889e 100644 --- a/dnsapi/dns_zonomi.sh +++ b/dnsapi/dns_zonomi.sh @@ -35,7 +35,7 @@ dns_zonomi_add() { if _contains "$response" "' | tr "<" "\n" | grep record | grep 'type="TXT"' | cut -d '"' -f 6); do _debug2 t "$t" _qstr="$_qstr&action[$_qindex]=SET&type[$_qindex]=TXT&name[$_qindex]=$fulldomain&value[$_qindex]=$t" @@ -71,7 +71,6 @@ dns_zonomi_rm() { _zm_request() { qstr="$1" - _debug2 "action" "$action" _debug2 "qstr" "$qstr" _zm_url="$ZM_Api?api_key=$ZM_Key&$qstr" @@ -79,7 +78,6 @@ _zm_request() { response="$(_get "$_zm_url")" if [ "$?" != "0" ]; then - _err "error $ep" return 1 fi _debug2 response "$response" From 2c83224f07ff1a29cbf1ea88cbf09aac1ccd36ac Mon Sep 17 00:00:00 2001 From: Martin Donlon Date: Sun, 11 Feb 2018 07:37:15 -0800 Subject: [PATCH 116/156] Fixup dns_dreamhost travis failures --- README.md | 1 + dnsapi/README.md | 14 +++++- dnsapi/dns_dreamhost.sh | 97 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 dnsapi/dns_dreamhost.sh diff --git a/README.md b/README.md index c66b7f6..e7b62f7 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ You don't have to do anything manually! 1. Dynu API (https://www.dynu.com) 1. DNSimple API 1. NS1.com API +1. DreamHost.com API 1. DuckDNS.org API 1. Name.com API 1. Dyn Managed DNS API diff --git a/dnsapi/README.md b/dnsapi/README.md index 32eca13..aef528e 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -512,7 +512,7 @@ acme.sh --issue --dns dns_nsone -d example.com -d www.example.com export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" ``` -Please note that since DuckDNS uses StartSSL as their cert provider, thus +Please note that since DuckDNS uses StartSSL as their cert provider, thus --insecure may need to be used when issuing certs: ``` acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org @@ -719,6 +719,18 @@ acme.sh --issue --dns dns_selectel -d example.com -d www.example.com The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 39. Use DreamHost DNS API + +DNS API keys may be created at https://panel.dreamhost.com/?tree=home.api. +Ensure the created key has add and remove privelages. + +``` +export DH_API_Key="" +acme.sh --issue --dns dns_dreamhost -d example.com -d www.example.com +``` + +The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will +be reused when needed. # Use custom API diff --git a/dnsapi/dns_dreamhost.sh b/dnsapi/dns_dreamhost.sh new file mode 100644 index 0000000..35b3444 --- /dev/null +++ b/dnsapi/dns_dreamhost.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env sh + +#Author: RhinoLance +#Report Bugs here: https://github.com/RhinoLance/acme.sh +# + +#define the api endpoint +DH_API_ENDPOINT="https://api.dreamhost.com/" +querystring="" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_dreamhost_add() { + fulldomain=$1 + txtvalue=$2 + + if ! validate "$fulldomain" "$txtvalue"; then + return 1 + fi + + querystring="key=$DH_API_KEY&cmd=dns-add_record&record=$fulldomain&type=TXT&value=$txtvalue" + if ! submit "$querystring"; then + return 1 + fi + + return 0 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_dreamhost_rm() { + fulldomain=$1 + txtvalue=$2 + + if ! validate "$fulldomain" "$txtvalue"; then + return 1 + fi + + querystring="key=$DH_API_KEY&cmd=dns-remove_record&record=$fulldomain&type=TXT&value=$txtvalue" + if ! submit "$querystring"; then + return 1 + fi + + return 0 +} + +#################### Private functions below ################################## + +#send the command to the api endpoint. +submit() { + querystring=$1 + + url="$DH_API_ENDPOINT?$querystring" + + _debug url "$url" + + if ! response="$(_get "$url")"; then + _err "Error <$1>" + return 1 + fi + + if [ -z "$2" ]; then + message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" + if [ -n "$message" ]; then + _err "$message" + return 1 + fi + fi + + _debug response "$response" + + return 0 +} + +#check that we have a valid API Key +validate() { + fulldomain=$1 + txtvalue=$2 + + _info "Using dreamhost" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + #retrieve the API key from the environment variable if it exists, otherwise look for a saved key. + DH_API_KEY="${DH_API_KEY:-$(_readaccountconf_mutable DH_API_KEY)}" + + if [ -z "$DH_API_KEY" ]; then + DH_API_KEY="" + _err "You didn't specify the DreamHost api key yet (export DH_API_KEY=\"\")" + _err "Please login to your control panel, create a key and try again." + return 1 + fi + + #save the api key to the account conf file. + _saveaccountconf_mutable DH_API_KEY "$DH_API_KEY" +} From 012dd6986b259f73ef82610084fc393b841e07d1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 12 Feb 2018 20:01:40 +0800 Subject: [PATCH 117/156] nginx --- acme.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index ebddf81..a4224fc 100755 --- a/acme.sh +++ b/acme.sh @@ -2763,9 +2763,9 @@ _isRealNginxConf() { _left="$(sed -n "${_start_nn},99999p" "$2")" _debug2 _left "$_left" - if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" >/dev/null; then - _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | _head_n 1) - _debug "_end" "$_end" + _end="$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | grep -v server_name | _head_n 1)" + _debug "_end" "$_end" + if [ "$_end" ]; then _end_n=$(echo "$_end" | cut -d : -f 1) _debug "_end_n" "$_end_n" _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p") From 2655e726c9dc2fee656bc642ea0e8e5c7aa866e6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 12 Feb 2018 20:40:24 +0800 Subject: [PATCH 118/156] update dns he --- README.md | 7 +++++++ dnsapi/README.md | 2 +- dnsapi/dns_he.sh | 9 ++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6b2c55..9e3ec1a 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,13 @@ You don't have to do anything manually! 1. Azure DNS 1. selectel.com(selectel.ru) DNS API 1. zonomi.com DNS API + + + + + + + And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/dnsapi/README.md b/dnsapi/README.md index 5ded260..fa0780b 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -585,7 +585,7 @@ For issues, please report to https://github.com/non7top/acme.sh/issues. ## 31. Use Hurricane Electric -Hurricane Electric doesn't have an API so just set your login credentials like so: +Hurricane Electric (https://dns.he.net/) doesn't have an API so just set your login credentials like so: ``` export HE_Username="yourusername" diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 4d1973a..7b854ea 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -19,14 +19,16 @@ dns_he_add() { _txt_value=$2 _info "Using DNS-01 Hurricane Electric hook" + HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}" + HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}" if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then HE_Username= HE_Password= _err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password envoronment variables." return 1 fi - _saveaccountconf HE_Username "$HE_Username" - _saveaccountconf HE_Password "$HE_Password" + _saveaccountconf_mutable HE_Username "$HE_Username" + _saveaccountconf_mutable HE_Password "$HE_Password" # Fills in the $_zone_id _find_zone "$_full_domain" || return 1 @@ -62,7 +64,8 @@ dns_he_rm() { _full_domain=$1 _txt_value=$2 _info "Cleaning up after DNS-01 Hurricane Electric hook" - + HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}" + HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}" # fills in the $_zone_id _find_zone "$_full_domain" || return 1 _debug "Zone id \"$_zone_id\" will be used." From 9144ce746e0e138c0eb34fc05f6efdb3c811375c Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 19:30:54 +0800 Subject: [PATCH 119/156] fix for v2 wildcard --- dnsapi/dns_aws.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index ed31746..bbc5428 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -42,7 +42,7 @@ dns_aws_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _aws_tmpl_xml="UPSERT$fulldomainTXT300\"$txtvalue\"" + _aws_tmpl_xml="CREATE$fulldomainTXT300\"$txtvalue\"" if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "txt record updated success." From 849a6c12be49ae3d03a8b6068451b241b3c5f284 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 20:08:05 +0800 Subject: [PATCH 120/156] fix for acme v2 --- dnsapi/dns_cx.sh | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index e2f0f09..d27cd84 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -36,33 +36,18 @@ dns_cx_add() { return 1 fi - existing_records "$_domain" "$_sub_domain" - _debug count "$count" - if [ "$?" != "0" ]; then - _err "Error get existing records." - return 1 - fi - - if [ "$count" = "0" ]; then - add_record "$_domain" "$_sub_domain" "$txtvalue" - else - update_record "$_domain" "$_sub_domain" "$txtvalue" - fi - - if [ "$?" = "0" ]; then - return 0 - fi - return 1 + add_record "$_domain" "$_sub_domain" "$txtvalue" } -#fulldomain +#fulldomain txtvalue dns_cx_rm() { fulldomain=$1 + txtvalue=$2 REST_API="$CX_Api" if _get_root "$fulldomain"; then record_id="" - existing_records "$_domain" "$_sub_domain" - if ! [ "$record_id" = "" ]; then + existing_records "$_domain" "$_sub_domain" "$txtvalue" + if [ "$record_id" ]; then _rest DELETE "record/$record_id/$_domain_id" "{}" _info "Deleted record ${fulldomain}" fi @@ -114,23 +99,6 @@ add_record() { return 0 } -#update the txt record -#Usage: root sub txtvalue -update_record() { - root=$1 - sub=$2 - txtvalue=$3 - fulldomain="$sub.$root" - - _info "Updating record" - - if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then - return 0 - fi - - return 1 -} - #################### Private functions below ################################## #_acme-challenge.www.domain.com #returns From 64f07d9bf39be06b53475e4219ceb83530345611 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 13 Feb 2018 22:17:20 +0800 Subject: [PATCH 121/156] fix aws for acme v2 --- dnsapi/dns_aws.sh | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index bbc5428..33e7e70 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -42,7 +42,26 @@ dns_aws_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _aws_tmpl_xml="CREATE$fulldomainTXT300\"$txtvalue\"" + _info "Geting existing records for $fulldomain" + if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then + return 1 + fi + + if _contains "$response" "$fulldomain."; then + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _debug "_resource_record" "$_resource_record" + else + _debug "single new add" + fi + + if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then + _info "The txt record already exists, skip" + return 0 + fi + + _debug "Adding records" + + _aws_tmpl_xml="UPSERT$fulldomainTXT300$_resource_record\"$txtvalue\"" if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "txt record updated success." @@ -68,7 +87,20 @@ dns_aws_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _aws_tmpl_xml="DELETE\"$txtvalue\"$fulldomain.TXT300" + _info "Geting existing records for $fulldomain" + if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then + return 1 + fi + + if _contains "$response" "$fulldomain."; then + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _debug "_resource_record" "$_resource_record" + else + _debug "no records exists, skip" + return 0 + fi + + _aws_tmpl_xml="DELETE$_resource_record$fulldomain.TXT300" if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "txt record deleted success." @@ -87,7 +119,6 @@ _get_root() { p=1 if aws_rest GET "2013-04-01/hostedzone"; then - _debug "response" "$response" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug2 "Checking domain: $h" @@ -236,6 +267,7 @@ aws_rest() { fi _ret="$?" + _debug2 response "$response" if [ "$_ret" = "0" ]; then if _contains "$response" " Date: Tue, 13 Feb 2018 22:23:36 +0800 Subject: [PATCH 122/156] fix format --- dnsapi/dns_aws.sh | 4 ++-- dnsapi/dns_cx.sh | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 33e7e70..ee8efb3 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -48,7 +48,7 @@ dns_aws_add() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "single new add" @@ -93,7 +93,7 @@ dns_aws_rm() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "no records exists, skip" diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index d27cd84..b3e0403 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -74,7 +74,6 @@ existing_records() { fi if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then - count=1 record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1) _debug record_id "$record_id" return 0 From 84649e9d20603e402462b09fd1481371cd1b5ebc Mon Sep 17 00:00:00 2001 From: Martin Donlon Date: Tue, 13 Feb 2018 21:02:38 -0800 Subject: [PATCH 123/156] Addressing PR feedback Replace printf with echo Move dreamhost to bottom of DNS API list --- README.md | 2 +- dnsapi/dns_dreamhost.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6c73ba..5e3709a 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,6 @@ You don't have to do anything manually! 1. Dynu API (https://www.dynu.com) 1. DNSimple API 1. NS1.com API -1. DreamHost.com API 1. DuckDNS.org API 1. Name.com API 1. Dyn Managed DNS API @@ -316,6 +315,7 @@ You don't have to do anything manually! 1. Azure DNS 1. selectel.com(selectel.ru) DNS API 1. zonomi.com DNS API +1. DreamHost.com API And: 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api diff --git a/dnsapi/dns_dreamhost.sh b/dnsapi/dns_dreamhost.sh index 35b3444..a401793 100644 --- a/dnsapi/dns_dreamhost.sh +++ b/dnsapi/dns_dreamhost.sh @@ -61,7 +61,7 @@ submit() { fi if [ -z "$2" ]; then - message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" + message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" if [ -n "$message" ]; then _err "$message" return 1 From 7b92371a035bb013746bbffb0d62858850f2690b Mon Sep 17 00:00:00 2001 From: Jose Luis Duran Date: Wed, 14 Feb 2018 06:44:06 -0200 Subject: [PATCH 124/156] Fix key file permissions Introduced in 8201458332ea5898177118097621dbac842ad64f. Related to #1256. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index a4224fc..b9e57a7 100755 --- a/acme.sh +++ b/acme.sh @@ -4545,7 +4545,7 @@ _installcert() { cat "$CERT_KEY_PATH" >"$_real_key" else cat "$CERT_KEY_PATH" >"$_real_key" - chmod 700 "$_real_key" + chmod 600 "$_real_key" fi fi From 5f345d208939bad2408c7e652646dc42ef5bf6d4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 19:39:47 +0800 Subject: [PATCH 125/156] fix https://github.com/Neilpang/acme.sh/issues/1262 --- dnsapi/dns_aws.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index ee8efb3..71f969f 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -48,7 +48,7 @@ dns_aws_add() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | sed 's//"/g' | tr '"' "\n" | grep "$fulldomain." | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "single new add" @@ -93,7 +93,7 @@ dns_aws_rm() { fi if _contains "$response" "$fulldomain."; then - _resource_record="$(echo "$response" | _egrep_o "" | sed "s///" | sed "s###")" + _resource_record="$(echo "$response" | sed 's//"/g' | tr '"' "\n" | grep "$fulldomain." | _egrep_o "" | sed "s///" | sed "s###")" _debug "_resource_record" "$_resource_record" else _debug "no records exists, skip" From 28145a9debc32232ebfe987fe73bec189ac2bf30 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 20:40:49 +0800 Subject: [PATCH 126/156] fix ovh --- dnsapi/dns_cx.sh | 1 - dnsapi/dns_ovh.sh | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_cx.sh b/dnsapi/dns_cx.sh index b3e0403..f2d3ead 100755 --- a/dnsapi/dns_cx.sh +++ b/dnsapi/dns_cx.sh @@ -62,7 +62,6 @@ existing_records() { _debug "Getting txt records" root=$1 sub=$2 - count=0 if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then return 1 fi diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 6009473..296a269 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -79,6 +79,9 @@ _ovh_get_api() { } _initAuth() { + OVH_AK="${OVH_AK:-$(_readaccountconf_mutable OVH_AK)}" + OVH_AS="${OVH_AS:-$(_readaccountconf_mutable OVH_AS)}" + if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then OVH_AK="" OVH_AS="" @@ -87,21 +90,22 @@ _initAuth() { return 1 fi - #save the api key and email to the account conf file. - _saveaccountconf OVH_AK "$OVH_AK" - _saveaccountconf OVH_AS "$OVH_AS" + _saveaccountconf_mutable OVH_AK "$OVH_AK" + _saveaccountconf_mutable OVH_AS "$OVH_AS" + OVH_END_POINT="${OVH_END_POINT:-$(_readaccountconf_mutable OVH_END_POINT)}" if [ -z "$OVH_END_POINT" ]; then OVH_END_POINT="ovh-eu" fi _info "Using OVH endpoint: $OVH_END_POINT" if [ "$OVH_END_POINT" != "ovh-eu" ]; then - _saveaccountconf OVH_END_POINT "$OVH_END_POINT" + _saveaccountconf_mutable OVH_END_POINT "$OVH_END_POINT" fi OVH_API="$(_ovh_get_api $OVH_END_POINT)" _debug OVH_API "$OVH_API" + OVH_CK="${OVH_CK:-$(_readaccountconf_mutable OVH_CK)}" if [ -z "$OVH_CK" ]; then _info "OVH consumer key is empty, Let's get one:" if ! _ovh_authentication; then From a6b6e31cdaf05f0f18d7be98c488a8fe13624376 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 20:52:06 +0800 Subject: [PATCH 127/156] fix dp --- dnsapi/dns_dp.sh | 75 ++++++------------------------------------------ 1 file changed, 9 insertions(+), 66 deletions(-) diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index 301a1f6..bf623e2 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -15,6 +15,8 @@ dns_dp_add() { fulldomain=$1 txtvalue=$2 + DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}" + DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}" if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then DP_Id="" DP_Key="" @@ -24,8 +26,8 @@ dns_dp_add() { fi #save the api key and email to the account conf file. - _saveaccountconf DP_Id "$DP_Id" - _saveaccountconf DP_Key "$DP_Key" + _saveaccountconf_mutable DP_Id "$DP_Id" + _saveaccountconf_mutable DP_Key "$DP_Key" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -33,24 +35,18 @@ dns_dp_add() { return 1 fi - existing_records "$_domain" "$_sub_domain" - _debug count "$count" - if [ "$?" != "0" ]; then - _err "Error get existing records." - return 1 - fi + add_record "$_domain" "$_sub_domain" "$txtvalue" - if [ "$count" = "0" ]; then - add_record "$_domain" "$_sub_domain" "$txtvalue" - else - update_record "$_domain" "$_sub_domain" "$txtvalue" - fi } #fulldomain txtvalue dns_dp_rm() { fulldomain=$1 txtvalue=$2 + + DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}" + DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}" + _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -83,37 +79,6 @@ dns_dp_rm() { } -#usage: root sub -#return if the sub record already exists. -#echos the existing records count. -# '0' means doesn't exist -existing_records() { - _debug "Getting txt records" - root=$1 - sub=$2 - - if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then - return 1 - fi - - if _contains "$response" 'No records'; then - count=0 - return 0 - fi - - if _contains "$response" "Action completed successful"; then - count=$(printf "%s" "$response" | grep -c 'TXT' | tr -d ' ') - record_id=$(printf "%s" "$response" | grep '^' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1) - _debug record_id "$record_id" - return 0 - else - _err "get existing records error." - return 1 - fi - - count=0 -} - #add the txt record. #usage: root sub txtvalue add_record() { @@ -136,28 +101,6 @@ add_record() { return 1 #error } -#update the txt record -#Usage: root sub txtvalue -update_record() { - root=$1 - sub=$2 - txtvalue=$3 - fulldomain="$sub.$root" - - _info "Updating record" - - if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then - return 1 - fi - - if _contains "$response" "Action completed successful"; then - - return 0 - fi - - return 1 #error -} - #################### Private functions below ################################## #_acme-challenge.www.domain.com #returns From c6f5c7f1a3bd48bcbe4193b363f2a6b82add58b2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 22:31:02 +0800 Subject: [PATCH 128/156] fix gd --- dnsapi/dns_gd.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh index f2dd1fd..0e25f9d 100755 --- a/dnsapi/dns_gd.sh +++ b/dnsapi/dns_gd.sh @@ -15,6 +15,8 @@ dns_gd_add() { fulldomain=$1 txtvalue=$2 + GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}" + GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}" if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then GD_Key="" GD_Secret="" @@ -24,8 +26,8 @@ dns_gd_add() { fi #save the api key and email to the account conf file. - _saveaccountconf GD_Key "$GD_Key" - _saveaccountconf GD_Secret "$GD_Secret" + _saveaccountconf_mutable GD_Key "$GD_Key" + _saveaccountconf_mutable GD_Secret "$GD_Secret" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -36,8 +38,27 @@ dns_gd_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" + _debug "Getting existing records" + if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then + return 1 + fi + + if _contains "$response" "$txtvalue"; then + _info "The record is existing, skip" + return 0; + fi + + _add_data="{\"data\":\"$txtvalue\"}" + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + _debug2 t "$t" + if [ "$t" ]; then + _add_data="$_add_data,{\"data\":$t}" + fi + done + _debug2 _add_data "$_add_data" + _info "Adding record" - if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then + if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then if [ "$response" = "{}" ]; then _info "Added, sleeping 10 seconds" _sleep 10 @@ -56,7 +77,47 @@ dns_gd_add() { #fulldomain dns_gd_rm() { fulldomain=$1 + txtvalue=$2 + + GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}" + GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting existing records" + if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then + return 1 + fi + + if ! _contains "$response" "$txtvalue"; then + _info "The record is not existing, skip" + return 0; + fi + + _add_data="" + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + _debug2 t "$t" + if [ "$t" ] && [ "$t" != "\"$txtvalue\"" ]; then + if [ "$_add_data" ]; then + _add_data="$_add_data,{\"data\":$t}" + else + _add_data="{\"data\":$t}" + fi + fi + done + if [ -z "$_add_data" ]; then + _add_data="{\"data\":\"\"}" + fi + _debug2 _add_data "$_add_data" + _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; } #################### Private functions below ################################## From d8eb08e21405dc65dc2d9312ec8e040fd016e565 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 14 Feb 2018 22:36:17 +0800 Subject: [PATCH 129/156] fix format --- dnsapi/dns_gd.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh index 0e25f9d..5fb1b17 100755 --- a/dnsapi/dns_gd.sh +++ b/dnsapi/dns_gd.sh @@ -45,11 +45,11 @@ dns_gd_add() { if _contains "$response" "$txtvalue"; then _info "The record is existing, skip" - return 0; + return 0 fi _add_data="{\"data\":\"$txtvalue\"}" - for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do _debug2 t "$t" if [ "$t" ]; then _add_data="$_add_data,{\"data\":$t}" @@ -98,11 +98,11 @@ dns_gd_rm() { if ! _contains "$response" "$txtvalue"; then _info "The record is not existing, skip" - return 0; + return 0 fi _add_data="" - for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do + for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do _debug2 t "$t" if [ "$t" ] && [ "$t" != "\"$txtvalue\"" ]; then if [ "$_add_data" ]; then @@ -117,7 +117,7 @@ dns_gd_rm() { fi _debug2 _add_data "$_add_data" - _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; + _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]" } #################### Private functions below ################################## From b51ed9bbb74356fa54e83b86204232499ccb5edd Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 10:29:03 +0800 Subject: [PATCH 130/156] https://github.com/Neilpang/acme.sh/issues/1251 --- acme.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acme.sh b/acme.sh index b9e57a7..8cd9c59 100755 --- a/acme.sh +++ b/acme.sh @@ -3598,6 +3598,10 @@ $_authorizations_map" _debug entry "$entry" if [ -z "$entry" ]; then _err "Error, can not get domain token entry $d" + _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" + if [ "$_supported_vtypes" ]; then + _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype" + fi _clearup _on_issue_err "$_post_hook" return 1 From ce6c7d4b594ccc0ecc253c459ffa8020fb1ec447 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 10:51:13 +0800 Subject: [PATCH 131/156] fix dp --- dnsapi/dns_dp.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index bf623e2..3cc720a 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -93,12 +93,7 @@ add_record() { return 1 fi - if _contains "$response" "Action completed successful"; then - - return 0 - fi - - return 1 #error + _contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists" } #################### Private functions below ################################## From 0096ef4ddb5e6b86c512a6a4a57afc62ba725701 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 12:26:35 +0800 Subject: [PATCH 132/156] fix ali --- dnsapi/dns_ali.sh | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh index f796f07..543a0a5 100755 --- a/dnsapi/dns_ali.sh +++ b/dnsapi/dns_ali.sh @@ -10,6 +10,8 @@ dns_ali_add() { fulldomain=$1 txtvalue=$2 + Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}" + Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}" if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then Ali_Key="" Ali_Secret="" @@ -18,8 +20,8 @@ dns_ali_add() { fi #save the api key and secret to the account conf file. - _saveaccountconf Ali_Key "$Ali_Key" - _saveaccountconf Ali_Secret "$Ali_Secret" + _saveaccountconf_mutable Ali_Key "$Ali_Key" + _saveaccountconf_mutable Ali_Secret "$Ali_Secret" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -32,6 +34,15 @@ dns_ali_add() { dns_ali_rm() { fulldomain=$1 + txtvalue=$2 + Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}" + Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + return 1 + fi + _clean } @@ -76,16 +87,14 @@ _ali_rest() { return 1 fi + _debug2 response "$response" if [ -z "$2" ]; then - message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" - if [ -n "$message" ]; then + message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" + if [ "$message" ]; then _err "$message" return 1 fi fi - - _debug2 response "$response" - return 0 } _ali_urlencode() { @@ -112,12 +121,14 @@ _ali_nonce() { } _check_exist_query() { + _qdomain="$1" + _qsubdomain="$2" query='' query=$query'AccessKeyId='$Ali_Key query=$query'&Action=DescribeDomainRecords' - query=$query'&DomainName='$1 + query=$query'&DomainName='$_qdomain query=$query'&Format=json' - query=$query'&RRKeyWord=_acme-challenge' + query=$query'&RRKeyWord='$_qsubdomain query=$query'&SignatureMethod=HMAC-SHA1' query=$query"&SignatureNonce=$(_ali_nonce)" query=$query'&SignatureVersion=1.0' @@ -169,17 +180,21 @@ _describe_records_query() { } _clean() { - _check_exist_query "$_domain" + _check_exist_query "$_domain" "$_sub_domain" if ! _ali_rest "Check exist records" "ignore"; then return 1 fi - records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" - printf "%s" "$records" \ - | while read -r record_id; do - _delete_record_query "$record_id" - _ali_rest "Delete record $record_id" "ignore" - done + record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)" + _debug2 record_id "$record_id" + + if [ -z "$record_id" ]; then + _debug "record not found, skip" + else + _delete_record_query "$record_id" + _ali_rest "Delete record $record_id" "ignore" + fi + } _timestamp() { From f213215c81c2b3df4ea18cdc0ac86e757099c050 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 12:38:45 +0800 Subject: [PATCH 133/156] fix lua --- dnsapi/dns_lua.sh | 50 +++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index 00c5443..be678b9 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -17,6 +17,8 @@ dns_lua_add() { fulldomain=$1 txtvalue=$2 + LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" + LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then LUA_Key="" LUA_Email="" @@ -26,8 +28,8 @@ dns_lua_add() { fi #save the api key and email to the account conf file. - _saveaccountconf LUA_Key "$LUA_Key" - _saveaccountconf LUA_Email "$LUA_Email" + _saveaccountconf_mutable LUA_Key "$LUA_Key" + _saveaccountconf_mutable LUA_Email "$LUA_Email" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -38,50 +40,26 @@ dns_lua_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _debug "Getting txt records" - _LUA_rest GET "zones/${_domain_id}/records" - - if ! _contains "$response" "\"id\":"; then - _err "Error" - return 1 - fi - - count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ") - _debug count "$count" - if [ "$count" = "0" ]; then - _info "Adding record" - if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if _contains "$response" "$fulldomain"; then - _info "Added" - #todo: check if the record takes effect - return 0 - else - _err "Add txt record error." - return 1 - fi - fi - _err "Add txt record error." - else - _info "Updating record" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1) - _debug "record_id" "$record_id" - - _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}" - if [ "$?" = "0" ] && _contains "$response" "updated_at"; then - _info "Updated!" + _info "Adding record" + if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if _contains "$response" "$fulldomain"; then + _info "Added" #todo: check if the record takes effect return 0 + else + _err "Add txt record error." + return 1 fi - _err "Update error" - return 1 fi - } #fulldomain dns_lua_rm() { fulldomain=$1 txtvalue=$2 + + LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" + LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" From abd0dad2bf500885f2fdb005b51c43f76380fcf9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 20:35:31 +0800 Subject: [PATCH 134/156] fix https://github.com/Neilpang/acme.sh/issues/1145#issuecomment-365863118 --- dnsapi/dns_ovh.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 296a269..2669cc8 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -90,6 +90,10 @@ _initAuth() { return 1 fi + if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then + _info "It seems that your ovh key is changed, let's clear consumer key first." + _clearaccountconf OVH_CK + fi _saveaccountconf_mutable OVH_AK "$OVH_AK" _saveaccountconf_mutable OVH_AS "$OVH_AS" From a63766a0050065b90596eec429c93015a6099e3f Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 15 Feb 2018 21:04:53 +0800 Subject: [PATCH 135/156] fix format --- README.md | 6 +++--- acme.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 66cac02..db2e024 100644 --- a/README.md +++ b/README.md @@ -317,13 +317,13 @@ You don't have to do anything manually! 1. zonomi.com DNS API 1. DreamHost.com API + And: -1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api - (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) +**lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api + (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)** - **More APIs coming soon...** If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. diff --git a/acme.sh b/acme.sh index 8cd9c59..2be989f 100755 --- a/acme.sh +++ b/acme.sh @@ -3598,7 +3598,7 @@ $_authorizations_map" _debug entry "$entry" if [ -z "$entry" ]; then _err "Error, can not get domain token entry $d" - _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" + _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" if [ "$_supported_vtypes" ]; then _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype" fi From 90e587a9742569f2ba0752ac2d18a553869c05a4 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Thu, 15 Feb 2018 15:34:47 -0500 Subject: [PATCH 136/156] add vault deploy hook script --- deploy/vault.sh | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 deploy/vault.sh diff --git a/deploy/vault.sh b/deploy/vault.sh new file mode 100644 index 0000000..b95d8af --- /dev/null +++ b/deploy/vault.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +# Here is a script to deploy cert to hashicorp vault +# (https://www.vaultproject.io/) +# +# it requires the vault binary to be available in PATH, and the following +# environment variables: +# +# VAULT_PREFIX - this contains the prefix path in vault +# VAULT_ADDR - vault requires this to find your vault server +# +# additionally, you need to ensure that VAULT_TOKEN is avialable or +# `vault auth` has applied the appropriate authorization for the vault binary +# to access the vault server + +#returns 0 means success, otherwise error. + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +vault_deploy() { + + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + # validate required env vars + if [ -z "$VAULT_PREFIX" ] + then + _err "VAULT_PREFIX needs to be defined (contains prefix path in vault)" + return 1 + fi + + if [ -z "$VAULT_ADDR" ] + then + _err "VAULT_ADDR needs to be defined (contains vault connection address)" + return 1 + fi + + VAULT_CMD=$(which vault) + if [ ! $? ] + then + _err "cannot find vault binary!" + return 1 + fi + + $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1 + $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1 + $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1 + +} From d5865989cfb0a424362b364f04d5f88a5b264e35 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 10:48:25 +0800 Subject: [PATCH 137/156] update doc --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index db2e024..220168b 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ https://github.com/Neilpang/acmetest - Webroot mode - Standalone mode - Apache mode -- Nginx mode ( Beta ) +- Nginx mode - DNS mode - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) @@ -238,7 +238,7 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. -Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder. +Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder. Just set string "apache" as the second argument and it will force use of apache plugin automatically. @@ -246,6 +246,10 @@ Just set string "apache" as the second argument and it will force use of apache acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com ``` +**This apache mode is only to issue the cert, it will not change your apache config files. +You will need to configure your website config files to use the cert by yourself. +We don't want to mess your apache server, don't worry.** + More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert # 7. Use Nginx mode @@ -266,6 +270,10 @@ So, the config is not changed. acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` +**This apache mode is only to issue the cert, it will not change your apache config files. +You will need to configure your website config files to use the cert by yourself. +We don't want to mess your apache server, don't worry.** + More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert # 8. Automatic DNS API integration @@ -332,7 +340,7 @@ For more details: [How to use DNS API](dnsapi) # 9. Use DNS manual mode: -If your dns provider doesn't support any api access, you will have to add the txt record by your hand. +If your dns provider doesn't support any api access, you can add the txt record by your hand. ```bash acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com @@ -370,7 +378,7 @@ Ok, it's done. And we support them too! -Just set the `length` parameter with a prefix `ec-`. +Just set the `keylength` parameter with a prefix `ec-`. For example: @@ -386,7 +394,7 @@ acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 ``` -Please look at the last parameter above. +Please look at the `keylength` parameter above. Valid values are: From c1f5229906414e78d24330568409c9d91a4fef7a Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 11:21:14 +0800 Subject: [PATCH 138/156] update doc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 220168b..1f9002c 100644 --- a/README.md +++ b/README.md @@ -270,9 +270,9 @@ So, the config is not changed. acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com ``` -**This apache mode is only to issue the cert, it will not change your apache config files. +**This nginx mode is only to issue the cert, it will not change your nginx config files. You will need to configure your website config files to use the cert by yourself. -We don't want to mess your apache server, don't worry.** +We don't want to mess your nginx server, don't worry.** More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert From fac0beaa0ab48a6e63b08474c66b3159dd748bca Mon Sep 17 00:00:00 2001 From: Jose Luis Duran Date: Fri, 16 Feb 2018 11:23:10 -0200 Subject: [PATCH 139/156] Add support for strongSwan deploys in FreeBSD Related to 8ea800205c2e5496b63e3244dc4849d629acc1ad --- deploy/strongswan.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/strongswan.sh b/deploy/strongswan.sh index f991d69..3d5f1b3 100644 --- a/deploy/strongswan.sh +++ b/deploy/strongswan.sh @@ -22,6 +22,8 @@ strongswan_deploy() { _ipsec=/usr/sbin/ipsec elif [ -x /usr/sbin/strongswan ]; then _ipsec=/usr/sbin/strongswan + elif [ -x /usr/local/sbin/ipsec ]; then + _ipsec=/usr/local/sbin/ipsec else _err "no strongswan or ipsec command is detected" return 1 From b8418ced44167f1923354ff34441a58ddcef0d88 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Fri, 16 Feb 2018 09:01:26 -0500 Subject: [PATCH 140/156] syntax fixes --- deploy/vault.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/deploy/vault.sh b/deploy/vault.sh index b95d8af..8a6c55b 100644 --- a/deploy/vault.sh +++ b/deploy/vault.sh @@ -33,21 +33,18 @@ vault_deploy() { _debug _cfullchain "$_cfullchain" # validate required env vars - if [ -z "$VAULT_PREFIX" ] - then + if [ -z "$VAULT_PREFIX" ]; then _err "VAULT_PREFIX needs to be defined (contains prefix path in vault)" return 1 fi - if [ -z "$VAULT_ADDR" ] - then + if [ -z "$VAULT_ADDR" ]; then _err "VAULT_ADDR needs to be defined (contains vault connection address)" return 1 fi VAULT_CMD=$(which vault) - if [ ! $? ] - then + if [ ! $? ]; then _err "cannot find vault binary!" return 1 fi From c86755f1ab80e0de9c238e266bdd05137b366000 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Fri, 16 Feb 2018 09:19:47 -0500 Subject: [PATCH 141/156] format fix --- deploy/vault.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/vault.sh b/deploy/vault.sh index 8a6c55b..7b91ec5 100644 --- a/deploy/vault.sh +++ b/deploy/vault.sh @@ -44,7 +44,7 @@ vault_deploy() { fi VAULT_CMD=$(which vault) - if [ ! $? ]; then + if [ ! $? ]; then _err "cannot find vault binary!" return 1 fi From 6d6b2efdb5131e77a1c4efa08f857b29013ae1d3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 23:16:25 +0800 Subject: [PATCH 142/156] fix he for solaris --- acme.sh | 2 +- dnsapi/dns_he.sh | 76 ++++++++++++++++++------------------------------ 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/acme.sh b/acme.sh index 2be989f..4829507 100755 --- a/acme.sh +++ b/acme.sh @@ -1838,7 +1838,7 @@ _send_signed_request() { _body="$response" if [ "$needbase64" ]; then _body="$(echo "$_body" | _dbase64)" - _debug2 _body "$_body" + _debug3 _body "$_body" fi if _contains "$_body" "JWS has invalid anti-replay nonce"; then diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 7b854ea..d1744dc 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -75,17 +75,19 @@ dns_he_rm() { body="$body&hosted_dns_zoneid=$_zone_id" body="$body&menu=edit_zone" body="$body&hosted_dns_editzone=" - domain_regex="$(echo "$_full_domain" | sed 's/\./\\./g')" # escape dots - _record_id=$(_post "$body" "https://dns.he.net/" \ - | tr -d '\n' \ - | _egrep_o "data=\""${_txt_value}"([^>]+>){6}[^<]+<[^;]+;deleteRecord\('[0-9]+','${domain_regex}','TXT'\)" \ - | _egrep_o "[0-9]+','${domain_regex}','TXT'\)$" \ - | _egrep_o "^[0-9]+" - ) - # The series of egreps above could have been done a bit shorter but - # I wanted to double-check whether it's the correct record (in case - # HE changes their website somehow). + response="$(_post "$body" "https://dns.he.net/")" + _debug2 "response" "$response" + if ! _contains "$response" "$_txt_value"; then + _debug "The txt record is not found, just skip" + return 0 + fi + _record_id="$(echo "$response" | tr -d "#" | sed "s/ Date: Fri, 16 Feb 2018 15:40:05 -0200 Subject: [PATCH 143/156] Set the account key file permissions --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index 4829507..d958b07 100755 --- a/acme.sh +++ b/acme.sh @@ -1281,6 +1281,7 @@ _create_account_key() { else #generate account key _createkey "$length" "$ACCOUNT_KEY_PATH" + chmod 600 "$ACCOUNT_KEY_PATH" fi } From d84665cb64afb4dbb81eed775e50ce7f7fddf060 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 17 Feb 2018 10:16:04 +0800 Subject: [PATCH 144/156] fix https://github.com/Neilpang/acme.sh/issues/1271 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 1586b73..f79ccc8 100755 --- a/acme.sh +++ b/acme.sh @@ -2010,8 +2010,8 @@ _startserver() { SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork #Adding bind to local-address - if [ "$_local_address" ]; then - $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${_local_address}" + if [ "$ncaddr" ]; then + $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi _debug "_NC" "$_NC" From 5c568d6999905414781812f2d8089fe6332d03b4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 17 Feb 2018 10:31:34 +0800 Subject: [PATCH 145/156] https://github.com/Neilpang/acme.sh/issues/1277 --- acme.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index f79ccc8..20f446a 100755 --- a/acme.sh +++ b/acme.sh @@ -2007,6 +2007,10 @@ _startserver() { _NC="$_NC -6" fi + if [ "$DEBUG" -gt "1" ]; then + _NC="$_NC -d -d -v" + fi + SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork #Adding bind to local-address @@ -2014,8 +2018,8 @@ _startserver() { $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi - _debug "_NC" "$_NC" - $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & + _debug "_NC" "$_NC $SOCAT_OPTIONS" + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From b00919c6928571baacacb2210662ad30dc2b8ba0 Mon Sep 17 00:00:00 2001 From: nytral Date: Sat, 17 Feb 2018 15:08:13 +0100 Subject: [PATCH 146/156] various fixes --- dnsapi/dns_lua.sh | 6 ++++-- dnsapi/dns_nsone.sh | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index be678b9..9bf8cd9 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/bash # bug reports to dev@1e.ca @@ -8,7 +8,6 @@ #LUA_Email="user@luadns.net" LUA_Api="https://api.luadns.com/v1" -LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64) ######## Public functions ##################### @@ -19,6 +18,8 @@ dns_lua_add() { LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" + LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64) + if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then LUA_Key="" LUA_Email="" @@ -60,6 +61,7 @@ dns_lua_rm() { LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}" LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}" + LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64) _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" diff --git a/dnsapi/dns_nsone.sh b/dnsapi/dns_nsone.sh index adf1f42..00e186d 100644 --- a/dnsapi/dns_nsone.sh +++ b/dnsapi/dns_nsone.sh @@ -59,10 +59,10 @@ dns_nsone_add() { _err "Add txt record error." else _info "Updating record" - record_id=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain.\",[^{]*\"type\":\"TXT\",\"id\":\"[^,]*\"" | _head_n 1 | cut -d: -f7 | cut -d, -f1) - _debug "record_id" "$record_id" + prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1) + _debug "prev_txt" "$prev_txt" - _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}" + _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}" if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then _info "Updated!" #todo: check if the record takes effect From 55787ff7b9d65ac64684dac19d7fe9bc5d1ba813 Mon Sep 17 00:00:00 2001 From: nytral Date: Sat, 17 Feb 2018 15:12:19 +0100 Subject: [PATCH 147/156] other fixes --- dnsapi/dns_lua.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index 9bf8cd9..30c1557 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env sh # bug reports to dev@1e.ca From d6f8d6374231df715f02ec8efbdd5a8d6fee7118 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 19 Feb 2018 12:43:56 +0800 Subject: [PATCH 148/156] fix https://github.com/Neilpang/acme.sh/issues/1286 --- dnsapi/dns_yandex.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 7ebb15d..5fbb09d 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,7 +16,7 @@ dns_yandex_add() { _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" - curDomain=$(_PDD_get_domain "$fulldomain") + _PDD_get_domain "$fulldomain" _debug "Found suitable domain in pdd: $curDomain" curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" curUri="https://pddimp.yandex.ru/api2/admin/dns/add" @@ -33,7 +33,7 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curDomain=$(_PDD_get_domain "$fulldomain") + _PDD_get_domain "$fulldomain" _debug "Found suitable domain in pdd: $curDomain" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" @@ -72,8 +72,8 @@ _PDD_get_domain() { if [ "$d" = "$__t" ]; then p=$(_math $k - 1) curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")" - echo "$__t" - return + curDomain="$__t" + return 0 fi done k=$(_math $k + 1) @@ -96,7 +96,7 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain=$(_PDD_get_domain "$fulldomain") + _PDD_get_domain "$fulldomain" _debug "Found suitable domain in pdd: $curDomain" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" From 9ad7ac632aa6920d71b9aaa21f93a8cede0518fe Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 19 Feb 2018 21:07:01 +0800 Subject: [PATCH 149/156] fix https://github.com/Neilpang/acme.sh/issues/1284#issuecomment-366616855 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 20f446a..3aa2c7b 100755 --- a/acme.sh +++ b/acme.sh @@ -2007,7 +2007,7 @@ _startserver() { _NC="$_NC -6" fi - if [ "$DEBUG" -gt "1" ]; then + if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then _NC="$_NC -d -d -v" fi @@ -2015,7 +2015,7 @@ _startserver() { #Adding bind to local-address if [ "$ncaddr" ]; then - $SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" + SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}" fi _debug "_NC" "$_NC $SOCAT_OPTIONS" From 48eaa0e5bfb7579b6719a640eb6253f92c93c842 Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Mon, 19 Feb 2018 17:48:54 +0000 Subject: [PATCH 150/156] Let AWS DNS API code pull creds from instance role Add option (AWS_USE_INSTANCE_ROLE) to have the AWS DNS API driver pull the necessary credentials from the AWS EC2 instance metadata endpoint when required. This is a non-breaking change as it only takes effect when explicitly turned on via the environment variable, and fails safe back to the normal code path. --- dnsapi/dns_aws.sh | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 71f969f..c754341 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -9,6 +9,7 @@ AWS_HOST="route53.amazonaws.com" AWS_URL="https://$AWS_HOST" +AWS_METADATA_URL="http://169.254.169.254/latest/meta-data" AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" @@ -19,6 +20,10 @@ dns_aws_add() { fulldomain=$1 txtvalue=$2 + if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + _use_instance_role + fi + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then @@ -30,8 +35,12 @@ dns_aws_add() { fi #save for future use - _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" - _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" + if [ -n "$AWS_USE_INSTANCE_ROLE" ]; then + _saveaccountconf_mutable AWS_USE_INSTANCE_ROLE "$AWS_USE_INSTANCE_ROLE" + else + _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" + _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -76,6 +85,10 @@ dns_aws_rm() { fulldomain=$1 txtvalue=$2 + if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + _use_instance_role + fi + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" _debug "First detect the root zone" @@ -162,6 +175,34 @@ _get_root() { return 1 } +_use_instance_role() { + if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true | _head_n 1 | grep -Fq 200; then + _err "Unable to fetch IAM role from AWS instance metadata." + return + fi + _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/") + _debug "_aws_role" "$_aws_role" + _aws_creds="$( + _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" \ + | _normalizeJson \ + | tr '{,}' '\n' \ + | while read -r _line; do + _key="$(echo "${_line%%:*}" | tr -d '"')" + _value="${_line#*:}" + _debug3 "_key" "$_key" + _secure_debug3 "_value" "$_value" + case "$_key" in + AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;; + SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;; + Token) echo "AWS_SESSION_TOKEN=$_value" ;; + esac + done \ + | paste -sd' ' - + )" + _secure_debug "_aws_creds" "$_aws_creds" + eval "$_aws_creds" +} + #method uri qstr data aws_rest() { mtd="$1" From 693627a858e22391201ef385388756508da9c070 Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Tue, 20 Feb 2018 00:34:55 +0000 Subject: [PATCH 151/156] Emulate Boto when using role metadata Use the behavior established in the botocore python library to inform how and when instance metadata is fetched in an attempt to acquire valid AWS credentials. - Use it as a fallback when no other credentials are provided - Set the timeout of metadata requests to 1 second --- dnsapi/dns_aws.sh | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index c754341..05a6200 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -20,12 +20,13 @@ dns_aws_add() { fulldomain=$1 txtvalue=$2 - if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" + + if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then _use_instance_role fi - AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" - AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" @@ -34,10 +35,8 @@ dns_aws_add() { return 1 fi - #save for future use - if [ -n "$AWS_USE_INSTANCE_ROLE" ]; then - _saveaccountconf_mutable AWS_USE_INSTANCE_ROLE "$AWS_USE_INSTANCE_ROLE" - else + #save for future use, unless using a role which will be fetched as needed + if [ -z "$_using_instance_role" ]; then _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" fi @@ -85,12 +84,13 @@ dns_aws_rm() { fulldomain=$1 txtvalue=$2 - if [ -n "${AWS_USE_INSTANCE_ROLE:=$(_readaccountconf_mutable AWS_USE_INSTANCE_ROLE)}" ]; then + AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" + AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" + + if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then _use_instance_role fi - AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" - AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -176,14 +176,14 @@ _get_root() { } _use_instance_role() { - if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true | _head_n 1 | grep -Fq 200; then + if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true 1 | _head_n 1 | grep -Fq 200; then _err "Unable to fetch IAM role from AWS instance metadata." return fi - _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/") + _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/" "" 1) _debug "_aws_role" "$_aws_role" _aws_creds="$( - _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" \ + _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" "" 1 \ | _normalizeJson \ | tr '{,}' '\n' \ | while read -r _line; do @@ -201,6 +201,7 @@ _use_instance_role() { )" _secure_debug "_aws_creds" "$_aws_creds" eval "$_aws_creds" + _using_instance_role=true } #method uri qstr data From 759f4f2c62bd5118495937713134f9e7960bd98e Mon Sep 17 00:00:00 2001 From: Mal Graty Date: Tue, 20 Feb 2018 12:40:24 +0000 Subject: [PATCH 152/156] Make the instance metadata fetcher self-contained This is to provide a clean path to future extension work such as adding a _use_container_role function to offer similar support for ECS containers. The $_using_role flag has also been made generic so that future role providers can also make use of it. --- dnsapi/dns_aws.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 05a6200..9c9e931 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -9,7 +9,6 @@ AWS_HOST="route53.amazonaws.com" AWS_URL="https://$AWS_HOST" -AWS_METADATA_URL="http://169.254.169.254/latest/meta-data" AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" @@ -36,7 +35,7 @@ dns_aws_add() { fi #save for future use, unless using a role which will be fetched as needed - if [ -z "$_using_instance_role" ]; then + if [ -z "$_using_role" ]; then _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" fi @@ -176,14 +175,16 @@ _get_root() { } _use_instance_role() { - if ! _get "$AWS_METADATA_URL/iam/security-credentials/" true 1 | _head_n 1 | grep -Fq 200; then + _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" + _debug "_url" "$_url" + if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then _err "Unable to fetch IAM role from AWS instance metadata." return fi - _aws_role=$(_get "$AWS_METADATA_URL/iam/security-credentials/" "" 1) + _aws_role=$(_get "$_url" "" 1) _debug "_aws_role" "$_aws_role" _aws_creds="$( - _get "$AWS_METADATA_URL/iam/security-credentials/$_aws_role" "" 1 \ + _get "$_url$_aws_role" "" 1 \ | _normalizeJson \ | tr '{,}' '\n' \ | while read -r _line; do @@ -201,7 +202,7 @@ _use_instance_role() { )" _secure_debug "_aws_creds" "$_aws_creds" eval "$_aws_creds" - _using_instance_role=true + _using_role=true } #method uri qstr data From 2c45f27356de74567ecd2fcc82842e9a8863ccc3 Mon Sep 17 00:00:00 2001 From: Bob Belnap Date: Tue, 20 Feb 2018 09:11:45 -0500 Subject: [PATCH 153/156] rename deploy hook vault to vault_cli --- deploy/{vault.sh => vault_cli.sh} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename deploy/{vault.sh => vault_cli.sh} (98%) diff --git a/deploy/vault.sh b/deploy/vault_cli.sh similarity index 98% rename from deploy/vault.sh rename to deploy/vault_cli.sh index 7b91ec5..02617c5 100644 --- a/deploy/vault.sh +++ b/deploy/vault_cli.sh @@ -18,7 +18,7 @@ ######## Public functions ##################### #domain keyfile certfile cafile fullchain -vault_deploy() { +vault_cli_deploy() { _cdomain="$1" _ckey="$2" From bae50da799e73b8069393248824da3de14137e72 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Feb 2018 09:45:36 +0800 Subject: [PATCH 154/156] fix https://github.com/Neilpang/acme.sh/issues/1266 --- README.md | 4 +++- acme.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f9002c..1e2defb 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) -# Who are using **acme.sh** +# Who: - [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) - [ruby-china.org](https://ruby-china.org/topics/31983) - [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer)) @@ -204,6 +204,8 @@ Install/copy the cert/key to the production Apache or Nginx path. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. +**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewwed cert in 60 days.** + # 4. Use Standalone server to issue cert **(requires you to be root/sudoer or have permission to listen on port 80 (TCP))** diff --git a/acme.sh b/acme.sh index 3aa2c7b..a81b42d 100755 --- a/acme.sh +++ b/acme.sh @@ -2019,7 +2019,7 @@ _startserver() { fi _debug "_NC" "$_NC $SOCAT_OPTIONS" - $_NC $SOCAT_OPTIONS SYSTEM:"sleep 0.5; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & + $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From 86ef0a260942ef5962ba01e0e7f1aeeeaaa846ab Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Feb 2018 10:05:27 +0800 Subject: [PATCH 155/156] fix https://github.com/Neilpang/acme.sh/issues/1295 --- acme.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index a81b42d..56d10f8 100755 --- a/acme.sh +++ b/acme.sh @@ -5073,7 +5073,7 @@ _installalias() { } -# nocron confighome +# nocron confighome noprofile install() { if [ -z "$LE_WORKING_DIR" ]; then @@ -5082,6 +5082,7 @@ install() { _nocron="$1" _c_home="$2" + _noprofile="$3" if ! _initpath; then _err "Install failed." return 1 @@ -5147,7 +5148,7 @@ install() { _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" - if [ "$IN_CRON" != "1" ]; then + if [ "$IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then _installalias "$_c_home" fi @@ -5373,10 +5374,11 @@ Parameters: " } -# nocron +# nocron noprofile _installOnline() { _info "Installing from online archive." _nocron="$1" + _noprofile="$2" if [ ! "$BRANCH" ]; then BRANCH="master" fi @@ -5397,7 +5399,7 @@ _installOnline() { cd "$PROJECT_NAME-$BRANCH" chmod +x $PROJECT_ENTRY - if ./$PROJECT_ENTRY install "$_nocron"; then + if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then _info "Install success!" fi @@ -5413,7 +5415,7 @@ upgrade() { _initpath export LE_WORKING_DIR cd "$LE_WORKING_DIR" - _installOnline "nocron" + _installOnline "nocron" "noprofile" ); then _info "Upgrade success!" exit 0 From 58f753136a6b92607fe8767b3367ab56108efe56 Mon Sep 17 00:00:00 2001 From: hebbet Date: Wed, 21 Feb 2018 09:01:56 +0100 Subject: [PATCH 156/156] small typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e2defb..ad2a245 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ Install/copy the cert/key to the production Apache or Nginx path. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. -**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewwed cert in 60 days.** +**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewed cert in 60 days.** # 4. Use Standalone server to issue cert