From 137f5f8b786cea3bf2dc11aab357680eee884a92 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 10:24:41 +0800 Subject: [PATCH 01/40] minor,add info message. --- le.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/le.sh b/le.sh index 493e4438..6bacce2f 100755 --- a/le.sh +++ b/le.sh @@ -64,6 +64,7 @@ _base64() { #domain [2048] createAccountKey() { + _info "Creating account key" if [ -z "$1" ] ; then echo Usage: $0 account-domain [2048] return @@ -89,6 +90,7 @@ createAccountKey() { #domain length createDomainKey() { + _info "Creating domain key" if [ -z "$1" ] ; then echo Usage: $0 domain [2048] return @@ -120,6 +122,7 @@ createDomainKey() { # domain domainlist createCSR() { + _info "Creating csr" if [ -z "$1" ] ; then echo Usage: $0 domain [domainlist] return From bb2294e76119e454f77cad5321e66502fc532b3b Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 10:39:02 +0800 Subject: [PATCH 02/40] fix issue: do not re-generate domain key when call renew command --- le.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/le.sh b/le.sh index 6bacce2f..f90ebe3e 100755 --- a/le.sh +++ b/le.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER=1.1.1 +VER=1.1.2 PROJECT="https://github.com/Neilpang/le" DEFAULT_CA="https://acme-v01.api.letsencrypt.org" @@ -104,7 +104,10 @@ createDomainKey() { fi _initpath $domain - if [ -f "$CERT_KEY_PATH" ] && ! [ "$FORCE" ] ; then + if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then + #generate account key + openssl genrsa $length > "$CERT_KEY_PATH" + else if [ "$IS_RENEW" ] ; then _info "Domain key exists, skip" return 0 @@ -113,9 +116,6 @@ createDomainKey() { _err "Set FORCE=1, and try again." return 1 fi - else - #generate account key - openssl genrsa $length > "$CERT_KEY_PATH" fi } From 175c9decd7189f15f47f8175f2cad9656a4dddcf Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 21:00:36 +0800 Subject: [PATCH 03/40] init dnsapi --- dnsapi/dns-cf.sh | 139 +++++++++++++++++++++++++++++++++++++++++++++++ le.sh | 61 ++++++++++++++++----- 2 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 dnsapi/dns-cf.sh diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh new file mode 100644 index 00000000..40987a21 --- /dev/null +++ b/dnsapi/dns-cf.sh @@ -0,0 +1,139 @@ +#!/bin/bash + + +# +#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# +#CF_Email="xxxx@sss.com" + + +CF_Api="https://api.cloudflare.com/client/v4/" + +#Usage: _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns-cf-add() { + fulldomain=$1 + txtvalue=$2 + + _info "first detect the root zone" + if ! _get_root $fulldomain > /dev/null ; then + _err "invalid domain" + return 1 + fi + + _cf_rest GET "/zones/$_domain_id/dns_records?type=TXT&name=$fulldomain" + + if [ "$?" != "0" ] || ! printf $response | grep \"success\":true > /dev/null ; then + _err "Error" + return 1 + fi + + count=$(printf $response | grep -o \"count\":[^,]* | cut -d : -f 2) + + if [ "$count" == "0" ] ; then + _info "Adding record" + if _cf_rest GET "/zones/$_domain_id/dns_records?type=TXT&name=$fulldomain&content=$txtvalue" ; then + _info "Added, sleeping 10 seconds" + sleep 10 + return 0 + fi + _err "Add txt record error." + else + _info "Updating record" + record_id=$(printf $response | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") + _info "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, sleeping 10 seconds" + sleep 10 + return 0; + fi + _err "Update error" + return 1 + fi + +} + + +#_acme-challenge.www.domain.com +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=2 + p=1 + while [ '1' ] ; do + h=$(printf $domain | cut -d . -f $i-100) + if [ -z "$h" ] ; then + #not valid + return 1; + fi + + if ! _cf_get "zones?name=$h" ; then + return 1 + fi + + if printf $response | grep \"name\":\"$h\" ; then + _domain_id=$(printf $response | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") + if [ "$_domain_id" ] ; then + _sub_domain=$(printf $domain | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + return 1 + fi + p=$i + let "i+=1" + done + return 1 +} + + +_cf_rest() { + m=$1 + ep="$2" + echo $ep + if [ "$3" ] ; then + data="--data \"$3\"" + fi + response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json" $data)" + if [ "$?" != "0" ] ; then + echo $error $ep + return 1 + fi + echo $response + return 0 +} + + +_debug() { + + if [ -z "$DEBUG" ] ; then + return + fi + + if [ -z "$2" ] ; then + echo $1 + else + echo "$1"="$2" + fi +} + +_info() { + if [ -z "$2" ] ; then + echo "$1" + else + echo "$1"="$2" + fi +} + +_err() { + if [ -z "$2" ] ; then + echo "$1" >&2 + else + echo "$1"="$2" >&2 + fi +} + + diff --git a/le.sh b/le.sh index f90ebe3e..8e70b04f 100755 --- a/le.sh +++ b/le.sh @@ -320,7 +320,6 @@ _initpath() { if [ -z "$CA_CERT_PATH" ] ; then CA_CERT_PATH="$WORKING_DIR/$domain/ca.cer" fi - } @@ -619,12 +618,44 @@ issue() { _debug txt "$txt" #dns #1. check use api - _err "Add the following TXT record:" - _err "Domain: $txtdomain" - _err "TXT value: $txt" - _err "Please be aware that you prepend _acme-challenge. before your domain" - _err "so the resulting subdomain will be: $txtdomain" - #dnsadded='1' + d_api="" + if [ -f "$WORKING_DIR/$d/$Le_Webroot" ] ; then + d_api="$WORKING_DIR/$d/$Le_Webroot" + elif [ -f "$WORKING_DIR/$d/$Le_Webroot.sh" ] ; then + d_api="$WORKING_DIR/$d/$Le_Webroot.sh" + elif [ -f "$WORKING_DIR/$Le_Webroot" ] ; then + d_api="$WORKING_DIR/$Le_Webroot" + elif [ -f "$WORKING_DIR/$Le_Webroot.sh" ] ; then + d_api="$WORKING_DIR/$Le_Webroot.sh" + fi + + if [ "$d_api" ]; then + _info "Found domain api file: $d_api" + else + _err "Add the following TXT record:" + _err "Domain: $txtdomain" + _err "TXT value: $txt" + _err "Please be aware that you prepend _acme-challenge. before your domain" + _err "so the resulting subdomain will be: $txtdomain" + continue + fi + + if ! source $d_api ; then + _err "Load file $d_api error. Please check your api file and try again." + return 1 + fi + + addcommand="$Le_Webroot-add" + if ! command -v $addcommand ; then + _err "It seems that your api file is not correct, it must have a function named: $Le_Webroot" + return 1 + fi + + if ! $addcommand $txtdomain $txt ; then + _err "Error add txt for domain:$txtdomain" + return 1 + fi + dnsadded='1' fi done @@ -806,13 +837,17 @@ renew() { _initpath $Le_Domain - if [ -f "$DOMAIN_CONF" ] ; then - source "$DOMAIN_CONF" - if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then - _info "Skip, Next renewal time is: $Le_NextRenewTimeStr" - return 2 - fi + if [ ! -f "$DOMAIN_CONF" ] ; then + _err "$Le_Domain is not a issued domain, skip." + return 1; + fi + + source "$DOMAIN_CONF" + if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then + _info "Skip, Next renewal time is: $Le_NextRenewTimeStr" + return 2 fi + IS_RENEW="1" issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" IS_RENEW="" From 23bcf2c62371cbe669d1f8e7cec635eb02c15aab Mon Sep 17 00:00:00 2001 From: root Date: Sat, 30 Jan 2016 16:16:29 +0300 Subject: [PATCH 04/40] exec --- dnsapi/dns-cf.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 dnsapi/dns-cf.sh diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh old mode 100644 new mode 100755 From b4a156da60d8a6d08c43185b4beb8696976b57e2 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 21:08:43 +0800 Subject: [PATCH 05/40] install dnsapi --- le.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/le.sh b/le.sh index 8e70b04f..d8a4a81f 100755 --- a/le.sh +++ b/le.sh @@ -1028,6 +1028,8 @@ install() { rm -f $WORKING_DIR/le ln -s $WORKING_DIR/le.sh $WORKING_DIR/le + cp -r dnsapi $WORKING_DIR/dnsapi + installcronjob _info OK From 611f6877392d56945d2037c916d981c7b68654da Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 21:14:41 +0800 Subject: [PATCH 06/40] dnsapi folder --- le.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/le.sh b/le.sh index d8a4a81f..c4437822 100755 --- a/le.sh +++ b/le.sh @@ -627,7 +627,12 @@ issue() { d_api="$WORKING_DIR/$Le_Webroot" elif [ -f "$WORKING_DIR/$Le_Webroot.sh" ] ; then d_api="$WORKING_DIR/$Le_Webroot.sh" + elif [ -f "$WORKING_DIR/dnsapi/$Le_Webroot" ] ; then + d_api="$WORKING_DIR/dnsapi/$Le_Webroot" + elif [ -f "$WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then + d_api="$WORKING_DIR/dnsapi/$Le_Webroot.sh" fi + _debug d_api "$d_api" if [ "$d_api" ]; then _info "Found domain api file: $d_api" From 1b5bd0e03ef7749d3b32f358e815f44dbfc453b0 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 22:11:09 +0800 Subject: [PATCH 07/40] minor fix --- dnsapi/dns-cf.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh index 40987a21..547a077b 100755 --- a/dnsapi/dns-cf.sh +++ b/dnsapi/dns-cf.sh @@ -14,12 +14,13 @@ dns-cf-add() { fulldomain=$1 txtvalue=$2 - _info "first detect the root zone" - if ! _get_root $fulldomain > /dev/null ; then + _debug "First detect the root zone" + if ! _get_root $fulldomain ; then _err "invalid domain" return 1 fi + _debug "Getting txt records" _cf_rest GET "/zones/$_domain_id/dns_records?type=TXT&name=$fulldomain" if [ "$?" != "0" ] || ! printf $response | grep \"success\":true > /dev/null ; then @@ -31,7 +32,7 @@ dns-cf-add() { if [ "$count" == "0" ] ; then _info "Adding record" - if _cf_rest GET "/zones/$_domain_id/dns_records?type=TXT&name=$fulldomain&content=$txtvalue" ; then + if _cf_rest POST "/zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then _info "Added, sleeping 10 seconds" sleep 10 return 0 @@ -56,6 +57,7 @@ dns-cf-add() { #_acme-challenge.www.domain.com +#returns # _sub_domain=_acme-challenge.www # _domain=domain.com # _domain_id=sdjkglgdfewsdfg @@ -70,7 +72,7 @@ _get_root() { return 1; fi - if ! _cf_get "zones?name=$h" ; then + if ! _cf_rest GET "zones?name=$h" ; then return 1 fi @@ -95,7 +97,8 @@ _cf_rest() { ep="$2" echo $ep if [ "$3" ] ; then - data="--data \"$3\"" + data="--data \'$3\'" + _debug data "$data" fi response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json" $data)" if [ "$?" != "0" ] ; then From 638b9a0559d50088db1888b8d97e53e4a7c1786b Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 22:34:35 +0800 Subject: [PATCH 08/40] fix bugs --- dnsapi/dns-cf.sh | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh index 547a077b..ac3643c4 100755 --- a/dnsapi/dns-cf.sh +++ b/dnsapi/dns-cf.sh @@ -9,7 +9,9 @@ CF_Api="https://api.cloudflare.com/client/v4/" -#Usage: _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns-cf-add() { fulldomain=$1 txtvalue=$2 @@ -33,15 +35,20 @@ dns-cf-add() { if [ "$count" == "0" ] ; then _info "Adding record" if _cf_rest POST "/zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - _info "Added, sleeping 10 seconds" - sleep 10 - return 0 + if printf $response | grep $fulldomain > /dev/null ; then + _info "Added, sleeping 10 seconds" + sleep 1 + return 0 + else + _err "Add txt record error." + return 1 + fi fi _err "Add txt record error." else _info "Updating record" record_id=$(printf $response | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") - _info "record_id" $record_id + _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 @@ -56,6 +63,10 @@ dns-cf-add() { } + + + +#################### Private functions bellow ################################## #_acme-challenge.www.domain.com #returns # _sub_domain=_acme-challenge.www @@ -95,17 +106,20 @@ _get_root() { _cf_rest() { m=$1 ep="$2" - echo $ep + _debug $ep if [ "$3" ] ; then - data="--data \'$3\'" + data="$3" _debug data "$data" + response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json" --data $data)" + else + response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json")" fi - response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json" $data)" + if [ "$?" != "0" ] ; then - echo $error $ep + _err "error $ep" return 1 fi - echo $response + _debug response "$response" return 0 } From 0ed4c9391e2956769bdb1be64c8a05df1e312293 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 22:47:22 +0800 Subject: [PATCH 09/40] sleep to wait dns record to take effect. --- dnsapi/dns-cf.sh | 4 +++- le.sh | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh index ac3643c4..888e9b3f 100755 --- a/dnsapi/dns-cf.sh +++ b/dnsapi/dns-cf.sh @@ -37,7 +37,8 @@ dns-cf-add() { if _cf_rest POST "/zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if printf $response | grep $fulldomain > /dev/null ; then _info "Added, sleeping 10 seconds" - sleep 1 + sleep 10 + #todo: check if the record takes effect return 0 else _err "Add txt record error." @@ -54,6 +55,7 @@ dns-cf-add() { if [ "$?" == "0" ]; then _info "Updated, sleeping 10 seconds" sleep 10 + #todo: check if the record takes effect return 0; fi _err "Update error" diff --git a/le.sh b/le.sh index c4437822..a6122418 100755 --- a/le.sh +++ b/le.sh @@ -673,6 +673,10 @@ issue() { fi + if [ "$dnsadded" == '1' ] ; then + _info "Sleep 60 seconds for the txt records to take effect" + sleep 60 + fi _debug "ok, let's start to verify" ventries=$(echo "$vlist" | sed "s/,/ /g") From a28b3a653cdb8fd9cf041365ae6e488d8bbd07ad Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 22:51:36 +0800 Subject: [PATCH 10/40] install dnsapi --- le.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/le.sh b/le.sh index a6122418..768efcf7 100755 --- a/le.sh +++ b/le.sh @@ -1037,7 +1037,8 @@ install() { rm -f $WORKING_DIR/le ln -s $WORKING_DIR/le.sh $WORKING_DIR/le - cp -r dnsapi $WORKING_DIR/dnsapi + mkdir -p $WORKING_DIR/dnsapi + cp dnsapi/* $WORKING_DIR/dnsapi/ installcronjob From ab49796192c752ec10253ae9a6b1bb308f65e97d Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 30 Jan 2016 23:15:30 +0800 Subject: [PATCH 11/40] how to use cloudflare api and create custom api --- README.md | 45 ++++++++++++++++++++++++++++++--- dnsapi/dns-cf.sh | 6 +++++ dnsapi/dns-myapi.sh | 61 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 dnsapi/dns-myapi.sh diff --git a/README.md b/README.md index 7069f040..f3e18b7e 100644 --- a/README.md +++ b/README.md @@ -139,9 +139,6 @@ Support the latest dns-01 challenge. le issue dns aa.com www.aa.com,user.aa.com ``` -Use domain api to automatically add dns record is not finished yet. -So, you must manually add the txt record to finish verifying. - You will get the output like bellow: ``` Add the following txt record: @@ -165,6 +162,48 @@ Ok, it's finished. +# Use CloudFlare domain api to automatically issue cert + +For now, we support clourflare integeration. + +First you need to login to your clourflare account to get you apikey. + +Then open `~/.le/dnsapi/dns-cf.sh`, and fill your api key and email there: +and uncomment the lines: +``` +CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" + +CF_Email="xxxx@sss.com" + +``` + +Ok, let's issue cert now: +``` +le.sh issue dns-cf aa.com www.aa.com +``` + +More api integerations are coming. Godaddy, Dnspod, etc.... + + +# Use custom api + +If your api is not supported yet, you can write your own dns api. + +Let's assume you want to name it 'myapi', + +1. Create a bash script named `~/.le/dns-myapi.sh`, +2. In the scrypt, you must have a function named `dns-myapi-add()`. Which will be called by le.sh to add dns records. +3. Then you can use your api to issue cert like: + +``` +le.sh issue dns-myapi aa.com www.aa.com +``` + +For more details, please check our sample script: `dnsapi/dns-myapi.sh` + + + + #Under the Hood Speak ACME language with bash directly to Let's encrypt. diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh index 888e9b3f..7cb086b8 100755 --- a/dnsapi/dns-cf.sh +++ b/dnsapi/dns-cf.sh @@ -16,6 +16,12 @@ dns-cf-add() { fulldomain=$1 txtvalue=$2 + if [ -z "$CF_Key" ] || [ -z "$CF_Email" ] ; then + _err "You don't specify cloudflare api key and email 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" diff --git a/dnsapi/dns-myapi.sh b/dnsapi/dns-myapi.sh new file mode 100644 index 00000000..463a44c9 --- /dev/null +++ b/dnsapi/dns-myapi.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +#Here is a sample custom api script. +#This file name is "dhs-myapi.sh" +#So, here must be a method dhs-myapi-add() +#Which will be called by le.sh to add the txt record to your api system. +#returns 0 meanst success, otherwise error. + + + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns-myapi-add() { + fulldomain=$1 + txtvalue=$2 + _err "Not implemented!" + return 1; +} + + + + + + + + + +#################### Private functions bellow ################################## + + +_debug() { + + if [ -z "$DEBUG" ] ; then + return + fi + + if [ -z "$2" ] ; then + echo $1 + else + echo "$1"="$2" + fi +} + +_info() { + if [ -z "$2" ] ; then + echo "$1" + else + echo "$1"="$2" + fi +} + +_err() { + if [ -z "$2" ] ; then + echo "$1" >&2 + else + echo "$1"="$2" >&2 + fi +} + + From e9209938cb292203154423a68ce26c0df54058b3 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 31 Jan 2016 16:30:01 +0800 Subject: [PATCH 12/40] support account config file to save dns api key --- dnsapi/dns-cf.sh | 4 ++++ le.sh | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns-cf.sh b/dnsapi/dns-cf.sh index 7cb086b8..b1e4b47d 100755 --- a/dnsapi/dns-cf.sh +++ b/dnsapi/dns-cf.sh @@ -22,6 +22,10 @@ dns-cf-add() { return 1 fi + #save the api key and email to the account conf file. + _saveaccountconf CF_Key "$CF_Key" + _saveaccountconf CF_Email "$CF_Email" + _debug "First detect the root zone" if ! _get_root $fulldomain ; then _err "invalid domain" diff --git a/le.sh b/le.sh index 768efcf7..1646d6f2 100755 --- a/le.sh +++ b/le.sh @@ -242,6 +242,29 @@ _setopt() { _debug "$(grep -H -n "^$__opt$__sep" $__conf)" } +#_savedomainconf key value +#save to domain.conf +_savedomainconf() { + key="$1" + value="$2" + if [ "$DOMAIN_CONF" ] ; then + _setopt $DOMAIN_CONF "$key" "=" "$value" + else + _debug "DOMAIN_CONF is empty, can not save $key=$value" + fi +} + +#_saveaccountconf key value +_saveaccountconf() { + key="$1" + value="$2" + if [ "$ACCOUNT_CONF_PATH" ] ; then + _setopt $ACCOUNT_CONF_PATH "$key" "=" "$value" + else + _debug "ACCOUNT_CONF_PATH is empty, can not save $key=$value" + fi +} + _startserver() { content="$1" _NC="nc -q 1" @@ -296,7 +319,15 @@ _initpath() { mkdir -p "$WORKING_DIR" if [ -z "$ACCOUNT_KEY_PATH" ] ; then - ACCOUNT_KEY_PATH="$WORKING_DIR/account.acc" + ACCOUNT_KEY_PATH="$WORKING_DIR/account.key" + fi + + if [ -z "$ACCOUNT_CONF_PATH" ] ; then + ACCOUNT_CONF_PATH="$WORKING_DIR/account.conf" + fi + + if [ -f "$ACCOUNT_CONF_PATH" ] ; then + source "$ACCOUNT_CONF_PATH" fi if [ -z "$domain" ] ; then From b25d22b05a6e199817e3baf42de31dd2b908b1ee Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 31 Jan 2016 16:38:52 +0800 Subject: [PATCH 13/40] keep compatible --- le.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/le.sh b/le.sh index 1646d6f2..b2d7f1f9 100755 --- a/le.sh +++ b/le.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER=1.1.2 +VER=1.1.3 PROJECT="https://github.com/Neilpang/le" DEFAULT_CA="https://acme-v01.api.letsencrypt.org" @@ -1071,6 +1071,11 @@ install() { mkdir -p $WORKING_DIR/dnsapi cp dnsapi/* $WORKING_DIR/dnsapi/ + #to keep compatible mv the .acc file to .key file + if [ -f "$WORKING_DIR/account.acc" ] ; then + mv "$WORKING_DIR/account.acc" "$WORKING_DIR/account.key" + fi + installcronjob _info OK From a925dd56b7acc3436da41cd55039f0dd7ec0dfdd Mon Sep 17 00:00:00 2001 From: Neil Date: Sun, 31 Jan 2016 16:48:21 +0800 Subject: [PATCH 14/40] new usage of cloudflare api key --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f3e18b7e..dc090f3b 100644 --- a/README.md +++ b/README.md @@ -166,14 +166,12 @@ Ok, it's finished. For now, we support clourflare integeration. -First you need to login to your clourflare account to get you apikey. +First you need to login to your clourflare account to get your api key. -Then open `~/.le/dnsapi/dns-cf.sh`, and fill your api key and email there: -and uncomment the lines: ``` -CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" -CF_Email="xxxx@sss.com" +export CF_Email="xxxx@sss.com" ``` @@ -182,6 +180,9 @@ Ok, let's issue cert now: le.sh issue dns-cf aa.com www.aa.com ``` +The `CF_Key` and `CF_Email` will be saved in `~/.le/account.conf`, when next time you use cloudflare api, it will reuse this key. + + More api integerations are coming. Godaddy, Dnspod, etc.... From 525997ee045ac2f75634bbe2396a3ecce0299183 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 31 Jan 2016 17:15:02 +0800 Subject: [PATCH 15/40] do not write /usr/loca/bin/ anymore. install to home. --- le.sh | 164 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 63 deletions(-) diff --git a/le.sh b/le.sh index b2d7f1f9..2bb78f0c 100755 --- a/le.sh +++ b/le.sh @@ -163,8 +163,8 @@ _send_signed_request() { _debug url $url _debug payload "$payload" - CURL_HEADER="$WORKING_DIR/curl.header" - dp="$WORKING_DIR/curl.dump" + CURL_HEADER="$LE_WORKING_DIR/curl.header" + dp="$LE_WORKING_DIR/curl.dump" CURL="curl --silent --dump-header $CURL_HEADER " if [ "$DEBUG" ] ; then CURL="$CURL --trace-ascii $dp " @@ -303,8 +303,8 @@ _initpath() { fi fi - if [ -z "$WORKING_DIR" ]; then - WORKING_DIR=$HOME/.le + if [ -z "$LE_WORKING_DIR" ]; then + LE_WORKING_DIR=$HOME/.le fi if [ -z "$ACME_DIR" ] ; then @@ -312,18 +312,18 @@ _initpath() { fi if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then - APACHE_CONF_BACKUP_DIR="$WORKING_DIR/" + APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/" fi domain="$1" - mkdir -p "$WORKING_DIR" + mkdir -p "$LE_WORKING_DIR" if [ -z "$ACCOUNT_KEY_PATH" ] ; then - ACCOUNT_KEY_PATH="$WORKING_DIR/account.key" + ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key" fi if [ -z "$ACCOUNT_CONF_PATH" ] ; then - ACCOUNT_CONF_PATH="$WORKING_DIR/account.conf" + ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf" fi if [ -f "$ACCOUNT_CONF_PATH" ] ; then @@ -334,22 +334,22 @@ _initpath() { return 0 fi - mkdir -p "$WORKING_DIR/$domain" + mkdir -p "$LE_WORKING_DIR/$domain" if [ -z "$DOMAIN_CONF" ] ; then - DOMAIN_CONF="$WORKING_DIR/$domain/$Le_Domain.conf" + DOMAIN_CONF="$LE_WORKING_DIR/$domain/$Le_Domain.conf" fi if [ -z "$CSR_PATH" ] ; then - CSR_PATH="$WORKING_DIR/$domain/$domain.csr" + CSR_PATH="$LE_WORKING_DIR/$domain/$domain.csr" fi if [ -z "$CERT_KEY_PATH" ] ; then - CERT_KEY_PATH="$WORKING_DIR/$domain/$domain.key" + CERT_KEY_PATH="$LE_WORKING_DIR/$domain/$domain.key" fi if [ -z "$CERT_PATH" ] ; then - CERT_PATH="$WORKING_DIR/$domain/$domain.cer" + CERT_PATH="$LE_WORKING_DIR/$domain/$domain.cer" fi if [ -z "$CA_CERT_PATH" ] ; then - CA_CERT_PATH="$WORKING_DIR/$domain/ca.cer" + CA_CERT_PATH="$LE_WORKING_DIR/$domain/ca.cer" fi } @@ -584,7 +584,7 @@ issue() { if [ "$code" == "" ] || [ "$code" == '201' ] ; then _info "Registered" - echo $response > $WORKING_DIR/account.json + echo $response > $LE_WORKING_DIR/account.json elif [ "$code" == '409' ] ; then _info "Already registered" else @@ -650,18 +650,18 @@ issue() { #dns #1. check use api d_api="" - if [ -f "$WORKING_DIR/$d/$Le_Webroot" ] ; then - d_api="$WORKING_DIR/$d/$Le_Webroot" - elif [ -f "$WORKING_DIR/$d/$Le_Webroot.sh" ] ; then - d_api="$WORKING_DIR/$d/$Le_Webroot.sh" - elif [ -f "$WORKING_DIR/$Le_Webroot" ] ; then - d_api="$WORKING_DIR/$Le_Webroot" - elif [ -f "$WORKING_DIR/$Le_Webroot.sh" ] ; then - d_api="$WORKING_DIR/$Le_Webroot.sh" - elif [ -f "$WORKING_DIR/dnsapi/$Le_Webroot" ] ; then - d_api="$WORKING_DIR/dnsapi/$Le_Webroot" - elif [ -f "$WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then - d_api="$WORKING_DIR/dnsapi/$Le_Webroot.sh" + if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then + d_api="$LE_WORKING_DIR/$d/$Le_Webroot" + elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then + d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh" + elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then + d_api="$LE_WORKING_DIR/$Le_Webroot" + elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then + d_api="$LE_WORKING_DIR/$Le_Webroot.sh" + elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then + d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot" + elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then + d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" fi _debug d_api "$d_api" @@ -897,7 +897,7 @@ renewAll() { _initpath _info "renewAll" - for d in $(ls -F $WORKING_DIR | grep '/$') ; do + for d in $(ls -F $LE_WORKING_DIR | grep '/$') ; do d=$(echo $d | cut -d '/' -f 1) _info "renew $d" @@ -992,13 +992,13 @@ installcronjob() { _initpath _info "Installing cron job" if ! crontab -l | grep 'le.sh cron' ; then - if [ -f "$WORKING_DIR/le.sh" ] ; then - lesh="\"$WORKING_DIR\"/le.sh" + if [ -f "$LE_WORKING_DIR/le.sh" ] ; then + lesh="\"$LE_WORKING_DIR\"/le.sh" else _err "Can not install cronjob, le.sh not found." return 1 fi - crontab -l | { cat; echo "0 0 * * * $SUDO WORKING_DIR=\"$WORKING_DIR\" $lesh cron > /dev/null"; } | crontab - + crontab -l | { cat; echo "0 0 * * * $SUDO LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab - fi return 0 } @@ -1008,13 +1008,53 @@ uninstallcronjob() { cr="$(crontab -l | grep 'le.sh cron')" if [ "$cr" ] ; then crontab -l | sed "/le.sh cron/d" | crontab - - WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 7 | cut -d '=' -f 2 | tr -d '"')" - _info WORKING_DIR "$WORKING_DIR" + LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 7 | cut -d '=' -f 2 | tr -d '"')" + _info LE_WORKING_DIR "$LE_WORKING_DIR" fi _initpath } + +# Detect profile file if not specified as environment variable +_detect_profile() { + if [ -n "$PROFILE" -a -f "$PROFILE" ]; then + echo "$PROFILE" + return + fi + + local DETECTED_PROFILE + DETECTED_PROFILE='' + local SHELLTYPE + SHELLTYPE="$(basename "/$SHELL")" + + if [ "$SHELLTYPE" = "bash" ]; then + if [ -f "$HOME/.bashrc" ]; then + DETECTED_PROFILE="$HOME/.bashrc" + elif [ -f "$HOME/.bash_profile" ]; then + DETECTED_PROFILE="$HOME/.bash_profile" + fi + elif [ "$SHELLTYPE" = "zsh" ]; then + DETECTED_PROFILE="$HOME/.zshrc" + fi + + if [ -z "$DETECTED_PROFILE" ]; then + if [ -f "$HOME/.profile" ]; then + DETECTED_PROFILE="$HOME/.profile" + elif [ -f "$HOME/.bashrc" ]; then + DETECTED_PROFILE="$HOME/.bashrc" + elif [ -f "$HOME/.bash_profile" ]; then + DETECTED_PROFILE="$HOME/.bash_profile" + elif [ -f "$HOME/.zshrc" ]; then + DETECTED_PROFILE="$HOME/.zshrc" + fi + fi + + if [ ! -z "$DETECTED_PROFILE" ]; then + echo "$DETECTED_PROFILE" + fi +} + install() { _initpath @@ -1047,33 +1087,33 @@ install() { return 1 fi - _info "Installing to $WORKING_DIR" + _info "Installing to $LE_WORKING_DIR" - #try install to /bin if is root - if [ ! -f /usr/local/bin/le.sh ] ; then - #if root - if $SUDO cp le.sh /usr/local/bin/le.sh > /dev/null 2>&1; then - $SUDO chmod 755 /usr/local/bin/le.sh - $SUDO ln -s "/usr/local/bin/le.sh" /usr/local/bin/le - rm -f $WORKING_DIR/le.sh - $SUDO ln -s /usr/local/bin/le.sh $WORKING_DIR/le.sh - _info "Installed to /usr/local/bin/le" - else - #install to home, for non root user - cp le.sh $WORKING_DIR/ - chmod +x $WORKING_DIR/le.sh - _info "Installed to $WORKING_DIR/le.sh" - fi + _info "Installed to $LE_WORKING_DIR/le.sh" + cp le.sh $LE_WORKING_DIR/ + chmod +x $LE_WORKING_DIR/le.sh + + _profile="$(_detect_profile)" + if [ "$_profile" ] ; then + _debug "Found profile: $_profile" + + echo "LE_WORKING_DIR=$LE_WORKING_DIR +alias le=\"$LE_WORKING_DIR/le.sh\" +alias le.sh=\"$LE_WORKING_DIR/le.sh\" + " > "$LE_WORKING_DIR/le.env" + + _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\"" + + else + _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh" fi - rm -f $WORKING_DIR/le - ln -s $WORKING_DIR/le.sh $WORKING_DIR/le - mkdir -p $WORKING_DIR/dnsapi - cp dnsapi/* $WORKING_DIR/dnsapi/ + mkdir -p $LE_WORKING_DIR/dnsapi + cp dnsapi/* $LE_WORKING_DIR/dnsapi/ #to keep compatible mv the .acc file to .key file - if [ -f "$WORKING_DIR/account.acc" ] ; then - mv "$WORKING_DIR/account.acc" "$WORKING_DIR/account.key" + if [ -f "$LE_WORKING_DIR/account.acc" ] ; then + mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key" fi installcronjob @@ -1085,15 +1125,13 @@ uninstall() { uninstallcronjob _initpath - if [ -f "/usr/local/bin/le.sh" ] ; then - _info "Removing /usr/local/bin/le.sh" - if $SUDO rm -f /usr/local/bin/le.sh ; then - $SUDO rm -f /usr/local/bin/le - fi + _profile="$(_detect_profile)" + if [ "$_profile" ] ; then + sed -i "/source \"$LE_WORKING_DIR/le.env\"/d" "$_profile" fi - rm -f $WORKING_DIR/le - rm -f $WORKING_DIR/le.sh - _info "The keys and certs are in $WORKING_DIR, you can remove them by yourself." + + rm -f $LE_WORKING_DIR/le.sh + _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself." } From 4ac8d68d060eff0504570643436d3058ff5348fc Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 31 Jan 2016 17:38:44 +0800 Subject: [PATCH 16/40] uninstall alias --- le.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/le.sh b/le.sh index 2bb78f0c..670000e2 100755 --- a/le.sh +++ b/le.sh @@ -1127,7 +1127,7 @@ uninstall() { _profile="$(_detect_profile)" if [ "$_profile" ] ; then - sed -i "/source \"$LE_WORKING_DIR/le.env\"/d" "$_profile" + sed -i /le.env/d "$_profile" fi rm -f $LE_WORKING_DIR/le.sh From bf3135ef847c8d6c7f5403a4dd19a413af732d87 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 31 Jan 2016 17:43:32 +0800 Subject: [PATCH 17/40] add alias after install --- le.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/le.sh b/le.sh index 670000e2..faf4ae2e 100755 --- a/le.sh +++ b/le.sh @@ -1092,7 +1092,10 @@ install() { _info "Installed to $LE_WORKING_DIR/le.sh" cp le.sh $LE_WORKING_DIR/ chmod +x $LE_WORKING_DIR/le.sh - + + alias le=\"$LE_WORKING_DIR/le.sh\" + alias le.sh=\"$LE_WORKING_DIR/le.sh\" + _profile="$(_detect_profile)" if [ "$_profile" ] ; then _debug "Found profile: $_profile" From b4e2604c824482289c65afde7f4bd795baa88b1d Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 31 Jan 2016 17:53:19 +0800 Subject: [PATCH 18/40] info: Close and reopen your terminal to start using le --- le.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/le.sh b/le.sh index faf4ae2e..f21cc52d 100755 --- a/le.sh +++ b/le.sh @@ -1093,9 +1093,6 @@ install() { cp le.sh $LE_WORKING_DIR/ chmod +x $LE_WORKING_DIR/le.sh - alias le=\"$LE_WORKING_DIR/le.sh\" - alias le.sh=\"$LE_WORKING_DIR/le.sh\" - _profile="$(_detect_profile)" if [ "$_profile" ] ; then _debug "Found profile: $_profile" @@ -1106,7 +1103,7 @@ alias le.sh=\"$LE_WORKING_DIR/le.sh\" " > "$LE_WORKING_DIR/le.env" _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\"" - + _info "OK, Close and reopen your terminal to start using le" else _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh" fi From 9b292d584e1eeec854fae38d8814e43d1dcb83c7 Mon Sep 17 00:00:00 2001 From: Neil Date: Sun, 31 Jan 2016 23:48:23 +0800 Subject: [PATCH 19/40] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc090f3b..f9ab7311 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,10 @@ You don't have to be root then, altough it is recommended. Which does 3 jobs: * create and copy `le.sh` to your home dir: `~/.le` All the certs will be placed in this folder. -* create symbol link: `/usr/local/bin/le -> ~/.le/le.sh` . (You must be root to do so.) +* create alias : `le.sh=~/.le/le.sh` and `le=~/.le/le.sh`. * create everyday cron job to check and renew the cert if needed. +After install, you must close current terminal and reopen again to make the alias take effect. Ok, you are ready to issue cert now. Show help message: From 55ce170842339aa4ac8734623c307c1931125e85 Mon Sep 17 00:00:00 2001 From: Neil Date: Mon, 1 Feb 2016 09:39:58 +0800 Subject: [PATCH 20/40] remove unnecessary error message if the folder is not a domain remove unnecessary error message if the folder is not a domain. The error message output made the cron send mail. --- le.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/le.sh b/le.sh index f21cc52d..52cb4c6e 100755 --- a/le.sh +++ b/le.sh @@ -878,8 +878,8 @@ renew() { _initpath $Le_Domain if [ ! -f "$DOMAIN_CONF" ] ; then - _err "$Le_Domain is not a issued domain, skip." - return 1; + _info "$Le_Domain is not a issued domain, skip." + return 0; fi source "$DOMAIN_CONF" From 94917d1e0b969b7d1f6f95f126affc08ac825861 Mon Sep 17 00:00:00 2001 From: neil Date: Tue, 2 Feb 2016 13:05:54 +0800 Subject: [PATCH 21/40] load account.conf at a earlier time, so that "STAGE" macro can be placed in the conf file. --- le.sh | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/le.sh b/le.sh index 52cb4c6e..6603c3c6 100755 --- a/le.sh +++ b/le.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER=1.1.3 +VER=1.1.4 PROJECT="https://github.com/Neilpang/le" DEFAULT_CA="https://acme-v01.api.letsencrypt.org" @@ -293,6 +293,18 @@ _initpath() { SUDO=sudo fi fi + + if [ -z "$LE_WORKING_DIR" ]; then + LE_WORKING_DIR=$HOME/.le + fi + + if [ -z "$ACCOUNT_CONF_PATH" ] ; then + ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf" + fi + + if [ -f "$ACCOUNT_CONF_PATH" ] ; then + source "$ACCOUNT_CONF_PATH" + fi if [ -z "$API" ] ; then if [ -z "$STAGE" ] ; then @@ -303,10 +315,6 @@ _initpath() { fi fi - if [ -z "$LE_WORKING_DIR" ]; then - LE_WORKING_DIR=$HOME/.le - fi - if [ -z "$ACME_DIR" ] ; then ACME_DIR="/home/.acme" fi @@ -322,13 +330,7 @@ _initpath() { ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key" fi - if [ -z "$ACCOUNT_CONF_PATH" ] ; then - ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf" - fi - - if [ -f "$ACCOUNT_CONF_PATH" ] ; then - source "$ACCOUNT_CONF_PATH" - fi + if [ -z "$domain" ] ; then return 0 @@ -829,7 +831,7 @@ issue() { if [ -z "$Le_LinkCert" ] ; then response="$(echo $response | openssl base64 -d)" - _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')" + _err "Sign failed: $response" return 1 fi From 4e76098bdc83c83a3016467c8bed853f9d5266e6 Mon Sep 17 00:00:00 2001 From: Neil Date: Tue, 2 Feb 2016 13:08:41 +0800 Subject: [PATCH 22/40] Support Windows --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f9ab7311..98c5953e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Do NOT require to be `root/sudoer`. #Tested OS 1. Ubuntu/Debian. 2. CentOS +3. Windows (cygwin with curl, openssl and crontab included) #Supported Mode From 36d5db158a7ff6cd79fd1ffc91d30cc91648ab79 Mon Sep 17 00:00:00 2001 From: Neil Date: Tue, 2 Feb 2016 13:49:09 +0800 Subject: [PATCH 23/40] typos --- dnsapi/dns-myapi.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns-myapi.sh b/dnsapi/dns-myapi.sh index 463a44c9..af7dda7a 100644 --- a/dnsapi/dns-myapi.sh +++ b/dnsapi/dns-myapi.sh @@ -1,8 +1,8 @@ #!/bin/bash #Here is a sample custom api script. -#This file name is "dhs-myapi.sh" -#So, here must be a method dhs-myapi-add() +#This file name is "dns-myapi.sh" +#So, here must be a method dns-myapi-add() #Which will be called by le.sh to add the txt record to your api system. #returns 0 meanst success, otherwise error. From d0064bc30e83dbe45a8b1e80080b469573ebe9f1 Mon Sep 17 00:00:00 2001 From: dawsonbotsford Date: Tue, 2 Feb 2016 00:34:56 -0700 Subject: [PATCH 24/40] Spelling correction automated from the Spelling Bee --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 98c5953e..f7b89cca 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Do NOT require to be `root/sudoer`. ``` ./le.sh install ``` -You don't have to be root then, altough it is recommended. +You don't have to be root then, although it is recommended. Which does 3 jobs: * create and copy `le.sh` to your home dir: `~/.le` @@ -45,7 +45,7 @@ root@v1:~# le.sh https://github.com/Neilpang/le v1.1.1 Usage: le.sh [command] ...[args].... -Avalible commands: +Available commands: install: Install le.sh to your system. @@ -106,7 +106,7 @@ The issued cert will be renewed every 80 days automatically. # Install issued cert to apache/nginx etc. ``` -le installcert aa.com /path/to/certfile/in/apache/nginx /path/to/keyfile/in/apache/nginx /path/to/ca/certfile/apahce/nginx "service apache2|nginx reload" +le installcert aa.com /path/to/certfile/in/apache/nginx /path/to/keyfile/in/apache/nginx /path/to/ca/certfile/apache/nginx "service apache2|nginx reload" ``` Install the issued cert/key to the production apache or nginx path. From 18ab2c5c5b3491ad8105de163079b0e9f58d4353 Mon Sep 17 00:00:00 2001 From: Dawson Botsford Date: Tue, 2 Feb 2016 00:36:43 -0700 Subject: [PATCH 25/40] Manual spacing fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7b89cca..42fb6743 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ License is GPLv3 Please Star and Fork me. -Issues and pullrequests are welcomed. +Issues and pull requests are welcomed. From 9773342ad15733dbafa9a479c7ecd1986c7e0f55 Mon Sep 17 00:00:00 2001 From: neil Date: Tue, 2 Feb 2016 23:54:49 +0800 Subject: [PATCH 26/40] support dnspod.cn api --- dnsapi/dns-dp.sh | 229 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 dnsapi/dns-dp.sh diff --git a/dnsapi/dns-dp.sh b/dnsapi/dns-dp.sh new file mode 100644 index 00000000..cc213f72 --- /dev/null +++ b/dnsapi/dns-dp.sh @@ -0,0 +1,229 @@ +#!/bin/bash + +# Dnspod.cn Domain api +# +#DP_Id="1234" +# +#DP_Key="sADDsdasdgdsf" + + +DP_Api="https://dnsapi.cn" + + +#REST_API +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns-dp-add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$DP_Id" ] || [ -z "$DP_Key" ] ; then + _err "You don't specify cloudflare api key and email yet." + _err "Please create you key and try again." + return 1 + fi + + REST_API=$DP_Api + + #save the api key and email to the account conf file. + _saveaccountconf DP_Id "$DP_Id" + _saveaccountconf DP_Key "$DP_Key" + + + _debug "First detect the root zone" + if ! _get_root $fulldomain ; then + _err "invalid domain" + 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 +} + +#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 printf "$response" | grep 'No records' ; then + count=0; + return 0 + fi + + if printf "$response" | grep "Action completed successful" >/dev/null ; then + count=$(printf "$response" | grep 'TXT' | wc -l) + + record_id=$(printf "$response" | grep '^' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1) + return 0 + else + _err "get existing records error." + return 1 + fi + + + count=0 +} + +#add the txt record. +#usage: root sub txtvalue +add_record() { + root=$1 + sub=$2 + txtvalue=$3 + fulldomain=$sub.$$root + + _info "Adding record" + + if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then + return 1 + fi + + if printf "$response" | grep "Action completed successful" ; then + + return 0 + fi + + + 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 printf "$response" | grep "Action completed successful" ; then + + return 0 + fi + + return 1 #error +} + + + + +#################### Private functions bellow ################################## +#_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 [ '1' ] ; do + h=$(printf $domain | cut -d . -f $i-100) + if [ -z "$h" ] ; then + #not valid + return 1; + fi + + if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then + return 1 + fi + + if printf "$response" | grep "Action completed successful" ; then + _domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") + _debug _domain_id "$_domain_id" + if [ "$_domain_id" ] ; then + _sub_domain=$(printf $domain | cut -d . -f 1-$p) + _debug _sub_domain $_sub_domain + _domain=$h + _debug _domain $_domain + return 0 + fi + return 1 + fi + p=$i + let "i+=1" + done + return 1 +} + + +#Usage: method URI data +_rest() { + m=$1 + ep="$2" + _debug $ep + url="$REST_API/$ep" + + _debug url "$url" + + if [ "$3" ] ; then + data="$3" + _debug data "$data" + response="$(curl --silent -X $m "$url" -d $data)" + else + response="$(curl --silent -X $m "$url" )" + fi + + if [ "$?" != "0" ] ; then + _err "error $ep" + return 1 + fi + _debug response "$response" + return 0 +} + + +_debug() { + + if [ -z "$DEBUG" ] ; then + return + fi + + if [ -z "$2" ] ; then + echo $1 + else + echo "$1"="$2" + fi +} + +_info() { + if [ -z "$2" ] ; then + echo "$1" + else + echo "$1"="$2" + fi +} + +_err() { + if [ -z "$2" ] ; then + echo "$1" >&2 + else + echo "$1"="$2" >&2 + fi +} + + From 03bf3c2da14a3fe5345a78d70647b5d9b8254548 Mon Sep 17 00:00:00 2001 From: Neil Date: Wed, 3 Feb 2016 00:00:14 +0800 Subject: [PATCH 27/40] support dnspod.cn api --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 42fb6743..ccff7feb 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,28 @@ le.sh issue dns-cf aa.com www.aa.com The `CF_Key` and `CF_Email` will be saved in `~/.le/account.conf`, when next time you use cloudflare api, it will reuse this key. -More api integerations are coming. Godaddy, Dnspod, etc.... +More api integerations are coming. Godaddy, etc.... + +# Use Dnspod.cn domain api to automatically issue cert + +For now, we support dnspod.cn integeration. + +First you need to login to your dnspod.cn account to get your api key and key id. + +``` +export DP_Id="1234" + +export DP_Key="sADDsdasdgdsf" + +``` + +Ok, let's issue cert now: +``` +le.sh issue dns-dp aa.com www.aa.com +``` + +The `DP_Id` and `DP_Key` will be saved in `~/.le/account.conf`, when next time you use dnspod.cn api, it will reuse this key. + # Use custom api From 7e3cbb4661f6a962dc353767f428cd4a32642c07 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Feb 2016 13:32:46 +0800 Subject: [PATCH 28/40] fix issue, decode the error message. --- le.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/le.sh b/le.sh index 6603c3c6..111caca9 100755 --- a/le.sh +++ b/le.sh @@ -457,7 +457,7 @@ _clearupwebbroot() { _debug "remove $__webroot/.well-known/acme-challenge/$3" rm -rf "$__webroot/.well-known/acme-challenge/$3" else - _info "skip for removelevel:$2" + _info "Skip for removelevel:$2" fi return 0 @@ -830,8 +830,8 @@ issue() { if [ -z "$Le_LinkCert" ] ; then - response="$(echo $response | openssl base64 -d)" - _err "Sign failed: $response" + response="$(echo $response | openssl base64 -d -A)" + _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')" return 1 fi From 65785b5fef2b0d2006827af1a0a2f7f52580dce5 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Feb 2016 19:35:15 +0800 Subject: [PATCH 29/40] typos --- dnsapi/dns-dp.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns-dp.sh b/dnsapi/dns-dp.sh index cc213f72..4786b2ed 100644 --- a/dnsapi/dns-dp.sh +++ b/dnsapi/dns-dp.sh @@ -19,7 +19,7 @@ dns-dp-add() { txtvalue=$2 if [ -z "$DP_Id" ] || [ -z "$DP_Key" ] ; then - _err "You don't specify cloudflare api key and email yet." + _err "You don't specify dnspod api key and key id yet." _err "Please create you key and try again." return 1 fi From b47723adbb6743c116150b37524de2ff6e9d58ff Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Feb 2016 23:06:05 +0800 Subject: [PATCH 30/40] minor, renewAll skip 'dnsapi' folder. --- le.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/le.sh b/le.sh index 111caca9..d2bc962b 100755 --- a/le.sh +++ b/le.sh @@ -899,7 +899,7 @@ renewAll() { _initpath _info "renewAll" - for d in $(ls -F $LE_WORKING_DIR | grep '/$') ; do + for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do d=$(echo $d | cut -d '/' -f 1) _info "renew $d" From ce56c1bbfc34b30b49d040da9dcb3d941396746e Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Feb 2016 23:23:00 +0800 Subject: [PATCH 31/40] generate default sample account.conf for the first time install --- le.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/le.sh b/le.sh index d2bc962b..d0a06f93 100755 --- a/le.sh +++ b/le.sh @@ -1057,6 +1057,35 @@ _detect_profile() { fi } +_initconf() { + _initpath + if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then + echo "#Account configurations: +#Here are the supported macros, uncomment them to make them take effect. +#ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account. + +#STAGE=1 # Use the staging api +#FORCE=1 # Force to issue cert + +#dns api +####################### +#Cloudflare: +#api key +#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +#account email +#CF_Email="xxxx@sss.com" + +####################### +#Dnspod.cn: +#api key id +#DP_Id="1234" +#api key +#DP_Key="sADDsdasdgdsf" + + " > $ACCOUNT_CONF_PATH + fi +} + install() { _initpath @@ -1120,6 +1149,9 @@ alias le.sh=\"$LE_WORKING_DIR/le.sh\" installcronjob + if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then + _initconf + fi _info OK } From 08094865292a856ddf6001c40603dd0464a72060 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 7 Feb 2016 18:26:12 +0800 Subject: [PATCH 32/40] support cloudxns.com api --- README.md | 68 ++------------ dnsapi/dns-cx.sh | 234 +++++++++++++++++++++++++++++++++++++++++++++++ dnsapi/dns-dp.sh | 2 +- le.sh | 9 +- 4 files changed, 253 insertions(+), 60 deletions(-) create mode 100644 dnsapi/dns-cx.sh diff --git a/README.md b/README.md index ccff7feb..497dce3e 100644 --- a/README.md +++ b/README.md @@ -163,69 +163,21 @@ le renew aa.com Ok, it's finished. +#Automatic dns api integeration -# Use CloudFlare domain api to automatically issue cert +If your dns provider support api access, we can use api to automatically issue certs. +You don't have do anything manually. -For now, we support clourflare integeration. +Current we support: +## Cloudflare.com api +## Dnspod.cn api +## Cloudxns.com api -First you need to login to your clourflare account to get your api key. - -``` -export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" - -export CF_Email="xxxx@sss.com" - -``` - -Ok, let's issue cert now: -``` -le.sh issue dns-cf aa.com www.aa.com -``` - -The `CF_Key` and `CF_Email` will be saved in `~/.le/account.conf`, when next time you use cloudflare api, it will reuse this key. - - -More api integerations are coming. Godaddy, etc.... - -# Use Dnspod.cn domain api to automatically issue cert - -For now, we support dnspod.cn integeration. - -First you need to login to your dnspod.cn account to get your api key and key id. - -``` -export DP_Id="1234" - -export DP_Key="sADDsdasdgdsf" - -``` - -Ok, let's issue cert now: -``` -le.sh issue dns-dp aa.com www.aa.com -``` - -The `DP_Id` and `DP_Key` will be saved in `~/.le/account.conf`, when next time you use dnspod.cn api, it will reuse this key. - - - -# Use custom api - -If your api is not supported yet, you can write your own dns api. - -Let's assume you want to name it 'myapi', - -1. Create a bash script named `~/.le/dns-myapi.sh`, -2. In the scrypt, you must have a function named `dns-myapi-add()`. Which will be called by le.sh to add dns records. -3. Then you can use your api to issue cert like: - -``` -le.sh issue dns-myapi aa.com www.aa.com -``` - -For more details, please check our sample script: `dnsapi/dns-myapi.sh` +More apis are comming soon.... +##If your dns provider is not in the supported list above, you write your own script api easily. +For more details: [How to use dns api](/Neilpang/le/blob/master/dnsapi/README.md) #Under the Hood diff --git a/dnsapi/dns-cx.sh b/dnsapi/dns-cx.sh new file mode 100644 index 00000000..07c9cf08 --- /dev/null +++ b/dnsapi/dns-cx.sh @@ -0,0 +1,234 @@ +#!/bin/bash + +# Cloudxns.com Domain api +# +#CX_Key="1234" +# +#CX_Secret="sADDsdasdgdsf" + + +CX_Api="https://www.cloudxns.net/api2" + + +#REST_API +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns-cx-add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ] ; then + _err "You don't specify cloudxns.com api key or secret yet." + _err "Please create you key and try again." + return 1 + fi + + REST_API=$CX_Api + + #save the api key and email to the account conf file. + _saveaccountconf CX_Key "$CX_Key" + _saveaccountconf CX_Secret "$CX_Secret" + + + _debug "First detect the root zone" + if ! _get_root $fulldomain ; then + _err "invalid domain" + 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 +} + +#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 GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100" ; then + return 1 + fi + count=0 + seg=$(printf "$response" | grep -o "{[^{]*host\":\"$_sub_domain[^}]*}") + _debug seg "$seg" + if [ -z "$seg" ] ; then + return 0 + fi + + if printf "$response" | grep '"type":"TXT"' > /dev/null ; then + count=1 + record_id=$(printf "$seg" | grep -o \"record_id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") + _debug record_id "$record_id" + return 0 + fi + +} + +#add the txt record. +#usage: root sub txtvalue +add_record() { + root=$1 + sub=$2 + txtvalue=$3 + fulldomain=$sub.$root + + _info "Adding record" + + if ! _rest POST "record" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then + return 1 + fi + + 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 bellow ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=2 + p=1 + + if ! _rest GET "domain" ; then + return 1 + fi + + while [ '1' ] ; do + h=$(printf $domain | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ] ; then + #not valid + return 1; + fi + + if printf "$response" | grep "$h." ; then + seg=$(printf "$response" | grep -o "{[^{]*$h\.[^}]*\}" ) + _debug seg "$seg" + _domain_id=$(printf "$seg" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") + _debug _domain_id "$_domain_id" + if [ "$_domain_id" ] ; then + _sub_domain=$(printf $domain | cut -d . -f 1-$p) + _debug _sub_domain $_sub_domain + _domain=$h + _debug _domain $_domain + return 0 + fi + return 1 + fi + p=$i + let "i+=1" + done + return 1 +} + + +#Usage: method URI data +_rest() { + m=$1 + ep="$2" + _debug $ep + url="$REST_API/$ep" + _debug url "$url" + + cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC") + _debug cdate "$cdate" + + data="$3" + _debug data "$data" + + sec="$CX_Key$url$data$cdate$CX_Secret" + _debug sec "$sec" + hmac=$(printf "$sec"| openssl md5 |cut -d " " -f 2) + _debug hmac "$hmac" + + if [ "$3" ] ; then + response="$(curl --silent -X $m "$url" -H "API-KEY: $CX_Key" -H "API-REQUEST-DATE: $cdate" -H "API-HMAC: $hmac" -H 'Content-Type: application/json' -d "$data")" + else + response="$(curl --silent -X $m "$url" -H "API-KEY: $CX_Key" -H "API-REQUEST-DATE: $cdate" -H "API-HMAC: $hmac" -H 'Content-Type: application/json')" + fi + + if [ "$?" != "0" ] ; then + _err "error $ep" + return 1 + fi + _debug response "$response" + if ! printf "$response" | grep '"message":"success"' > /dev/null ; then + return 1 + fi + return 0 +} + + +_debug() { + + if [ -z "$DEBUG" ] ; then + return + fi + + if [ -z "$2" ] ; then + echo $1 + else + echo "$1"="$2" + fi +} + +_info() { + if [ -z "$2" ] ; then + echo "$1" + else + echo "$1"="$2" + fi +} + +_err() { + if [ -z "$2" ] ; then + echo "$1" >&2 + else + echo "$1"="$2" >&2 + fi +} + + diff --git a/dnsapi/dns-dp.sh b/dnsapi/dns-dp.sh index 4786b2ed..b39e3c40 100644 --- a/dnsapi/dns-dp.sh +++ b/dnsapi/dns-dp.sh @@ -89,7 +89,7 @@ add_record() { root=$1 sub=$2 txtvalue=$3 - fulldomain=$sub.$$root + fulldomain=$sub.$root _info "Adding record" diff --git a/le.sh b/le.sh index d0a06f93..64f6cede 100755 --- a/le.sh +++ b/le.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER=1.1.4 +VER=1.1.5 PROJECT="https://github.com/Neilpang/le" DEFAULT_CA="https://acme-v01.api.letsencrypt.org" @@ -1066,6 +1066,7 @@ _initconf() { #STAGE=1 # Use the staging api #FORCE=1 # Force to issue cert +#DEBUG=1 # Debug mode #dns api ####################### @@ -1082,6 +1083,12 @@ _initconf() { #api key #DP_Key="sADDsdasdgdsf" +####################### +#Cloudxns.com: +#CX_Key="1234" +# +#CX_Secret="sADDsdasdgdsf" + " > $ACCOUNT_CONF_PATH fi } From 39c6df299caa7aad0b3fef1fc951cf930f1fc140 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 7 Feb 2016 18:29:28 +0800 Subject: [PATCH 33/40] readme for using api --- README.md | 12 +++---- dnsapi/README.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 dnsapi/README.md diff --git a/README.md b/README.md index 497dce3e..bc0d133b 100644 --- a/README.md +++ b/README.md @@ -165,17 +165,17 @@ Ok, it's finished. #Automatic dns api integeration -If your dns provider support api access, we can use api to automatically issue certs. +If your dns provider supports api access, we can use api to automatically issue certs. You don't have do anything manually. -Current we support: -## Cloudflare.com api -## Dnspod.cn api -## Cloudxns.com api +Currently we support: +1. Cloudflare.com api +2. Dnspod.cn api +3. Cloudxns.com api More apis are comming soon.... -##If your dns provider is not in the supported list above, you write your own script api easily. +If your dns provider is not in the supported list above, you can write your own script api easily. For more details: [How to use dns api](/Neilpang/le/blob/master/dnsapi/README.md) diff --git a/dnsapi/README.md b/dnsapi/README.md new file mode 100644 index 00000000..005b8434 --- /dev/null +++ b/dnsapi/README.md @@ -0,0 +1,86 @@ +# How to use dns api + +## Use CloudFlare domain api to automatically issue cert + +For now, we support clourflare integeration. + +First you need to login to your clourflare account to get your api key. + +``` +export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" + +export CF_Email="xxxx@sss.com" + +``` + +Ok, let's issue cert now: +``` +le.sh issue dns-cf aa.com www.aa.com +``` + +The `CF_Key` and `CF_Email` will be saved in `~/.le/account.conf`, when next time you use cloudflare api, it will reuse this key. + + + +## Use Dnspod.cn domain api to automatically issue cert + +For now, we support dnspod.cn integeration. + +First you need to login to your dnspod.cn account to get your api key and key id. + +``` +export DP_Id="1234" + +export DP_Key="sADDsdasdgdsf" + +``` + +Ok, let's issue cert now: +``` +le.sh issue dns-dp aa.com www.aa.com +``` + +The `DP_Id` and `DP_Key` will be saved in `~/.le/account.conf`, when next time you use dnspod.cn api, it will reuse this key. + + +## Use Cloudxns.com domain api to automatically issue cert + +For now, we support Cloudxns.com integeration. + +First you need to login to your Cloudxns.com account to get your api key and key secret. + +``` +export CX_Key="1234" + +export CX_Api="sADDsdasdgdsf" + +``` + +Ok, let's issue cert now: +``` +le.sh issue dns-cx aa.com www.aa.com +``` + +The `CX_Key` and `CX_Api` will be saved in `~/.le/account.conf`, when next time you use Cloudxns.com api, it will reuse this key. + + + +# Use custom api + +If your api is not supported yet, you can write your own dns api. + +Let's assume you want to name it 'myapi', + +1. Create a bash script named `~/.le/dns-myapi.sh`, +2. In the scrypt, you must have a function named `dns-myapi-add()`. Which will be called by le.sh to add dns records. +3. Then you can use your api to issue cert like: + +``` +le.sh issue dns-myapi aa.com www.aa.com +``` + +For more details, please check our sample script: [dnsapi/dns-myapi.sh](README.md) + + + + From a1f0fb3eae2ba2f03e9cfb9921f1e8daa3651055 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 7 Feb 2016 18:33:04 +0800 Subject: [PATCH 34/40] fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc0d133b..62e407cf 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ More apis are comming soon.... If your dns provider is not in the supported list above, you can write your own script api easily. -For more details: [How to use dns api](/Neilpang/le/blob/master/dnsapi/README.md) +For more details: [How to use dns api](dnsapi/README.md) #Under the Hood From 855d9499859b1ad4c671d06e50cdb65ee772f4ee Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 7 Feb 2016 18:37:04 +0800 Subject: [PATCH 35/40] fix links --- README.md | 5 +++-- dnsapi/README.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 62e407cf..ee2854c7 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,8 @@ Ok, it's finished. If your dns provider supports api access, we can use api to automatically issue certs. You don't have do anything manually. -Currently we support: +###Currently we support: + 1. Cloudflare.com api 2. Dnspod.cn api 3. Cloudxns.com api @@ -177,7 +178,7 @@ More apis are comming soon.... If your dns provider is not in the supported list above, you can write your own script api easily. -For more details: [How to use dns api](dnsapi/README.md) +For more details: [How to use dns api](dnsapi) #Under the Hood diff --git a/dnsapi/README.md b/dnsapi/README.md index 005b8434..8b56ad43 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -79,7 +79,7 @@ Let's assume you want to name it 'myapi', le.sh issue dns-myapi aa.com www.aa.com ``` -For more details, please check our sample script: [dnsapi/dns-myapi.sh](README.md) +For more details, please check our sample script: [dns-myapi.sh](dns-myapi.sh) From 86c9e55ca2d8529e389d1275349c2305227273ff Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 7 Feb 2016 20:14:53 +0800 Subject: [PATCH 36/40] fix doc error --- dnsapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 8b56ad43..3588dd44 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -52,7 +52,7 @@ First you need to login to your Cloudxns.com account to get your api key and key ``` export CX_Key="1234" -export CX_Api="sADDsdasdgdsf" +export CX_Secret="sADDsdasdgdsf" ``` @@ -61,7 +61,7 @@ Ok, let's issue cert now: le.sh issue dns-cx aa.com www.aa.com ``` -The `CX_Key` and `CX_Api` will be saved in `~/.le/account.conf`, when next time you use Cloudxns.com api, it will reuse this key. +The `CX_Key` and `CX_Secret` will be saved in `~/.le/account.conf`, when next time you use Cloudxns.com api, it will reuse this key. From 2c06a14d19735d00c736ded5bf4416d48fa85373 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 11 Feb 2016 22:45:21 +0100 Subject: [PATCH 37/40] renew() should return the function result code of issue() currently the renew function does always return 0 even when the cert issuing failed. for now just return the function return code of issue(). maybe an additional offset (like +100) should be added to distinguish the issue error codes from the renew error codes. --- le.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/le.sh b/le.sh index 64f6cede..953136e3 100755 --- a/le.sh +++ b/le.sh @@ -892,7 +892,10 @@ renew() { IS_RENEW="1" issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" + local res=$? IS_RENEW="" + + return $res } renewAll() { From 0f71a9fe96c7be6f8d8961a3ac12b6b5e6fe2528 Mon Sep 17 00:00:00 2001 From: neil Date: Fri, 12 Feb 2016 08:54:28 +0800 Subject: [PATCH 38/40] Use openssl dgst -sha256 instead of openssl sha --- le.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/le.sh b/le.sh index 953136e3..5e700397 100755 --- a/le.sh +++ b/le.sh @@ -574,7 +574,7 @@ issue() { _debug HEADER "$HEADER" accountkey_json=$(echo -n "$jwk" | sed "s/ //g") - thumbprint=$(echo -n "$accountkey_json" | openssl sha -sha256 -binary | _base64 | _b64) + thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _b64) _info "Registering account" From 1add47a6b61b86f451d984fc22a2b67f0f2063f7 Mon Sep 17 00:00:00 2001 From: neil Date: Fri, 12 Feb 2016 17:56:50 +0800 Subject: [PATCH 39/40] support ECC key, ECDSA certificate --- README.md | 19 +++++++++++++++ le.sh | 71 +++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ee2854c7..5851e179 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,25 @@ If your dns provider is not in the supported list above, you can write your own For more details: [How to use dns api](dnsapi) +# Issue ECC certificate: +LetsEncrypt now can issue ECDSA certificate. +And we also support it. + +Just set key length to the `length` paramiter with a prefix "ec-". +For example: +``` +le issue /home/wwwroot/aa.com aa.com www.aa.com ec-256 +``` +Please look at the last parameter above. + +Valid values are: + +1. ec-256 (prime256v1, "ECDSA P-256") +2. ec-384 (secp384r1, "ECDSA P-384") +3. ec-521 (secp521r1, "ECDSA P-521", not supported by letsencrypt yet.) + + + #Under the Hood Speak ACME language with bash directly to Let's encrypt. diff --git a/le.sh b/le.sh index 5e700397..76df5cd8 100755 --- a/le.sh +++ b/le.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER=1.1.5 +VER=1.1.6 PROJECT="https://github.com/Neilpang/le" DEFAULT_CA="https://acme-v01.api.letsencrypt.org" @@ -41,6 +41,7 @@ _err() { else echo "$1"="$2" >&2 fi + return 1 } _h2b() { @@ -66,12 +67,17 @@ _base64() { createAccountKey() { _info "Creating account key" if [ -z "$1" ] ; then - echo Usage: $0 account-domain [2048] + echo Usage: createAccountKey account-domain [2048] return fi account=$1 length=$2 + + if [[ "$length" == "ec-"* ]] ; then + length=2048 + fi + if [ -z "$2" ] ; then _info "Use default length 2048" length=2048 @@ -92,21 +98,50 @@ createAccountKey() { createDomainKey() { _info "Creating domain key" if [ -z "$1" ] ; then - echo Usage: $0 domain [2048] + echo Usage: createDomainKey domain [2048] return fi domain=$1 length=$2 - if [ -z "$2" ] ; then - _info "Use default length 2048" - length=2048 + isec="" + if [[ "$length" == "ec-"* ]] ; then + isec="1" + length=$(printf $length | cut -d '-' -f 2-100) + eccname="$length" + fi + + if [ -z "$length" ] ; then + if [ "$isec" ] ; then + length=256 + else + length=2048 + fi + fi + _info "Use length $length" + + if [ "$isec" ] ; then + if [ "$length" == "256" ] ; then + eccname="prime256v1" + fi + if [ "$length" == "384" ] ; then + eccname="secp384r1" + fi + if [ "$length" == "521" ] ; then + eccname="secp521r1" + fi + _info "Using ec name: $eccname" fi + _initpath $domain if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then #generate account key - openssl genrsa $length > "$CERT_KEY_PATH" + if [ "$isec" ] ; then + openssl ecparam -name $eccname -genkey 2>/dev/null > "$CERT_KEY_PATH" + else + openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH" + fi else if [ "$IS_RENEW" ] ; then _info "Domain key exists, skip" @@ -250,7 +285,7 @@ _savedomainconf() { if [ "$DOMAIN_CONF" ] ; then _setopt $DOMAIN_CONF "$key" "=" "$value" else - _debug "DOMAIN_CONF is empty, can not save $key=$value" + _err "DOMAIN_CONF is empty, can not save $key=$value" fi } @@ -261,7 +296,7 @@ _saveaccountconf() { if [ "$ACCOUNT_CONF_PATH" ] ; then _setopt $ACCOUNT_CONF_PATH "$key" "=" "$value" else - _debug "ACCOUNT_CONF_PATH is empty, can not save $key=$value" + _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value" fi } @@ -329,31 +364,31 @@ _initpath() { if [ -z "$ACCOUNT_KEY_PATH" ] ; then ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key" fi - - if [ -z "$domain" ] ; then return 0 fi - mkdir -p "$LE_WORKING_DIR/$domain" + domainhome="$LE_WORKING_DIR/$domain" + mkdir -p "$domainhome" if [ -z "$DOMAIN_CONF" ] ; then - DOMAIN_CONF="$LE_WORKING_DIR/$domain/$Le_Domain.conf" + DOMAIN_CONF="$domainhome/$Le_Domain.conf" fi + if [ -z "$CSR_PATH" ] ; then - CSR_PATH="$LE_WORKING_DIR/$domain/$domain.csr" + CSR_PATH="$domainhome/$domain.csr" fi if [ -z "$CERT_KEY_PATH" ] ; then - CERT_KEY_PATH="$LE_WORKING_DIR/$domain/$domain.key" + CERT_KEY_PATH="$domainhome/$domain.key" fi if [ -z "$CERT_PATH" ] ; then - CERT_PATH="$LE_WORKING_DIR/$domain/$domain.cer" + CERT_PATH="$domainhome/$domain.cer" fi if [ -z "$CA_CERT_PATH" ] ; then - CA_CERT_PATH="$LE_WORKING_DIR/$domain/ca.cer" + CA_CERT_PATH="$domainhome/ca.cer" fi - + } From 02c0453b71fa3571e7a634b5ffea8de91c520de1 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 13 Feb 2016 11:43:57 +0800 Subject: [PATCH 40/40] support customized standalone server port for servers, that is behind HAProxy or load balance, default is 80. --- le.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/le.sh b/le.sh index 76df5cd8..7bfd2c29 100755 --- a/le.sh +++ b/le.sh @@ -308,9 +308,9 @@ _startserver() { fi # while true ; do if [ "$DEBUG" ] ; then - echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p 80 -vv + echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort -vv else - echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p 80 > /dev/null + echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort > /dev/null fi # done } @@ -558,11 +558,16 @@ issue() { _err "Please install netcat(nc) tools first." return 1 fi - - netprc="$(ss -ntpl | grep ':80 ')" + + if [ -z "$Le_HTTPPort" ] ; then + Le_HTTPPort=80 + fi + _setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort" + + netprc="$(ss -ntpl | grep :$Le_HTTPPort" ")" if [ "$netprc" ] ; then _err "$netprc" - _err "tcp port 80 is already used by $(echo "$netprc" | cut -d : -f 4)" + _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)" _err "Please stop it first" return 1 fi