From 6c3430b6e592b0dad0783c4daee0ee692d5e0484 Mon Sep 17 00:00:00 2001 From: 2globalnomads Date: Sun, 2 Jul 2017 23:24:14 +0400 Subject: [PATCH 001/278] Wrote missing cpanel.sh This script I wrote works for me on GoDaddy, but I don't have any other hosting services or cpanel version to test it. Hope it's better than nothing for you. Thanks for your great script! Cheers, Santeri --- deploy/cpanel.sh | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/deploy/cpanel.sh b/deploy/cpanel.sh index bf1332f..6ab012a 100644 --- a/deploy/cpanel.sh +++ b/deploy/cpanel.sh @@ -1,8 +1,9 @@ -#!/usr/bin/env sh - -#Here is the script to deploy the cert to your cpanel account by the cpanel APIs. - -#returns 0 means success, otherwise error. +#!/bin/bash +# Here is the script to deploy the cert to your cpanel using the cpanel API. +# Uses command line uapi. Cpanel username is needed only when run as root. +# Returns 0 when success, otherwise error. +# Written by Santeri Kannisto +# Public domain, 2017 #export DEPLOY_CPANEL_USER=myusername #export DEPLOY_CPANEL_PASSWORD=PASSWORD @@ -10,6 +11,7 @@ ######## Public functions ##################### #domain keyfile certfile cafile fullchain + cpanel_deploy() { _cdomain="$1" _ckey="$2" @@ -23,7 +25,33 @@ cpanel_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _err "Not implemented yet" - return 1 + # read cert and key files and urlencode both + _certstr=`cat "$_ccert"` + _keystr=`cat "$_ckey"` + _cert=$(php -r "echo urlencode(\"$_certstr\");") + _key=$(php -r "echo urlencode(\"$_keystr\");") + + _debug _cert "$_cert" + _debug _key "$_key" + + if [[ $EUID -eq 0 ]] + then + _opt="--user=$DEPLOY_CPANEL_USER SSL install_ssl" + else + _opt="SSL install_ssl" + fi + + _debug _opt "$_opt" + + response=$(uapi $_opt domain="$_cdommain" cert="$_cert" key="$_key") + + if [ $? -ne 0 ] + then + _err "Error in deploying certificate:" + _err "$response" + return 1 + fi + _debug response "$response" + _info "Certificate successfully deployed" } From 5f8b60a0e5653595689703fe68d9b3e99d14d1ab Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 4 Jul 2017 08:23:09 +0800 Subject: [PATCH 002/278] fix https://github.com/Neilpang/acme.sh/issues/926 don't trigger validation for dns manually mode --- acme.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index fa090cd..ef468a4 100755 --- a/acme.sh +++ b/acme.sh @@ -3543,11 +3543,11 @@ issue() { if [ "$d_api" ]; then _info "Found domain api file: $d_api" else - _err "Add the following TXT record:" - _err "Domain: '$(__green "$txtdomain")'" - _err "TXT value: '$(__green "$txt")'" - _err "Please be aware that you prepend _acme-challenge. before your domain" - _err "so the resulting subdomain will be: $txtdomain" + _info "$(__red "Add the following TXT record:")" + _info "$(__red "Domain: '$(__green "$txtdomain")'")" + _info "$(__red "TXT value: '$(__green "$txt")'")" + _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")" + _info "$(__red "so the resulting subdomain will be: $txtdomain")" continue fi @@ -3583,7 +3583,7 @@ issue() { _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit." _err "Please add the TXT records to the domains, and retry again." _clearup - _on_issue_err "$_post_hook" "$vlist" + _on_issue_err "$_post_hook" return 1 fi From 81772fb703af18f7d7f8d7b7b7e3a6d61be14554 Mon Sep 17 00:00:00 2001 From: neil Date: Tue, 4 Jul 2017 09:08:54 +0800 Subject: [PATCH 003/278] minor, fix format --- acme.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index ef468a4..35842f4 100755 --- a/acme.sh +++ b/acme.sh @@ -3544,10 +3544,10 @@ issue() { _info "Found domain api file: $d_api" else _info "$(__red "Add the following TXT record:")" - _info "$(__red "Domain: '$(__green "$txtdomain")'")" - _info "$(__red "TXT value: '$(__green "$txt")'")" - _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")" - _info "$(__red "so the resulting subdomain will be: $txtdomain")" + _info "$(__red "Domain: '$(__green "$txtdomain")'")" + _info "$(__red "TXT value: '$(__green "$txt")'")" + _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")" + _info "$(__red "so the resulting subdomain will be: $txtdomain")" continue fi From 72fcf5ab85779aca28db1f4057e63d4e9ea850d4 Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Fri, 7 Jul 2017 09:51:43 -0500 Subject: [PATCH 004/278] Add 'dns_dyn' DNS challenge validation script for Dyn Managed DNS API --- dnsapi/README.md | 33 +++++ dnsapi/dns_dyn.sh | 340 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 dnsapi/dns_dyn.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 57c360b..41f89a8 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -540,6 +540,39 @@ acme.sh --issue --dns dns_namecom -d example.com -d www.example.com For issues, please report to https://github.com/raidenii/acme.sh/issues. +## 29. Use Dyn Managed DNS API to automatically issue cert + +First, login to your Dyn Managed DNS account: https://portal.dynect.net/login/ + +It is recommended to add a new user specific for API access. + +The minimum "Zones & Records Permissions" required are: +``` +RecordAdd +RecordUpdate +RecordDelete +RecordGet +ZoneGet +ZoneAddNode +ZoneRemoveNode +ZonePublish +``` + +Pass the API user credentials to the environment: +``` +export DYN_Customer="customer" +export DYN_Username="apiuser" +export DYN_Password="secret" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_dyn -d example.com -d www.example.com +``` + +The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_dyn.sh b/dnsapi/dns_dyn.sh new file mode 100644 index 0000000..d399880 --- /dev/null +++ b/dnsapi/dns_dyn.sh @@ -0,0 +1,340 @@ +#!/usr/bin/env sh +# +# Dyn.com Domain API +# +# Author: Gerd Naschenweng +# https://github.com/magicdude4eva +# +# Dyn Managed DNS API +# https://help.dyn.com/dns-api-knowledge-base/ +# +# It is recommended to add a "Dyn Managed DNS" user specific for API access. +# The "Zones & Records Permissions" required by this script are: +# -- +# RecordAdd +# RecordUpdate +# RecordDelete +# RecordGet +# ZoneGet +# ZoneAddNode +# ZoneRemoveNode +# ZonePublish +# -- +# +# Pass credentials before "acme.sh --issue --dns dns_dyn ..." +# -- +# export DYN_Customer="customer" +# export DYN_Username="apiuser" +# export DYN_Password="secret" +# -- + +DYN_API="https://api.dynect.net/REST" + +#REST_API +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "Challenge-code" +dns_dyn_add() { + fulldomain="$1" + txtvalue="$2" + + DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" + DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" + DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" + if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then + DYN_Customer="" + DYN_Username="" + DYN_Password="" + _err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" + return 1 + fi + + #save the config variables to the account conf file. + _saveaccountconf_mutable DYN_Customer "$DYN_Customer" + _saveaccountconf_mutable DYN_Username "$DYN_Username" + _saveaccountconf_mutable DYN_Password "$DYN_Password" + + if ! _dyn_get_authtoken; then + return 1 + fi + + if [ -z "$_dyn_authtoken" ]; then + _dyn_end_session + return 1 + fi + + if ! _dyn_get_zone; then + _dyn_end_session + return 1 + fi + + if ! _dyn_add_record; then + _dyn_end_session + return 1 + fi + + if ! _dyn_publish_zone; then + _dyn_end_session + return 1 + fi + + _dyn_end_session + + return 0 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_dyn_rm() { + fulldomain="$1" + txtvalue="$2" + + DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" + DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" + DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" + if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then + DYN_Customer="" + DYN_Username="" + DYN_Password="" + _err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" + return 1 + fi + + if ! _dyn_get_authtoken; then + return 1 + fi + + if [ -z "$_dyn_authtoken" ]; then + _dyn_end_session + return 1 + fi + + if ! _dyn_get_zone; then + _dyn_end_session + return 1 + fi + + if ! _dyn_get_record_id; then + _dyn_end_session + return 1 + fi + + if [ -z "$_dyn_record_id" ]; then + _dyn_end_session + return 1 + fi + + if ! _dyn_rm_record; then + _dyn_end_session + return 1 + fi + + if ! _dyn_publish_zone; then + _dyn_end_session + return 1 + fi + + _dyn_end_session + + return 0 +} + +#################### Private functions below ################################## + +#get Auth-Token +_dyn_get_authtoken() { + + _info "Start Dyn API Session" + + data="{\"customer_name\":\"$DYN_Customer\", \"user_name\":\"$DYN_Username\", \"password\":\"$DYN_Password\"}" + dyn_url="$DYN_API/Session/" + method="POST" + + _debug data "$data" + _debug dyn_url "$dyn_url" + + export _H1="Content-Type: application/json" + + response="$(_post "$data" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | head -n 1 | sed 's#^"token" *: *"##')" + _info "Token received" + _debug _dyn_authtoken "$_dyn_authtoken" + return 0 + fi + + _dyn_authtoken="" + _err "get token failed" + return 1 +} + +#fulldomain=_acme-challenge.www.domain.com +#returns +# _dyn_zone=domain.com +_dyn_get_zone() { + i=2 + while true; do + domain="$(printf "%s" "$fulldomain" | cut -d . -f "$i-100")" + if [ -z "$domain" ]; then + break + fi + + dyn_url="$DYN_API/Zone/$domain/" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_get "$dyn_url" "" "")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + + _debug dyn_url "$dyn_url" + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _dyn_zone="$domain" + return 0 + fi + i=$(_math "$i" + 1) + done + + _dyn_zone="" + _err "get zone failed" + return 1 +} + +#add TXT record +_dyn_add_record() { + + _info "Adding TXT record" + + data="{\"rdata\":{\"txtdata\":\"$txtvalue\"},\"ttl\":\"300\"}" + dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" + method="POST" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "$data" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _info "TXT Record successfully added" + return 0 + fi + + _err "add TXT record failed" + return 1 +} + +#publish the zone +_dyn_publish_zone() { + + _info "Publishing zone" + + data="{\"publish\":\"true\"}" + dyn_url="$DYN_API/Zone/$_dyn_zone/" + method="PUT" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "$data" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _info "Zone published" + return 0 + fi + + _err "publish zone failed" + return 1 +} + +#get record_id of TXT record so we can delete the record +_dyn_get_record_id() { + + _info "Getting record_id of TXT record" + + dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_get "$dyn_url" "" "")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | head -n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")" + _debug _dyn_record_id "$_dyn_record_id" + return 0 + fi + + _dyn_record_id="" + _err "getting record_id failed" + return 1 +} + +#delete TXT record +_dyn_rm_record() { + + _info "Deleting TXT record" + + dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/$_dyn_record_id/" + method="DELETE" + + _debug dyn_url "$dyn_url" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _info "TXT record successfully deleted" + return 0 + fi + + _err "delete TXT record failed" + return 1 +} + +#logout +_dyn_end_session() { + + _info "End Dyn API Session" + + dyn_url="$DYN_API/Session/" + method="DELETE" + + _debug dyn_url "$dyn_url" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "" "$dyn_url" "" "$method")" + + _debug response "$response" + + _dyn_authtoken="" + return 0 +} + From 0aba5dc8de914cae7256cacdc189e2f31e491b74 Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Fri, 7 Jul 2017 10:22:20 -0500 Subject: [PATCH 005/278] dns_dyn.sh, remove empty line at end --- dnsapi/dns_dyn.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_dyn.sh b/dnsapi/dns_dyn.sh index d399880..c0dc1c7 100644 --- a/dnsapi/dns_dyn.sh +++ b/dnsapi/dns_dyn.sh @@ -337,4 +337,3 @@ _dyn_end_session() { _dyn_authtoken="" return 0 } - From 528d2f29d34a3d9e417a91f02ffde35cc63b2476 Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Fri, 7 Jul 2017 10:33:24 -0500 Subject: [PATCH 006/278] dns_dyn.sh, remove trailing spaces at end of line --- dnsapi/dns_dyn.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_dyn.sh b/dnsapi/dns_dyn.sh index c0dc1c7..e112001 100644 --- a/dnsapi/dns_dyn.sh +++ b/dnsapi/dns_dyn.sh @@ -240,7 +240,7 @@ _dyn_publish_zone() { _info "Publishing zone" - data="{\"publish\":\"true\"}" + data="{\"publish\":\"true\"}" dyn_url="$DYN_API/Zone/$_dyn_zone/" method="PUT" From 13a8c309f5b4de09ac263a5ad2ae0cbeed044f30 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 8 Jul 2017 09:20:12 +0800 Subject: [PATCH 007/278] fix new shellcheck errors --- .travis.yml | 2 +- dnsapi/dns_aws.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7f7e120..2ba02b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ script: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi + - 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 diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 800c3d0..4078257 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -208,7 +208,7 @@ aws_rest() { kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" _debug2 kServiceH "$kServiceH" - kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)" + kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)" _debug2 kSigningH "$kSigningH" signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" From 0b797f596409683f90ed4fd1354f4027d3bf7b3c Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 8 Jul 2017 14:12:31 +0800 Subject: [PATCH 008/278] fix new shellcheck error --- dnsapi/dns_infoblox.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_infoblox.sh b/dnsapi/dns_infoblox.sh index 06ac87e..4cbb214 100644 --- a/dnsapi/dns_infoblox.sh +++ b/dnsapi/dns_infoblox.sh @@ -41,10 +41,10 @@ dns_infoblox_add() { export _H2="Authorization: Basic $Infoblox_CredsEncoded" ## Add the challenge record to the Infoblox grid member - result=$(_post "" "$baseurlnObject" "" "POST") + result="$(_post "" "$baseurlnObject" "" "POST")" ## Let's see if we get something intelligible back from the unit - if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then + if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then _info "Successfully created the txt record" return 0 else @@ -66,7 +66,7 @@ dns_infoblox_rm() { _debug txtvalue "$txtvalue" ## Base64 encode the credentials - Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64) + Infoblox_CredsEncoded="$(printf "%b" "$Infoblox_Creds" | _base64)" ## Construct the HTTP Authorization header export _H1="Accept-Language:en-US" @@ -74,17 +74,17 @@ dns_infoblox_rm() { ## Does the record exist? Let's check. baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty" - result=$(_get "$baseurlnObject") + result="$(_get "$baseurlnObject")" ## Let's see if we get something intelligible back from the grid - if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then + if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then ## Extract the object reference - objRef=$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View") + objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef" ## Delete them! All the stale records! - rmResult=$(_post "" "$objRmUrl" "" "DELETE") + rmResult="$(_post "" "$objRmUrl" "" "DELETE")" ## Let's see if that worked - if echo "$rmResult" | egrep "record:txt/.*:.*/$Infoblox_View"; then + if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then _info "Successfully deleted $objRef" return 0 else From 200287254bce74e8deb1f28da611de432015c837 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 8 Jul 2017 17:25:01 +0800 Subject: [PATCH 009/278] add deactivate-account --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 8f36385..5849eb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,7 @@ RUN for verb in help \ create-domain-key \ createCSR \ deactivate \ + deactivate-account \ ; do \ printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ ; done From babe884b7c7a3b599defc37ff1e392d4c61bd6ad Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Sat, 8 Jul 2017 06:39:18 -0500 Subject: [PATCH 010/278] Replace 'head -n' with the '_head_n' function --- dnsapi/dns_dyn.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_dyn.sh b/dnsapi/dns_dyn.sh index e112001..024e0a3 100644 --- a/dnsapi/dns_dyn.sh +++ b/dnsapi/dns_dyn.sh @@ -156,13 +156,13 @@ _dyn_get_authtoken() { export _H1="Content-Type: application/json" response="$(_post "$data" "$dyn_url" "" "$method")" - sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" _debug response "$response" _debug sessionstatus "$sessionstatus" if [ "$sessionstatus" = "success" ]; then - _dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | head -n 1 | sed 's#^"token" *: *"##')" + _dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')" _info "Token received" _debug _dyn_authtoken "$_dyn_authtoken" return 0 @@ -190,7 +190,7 @@ _dyn_get_zone() { export _H2="Content-Type: application/json" response="$(_get "$dyn_url" "" "")" - sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" _debug dyn_url "$dyn_url" _debug response "$response" @@ -221,7 +221,7 @@ _dyn_add_record() { export _H2="Content-Type: application/json" response="$(_post "$data" "$dyn_url" "" "$method")" - sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" _debug response "$response" _debug sessionstatus "$sessionstatus" @@ -248,7 +248,7 @@ _dyn_publish_zone() { export _H2="Content-Type: application/json" response="$(_post "$data" "$dyn_url" "" "$method")" - sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" _debug response "$response" _debug sessionstatus "$sessionstatus" @@ -273,13 +273,13 @@ _dyn_get_record_id() { export _H2="Content-Type: application/json" response="$(_get "$dyn_url" "" "")" - sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" _debug response "$response" _debug sessionstatus "$sessionstatus" if [ "$sessionstatus" = "success" ]; then - _dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | head -n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")" + _dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | _head_n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")" _debug _dyn_record_id "$_dyn_record_id" return 0 fi @@ -303,7 +303,7 @@ _dyn_rm_record() { export _H2="Content-Type: application/json" response="$(_post "" "$dyn_url" "" "$method")" - sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | head -n 1 | sed 's#^"status" *: *"##')" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" _debug response "$response" _debug sessionstatus "$sessionstatus" From 9bd2d927559fce8fe393cfb935f96c7fd6fc8723 Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Sat, 8 Jul 2017 06:43:21 -0500 Subject: [PATCH 011/278] Update main README.md DNS API list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7780b9a..5c306a5 100644 --- a/README.md +++ b/README.md @@ -336,6 +336,7 @@ You don't have to do anything manually! 1. NS1.com API 1. DuckDNS.org API 1. Name.com API +1. Dyn Managed DNS API And: From 42b2adc03ed35a54f6cb2a237dd37f0e498774f2 Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Fri, 7 Jul 2017 09:51:43 -0500 Subject: [PATCH 012/278] Add 'dns_dyn' DNS challenge validation script for Dyn Managed DNS API dns_dyn.sh, remove empty line at end dns_dyn.sh, remove trailing spaces at end of line Replace 'head -n' with the '_head_n' function Update main README.md DNS API list --- .travis.yml | 2 +- Dockerfile | 1 + README.md | 1 + dnsapi/README.md | 33 ++++ dnsapi/dns_aws.sh | 2 +- dnsapi/dns_dyn.sh | 339 +++++++++++++++++++++++++++++++++++++++++ dnsapi/dns_infoblox.sh | 16 +- 7 files changed, 384 insertions(+), 10 deletions(-) create mode 100644 dnsapi/dns_dyn.sh diff --git a/.travis.yml b/.travis.yml index 7f7e120..2ba02b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ script: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi + - 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 diff --git a/Dockerfile b/Dockerfile index 8f36385..5849eb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,7 @@ RUN for verb in help \ create-domain-key \ createCSR \ deactivate \ + deactivate-account \ ; do \ printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ ; done diff --git a/README.md b/README.md index 7780b9a..5c306a5 100644 --- a/README.md +++ b/README.md @@ -336,6 +336,7 @@ You don't have to do anything manually! 1. NS1.com API 1. DuckDNS.org API 1. Name.com API +1. Dyn Managed DNS API And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 57c360b..41f89a8 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -540,6 +540,39 @@ acme.sh --issue --dns dns_namecom -d example.com -d www.example.com For issues, please report to https://github.com/raidenii/acme.sh/issues. +## 29. Use Dyn Managed DNS API to automatically issue cert + +First, login to your Dyn Managed DNS account: https://portal.dynect.net/login/ + +It is recommended to add a new user specific for API access. + +The minimum "Zones & Records Permissions" required are: +``` +RecordAdd +RecordUpdate +RecordDelete +RecordGet +ZoneGet +ZoneAddNode +ZoneRemoveNode +ZonePublish +``` + +Pass the API user credentials to the environment: +``` +export DYN_Customer="customer" +export DYN_Username="apiuser" +export DYN_Password="secret" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_dyn -d example.com -d www.example.com +``` + +The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + + # Use custom API If your API is not supported yet, you can write your own DNS API. diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 800c3d0..4078257 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -208,7 +208,7 @@ aws_rest() { kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" _debug2 kServiceH "$kServiceH" - kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)" + kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)" _debug2 kSigningH "$kSigningH" signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" diff --git a/dnsapi/dns_dyn.sh b/dnsapi/dns_dyn.sh new file mode 100644 index 0000000..024e0a3 --- /dev/null +++ b/dnsapi/dns_dyn.sh @@ -0,0 +1,339 @@ +#!/usr/bin/env sh +# +# Dyn.com Domain API +# +# Author: Gerd Naschenweng +# https://github.com/magicdude4eva +# +# Dyn Managed DNS API +# https://help.dyn.com/dns-api-knowledge-base/ +# +# It is recommended to add a "Dyn Managed DNS" user specific for API access. +# The "Zones & Records Permissions" required by this script are: +# -- +# RecordAdd +# RecordUpdate +# RecordDelete +# RecordGet +# ZoneGet +# ZoneAddNode +# ZoneRemoveNode +# ZonePublish +# -- +# +# Pass credentials before "acme.sh --issue --dns dns_dyn ..." +# -- +# export DYN_Customer="customer" +# export DYN_Username="apiuser" +# export DYN_Password="secret" +# -- + +DYN_API="https://api.dynect.net/REST" + +#REST_API +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "Challenge-code" +dns_dyn_add() { + fulldomain="$1" + txtvalue="$2" + + DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" + DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" + DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" + if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then + DYN_Customer="" + DYN_Username="" + DYN_Password="" + _err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" + return 1 + fi + + #save the config variables to the account conf file. + _saveaccountconf_mutable DYN_Customer "$DYN_Customer" + _saveaccountconf_mutable DYN_Username "$DYN_Username" + _saveaccountconf_mutable DYN_Password "$DYN_Password" + + if ! _dyn_get_authtoken; then + return 1 + fi + + if [ -z "$_dyn_authtoken" ]; then + _dyn_end_session + return 1 + fi + + if ! _dyn_get_zone; then + _dyn_end_session + return 1 + fi + + if ! _dyn_add_record; then + _dyn_end_session + return 1 + fi + + if ! _dyn_publish_zone; then + _dyn_end_session + return 1 + fi + + _dyn_end_session + + return 0 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_dyn_rm() { + fulldomain="$1" + txtvalue="$2" + + DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}" + DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}" + DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}" + if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then + DYN_Customer="" + DYN_Username="" + DYN_Password="" + _err "You must export variables: DYN_Customer, DYN_Username and DYN_Password" + return 1 + fi + + if ! _dyn_get_authtoken; then + return 1 + fi + + if [ -z "$_dyn_authtoken" ]; then + _dyn_end_session + return 1 + fi + + if ! _dyn_get_zone; then + _dyn_end_session + return 1 + fi + + if ! _dyn_get_record_id; then + _dyn_end_session + return 1 + fi + + if [ -z "$_dyn_record_id" ]; then + _dyn_end_session + return 1 + fi + + if ! _dyn_rm_record; then + _dyn_end_session + return 1 + fi + + if ! _dyn_publish_zone; then + _dyn_end_session + return 1 + fi + + _dyn_end_session + + return 0 +} + +#################### Private functions below ################################## + +#get Auth-Token +_dyn_get_authtoken() { + + _info "Start Dyn API Session" + + data="{\"customer_name\":\"$DYN_Customer\", \"user_name\":\"$DYN_Username\", \"password\":\"$DYN_Password\"}" + dyn_url="$DYN_API/Session/" + method="POST" + + _debug data "$data" + _debug dyn_url "$dyn_url" + + export _H1="Content-Type: application/json" + + response="$(_post "$data" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')" + _info "Token received" + _debug _dyn_authtoken "$_dyn_authtoken" + return 0 + fi + + _dyn_authtoken="" + _err "get token failed" + return 1 +} + +#fulldomain=_acme-challenge.www.domain.com +#returns +# _dyn_zone=domain.com +_dyn_get_zone() { + i=2 + while true; do + domain="$(printf "%s" "$fulldomain" | cut -d . -f "$i-100")" + if [ -z "$domain" ]; then + break + fi + + dyn_url="$DYN_API/Zone/$domain/" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_get "$dyn_url" "" "")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" + + _debug dyn_url "$dyn_url" + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _dyn_zone="$domain" + return 0 + fi + i=$(_math "$i" + 1) + done + + _dyn_zone="" + _err "get zone failed" + return 1 +} + +#add TXT record +_dyn_add_record() { + + _info "Adding TXT record" + + data="{\"rdata\":{\"txtdata\":\"$txtvalue\"},\"ttl\":\"300\"}" + dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" + method="POST" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "$data" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _info "TXT Record successfully added" + return 0 + fi + + _err "add TXT record failed" + return 1 +} + +#publish the zone +_dyn_publish_zone() { + + _info "Publishing zone" + + data="{\"publish\":\"true\"}" + dyn_url="$DYN_API/Zone/$_dyn_zone/" + method="PUT" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "$data" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _info "Zone published" + return 0 + fi + + _err "publish zone failed" + return 1 +} + +#get record_id of TXT record so we can delete the record +_dyn_get_record_id() { + + _info "Getting record_id of TXT record" + + dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_get "$dyn_url" "" "")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | _head_n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")" + _debug _dyn_record_id "$_dyn_record_id" + return 0 + fi + + _dyn_record_id="" + _err "getting record_id failed" + return 1 +} + +#delete TXT record +_dyn_rm_record() { + + _info "Deleting TXT record" + + dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/$_dyn_record_id/" + method="DELETE" + + _debug dyn_url "$dyn_url" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "" "$dyn_url" "" "$method")" + sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')" + + _debug response "$response" + _debug sessionstatus "$sessionstatus" + + if [ "$sessionstatus" = "success" ]; then + _info "TXT record successfully deleted" + return 0 + fi + + _err "delete TXT record failed" + return 1 +} + +#logout +_dyn_end_session() { + + _info "End Dyn API Session" + + dyn_url="$DYN_API/Session/" + method="DELETE" + + _debug dyn_url "$dyn_url" + + export _H1="Auth-Token: $_dyn_authtoken" + export _H2="Content-Type: application/json" + + response="$(_post "" "$dyn_url" "" "$method")" + + _debug response "$response" + + _dyn_authtoken="" + return 0 +} diff --git a/dnsapi/dns_infoblox.sh b/dnsapi/dns_infoblox.sh index 06ac87e..4cbb214 100644 --- a/dnsapi/dns_infoblox.sh +++ b/dnsapi/dns_infoblox.sh @@ -41,10 +41,10 @@ dns_infoblox_add() { export _H2="Authorization: Basic $Infoblox_CredsEncoded" ## Add the challenge record to the Infoblox grid member - result=$(_post "" "$baseurlnObject" "" "POST") + result="$(_post "" "$baseurlnObject" "" "POST")" ## Let's see if we get something intelligible back from the unit - if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then + if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then _info "Successfully created the txt record" return 0 else @@ -66,7 +66,7 @@ dns_infoblox_rm() { _debug txtvalue "$txtvalue" ## Base64 encode the credentials - Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64) + Infoblox_CredsEncoded="$(printf "%b" "$Infoblox_Creds" | _base64)" ## Construct the HTTP Authorization header export _H1="Accept-Language:en-US" @@ -74,17 +74,17 @@ dns_infoblox_rm() { ## Does the record exist? Let's check. baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty" - result=$(_get "$baseurlnObject") + result="$(_get "$baseurlnObject")" ## Let's see if we get something intelligible back from the grid - if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then + if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then ## Extract the object reference - objRef=$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View") + objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef" ## Delete them! All the stale records! - rmResult=$(_post "" "$objRmUrl" "" "DELETE") + rmResult="$(_post "" "$objRmUrl" "" "DELETE")" ## Let's see if that worked - if echo "$rmResult" | egrep "record:txt/.*:.*/$Infoblox_View"; then + if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then _info "Successfully deleted $objRef" return 0 else From 3bf2e89a1a9f68188264aa7cbf0f1b2e17400566 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 21:35:22 +0300 Subject: [PATCH 013/278] add pdd.yandex.ru dns api --- dnsapi/dns_yandex.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 dnsapi/dns_yandex.sh diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh new file mode 100755 index 0000000..db38eb5 --- /dev/null +++ b/dnsapi/dns_yandex.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env sh +# Author: non7top@gmail.com +# 07 Jul 2017 +# report bugs at https://github.com/non7top/acme.sh + +# Values to export: +# export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_yandex_add() { + fulldomain="${1}" + txtvalue="${2}" + _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" + _PDD_credentials + export _H1="PddToken: $PDD_Token" + + curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + 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}")" + _debug "Result: $curResult" +} + +#Usage: dns_myapi_rm _acme-challenge.www.domain.com +dns_yandex_rm() { + fulldomain="${1}" + _debug "Calling: dns_yandex_rm() '${fulldomain}'" + _PDD_credentials + export _H1="PddToken: $PDD_Token" + local record_id=$( pdd_get_record_id ${fulldomain} ) + + curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curSubdomain="$(echo ${fulldomain} | sed -e 's#$curDomain##')" + curUri="https://pddimp.yandex.ru/api2/admin/dns/del" + curData="domain=${curDomain}&record_id=${record_id}" + curResult="$(_post "${curData}" "${curUri}")" + _debug "Result: $curResult" +} + +#################### Private functions below ################################## + +_PDD_credentials() { + if [ -z "${PDD_Token}" ]; then + PDD_Token="" + _err "You haven't specified the ISPConfig Login data." + return 1 + else + _saveaccountconf PDD_Token "${PDD_Token}" + fi +} + +pdd_get_record_id() { + local fulldomain="${1}" + local curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + local curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" + local curResult="$(_get "${curUri}")" + echo ${curResult} | \ +python -c ' +import sys, json; +rs=json.load(sys.stdin)["records"] +for r in rs: + if r["fqdn"]=="${fulldomain}": + print r["record_id"] + exit +' +} From 6445a7674bd31e521e1eec12d16def544893dd69 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 21:52:47 +0300 Subject: [PATCH 014/278] fix syntax --- dnsapi/dns_yandex.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index db38eb5..a792d49 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials export _H1="PddToken: $PDD_Token" - curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo ${fulldomain} | sed -e 's#$curDomain##')" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + 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}")" @@ -30,10 +30,10 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials export _H1="PddToken: $PDD_Token" - local record_id=$( pdd_get_record_id ${fulldomain} ) + record_id=$( pdd_get_record_id ${fulldomain} ) - curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo ${fulldomain} | sed -e 's#$curDomain##')" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curSubdomain="$(echo "${fulldomain}" | sed -e 's#$curDomain##')" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -53,17 +53,17 @@ _PDD_credentials() { } pdd_get_record_id() { - local fulldomain="${1}" - local curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - local curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" - local curResult="$(_get "${curUri}")" + fulldomain="${1}" + curDomain="$(echo "${fulldomain}" |awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" + curResult="$(_get "${curUri}")" echo ${curResult} | \ -python -c ' + python -c " import sys, json; -rs=json.load(sys.stdin)["records"] +rs=json.load(sys.stdin)[\"records\"] for r in rs: - if r["fqdn"]=="${fulldomain}": - print r["record_id"] + if r[\"fqdn\"]==\"${fulldomain}\": + print r[\"record_id\"] exit -' +" } From a2038ab07e485153ac5b73fb5a8991d6c01c2352 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 21:57:27 +0300 Subject: [PATCH 015/278] fix syntax --- dnsapi/dns_yandex.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index a792d49..1b4b9ae 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo "${fulldomain}" | sed -e 's#$curDomain##')" + curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + 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}")" @@ -30,10 +30,10 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials export _H1="PddToken: $PDD_Token" - record_id=$( pdd_get_record_id ${fulldomain} ) + record_id=$( pdd_get_record_id "${fulldomain}" ) - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo "${fulldomain}" | sed -e 's#$curDomain##')" + curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curSubdomain="$( echo "${fulldomain}" | sed -e "s#$curDomain##" )" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -57,8 +57,8 @@ pdd_get_record_id() { curDomain="$(echo "${fulldomain}" |awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}")" - echo ${curResult} | \ - python -c " + echo "${curResult}" \ + | python -c " import sys, json; rs=json.load(sys.stdin)[\"records\"] for r in rs: From bd41f50ba5e7bd55f78fd3658da1470c57a33f0e Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 22:02:34 +0300 Subject: [PATCH 016/278] fix formatting --- dnsapi/dns_yandex.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 1b4b9ae..077e661 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials export _H1="PddToken: $PDD_Token" - curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$( echo "${fulldomain}" | sed -e "s#$curDomain##" )" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" + 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}")" @@ -30,10 +30,10 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials export _H1="PddToken: $PDD_Token" - record_id=$( pdd_get_record_id "${fulldomain}" ) + record_id=$(pdd_get_record_id "${fulldomain}") - curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$( echo "${fulldomain}" | sed -e "s#$curDomain##" )" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" + curSubdomain="$(echo "${fulldomain}" | sed -e "s#$curDomain##")" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -54,11 +54,11 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain="$(echo "${fulldomain}" |awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}")" echo "${curResult}" \ - | python -c " + | python -c " import sys, json; rs=json.load(sys.stdin)[\"records\"] for r in rs: From 8f3a3b293d46708eef7c6b5018379d721d82e815 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 13:00:42 +0300 Subject: [PATCH 017/278] add newline and checks --- dnsapi/dns_yandex.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 077e661..fc70781 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -13,7 +13,7 @@ dns_yandex_add() { fulldomain="${1}" txtvalue="${2}" _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" - _PDD_credentials + _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" @@ -28,7 +28,7 @@ dns_yandex_add() { dns_yandex_rm() { fulldomain="${1}" _debug "Calling: dns_yandex_rm() '${fulldomain}'" - _PDD_credentials + _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") @@ -67,3 +67,4 @@ for r in rs: exit " } + From 256cb90f3cf878086fa14b21f0379a11344b8cec Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 13:15:01 +0300 Subject: [PATCH 018/278] remove awk --- dnsapi/dns_yandex.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index fc70781..f6a7248 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" - curSubdomain="$(echo "${fulldomain}" | sed -e "s#$curDomain##")" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" curUri="https://pddimp.yandex.ru/api2/admin/dns/add" curResult="$(_post "${curData}" "${curUri}")" @@ -32,8 +32,8 @@ dns_yandex_rm() { export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" - curSubdomain="$(echo "${fulldomain}" | sed -e "s#$curDomain##")" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" From 9ec54ef89b50fd2367706c7d1747ccc9d36c63ca Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 13:21:55 +0300 Subject: [PATCH 019/278] shfmt fixes --- dnsapi/dns_yandex.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index f6a7248..664da77 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" curUri="https://pddimp.yandex.ru/api2/admin/dns/add" curResult="$(_post "${curData}" "${curUri}")" @@ -32,8 +32,8 @@ dns_yandex_rm() { export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -67,4 +67,3 @@ for r in rs: exit " } - From 266e9d06194c7eea0dd7854fd7c8fa9da680fcce Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 20:00:02 +0300 Subject: [PATCH 020/278] remove python --- dnsapi/dns_yandex.sh | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 664da77..2a47150 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -31,6 +31,7 @@ dns_yandex_rm() { _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") + _debug "Result: $record_id" curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" @@ -54,16 +55,10 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" - curResult="$(_get "${curUri}")" - echo "${curResult}" \ - | python -c " -import sys, json; -rs=json.load(sys.stdin)[\"records\"] -for r in rs: - if r[\"fqdn\"]==\"${fulldomain}\": - print r[\"record_id\"] - exit -" + curResult="$(_get "${curUri}" | _normalizeJson)" + _debug "Result: $curResult" + echo "$curResult" | grep -o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' } From 796647158ea23ebf673d817a254e1d61c7ce652c Mon Sep 17 00:00:00 2001 From: Santeri Date: Mon, 10 Jul 2017 15:36:16 +0400 Subject: [PATCH 021/278] Removed double quotes from _opt Broke GoDaddy cpanel causing error (thanks Hedgehog) --- deploy/cpanel.sh | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/deploy/cpanel.sh b/deploy/cpanel.sh index 6ab012a..b955239 100644 --- a/deploy/cpanel.sh +++ b/deploy/cpanel.sh @@ -1,12 +1,12 @@ -#!/bin/bash +#!/usr/bin/env sh # Here is the script to deploy the cert to your cpanel using the cpanel API. -# Uses command line uapi. Cpanel username is needed only when run as root. -# Returns 0 when success, otherwise error. +# Uses command line uapi. +# Cpanel username is needed only when run as root (I did not test this). +# Returns 0 when success. # Written by Santeri Kannisto # Public domain, 2017 #export DEPLOY_CPANEL_USER=myusername -#export DEPLOY_CPANEL_PASSWORD=PASSWORD ######## Public functions ##################### @@ -26,32 +26,28 @@ cpanel_deploy() { _debug _cfullchain "$_cfullchain" # read cert and key files and urlencode both - _certstr=`cat "$_ccert"` - _keystr=`cat "$_ckey"` + _certstr=$(cat "$_ccert") + _keystr=$(cat "$_ckey") _cert=$(php -r "echo urlencode(\"$_certstr\");") _key=$(php -r "echo urlencode(\"$_keystr\");") _debug _cert "$_cert" _debug _key "$_key" - if [[ $EUID -eq 0 ]] - then - _opt="--user=$DEPLOY_CPANEL_USER SSL install_ssl" - else - _opt="SSL install_ssl" - fi - - _debug _opt "$_opt" + if [ "$(id -u)" = 0 ]; then + _opt="--user=$DEPLOY_CPANEL_USER" + _debug _opt "$_opt" + fi - response=$(uapi $_opt domain="$_cdommain" cert="$_cert" key="$_key") + _response=$(uapi $_opt SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") - if [ $? -ne 0 ] - then + if [ $? -ne 0 ]; then _err "Error in deploying certificate:" - _err "$response" + _err "$_response" return 1 fi - _debug response "$response" + _debug response "$_response" _info "Certificate successfully deployed" + return 0 } From c4d0aec536dcd8886579d7f528f85217304c7366 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 10 Jul 2017 19:51:55 +0800 Subject: [PATCH 022/278] do not retry for a invalid cert in cronjob. fix https://github.com/Neilpang/acme.sh/issues/939 --- acme.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acme.sh b/acme.sh index 35842f4..486f867 100755 --- a/acme.sh +++ b/acme.sh @@ -4033,6 +4033,11 @@ renew() { return "$RENEW_SKIP" fi + if [ "$IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then + _info "Skip invalid cert for: $Le_Domain" + return 0 + fi + IS_RENEW="1" issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" res="$?" From a577c7215f0b2a5ad6729b175fb6e0033abeb4d5 Mon Sep 17 00:00:00 2001 From: Santeri Date: Mon, 10 Jul 2017 16:43:42 +0400 Subject: [PATCH 023/278] One more change to pass the check shellcheck test Now it is tested and works also when run as a root. --- deploy/cpanel.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/deploy/cpanel.sh b/deploy/cpanel.sh index b955239..9de8dbb 100644 --- a/deploy/cpanel.sh +++ b/deploy/cpanel.sh @@ -1,7 +1,6 @@ #!/usr/bin/env sh # Here is the script to deploy the cert to your cpanel using the cpanel API. -# Uses command line uapi. -# Cpanel username is needed only when run as root (I did not test this). +# Uses command line uapi. --user option is needed only if run as root. # Returns 0 when success. # Written by Santeri Kannisto # Public domain, 2017 @@ -35,12 +34,11 @@ cpanel_deploy() { _debug _key "$_key" if [ "$(id -u)" = 0 ]; then - _opt="--user=$DEPLOY_CPANEL_USER" - _debug _opt "$_opt" + _response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") + else + _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") fi - _response=$(uapi $_opt SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") - if [ $? -ne 0 ]; then _err "Error in deploying certificate:" _err "$_response" From 10cb7585a7140c7f709e2dcdee27913a55503545 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Tue, 11 Jul 2017 02:19:39 +0300 Subject: [PATCH 024/278] fix egrep and exit --- dnsapi/dns_yandex.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 2a47150..d2ccf18 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -13,7 +13,7 @@ dns_yandex_add() { fulldomain="${1}" txtvalue="${2}" _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" - _PDD_credentials || exit 1 + _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" @@ -28,7 +28,7 @@ dns_yandex_add() { dns_yandex_rm() { fulldomain="${1}" _debug "Calling: dns_yandex_rm() '${fulldomain}'" - _PDD_credentials || exit 1 + _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" @@ -60,5 +60,5 @@ pdd_get_record_id() { curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}" | _normalizeJson)" _debug "Result: $curResult" - echo "$curResult" | grep -o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' + echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' } From e6a95ecd0889779a39e988b392dcdd9568f34798 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Wed, 12 Jul 2017 03:51:48 +0300 Subject: [PATCH 025/278] rework root domain detection --- dnsapi/dns_yandex.sh | 57 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index d2ccf18..4b00219 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,9 @@ dns_yandex_add() { _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" + 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}")" @@ -33,8 +34,10 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" + 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}" curResult="$(_post "${curData}" "${curUri}")" @@ -43,10 +46,47 @@ dns_yandex_rm() { #################### Private functions below ################################## +_PDD_get_domain() { + fulldomain="${1}" + __page=1 + __last=0 + while [ $__last -eq 0 ]; do + uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" + res1=$(_get $uri1 | _normalizeJson) + #_debug "$res1" + __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p') + _debug "found: $__found results on page" + if [ $__found -lt 20 ]; then + _debug "last page: $__page" + __last=1 + fi + + __all_domains="$__all_domains $(echo "$res1" | sed -e "s@,@\n@g" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" + + __page=$(_math $__page + 1) + done + + k=2 + while [ $k -lt 10 ]; do + __t=$(echo "$fulldomain" | cut -d . -f $k-100) + _debug "finding zone for domain $__t" + for d in $__all_domains; do + if [ "$d" == "$__t" ]; then + echo "$__t" + return + fi + done + k=$(_math $k + 1) + done + _err "No suitable domain found in your account" + return 1 +} + _PDD_credentials() { if [ -z "${PDD_Token}" ]; then PDD_Token="" - _err "You haven't specified the ISPConfig Login data." + _err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx" + _err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token" return 1 else _saveaccountconf PDD_Token "${PDD_Token}" @@ -55,8 +95,11 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" + + 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)" _debug "Result: $curResult" From 6a9b4db448775b59913563aaeb3280296a0febc3 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Wed, 12 Jul 2017 04:07:18 +0300 Subject: [PATCH 026/278] fix formatting --- dnsapi/dns_yandex.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 4b00219..eb60d5a 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) + 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}" @@ -34,7 +34,7 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curDomain=$(_PDD_get_domain $fulldomain) + curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" @@ -52,11 +52,11 @@ _PDD_get_domain() { __last=0 while [ $__last -eq 0 ]; do uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" - res1=$(_get $uri1 | _normalizeJson) + res1=$(_get "$uri1" | _normalizeJson) #_debug "$res1" __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p') _debug "found: $__found results on page" - if [ $__found -lt 20 ]; then + if [ "$__found" -lt 20 ]; then _debug "last page: $__page" __last=1 fi @@ -71,7 +71,7 @@ _PDD_get_domain() { __t=$(echo "$fulldomain" | cut -d . -f $k-100) _debug "finding zone for domain $__t" for d in $__all_domains; do - if [ "$d" == "$__t" ]; then + if [ "$d" = "$__t" ]; then echo "$__t" return fi @@ -96,7 +96,7 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain=$(_PDD_get_domain $fulldomain) + curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" From 7d64e141e45be32479773f8a4fb44bd8304dacc1 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Wed, 12 Jul 2017 20:24:54 +0200 Subject: [PATCH 027/278] Add dns_he - DNS API script for Hurricane Electric DNS service ... Although not yet fully Posix compatible. --- dnsapi/README.md | 18 +++++ dnsapi/dns_he.sh | 200 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100755 dnsapi/dns_he.sh diff --git a/dnsapi/README.md b/dnsapi/README.md index 41f89a8..1539904 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -572,6 +572,24 @@ acme.sh --issue --dns dns_dyn -d example.com -d www.example.com The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +# 30. Use Hurricane Electric + +Hurricane Electric doesn't have an API so just set your login credentials like so: + +``` +export HE_Username="yourusername" +export HE_Password="password" +``` + +Then you can issue your certificate: + +``` +acme.sh --issue --dns dns_he -d example.com -d www.example.com +``` + +The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. + +Please report any issues to https://github.com/angel333/acme.sh or to . # Use custom API diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh new file mode 100755 index 0000000..269db8d --- /dev/null +++ b/dnsapi/dns_he.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env sh + +# TODO Somehow use _get instead of curl - not sure how to support +# cookies though... + +######################################################################## +# Hurricane Electric hook script for acme.sh +# +# Environment variables: +# +# - $HE_Username (your dns.he.net username) +# - $HE_Password (your dns.he.net password) +# +# Author: Ondrej Simek +# Git repo: https://github.com/angel333/acme.sh + + +#-- dns_he_add() - Add TXT record -------------------------------------- +# Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..." + +dns_he_add() { + _full_domain=$1 + _txt_value=$2 + _info "Using DNS-01 Hurricane Electric hook" + + _authenticate || return 1 + _saveaccountconf HE_Username "$HE_Username" + _saveaccountconf HE_Password "$HE_Password" + + # fills in the $_zone_id + _find_zone $_full_domain || return 1 + _debug "Zone id \"$_zone_id\" will be used." + + curl -L --silent --show-error --cookie "$_he_cookie" \ + --form "account=" \ + --form "menu=edit_zone" \ + --form "Type=TXT" \ + --form "hosted_dns_zoneid=$_zone_id" \ + --form "hosted_dns_recordid=" \ + --form "hosted_dns_editzone=1" \ + --form "Priority=" \ + --form "Name=$_full_domain" \ + --form "Content=$_txt_value" \ + --form "TTL=300" \ + --form "hosted_dns_editrecord=Submit" \ + "https://dns.he.net/" \ + > /dev/null +} + + +#-- dns_he_rm() - Remove TXT record ------------------------------------ +# Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..." + +dns_he_rm() { + _full_domain=$1 + _txt_value=$2 + _info "Cleaning up after DNS-01 Hurricane Electric hook" + + _authenticate || return 1 + + # fills in the $_zone_id + _find_zone $_full_domain || return 1 + _debug "Zone id \"$_zone_id\" will be used." + + # Find the record id to clean + _record_id=$( \ + curl -L --silent --show-error --cookie "$_he_cookie" \ + "https://dns.he.net/?hosted_dns_zoneid=$_zone_id&menu=edit_zone&hosted_dns_editzone" \ + | grep -A 1 "data=\"\("\)\?${_txt_value}\("\)\?\"" \ + | tail -n 1 \ + | _egrep_o "'[[:digit:]]+','[^']+','TXT'" \ + | cut -b 2- \ + | _egrep_o "[[:digit:]]+" \ + | head -n1) # ... oh my, what have I done... + + # Remove the record + curl -L --silent --show-error --cookie "$_he_cookie" \ + --form "menu=edit_zone" \ + --form "hosted_dns_zoneid=$_zone_id" \ + --form "hosted_dns_recordid=$_record_id" \ + --form "hosted_dns_editzone=1" \ + --form "hosted_dns_delrecord=1" \ + --form "hosted_dns_delconfirm=delete" \ + --form "hosted_dns_editzone=1" \ + "https://dns.he.net/" \ + | grep '
Successfully removed record.
' \ + > /dev/null + if [ $? -eq 0 ]; then + _info "Record removed successfuly." + else + _err \ + "Could not clean (remove) up the record. Please go to HE" \ + "administration interface and clean it by hand." + fi +} + + +########################## PRIVATE FUNCTIONS ########################### + + +#-- _find_zone() ------------------------------------------------------- + +# Usage: _authenticate +# +# - needs $HE_Username and $HE_Password +# - sets the $_he_cookie + +_authenticate() { + if [ -z "$HE_Username" ] && [ -z "$HE_Password" ]; then + _err \ + 'No auth details provided. Please set user credentials using the \ + \$HE_Username and \$HE_Password envoronment variables.' + return 1 + fi + # Just get a session + _he_cookie=$( \ + curl -L --silent --show-error -I "https://dns.he.net/" \ + | grep '^Set-Cookie:' \ + | _egrep_o 'CGISESSID=[a-z0-9]*') + # Attempt login + curl -L --silent --show-error --cookie "$_he_cookie" \ + --form "email=${HE_Username}" \ + --form "pass=${HE_Password}" \ + "https://dns.he.net/" \ + > /dev/null + # TODO detect unsuccessful logins +} + + +#-- _find_zone() ------------------------------------------------------- + +# Returns the most specific zone found in administration interface. +# +# - needs $_he_cookie +# +# Example: +# +# _find_zone first.second.third.co.uk +# +# ... will return the first zone that exists in admin out of these: +# - "first.second.third.co.uk" +# - "second.third.co.uk" +# - "third.co.uk" +# - "co.uk" <-- unlikely +# - "uk" <-' +# +# (another approach would be something like this: +# https://github.com/hlandau/acme/blob/master/_doc/dns.hook +# - that's better if there are multiple pages. It's so much simpler. +# ) + +_find_zone() { + + _domain="$1" + + ## _all_zones is an array that looks like this: + ## ( zone1:id zone2:id ... ) + _all_zones=( $(curl -L --silent --show-error --cookie "$_he_cookie" \ + "https://dns.he.net/" \ + | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" \ + | cut -d '"' -f 3,5 --output-delimiter=":" \ + ) ) + + _strip_counter=1 + while [ true ] + do + _attempted_zone=$(echo $_domain | cut -d . -f ${_strip_counter}-) + + # All possible zone names have been tried + if [ "$_attempted_zone" == "" ] + then + _err "No zone for domain \"$_domain\" found." + break + fi + + # Walk through all zones on the account + #echo "$_all_zones" | while IFS=' ' read _zone_name _zone_id + for i in ${_all_zones[@]} + do + _zone_name=$(echo $i | cut -d ':' -f 1) + _zone_id=$(echo $i | cut -d ':' -f 2) + if [ "$_zone_name" == "$_attempted_zone" ] + then + # Zone found - we got $_zone_name and $_zone_id, let's get out... + _debug "Found relevant zone \"$_zone_name\" with id" \ + "\"$_zone_id\" - will be used for domain \"$_domain\"." + return 0 + fi + done + + _debug "Zone \"$_attempted_zone\" doesn't exist, let's try another \ + variation." + _strip_counter=$(expr $_strip_counter + 1) + done + + # No zone found. + return 1 +} + +# vim: et:ts=2:sw=2: From accbda9d2f91c48c31f5e10ec073c44054647919 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 13 Jul 2017 20:52:44 +0800 Subject: [PATCH 028/278] output log --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5849eb4..7ca042e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ ADD ./ /install_acme.sh/ RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/ -RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh +RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | sed 's#> /dev/null##' | crontab - RUN for verb in help \ version \ From 84a251c8c7eea52e819445c4d407fefc2b52c852 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Thu, 13 Jul 2017 17:05:57 +0300 Subject: [PATCH 029/278] add documentation --- README.md | 1 + dnsapi/README.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 7780b9a..afb8a76 100644 --- a/README.md +++ b/README.md @@ -335,6 +335,7 @@ You don't have to do anything manually! 1. DNSimple API 1. NS1.com API 1. DuckDNS.org API +1. Yandex PDD API (https://pdd.yandex.ru) 1. Name.com API diff --git a/dnsapi/README.md b/dnsapi/README.md index 57c360b..55fd65a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -522,6 +522,21 @@ account file will always store the lastly used domain name. For issues, please report to https://github.com/raidenii/acme.sh/issues. + +## 27. Use pdd.yandex.ru API + +``` +export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +``` + +Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/ +``` +acme.sh --issue --dns dns_yandex -d mydomain.example.org +``` + +For issues, please report to https://github.com/non7top/acme.sh/issues. + + ## 28. Use Name.com API You'll need to fill out the form at https://www.name.com/reseller/apply to apply From bdee66fe2942affb0ba0675785d6c0ef567fbf8e Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Thu, 13 Jul 2017 18:37:13 +0300 Subject: [PATCH 030/278] minor fixes --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 55fd65a..7115403 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -523,7 +523,7 @@ account file will always store the lastly used domain name. For issues, please report to https://github.com/raidenii/acme.sh/issues. -## 27. Use pdd.yandex.ru API +## 28. Use pdd.yandex.ru API ``` export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" @@ -537,7 +537,7 @@ acme.sh --issue --dns dns_yandex -d mydomain.example.org For issues, please report to https://github.com/non7top/acme.sh/issues. -## 28. Use Name.com API +## 29. Use Name.com API You'll need to fill out the form at https://www.name.com/reseller/apply to apply for API username and token. From a460ac021fadffda93359b9acab6508cafcd6c24 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 21:35:22 +0300 Subject: [PATCH 031/278] add pdd.yandex.ru dns api --- dnsapi/dns_yandex.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 dnsapi/dns_yandex.sh diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh new file mode 100755 index 0000000..db38eb5 --- /dev/null +++ b/dnsapi/dns_yandex.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env sh +# Author: non7top@gmail.com +# 07 Jul 2017 +# report bugs at https://github.com/non7top/acme.sh + +# Values to export: +# export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_yandex_add() { + fulldomain="${1}" + txtvalue="${2}" + _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" + _PDD_credentials + export _H1="PddToken: $PDD_Token" + + curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + 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}")" + _debug "Result: $curResult" +} + +#Usage: dns_myapi_rm _acme-challenge.www.domain.com +dns_yandex_rm() { + fulldomain="${1}" + _debug "Calling: dns_yandex_rm() '${fulldomain}'" + _PDD_credentials + export _H1="PddToken: $PDD_Token" + local record_id=$( pdd_get_record_id ${fulldomain} ) + + curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curSubdomain="$(echo ${fulldomain} | sed -e 's#$curDomain##')" + curUri="https://pddimp.yandex.ru/api2/admin/dns/del" + curData="domain=${curDomain}&record_id=${record_id}" + curResult="$(_post "${curData}" "${curUri}")" + _debug "Result: $curResult" +} + +#################### Private functions below ################################## + +_PDD_credentials() { + if [ -z "${PDD_Token}" ]; then + PDD_Token="" + _err "You haven't specified the ISPConfig Login data." + return 1 + else + _saveaccountconf PDD_Token "${PDD_Token}" + fi +} + +pdd_get_record_id() { + local fulldomain="${1}" + local curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + local curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" + local curResult="$(_get "${curUri}")" + echo ${curResult} | \ +python -c ' +import sys, json; +rs=json.load(sys.stdin)["records"] +for r in rs: + if r["fqdn"]=="${fulldomain}": + print r["record_id"] + exit +' +} From a09b2c0074f43e017966cb22018100774df76832 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 21:52:47 +0300 Subject: [PATCH 032/278] fix syntax --- dnsapi/dns_yandex.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index db38eb5..a792d49 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials export _H1="PddToken: $PDD_Token" - curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo ${fulldomain} | sed -e 's#$curDomain##')" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + 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}")" @@ -30,10 +30,10 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials export _H1="PddToken: $PDD_Token" - local record_id=$( pdd_get_record_id ${fulldomain} ) + record_id=$( pdd_get_record_id ${fulldomain} ) - curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo ${fulldomain} | sed -e 's#$curDomain##')" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curSubdomain="$(echo "${fulldomain}" | sed -e 's#$curDomain##')" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -53,17 +53,17 @@ _PDD_credentials() { } pdd_get_record_id() { - local fulldomain="${1}" - local curDomain="$(echo ${fulldomain}|awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - local curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" - local curResult="$(_get "${curUri}")" + fulldomain="${1}" + curDomain="$(echo "${fulldomain}" |awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" + curResult="$(_get "${curUri}")" echo ${curResult} | \ -python -c ' + python -c " import sys, json; -rs=json.load(sys.stdin)["records"] +rs=json.load(sys.stdin)[\"records\"] for r in rs: - if r["fqdn"]=="${fulldomain}": - print r["record_id"] + if r[\"fqdn\"]==\"${fulldomain}\": + print r[\"record_id\"] exit -' +" } From e9d540779257ac5378ea594391de387393a03c84 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 21:57:27 +0300 Subject: [PATCH 033/278] fix syntax --- dnsapi/dns_yandex.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index a792d49..1b4b9ae 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo "${fulldomain}" | sed -e 's#$curDomain##')" + curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + 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}")" @@ -30,10 +30,10 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials export _H1="PddToken: $PDD_Token" - record_id=$( pdd_get_record_id ${fulldomain} ) + record_id=$( pdd_get_record_id "${fulldomain}" ) - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$(echo "${fulldomain}" | sed -e 's#$curDomain##')" + curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curSubdomain="$( echo "${fulldomain}" | sed -e "s#$curDomain##" )" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -57,8 +57,8 @@ pdd_get_record_id() { curDomain="$(echo "${fulldomain}" |awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}")" - echo ${curResult} | \ - python -c " + echo "${curResult}" \ + | python -c " import sys, json; rs=json.load(sys.stdin)[\"records\"] for r in rs: From 18cb11dcbf932fe324c9d0426546e63770cf7355 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sat, 8 Jul 2017 22:02:34 +0300 Subject: [PATCH 034/278] fix formatting --- dnsapi/dns_yandex.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 1b4b9ae..077e661 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials export _H1="PddToken: $PDD_Token" - curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$( echo "${fulldomain}" | sed -e "s#$curDomain##" )" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" + 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}")" @@ -30,10 +30,10 @@ dns_yandex_rm() { _debug "Calling: dns_yandex_rm() '${fulldomain}'" _PDD_credentials export _H1="PddToken: $PDD_Token" - record_id=$( pdd_get_record_id "${fulldomain}" ) + record_id=$(pdd_get_record_id "${fulldomain}") - curDomain="$( echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" - curSubdomain="$( echo "${fulldomain}" | sed -e "s#$curDomain##" )" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" + curSubdomain="$(echo "${fulldomain}" | sed -e "s#$curDomain##")" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -54,11 +54,11 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain="$(echo "${fulldomain}" |awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}' )" + curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}")" echo "${curResult}" \ - | python -c " + | python -c " import sys, json; rs=json.load(sys.stdin)[\"records\"] for r in rs: From 42ab7a5d725109a15df5d0a049232a3f461908bc Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 13:00:42 +0300 Subject: [PATCH 035/278] add newline and checks --- dnsapi/dns_yandex.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 077e661..fc70781 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -13,7 +13,7 @@ dns_yandex_add() { fulldomain="${1}" txtvalue="${2}" _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" - _PDD_credentials + _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" @@ -28,7 +28,7 @@ dns_yandex_add() { dns_yandex_rm() { fulldomain="${1}" _debug "Calling: dns_yandex_rm() '${fulldomain}'" - _PDD_credentials + _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") @@ -67,3 +67,4 @@ for r in rs: exit " } + From d61b687853e00f72dbfc1d7137767ba0ae4fdf57 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 13:15:01 +0300 Subject: [PATCH 036/278] remove awk --- dnsapi/dns_yandex.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index fc70781..f6a7248 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" - curSubdomain="$(echo "${fulldomain}" | sed -e "s#$curDomain##")" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" curUri="https://pddimp.yandex.ru/api2/admin/dns/add" curResult="$(_post "${curData}" "${curUri}")" @@ -32,8 +32,8 @@ dns_yandex_rm() { export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" - curSubdomain="$(echo "${fulldomain}" | sed -e "s#$curDomain##")" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" From 57d1db58db275bda6e89608d5af75ae4f60320f7 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 13:21:55 +0300 Subject: [PATCH 037/278] shfmt fixes --- dnsapi/dns_yandex.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index f6a7248..664da77 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,8 @@ dns_yandex_add() { _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}" curUri="https://pddimp.yandex.ru/api2/admin/dns/add" curResult="$(_post "${curData}" "${curUri}")" @@ -32,8 +32,8 @@ dns_yandex_rm() { export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev )" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev )" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" curUri="https://pddimp.yandex.ru/api2/admin/dns/del" curData="domain=${curDomain}&record_id=${record_id}" curResult="$(_post "${curData}" "${curUri}")" @@ -67,4 +67,3 @@ for r in rs: exit " } - From a0df46258dfb327ff5f2de471b13d1250b4ea3e3 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Sun, 9 Jul 2017 20:00:02 +0300 Subject: [PATCH 038/278] remove python --- dnsapi/dns_yandex.sh | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 664da77..2a47150 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -31,6 +31,7 @@ dns_yandex_rm() { _PDD_credentials || exit 1 export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") + _debug "Result: $record_id" curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" @@ -54,16 +55,10 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain="$(echo "${fulldomain}" | awk -F. '{printf("%s.%s\n",$(NF-1), $NF)}')" + curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" + curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" - curResult="$(_get "${curUri}")" - echo "${curResult}" \ - | python -c " -import sys, json; -rs=json.load(sys.stdin)[\"records\"] -for r in rs: - if r[\"fqdn\"]==\"${fulldomain}\": - print r[\"record_id\"] - exit -" + curResult="$(_get "${curUri}" | _normalizeJson)" + _debug "Result: $curResult" + echo "$curResult" | grep -o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' } From fceb728501472961e73ed8bbdd292311de31cd40 Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Tue, 11 Jul 2017 02:19:39 +0300 Subject: [PATCH 039/278] fix egrep and exit --- dnsapi/dns_yandex.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 2a47150..d2ccf18 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -13,7 +13,7 @@ dns_yandex_add() { fulldomain="${1}" txtvalue="${2}" _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" - _PDD_credentials || exit 1 + _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" @@ -28,7 +28,7 @@ dns_yandex_add() { dns_yandex_rm() { fulldomain="${1}" _debug "Calling: dns_yandex_rm() '${fulldomain}'" - _PDD_credentials || exit 1 + _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" @@ -60,5 +60,5 @@ pdd_get_record_id() { curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}" curResult="$(_get "${curUri}" | _normalizeJson)" _debug "Result: $curResult" - echo "$curResult" | grep -o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' + echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p' } From eb6be88fac9510aa137b3dea9e16b56aa3d10c1e Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Wed, 12 Jul 2017 03:51:48 +0300 Subject: [PATCH 040/278] rework root domain detection --- dnsapi/dns_yandex.sh | 57 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index d2ccf18..4b00219 100755 --- a/dnsapi/dns_yandex.sh +++ b/dnsapi/dns_yandex.sh @@ -16,8 +16,9 @@ dns_yandex_add() { _PDD_credentials || return 1 export _H1="PddToken: $PDD_Token" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" + 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}")" @@ -33,8 +34,10 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" + 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}" curResult="$(_post "${curData}" "${curUri}")" @@ -43,10 +46,47 @@ dns_yandex_rm() { #################### Private functions below ################################## +_PDD_get_domain() { + fulldomain="${1}" + __page=1 + __last=0 + while [ $__last -eq 0 ]; do + uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" + res1=$(_get $uri1 | _normalizeJson) + #_debug "$res1" + __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p') + _debug "found: $__found results on page" + if [ $__found -lt 20 ]; then + _debug "last page: $__page" + __last=1 + fi + + __all_domains="$__all_domains $(echo "$res1" | sed -e "s@,@\n@g" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')" + + __page=$(_math $__page + 1) + done + + k=2 + while [ $k -lt 10 ]; do + __t=$(echo "$fulldomain" | cut -d . -f $k-100) + _debug "finding zone for domain $__t" + for d in $__all_domains; do + if [ "$d" == "$__t" ]; then + echo "$__t" + return + fi + done + k=$(_math $k + 1) + done + _err "No suitable domain found in your account" + return 1 +} + _PDD_credentials() { if [ -z "${PDD_Token}" ]; then PDD_Token="" - _err "You haven't specified the ISPConfig Login data." + _err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx" + _err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token" return 1 else _saveaccountconf PDD_Token "${PDD_Token}" @@ -55,8 +95,11 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain="$(echo "${fulldomain}" | rev | cut -d . -f 1-2 | rev)" - curSubdomain="$(echo "${fulldomain}" | rev | cut -d . -f 3- | rev)" + + 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)" _debug "Result: $curResult" From c848d3ee22b085a18ff52567efb7d0f213374f7b Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Wed, 12 Jul 2017 04:07:18 +0300 Subject: [PATCH 041/278] fix formatting --- dnsapi/dns_yandex.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh index 4b00219..eb60d5a 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) + 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}" @@ -34,7 +34,7 @@ dns_yandex_rm() { record_id=$(pdd_get_record_id "${fulldomain}") _debug "Result: $record_id" - curDomain=$(_PDD_get_domain $fulldomain) + curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" @@ -52,11 +52,11 @@ _PDD_get_domain() { __last=0 while [ $__last -eq 0 ]; do uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20" - res1=$(_get $uri1 | _normalizeJson) + res1=$(_get "$uri1" | _normalizeJson) #_debug "$res1" __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p') _debug "found: $__found results on page" - if [ $__found -lt 20 ]; then + if [ "$__found" -lt 20 ]; then _debug "last page: $__page" __last=1 fi @@ -71,7 +71,7 @@ _PDD_get_domain() { __t=$(echo "$fulldomain" | cut -d . -f $k-100) _debug "finding zone for domain $__t" for d in $__all_domains; do - if [ "$d" == "$__t" ]; then + if [ "$d" = "$__t" ]; then echo "$__t" return fi @@ -96,7 +96,7 @@ _PDD_credentials() { pdd_get_record_id() { fulldomain="${1}" - curDomain=$(_PDD_get_domain $fulldomain) + curDomain=$(_PDD_get_domain "$fulldomain") _debug "Found suitable domain in pdd: $curDomain" curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")" From 377fe5ecdeb750a95ffdb38b669d66d77ad0a03d Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Thu, 13 Jul 2017 17:05:57 +0300 Subject: [PATCH 042/278] add documentation --- README.md | 1 + dnsapi/README.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 5c306a5..ae73b0f 100644 --- a/README.md +++ b/README.md @@ -335,6 +335,7 @@ You don't have to do anything manually! 1. DNSimple API 1. NS1.com API 1. DuckDNS.org API +1. Yandex PDD API (https://pdd.yandex.ru) 1. Name.com API 1. Dyn Managed DNS API diff --git a/dnsapi/README.md b/dnsapi/README.md index 41f89a8..afaf94f 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -522,6 +522,21 @@ account file will always store the lastly used domain name. For issues, please report to https://github.com/raidenii/acme.sh/issues. + +## 27. Use pdd.yandex.ru API + +``` +export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +``` + +Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/ +``` +acme.sh --issue --dns dns_yandex -d mydomain.example.org +``` + +For issues, please report to https://github.com/non7top/acme.sh/issues. + + ## 28. Use Name.com API You'll need to fill out the form at https://www.name.com/reseller/apply to apply From 283ef9adb7a9f5f33e04ce03b6db158802a30f3a Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Thu, 13 Jul 2017 18:37:13 +0300 Subject: [PATCH 043/278] minor fixes --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index afaf94f..3d571af 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -523,7 +523,7 @@ account file will always store the lastly used domain name. For issues, please report to https://github.com/raidenii/acme.sh/issues. -## 27. Use pdd.yandex.ru API +## 28. Use pdd.yandex.ru API ``` export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" @@ -537,7 +537,7 @@ acme.sh --issue --dns dns_yandex -d mydomain.example.org For issues, please report to https://github.com/non7top/acme.sh/issues. -## 28. Use Name.com API +## 29. Use Name.com API You'll need to fill out the form at https://www.name.com/reseller/apply to apply for API username and token. From ae302ee600b1a07bd1132676ab204df72208e15b Mon Sep 17 00:00:00 2001 From: Vladimir Berezhnoy Date: Fri, 14 Jul 2017 03:51:08 +0300 Subject: [PATCH 044/278] reformat docs --- README.md | 2 +- dnsapi/README.md | 29 +++++++++++++---------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ae73b0f..5a3a3ad 100644 --- a/README.md +++ b/README.md @@ -335,9 +335,9 @@ You don't have to do anything manually! 1. DNSimple API 1. NS1.com API 1. DuckDNS.org API -1. Yandex PDD API (https://pdd.yandex.ru) 1. Name.com API 1. Dyn Managed DNS API +1. Yandex PDD API (https://pdd.yandex.ru) And: diff --git a/dnsapi/README.md b/dnsapi/README.md index 3d571af..d3dff12 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -522,22 +522,7 @@ account file will always store the lastly used domain name. For issues, please report to https://github.com/raidenii/acme.sh/issues. - -## 28. Use pdd.yandex.ru API - -``` -export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -``` - -Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/ -``` -acme.sh --issue --dns dns_yandex -d mydomain.example.org -``` - -For issues, please report to https://github.com/non7top/acme.sh/issues. - - -## 29. Use Name.com API +## 28. Use Name.com API You'll need to fill out the form at https://www.name.com/reseller/apply to apply for API username and token. @@ -587,6 +572,18 @@ acme.sh --issue --dns dns_dyn -d example.com -d www.example.com The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 30. Use pdd.yandex.ru API + +``` +export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +``` + +Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/ +``` +acme.sh --issue --dns dns_yandex -d mydomain.example.org +``` + +For issues, please report to https://github.com/non7top/acme.sh/issues. # Use custom API From 90fd18bf423b91e6c48aa38ed1fa6dba52cde5e7 Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 18 Jul 2017 15:48:17 +0400 Subject: [PATCH 045/278] Renamed script to cpanel_uapi.sh As per Neil's request. --- deploy/{cpanel.sh => cpanel_uapi.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename deploy/{cpanel.sh => cpanel_uapi.sh} (100%) diff --git a/deploy/cpanel.sh b/deploy/cpanel_uapi.sh similarity index 100% rename from deploy/cpanel.sh rename to deploy/cpanel_uapi.sh From d09b5cb80eab19fb5e14a484cca5de09fb5c01ef Mon Sep 17 00:00:00 2001 From: Santeri Date: Wed, 19 Jul 2017 07:39:21 +0400 Subject: [PATCH 046/278] Rename cpanel_uapi.sh to cpanel_deploy.sh --- deploy/{cpanel_uapi.sh => cpanel_deploy.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename deploy/{cpanel_uapi.sh => cpanel_deploy.sh} (100%) diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_deploy.sh similarity index 100% rename from deploy/cpanel_uapi.sh rename to deploy/cpanel_deploy.sh From 4286b2917ef97ab44d0fd6208d183979633a401a Mon Sep 17 00:00:00 2001 From: Santeri Date: Wed, 19 Jul 2017 12:22:00 +0400 Subject: [PATCH 047/278] renamed function --- deploy/{cpanel_deploy.sh => cpanel_uapi.sh} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename deploy/{cpanel_deploy.sh => cpanel_uapi.sh} (98%) diff --git a/deploy/cpanel_deploy.sh b/deploy/cpanel_uapi.sh similarity index 98% rename from deploy/cpanel_deploy.sh rename to deploy/cpanel_uapi.sh index 9de8dbb..ded50d0 100644 --- a/deploy/cpanel_deploy.sh +++ b/deploy/cpanel_uapi.sh @@ -11,7 +11,7 @@ #domain keyfile certfile cafile fullchain -cpanel_deploy() { +cpanel_uapi() { _cdomain="$1" _ckey="$2" _ccert="$3" From 4285d81ca9d2805e99cf4ed0b601891b4ffd77ce Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sun, 23 Jul 2017 05:00:02 +0200 Subject: [PATCH 048/278] Get rid of curl. --- dnsapi/dns_he.sh | 102 +++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 65 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 269db8d..141268a 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -1,8 +1,5 @@ #!/usr/bin/env sh -# TODO Somehow use _get instead of curl - not sure how to support -# cookies though... - ######################################################################## # Hurricane Electric hook script for acme.sh # @@ -23,7 +20,12 @@ dns_he_add() { _txt_value=$2 _info "Using DNS-01 Hurricane Electric hook" - _authenticate || return 1 + if [ -z "$HE_Username" ] && [ -z "$HE_Password" ]; then + _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" @@ -31,20 +33,20 @@ dns_he_add() { _find_zone $_full_domain || return 1 _debug "Zone id \"$_zone_id\" will be used." - curl -L --silent --show-error --cookie "$_he_cookie" \ - --form "account=" \ - --form "menu=edit_zone" \ - --form "Type=TXT" \ - --form "hosted_dns_zoneid=$_zone_id" \ - --form "hosted_dns_recordid=" \ - --form "hosted_dns_editzone=1" \ - --form "Priority=" \ - --form "Name=$_full_domain" \ - --form "Content=$_txt_value" \ - --form "TTL=300" \ - --form "hosted_dns_editrecord=Submit" \ - "https://dns.he.net/" \ - > /dev/null + body="email=${HE_Username}&pass=${HE_Password}" + body="$body&account=" + body="$body&account=" + body="$body&menu=edit_zone" + body="$body&Type=TXT" + body="$body&hosted_dns_zoneid=$_zone_id" + body="$body&hosted_dns_recordid=" + body="$body&hosted_dns_editzone=1" + body="$body&Priority=" + body="$body&Name=$_full_domain" + body="$body&Content=$_txt_value" + body="$body&TTL=300" + body="$body&hosted_dns_editrecord=Submit" + _post $body "https://dns.he.net/" >/dev/null } @@ -56,16 +58,16 @@ dns_he_rm() { _txt_value=$2 _info "Cleaning up after DNS-01 Hurricane Electric hook" - _authenticate || return 1 - # fills in the $_zone_id _find_zone $_full_domain || return 1 _debug "Zone id \"$_zone_id\" will be used." # Find the record id to clean - _record_id=$( \ - curl -L --silent --show-error --cookie "$_he_cookie" \ - "https://dns.he.net/?hosted_dns_zoneid=$_zone_id&menu=edit_zone&hosted_dns_editzone" \ + body="email=${HE_Username}&pass=${HE_Password}" + body="$body&hosted_dns_zoneid=$_zone_id" + body="$body&menu=edit_zone" + body="$body&hosted_dns_editzone=" + _record_id=$(_post $body "https://dns.he.net/" \ | grep -A 1 "data=\"\("\)\?${_txt_value}\("\)\?\"" \ | tail -n 1 \ | _egrep_o "'[[:digit:]]+','[^']+','TXT'" \ @@ -74,15 +76,15 @@ dns_he_rm() { | head -n1) # ... oh my, what have I done... # Remove the record - curl -L --silent --show-error --cookie "$_he_cookie" \ - --form "menu=edit_zone" \ - --form "hosted_dns_zoneid=$_zone_id" \ - --form "hosted_dns_recordid=$_record_id" \ - --form "hosted_dns_editzone=1" \ - --form "hosted_dns_delrecord=1" \ - --form "hosted_dns_delconfirm=delete" \ - --form "hosted_dns_editzone=1" \ - "https://dns.he.net/" \ + body="email=${HE_Username}&pass=${HE_Password}" + body="$body&menu=edit_zone" + body="$body&hosted_dns_zoneid=$_zone_id" + body="$body&hosted_dns_recordid=$_record_id" + body="$body&hosted_dns_editzone=1" + body="$body&hosted_dns_delrecord=1" + body="$body&hosted_dns_delconfirm=delete" + body="$body&hosted_dns_editzone=1" + _post $body "https://dns.he.net/" \ | grep '
Successfully removed record.
' \ > /dev/null if [ $? -eq 0 ]; then @@ -98,41 +100,10 @@ dns_he_rm() { ########################## PRIVATE FUNCTIONS ########################### -#-- _find_zone() ------------------------------------------------------- - -# Usage: _authenticate -# -# - needs $HE_Username and $HE_Password -# - sets the $_he_cookie - -_authenticate() { - if [ -z "$HE_Username" ] && [ -z "$HE_Password" ]; then - _err \ - 'No auth details provided. Please set user credentials using the \ - \$HE_Username and \$HE_Password envoronment variables.' - return 1 - fi - # Just get a session - _he_cookie=$( \ - curl -L --silent --show-error -I "https://dns.he.net/" \ - | grep '^Set-Cookie:' \ - | _egrep_o 'CGISESSID=[a-z0-9]*') - # Attempt login - curl -L --silent --show-error --cookie "$_he_cookie" \ - --form "email=${HE_Username}" \ - --form "pass=${HE_Password}" \ - "https://dns.he.net/" \ - > /dev/null - # TODO detect unsuccessful logins -} - - #-- _find_zone() ------------------------------------------------------- # Returns the most specific zone found in administration interface. # -# - needs $_he_cookie -# # Example: # # _find_zone first.second.third.co.uk @@ -155,8 +126,9 @@ _find_zone() { ## _all_zones is an array that looks like this: ## ( zone1:id zone2:id ... ) - _all_zones=( $(curl -L --silent --show-error --cookie "$_he_cookie" \ - "https://dns.he.net/" \ + + body="email=${HE_Username}&pass=${HE_Password}" + _all_zones=( $(_post $body "https://dns.he.net/" \ | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" \ | cut -d '"' -f 3,5 --output-delimiter=":" \ ) ) From 8ca45d3d03718c4ba7dc89eb030209ebe2be9ac7 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sun, 23 Jul 2017 05:13:33 +0200 Subject: [PATCH 049/278] Add HE to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5a3a3ad..7d4353c 100644 --- a/README.md +++ b/README.md @@ -338,6 +338,7 @@ You don't have to do anything manually! 1. Name.com API 1. Dyn Managed DNS API 1. Yandex PDD API (https://pdd.yandex.ru) +1. Hurricane Electric DNS service (https://dns.he.net) And: From f7299403f7b7c0642fee665a7780ef0d8794681f Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sun, 23 Jul 2017 07:31:55 +0200 Subject: [PATCH 050/278] Incorporate Neilpang's comments --- dnsapi/dns_he.sh | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 141268a..019a7a0 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -20,7 +20,7 @@ dns_he_add() { _txt_value=$2 _info "Using DNS-01 Hurricane Electric hook" - if [ -z "$HE_Username" ] && [ -z "$HE_Password" ]; then + if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then _err \ 'No auth details provided. Please set user credentials using the \ \$HE_Username and \$HE_Password envoronment variables.' @@ -46,7 +46,8 @@ dns_he_add() { body="$body&Content=$_txt_value" body="$body&TTL=300" body="$body&hosted_dns_editrecord=Submit" - _post $body "https://dns.he.net/" >/dev/null + response="$(_post $body "https://dns.he.net/")" + _debug2 response "$response" } @@ -68,12 +69,14 @@ dns_he_rm() { body="$body&menu=edit_zone" body="$body&hosted_dns_editzone=" _record_id=$(_post $body "https://dns.he.net/" \ - | grep -A 1 "data=\"\("\)\?${_txt_value}\("\)\?\"" \ - | tail -n 1 \ - | _egrep_o "'[[:digit:]]+','[^']+','TXT'" \ - | cut -b 2- \ - | _egrep_o "[[:digit:]]+" \ - | head -n1) # ... oh my, what have I done... + | tr -d '\n' \ + | _egrep_o "data=\""${_txt_value}"([^>]+>){6}[^<]+<[^;]+;deleteRecord\('[0-9]+','${_full_domain}','TXT'\)" \ + | _egrep_o "[0-9]+','${_full_domain}','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). # Remove the record body="email=${HE_Username}&pass=${HE_Password}" @@ -134,12 +137,12 @@ _find_zone() { ) ) _strip_counter=1 - while [ true ] + while true do _attempted_zone=$(echo $_domain | cut -d . -f ${_strip_counter}-) # All possible zone names have been tried - if [ "$_attempted_zone" == "" ] + if [ -z "$_attempted_zone" ] then _err "No zone for domain \"$_domain\" found." break @@ -151,7 +154,7 @@ _find_zone() { do _zone_name=$(echo $i | cut -d ':' -f 1) _zone_id=$(echo $i | cut -d ':' -f 2) - if [ "$_zone_name" == "$_attempted_zone" ] + if [ "$_zone_name" = "$_attempted_zone" ] then # Zone found - we got $_zone_name and $_zone_id, let's get out... _debug "Found relevant zone \"$_zone_name\" with id" \ @@ -162,7 +165,7 @@ _find_zone() { _debug "Zone \"$_attempted_zone\" doesn't exist, let's try another \ variation." - _strip_counter=$(expr $_strip_counter + 1) + _strip_counter=$(_math $_strip_counter + 1) done # No zone found. From 3281043e2784154345b10aef7b107893ea7ff78b Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Tue, 25 Jul 2017 09:39:15 +0100 Subject: [PATCH 051/278] Clarify keylength parameter to _isEccKey() and _initpath() Closes #950 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 486f867..38965dd 100755 --- a/acme.sh +++ b/acme.sh @@ -926,7 +926,7 @@ _sign() { } -#keylength +#keylength or isEcc flag (empty str => not ecc) _isEccKey() { _length="$1" @@ -2251,7 +2251,7 @@ _initAPI() { _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" } -#[domain] [keylength] +#[domain] [keylength or isEcc flag] _initpath() { __initHome From 29b21b828b01b37ad9f6536cba27af79a1f6aac3 Mon Sep 17 00:00:00 2001 From: Jeremy Knope Date: Wed, 26 Jul 2017 09:44:11 -0400 Subject: [PATCH 052/278] Fix DNS API scripts on *BSD \n isn't available in all regex/sed --- dnsapi/dns_ad.sh | 3 ++- dnsapi/dns_do.sh | 6 ++++-- dnsapi/dns_freedns.sh | 6 ++++-- dnsapi/dns_linode.sh | 6 ++++-- dnsapi/dns_vscale.sh | 3 ++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh index fc4a664..79b8c2a 100755 --- a/dnsapi/dns_ad.sh +++ b/dnsapi/dns_ad.sh @@ -92,7 +92,8 @@ _get_root() { p=1 if _ad_rest GET "domain/"; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" + response="$(echo "$response" | tr -d "\n" | sed 's/{/\ +&/g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 3a2f8f4..fb6d7de 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -69,9 +69,11 @@ _dns_do_list_rrs() { fi _rr_list="$(echo "${response}" \ | tr -d "\n\r\t" \ - | sed -e 's//\n/g' \ + | sed -e 's//\ +/g' \ | grep ">$(_regexcape "$fulldomain")" \ - | sed -e 's/<\/item>/\n/g' \ + | sed -e 's/<\/item>/\ +/g' \ | grep '>id[0-9]{1,16}<' \ | tr -d '><')" diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 53da411..d967763 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -77,7 +77,8 @@ dns_freedns_add() { | grep -i -e ']*>/\n/Ig' \ + | sed 's/<\/TR[^>]*>/\ +/Ig' \ | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ @@ -216,7 +217,8 @@ dns_freedns_rm() { | grep -i -e ']*>/\n/Ig' \ + | sed 's/<\/TR[^>]*>/\ +/Ig' \ | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index 6d54e6c..d84ad5f 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -68,7 +68,8 @@ dns_linode_rm() { _parameters="&DomainID=$_domain_id" if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" + response="$(echo "$response" | tr -d "\n" | sed 's/{/\ +&/g')" resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then @@ -128,7 +129,8 @@ _get_root() { p=1 if _rest GET "domain.list"; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" + response="$(echo "$response" | tr -d "\n" | sed 's/{/\ +&/g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh index e50b7d8..fcd162c 100755 --- a/dnsapi/dns_vscale.sh +++ b/dnsapi/dns_vscale.sh @@ -93,7 +93,8 @@ _get_root() { p=1 if _vscale_rest GET "domains/"; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" + response="$(echo "$response" | tr -d "\n" | sed 's/{/\ +&/g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" From ff74778dea38ef22fb13fbdf452991c4a70135a1 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Wed, 26 Jul 2017 20:15:34 +0200 Subject: [PATCH 053/278] Fix few issues from Travis --- dnsapi/dns_he.sh | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 019a7a0..1a6f8db 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -21,16 +21,14 @@ dns_he_add() { _info "Using DNS-01 Hurricane Electric hook" if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then - _err \ - 'No auth details provided. Please set user credentials using the \ - \$HE_Username and \$HE_Password envoronment variables.' + _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" # fills in the $_zone_id - _find_zone $_full_domain || return 1 + _find_zone "$_full_domain" || return 1 _debug "Zone id \"$_zone_id\" will be used." body="email=${HE_Username}&pass=${HE_Password}" @@ -46,11 +44,10 @@ dns_he_add() { body="$body&Content=$_txt_value" body="$body&TTL=300" body="$body&hosted_dns_editrecord=Submit" - response="$(_post $body "https://dns.he.net/")" + response="$(_post "$body" "https://dns.he.net/")" _debug2 response "$response" } - #-- dns_he_rm() - Remove TXT record ------------------------------------ # Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..." @@ -60,7 +57,7 @@ dns_he_rm() { _info "Cleaning up after DNS-01 Hurricane Electric hook" # fills in the $_zone_id - _find_zone $_full_domain || return 1 + _find_zone "$_full_domain" || return 1 _debug "Zone id \"$_zone_id\" will be used." # Find the record id to clean @@ -68,7 +65,7 @@ dns_he_rm() { body="$body&hosted_dns_zoneid=$_zone_id" body="$body&menu=edit_zone" body="$body&hosted_dns_editzone=" - _record_id=$(_post $body "https://dns.he.net/" \ + _record_id=$(_post "$body" "https://dns.he.net/" \ | tr -d '\n' \ | _egrep_o "data=\""${_txt_value}"([^>]+>){6}[^<]+<[^;]+;deleteRecord\('[0-9]+','${_full_domain}','TXT'\)" \ | _egrep_o "[0-9]+','${_full_domain}','TXT'\)$" \ @@ -87,9 +84,9 @@ dns_he_rm() { body="$body&hosted_dns_delrecord=1" body="$body&hosted_dns_delconfirm=delete" body="$body&hosted_dns_editzone=1" - _post $body "https://dns.he.net/" \ + _post "$body" "https://dns.he.net/" \ | grep '
Successfully removed record.
' \ - > /dev/null + >/dev/null if [ $? -eq 0 ]; then _info "Record removed successfuly." else @@ -99,12 +96,9 @@ dns_he_rm() { fi } - ########################## PRIVATE FUNCTIONS ########################### - #-- _find_zone() ------------------------------------------------------- - # Returns the most specific zone found in administration interface. # # Example: @@ -131,31 +125,28 @@ _find_zone() { ## ( zone1:id zone2:id ... ) body="email=${HE_Username}&pass=${HE_Password}" - _all_zones=( $(_post $body "https://dns.he.net/" \ + # TODO arrays aren't supported in POSIX sh + _all_zones=($(_post $body "https://dns.he.net/" \ | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" \ - | cut -d '"' -f 3,5 --output-delimiter=":" \ - ) ) + | cut -d '"' -f 3,5 --output-delimiter=":" + )) _strip_counter=1 - while true - do - _attempted_zone=$(echo $_domain | cut -d . -f ${_strip_counter}-) + while true; do + _attempted_zone=$(echo "$_domain" | cut -d . -f ${_strip_counter}-) # All possible zone names have been tried - if [ -z "$_attempted_zone" ] - then + if [ -z "$_attempted_zone" ]; then _err "No zone for domain \"$_domain\" found." break fi # Walk through all zones on the account #echo "$_all_zones" | while IFS=' ' read _zone_name _zone_id - for i in ${_all_zones[@]} - do - _zone_name=$(echo $i | cut -d ':' -f 1) - _zone_id=$(echo $i | cut -d ':' -f 2) - if [ "$_zone_name" = "$_attempted_zone" ] - then + for i in ${_all_zones[@]}; do + _zone_name=$(echo "$i" | cut -d ':' -f 1) + _zone_id=$(echo "$i" | cut -d ':' -f 2) + if [ "$_zone_name" = "$_attempted_zone" ]; then # Zone found - we got $_zone_name and $_zone_id, let's get out... _debug "Found relevant zone \"$_zone_name\" with id" \ "\"$_zone_id\" - will be used for domain \"$_domain\"." From 1546b7e5a971578cef11f960a44aadad3a45b754 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Wed, 26 Jul 2017 20:21:36 +0200 Subject: [PATCH 054/278] Missing quotes --- dnsapi/dns_he.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 1a6f8db..3e23b2e 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -126,7 +126,7 @@ _find_zone() { body="email=${HE_Username}&pass=${HE_Password}" # TODO arrays aren't supported in POSIX sh - _all_zones=($(_post $body "https://dns.he.net/" \ + _all_zones=($(_post "$body" "https://dns.he.net/" \ | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" \ | cut -d '"' -f 3,5 --output-delimiter=":" )) From d39649f30d6718d7b0ec7e1e746a518b23bfd9be Mon Sep 17 00:00:00 2001 From: Jeremy Knope Date: Thu, 27 Jul 2017 08:52:46 -0400 Subject: [PATCH 055/278] Revert non-linode scripts since they're untested --- dnsapi/dns_ad.sh | 3 +-- dnsapi/dns_do.sh | 6 ++---- dnsapi/dns_freedns.sh | 6 ++---- dnsapi/dns_vscale.sh | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/dnsapi/dns_ad.sh b/dnsapi/dns_ad.sh index 79b8c2a..fc4a664 100755 --- a/dnsapi/dns_ad.sh +++ b/dnsapi/dns_ad.sh @@ -92,8 +92,7 @@ _get_root() { p=1 if _ad_rest GET "domain/"; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\ -&/g')" + response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index fb6d7de..3a2f8f4 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -69,11 +69,9 @@ _dns_do_list_rrs() { fi _rr_list="$(echo "${response}" \ | tr -d "\n\r\t" \ - | sed -e 's//\ -/g' \ + | sed -e 's//\n/g' \ | grep ">$(_regexcape "$fulldomain")" \ - | sed -e 's/<\/item>/\ -/g' \ + | sed -e 's/<\/item>/\n/g' \ | grep '>id[0-9]{1,16}<' \ | tr -d '><')" diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index d967763..53da411 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -77,8 +77,7 @@ dns_freedns_add() { | grep -i -e ']*>/\ -/Ig' \ + | sed 's/<\/TR[^>]*>/\n/Ig' \ | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ @@ -217,8 +216,7 @@ dns_freedns_rm() { | grep -i -e ']*>/\ -/Ig' \ + | sed 's/<\/TR[^>]*>/\n/Ig' \ | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \ | sed 's/^]*>\|<\/\?T[DH][^>]*>$//Ig' \ | sed 's/<\/T[DH][^>]*>]*>/,/Ig' \ diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh index fcd162c..e50b7d8 100755 --- a/dnsapi/dns_vscale.sh +++ b/dnsapi/dns_vscale.sh @@ -93,8 +93,7 @@ _get_root() { p=1 if _vscale_rest GET "domains/"; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\ -&/g')" + response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" From aefed1d1b9a5264f460aa94e119cb8a40f8bdddd Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Thu, 27 Jul 2017 18:04:53 +0200 Subject: [PATCH 056/278] Get rid of shell arrays. --- dnsapi/dns_he.sh | 73 +++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 3e23b2e..ada5f79 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -121,16 +121,19 @@ _find_zone() { _domain="$1" - ## _all_zones is an array that looks like this: - ## ( zone1:id zone2:id ... ) - body="email=${HE_Username}&pass=${HE_Password}" - # TODO arrays aren't supported in POSIX sh - _all_zones=($(_post "$body" "https://dns.he.net/" \ - | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" \ - | cut -d '"' -f 3,5 --output-delimiter=":" - )) - + _matches=$(_post "$body" "https://dns.he.net/" \ + | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" + ) + # Zone names and zone IDs are in same order + _zone_ids=$(echo "$_matches" | cut -d '"' -f 5 --output-delimiter=":") + _zone_names=$(echo "$_matches" | cut -d '"' -f 3 --output-delimiter=":") + _debug2 "These are the zones on this HE account:" + _debug2 "$_zone_names" + _debug2 "And these are their respective IDs:" + _debug2 "$_zone_ids" + + # Walk through all possible zone names _strip_counter=1 while true; do _attempted_zone=$(echo "$_domain" | cut -d . -f ${_strip_counter}-) @@ -138,28 +141,46 @@ _find_zone() { # All possible zone names have been tried if [ -z "$_attempted_zone" ]; then _err "No zone for domain \"$_domain\" found." - break + return 1 fi - # Walk through all zones on the account - #echo "$_all_zones" | while IFS=' ' read _zone_name _zone_id - for i in ${_all_zones[@]}; do - _zone_name=$(echo "$i" | cut -d ':' -f 1) - _zone_id=$(echo "$i" | cut -d ':' -f 2) - if [ "$_zone_name" = "$_attempted_zone" ]; then - # Zone found - we got $_zone_name and $_zone_id, let's get out... - _debug "Found relevant zone \"$_zone_name\" with id" \ - "\"$_zone_id\" - will be used for domain \"$_domain\"." - return 0 - fi - done - - _debug "Zone \"$_attempted_zone\" doesn't exist, let's try another \ - variation." + _debug "Looking for zone \"${_attempted_zone}\"" + _line_num=$(echo "$_zone_names" | _find_linenum "$_attempted_zone") + if [ -n "$_line_num" ]; then + _zone_id=$(echo "$_zone_ids" | sed "${_line_num}q;d") + _debug "Found relevant zone \"$_attempted_zone\" with id" \ + "\"$_zone_id\" - will be used for domain \"$_domain\"." + return 0 + fi + + _debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less" \ + "specific zone." _strip_counter=$(_math $_strip_counter + 1) done +} - # No zone found. +#-- _find_linenum()----------------------------------------------------- +# Returns line number of line (supplied as an argument) in STDIN. +# +# Example: +# +# printf "a\nb\nc" | _find_linenum "b" +# +# This will: +# - print out 2 because that's the line number of "b" +# - return code 0 because it was found + +_find_linenum() { + _current_line_num=0 + while read line; do + _current_line_num=$(expr "$_current_line_num" + 1) + if [ "$line" = "$1" ]; then + # Found! Let's echo the line number and quit + echo $_current_line_num + return 0 + fi + done + # Not found return 1 } From 235b5b0c154331879b70fd149d1fe7df19eac4c8 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Thu, 27 Jul 2017 18:06:19 +0200 Subject: [PATCH 057/278] Small cosmetic fixes. --- dnsapi/dns_he.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index ada5f79..51418aa 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -11,7 +11,6 @@ # Author: Ondrej Simek # Git repo: https://github.com/angel333/acme.sh - #-- dns_he_add() - Add TXT record -------------------------------------- # Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..." @@ -86,7 +85,7 @@ dns_he_rm() { body="$body&hosted_dns_editzone=1" _post "$body" "https://dns.he.net/" \ | grep '
Successfully removed record.
' \ - >/dev/null + >/dev/null if [ $? -eq 0 ]; then _info "Record removed successfuly." else From 577380e98e33c32f87d74df07d0d9d862b89b5be Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Thu, 27 Jul 2017 18:44:55 +0200 Subject: [PATCH 058/278] Few fixes for shellcheck --- dnsapi/dns_he.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 51418aa..9b789d3 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -154,7 +154,7 @@ _find_zone() { _debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less" \ "specific zone." - _strip_counter=$(_math $_strip_counter + 1) + _strip_counter=$(_math "$_strip_counter" + 1) done } @@ -171,11 +171,11 @@ _find_zone() { _find_linenum() { _current_line_num=0 - while read line; do - _current_line_num=$(expr "$_current_line_num" + 1) + while read -r line; do + _current_line_num=$(_math "$_current_line_num" + 1) if [ "$line" = "$1" ]; then # Found! Let's echo the line number and quit - echo $_current_line_num + echo "$_current_line_num" return 0 fi done From 8e6cf669ad01905d4d9e22f7a2874dcba8c7e263 Mon Sep 17 00:00:00 2001 From: Jeremy Knope Date: Thu, 27 Jul 2017 14:15:19 -0400 Subject: [PATCH 059/278] Try awk instead for \n replacements --- dnsapi/dns_linode.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index d84ad5f..ee3b1c8 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -68,8 +68,7 @@ dns_linode_rm() { _parameters="&DomainID=$_domain_id" if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\ -&/g')" + response="$(echo "$response" | tr -d "\n" | awk '{ gsub("{","\n{",$1); print $1 }')" resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then @@ -129,8 +128,7 @@ _get_root() { p=1 if _rest GET "domain.list"; then - response="$(echo "$response" | tr -d "\n" | sed 's/{/\ -&/g')" + response="$(echo "$response" | tr -d "\n" | awk '{ gsub("{","\n{",$1); print $1 }')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" From 8534e3b2f7fd16477b811c262f60e9b63cb85e32 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Fri, 28 Jul 2017 13:58:47 +0200 Subject: [PATCH 060/278] Make shellcheck happier --- dnsapi/dns_he.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 9b789d3..3ca00af 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -20,6 +20,8 @@ dns_he_add() { _info "Using DNS-01 Hurricane Electric hook" 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 From d6780f9e49f18bf503f46c5d4859289e593935c0 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Fri, 28 Jul 2017 15:29:35 +0200 Subject: [PATCH 061/278] Retain an exit code --- dnsapi/dns_he.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 3ca00af..ab4393c 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -46,7 +46,9 @@ dns_he_add() { body="$body&TTL=300" body="$body&hosted_dns_editrecord=Submit" response="$(_post "$body" "https://dns.he.net/")" + exitcode=$? _debug2 response "$response" + return $exitvalue } #-- dns_he_rm() - Remove TXT record ------------------------------------ From ab1efd923b56b9014f07e7fcac7a81325d93d0b8 Mon Sep 17 00:00:00 2001 From: Jeremy Knope Date: Fri, 28 Jul 2017 10:26:56 -0400 Subject: [PATCH 062/278] back to sed --- dnsapi/dns_linode.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index ee3b1c8..00634d5 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -68,7 +68,7 @@ dns_linode_rm() { _parameters="&DomainID=$_domain_id" if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then - response="$(echo "$response" | tr -d "\n" | awk '{ gsub("{","\n{",$1); print $1 }')" + response="$(echo "$response" | tr -d "\n" | sed $'s/{/\\\n&/g')" resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then @@ -128,7 +128,7 @@ _get_root() { p=1 if _rest GET "domain.list"; then - response="$(echo "$response" | tr -d "\n" | awk '{ gsub("{","\n{",$1); print $1 }')" + response="$(echo "$response" | tr -d "\n" | sed $'s/{/\\\n&/g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" From ccf9a9976c1b7cf31c60a4b74ead0f30fba1de28 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Fri, 28 Jul 2017 18:33:25 +0200 Subject: [PATCH 063/278] Fix the previous rushed commit. --- dnsapi/dns_he.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index ab4393c..2fb1101 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -46,9 +46,14 @@ dns_he_add() { body="$body&TTL=300" body="$body&hosted_dns_editrecord=Submit" response="$(_post "$body" "https://dns.he.net/")" - exitcode=$? + exit_code="$?" + if [ "$exit_code" -eq 0 ]; then + _info "TXT record added successfuly." + else + _err "Couldn't add the TXT record." + return "$exit_code" + fi _debug2 response "$response" - return $exitvalue } #-- dns_he_rm() - Remove TXT record ------------------------------------ @@ -90,7 +95,7 @@ dns_he_rm() { _post "$body" "https://dns.he.net/" \ | grep '
Successfully removed record.
' \ >/dev/null - if [ $? -eq 0 ]; then + if [ "$?" -eq 0 ]; then _info "Record removed successfuly." else _err \ From a5c56c547d024493bb90732cda20108d09392715 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 29 Jul 2017 15:23:31 +0800 Subject: [PATCH 064/278] minor, fix dns param --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 38965dd..9486bcc 100755 --- a/acme.sh +++ b/acme.sh @@ -5440,7 +5440,7 @@ _process() { ;; --dns) wvalue="dns" - if ! _startswith "$2" "-"; then + if [ "$2" ] && ! _startswith "$2" "-"; then wvalue="$2" shift fi From 31b67ab92efd23802acb8510126418b83e1af8db Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sat, 29 Jul 2017 13:52:28 +0200 Subject: [PATCH 065/278] Few non-critical fixes. --- dnsapi/dns_he.sh | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 2fb1101..a160e16 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -28,13 +28,12 @@ dns_he_add() { _saveaccountconf HE_Username "$HE_Username" _saveaccountconf HE_Password "$HE_Password" - # fills in the $_zone_id + # Fills in the $_zone_id _find_zone "$_full_domain" || return 1 _debug "Zone id \"$_zone_id\" will be used." body="email=${HE_Username}&pass=${HE_Password}" body="$body&account=" - body="$body&account=" body="$body&menu=edit_zone" body="$body&Type=TXT" body="$body&hosted_dns_zoneid=$_zone_id" @@ -91,16 +90,13 @@ dns_he_rm() { body="$body&hosted_dns_editzone=1" body="$body&hosted_dns_delrecord=1" body="$body&hosted_dns_delconfirm=delete" - body="$body&hosted_dns_editzone=1" _post "$body" "https://dns.he.net/" \ | grep '
Successfully removed record.
' \ >/dev/null if [ "$?" -eq 0 ]; then _info "Record removed successfuly." else - _err \ - "Could not clean (remove) up the record. Please go to HE" \ - "administration interface and clean it by hand." + _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand." fi } @@ -134,8 +130,8 @@ _find_zone() { | _egrep_o "delete_dom.*name=\"[^\"]+\" value=\"[0-9]+" ) # Zone names and zone IDs are in same order - _zone_ids=$(echo "$_matches" | cut -d '"' -f 5 --output-delimiter=":") - _zone_names=$(echo "$_matches" | cut -d '"' -f 3 --output-delimiter=":") + _zone_ids=$(echo "$_matches" | cut -d '"' -f 5) + _zone_names=$(echo "$_matches" | cut -d '"' -f 3) _debug2 "These are the zones on this HE account:" _debug2 "$_zone_names" _debug2 "And these are their respective IDs:" @@ -156,13 +152,11 @@ _find_zone() { _line_num=$(echo "$_zone_names" | _find_linenum "$_attempted_zone") if [ -n "$_line_num" ]; then _zone_id=$(echo "$_zone_ids" | sed "${_line_num}q;d") - _debug "Found relevant zone \"$_attempted_zone\" with id" \ - "\"$_zone_id\" - will be used for domain \"$_domain\"." + _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." return 0 fi - _debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less" \ - "specific zone." + _debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less specific zone." _strip_counter=$(_math "$_strip_counter" + 1) done } From a25b2af66cd6a4c69ee673ec32aaebd068079042 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sat, 29 Jul 2017 14:25:07 +0200 Subject: [PATCH 066/278] Get rid of _find_num --- dnsapi/dns_he.sh | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index a160e16..7dde55d 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -149,9 +149,18 @@ _find_zone() { fi _debug "Looking for zone \"${_attempted_zone}\"" - _line_num=$(echo "$_zone_names" | _find_linenum "$_attempted_zone") - if [ -n "$_line_num" ]; then - _zone_id=$(echo "$_zone_ids" | sed "${_line_num}q;d") + + # Take care of "." and only match whole lines. Note that grep -F + # cannot be used because there's no way to make it match whole + # lines. + regex="^$(echo "$_attempted_zone" | sed 's/\./\\./g')$" + line_num=$(echo "$_zone_names" \ + | grep -n "$regex" \ + | cut -d : -f 1 + ) + + if [ -n "$line_num" ]; then + _zone_id=$(echo "$_zone_ids" | sed "${line_num}q;d") _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." return 0 fi @@ -160,30 +169,4 @@ _find_zone() { _strip_counter=$(_math "$_strip_counter" + 1) done } - -#-- _find_linenum()----------------------------------------------------- -# Returns line number of line (supplied as an argument) in STDIN. -# -# Example: -# -# printf "a\nb\nc" | _find_linenum "b" -# -# This will: -# - print out 2 because that's the line number of "b" -# - return code 0 because it was found - -_find_linenum() { - _current_line_num=0 - while read -r line; do - _current_line_num=$(_math "$_current_line_num" + 1) - if [ "$line" = "$1" ]; then - # Found! Let's echo the line number and quit - echo "$_current_line_num" - return 0 - fi - done - # Not found - return 1 -} - # vim: et:ts=2:sw=2: From f438ff4bab695818932149d5b6bcecb1b6bed712 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sat, 29 Jul 2017 14:29:13 +0200 Subject: [PATCH 067/278] Fix an improbable corner case. --- dnsapi/dns_he.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 7dde55d..152d469 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -72,10 +72,11 @@ 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]+','${_full_domain}','TXT'\)" \ - | _egrep_o "[0-9]+','${_full_domain}','TXT'\)$" \ + | _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 From baa1160594921e465ee798b77c5add4ed6b1d907 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sat, 29 Jul 2017 14:31:27 +0200 Subject: [PATCH 068/278] Make Shellcheck happier about exit codes --- dnsapi/dns_he.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 152d469..3a1e697 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -94,10 +94,12 @@ dns_he_rm() { _post "$body" "https://dns.he.net/" \ | grep '
Successfully removed record.
' \ >/dev/null - if [ "$?" -eq 0 ]; then + exit_code="$?" + if [ "$exit_code" -eq 0 ]; then _info "Record removed successfuly." else _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand." + return "$exit_code" fi } From 4dd69a8b1adaacfbb5a4894a8a5fc0403e3b4e88 Mon Sep 17 00:00:00 2001 From: Ondrej Simek Date: Sat, 29 Jul 2017 14:40:56 +0200 Subject: [PATCH 069/278] Exit codes, exit codes... Exit codes everywhere... --- dnsapi/dns_he.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 3a1e697..7d86eb7 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -50,9 +50,9 @@ dns_he_add() { _info "TXT record added successfuly." else _err "Couldn't add the TXT record." - return "$exit_code" fi _debug2 response "$response" + return "$exit_code" } #-- dns_he_rm() - Remove TXT record ------------------------------------ From 8eab77f3c607075ff1cd59b59f76b074f1d46605 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Mon, 24 Jul 2017 14:23:01 +0100 Subject: [PATCH 070/278] Add deployment script for unifi controller --- acme.sh | 28 +++++++++++--- deploy/unifi.sh | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 deploy/unifi.sh diff --git a/acme.sh b/acme.sh index 9486bcc..60eef4b 100755 --- a/acme.sh +++ b/acme.sh @@ -1182,6 +1182,28 @@ _ss() { return 1 } +#outfile key cert cacert [password [name [caname]]] +_toPkcs() { + _cpfx="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + pfxPassword="$5" + pfxName="$6" + pfxCaname="$7" + + if [ "$pfxCaname" ]; then + ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" -caname "$pfxCaname" + elif [ "$pfxName" ]; then + ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" + elif [ "$pfxPassword" ]; then + ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" + else + ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" + fi + +} + #domain [password] [isEcc] toPkcs() { domain="$1" @@ -1195,11 +1217,7 @@ toPkcs() { _initpath "$domain" "$_isEcc" - if [ "$pfxPassword" ]; then - ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword" - else - ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" - fi + _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$pfxPassword" if [ "$?" = "0" ]; then _info "Success, Pfx is exported to: $CERT_PFX_PATH" diff --git a/deploy/unifi.sh b/deploy/unifi.sh new file mode 100644 index 0000000..184aa62 --- /dev/null +++ b/deploy/unifi.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env sh + +#Here is a script to deploy cert to unifi server. + +#returns 0 means success, otherwise error. + +#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" +#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise" +#DEPLOY_UNIFI_RELOAD="service unifi restart" + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +unifi_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if ! _exists keytool; then + _err "keytool not found" + return 1 + fi + + DEFAULT_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore" + _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-$DEFAULT_UNIFI_KEYSTORE}" + DEFAULT_UNIFI_KEYPASS="aircontrolenterprise" + _unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-$DEFAULT_UNIFI_KEYPASS}" + DEFAULT_UNIFI_RELOAD="service unifi restart" + _reload="${DEPLOY_UNIFI_RELOAD:-$DEFAULT_UNIFI_RELOAD}" + + _debug _unifi_keystore "$_unifi_keystore" + if [ ! -f "$_unifi_keystore" ]; then + if [ -z "$DEPLOY_UNIFI_KEYSTORE" ]; then + _err "unifi keystore is not found, please define DEPLOY_UNIFI_KEYSTORE" + return 1 + else + _err "It seems that the specified unifi keystore is not valid, please check." + return 1 + fi + fi + if [ ! -w "$_unifi_keystore" ]; then + _err "The file $_unifi_keystore is not writable, please change the permission." + return 1 + fi + + _info "Generate import pkcs12" + _import_pkcs12="$(_mktemp)" + _toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root + if [ "$?" != "0" ]; then + _err "Oops, error creating import pkcs12, please report bug to us." + return 1 + fi + + _info "Modify unifi keystore: $_unifi_keystore" + if keytool -importkeystore \ + -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \ + -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \ + -alias unifi -noprompt; then + _info "Import keystore success!" + rm "$_import_pkcs12" + else + _err "Import unifi keystore error, please report bug to us." + rm "$_import_pkcs12" + return 1 + fi + + _info "Run reload: $_reload" + if eval "$_reload"; then + _info "Reload success!" + if [ "$DEPLOY_UNIFI_KEYSTORE" ]; then + _savedomainconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE" + else + _cleardomainconf DEPLOY_UNIFI_KEYSTORE + fi + if [ "$DEPLOY_UNIFI_KEYPASS" ]; then + _savedomainconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS" + else + _cleardomainconf DEPLOY_UNIFI_KEYPASS + fi + if [ "$DEPLOY_UNIFI_RELOAD" ]; then + _savedomainconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD" + else + _cleardomainconf DEPLOY_UNIFI_RELOAD + fi + return 0 + else + _err "Reload error" + return 1 + fi + return 0 + +} From cd3a4573f26c03315121f505ac97b45c511d9cb9 Mon Sep 17 00:00:00 2001 From: Jeremy Knope Date: Mon, 31 Jul 2017 08:37:30 -0400 Subject: [PATCH 071/278] Fix shell check errors --- dnsapi/dns_linode.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index 00634d5..26ca53e 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -68,7 +68,7 @@ dns_linode_rm() { _parameters="&DomainID=$_domain_id" if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then - response="$(echo "$response" | tr -d "\n" | sed $'s/{/\\\n&/g')" + response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then @@ -128,7 +128,7 @@ _get_root() { p=1 if _rest GET "domain.list"; then - response="$(echo "$response" | tr -d "\n" | sed $'s/{/\\\n&/g')" + response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" From c145f24621455f8608c179fc253d1fd00244bb6f Mon Sep 17 00:00:00 2001 From: Jeremy Knope Date: Thu, 3 Aug 2017 09:47:45 -0400 Subject: [PATCH 072/278] Remove extra space --- dnsapi/dns_linode.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_linode.sh b/dnsapi/dns_linode.sh index 26ca53e..ead5b16 100755 --- a/dnsapi/dns_linode.sh +++ b/dnsapi/dns_linode.sh @@ -68,7 +68,7 @@ dns_linode_rm() { _parameters="&DomainID=$_domain_id" if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then - response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" + response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" if [ "$resource" ]; then @@ -128,7 +128,7 @@ _get_root() { p=1 if _rest GET "domain.list"; then - response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" + response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug h "$h" From 5c3b41bd93c2c4e6da40c445cd5f15a1ba3f7274 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 4 Aug 2017 21:57:45 +0800 Subject: [PATCH 073/278] format --- dnsapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 0888517..b3b6344 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -585,7 +585,7 @@ acme.sh --issue --dns dns_yandex -d mydomain.example.org For issues, please report to https://github.com/non7top/acme.sh/issues. -# 31. Use Hurricane Electric +## 31. Use Hurricane Electric Hurricane Electric doesn't have an API so just set your login credentials like so: From 415f375ce6f0de5a20545ba7ff6a9a7aa044c484 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 10 Aug 2017 21:31:28 +0800 Subject: [PATCH 074/278] support fcrontab, fix https://github.com/Neilpang/acme.sh/issues/966 --- acme.sh | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/acme.sh b/acme.sh index 60eef4b..fe884e3 100755 --- a/acme.sh +++ b/acme.sh @@ -4417,15 +4417,19 @@ _installcert() { installcronjob() { _c_home="$1" _initpath - if ! _exists "crontab"; then - _err "crontab doesn't exist, so, we can not install cron jobs." + _CRONTAB="crontab" + if ! _exists "$_CRONTAB" && _exists "fcrontab"; then + _CRONTAB="fcrontab" + fi + if ! _exists "$_CRONTAB"; then + _err "crontab/fcrontab doesn't exist, so, we can not install cron jobs." _err "All your certs will not be renewed automatically." _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." return 1 fi _info "Installing cron job" - if ! crontab -l | grep "$PROJECT_ENTRY --cron"; then + if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" else @@ -4439,15 +4443,15 @@ installcronjob() { _t=$(_time) random_minute=$(_math $_t % 60) if _exists uname && uname -a | grep SunOS >/dev/null; then - crontab -l | { + $_CRONTAB -l | { cat echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" - } | crontab -- + } | $_CRONTAB -- else - crontab -l | { + $_CRONTAB -l | { cat echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" - } | crontab - + } | $_CRONTAB - fi fi if [ "$?" != "0" ]; then @@ -4459,16 +4463,21 @@ installcronjob() { } uninstallcronjob() { - if ! _exists "crontab"; then + _CRONTAB="crontab" + if ! _exists "$_CRONTAB" && _exists "fcrontab"; then + _CRONTAB="fcrontab" + fi + + if ! _exists "$_CRONTAB"; then return fi _info "Removing cron job" - cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")" + cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")" if [ "$cr" ]; then if _exists uname && uname -a | grep solaris >/dev/null; then - crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -- + $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -- else - crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab - + $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB - fi LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')" _info LE_WORKING_DIR "$LE_WORKING_DIR" @@ -4745,7 +4754,7 @@ _precheck() { fi if [ -z "$_nocron" ]; then - if ! _exists "crontab"; then + if ! _exists "crontab" && ! _exists "fcrontab"; then _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." _err "We need to set cron job to renew the certs automatically." _err "Otherwise, your certs will not be able to be renewed automatically." From 96801e3478d64427f8ec0937de1d30ec05b9432a Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Fri, 11 Aug 2017 16:46:29 -0500 Subject: [PATCH 075/278] Update DuckDNS support, fix failure on first call, no longer save the domain/username as a global, and other tweaks --- dnsapi/README.md | 5 +-- dnsapi/dns_duckdns.sh | 79 +++++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index b3b6344..34b3867 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -512,14 +512,11 @@ export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" ``` Please note that since DuckDNS uses StartSSL as their cert provider, thus ---insecure must be used when issuing certs: +--insecure may need to be used when issuing certs: ``` acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org ``` -Also, DuckDNS uses the domain name as username for recording changing, so the -account file will always store the lastly used domain name. - For issues, please report to https://github.com/raidenii/acme.sh/issues. ## 28. Use Name.com API diff --git a/dnsapi/dns_duckdns.sh b/dnsapi/dns_duckdns.sh index d698735..711b81e 100755 --- a/dnsapi/dns_duckdns.sh +++ b/dnsapi/dns_duckdns.sh @@ -3,11 +3,14 @@ #Created by RaidenII, to use DuckDNS's API to add/remove text records #06/27/2017 -# Currently only support single domain access -# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure must be used with acme.sh +# Pass credentials before "acme.sh --issue --dns dns_duckdns ..." +# -- +# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" +# -- +# +# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh DuckDNS_API="https://www.duckdns.org/update" -API_Params="domains=$DuckDNS_Domain&token=$DuckDNS_Token" ######## Public functions ##################### @@ -16,35 +19,36 @@ dns_duckdns_add() { fulldomain=$1 txtvalue=$2 - # We'll extract the domain/username from full domain - DuckDNS_Domain=$(echo "$fulldomain" | _lower_case | _egrep_o '.[^.]*.duckdns.org' | cut -d . -f 2) - - if [ -z "$DuckDNS_Domain" ]; then - _err "Error extracting the domain." - return 1 - fi - + DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}" if [ -z "$DuckDNS_Token" ]; then - DuckDNS_Token="" + _err "You must export variable: DuckDNS_Token" _err "The token for your DuckDNS account is necessary." _err "You can look it up in your DuckDNS account." return 1 fi # Now save the credentials. - _saveaccountconf DuckDNS_Domain "$DuckDNS_Domain" - _saveaccountconf DuckDNS_Token "$DuckDNS_Token" + _saveaccountconf_mutable DuckDNS_Token "$DuckDNS_Token" # Unfortunately, DuckDNS does not seems to support lookup domain through API # So I assume your credentials (which are your domain and token) are correct # If something goes wrong, we will get a KO response from DuckDNS + if ! _duckdns_get_domain; then + return 1 + fi + # Now add the TXT record to DuckDNS _info "Trying to add TXT record" - if _duckdns_rest GET "$API_Params&txt=$txtvalue" && [ "$response" = "OK" ]; then - _info "TXT record has been successfully added to your DuckDNS domain." - _info "Note that all subdomains under this domain uses the same TXT record." - return 0 + if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=$txtvalue"; then + if [ "$response" = "OK" ]; then + _info "TXT record has been successfully added to your DuckDNS domain." + _info "Note that all subdomains under this domain uses the same TXT record." + return 0 + else + _err "Errors happened during adding the TXT record, response=$response" + return 1 + fi else _err "Errors happened during adding the TXT record." return 1 @@ -57,11 +61,28 @@ dns_duckdns_rm() { fulldomain=$1 txtvalue=$2 + DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}" + if [ -z "$DuckDNS_Token" ]; then + _err "You must export variable: DuckDNS_Token" + _err "The token for your DuckDNS account is necessary." + _err "You can look it up in your DuckDNS account." + return 1 + fi + + if ! _duckdns_get_domain; then + return 1 + fi + # Now remove the TXT record from DuckDNS _info "Trying to remove TXT record" - if _duckdns_rest GET "$API_Params&txt=&clear=true" && [ "$response" = "OK" ]; then - _info "TXT record has been successfully removed from your DuckDNS domain." - return 0 + if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=&clear=true"; then + if [ "$response" = "OK" ]; then + _info "TXT record has been successfully removed from your DuckDNS domain." + return 0 + else + _err "Errors happened during removing the TXT record, response=$response" + return 1 + fi else _err "Errors happened during removing the TXT record." return 1 @@ -70,6 +91,22 @@ dns_duckdns_rm() { #################### Private functions below ################################## +#fulldomain=_acme-challenge.domain.duckdns.org +#returns +# _duckdns_domain=domain +_duckdns_get_domain() { + + # We'll extract the domain/username from full domain + _duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.]duckdns.org' | cut -d . -f 2)" + + if [ -z "$_duckdns_domain" ]; then + _err "Error extracting the domain." + return 1 + fi + + return 0 +} + #Usage: method URI _duckdns_rest() { method=$1 From a07395fb563faff04f786e2e352aacf4dc7840ce Mon Sep 17 00:00:00 2001 From: Michele Date: Thu, 17 Aug 2017 12:50:28 +0200 Subject: [PATCH 076/278] Added support for busybox in time2str() awk fallback if none of preceding method works --- acme.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acme.sh b/acme.sh index fe884e3..9bf9e1e 100755 --- a/acme.sh +++ b/acme.sh @@ -1367,6 +1367,10 @@ _time2str() { echo "$_t_s_a" fi + #Busybox + if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then + return + fi } _normalizeJson() { From 309bec474f11c1c9a2be472c44818703bfe6d911 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 22 Aug 2017 20:27:13 +0800 Subject: [PATCH 077/278] add warning for dns manual mode --- acme.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/acme.sh b/acme.sh index 9bf9e1e..cd2f981 100755 --- a/acme.sh +++ b/acme.sh @@ -100,6 +100,10 @@ _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations" _STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode" +_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." + +_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR" + __INTERACTIVE="" if [ -t 1 ]; then __INTERACTIVE="1" @@ -3046,6 +3050,10 @@ _on_issue_err() { ) fi + if [ "$IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "dns"; then + _err "$_DNS_MANUAL_ERR" + fi + if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then _debug "$(_dlg_versions)" fi @@ -3078,6 +3086,10 @@ _on_issue_success() { fi fi + if _hasfield "$Le_Webroot" "dns"; then + _err "$_DNS_MANUAL_WARN" + fi + } updateaccount() { From 48e9006cd120317b62ee17e207c82517cc629d31 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 25 Aug 2017 20:51:31 +0800 Subject: [PATCH 078/278] fix cpanel uapi --- deploy/README.md | 18 ++++++++++++++++++ deploy/cpanel_uapi.sh | 15 ++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index 232fdb4..e026cad 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -80,3 +80,21 @@ acme.sh --deploy -d ftp.example.com --deploy-hook exim4 ```sh acme.sh --deploy -d ftp.example.com --deploy-hook keychain ``` + +## 7. Deploy to cpanel host. + +``` +acme.sh --deploy -d example.com --deploy-hook cpanel_uapi +``` + +If you are login as root, please specify the username to deploy cert to: + +```sh +export DEPLOY_CPANEL_USER=username +acme.sh --deploy -d example.com --deploy-hook cpanel_uapi +``` + + + + + diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh index ded50d0..89ce198 100644 --- a/deploy/cpanel_uapi.sh +++ b/deploy/cpanel_uapi.sh @@ -11,7 +11,7 @@ #domain keyfile certfile cafile fullchain -cpanel_uapi() { +cpanel_uapi_deploy() { _cdomain="$1" _ckey="$2" _ccert="$3" @@ -24,6 +24,14 @@ cpanel_uapi() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" + if ! _exists uapi; then + _err "The command uapi is not found." + return 1 + fi + if ! _exists php; then + _err "The command php is not found." + return 1 + fi # read cert and key files and urlencode both _certstr=$(cat "$_ccert") _keystr=$(cat "$_ckey") @@ -34,6 +42,11 @@ cpanel_uapi() { _debug _key "$_key" if [ "$(id -u)" = 0 ]; then + if [ -z "$DEPLOY_CPANEL_USER" ]; then + _err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username" + return 1; + fi + _savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER" _response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") else _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") From a2d6daaef47feec91dcbf4dc1c4caec8630c1484 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 25 Aug 2017 20:54:10 +0800 Subject: [PATCH 079/278] fix cpanel_uapi.sh --- deploy/cpanel_uapi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh index 89ce198..2ba112c 100644 --- a/deploy/cpanel_uapi.sh +++ b/deploy/cpanel_uapi.sh @@ -44,7 +44,7 @@ cpanel_uapi_deploy() { if [ "$(id -u)" = 0 ]; then if [ -z "$DEPLOY_CPANEL_USER" ]; then _err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username" - return 1; + return 1 fi _savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER" _response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") From 88bb7b780d445d1874611aa841b51bdb153f1bfb Mon Sep 17 00:00:00 2001 From: fritteli Date: Tue, 29 Aug 2017 22:35:11 +0200 Subject: [PATCH 080/278] Typos --- dnsapi/dns_he.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index 7d86eb7..4d1973a 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -47,7 +47,7 @@ dns_he_add() { response="$(_post "$body" "https://dns.he.net/")" exit_code="$?" if [ "$exit_code" -eq 0 ]; then - _info "TXT record added successfuly." + _info "TXT record added successfully." else _err "Couldn't add the TXT record." fi @@ -96,7 +96,7 @@ dns_he_rm() { >/dev/null exit_code="$?" if [ "$exit_code" -eq 0 ]; then - _info "Record removed successfuly." + _info "Record removed successfully." else _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand." return "$exit_code" From f6da19ba835e52fba79caec2544aa89af0116c62 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 29 Aug 2017 23:13:29 +0200 Subject: [PATCH 081/278] add deploy script for the AVM FRITZ!Box --- deploy/fritzbox.sh | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 deploy/fritzbox.sh diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh new file mode 100644 index 0000000..1290dc7 --- /dev/null +++ b/deploy/fritzbox.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env sh + +#Here is a script to deploy cert to an AVM FRITZ!Box router. + +#returns 0 means success, otherwise error. + +#DEPLOY_FRITZBOX_USERNAME="username" +#DEPLOY_FRITZBOX_PASSWORD="password" +#DEPLOY_FRITZBOX_URL="https://fritz.box" + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +fritzbox_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if ! _exists wget; then + _err "wget not found" + return 1 + fi + if ! exists iconv; then + _err "iconv not found" + return 1 + fi + + _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" + _fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}" + _fritzbox_url="${DEPLOY_FRITZBOX_URL}" + + _debug _fritzbox_url "$_fritzbox_url" + _debug _fritzbox_usename "$_fritzbox_username" + _secure_debug _fritzbox_password "$_fritzbox_password" + if [ ! -z "$_fritzbox_username" ]; then + _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." + return 1 + fi + if [ ! -z "$_fritzbox_password" ]; then + _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." + return 1 + fi + if [ ! -z "$_fritzbox_url" ]; then + _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." + return 1 + fi + + _info "Log in in to the FRITZ!Box" + _fritzbox_challenge="$(wget -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" + _fritzbox_hash="$(echo -n ${_fritzbox_challenge}-${_fritzbox_password} | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" + _fritzbox_sid="$(wget -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_frithbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + + _info "Generate form POST request" + _post_request="$(_mktemp)" + _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" + printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n${_fritzbox_sid}\r\n" >> "${_post_request}" + printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! + printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n${_CERTPASSWORD_}\r\n" >> "${_post_request}" + printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> "${_post_request}" + printf "Content-Type: application/octet-stream\r\n\r\n" >> "${_post_request}" + cat "${_ckey}" >> "${_post_request}" + cat "${_cfullchain}" >> "${_post_request}" + printf "\r\n" >> "${_post_request}" + printf -- "--${_post_boundary}--" >> "${_post_request}" + + _info "Upload certificate to the FRITZ!Box" + wget -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" + + _info "Upload successful" + rm "${_post_request}" + + return 0 +} + From 4bb488258d24159284fc55b5b81d7a146880f0fc Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 29 Aug 2017 23:53:41 +0200 Subject: [PATCH 082/278] - Bugfixes - Make sure the login actually worked - Less output --- deploy/fritzbox.sh | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index 1290dc7..c6a730c 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -28,7 +28,7 @@ fritzbox_deploy() { _err "wget not found" return 1 fi - if ! exists iconv; then + if ! _exists iconv; then _err "iconv not found" return 1 fi @@ -40,23 +40,32 @@ fritzbox_deploy() { _debug _fritzbox_url "$_fritzbox_url" _debug _fritzbox_usename "$_fritzbox_username" _secure_debug _fritzbox_password "$_fritzbox_password" - if [ ! -z "$_fritzbox_username" ]; then + if [ -z "$_fritzbox_username" ]; then _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." return 1 fi - if [ ! -z "$_fritzbox_password" ]; then + if [ -z "$_fritzbox_password" ]; then _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." return 1 fi - if [ ! -z "$_fritzbox_url" ]; then + if [ -z "$_fritzbox_url" ]; then _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." return 1 fi - _info "Log in in to the FRITZ!Box" + _saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}" + _saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" + _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" + + _info "Log in to the FRITZ!Box" _fritzbox_challenge="$(wget -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" _fritzbox_hash="$(echo -n ${_fritzbox_challenge}-${_fritzbox_password} | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" - _fritzbox_sid="$(wget -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_frithbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + _fritzbox_sid="$(wget -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + + if [ -z "${_fritzbox_sid}" -o "${_fritzbox_sid}" = "0000000000000000" ] ; then + _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." + return 1 + fi _info "Generate form POST request" _post_request="$(_mktemp)" @@ -65,6 +74,7 @@ fritzbox_deploy() { printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n${_fritzbox_sid}\r\n" >> "${_post_request}" printf -- "--${_post_boundary}\r\n" >> "${_post_request}" # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! + _CERTPASSWORD_= printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n${_CERTPASSWORD_}\r\n" >> "${_post_request}" printf -- "--${_post_boundary}\r\n" >> "${_post_request}" printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> "${_post_request}" @@ -75,11 +85,10 @@ fritzbox_deploy() { printf -- "--${_post_boundary}--" >> "${_post_request}" _info "Upload certificate to the FRITZ!Box" - wget -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" + wget -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL _info "Upload successful" rm "${_post_request}" return 0 } - From d50281453d6bc704c0a647b523073f06b002ca34 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 29 Aug 2017 23:57:24 +0200 Subject: [PATCH 083/278] Add --no-check-certificate option to wget, or else the initial deployment won't work because there isn't a valid certificate installed on the router yet. --- deploy/fritzbox.sh | 6 +-- deploy/fritzbox.sh~ | 94 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 deploy/fritzbox.sh~ diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index c6a730c..16c310b 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -58,9 +58,9 @@ fritzbox_deploy() { _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" _info "Log in to the FRITZ!Box" - _fritzbox_challenge="$(wget -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" + _fritzbox_challenge="$(wget --no-check-certificate -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" _fritzbox_hash="$(echo -n ${_fritzbox_challenge}-${_fritzbox_password} | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" - _fritzbox_sid="$(wget -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + _fritzbox_sid="$(wget --no-check-certificate -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" if [ -z "${_fritzbox_sid}" -o "${_fritzbox_sid}" = "0000000000000000" ] ; then _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." @@ -85,7 +85,7 @@ fritzbox_deploy() { printf -- "--${_post_boundary}--" >> "${_post_request}" _info "Upload certificate to the FRITZ!Box" - wget -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL + wget --no-check-certificate -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL _info "Upload successful" rm "${_post_request}" diff --git a/deploy/fritzbox.sh~ b/deploy/fritzbox.sh~ new file mode 100644 index 0000000..c6a730c --- /dev/null +++ b/deploy/fritzbox.sh~ @@ -0,0 +1,94 @@ +#!/usr/bin/env sh + +#Here is a script to deploy cert to an AVM FRITZ!Box router. + +#returns 0 means success, otherwise error. + +#DEPLOY_FRITZBOX_USERNAME="username" +#DEPLOY_FRITZBOX_PASSWORD="password" +#DEPLOY_FRITZBOX_URL="https://fritz.box" + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +fritzbox_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if ! _exists wget; then + _err "wget not found" + return 1 + fi + if ! _exists iconv; then + _err "iconv not found" + return 1 + fi + + _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" + _fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}" + _fritzbox_url="${DEPLOY_FRITZBOX_URL}" + + _debug _fritzbox_url "$_fritzbox_url" + _debug _fritzbox_usename "$_fritzbox_username" + _secure_debug _fritzbox_password "$_fritzbox_password" + if [ -z "$_fritzbox_username" ]; then + _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." + return 1 + fi + if [ -z "$_fritzbox_password" ]; then + _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." + return 1 + fi + if [ -z "$_fritzbox_url" ]; then + _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." + return 1 + fi + + _saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}" + _saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" + _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" + + _info "Log in to the FRITZ!Box" + _fritzbox_challenge="$(wget -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" + _fritzbox_hash="$(echo -n ${_fritzbox_challenge}-${_fritzbox_password} | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" + _fritzbox_sid="$(wget -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + + if [ -z "${_fritzbox_sid}" -o "${_fritzbox_sid}" = "0000000000000000" ] ; then + _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." + return 1 + fi + + _info "Generate form POST request" + _post_request="$(_mktemp)" + _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" + printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n${_fritzbox_sid}\r\n" >> "${_post_request}" + printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! + _CERTPASSWORD_= + printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n${_CERTPASSWORD_}\r\n" >> "${_post_request}" + printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> "${_post_request}" + printf "Content-Type: application/octet-stream\r\n\r\n" >> "${_post_request}" + cat "${_ckey}" >> "${_post_request}" + cat "${_cfullchain}" >> "${_post_request}" + printf "\r\n" >> "${_post_request}" + printf -- "--${_post_boundary}--" >> "${_post_request}" + + _info "Upload certificate to the FRITZ!Box" + wget -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL + + _info "Upload successful" + rm "${_post_request}" + + return 0 +} From e6f81173a38c5768aae800acf058a2aea07f092e Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 29 Aug 2017 23:58:20 +0200 Subject: [PATCH 084/278] Delete auto-backup file --- deploy/fritzbox.sh~ | 94 --------------------------------------------- 1 file changed, 94 deletions(-) delete mode 100644 deploy/fritzbox.sh~ diff --git a/deploy/fritzbox.sh~ b/deploy/fritzbox.sh~ deleted file mode 100644 index c6a730c..0000000 --- a/deploy/fritzbox.sh~ +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env sh - -#Here is a script to deploy cert to an AVM FRITZ!Box router. - -#returns 0 means success, otherwise error. - -#DEPLOY_FRITZBOX_USERNAME="username" -#DEPLOY_FRITZBOX_PASSWORD="password" -#DEPLOY_FRITZBOX_URL="https://fritz.box" - -######## Public functions ##################### - -#domain keyfile certfile cafile fullchain -fritzbox_deploy() { - _cdomain="$1" - _ckey="$2" - _ccert="$3" - _cca="$4" - _cfullchain="$5" - - _debug _cdomain "$_cdomain" - _debug _ckey "$_ckey" - _debug _ccert "$_ccert" - _debug _cca "$_cca" - _debug _cfullchain "$_cfullchain" - - if ! _exists wget; then - _err "wget not found" - return 1 - fi - if ! _exists iconv; then - _err "iconv not found" - return 1 - fi - - _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" - _fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}" - _fritzbox_url="${DEPLOY_FRITZBOX_URL}" - - _debug _fritzbox_url "$_fritzbox_url" - _debug _fritzbox_usename "$_fritzbox_username" - _secure_debug _fritzbox_password "$_fritzbox_password" - if [ -z "$_fritzbox_username" ]; then - _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." - return 1 - fi - if [ -z "$_fritzbox_password" ]; then - _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." - return 1 - fi - if [ -z "$_fritzbox_url" ]; then - _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." - return 1 - fi - - _saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}" - _saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" - _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" - - _info "Log in to the FRITZ!Box" - _fritzbox_challenge="$(wget -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" - _fritzbox_hash="$(echo -n ${_fritzbox_challenge}-${_fritzbox_password} | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" - _fritzbox_sid="$(wget -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" - - if [ -z "${_fritzbox_sid}" -o "${_fritzbox_sid}" = "0000000000000000" ] ; then - _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." - return 1 - fi - - _info "Generate form POST request" - _post_request="$(_mktemp)" - _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" - printf -- "--${_post_boundary}\r\n" >> "${_post_request}" - printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n${_fritzbox_sid}\r\n" >> "${_post_request}" - printf -- "--${_post_boundary}\r\n" >> "${_post_request}" - # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! - _CERTPASSWORD_= - printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n${_CERTPASSWORD_}\r\n" >> "${_post_request}" - printf -- "--${_post_boundary}\r\n" >> "${_post_request}" - printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> "${_post_request}" - printf "Content-Type: application/octet-stream\r\n\r\n" >> "${_post_request}" - cat "${_ckey}" >> "${_post_request}" - cat "${_cfullchain}" >> "${_post_request}" - printf "\r\n" >> "${_post_request}" - printf -- "--${_post_boundary}--" >> "${_post_request}" - - _info "Upload certificate to the FRITZ!Box" - wget -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL - - _info "Upload successful" - rm "${_post_request}" - - return 0 -} From 412e4e6cf9ecbf26c4d9b9330c3dfef1ddc92e42 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Wed, 30 Aug 2017 00:24:31 +0200 Subject: [PATCH 085/278] Add acknowledgement note --- deploy/fritzbox.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index 16c310b..804548c 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -8,6 +8,9 @@ #DEPLOY_FRITZBOX_PASSWORD="password" #DEPLOY_FRITZBOX_URL="https://fritz.box" +# Kudos to wikrie at Github for his FRITZ!Box update script: +# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb + ######## Public functions ##################### #domain keyfile certfile cafile fullchain From b6d48b7a144cefeebd3db92f8a3cabcad05139c6 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Wed, 30 Aug 2017 00:45:03 +0200 Subject: [PATCH 086/278] Update README.md for the deploy hooks. --- deploy/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index e026cad..d0f3d7f 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -93,8 +93,18 @@ If you are login as root, please specify the username to deploy cert to: export DEPLOY_CPANEL_USER=username acme.sh --deploy -d example.com --deploy-hook cpanel_uapi ``` +## 8. Deploy the cert to your FRITZ!Box router +You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables: +```sh +$ export DEPLOY_FRITZBOX_USERNAME=my_username +$ export DEPLOY_FRITZBOX_PASSWORD=the_password +$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com +``` +After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this: - +```sh +acme.sh --deploy -d fritz.box --deploy-hook fritzbox +``` From 103fa959cb7346d6bbade60d4009df84f1726a21 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Wed, 30 Aug 2017 00:47:31 +0200 Subject: [PATCH 087/278] Typo --- deploy/fritzbox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index 804548c..cea84f5 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -41,7 +41,7 @@ fritzbox_deploy() { _fritzbox_url="${DEPLOY_FRITZBOX_URL}" _debug _fritzbox_url "$_fritzbox_url" - _debug _fritzbox_usename "$_fritzbox_username" + _debug _fritzbox_username "$_fritzbox_username" _secure_debug _fritzbox_password "$_fritzbox_password" if [ -z "$_fritzbox_username" ]; then _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." From a3a92ff1dfc283ffc80bfd2a3cb9527295a832e7 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Thu, 31 Aug 2017 17:12:11 +0200 Subject: [PATCH 088/278] Fix formatting errors. --- deploy/fritzbox.sh | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index cea84f5..e2102ed 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -61,11 +61,11 @@ fritzbox_deploy() { _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" _info "Log in to the FRITZ!Box" - _fritzbox_challenge="$(wget --no-check-certificate -q -O - ${_fritzbox_url}/login_sid.lua | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" - _fritzbox_hash="$(echo -n ${_fritzbox_challenge}-${_fritzbox_password} | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" - _fritzbox_sid="$(wget --no-check-certificate -q -O - ${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash} | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + _fritzbox_challenge="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" + _fritzbox_hash="$(echo -n "${_fritzbox_challenge}-${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" + _fritzbox_sid="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" - if [ -z "${_fritzbox_sid}" -o "${_fritzbox_sid}" = "0000000000000000" ] ; then + if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." return 1 fi @@ -73,19 +73,24 @@ fritzbox_deploy() { _info "Generate form POST request" _post_request="$(_mktemp)" _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" - printf -- "--${_post_boundary}\r\n" >> "${_post_request}" - printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n${_fritzbox_sid}\r\n" >> "${_post_request}" - printf -- "--${_post_boundary}\r\n" >> "${_post_request}" + { + printf -- "--%s\r\n" "${_post_boundary}"; + printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"; + printf -- "--%s\r\n""${_post_boundary}"; + } >>"${_post_request}" # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! _CERTPASSWORD_= - printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n${_CERTPASSWORD_}\r\n" >> "${_post_request}" - printf -- "--${_post_boundary}\r\n" >> "${_post_request}" - printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> "${_post_request}" - printf "Content-Type: application/octet-stream\r\n\r\n" >> "${_post_request}" - cat "${_ckey}" >> "${_post_request}" - cat "${_cfullchain}" >> "${_post_request}" - printf "\r\n" >> "${_post_request}" - printf -- "--${_post_boundary}--" >> "${_post_request}" + { + printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"; + printf -- "--%s\r\n" "${_post_boundary}"; + printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"; + printf "Content-Type: application/octet-stream\r\n\r\n"; + } >>"${_post_request}" + cat "${_ckey}${_cfullchain}" >>"${_post_request}" + { + printf "\r\n"; + printf -- "--%s--" "${_post_boundary}"; + } >>"${_post_request}" _info "Upload certificate to the FRITZ!Box" wget --no-check-certificate -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL From 6cb5377d73c2ad6a4c9e7adfc727b191f127910b Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Thu, 31 Aug 2017 17:25:08 +0200 Subject: [PATCH 089/278] Fix bugs and more/new formatting errors. --- deploy/fritzbox.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index e2102ed..1fe28e8 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -63,7 +63,7 @@ fritzbox_deploy() { _info "Log in to the FRITZ!Box" _fritzbox_challenge="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" _fritzbox_hash="$(echo -n "${_fritzbox_challenge}-${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" - _fritzbox_sid="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua?sid=0000000000000000\&username=${_fritzbox_username}\&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + _fritzbox_sid="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." @@ -74,22 +74,22 @@ fritzbox_deploy() { _post_request="$(_mktemp)" _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" { - printf -- "--%s\r\n" "${_post_boundary}"; - printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"; - printf -- "--%s\r\n""${_post_boundary}"; + printf -- "--%s\r\n" "${_post_boundary}" + printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}" + printf -- "--%s\r\n" "${_post_boundary}" } >>"${_post_request}" # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! _CERTPASSWORD_= { - printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"; - printf -- "--%s\r\n" "${_post_boundary}"; - printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"; - printf "Content-Type: application/octet-stream\r\n\r\n"; + printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}" + printf -- "--%s\r\n" "${_post_boundary}" + printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" + printf "Content-Type: application/octet-stream\r\n\r\n" } >>"${_post_request}" - cat "${_ckey}${_cfullchain}" >>"${_post_request}" + cat "${_ckey}" "${_cfullchain}" >>"${_post_request}" { - printf "\r\n"; - printf -- "--%s--" "${_post_boundary}"; + printf "\r\n" + printf -- "--%s--" "${_post_boundary}" } >>"${_post_request}" _info "Upload certificate to the FRITZ!Box" From 3794b5cb5846cc9713979027d68fc930211ec86e Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 1 Sep 2017 23:01:37 +0800 Subject: [PATCH 090/278] fix changes for :https://community.letsencrypt.org/t/acme-sh-standalone-fails-multiple-validation-requests-staging-multi-va/41249/8 use socat instead of nc. --- acme.sh | 79 +++++++++++++-------------------------------------------- 1 file changed, 17 insertions(+), 62 deletions(-) diff --git a/acme.sh b/acme.sh index cd2f981..c7f8edd 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.3 +VER=2.7.4 PROJECT_NAME="acme.sh" @@ -164,11 +164,11 @@ _dlg_versions() { echo "nginx doesn't exists." fi - echo "nc:" - if _exists "nc"; then - nc -h 2>&1 + echo "socat:" + if _exists "socat"; then + socat -h 2>&1 else - _debug "nc doesn't exists." + _debug "socat doesn't exists." fi } @@ -1967,68 +1967,22 @@ _startserver() { _debug "ncaddr" "$ncaddr" _debug "startserver: $$" - nchelp="$(nc -h 2>&1)" _debug Le_HTTPPort "$Le_HTTPPort" _debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V6 "$Le_Listen_V6" - _NC="nc" + _NC="socat" if [ "$Le_Listen_V4" ]; then _NC="$_NC -4" elif [ "$Le_Listen_V6" ]; then _NC="$_NC -6" fi - if [ "$Le_Listen_V4$Le_Listen_V6$ncaddr" ]; then - if ! _contains "$nchelp" "-4"; then - _err "The nc doesn't support '-4', '-6' or local-address, please install 'netcat-openbsd' and try again." - _err "See $(__green $_PREPARE_LINK)" - return 1 - fi - fi - - if echo "$nchelp" | grep "\-q[ ,]" >/dev/null; then - _NC="$_NC -q 1 -l $ncaddr" - else - if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null; then - _NC="$_NC -c -l $ncaddr" - elif echo "$nchelp" | grep "\-N" | grep "Shutdown the network socket after EOF on stdin" >/dev/null; then - _NC="$_NC -N -l $ncaddr" - else - _NC="$_NC -l $ncaddr" - fi - fi - _debug "_NC" "$_NC" - - #for centos ncat - if _contains "$nchelp" "nmap.org"; then - _debug "Using ncat: nmap.org" - if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"; then - _exec_err - return 1 - fi - if [ "$DEBUG" ]; then - _exec_err - fi - return - fi - - # while true ; do - if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC -p \"$Le_HTTPPort\" >&2"; then - _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2" - fi - - if [ "$?" != "0" ]; then - _err "nc listen error." - _exec_err - exit 1 - fi - if [ "$DEBUG" ]; then - _exec_err - fi - # done + #todo listen address + socat TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & + serverproc="$!" } _stopserver() { @@ -2038,6 +1992,8 @@ _stopserver() { return fi + kill $pid + _debug2 "Le_HTTPPort" "$Le_HTTPPort" if [ "$Le_HTTPPort" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then @@ -2943,8 +2899,8 @@ _on_before_issue() { fi if _hasfield "$_chk_web_roots" "$NO_VALUE"; then - if ! _exists "nc"; then - _err "Please install netcat(nc) tools first." + if ! _exists "socat"; then + _err "Please install socat tools first." return 1 fi fi @@ -3665,13 +3621,12 @@ issue() { _info "Standalone mode server" _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")" _ncIndex="$(_math $_ncIndex + 1)" - _startserver "$keyauthorization" "$_ncaddr" & + _startserver "$keyauthorization" "$_ncaddr" if [ "$?" != "0" ]; then _clearup _on_issue_err "$_post_hook" "$vlist" return 1 fi - serverproc="$!" sleep 1 _debug serverproc "$serverproc" elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then @@ -4788,9 +4743,9 @@ _precheck() { return 1 fi - if ! _exists "nc"; then - _err "It is recommended to install nc first, try to install 'nc' or 'netcat'." - _err "We use nc for standalone server if you use standalone mode." + if ! _exists "socat"; then + _err "It is recommended to install socat first." + _err "We use socat for standalone server if you use standalone mode." _err "If you don't use standalone mode, just ignore this warning." fi From 443a5ca0c2d0af8e11f720f3acbb04ee628a001b Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 1 Sep 2017 23:44:52 +0800 Subject: [PATCH 091/278] socat tls mode --- acme.sh | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/acme.sh b/acme.sh index c7f8edd..84ae7e9 100755 --- a/acme.sh +++ b/acme.sh @@ -1994,25 +1994,6 @@ _stopserver() { kill $pid - _debug2 "Le_HTTPPort" "$Le_HTTPPort" - if [ "$Le_HTTPPort" ]; then - if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then - _get "http://localhost:$Le_HTTPPort" "" 1 - else - _get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1 - fi - fi - - _debug2 "Le_TLSPort" "$Le_TLSPort" - if [ "$Le_TLSPort" ]; then - if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then - _get "https://localhost:$Le_TLSPort" "" 1 - _get "https://localhost:$Le_TLSPort" "" 1 - else - _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1 - _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1 - fi - fi } # sleep sec @@ -2067,12 +2048,7 @@ _starttlsserver() { return 1 fi - __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -cert $TLS_CERT -key $TLS_KEY " - if [ "$opaddr" ]; then - __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" - else - __S_OPENSSL="$__S_OPENSSL -accept $port" - fi + __S_OPENSSL="socat" _debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V6 "$Le_Listen_V6" @@ -2083,12 +2059,9 @@ _starttlsserver() { fi _debug "$__S_OPENSSL" - if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then - (printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL -tlsextdebug) & - else - (printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL >/dev/null 2>&1) & - fi + #todo listen address + $__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" sleep 1 _debug serverproc "$serverproc" From 7b8ddfdd96d5edd6992335a1f289b845ab9403d2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Sep 2017 08:59:30 +0800 Subject: [PATCH 092/278] use socat --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2ba02b9..1378e94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ install: - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then brew update && brew install openssl; brew info openssl; + brew info socat; 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; @@ -30,6 +31,7 @@ install: openssl version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true; export PATH="$_old_path"; + else sudo apt-get install socat; fi script: From cc6610edc2fe1c4d19aed233ac5449b2124de4ea Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Sep 2017 17:32:40 +0800 Subject: [PATCH 093/278] add socat --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1378e94..b45cc46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,8 @@ addons: install: - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then - brew update && brew install openssl; + brew update && brew install openssl socat; brew info openssl; - brew info socat; 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; From f9cdfd3e5baf89eb84f04a5a912223e2c57c01bb Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Sep 2017 18:58:07 +0800 Subject: [PATCH 094/278] fix for behind proxy --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 84ae7e9..07617c4 100755 --- a/acme.sh +++ b/acme.sh @@ -1981,7 +1981,7 @@ _startserver() { _debug "_NC" "$_NC" #todo listen address - socat TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & + socat TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" } @@ -2061,7 +2061,7 @@ _starttlsserver() { _debug "$__S_OPENSSL" #todo listen address - $__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & + $__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" sleep 1 _debug serverproc "$serverproc" From 6104680caa1c1542b5c2291b8392f5b67b061717 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Sep 2017 20:46:04 +0800 Subject: [PATCH 095/278] fix https://github.com/Neilpang/acme.sh/issues/999 --- acme.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 07617c4..45287ee 100755 --- a/acme.sh +++ b/acme.sh @@ -3934,7 +3934,10 @@ issue() { Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400) _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime" - _on_issue_success "$_post_hook" "$_renew_hook" + if ! _on_issue_success "$_post_hook" "$_renew_hook"; then + _err "Call hook error." + return 1 + fi if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then _savedomainconf "Le_RealCertPath" "$_real_cert" From 4356eefbb10d519cdbd6d44a52b757e3df348075 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Sep 2017 08:42:44 +0800 Subject: [PATCH 096/278] fix https://github.com/Neilpang/acme.sh/issues/1005#event-1232471561 --- acme.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 45287ee..f6447f7 100755 --- a/acme.sh +++ b/acme.sh @@ -4821,9 +4821,11 @@ install() { _debug "Skip install cron job" fi - if ! _precheck "$_nocron"; then - _err "Pre-check failed, can not install." - return 1 + if [ "$IN_CRON" != "1" ]; then + if ! _precheck "$_nocron"; then + _err "Pre-check failed, can not install." + return 1 + fi fi if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then @@ -4876,7 +4878,9 @@ install() { _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" - _installalias "$_c_home" + if [ "$IN_CRON" != "1" ]; then + _installalias "$_c_home" + fi for subf in $_SUB_FOLDERS; do if [ -d "$subf" ]; then From c73fdd402243071d01a23eae7ae17088b5914d7d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 3 Sep 2017 08:45:58 +0800 Subject: [PATCH 097/278] minor --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index f6447f7..e482b87 100755 --- a/acme.sh +++ b/acme.sh @@ -4970,7 +4970,7 @@ _uninstallalias() { } cron() { - IN_CRON=1 + export IN_CRON=1 _initpath _info "$(__green "===Starting cron===")" if [ "$AUTO_UPGRADE" = "1" ]; then From e52304edb47b974c905e88bc41b7124705f514e6 Mon Sep 17 00:00:00 2001 From: Santeri Date: Sun, 3 Sep 2017 13:35:20 +0400 Subject: [PATCH 098/278] cpanel_uapi tried to make it a bit less confusing --- deploy/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index e026cad..62a3fb6 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -6,6 +6,8 @@ Here are the scripts to deploy the certs/key to the server/services. ## 1. Deploy the certs to your cpanel host. +If you want to deploy using cpanel UAPI see 7. + (cpanel deploy hook is not finished yet, this is just an example.) @@ -81,14 +83,13 @@ acme.sh --deploy -d ftp.example.com --deploy-hook exim4 acme.sh --deploy -d ftp.example.com --deploy-hook keychain ``` -## 7. Deploy to cpanel host. +## 7. Deploy to cpanel host using UAPI +This hook is using UAPI and works in cPanel & WHM version 56 or newer. ``` acme.sh --deploy -d example.com --deploy-hook cpanel_uapi ``` - -If you are login as root, please specify the username to deploy cert to: - +DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username. ```sh export DEPLOY_CPANEL_USER=username acme.sh --deploy -d example.com --deploy-hook cpanel_uapi From a098167bdbd145ff3d522b33c4d18f99e8ff09ec Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Mon, 4 Sep 2017 14:07:10 +0200 Subject: [PATCH 099/278] Fix more formatting errors --- deploy/fritzbox.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index 1fe28e8..c86b44a 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -62,7 +62,7 @@ fritzbox_deploy() { _info "Log in to the FRITZ!Box" _fritzbox_challenge="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" - _fritzbox_hash="$(echo -n "${_fritzbox_challenge}-${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" + _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" _fritzbox_sid="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then @@ -73,14 +73,12 @@ fritzbox_deploy() { _info "Generate form POST request" _post_request="$(_mktemp)" _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)" + # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it! + _CERTPASSWORD_= { printf -- "--%s\r\n" "${_post_boundary}" printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}" printf -- "--%s\r\n" "${_post_boundary}" - } >>"${_post_request}" - # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a passwort. But if they ever do, here's the place to use it! - _CERTPASSWORD_= - { printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}" printf -- "--%s\r\n" "${_post_boundary}" printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" From bd8b1a2501a867a373772398b3687ac47341f0f5 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Mon, 4 Sep 2017 14:27:22 +0200 Subject: [PATCH 100/278] Don't use wget directly, but instead use _get and _post. --- deploy/fritzbox.sh | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index c86b44a..e7d01a8 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -27,10 +27,6 @@ fritzbox_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - if ! _exists wget; then - _err "wget not found" - return 1 - fi if ! _exists iconv; then _err "iconv not found" return 1 @@ -60,10 +56,13 @@ fritzbox_deploy() { _saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" _saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" + # Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate + export HTTPS_INSECURE=1 + _info "Log in to the FRITZ!Box" - _fritzbox_challenge="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" + _fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*//' -e 's/<\/Challenge>.*$//')" _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" - _fritzbox_sid="$(wget --no-check-certificate -q -O - "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" + _fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*//' -e 's/<\/SID>.*$//')" if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then _err "Logging in to the FRITZ!Box failed. Please check username, password and URL." @@ -91,10 +90,17 @@ fritzbox_deploy() { } >>"${_post_request}" _info "Upload certificate to the FRITZ!Box" - wget --no-check-certificate -q -O - "${_fritzbox_url}/cgi-bin/firmwarecfg" --header="Content-type: multipart/form-data boundary=${_post_boundary}" --post-file "${_post_request}" | grep SSL - _info "Upload successful" + export _H1="Content-type: multipart/form-data boundary=${_post_boundary}" + _post "$(cat ${_post_request})" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL + + retval=$? + if [ $retval = 0 ] ; then + _info "Upload successful" + else + _err "Upload failed" + fi rm "${_post_request}" - return 0 + return $retval } From 8ee5ede834f2493b9d0e3af9a251ccfbaf4156ec Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Mon, 4 Sep 2017 14:30:40 +0200 Subject: [PATCH 101/278] Fix more formatting errors --- deploy/fritzbox.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index e7d01a8..a6f6684 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -92,10 +92,10 @@ fritzbox_deploy() { _info "Upload certificate to the FRITZ!Box" export _H1="Content-type: multipart/form-data boundary=${_post_boundary}" - _post "$(cat ${_post_request})" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL + _post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL retval=$? - if [ $retval = 0 ] ; then + if [ $retval = 0 ]; then _info "Upload successful" else _err "Upload failed" From 72e1eb88d969dfb26935b1c8070db7685105d6b1 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Mon, 4 Sep 2017 14:40:28 +0200 Subject: [PATCH 102/278] Don't use individual redirects, but do it all in one block. --- deploy/fritzbox.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index a6f6684..dbff368 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -82,9 +82,7 @@ fritzbox_deploy() { printf -- "--%s\r\n" "${_post_boundary}" printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" printf "Content-Type: application/octet-stream\r\n\r\n" - } >>"${_post_request}" - cat "${_ckey}" "${_cfullchain}" >>"${_post_request}" - { + cat "${_ckey}" "${_cfullchain}" printf "\r\n" printf -- "--%s--" "${_post_boundary}" } >>"${_post_request}" From 1e30718df63555700444226ef056f132f8620a1c Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Mon, 4 Sep 2017 14:48:27 +0200 Subject: [PATCH 103/278] Try and work around shellcheck error SC2039: In POSIX sh, printf -%s-- is undefined. --- deploy/fritzbox.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deploy/fritzbox.sh b/deploy/fritzbox.sh index dbff368..943b198 100644 --- a/deploy/fritzbox.sh +++ b/deploy/fritzbox.sh @@ -75,16 +75,20 @@ fritzbox_deploy() { # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it! _CERTPASSWORD_= { - printf -- "--%s\r\n" "${_post_boundary}" + printf -- "--" + printf -- "%s\r\n" "${_post_boundary}" printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}" - printf -- "--%s\r\n" "${_post_boundary}" + printf -- "--" + printf -- "%s\r\n" "${_post_boundary}" printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}" - printf -- "--%s\r\n" "${_post_boundary}" + printf -- "--" + printf -- "%s\r\n" "${_post_boundary}" printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" printf "Content-Type: application/octet-stream\r\n\r\n" cat "${_ckey}" "${_cfullchain}" printf "\r\n" - printf -- "--%s--" "${_post_boundary}" + printf -- "--" + printf -- "%s--" "${_post_boundary}" } >>"${_post_request}" _info "Upload certificate to the FRITZ!Box" From 8148bfeacf43a311551c00473f39c514e5f50d52 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 5 Sep 2017 20:32:14 +0800 Subject: [PATCH 104/278] fix https://github.com/Neilpang/acme.sh/issues/998 --- acme.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index e482b87..d985565 100755 --- a/acme.sh +++ b/acme.sh @@ -1814,7 +1814,12 @@ _send_signed_request() { _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" - if _contains "$response" "JWS has invalid anti-replay nonce"; then + _body="$response" + if [ "$needbase64" ]; then + _body="$(echo "$_body" | _dbase64)" + fi + _debug3 _body "$_body" + if _contains "$_body" "JWS has invalid anti-replay nonce"; then _info "It seems the CA server is busy now, let's wait and retry." _request_retry_times=$(_math "$_request_retry_times" + 1) _sleep 5 From f81d4033fa0e85f4a4cb789e250bb52d3a282e5f Mon Sep 17 00:00:00 2001 From: Santeri Kannisto Date: Tue, 5 Sep 2017 17:37:48 +0400 Subject: [PATCH 105/278] One cert per domain Deploy works only for the first domain --- deploy/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/README.md b/deploy/README.md index 62a3fb6..48caea6 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -94,6 +94,7 @@ DEPLOY_CPANEL_USER is required only if you run the script as root and it should export DEPLOY_CPANEL_USER=username acme.sh --deploy -d example.com --deploy-hook cpanel_uapi ``` +Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue only one certificate per domain. From a9726fde1948c7404e8873e9b6264785081b025a Mon Sep 17 00:00:00 2001 From: Santeri Kannisto Date: Tue, 5 Sep 2017 17:42:17 +0400 Subject: [PATCH 106/278] 1 cert per domain for cpanel_uapi --- deploy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index 48caea6..c80a567 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -94,7 +94,7 @@ DEPLOY_CPANEL_USER is required only if you run the script as root and it should export DEPLOY_CPANEL_USER=username acme.sh --deploy -d example.com --deploy-hook cpanel_uapi ``` -Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue only one certificate per domain. +Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain. From 3eeb090578a371eb5e5ce507adb27a6cf1676ac8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 6 Sep 2017 20:50:31 +0800 Subject: [PATCH 107/278] fix tls mode back to use openssl --- acme.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index d985565..83c06fa 100755 --- a/acme.sh +++ b/acme.sh @@ -2053,7 +2053,12 @@ _starttlsserver() { return 1 fi - __S_OPENSSL="socat" + __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY " + if [ "$opaddr" ]; then + __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" + else + __S_OPENSSL="$__S_OPENSSL -accept $port" + fi _debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V6 "$Le_Listen_V6" @@ -2064,9 +2069,12 @@ _starttlsserver() { fi _debug "$__S_OPENSSL" + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + $__S_OPENSSL -tlsextdebug & + else + $__S_OPENSSL >/dev/null 2>&1 & + fi - #todo listen address - $__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" sleep 1 _debug serverproc "$serverproc" From 53273a15bf54d2b3c48e8351c592f42ee1e49dab Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 6 Sep 2017 21:18:02 +0800 Subject: [PATCH 108/278] use socat for tls mode --- acme.sh | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/acme.sh b/acme.sh index 83c06fa..d985565 100755 --- a/acme.sh +++ b/acme.sh @@ -2053,12 +2053,7 @@ _starttlsserver() { return 1 fi - __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY " - if [ "$opaddr" ]; then - __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" - else - __S_OPENSSL="$__S_OPENSSL -accept $port" - fi + __S_OPENSSL="socat" _debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V6 "$Le_Listen_V6" @@ -2069,12 +2064,9 @@ _starttlsserver() { fi _debug "$__S_OPENSSL" - if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then - $__S_OPENSSL -tlsextdebug & - else - $__S_OPENSSL >/dev/null 2>&1 & - fi + #todo listen address + $__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" sleep 1 _debug serverproc "$serverproc" From 856811bd2e23279fcc44efed041c65e011253eb7 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 6 Sep 2017 17:04:40 +0000 Subject: [PATCH 109/278] Use stable gandi API url The gandi api has changed its url for a more stable one. Although https://dns.beta.gandi.net will continue to work for the foreseable future, this commits updates the url to new official one. --- dnsapi/dns_gandi_livedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index 82ed599..7a21aba 100755 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -11,7 +11,7 @@ # ######## Public functions ##################### -GANDI_LIVEDNS_API="https://dns.beta.gandi.net/api/v5" +GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5" #Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_gandi_livedns_add() { From a6b399286e4dcd387c5193589358d066174d51eb Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 9 Sep 2017 14:15:11 +0800 Subject: [PATCH 110/278] add socat --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7ca042e..e85098e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ RUN apk update -f \ && apk --no-cache add -f \ openssl \ curl \ - netcat-openbsd \ + socat \ && rm -rf /var/cache/apk/* ENV LE_CONFIG_HOME /acme.sh From 36309e6dbc5b074b108c268bb2a652689aae379d Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 11 Sep 2017 21:28:37 +0800 Subject: [PATCH 111/278] minor, fix debug info --- acme.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index d985565..b1a7f68 100755 --- a/acme.sh +++ b/acme.sh @@ -1817,8 +1817,9 @@ _send_signed_request() { _body="$response" if [ "$needbase64" ]; then _body="$(echo "$_body" | _dbase64)" + _debug2 _body "$_body" fi - _debug3 _body "$_body" + if _contains "$_body" "JWS has invalid anti-replay nonce"; then _info "It seems the CA server is busy now, let's wait and retry." _request_retry_times=$(_math "$_request_retry_times" + 1) From 95949b6519b4b7e8de5f8dc83ef900d558caa331 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 11 Sep 2017 21:40:56 +0800 Subject: [PATCH 112/278] minor --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index b1a7f68..e774693 100755 --- a/acme.sh +++ b/acme.sh @@ -1987,7 +1987,7 @@ _startserver() { _debug "_NC" "$_NC" #todo listen address - socat TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & + $_NC TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" } From 2fc0225bc98da5e0a7221c12ea13b20c9cf26ec2 Mon Sep 17 00:00:00 2001 From: Manuel Friedli Date: Tue, 12 Sep 2017 11:35:21 +0200 Subject: [PATCH 113/278] Make command line example consistent with env variable example. --- deploy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index f76c667..af6fc5f 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -108,6 +108,6 @@ $ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this: ```sh -acme.sh --deploy -d fritz.box --deploy-hook fritzbox +acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox ``` From 270ce87582e8749b629033c2448bccf313aeb365 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 23 Sep 2017 22:12:17 +0800 Subject: [PATCH 114/278] add debug info --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index e774693..0f2a436 100755 --- a/acme.sh +++ b/acme.sh @@ -2241,6 +2241,7 @@ _initpath() { fi fi + _debug2 ACME_DIRECTORY "$ACME_DIRECTORY" _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)" _debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST" From acf117584bb92c3c5e06774ef1c5b5fd7c4582cc Mon Sep 17 00:00:00 2001 From: Santeri Kannisto Date: Tue, 26 Sep 2017 07:04:30 +0400 Subject: [PATCH 115/278] #1042 Apparently UAPI does not return any error code, just JSON output that has a string "status: 0" whenever the command fails. --- deploy/cpanel_uapi.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh index 2ba112c..4563b9c 100644 --- a/deploy/cpanel_uapi.sh +++ b/deploy/cpanel_uapi.sh @@ -51,8 +51,8 @@ cpanel_uapi_deploy() { else _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") fi - - if [ $? -ne 0 ]; then + error_response="status: 0" + if test "${_response#*$error_response}" != "$_response"; then _err "Error in deploying certificate:" _err "$_response" return 1 From 5261162fdfbc4cfe458267e822a9f89a4bea7f16 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 26 Sep 2017 19:43:06 +0400 Subject: [PATCH 116/278] Remove stray single-quote Get rid of a single stray erroneous single-quote that is breaking socat when using standalone mode. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 0f2a436..3e63282 100755 --- a/acme.sh +++ b/acme.sh @@ -1987,7 +1987,7 @@ _startserver() { _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 TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK; echo ; echo $content; echo;" & serverproc="$!" } From 754a4a7c8bef3fcaa384be159e1744cdae0399bc Mon Sep 17 00:00:00 2001 From: sahsanu Date: Sat, 30 Sep 2017 20:12:53 +0200 Subject: [PATCH 117/278] Update dns_cloudns.sh Added code to save CLOUDNS_AUTH_ID and CLOUDNS_AUTH_PASSWORD on account.conf file so the id and password for cloudns can be reused. --- dnsapi/dns_cloudns.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index f48a805..14403a7 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -96,6 +96,16 @@ _dns_cloudns_init_check() { return 0 fi + CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}" + CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}" + if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then + CLOUDNS_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" return 1 @@ -113,8 +123,12 @@ _dns_cloudns_init_check() { return 1 fi + #save the api id and password to the account conf file. + _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" + CLOUDNS_INIT_CHECK_COMPLETED=1 - + return 0 } From 6c7da215e7d364fb9d9af3274397c947269b966e Mon Sep 17 00:00:00 2001 From: sahsanu Date: Sun, 1 Oct 2017 10:06:38 +0200 Subject: [PATCH 118/278] Update dns_cloudns.sh --- dnsapi/dns_cloudns.sh | 272 ++++++++++++++++++++---------------------- 1 file changed, 129 insertions(+), 143 deletions(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index 14403a7..2ad77ca 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -11,174 +11,160 @@ CLOUDNS_API="https://api.cloudns.net" #Usage: dns_cloudns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_cloudns_add() { - _info "Using cloudns" - - if ! _dns_cloudns_init_check; then - return 1 - fi - - zone="$(_dns_cloudns_get_zone_name "$1")" - if [ -z "$zone" ]; then - _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." - return 1 - fi - - host="$(echo "$1" | sed "s/\.$zone\$//")" - record=$2 - record_id=$(_dns_cloudns_get_record_id "$zone" "$host") - - _debug zone "$zone" - _debug host "$host" - _debug record "$record" - _debug record_id "$record_id" - - if [ -z "$record_id" ]; then - _info "Adding the TXT record for $1" - _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "Record cannot be added." - return 1 - fi - _info "Added." - else - _info "Updating the TXT record for $1" - _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "The TXT record for $1 cannot be updated." - return 1 - fi - _info "Updated." - fi - - return 0 + _info "Using cloudns" + + if ! _dns_cloudns_init_check; then + return 1 + fi + + zone="$(_dns_cloudns_get_zone_name "$1")" + if [ -z "$zone" ]; then + _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." + return 1 + fi + + host="$(echo "$1" | sed "s/\.$zone\$//")" + record=$2 + record_id=$(_dns_cloudns_get_record_id "$zone" "$host") + + _debug zone "$zone" + _debug host "$host" + _debug record "$record" + _debug record_id "$record_id" + + if [ -z "$record_id" ]; then + _info "Adding the TXT record for $1" + _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "Record cannot be added." + return 1 + fi + _info "Added." + else + _info "Updating the TXT record for $1" + _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "The TXT record for $1 cannot be updated." + return 1 + fi + _info "Updated." + fi + + return 0 } #Usage: dns_cloudns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_cloudns_rm() { - _info "Using cloudns" - - if ! _dns_cloudns_init_check; then - return 1 - fi - - if [ -z "$zone" ]; then - zone="$(_dns_cloudns_get_zone_name "$1")" - if [ -z "$zone" ]; then - _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." - return 1 - fi - fi - - host="$(echo "$1" | sed "s/\.$zone\$//")" - record=$2 - record_id=$(_dns_cloudns_get_record_id "$zone" "$host") - - _debug zone "$zone" - _debug host "$host" - _debug record "$record" - _debug record_id "$record_id" - - if [ ! -z "$record_id" ]; then - _info "Deleting the TXT record for $1" - _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "The TXT record for $1 cannot be deleted." - return 1 - fi - _info "Deleted." - fi - return 0 + _info "Using cloudns" + + if ! _dns_cloudns_init_check; then + return 1 + fi + + if [ -z "$zone" ]; then + zone="$(_dns_cloudns_get_zone_name "$1")" + if [ -z "$zone" ]; then + _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." + return 1 + fi + fi + + host="$(echo "$1" | sed "s/\.$zone\$//")" + record=$2 + record_id=$(_dns_cloudns_get_record_id "$zone" "$host") + + _debug zone "$zone" + _debug host "$host" + _debug record "$record" + _debug record_id "$record_id" + + if [ ! -z "$record_id" ]; then + _info "Deleting the TXT record for $1" + _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "The TXT record for $1 cannot be deleted." + return 1 + fi + _info "Deleted." + fi + return 0 } #################### Private functions below ################################## _dns_cloudns_init_check() { - if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then - return 0 - fi - - CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}" - CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}" - if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then - CLOUDNS_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" - return 1 - fi - - if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then - _err "CLOUDNS_AUTH_PASSWORD is not configured" - return 1 - fi - - _dns_cloudns_http_api_call "dns/login.json" "" - - if ! _contains "$response" "\"status\":\"Success\""; then - _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials." - return 1 - fi - - #save the api id and password to the account conf file. - _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" - _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" - - CLOUDNS_INIT_CHECK_COMPLETED=1 - - return 0 + if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then + return 0 + fi + + if [ -z "$CLOUDNS_AUTH_ID" ]; then + _err "CLOUDNS_AUTH_ID is not configured" + return 1 + fi + + if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then + _err "CLOUDNS_AUTH_PASSWORD is not configured" + return 1 + fi + + _dns_cloudns_http_api_call "dns/login.json" "" + + if ! _contains "$response" "\"status\":\"Success\""; then + _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials." + return 1 + fi + + CLOUDNS_INIT_CHECK_COMPLETED=1 + + return 0 } _dns_cloudns_get_zone_name() { - i=2 - while true; do - zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100) + i=2 + while true; do + zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100) - if [ -z "$zoneForCheck" ]; then - return 1 - fi + if [ -z "$zoneForCheck" ]; then + return 1 + fi - _debug zoneForCheck "$zoneForCheck" + _debug zoneForCheck "$zoneForCheck" - _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck" + _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck" - if ! _contains "$response" "\"status\":\"Failed\""; then - echo "$zoneForCheck" - return 0 - fi + if ! _contains "$response" "\"status\":\"Failed\""; then + echo "$zoneForCheck" + return 0 + fi - i=$(_math "$i" + 1) - done - return 1 + i=$(_math "$i" + 1) + done + return 1 } _dns_cloudns_get_record_id() { - _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" - if _contains "$response" "\"id\":"; then - echo "$response" | cut -d '"' -f 2 - return 0 - fi - return 1 + _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" + if _contains "$response" "\"id\":"; then + echo "$response" | cut -d '"' -f 2 + return 0 + fi + return 1 } _dns_cloudns_http_api_call() { - method=$1 + method=$1 - _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" - _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" + _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" - if [ -z "$2" ]; then - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" - else - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" - fi + if [ -z "$2" ]; then + data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" + else + data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" + fi - response="$(_get "$CLOUDNS_API/$method?$data")" + response="$(_get "$CLOUDNS_API/$method?$data")" - _debug2 response "$response" + _debug2 response "$response" - return 0 + return 0 } From c73c33f94c4d5731ef1d9bee92a33ab71dea1f92 Mon Sep 17 00:00:00 2001 From: sahsanu Date: Sun, 1 Oct 2017 10:31:38 +0200 Subject: [PATCH 119/278] Update dns_cloudns.sh --- dnsapi/dns_cloudns.sh | 272 ++++++++++++++++++++++-------------------- 1 file changed, 143 insertions(+), 129 deletions(-) diff --git a/dnsapi/dns_cloudns.sh b/dnsapi/dns_cloudns.sh index 2ad77ca..b1861b2 100755 --- a/dnsapi/dns_cloudns.sh +++ b/dnsapi/dns_cloudns.sh @@ -11,160 +11,174 @@ CLOUDNS_API="https://api.cloudns.net" #Usage: dns_cloudns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_cloudns_add() { - _info "Using cloudns" - - if ! _dns_cloudns_init_check; then - return 1 - fi - - zone="$(_dns_cloudns_get_zone_name "$1")" - if [ -z "$zone" ]; then - _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." - return 1 - fi - - host="$(echo "$1" | sed "s/\.$zone\$//")" - record=$2 - record_id=$(_dns_cloudns_get_record_id "$zone" "$host") - - _debug zone "$zone" - _debug host "$host" - _debug record "$record" - _debug record_id "$record_id" - - if [ -z "$record_id" ]; then - _info "Adding the TXT record for $1" - _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "Record cannot be added." - return 1 - fi - _info "Added." - else - _info "Updating the TXT record for $1" - _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "The TXT record for $1 cannot be updated." - return 1 - fi - _info "Updated." - fi - - return 0 + _info "Using cloudns" + + if ! _dns_cloudns_init_check; then + return 1 + fi + + zone="$(_dns_cloudns_get_zone_name "$1")" + if [ -z "$zone" ]; then + _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." + return 1 + fi + + host="$(echo "$1" | sed "s/\.$zone\$//")" + record=$2 + record_id=$(_dns_cloudns_get_record_id "$zone" "$host") + + _debug zone "$zone" + _debug host "$host" + _debug record "$record" + _debug record_id "$record_id" + + if [ -z "$record_id" ]; then + _info "Adding the TXT record for $1" + _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "Record cannot be added." + return 1 + fi + _info "Added." + else + _info "Updating the TXT record for $1" + _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "The TXT record for $1 cannot be updated." + return 1 + fi + _info "Updated." + fi + + return 0 } #Usage: dns_cloudns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_cloudns_rm() { - _info "Using cloudns" - - if ! _dns_cloudns_init_check; then - return 1 - fi - - if [ -z "$zone" ]; then - zone="$(_dns_cloudns_get_zone_name "$1")" - if [ -z "$zone" ]; then - _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." - return 1 - fi - fi - - host="$(echo "$1" | sed "s/\.$zone\$//")" - record=$2 - record_id=$(_dns_cloudns_get_record_id "$zone" "$host") - - _debug zone "$zone" - _debug host "$host" - _debug record "$record" - _debug record_id "$record_id" - - if [ ! -z "$record_id" ]; then - _info "Deleting the TXT record for $1" - _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" - if ! _contains "$response" "\"status\":\"Success\""; then - _err "The TXT record for $1 cannot be deleted." - return 1 - fi - _info "Deleted." - fi - return 0 + _info "Using cloudns" + + if ! _dns_cloudns_init_check; then + return 1 + fi + + if [ -z "$zone" ]; then + zone="$(_dns_cloudns_get_zone_name "$1")" + if [ -z "$zone" ]; then + _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." + return 1 + fi + fi + + host="$(echo "$1" | sed "s/\.$zone\$//")" + record=$2 + record_id=$(_dns_cloudns_get_record_id "$zone" "$host") + + _debug zone "$zone" + _debug host "$host" + _debug record "$record" + _debug record_id "$record_id" + + if [ ! -z "$record_id" ]; then + _info "Deleting the TXT record for $1" + _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" + if ! _contains "$response" "\"status\":\"Success\""; then + _err "The TXT record for $1 cannot be deleted." + return 1 + fi + _info "Deleted." + fi + return 0 } #################### Private functions below ################################## _dns_cloudns_init_check() { - if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then - return 0 - fi - - if [ -z "$CLOUDNS_AUTH_ID" ]; then - _err "CLOUDNS_AUTH_ID is not configured" - return 1 - fi - - if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then - _err "CLOUDNS_AUTH_PASSWORD is not configured" - return 1 - fi - - _dns_cloudns_http_api_call "dns/login.json" "" - - if ! _contains "$response" "\"status\":\"Success\""; then - _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials." - return 1 - fi - - CLOUDNS_INIT_CHECK_COMPLETED=1 - - return 0 + if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then + return 0 + fi + + CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}" + CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}" + if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then + CLOUDNS_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" + return 1 + fi + + if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then + _err "CLOUDNS_AUTH_PASSWORD is not configured" + return 1 + fi + + _dns_cloudns_http_api_call "dns/login.json" "" + + if ! _contains "$response" "\"status\":\"Success\""; then + _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials." + return 1 + fi + + #save the api id and password to the account conf file. + _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" + + CLOUDNS_INIT_CHECK_COMPLETED=1 + + return 0 } _dns_cloudns_get_zone_name() { - i=2 - while true; do - zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100) + i=2 + while true; do + zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100) - if [ -z "$zoneForCheck" ]; then - return 1 - fi + if [ -z "$zoneForCheck" ]; then + return 1 + fi - _debug zoneForCheck "$zoneForCheck" + _debug zoneForCheck "$zoneForCheck" - _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck" + _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck" - if ! _contains "$response" "\"status\":\"Failed\""; then - echo "$zoneForCheck" - return 0 - fi + if ! _contains "$response" "\"status\":\"Failed\""; then + echo "$zoneForCheck" + return 0 + fi - i=$(_math "$i" + 1) - done - return 1 + i=$(_math "$i" + 1) + done + return 1 } _dns_cloudns_get_record_id() { - _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" - if _contains "$response" "\"id\":"; then - echo "$response" | cut -d '"' -f 2 - return 0 - fi - return 1 + _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" + if _contains "$response" "\"id\":"; then + echo "$response" | cut -d '"' -f 2 + return 0 + fi + return 1 } _dns_cloudns_http_api_call() { - method=$1 + method=$1 - _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" - _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" + _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" + _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" - if [ -z "$2" ]; then - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" - else - data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" - fi + if [ -z "$2" ]; then + data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" + else + data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" + fi - response="$(_get "$CLOUDNS_API/$method?$data")" + response="$(_get "$CLOUDNS_API/$method?$data")" - _debug2 response "$response" + _debug2 response "$response" - return 0 + return 0 } From 641a2895a6282472fe3c8d52e5289165bf8a3d7d Mon Sep 17 00:00:00 2001 From: hiska Date: Mon, 2 Oct 2017 08:32:36 +0900 Subject: [PATCH 120/278] Create strongswan.sh --- deploy/strongswan.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 deploy/strongswan.sh diff --git a/deploy/strongswan.sh b/deploy/strongswan.sh new file mode 100644 index 0000000..7323278 --- /dev/null +++ b/deploy/strongswan.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env sh + +#Here is a sample custom api script. +#This file name is "myapi.sh" +#So, here must be a method myapi_deploy() +#Which will be called by acme.sh to deploy the cert +#returns 0 means success, otherwise error. + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +strongswan_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _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")" + + ipsec reload + + return 0 + +} From afe3283c53930c66017aadbb8e35d5f01b3c714e Mon Sep 17 00:00:00 2001 From: hiska Date: Mon, 2 Oct 2017 08:34:32 +0900 Subject: [PATCH 121/278] Update README.md --- deploy/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deploy/README.md b/deploy/README.md index c80a567..3105357 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -97,6 +97,10 @@ acme.sh --deploy -d example.com --deploy-hook cpanel_uapi Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain. +## 8. Deploy the cert to strongswan +```sh +acme.sh --deploy -d ftp.example.com --deploy-hook strongswan +``` From 372f691fd69a191cb2d718ace2d6f6a16568514e Mon Sep 17 00:00:00 2001 From: hebbet Date: Mon, 2 Oct 2017 15:04:02 +0200 Subject: [PATCH 122/278] unify headlines unify headlines in deploy readme --- deploy/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index af6fc5f..4e4a726 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -4,7 +4,7 @@ Before you can deploy your cert, you must [issue the cert first](https://github. Here are the scripts to deploy the certs/key to the server/services. -## 1. Deploy the certs to your cpanel host. +## 1. Deploy the certs to your cpanel host If you want to deploy using cpanel UAPI see 7. @@ -20,7 +20,7 @@ export DEPLOY_CPANEL_PASSWORD=PASSWORD acme.sh --deploy -d example.com --deploy-hook cpanel ``` -## 2. Deploy ssl cert on kong proxy engine based on api. +## 2. Deploy ssl cert on kong proxy engine based on api Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). Currently supports Kong-v0.10.x. @@ -29,11 +29,11 @@ Currently supports Kong-v0.10.x. acme.sh --deploy -d ftp.example.com --deploy-hook kong ``` -## 3. Deploy the cert to remote server through SSH access. +## 3. Deploy the cert to remote server through SSH access (TODO) -## 4. Deploy the cert to local vsftpd server. +## 4. Deploy the cert to local vsftpd server ```sh acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd @@ -55,7 +55,7 @@ export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd ``` -## 5. Deploy the cert to local exim4 server. +## 5. Deploy the cert to local exim4 server ```sh acme.sh --deploy -d ftp.example.com --deploy-hook exim4 From c924e7c537249a33713e8dc6691ae3311e0b7a23 Mon Sep 17 00:00:00 2001 From: hiska Date: Wed, 4 Oct 2017 06:44:02 +0900 Subject: [PATCH 123/278] remove "return 0" --- deploy/strongswan.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/deploy/strongswan.sh b/deploy/strongswan.sh index 7323278..2de18f8 100644 --- a/deploy/strongswan.sh +++ b/deploy/strongswan.sh @@ -29,6 +29,4 @@ strongswan_deploy() { ipsec reload - return 0 - } From ee56b9cd4e50da0ff36af392676074d3914f12d7 Mon Sep 17 00:00:00 2001 From: sahsanu Date: Sat, 7 Oct 2017 21:31:24 +0200 Subject: [PATCH 124/278] Update ClouDNS.net API doc Update ClouDNS.net API doc to show that CLOUDNS_AUTH_ID and CLOUDNS_AUTH_PASSWORD will be saved in ~/.acme.sh/account.conf --- dnsapi/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index 34b3867..ce8bbfb 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -420,6 +420,7 @@ Ok, let's issue a cert now: ``` acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com ``` +The `CLOUDNS_AUTH_ID` and `CLOUDNS_AUTH_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. ## 22. Use Infoblox API From 5f6e3da766ef6ffcefca8d5ced3df4b2fcdd7a62 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 8 Oct 2017 19:45:50 +0800 Subject: [PATCH 125/278] fix https://github.com/Neilpang/acme.sh/issues/1062 change back to use openssl for tls mode. --- acme.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 3e63282..9b917f7 100755 --- a/acme.sh +++ b/acme.sh @@ -2054,7 +2054,12 @@ _starttlsserver() { return 1 fi - __S_OPENSSL="socat" + __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY " + if [ "$opaddr" ]; then + __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" + else + __S_OPENSSL="$__S_OPENSSL -accept $port" + fi _debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V6 "$Le_Listen_V6" @@ -2065,9 +2070,12 @@ _starttlsserver() { fi _debug "$__S_OPENSSL" + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + $__S_OPENSSL -tlsextdebug & + else + $__S_OPENSSL >/dev/null 2>&1 & + fi - #todo listen address - $__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" & serverproc="$!" sleep 1 _debug serverproc "$serverproc" From 872bfe4757a9593e1a3a6f8e9969ad465ef66beb Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 11 Oct 2017 20:34:56 +0800 Subject: [PATCH 126/278] fix for PR https://github.com/Neilpang/acme.sh/pull/1069 --- dnsapi/dns_aws.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 4078257..5a71651 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -87,6 +87,7 @@ _get_root() { _debug "response" "$response" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 "Checking domain: $h" if [ -z "$h" ]; then if _contains "$response" "true" && _contains "$response" ""; then _debug "IsTruncated" @@ -102,23 +103,23 @@ _get_root() { fi fi #not valid + _err "Invalid domain" return 1 fi if _contains "$response" "$h."; then hostedzone="$(echo "$response" | sed 's//#&/g' | tr '#' '\n' | _egrep_o "[^<]*<.Id>$h.<.Name>.*false<.PrivateZone>.*<.HostedZone>")" _debug hostedzone "$hostedzone" - if [ -z "$hostedzone" ]; then - _err "Error, can not get hostedzone." + if [ "$hostedzone" ]; then + _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o ".*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>") + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + _err "Can not find domain id: $h" return 1 fi - _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o ".*<.Id>" | head -n 1 | _egrep_o ">.*<" | 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) From 352dd907ac0d90ed5cecf4a9553a59d25ca4ac3d Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 18 Oct 2017 20:27:09 +0800 Subject: [PATCH 127/278] fix https://github.com/Neilpang/acme.sh/issues/1074 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 9b917f7..d988eb0 100755 --- a/acme.sh +++ b/acme.sh @@ -3135,7 +3135,7 @@ _regAccount() { 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" From 7902d10a3aaeff25bd2225e5c2ffd1af1f8e834b Mon Sep 17 00:00:00 2001 From: max2711 <32955673+max2711@users.noreply.github.com> Date: Fri, 20 Oct 2017 14:22:20 +0200 Subject: [PATCH 128/278] remove unused crontab jobs --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e85098e..97626ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ ADD ./ /install_acme.sh/ RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/ -RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | sed 's#> /dev/null##' | crontab - +RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab - RUN for verb in help \ version \ From 4c99c0127b849a8db29151a10b78acb66f78ebdd Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 1 Nov 2017 10:14:44 +0800 Subject: [PATCH 129/278] add dev guide --- dnsapi/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/README.md b/dnsapi/README.md index ce8bbfb..cff59c7 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -618,6 +618,7 @@ acme.sh --issue --dns dns_myapi -d example.com -d www.example.com For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh) +See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API From 212d0f24d8d264c30969d5f19bb61c5840ae5108 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Tue, 31 Oct 2017 22:35:15 -0700 Subject: [PATCH 130/278] [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 6e93ff8bcac9744ff451181b6feeb7269d2ce1a8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 10 Nov 2017 23:01:29 +0800 Subject: [PATCH 131/278] fix https://github.com/Neilpang/acme.sh/issues/1106 --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index d988eb0..9c8d6d4 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.7.4 +VER=2.7.5 PROJECT_NAME="acme.sh" @@ -3474,7 +3474,7 @@ 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 2,3 | tr -d '"')" + uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)" _debug uri "$uri" keyauthorization="$token.$thumbprint" From bab4f691c5bade8a50a88ef715b834c8ec18cb1e Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 12 Nov 2017 18:38:30 -0800 Subject: [PATCH 132/278] 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 133/278] 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 134/278] 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 135/278] 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 136/278] 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 137/278] 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 138/278] 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 139/278] 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 140/278] 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 141/278] 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 142/278] 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 143/278] 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 144/278] 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 145/278] 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 146/278] 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 147/278] 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 148/278] 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 149/278] 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 150/278] 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 151/278] 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 152/278] 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 153/278] 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 154/278] 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 155/278] 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 156/278] 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 157/278] 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 158/278] 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 159/278] 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 160/278] 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 161/278] 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 162/278] 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 163/278] 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 164/278] 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 165/278] 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 166/278] 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 167/278] 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 168/278] 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 169/278] 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 170/278] 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 171/278] 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 172/278] 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 173/278] 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 174/278] 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 175/278] 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 176/278] 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 177/278] 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 178/278] 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 179/278] 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 180/278] 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 181/278] 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 182/278] 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 183/278] 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 184/278] 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 185/278] 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 186/278] 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 187/278] 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 188/278] 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 189/278] 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 190/278] 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 191/278] 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 192/278] 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 193/278] 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 194/278] 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 195/278] 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 196/278] 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 197/278] 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 198/278] 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 199/278] 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 200/278] 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 201/278] 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 202/278] 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 203/278] 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 204/278] 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 205/278] 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 206/278] 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 207/278] 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 208/278] 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 209/278] 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 210/278] 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 211/278] 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 212/278] 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 213/278] 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 214/278] 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 215/278] 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 216/278] 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 217/278] 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 218/278] 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 219/278] 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 220/278] 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 221/278] 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 222/278] 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 223/278] 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 224/278] 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 225/278] 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 226/278] 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 227/278] 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 228/278] 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 229/278] 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 230/278] 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 231/278] 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 232/278] 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 233/278] 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 234/278] 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 235/278] 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 236/278] 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 237/278] 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 238/278] 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 239/278] 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 240/278] 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 241/278] 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 242/278] 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 243/278] 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 244/278] 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 245/278] 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 246/278] 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 247/278] 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 248/278] 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 249/278] 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 250/278] 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 251/278] 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 252/278] 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 253/278] 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 254/278] 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 255/278] 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 256/278] 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 257/278] 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 258/278] 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 259/278] 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 260/278] 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 261/278] 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 262/278] 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 263/278] 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 264/278] 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 265/278] 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 d5865989cfb0a424362b364f04d5f88a5b264e35 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 10:48:25 +0800 Subject: [PATCH 266/278] 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 267/278] 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 268/278] 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 6d6b2efdb5131e77a1c4efa08f857b29013ae1d3 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 16 Feb 2018 23:16:25 +0800 Subject: [PATCH 269/278] 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 270/278] 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 271/278] 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 272/278] 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 273/278] 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 274/278] 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 275/278] 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 276/278] 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 bae50da799e73b8069393248824da3de14137e72 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 21 Feb 2018 09:45:36 +0800 Subject: [PATCH 277/278] 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 278/278] 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