diff --git a/README.md b/README.md index ccff7fe..497dce3 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 0000000..07c9cf0 --- /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 4786b2e..b39e3c4 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 d0a06f9..64f6ced 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 }