From 803fb243bfceecb4ecb054cb914a161411b2538a Mon Sep 17 00:00:00 2001 From: nytral Date: Mon, 7 Nov 2016 21:50:59 +0100 Subject: [PATCH 01/58] adding DNSMadeEasy API --- README.md | 1 + dnsapi/README.md | 19 ++++++ dnsapi/dns_me.sh | 148 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100755 dnsapi/dns_me.sh diff --git a/README.md b/README.md index 79b5a52f..104532bf 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,7 @@ You don't have do anything manually! 8. 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.) 9. LuaDNS.com API +10. DNSMadeEasy.com API ##### More APIs are coming soon... diff --git a/dnsapi/README.md b/dnsapi/README.md index 19769111..7eff6de1 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -154,4 +154,23 @@ acme.sh --issue --dns dns_lua --dnssleep 3 -d example.com -d www.example.c The `LUA_Key` and `LUA_Email` will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. +## Use DNSMadeEasy domain API + +Get your API credentials at https://cp.dnsmadeeasy.com/account/info + +``` +export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" + +export ME_Secret="qdfqsdfkjdskfj" + +``` + +To issue a cert: +``` +acme.sh --issue --dns dns_me --dnssleep 3 -d example.com -d www.example.com +``` + +The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. + + diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh new file mode 100755 index 00000000..fffd8d49 --- /dev/null +++ b/dnsapi/dns_me.sh @@ -0,0 +1,148 @@ +#!/bin/sh + +# bug reports to dev@1e.ca + +# ME_Key=qmlkdjflmkqdjf +# ME_Secret=qmsdlkqmlksdvnnpae + +ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_me_add(){ + fulldomain=$1 + txtvalue=$2 + + if [ -z "$ME_Key" ] || [ -z "$ME_Secret" ] ; then + _err "You didn't specify DNSMadeEasy api key and secret yet." + _err "Please create you key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf ME_Key "$ME_Key" + _saveaccountconf ME_Secret "$ME_Secret" + + _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" + _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT" + + if ! printf "$response" | grep \"totalRecords\": > /dev/null ; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o \"totalRecords\":[^,]* | cut -d : -f 2) + _debug count "$count" + if [ "$count" = "0" ] ; then + _info "Adding record" + if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then + if printf -- "%s" "$response" | grep id: > /dev/null ; 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\":[^,]* | cut -d : -f 2 | head -n 1) + _debug "record_id" $record_id + + _me_rest PUT "$_domain_id/records/$record_id/" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}" + if [ "$?" = "0" ]; then + _info "Updated" + #todo: check if the record takes effect + return 0; + fi + _err "Update error" + return 1 + fi + +} + + +#fulldomain +dns_me_rm() { + fulldomain=$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 + while [ '1' ] ; do + h=$(printf $domain | cut -d . -f $i-100) + if [ -z "$h" ] ; then + #not valid + return 1; + fi + + if ! _me_rest GET "name?domainname=$h" ; then + return 1 + fi + + if printf $response | grep \"name\":\"$h\" >/dev/null ; then + _domain_id=$(printf "%s\n" "$response" | _egrep_o \"id\":[^,]* | head -n 1 | cut -d : -f 2 ) + if [ "$_domain_id" ] ; then + _sub_domain=$(printf $domain | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + return 1 + fi + p=$i + i=$(expr $i + 1) + done + return 1 +} + +_me_rest() { + m=$1 + ep="$2" + data="$3" + _debug $ep + + cdate=$(date -Ru) + hmac=$(printf "$cdate" | openssl dgst -sha1 -hmac $ME_Secret | cut -d = -f 2 | tr -d ' ') + + _H1="x-dnsme-apiKey: $ME_Key" + _H2="x-dnsme-requestDate: $cdate" + _H3="x-dnsme-hmac: $hmac" + + if [ "$data" ] ; then + _debug data "$data" + response="$(_post "$data" "$ME_Api/$ep" "" $m)" + else + response="$(_get "$ME_Api/$ep")" + fi + + if [ "$?" != "0" ] ; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} + + From 49e1f7d8bf8fc9b38918c03255badc5ccca1f383 Mon Sep 17 00:00:00 2001 From: nytral Date: Mon, 7 Nov 2016 22:16:00 +0100 Subject: [PATCH 02/58] bugfix --- dnsapi/dns_me.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index fffd8d49..4234ac9c 100755 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -46,7 +46,7 @@ dns_me_add(){ if [ "$count" = "0" ] ; then _info "Adding record" if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then - if printf -- "%s" "$response" | grep id: > /dev/null ; then + if [ "$?" = "0" ]; then _info "Added" #todo: check if the record takes effect return 0 From 4fe7b6cd65df54833da378f3b2bec36b7c5202c2 Mon Sep 17 00:00:00 2001 From: nytral Date: Mon, 7 Nov 2016 22:16:53 +0100 Subject: [PATCH 03/58] better bugfix --- dnsapi/dns_me.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index 4234ac9c..b379fe98 100755 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -46,7 +46,7 @@ dns_me_add(){ if [ "$count" = "0" ] ; then _info "Adding record" if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then - if [ "$?" = "0" ]; then + if printf -- "%s" "$response" | grep \"id\": > /dev/null ; then _info "Added" #todo: check if the record takes effect return 0 From 0b5bff01e1a43cc531813c7a359e47735751a8aa Mon Sep 17 00:00:00 2001 From: nytral Date: Tue, 8 Nov 2016 14:13:05 +0100 Subject: [PATCH 04/58] s/bash/sh/ --- dnsapi/dns_me.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index b379fe98..d8dd7e45 100755 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh # bug reports to dev@1e.ca From 22b83d76303eadfa05e6da859fa1e1b8ae769ea6 Mon Sep 17 00:00:00 2001 From: nytral Date: Tue, 8 Nov 2016 15:56:46 +0100 Subject: [PATCH 05/58] _hmac use and generic date --- dnsapi/dns_me.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index d8dd7e45..3135718e 100755 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -123,8 +123,8 @@ _me_rest() { data="$3" _debug $ep - cdate=$(date -Ru) - hmac=$(printf "$cdate" | openssl dgst -sha1 -hmac $ME_Secret | cut -d = -f 2 | tr -d ' ') + cdate=$(date -u +"%a, %d %b %Y %T %Z") + hmac=$(printf "$cdate" | _hmac sha1 "$ME_Secret" 1) _H1="x-dnsme-apiKey: $ME_Key" _H2="x-dnsme-requestDate: $cdate" From 044da37c953003e284c2307624c858a4fecbba0c Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 21 Feb 2017 21:34:43 +0800 Subject: [PATCH 06/58] change installcert parameter order --- acme.sh | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/acme.sh b/acme.sh index 43f87e98..e24d4d9a 100755 --- a/acme.sh +++ b/acme.sh @@ -3653,7 +3653,7 @@ issue() { _savedomainconf "Le_RealKeyPath" "$_real_key" _savedomainconf "Le_ReloadCmd" "$_reload_cmd" _savedomainconf "Le_RealFullChainPath" "$_real_fullchain" - _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" + _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd" fi } @@ -3964,16 +3964,18 @@ installcert() { _savedomainconf "Le_ReloadCmd" "$_reload_cmd" _savedomainconf "Le_RealFullChainPath" "$_real_fullchain" - _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" + _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd" } +#domain cert key ca fullchain reloadcmd backup-prefix _installcert() { _main_domain="$1" _real_cert="$2" _real_key="$3" _real_ca="$4" - _reload_cmd="$5" - _real_fullchain="$6" + _real_fullchain="$5" + _reload_cmd="$6" + _backup_prefix="$7" if [ "$_real_cert" = "$NO_VALUE" ]; then _real_cert="" @@ -3991,11 +3993,13 @@ _installcert() { _real_fullchain="" fi + _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix" + mkdir -p "$_backup_path" + if [ "$_real_cert" ]; then _info "Installing cert to:$_real_cert" if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then - mkdir -p "$DOMAIN_BACKUP_PATH" - cp "$_real_cert" "$DOMAIN_BACKUP_PATH/cert.bak" + cp "$_real_cert" "$_backup_path/cert.bak" fi cat "$CERT_PATH" >"$_real_cert" fi @@ -4007,8 +4011,7 @@ _installcert() { cat "$CA_CERT_PATH" >>"$_real_ca" else if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then - mkdir -p "$DOMAIN_BACKUP_PATH" - cp "$_real_ca" "$DOMAIN_BACKUP_PATH/ca.bak" + cp "$_real_ca" "$_backup_path/ca.bak" fi cat "$CA_CERT_PATH" >"$_real_ca" fi @@ -4017,8 +4020,7 @@ _installcert() { if [ "$_real_key" ]; then _info "Installing key to:$_real_key" if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then - mkdir -p "$DOMAIN_BACKUP_PATH" - cp "$_real_key" "$DOMAIN_BACKUP_PATH/key.bak" + cp "$_real_key" "$_backup_path/key.bak" fi cat "$CERT_KEY_PATH" >"$_real_key" fi @@ -4026,8 +4028,7 @@ _installcert() { if [ "$_real_fullchain" ]; then _info "Installing full chain to:$_real_fullchain" if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then - mkdir -p "$DOMAIN_BACKUP_PATH" - cp "$_real_fullchain" "$DOMAIN_BACKUP_PATH/fullchain.bak" + cp "$_real_fullchain" "$_backup_path/fullchain.bak" fi cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" fi From 6dfc8fe0ea0f0d0889946d3cc54bd5ec21f7e59d Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 21 Feb 2017 23:18:11 +0800 Subject: [PATCH 07/58] support vsftpd hook --- deploy/README.md | 20 +++++++++++++ deploy/vsftpd.sh | 75 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index fcdf8019..cb500311 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -26,5 +26,25 @@ Before you can deploy your cert, you must [issue the cert first](https://github. (TODO) +## 4. Deploy the cert to local vsftpd server. +```sh +acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd +``` + +The default vsftpd conf file is `/etc/vsftpd.conf`, if your vsftpd conf is not in the default location, you can specify one: + +```sh +export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf" + +acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd +``` + +The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one: + +```sh +export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" + +acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd +``` diff --git a/deploy/vsftpd.sh b/deploy/vsftpd.sh index 5e89ea95..d2d8aa16 100644 --- a/deploy/vsftpd.sh +++ b/deploy/vsftpd.sh @@ -4,6 +4,9 @@ #returns 0 means success, otherwise error. +#DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf" +#DEPLOY_VSFTPD_RELOAD="service vsftpd restart" + ######## Public functions ##################### #domain keyfile certfile cafile fullchain @@ -20,7 +23,75 @@ vsftpd_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _err "deploy cert to vsftpd server, Not implemented yet" - return 1 + _ssl_path="/etc/acme.sh/vsftpd" + if ! mkdir -p "$_ssl_path"; then + _err "Can not create folder:$_ssl_path" + return 1 + fi + + DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf" + _vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}" + if [ ! -f "$_vsftpd_conf" ]; then + if [ -z "$DEPLOY_VSFTPD_CONF" ]; then + _err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF" + return 1 + else + _err "It seems that the specified vsftpd conf is not valid, please check." + return 1 + fi + fi + + if [ ! -w "$_vsftpd_conf" ]; then + _err "The file $_vsftpd_conf is not writable, please change the permission." + return 1 + fi + + _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak" + _info "Backup $_vsftpd_conf to $_backup_conf" + cp "$_vsftpd_conf" "$_backup_conf" + + _info "Copying key and cert" + _real_key="$_ssl_path/vsftpd.key" + if ! cat "$_ckey" >"$_real_key"; then + _err "Error: write key file to: $_real_key" + return 1 + fi + _real_fullchain="$_ssl_path/vsftpd.chain.pem" + if ! cat "$_cfullchain" >"$_real_fullchain"; then + _err "Error: write key file to: $_real_fullchain" + return 1 + fi + _info "Modify vsftpd conf: $_vsftpd_conf" + + DEFAULT_VSFTPD_RELOAD="service vsftpd restart" + _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}" + if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \ + && _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \ + && _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES" \ + && eval "$_reload"; then + _info "Deploy success!" + if [ "$DEPLOY_VSFTPD_CONF" ]; then + _savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF" + else + _cleardomainconf DEPLOY_VSFTPD_CONF + fi + if [ "$DEPLOY_VSFTPD_RELOAD" ]; then + _savedomainconf DEPLOY_VSFTPD_RELOAD "$DEPLOY_VSFTPD_RELOAD" + else + _cleardomainconf DEPLOY_VSFTPD_RELOAD + fi + return 0 + else + _err "Config vsftpd server error, please report bug to us." + _info "Restoring vsftpd conf" + if cat "$_backup_conf" >"$_vsftpd_conf"; then + _info "Restore conf success" + eval "$_reload" + else + _err "Opps, error restore vsftpd conf, please report bug to us." + fi + return 1 + fi + return 1 } From 45d6e00ff14a48f4ebd9a4a5a5a06cecad6b41d2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 22 Feb 2017 20:17:36 +0800 Subject: [PATCH 08/58] fix format --- deploy/vsftpd.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/vsftpd.sh b/deploy/vsftpd.sh index d2d8aa16..cc57f990 100644 --- a/deploy/vsftpd.sh +++ b/deploy/vsftpd.sh @@ -39,7 +39,7 @@ vsftpd_deploy() { else _err "It seems that the specified vsftpd conf is not valid, please check." return 1 - fi + fi fi if [ ! -w "$_vsftpd_conf" ]; then @@ -50,7 +50,7 @@ vsftpd_deploy() { _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak" _info "Backup $_vsftpd_conf to $_backup_conf" cp "$_vsftpd_conf" "$_backup_conf" - + _info "Copying key and cert" _real_key="$_ssl_path/vsftpd.key" if ! cat "$_ckey" >"$_real_key"; then @@ -63,7 +63,7 @@ vsftpd_deploy() { return 1 fi _info "Modify vsftpd conf: $_vsftpd_conf" - + DEFAULT_VSFTPD_RELOAD="service vsftpd restart" _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}" if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \ From ddf293bbcda2196ea9e7ec18d97d9f5a9a45d4c5 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 22 Feb 2017 20:40:33 +0800 Subject: [PATCH 09/58] reload only for renewal --- deploy/vsftpd.sh | 75 ++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/deploy/vsftpd.sh b/deploy/vsftpd.sh index cc57f990..1c6410a6 100644 --- a/deploy/vsftpd.sh +++ b/deploy/vsftpd.sh @@ -29,28 +29,6 @@ vsftpd_deploy() { return 1 fi - DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf" - _vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}" - - if [ ! -f "$_vsftpd_conf" ]; then - if [ -z "$DEPLOY_VSFTPD_CONF" ]; then - _err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF" - return 1 - else - _err "It seems that the specified vsftpd conf is not valid, please check." - return 1 - fi - fi - - if [ ! -w "$_vsftpd_conf" ]; then - _err "The file $_vsftpd_conf is not writable, please change the permission." - return 1 - fi - - _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak" - _info "Backup $_vsftpd_conf to $_backup_conf" - cp "$_vsftpd_conf" "$_backup_conf" - _info "Copying key and cert" _real_key="$_ssl_path/vsftpd.key" if ! cat "$_ckey" >"$_real_key"; then @@ -62,15 +40,51 @@ vsftpd_deploy() { _err "Error: write key file to: $_real_fullchain" return 1 fi - _info "Modify vsftpd conf: $_vsftpd_conf" DEFAULT_VSFTPD_RELOAD="service vsftpd restart" _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}" - if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \ - && _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \ - && _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES" \ - && eval "$_reload"; then - _info "Deploy success!" + + if [ -z "$IS_RENEW" ]; then + DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf" + _vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}" + if [ ! -f "$_vsftpd_conf" ]; then + if [ -z "$DEPLOY_VSFTPD_CONF" ]; then + _err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF" + return 1 + else + _err "It seems that the specified vsftpd conf is not valid, please check." + return 1 + fi + fi + if [ ! -w "$_vsftpd_conf" ]; then + _err "The file $_vsftpd_conf is not writable, please change the permission." + return 1 + fi + _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak" + _info "Backup $_vsftpd_conf to $_backup_conf" + cp "$_vsftpd_conf" "$_backup_conf" + + _info "Modify vsftpd conf: $_vsftpd_conf" + if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \ + && _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \ + && _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then + _info "Set config success!" + else + _err "Config vsftpd server error, please report bug to us." + _info "Restoring vsftpd conf" + if cat "$_backup_conf" >"$_vsftpd_conf"; then + _info "Restore conf success" + eval "$_reload" + else + _err "Opps, error restore vsftpd conf, please report bug to us." + fi + return 1 + fi + fi + + _info "Run reload: $_reload" + if eval "$_reload"; then + _info "Reload success!" if [ "$DEPLOY_VSFTPD_CONF" ]; then _savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF" else @@ -83,8 +97,7 @@ vsftpd_deploy() { fi return 0 else - _err "Config vsftpd server error, please report bug to us." - _info "Restoring vsftpd conf" + _err "Reload error, restoring" if cat "$_backup_conf" >"$_vsftpd_conf"; then _info "Restore conf success" eval "$_reload" @@ -93,5 +106,5 @@ vsftpd_deploy() { fi return 1 fi - return 1 + return 0 } From 35ca729cb939fb3a2e4798992fc51eb5b1ddda88 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 23 Feb 2017 19:01:48 +0800 Subject: [PATCH 10/58] fix doc --- deploy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index cb500311..40800401 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -13,7 +13,7 @@ Then you can deploy now: ```sh export DEPLOY_CPANEL_USER=myusername export DEPLOY_CPANEL_PASSWORD=PASSWORD -acme.sh --deploy -d example.com --deploy --deploy-hook cpanel +acme.sh --deploy -d example.com --deploy-hook cpanel ``` ## 2. Deploy ssl cert on kong proxy engine based on api. From a239a9efd508aa45d7299df6bb24e216b113251e Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 23 Feb 2017 19:04:08 +0800 Subject: [PATCH 11/58] fix doc --- deploy/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deploy/README.md b/deploy/README.md index 40800401..a4e9136f 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,12 +1,14 @@ # Using deploy api +Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). + Here are the scripts to deploy the certs/key to the server/services. ## 1. Deploy the certs to your cpanel host. (cpanel deploy hook is not finished yet, this is just an example.) -Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). + Then you can deploy now: From 1965035166a6259641d3f0c43de930ebb8d8b878 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 23 Feb 2017 20:03:03 +0800 Subject: [PATCH 12/58] support exim4 deploy --- deploy/README.md | 22 ++++++++++++ deploy/exim4.sh | 92 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index a4e9136f..4a13e096 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -50,3 +50,25 @@ export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd ``` +## 5. Deploy the cert to local exim4 server. + +```sh +acme.sh --deploy -d ftp.example.com --deploy-hook exim4 +``` + +The default exim4 conf file is `/etc/exim/exim.conf`, if your exim4 conf is not in the default location, you can specify one: + +```sh +export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template" + +acme.sh --deploy -d ftp.example.com --deploy-hook exim4 +``` + +The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one: + +```sh +export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart" + +acme.sh --deploy -d ftp.example.com --deploy-hook exim4 +``` + diff --git a/deploy/exim4.sh b/deploy/exim4.sh index b53f58ec..bf92b438 100644 --- a/deploy/exim4.sh +++ b/deploy/exim4.sh @@ -4,6 +4,9 @@ #returns 0 means success, otherwise error. +#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf" +#DEPLOY_EXIM4_RELOAD="service exim4 restart" + ######## Public functions ##################### #domain keyfile certfile cafile fullchain @@ -20,7 +23,92 @@ exim4_deploy() { _debug _cca "$_cca" _debug _cfullchain "$_cfullchain" - _err "deploy cert to exim4 server, Not implemented yet" - return 1 + _ssl_path="/etc/acme.sh/exim4" + if ! mkdir -p "$_ssl_path"; then + _err "Can not create folder:$_ssl_path" + return 1 + fi + + _info "Copying key and cert" + _real_key="$_ssl_path/exim4.key" + if ! cat "$_ckey" >"$_real_key"; then + _err "Error: write key file to: $_real_key" + return 1 + fi + _real_fullchain="$_ssl_path/exim4.pem" + if ! cat "$_cfullchain" >"$_real_fullchain"; then + _err "Error: write key file to: $_real_fullchain" + return 1 + fi + + DEFAULT_EXIM4_RELOAD="service exim4 restart" + _reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}" + + if [ -z "$IS_RENEW" ]; then + DEFAULT_EXIM4_CONF="/etc/exim/exim.conf" + if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then + DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template" + fi + _exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}" + _debug _exim4_conf "$_exim4_conf" + if [ ! -f "$_exim4_conf" ]; then + if [ -z "$DEPLOY_EXIM4_CONF" ]; then + _err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF" + return 1 + else + _err "It seems that the specified exim4 conf is not valid, please check." + return 1 + fi + fi + if [ ! -w "$_exim4_conf" ]; then + _err "The file $_exim4_conf is not writable, please change the permission." + return 1 + fi + _backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak" + _info "Backup $_exim4_conf to $_backup_conf" + cp "$_exim4_conf" "$_backup_conf" + + _info "Modify exim4 conf: $_exim4_conf" + if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" \ + && _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then + _info "Set config success!" + else + _err "Config exim4 server error, please report bug to us." + _info "Restoring exim4 conf" + if cat "$_backup_conf" >"$_exim4_conf"; then + _info "Restore conf success" + eval "$_reload" + else + _err "Opps, error restore exim4 conf, please report bug to us." + fi + return 1 + fi + fi + + _info "Run reload: $_reload" + if eval "$_reload"; then + _info "Reload success!" + if [ "$DEPLOY_EXIM4_CONF" ]; then + _savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF" + else + _cleardomainconf DEPLOY_EXIM4_CONF + fi + if [ "$DEPLOY_EXIM4_RELOAD" ]; then + _savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD" + else + _cleardomainconf DEPLOY_EXIM4_RELOAD + fi + return 0 + else + _err "Reload error, restoring" + if cat "$_backup_conf" >"$_exim4_conf"; then + _info "Restore conf success" + eval "$_reload" + else + _err "Opps, error restore exim4 conf, please report bug to us." + fi + return 1 + fi + return 0 } From 7b2fa1edb41a3b8a447cc590d0450a4a60c4029f Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Fri, 2 Dec 2016 20:10:13 +0000 Subject: [PATCH 13/58] add API for www.do.de/www.resellerinterface.de --- dnsapi/dns_do.sh | 167 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100755 dnsapi/dns_do.sh diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh new file mode 100755 index 00000000..43ce678f --- /dev/null +++ b/dnsapi/dns_do.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env sh + +# DNS API for Domain-Offensive / Resellerinterface / Domainrobot + +# DO_PID="KD-1234567" +# DO_PW="cdfkjl3n2" + +DO_URL="https://soap.resellerinterface.de/" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_do_add() { + fulldomain=$1 + txtvalue=$2 + _cookiejar="$(_mktemp)" + if _dns_do_authenticate; then + _info "Adding TXT record to ${_domain} as ${fulldomain}" + _dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300 + if _contains "${response}" '>success<'; then + return 0 + fi + _err "Could not create resource record, check logs" + fi + return 1 +} + +#fulldomain +dns_do_rm() { + fulldomain=$1 + _cookiejar="$(_mktemp)" + if _dns_do_authenticate; then + if _dns_do_list_rrs; then + for _rrid in ${_rr_list}; do + _info "Deleting resource record $_rrid for $_domain" + _dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}" + if ! _contains "${response}" '>success<'; then + _err "Could not delete resource record for ${_domain}, id ${_rrid}" + fi + done + return 0 + fi + fi + return 1 +} + +#################### Private functions below ################################## +_dns_do_authenticate() { + _info "Authenticating as ${DO_PID}" + _dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}" + if _contains "${response}" '>success<'; then + _get_root "$fulldomain" + _debug "_domain $_domain" + return 0 + else + _err "Authentication failed, check logs" + fi + return 1 +} + +_dns_do_list_rrs() { + _dns_do_soap getRRList origin "${_domain}" + if ! _contains "${response}" 'SOAP-ENC:Array'; then + _err "getRRList origin ${_domain} failed" + return 1 + fi + _rr_list="$(echo "${response}" \ + | tr -d "\n\r\t" \ + | sed -e 's//\n/g' \ + | grep -F ">${fulldomain}" \ + | sed -e 's//\n\0/g' \ + | grep -F '>id' \ + | sed -re 's/.*]*>([^<]+)<\/value>.*/\1/')" + [ "${_rr_list}" ] +} + +_dns_do_soap() { + func="$1" + shift + # put the parameters to xml + body="" + while [ "$1" ] ; do + _k="$1" + shift + _v="$1" + shift + body="$body<$_k>$_v" + done + body="$body" + _debug2 "SOAP request ${body}" + + # build SOAP XML + _xml=' + + '"$body"' +' + + # set SOAP headers + _H1="SOAPAction: ${DO_URL}#${func}" + # add cookie header if present + [ -s "${_cookiejar}" ] && _H2="$(cat "${_cookiejar}")" + + if ! response="$(_post "${_xml}" "${DO_URL}")"; then + _err "Error <$1>" + return 1 + fi + _debug2 "SOAP response $response" + + # retrieve cookie header + grep -F 'Set-Cookie:' "$HTTP_HEADER" | sed -re 's/^Set-(Cookie: [^;]+).*/\1/' | head -1 > "${_cookiejar}" + + return 0 +} + +_get_root() { + domain=$1 + i=1 + + _all_domains="$(_mktemp)" + _dns_do_soap getDomainList + echo "${response}" | tr -d "\n\r\t " | grep -Eo 'domain]+>[^<]+' | sed -re 's/^domain<\/key>]+>//g' > "${_all_domains}" + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + if [ -z "$h" ]; then + return 1 + fi + + if grep -qF "$h" "${_all_domains}"; then + _domain="$h" + return 0 + fi + + i=$(_math $i + 1) + done + _debug "$domain not found" + + return 1 +} + +_info() { + if [ -z "$2" ]; then + echo "[$(date)] $1" + else + echo "[$(date)] $1='$2'" + fi +} + +_err() { + _info "$@" >&2 + return 1 +} + +_debug() { + if [ -z "$DEBUG" ]; then + return + fi + _err "$@" + return 0 +} + +_debug2() { + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + _debug "$@" + fi + return +} From 76a3371b402b3006ee2f26872debffc8b6dca103 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Sat, 3 Dec 2016 10:07:13 +0000 Subject: [PATCH 14/58] remove non-POSIX sed -r and use built-in functions --- dnsapi/dns_do.sh | 43 ++++++++----------------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 43ce678f..cea1beb3 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -67,10 +67,11 @@ _dns_do_list_rrs() { _rr_list="$(echo "${response}" \ | tr -d "\n\r\t" \ | sed -e 's//\n/g' \ - | grep -F ">${fulldomain}" \ - | sed -e 's//\n\0/g' \ - | grep -F '>id' \ - | sed -re 's/.*]*>([^<]+)<\/value>.*/\1/')" + | fgrep ">${fulldomain}" \ + | sed -e 's/<\/item>/\n/g' \ + | grep '>id[0-9]{1,16}<' \ + | tr -d '><')" [ "${_rr_list}" ] } @@ -107,7 +108,7 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - grep -F 'Set-Cookie:' "$HTTP_HEADER" | sed -re 's/^Set-(Cookie: [^;]+).*/\1/' | head -1 > "${_cookiejar}" + cat "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]+' | head -1 > "${_cookiejar}" return 0 } @@ -118,7 +119,7 @@ _get_root() { _all_domains="$(_mktemp)" _dns_do_soap getDomainList - echo "${response}" | tr -d "\n\r\t " | grep -Eo 'domain]+>[^<]+' | sed -re 's/^domain<\/key>]+>//g' > "${_all_domains}" + echo "${response}" | tr -d "\n\r\t " | _egrep_o 'domain]+>[^<]+' | sed -e 's/^domain<\/key>]+>//g' > "${_all_domains}" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -126,7 +127,7 @@ _get_root() { return 1 fi - if grep -qF "$h" "${_all_domains}"; then + if fgrep -q "$h" "${_all_domains}"; then _domain="$h" return 0 fi @@ -137,31 +138,3 @@ _get_root() { return 1 } - -_info() { - if [ -z "$2" ]; then - echo "[$(date)] $1" - else - echo "[$(date)] $1='$2'" - fi -} - -_err() { - _info "$@" >&2 - return 1 -} - -_debug() { - if [ -z "$DEBUG" ]; then - return - fi - _err "$@" - return 0 -} - -_debug2() { - if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then - _debug "$@" - fi - return -} From 88ed5e506a9c1f862fc99d4c681bf47b2b97f253 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Sat, 3 Dec 2016 19:04:31 +0000 Subject: [PATCH 15/58] fix whitespace and UUOC --- dnsapi/dns_do.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index cea1beb3..034e6aa6 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -80,7 +80,7 @@ _dns_do_soap() { shift # put the parameters to xml body="" - while [ "$1" ] ; do + while [ "$1" ]; do _k="$1" shift _v="$1" @@ -108,7 +108,7 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - cat "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]+' | head -1 > "${_cookiejar}" + _egrep_o 'Cookie: [^;]+' < "$HTTP_HEADER" | head -1 >"${_cookiejar}" return 0 } @@ -119,7 +119,7 @@ _get_root() { _all_domains="$(_mktemp)" _dns_do_soap getDomainList - echo "${response}" | tr -d "\n\r\t " | _egrep_o 'domain]+>[^<]+' | sed -e 's/^domain<\/key>]+>//g' > "${_all_domains}" + echo "${response}" | tr -d "\n\r\t " | _egrep_o 'domain]+>[^<]+' | sed -e 's/^domain<\/key>]+>//g' >"${_all_domains}" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) From 0d4035e99662c737ab4160e6a77e8071f620c4c4 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Sat, 3 Dec 2016 19:24:42 +0000 Subject: [PATCH 16/58] remove fgrep, escape regex chars instead --- dnsapi/dns_do.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 034e6aa6..075cb3b0 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -67,7 +67,7 @@ _dns_do_list_rrs() { _rr_list="$(echo "${response}" \ | tr -d "\n\r\t" \ | sed -e 's//\n/g' \ - | fgrep ">${fulldomain}" \ + | grep ">$(_regexcape "$fulldomain")" \ | sed -e 's/<\/item>/\n/g' \ | grep '>id[0-9]{1,16}<' \ @@ -127,7 +127,7 @@ _get_root() { return 1 fi - if fgrep -q "$h" "${_all_domains}"; then + if grep -q "$(_regexcape "$h")" "${_all_domains}"; then _domain="$h" return 0 fi @@ -138,3 +138,7 @@ _get_root() { return 1 } + +_regexcape() { + echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g' +} From 3ebbeb103c195737b5d234a60c73056a9a5bb88f Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Sat, 3 Dec 2016 19:27:46 +0000 Subject: [PATCH 17/58] old habits --- dnsapi/dns_do.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 075cb3b0..cc8f339e 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -108,7 +108,7 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - _egrep_o 'Cookie: [^;]+' < "$HTTP_HEADER" | head -1 >"${_cookiejar}" + _egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | head -1 >"${_cookiejar}" return 0 } From b95a99e0c2c8aa0e31b44df39fbe46119864dd55 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Mon, 5 Dec 2016 20:35:31 +0000 Subject: [PATCH 18/58] remove cookiejar temp file --- dnsapi/dns_do.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index cc8f339e..17ab23d7 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -98,8 +98,6 @@ _dns_do_soap() { # set SOAP headers _H1="SOAPAction: ${DO_URL}#${func}" - # add cookie header if present - [ -s "${_cookiejar}" ] && _H2="$(cat "${_cookiejar}")" if ! response="$(_post "${_xml}" "${DO_URL}")"; then _err "Error <$1>" @@ -108,7 +106,7 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - _egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | head -1 >"${_cookiejar}" + _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | head -1)" return 0 } From 743f821f1ee9e4827e95a4b1c4088949cbfc78ac Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Mon, 5 Dec 2016 20:49:46 +0000 Subject: [PATCH 19/58] improve error message on failed authentication --- dnsapi/dns_do.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 17ab23d7..7576092b 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -53,7 +53,7 @@ _dns_do_authenticate() { _debug "_domain $_domain" return 0 else - _err "Authentication failed, check logs" + _err "Authentication failed, are DO_PID and DO_PW set correctly?" fi return 1 } From d1d2f6f4518cc7332b92ef856471b1a76a6c03e8 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Mon, 5 Dec 2016 20:56:38 +0000 Subject: [PATCH 20/58] avoid temp file for domain list --- dnsapi/dns_do.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 7576092b..1a9458f8 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -115,9 +115,11 @@ _get_root() { domain=$1 i=1 - _all_domains="$(_mktemp)" _dns_do_soap getDomainList - echo "${response}" | tr -d "\n\r\t " | _egrep_o 'domain]+>[^<]+' | sed -e 's/^domain<\/key>]+>//g' >"${_all_domains}" + _all_domains="/$(echo "${response}" \ + | tr -d "\n\r\t " \ + | _egrep_o 'domain]+>[^<]+' \ + | sed -e 's/^domain<\/key>]*>//g')" while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) @@ -125,7 +127,7 @@ _get_root() { return 1 fi - if grep -q "$(_regexcape "$h")" "${_all_domains}"; then + if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then _domain="$h" return 0 fi From 1cb6e9e7d0653b2920fb6eac0bc11d3abdfa5190 Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Wed, 7 Dec 2016 11:36:22 +0100 Subject: [PATCH 21/58] remove cookiejar file d'oh --- dnsapi/dns_do.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 1a9458f8..edcf4559 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -13,7 +13,6 @@ DO_URL="https://soap.resellerinterface.de/" dns_do_add() { fulldomain=$1 txtvalue=$2 - _cookiejar="$(_mktemp)" if _dns_do_authenticate; then _info "Adding TXT record to ${_domain} as ${fulldomain}" _dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300 From cdec38ba12a3779f257f321fd8a84b02fee0dbec Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Wed, 7 Dec 2016 11:39:10 +0100 Subject: [PATCH 22/58] return error if any removal failed --- dnsapi/dns_do.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index edcf4559..1450ee5a 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -30,14 +30,16 @@ dns_do_rm() { _cookiejar="$(_mktemp)" if _dns_do_authenticate; then if _dns_do_list_rrs; then + _dns_do_had_error=0 for _rrid in ${_rr_list}; do _info "Deleting resource record $_rrid for $_domain" _dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}" if ! _contains "${response}" '>success<'; then + _dns_do_had_error=1 _err "Could not delete resource record for ${_domain}, id ${_rrid}" fi done - return 0 + return _dns_do_had_error fi fi return 1 From e55605dbe9b91702f9b93ac0c0a84736fb8e0145 Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Wed, 7 Dec 2016 11:41:32 +0100 Subject: [PATCH 23/58] remove _all_ mktemp --- dnsapi/dns_do.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 1450ee5a..43a73b97 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -27,7 +27,6 @@ dns_do_add() { #fulldomain dns_do_rm() { fulldomain=$1 - _cookiejar="$(_mktemp)" if _dns_do_authenticate; then if _dns_do_list_rrs; then _dns_do_had_error=0 From 1633d14547ad6d1e5521af0ce814373cd840fc14 Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Wed, 7 Dec 2016 12:07:40 +0100 Subject: [PATCH 24/58] forgot dollar sign --- dnsapi/dns_do.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 43a73b97..bbf86bac 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -38,7 +38,7 @@ dns_do_rm() { _err "Could not delete resource record for ${_domain}, id ${_rrid}" fi done - return _dns_do_had_error + return $_dns_do_had_error fi fi return 1 From bf8ffade29daca74e15e53cf40cf1b1ba451272f Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Thu, 8 Dec 2016 08:43:29 +0100 Subject: [PATCH 25/58] replace head -1, add link to GitHub --- dnsapi/dns_do.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index bbf86bac..b718cd41 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -2,6 +2,9 @@ # DNS API for Domain-Offensive / Resellerinterface / Domainrobot +# Report bugs at https://github.com/seidler2547/acme.sh/issues + +# set these environment variables to match your customer ID and password: # DO_PID="KD-1234567" # DO_PW="cdfkjl3n2" @@ -106,7 +109,7 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | head -1)" + _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)" return 0 } From 383fa8401d1dffb6d9a325b8df1c3324aba8f3b8 Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Thu, 19 Jan 2017 09:35:47 +0100 Subject: [PATCH 26/58] Remove stray characater fixes issue where the first listed domain would not work --- dnsapi/dns_do.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index b718cd41..b08e6f1e 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -119,7 +119,7 @@ _get_root() { i=1 _dns_do_soap getDomainList - _all_domains="/$(echo "${response}" \ + _all_domains="$(echo "${response}" \ | tr -d "\n\r\t " \ | _egrep_o 'domain]+>[^<]+' \ | sed -e 's/^domain<\/key>]*>//g')" From 9efd40a3662d64c54eeb98858ddbd9bfc589ba2c Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Sun, 19 Feb 2017 21:26:25 +0000 Subject: [PATCH 27/58] use export for headers --- dnsapi/dns_do.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index b08e6f1e..06a86b28 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -100,7 +100,7 @@ _dns_do_soap() { ' # set SOAP headers - _H1="SOAPAction: ${DO_URL}#${func}" + export _H1="SOAPAction: ${DO_URL}#${func}" if ! response="$(_post "${_xml}" "${DO_URL}")"; then _err "Error <$1>" @@ -109,7 +109,7 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)" + export _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)" return 0 } From 3d6a125bdc08e4419697a1913d00d68e0715b5e8 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Sun, 19 Feb 2017 21:34:10 +0000 Subject: [PATCH 28/58] add documentation --- README.md | 1 + dnsapi/README.md | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/README.md b/README.md index b33f8636..c8aeeb0f 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,7 @@ You don't have to do anything manually! 1. Linode.com API 1. FreeDNS (https://freedns.afraid.org/) 1. cyon.ch +1. Domain-Offensive/Resellerinterface/Domainrobot API **More APIs coming soon...** diff --git a/dnsapi/README.md b/dnsapi/README.md index fd88d579..3be5a7fe 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -240,6 +240,7 @@ acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +<<<<<<< HEAD ## 13. Use Alwaysdata domain API First you need to login to your Alwaysdata account to get your API Key. @@ -323,6 +324,19 @@ acme.sh --issue --dns dns_cyon -d example.com -d www.example.com The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. +## 17. Use Domain-Offensive/Resellerinterface/Domainrobot API + +You will need your login credentials (Partner ID+Password) to the Resellerinterface, and export them before you run `acme.sh`: +``` +export DO_PID="KD-1234567" +export DO_PW="cdfkjl3n2" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_do -d example.com -d www.example.com +``` + # Use custom API If your API is not supported yet, you can write your own DNS API. From 2b2b65fe1877725f4c7d400da28847bb31a09fbc Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Sun, 19 Feb 2017 21:42:55 +0000 Subject: [PATCH 29/58] Declare and assign separately to avoid masking return values --- dnsapi/dns_do.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh index 06a86b28..3a2f8f49 100755 --- a/dnsapi/dns_do.sh +++ b/dnsapi/dns_do.sh @@ -109,7 +109,8 @@ _dns_do_soap() { _debug2 "SOAP response $response" # retrieve cookie header - export _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)" + _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)" + export _H2 return 0 } From abf4278d0911be6ff199380c66542b93ecca9824 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Sun, 19 Feb 2017 21:46:33 +0000 Subject: [PATCH 30/58] resolve conflicts --- dnsapi/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 3be5a7fe..7df7142a 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -240,7 +240,6 @@ acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. -<<<<<<< HEAD ## 13. Use Alwaysdata domain API First you need to login to your Alwaysdata account to get your API Key. From e6cd596dc90bf51e3237b420b1715b000b64c3a6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 25 Feb 2017 18:02:23 +0800 Subject: [PATCH 31/58] add debug info --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index e24d4d9a..b989d246 100755 --- a/acme.sh +++ b/acme.sh @@ -4369,7 +4369,7 @@ _precheck() { fi if ! _exists "$OPENSSL_BIN"; then - _err "Please install openssl first." + _err "Please install openssl first. OPENSSL_BIN=$OPENSSL_BIN" _err "We need openssl to generate keys." return 1 fi From 851fedf7512216c1926f3f64e899167ed6f06f56 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 25 Feb 2017 19:08:00 +0800 Subject: [PATCH 32/58] rename OPENSSL_BIN to ACME_OPENSSL_BIN --- acme.sh | 80 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/acme.sh b/acme.sh index b989d246..937da149 100755 --- a/acme.sh +++ b/acme.sh @@ -137,11 +137,11 @@ _printargs() { _dlg_versions() { echo "Diagnosis versions: " - echo "openssl:$OPENSSL_BIN" - if _exists "$OPENSSL_BIN"; then - $OPENSSL_BIN version 2>&1 + echo "openssl:$ACME_OPENSSL_BIN" + if _exists "$ACME_OPENSSL_BIN"; then + $ACME_OPENSSL_BIN version 2>&1 else - echo "$OPENSSL_BIN doesn't exists." + echo "$ACME_OPENSSL_BIN doesn't exists." fi echo "apache:" @@ -780,19 +780,19 @@ _base64() { [ "" ] #urgly if [ "$1" ]; then _debug3 "base64 multiline:'$1'" - $OPENSSL_BIN base64 -e + $ACME_OPENSSL_BIN base64 -e else _debug3 "base64 single line." - $OPENSSL_BIN base64 -e | tr -d '\r\n' + $ACME_OPENSSL_BIN base64 -e | tr -d '\r\n' fi } #Usage: multiline _dbase64() { if [ "$1" ]; then - $OPENSSL_BIN base64 -d -A + $ACME_OPENSSL_BIN base64 -d -A else - $OPENSSL_BIN base64 -d + $ACME_OPENSSL_BIN base64 -d fi } @@ -809,9 +809,9 @@ _digest() { if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then if [ "$outputhex" ]; then - $OPENSSL_BIN dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' ' + $ACME_OPENSSL_BIN dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' ' else - $OPENSSL_BIN dgst -"$alg" -binary | _base64 + $ACME_OPENSSL_BIN dgst -"$alg" -binary | _base64 fi else _err "$alg is not supported yet" @@ -834,9 +834,9 @@ _hmac() { if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then if [ "$outputhex" ]; then - ($OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || $OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' ' + ($ACME_OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || $ACME_OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' ' else - $OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || $OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary + $ACME_OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || $ACME_OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary fi else _err "$alg is not supported yet" @@ -855,7 +855,7 @@ _sign() { return 1 fi - _sign_openssl="$OPENSSL_BIN dgst -sign $keyfile " + _sign_openssl="$ACME_OPENSSL_BIN dgst -sign $keyfile " if [ "$alg" = "sha256" ]; then _sign_openssl="$_sign_openssl -$alg" else @@ -866,7 +866,7 @@ _sign() { if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then $_sign_openssl | _base64 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then - if ! _signedECText="$($_sign_openssl | $OPENSSL_BIN asn1parse -inform DER)"; then + if ! _signedECText="$($_sign_openssl | $ACME_OPENSSL_BIN asn1parse -inform DER)"; then _err "Sign failed: $_sign_openssl" _err "Key file: $keyfile" _err "Key content:$(wc -l <"$keyfile") lises" @@ -929,10 +929,10 @@ _createkey() { if _isEccKey "$length"; then _debug "Using ec name: $eccname" - $OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f" + $ACME_OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f" else _debug "Using RSA: $length" - $OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f" + $ACME_OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f" fi if [ "$?" != "0" ]; then @@ -1019,9 +1019,9 @@ _createcsr() { _csr_cn="$(_idn "$domain")" _debug2 _csr_cn "$_csr_cn" if _contains "$(uname -a)" "MINGW"; then - $OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr" + $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr" else - $OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr" + $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr" fi } @@ -1033,7 +1033,7 @@ _signcsr() { cert="$4" _debug "_signcsr" - _msg="$($OPENSSL_BIN x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)" + _msg="$($ACME_OPENSSL_BIN x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)" _ret="$?" _debug "$_msg" return $_ret @@ -1046,7 +1046,7 @@ _readSubjectFromCSR() { _usage "_readSubjectFromCSR mycsr.csr" return 1 fi - $OPENSSL_BIN req -noout -in "$_csrfile" -subject | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n' + $ACME_OPENSSL_BIN req -noout -in "$_csrfile" -subject | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n' } #_csrfile @@ -1061,7 +1061,7 @@ _readSubjectAltNamesFromCSR() { _csrsubj="$(_readSubjectFromCSR "$_csrfile")" _debug _csrsubj "$_csrsubj" - _dnsAltnames="$($OPENSSL_BIN req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')" + _dnsAltnames="$($ACME_OPENSSL_BIN req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')" _debug _dnsAltnames "$_dnsAltnames" if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then @@ -1082,7 +1082,7 @@ _readKeyLengthFromCSR() { return 1 fi - _outcsr="$($OPENSSL_BIN req -noout -text -in "$_csrfile")" + _outcsr="$($ACME_OPENSSL_BIN req -noout -text -in "$_csrfile")" if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then _debug "ECC CSR" echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' @@ -1136,9 +1136,9 @@ toPkcs() { _initpath "$domain" "$_isEcc" if [ "$pfxPassword" ]; then - $OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword" + $ACME_OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword" else - $OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" + $ACME_OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" fi if [ "$?" = "0" ]; then @@ -1300,7 +1300,7 @@ _calcjwk() { if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then _debug "RSA key" - pub_exp=$($OPENSSL_BIN rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) + pub_exp=$($ACME_OPENSSL_BIN rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) if [ "${#pub_exp}" = "5" ]; then pub_exp=0$pub_exp fi @@ -1309,7 +1309,7 @@ _calcjwk() { e=$(echo "$pub_exp" | _h2b | _base64) _debug3 e "$e" - modulus=$($OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2) + modulus=$($ACME_OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2) _debug3 modulus "$modulus" n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)" _debug3 n "$n" @@ -1322,12 +1322,12 @@ _calcjwk() { JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}' elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then _debug "EC key" - crv="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" + crv="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" _debug3 crv "$crv" if [ -z "$crv" ]; then _debug "Let's try ASN1 OID" - crv_oid="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")" + crv_oid="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")" _debug3 crv_oid "$crv_oid" case "${crv_oid}" in "prime256v1") @@ -1347,15 +1347,15 @@ _calcjwk() { _debug3 crv "$crv" fi - pubi="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)" + pubi="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)" pubi=$(_math "$pubi" + 1) _debug3 pubi "$pubi" - pubj="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)" + pubj="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)" pubj=$(_math "$pubj" - 1) _debug3 pubj "$pubj" - pubtext="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")" + pubtext="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")" _debug3 pubtext "$pubtext" xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)" @@ -1964,7 +1964,7 @@ _starttlsserver() { return 1 fi - __S_OPENSSL="$OPENSSL_BIN s_server -cert $TLS_CERT -key $TLS_KEY " + __S_OPENSSL="$ACME_OPENSSL_BIN s_server -cert $TLS_CERT -key $TLS_KEY " if [ "$opaddr" ]; then __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" else @@ -2143,8 +2143,8 @@ _initpath() { CERT_HOME="$_DEFAULT_CERT_HOME" fi - if [ -z "$OPENSSL_BIN" ]; then - OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" + if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ] ; then + ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" fi if [ -z "$1" ]; then @@ -4368,8 +4368,8 @@ _precheck() { fi fi - if ! _exists "$OPENSSL_BIN"; then - _err "Please install openssl first. OPENSSL_BIN=$OPENSSL_BIN" + if ! _exists "$ACME_OPENSSL_BIN"; then + _err "Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN" _err "We need openssl to generate keys." return 1 fi @@ -4791,9 +4791,9 @@ _processAccountConf() { fi if [ "$_openssl_bin" ]; then - _saveaccountconf "OPENSSL_BIN" "$_openssl_bin" - elif [ "$OPENSSL_BIN" ] && [ "$OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then - _saveaccountconf "OPENSSL_BIN" "$OPENSSL_BIN" + _saveaccountconf "ACME_OPENSSL_BIN" "$_openssl_bin" + elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then + _saveaccountconf "ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN" fi if [ "$_auto_upgrade" ]; then @@ -5219,7 +5219,7 @@ _process() { ;; --openssl-bin) _openssl_bin="$2" - OPENSSL_BIN="$_openssl_bin" + ACME_OPENSSL_BIN="$_openssl_bin" ;; *) _err "Unknown parameter : $1" From 77f1ea40cd02024568b36daebdce55b2f41f565d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 25 Feb 2017 19:12:20 +0800 Subject: [PATCH 33/58] fix format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 937da149..d9f5a34e 100755 --- a/acme.sh +++ b/acme.sh @@ -2143,7 +2143,7 @@ _initpath() { CERT_HOME="$_DEFAULT_CERT_HOME" fi - if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ] ; then + if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ]; then ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" fi From 4410226db1bf51e543ea46d84cdb9d567a93d92a Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 25 Feb 2017 19:31:52 +0800 Subject: [PATCH 34/58] add --toPkcs8 command fix https://github.com/Neilpang/acme.sh/issues/664 --- acme.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/acme.sh b/acme.sh index d9f5a34e..e55265d0 100755 --- a/acme.sh +++ b/acme.sh @@ -1147,6 +1147,27 @@ toPkcs() { } +#domain [isEcc] +toPkcs8() { + domain="$1" + + if [ -z "$domain" ]; then + _usage "Usage: $PROJECT_ENTRY --toPkcs8 -d domain [--ecc]" + return 1 + fi + + _isEcc="$2" + + _initpath "$domain" "$_isEcc" + + $ACME_OPENSSL_BIN pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH" + + if [ "$?" = "0" ]; then + _info "Success, $CERT_PKCS8_PATH" + fi + +} + #[2048] createAccountKey() { _info "Creating account key" @@ -2200,6 +2221,9 @@ _initpath() { if [ -z "$CERT_PFX_PATH" ]; then CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx" fi + if [ -z "$CERT_PKCS8_PATH" ]; then + CERT_PKCS8_PATH="$DOMAIN_PATH/$domain.pkcs8" + fi if [ -z "$TLS_CONF" ]; then TLS_CONF="$DOMAIN_PATH/tls.valdation.conf" @@ -4661,6 +4685,7 @@ Commands: --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically. --cron Run cron job to renew all the certs. --toPkcs Export the certificate and key to a pfx file. + --toPkcs8 Convert to pkcs8 format. --update-account Update account info. --register-account Register account key. --create-account-key Create an account private key, professional use. @@ -4908,6 +4933,9 @@ _process() { --toPkcs) _CMD="toPkcs" ;; + --toPkcs8) + _CMD="toPkcs8" + ;; --createAccountKey | --createaccountkey | -cak | --create-account-key) _CMD="createAccountKey" ;; @@ -5320,6 +5348,9 @@ _process() { toPkcs) toPkcs "$_domain" "$_password" "$_ecc" ;; + toPkcs8) + toPkcs8 "$_domain" "$_ecc" + ;; createAccountKey) createAccountKey "$_accountkeylength" ;; From 342128a457a5d1124b084d419653dd4957c7c2c8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 25 Feb 2017 21:09:06 +0800 Subject: [PATCH 35/58] fix format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index e55265d0..3def82a0 100755 --- a/acme.sh +++ b/acme.sh @@ -4935,7 +4935,7 @@ _process() { ;; --toPkcs8) _CMD="toPkcs8" - ;; + ;; --createAccountKey | --createaccountkey | -cak | --create-account-key) _CMD="createAccountKey" ;; From 4fd63f4e302c381cd167f9e3ed1c4bdc3ba0da2e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 25 Feb 2017 21:22:56 +0800 Subject: [PATCH 36/58] fix ci --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d54faed..7f7e120c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,9 +26,9 @@ install: _old_path="$PATH"; echo "PATH=$PATH"; export PATH=""; - export OPENSSL_BIN="/usr/local/openssl"; + export ACME_OPENSSL_BIN="/usr/local/openssl"; openssl version 2>&1 || true; - $OPENSSL_BIN version 2>&1 || true; + $ACME_OPENSSL_BIN version 2>&1 || true; export PATH="$_old_path"; fi @@ -44,7 +44,7 @@ script: - 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" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; 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 matrix: From 58e4d337e4eaa93de9ca0fdeb661df5ecdb1e50e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 26 Feb 2017 12:07:06 +0800 Subject: [PATCH 37/58] clear the pending authz when issue error fix bug https://github.com/Neilpang/acme.sh/issues/663 --- acme.sh | 59 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/acme.sh b/acme.sh index 3def82a0..f11f1f07 100755 --- a/acme.sh +++ b/acme.sh @@ -2819,6 +2819,7 @@ _on_before_issue() { _on_issue_err() { _chk_post_hook="$1" + _chk_vlist="$2" _debug _on_issue_err if [ "$LOG_FILE" ]; then _err "Please check log file for more details: $LOG_FILE" @@ -2827,10 +2828,6 @@ _on_issue_err() { _err "See: $_DEBUG_WIKI" fi - if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then - _debug "$(_dlg_versions)" - fi - #run the post hook if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" @@ -2841,6 +2838,28 @@ _on_issue_err() { return 1 fi fi + + #trigger the validation to flush the pending authz + if [ "$_chk_vlist" ]; then + ( + _debug2 "_chk_vlist" "$_chk_vlist" + _debug2 "start to deactivate authz" + ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ') + for ventry in $ventries; do + d=$(echo "$ventry" | cut -d "$sep" -f 1) + keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) + uri=$(echo "$ventry" | cut -d "$sep" -f 3) + vtype=$(echo "$ventry" | cut -d "$sep" -f 4) + _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) + __trigger_validaton "$uri" "$keyauthorization" + done + ) + fi + + if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then + _debug "$(_dlg_versions)" + fi + } _on_issue_success() { @@ -3053,6 +3072,16 @@ __get_domain_new_authz() { } +#uri keyAuthorization +__trigger_validaton() { + _debug2 "tigger domain validation." + _t_url="$1" + _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\"}" +} + #webroot, domain domainlist keylength issue() { if [ -z "$2" ]; then @@ -3366,7 +3395,7 @@ issue() { _startserver "$keyauthorization" "$_ncaddr" & if [ "$?" != "0" ]; then _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi serverproc="$!" @@ -3382,7 +3411,7 @@ issue() { BACKUP_NGINX_CONF="" if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi @@ -3417,7 +3446,7 @@ issue() { _err "$d:Can not write token to file : $wellknown_path/$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi @@ -3462,16 +3491,16 @@ issue() { _err "Start tls server error." _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi fi - if ! _send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"; then + if ! __trigger_validaton "$uri" "$keyauthorization"; then _err "$d:Can not get challenge: $response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi @@ -3479,7 +3508,7 @@ issue() { _err "$d:Challenge error: $response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi @@ -3494,7 +3523,7 @@ issue() { _err "$d:Timeout" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err + _on_issue_err "$_post_hook" "$vlist" return 1 fi @@ -3506,7 +3535,7 @@ issue() { _err "$d:Verify error:$response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi _debug2 original "$response" @@ -3541,7 +3570,7 @@ issue() { fi _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi @@ -3551,7 +3580,7 @@ issue() { _err "$d:Verify error:$response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearup - _on_issue_err "$_post_hook" + _on_issue_err "$_post_hook" "$vlist" return 1 fi From c719a61ea70ad1a19337fe0cbb61c1cc0e80540b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 26 Feb 2017 12:15:39 +0800 Subject: [PATCH 38/58] fix format --- acme.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/acme.sh b/acme.sh index f11f1f07..ecc6cb24 100755 --- a/acme.sh +++ b/acme.sh @@ -2842,17 +2842,17 @@ _on_issue_err() { #trigger the validation to flush the pending authz if [ "$_chk_vlist" ]; then ( - _debug2 "_chk_vlist" "$_chk_vlist" - _debug2 "start to deactivate authz" - ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ') - for ventry in $ventries; do - d=$(echo "$ventry" | cut -d "$sep" -f 1) - keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) - uri=$(echo "$ventry" | cut -d "$sep" -f 3) - vtype=$(echo "$ventry" | cut -d "$sep" -f 4) - _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) - __trigger_validaton "$uri" "$keyauthorization" - done + _debug2 "_chk_vlist" "$_chk_vlist" + _debug2 "start to deactivate authz" + ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ') + for ventry in $ventries; do + d=$(echo "$ventry" | cut -d "$sep" -f 1) + keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) + uri=$(echo "$ventry" | cut -d "$sep" -f 3) + vtype=$(echo "$ventry" | cut -d "$sep" -f 4) + _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) + __trigger_validaton "$uri" "$keyauthorization" + done ) fi From 7c2e87549496e18c57253428252f8ff67ed73d53 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 26 Feb 2017 22:20:08 +0800 Subject: [PATCH 39/58] fix https://github.com/Neilpang/acme.sh/issues/675 --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index ecc6cb24..724f0e97 100755 --- a/acme.sh +++ b/acme.sh @@ -5277,6 +5277,7 @@ _process() { --openssl-bin) _openssl_bin="$2" ACME_OPENSSL_BIN="$_openssl_bin" + shift ;; *) _err "Unknown parameter : $1" From 81532f375ea6f9b55e19b07bbe1c106f3d164b19 Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 27 Feb 2017 13:38:29 +0800 Subject: [PATCH 40/58] fix https://github.com/Neilpang/acme.sh/issues/667#issuecomment-282629936 --- acme.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/acme.sh b/acme.sh index 724f0e97..d2bb6c53 100755 --- a/acme.sh +++ b/acme.sh @@ -927,6 +927,15 @@ _createkey() { _debug "Use length $length" + if ! touch "$f" >/dev/null 2>&1; then + _f_path="$(dirname "$f")" + _debug _f_path "$_f_path" + if ! mkdir -p "$_f_path"; then + _err "Can not create path: $_f_path" + return 1 + fi + fi + if _isEccKey "$length"; then _debug "Using ec name: $eccname" $ACME_OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f" From 9b124070286abde59d5afcffe67a6773c3135a78 Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 27 Feb 2017 20:48:48 +0800 Subject: [PATCH 41/58] Wget (#678) * --use-wget force to use wget * fix force wget --- acme.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index d2bb6c53..3e9e4d15 100755 --- a/acme.sh +++ b/acme.sh @@ -1505,7 +1505,7 @@ _post() { _inithttp - if [ "$_ACME_CURL" ]; then + if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then _CURL="$_ACME_CURL" if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " @@ -1572,7 +1572,7 @@ _get() { _inithttp - if [ "$_ACME_CURL" ]; then + if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then _CURL="$_ACME_CURL" if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " @@ -4787,6 +4787,7 @@ Parameters: --listen-v4 Force standalone/tls server to listen at ipv4. --listen-v6 Force standalone/tls server to listen at ipv6. --openssl-bin Specifies a custom openssl bin location. + --use-wget Force to use wget, if you have both curl and wget installed. " } @@ -4865,6 +4866,12 @@ _processAccountConf() { _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE" fi + if [ "$_use_wget" ]; then + _saveaccountconf "ACME_USE_WGET" "$_use_wget" + elif [ "$ACME_USE_WGET" ]; then + _saveaccountconf "ACME_USE_WGET" "$ACME_USE_WGET" + fi + } _process() { @@ -4909,6 +4916,7 @@ _process() { _listen_v6="" _openssl_bin="" _syslog="" + _use_wget="" while [ ${#} -gt 0 ]; do case "${1}" in @@ -5288,6 +5296,10 @@ _process() { ACME_OPENSSL_BIN="$_openssl_bin" shift ;; + --use-wget) + _use_wget="1" + ACME_USE_WGET="1" + ;; *) _err "Unknown parameter : $1" return 1 From 59f7a2f6efb93fd81098146a0f8fe8367c155eb8 Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 27 Feb 2017 20:54:38 +0800 Subject: [PATCH 42/58] Wget (#678) (#679) * --use-wget force to use wget * fix force wget --- acme.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index d2bb6c53..3e9e4d15 100755 --- a/acme.sh +++ b/acme.sh @@ -1505,7 +1505,7 @@ _post() { _inithttp - if [ "$_ACME_CURL" ]; then + if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then _CURL="$_ACME_CURL" if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " @@ -1572,7 +1572,7 @@ _get() { _inithttp - if [ "$_ACME_CURL" ]; then + if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then _CURL="$_ACME_CURL" if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " @@ -4787,6 +4787,7 @@ Parameters: --listen-v4 Force standalone/tls server to listen at ipv4. --listen-v6 Force standalone/tls server to listen at ipv6. --openssl-bin Specifies a custom openssl bin location. + --use-wget Force to use wget, if you have both curl and wget installed. " } @@ -4865,6 +4866,12 @@ _processAccountConf() { _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE" fi + if [ "$_use_wget" ]; then + _saveaccountconf "ACME_USE_WGET" "$_use_wget" + elif [ "$ACME_USE_WGET" ]; then + _saveaccountconf "ACME_USE_WGET" "$ACME_USE_WGET" + fi + } _process() { @@ -4909,6 +4916,7 @@ _process() { _listen_v6="" _openssl_bin="" _syslog="" + _use_wget="" while [ ${#} -gt 0 ]; do case "${1}" in @@ -5288,6 +5296,10 @@ _process() { ACME_OPENSSL_BIN="$_openssl_bin" shift ;; + --use-wget) + _use_wget="1" + ACME_USE_WGET="1" + ;; *) _err "Unknown parameter : $1" return 1 From fab2d9dc6ada42e2ac15abda2bd2f2ea73cf8bfc Mon Sep 17 00:00:00 2001 From: Frederic Crozat Date: Tue, 28 Feb 2017 12:58:04 +0100 Subject: [PATCH 43/58] add API for Gandi LiveDNS (#680) * add API for Gandi LiveDNS * ensure Gandi API key is saved for renewing certificate. * gandi_livedns: use PUT instead of POST for creating DNS record * gandi_livedns: fix formatting * dns_gandi_livedns: fix shellcheck errors --- README.md | 1 + dnsapi/README.md | 12 ++++ dnsapi/dns_gandi_livedns.sh | 120 ++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100755 dnsapi/dns_gandi_livedns.sh diff --git a/README.md b/README.md index c8aeeb0f..fd867015 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ You don't have to do anything manually! 1. FreeDNS (https://freedns.afraid.org/) 1. cyon.ch 1. Domain-Offensive/Resellerinterface/Domainrobot API +1. Gandi LiveDNS API **More APIs coming soon...** diff --git a/dnsapi/README.md b/dnsapi/README.md index 7df7142a..7607257d 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -336,6 +336,18 @@ Ok, let's issue a cert now: acme.sh --issue --dns dns_do -d example.com -d www.example.com ``` +## 18. Use Gandi LiveDNS API + +You will need your Gandi API key (on your Account preferences, go to Security and generate your API key) and export it before you run `acme.sh`: +``` +export GANDI_LIVEDNS_KEY="fdmlfsdklmfdkmqsdfk" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_gandi_livedns -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_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh new file mode 100755 index 00000000..9a170b8b --- /dev/null +++ b/dnsapi/dns_gandi_livedns.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env sh + +# Gandi LiveDNS v5 API +# http://doc.livedns.gandi.net/ +# currently under beta +# +# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable +# +#Author: Frédéric Crozat +#Report Bugs here: https://github.com/fcrozat/acme.sh +# +######## Public functions ##################### + +GANDI_LIVEDNS_API="https://dns.beta.gandi.net/api/v5" + +#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_gandi_livedns_add() { + fulldomain=$1 + txtvalue=$2 + + if [ -z "$GANDI_LIVEDNS_KEY" ]; then + _err "No API key specifed for Gandi LiveDNS." + _err "Create your key and export it as GANDI_LIVEDNS_KEY" + return 1 + fi + + _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + _debug domain "$_domain" + _debug sub_domain "$_sub_domain" + + _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" + + return $? +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_gandi_livedns_rm() { + fulldomain=$1 + txtvalue=$2 + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug fulldomain "$fulldomain" + _debug domain "$_domain" + _debug sub_domain "$_sub_domain" + + _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" "" + + return $? +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + if [ -z "$h" ]; then + #not valid + return 1 + fi + + if ! _gandi_livedns_rest GET "domains/$h"; then + return 1 + fi + + if _contains "$response" '"code": 404'; then + _debug "$h not found" + else + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$h" + return 0 + fi + p="$i" + i=$(_math "$i" + 1) + done + return 1 +} + +_gandi_livedns_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + export _H1="Content-Type: application/json" + export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY" + + if [ "$data" ] || [ "$m" = "DELETE" ]; then + _debug data "$data" + response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")" + else + response="$(_get "$GANDI_LIVEDNS_API/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 9683ffe13a08ffee278c74b7059a190e4084f417 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 28 Feb 2017 20:39:23 +0800 Subject: [PATCH 44/58] minor fix error message --- dnsapi/README.md | 3 ++- dnsapi/dns_gandi_livedns.sh | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 7607257d..18c1ca9f 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -338,7 +338,8 @@ acme.sh --issue --dns dns_do -d example.com -d www.example.com ## 18. Use Gandi LiveDNS API -You will need your Gandi API key (on your Account preferences, go to Security and generate your API key) and export it before you run `acme.sh`: +You must enable the new Gandi LiveDNS API first and the create your api key, See: http://doc.livedns.gandi.net/ + ``` export GANDI_LIVEDNS_KEY="fdmlfsdklmfdkmqsdfk" ``` diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index 9a170b8b..76c4c8a9 100755 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -38,7 +38,6 @@ dns_gandi_livedns_add() { _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" - return $? } #Usage: fulldomain txtvalue @@ -59,7 +58,6 @@ dns_gandi_livedns_rm() { _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" "" - return $? } #################### Private functions below ################################## @@ -82,7 +80,10 @@ _get_root() { return 1 fi - if _contains "$response" '"code": 404'; then + if _contains "$response" '"code": 401'; then + _err "$response" + return 1 + elif _contains "$response" '"code": 404'; then _debug "$h not found" else _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) @@ -104,11 +105,11 @@ _gandi_livedns_rest() { export _H1="Content-Type: application/json" export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY" - if [ "$data" ] || [ "$m" = "DELETE" ]; then + if [ "$m" = "GET" ]; then + response="$(_get "$GANDI_LIVEDNS_API/$ep")" + else _debug data "$data" response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")" - else - response="$(_get "$GANDI_LIVEDNS_API/$ep")" fi if [ "$?" != "0" ]; then From d24a87caf189dbcfaa066645f4076d755afb2a87 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 28 Feb 2017 20:56:11 +0800 Subject: [PATCH 45/58] minor --- dnsapi/dns_gandi_livedns.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index 76c4c8a9..d2521941 100755 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -37,7 +37,7 @@ dns_gandi_livedns_add() { _debug sub_domain "$_sub_domain" _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" - + _contains "$response" '{"message": "Zone Record Created"}' } #Usage: fulldomain txtvalue @@ -71,6 +71,7 @@ _get_root() { 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 From 39a1f1ef644cef0daeb8fffa94cc8d844e207639 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 28 Feb 2017 21:04:33 +0800 Subject: [PATCH 46/58] fix 404 for wget --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 3e9e4d15..a8b3e038 100755 --- a/acme.sh +++ b/acme.sh @@ -1610,7 +1610,7 @@ _get() { fi ret=$? if [ "$_ret" = "8" ]; then - _ret=0 + ret=0 _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later." fi if [ "$ret" != "0" ]; then From f731a4c7041416612ffdf593f9d5763a850c09ac Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 28 Feb 2017 21:06:02 +0800 Subject: [PATCH 47/58] fix 404 for wget --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index a8b3e038..b87df6d6 100755 --- a/acme.sh +++ b/acme.sh @@ -1609,7 +1609,7 @@ _get() { $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url" fi ret=$? - if [ "$_ret" = "8" ]; then + if [ "$ret" = "8" ]; then ret=0 _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later." fi From 810c129ca970b7ae26e1e648f90670b8d91d2beb Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 28 Feb 2017 21:08:20 +0800 Subject: [PATCH 48/58] minor fix error message --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index b87df6d6..a18be6f4 100755 --- a/acme.sh +++ b/acme.sh @@ -1546,7 +1546,7 @@ _post() { _ret="$?" if [ "$_ret" = "8" ]; then _ret=0 - _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later." + _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later." fi if [ "$_ret" != "0" ]; then _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret" @@ -1611,7 +1611,7 @@ _get() { ret=$? if [ "$ret" = "8" ]; then ret=0 - _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later." + _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later." fi if [ "$ret" != "0" ]; then _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret" From 177b57e1c007f185c87e9791c6f31b68ba0b302e Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 28 Feb 2017 21:35:20 +0800 Subject: [PATCH 49/58] fix wget content on 404 error --- acme.sh | 5 +++++ dnsapi/dns_gandi_livedns.sh | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index a18be6f4..7c2875fd 100755 --- a/acme.sh +++ b/acme.sh @@ -1485,6 +1485,11 @@ _inithttp() { fi fi + #from wget 1.14: do not skip body on 404 error + if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help)" "--content-on-error"; then + _ACME_WGET="$_ACME_WGET --content-on-error " + fi + __HTTP_INITIALIZED=1 } diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index d2521941..55218a20 100755 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -36,8 +36,9 @@ dns_gandi_livedns_add() { _debug domain "$_domain" _debug sub_domain "$_sub_domain" - _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" - _contains "$response" '{"message": "Zone Record Created"}' + _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \ + && _contains "$response" '{"message": "Zone Record Created"}' \ + && _info "Add $(__green "success")" } #Usage: fulldomain txtvalue From 58ef6d83852f5bffc6ad00311a139f5f60ec4a2b Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 1 Mar 2017 13:12:29 +0800 Subject: [PATCH 50/58] fix wget error message --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 7c2875fd..0313e0e1 100755 --- a/acme.sh +++ b/acme.sh @@ -1486,7 +1486,7 @@ _inithttp() { fi #from wget 1.14: do not skip body on 404 error - if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help)" "--content-on-error"; then + if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then _ACME_WGET="$_ACME_WGET --content-on-error " fi From 839f18d052c0dff1d6e38c098087e68832788f39 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 1 Mar 2017 19:17:20 +0800 Subject: [PATCH 51/58] fix format --- dnsapi/dns_gandi_livedns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh index 55218a20..41f42980 100755 --- a/dnsapi/dns_gandi_livedns.sh +++ b/dnsapi/dns_gandi_livedns.sh @@ -37,8 +37,8 @@ dns_gandi_livedns_add() { _debug sub_domain "$_sub_domain" _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \ - && _contains "$response" '{"message": "Zone Record Created"}' \ - && _info "Add $(__green "success")" + && _contains "$response" '{"message": "Zone Record Created"}' \ + && _info "Add $(__green "success")" } #Usage: fulldomain txtvalue From 29992f54a35394a9c4d75bb52e75b3c702b2b5b9 Mon Sep 17 00:00:00 2001 From: nytral Date: Wed, 1 Mar 2017 18:28:39 +0100 Subject: [PATCH 52/58] delete support for dns_me --- dnsapi/dns_me.sh | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index f63621d9..59e9cec7 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -78,7 +78,41 @@ dns_me_add() { #fulldomain dns_me_rm() { fulldomain=$1 + txtvalue=$2 + _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" + _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT" + + if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2) + _debug count "$count" + if [ "$count" = "0" ]; then + _info "Don't need to remove." + else + record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | head -n 1) + _debug "record_id" "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if ! _me_rest DELETE "$_domain_id/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" '"success":true' + fi } #################### Private functions below ################################## @@ -130,7 +164,7 @@ _me_rest() { export _H2="x-dnsme-requestDate: $cdate" export _H3="x-dnsme-hmac: $hmac" - if [ "$data" ]; then + if [ "$m" != "GET" ]; then _debug data "$data" response="$(_post "$data" "$ME_Api/$ep" "" "$m")" else From a1e1bfc71b24ee04856230e2a28e27b746652cd6 Mon Sep 17 00:00:00 2001 From: nytral Date: Wed, 1 Mar 2017 19:20:16 +0100 Subject: [PATCH 53/58] removed useless code --- dnsapi/dns_me.sh | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index 59e9cec7..7c4a2ae3 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -91,11 +91,6 @@ dns_me_rm() { _debug "Getting txt records" _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT" - if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then - _err "Error" - return 1 - fi - count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2) _debug count "$count" if [ "$count" = "0" ]; then @@ -107,10 +102,7 @@ dns_me_rm() { _err "Can not get record id to remove." return 1 fi - if ! _me_rest DELETE "$_domain_id/records/$record_id"; then - _err "Delete record error." - return 1 - fi + _me_rest DELETE "$_domain_id/records/$record_id" _contains "$response" '"success":true' fi } From 8d53ec53530cb204bd873e2984df6300e4cb2392 Mon Sep 17 00:00:00 2001 From: nytral Date: Wed, 1 Mar 2017 19:38:02 +0100 Subject: [PATCH 54/58] fixed validation, added LUA while I'm at it --- dnsapi/dns_lua.sh | 31 ++++++++++++++++++++++++++++++- dnsapi/dns_me.sh | 7 +++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh index 828e8012..00c54430 100755 --- a/dnsapi/dns_lua.sh +++ b/dnsapi/dns_lua.sh @@ -81,7 +81,36 @@ dns_lua_add() { #fulldomain dns_lua_rm() { fulldomain=$1 + txtvalue=$2 + _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" + _LUA_rest GET "zones/${_domain_id}/records" + count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ") + _debug count "$count" + if [ "$count" = "0" ]; then + _info "Don't need to remove." + else + 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" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if ! _LUA_rest DELETE "/zones/$_domain_id/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" "$record_id" + fi } #################### Private functions below ################################## @@ -129,7 +158,7 @@ _LUA_rest() { export _H1="Accept: application/json" export _H2="Authorization: Basic $LUA_auth" - if [ "$data" ]; then + if [ "$m" != "GET" ]; then _debug data "$data" response="$(_post "$data" "$LUA_Api/$ep" "" "$m")" else diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index 7c4a2ae3..3393fb75 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -102,8 +102,11 @@ dns_me_rm() { _err "Can not get record id to remove." return 1 fi - _me_rest DELETE "$_domain_id/records/$record_id" - _contains "$response" '"success":true' + if ! _me_rest DELETE "$_domain_id/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" '' fi } From 51f8bec81b7db02217d118c9b6a82b45a2aabcb1 Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Thu, 2 Mar 2017 08:38:25 +0100 Subject: [PATCH 55/58] deploy apache script: fix comment (dovecot/apache) --- deploy/apache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/apache.sh b/deploy/apache.sh index b6c1fbc2..7b34bd5f 100644 --- a/deploy/apache.sh +++ b/deploy/apache.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -#Here is a script to deploy cert to dovecot server. +#Here is a script to deploy cert to apache server. #returns 0 means success, otherwise error. From 5288c54aade8f54e4b4ec50f7c44513bc32cc11b Mon Sep 17 00:00:00 2001 From: Felix Wolfsteller Date: Thu, 2 Mar 2017 08:38:25 +0100 Subject: [PATCH 56/58] deploy apache script: fix comment (dovecot/apache) Closes #690 . --- deploy/apache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/apache.sh b/deploy/apache.sh index b6c1fbc2..7b34bd5f 100644 --- a/deploy/apache.sh +++ b/deploy/apache.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -#Here is a script to deploy cert to dovecot server. +#Here is a script to deploy cert to apache server. #returns 0 means success, otherwise error. From e735d8d4e5a0dd7810dd6cc937fec93c8fdbaad2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 3 Mar 2017 22:03:19 +0800 Subject: [PATCH 57/58] minor --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 0313e0e1..38bed5f6 100755 --- a/acme.sh +++ b/acme.sh @@ -146,7 +146,7 @@ _dlg_versions() { echo "apache:" if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then - _APACHECTL -V 2>&1 + $_APACHECTL -V 2>&1 else echo "apache doesn't exists." fi From 6fb2a1ed39c263407c2d1bb6d8b2e8a0cd9a7d07 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 5 Mar 2017 19:56:06 +0800 Subject: [PATCH 58/58] minor fix comments --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 38bed5f6..d6ed13a4 100755 --- a/acme.sh +++ b/acme.sh @@ -1279,12 +1279,12 @@ _url_replace() { } _time2str() { - #BSD + #Linux if date -u -d@"$1" 2>/dev/null; then return fi - #Linux + #BSD if date -u -r "$1" 2>/dev/null; then return fi