Merge branch 'dev' into patch-3

This commit is contained in:
shar0119 2017-04-26 15:58:58 -07:00 committed by GitHub
commit 90c70fa5bf
10 changed files with 455 additions and 28 deletions

View File

@ -48,5 +48,12 @@ RUN for verb in help \
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
; done ; done
ENTRYPOINT ["/root/.acme.sh/acme.sh", "--config-home", "/acme.sh"] RUN printf "%b" '#!'"/usr/bin/env sh\n \
if [ \"\$1\" = \"daemon\" ]; then \n \
crond; tail -f /dev/null;\n \
else \n \
/root/.acme.sh/acme.sh --config-home /acme.sh \"\$@\"\n \
fi" >/entry.sh && chmod +x /entry.sh
ENTRYPOINT ["/entry.sh"]
CMD ["--help"] CMD ["--help"]

View File

@ -9,7 +9,7 @@
- DOES NOT require `root/sudoer` access. - DOES NOT require `root/sudoer` access.
- Docker friendly - Docker friendly
It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
Wiki: https://github.com/Neilpang/acme.sh/wiki Wiki: https://github.com/Neilpang/acme.sh/wiki
@ -31,6 +31,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
- [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html) - [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) - [archlinux](https://aur.archlinux.org/packages/acme.sh-git/)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) - [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials)
# Tested OS # Tested OS
@ -313,6 +314,7 @@ You don't have to do anything manually!
1. DigitalOcean API (native) 1. DigitalOcean API (native)
1. ClouDNS.net API 1. ClouDNS.net API
1. Infoblox NIOS API (https://www.infoblox.com/) 1. Infoblox NIOS API (https://www.infoblox.com/)
1. VSCALE (https://vscale.io/)
**More APIs coming soon...** **More APIs coming soon...**

35
acme.sh
View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.6.8 VER=2.6.9
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
@ -107,7 +107,7 @@ __green() {
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE" ]; then
printf '\033[1;31;32m' printf '\033[1;31;32m'
fi fi
printf -- "$1" printf -- "%b" "$1"
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE" ]; then
printf '\033[0m' printf '\033[0m'
fi fi
@ -117,7 +117,7 @@ __red() {
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE" ]; then
printf '\033[1;31;40m' printf '\033[1;31;40m'
fi fi
printf -- "$1" printf -- "%b" "$1"
if [ "$__INTERACTIVE" ]; then if [ "$__INTERACTIVE" ]; then
printf '\033[0m' printf '\033[0m'
fi fi
@ -1102,12 +1102,13 @@ _readKeyLengthFromCSR() {
fi fi
_outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")" _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
_debug2 _outcsr "$_outcsr"
if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
_debug "ECC CSR" _debug "ECC CSR"
echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
else else
_debug "RSA CSR" _debug "RSA CSR"
echo "$_outcsr" | _egrep_o "(^ *|^RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1 echo "$_outcsr" | tr "\t" " " | _egrep_o "(^ *|RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
fi fi
} }
@ -1846,6 +1847,24 @@ _saveaccountconf() {
_save_conf "$ACCOUNT_CONF_PATH" "$1" "$2" _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2"
} }
#key value
_saveaccountconf_mutable() {
_save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2"
#remove later
_clearaccountconf "$1"
}
#key
_readaccountconf() {
_read_conf "$ACCOUNT_CONF_PATH" "$1"
}
#key
_readaccountconf_mutable() {
_rac_key="$1"
_readaccountconf "SAVED_$_rac_key"
}
#_clearaccountconf key #_clearaccountconf key
_clearaccountconf() { _clearaccountconf() {
_clear_conf "$ACCOUNT_CONF_PATH" "$1" _clear_conf "$ACCOUNT_CONF_PATH" "$1"
@ -3120,6 +3139,10 @@ issue() {
_usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ " _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
return 1 return 1
fi fi
if [ -z "$1" ]; then
_usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
return 1
fi
_web_roots="$1" _web_roots="$1"
_main_domain="$2" _main_domain="$2"
_alt_domains="$3" _alt_domains="$3"
@ -3860,7 +3883,7 @@ renewAll() {
return "$rc" return "$rc"
else else
_ret="$rc" _ret="$rc"
_err "Error renew $d, Go ahead to next one." _err "Error renew $d."
fi fi
fi fi
done done

View File

@ -468,6 +468,7 @@ acme.sh --issue --dns dns_dynu -d example.com -d www.example.com
The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
# Use custom API # Use custom API
If your API is not supported yet, you can write your own DNS API. If your API is not supported yet, you can write your own DNS API.

View File

@ -88,6 +88,19 @@ _get_root() {
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
_debug "IsTruncated"
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug "NextMarker" "$_nextMarker"
if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then
_debug "Truncated request OK"
i=2
p=1
continue
else
_err "Truncated request error."
fi
fi
#not valid #not valid
return 1 return 1
fi fi
@ -208,6 +221,9 @@ aws_rest() {
_debug _H2 "$_H2" _debug _H2 "$_H2"
url="$AWS_URL/$ep" url="$AWS_URL/$ep"
if [ "$qsr" ]; then
url="$AWS_URL/$ep?$qsr"
fi
if [ "$mtd" = "GET" ]; then if [ "$mtd" = "GET" ]; then
response="$(_get "$url")" response="$(_get "$url")"

View File

@ -14,6 +14,8 @@ dns_cf_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
CF_Email="" CF_Email=""
@ -29,8 +31,8 @@ dns_cf_add() {
fi fi
#save the api key and email to the account conf file. #save the api key and email to the account conf file.
_saveaccountconf CF_Key "$CF_Key" _saveaccountconf_mutable CF_Key "$CF_Key"
_saveaccountconf CF_Email "$CF_Email" _saveaccountconf_mutable CF_Email "$CF_Email"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@ -83,6 +85,17 @@ dns_cf_add() {
dns_cf_rm() { dns_cf_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key=""
CF_Email=""
_err "You don't specify cloudflare api key and email yet."
_err "Please create you key and try again."
return 1
fi
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"

216
dnsapi/dns_dynu.sh Normal file
View File

@ -0,0 +1,216 @@
#!/usr/bin/env sh
#Client ID
#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
#
#Secret
#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
#
#Token
Dynu_Token=""
#
#Endpoint
Dynu_EndPoint="https://api.dynu.com/v1"
#
#Author: Dynu Systems, Inc.
#Report Bugs here: https://github.com/shar0119/acme.sh
#
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynu_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
Dynu_ClientId=""
Dynu_Secret=""
_err "Dynu client id and secret is not specified."
_err "Please create you API client id and secret and try again."
return 1
fi
#save the client id and secret to the account conf file.
_saveaccountconf Dynu_ClientId "$Dynu_ClientId"
_saveaccountconf Dynu_Secret "$Dynu_Secret"
if [ -z "$Dynu_Token" ]; then
_info "Getting Dynu token."
if ! _dynu_authentication; then
_err "Can not get token."
fi
fi
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "Invalid domain."
return 1
fi
_debug _node "$_node"
_debug _domain_name "$_domain_name"
_info "Creating TXT record."
if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then
return 1
fi
if ! _contains "$response" "text_data"; then
_err "Could not add TXT record."
return 1
fi
return 0
}
#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynu_rm() {
fulldomain=$1
txtvalue=$2
if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
Dynu_ClientId=""
Dynu_Secret=""
_err "Dynu client id and secret is not specified."
_err "Please create you API client id and secret and try again."
return 1
fi
#save the client id and secret to the account conf file.
_saveaccountconf Dynu_ClientId "$Dynu_ClientId"
_saveaccountconf Dynu_Secret "$Dynu_Secret"
if [ -z "$Dynu_Token" ]; then
_info "Getting Dynu token."
if ! _dynu_authentication; then
_err "Can not get token."
fi
fi
_debug "Detect root zone."
if ! _get_root "$fulldomain"; then
_err "Invalid domain."
return 1
fi
_debug _node "$_node"
_debug _domain_name "$_domain_name"
_info "Checking for TXT record."
if ! _get_recordid "$fulldomain" "$txtvalue"; then
_err "Could not get TXT record id."
return 1
fi
if [ "$_dns_record_id" = "" ]; then
_err "TXT record not found."
return 1
fi
_info "Removing TXT record."
if ! _delete_txt_record "$_dns_record_id"; then
_err "Could not remove TXT record $_dns_record_id."
fi
return 0
}
######## Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _node=_acme-challenge.www
# _domain_name=domain.com
_get_root() {
domain=$1
if ! _dynu_rest GET "dns/getroot/$domain"; then
return 1
fi
if ! _contains "$response" "domain_name"; then
_debug "Domain name not found."
return 1
fi
_domain_name=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
_node=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 3 | cut -d : -f 2 | cut -d '"' -f 2)
return 0
}
_get_recordid() {
fulldomain=$1
txtvalue=$2
if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then
return 1
fi
if ! _contains "$response" "$txtvalue"; then
_dns_record_id=0
return 0
fi
_dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | grep -Po '"id":\K[0-9]+')
return 0
}
_delete_txt_record() {
_dns_record_id=$1
if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then
return 1
fi
if ! _contains "$response" "true"; then
return 1
fi
return 0
}
_dynu_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Authorization: Bearer $Dynu_Token"
export _H2="Content-Type: application/json"
if [ "$data" ]; then
_debug data "$data"
response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")"
else
_info "Getting $Dynu_EndPoint/$ep"
response="$(_get "$Dynu_EndPoint/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_dynu_authentication() {
realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)"
export _H1="Authorization: Basic $realm"
export _H2="Content-Type: application/json"
response="$(_get "$Dynu_EndPoint/oauth2/token")"
if [ "$?" != "0" ]; then
_err "Authentication failed."
return 1
fi
if _contains "$response" "accessToken"; then
Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2)
fi
if _contains "$Dynu_Token" "null"; then
Dynu_Token=""
fi
_debug2 response "$response"
return 0
}

149
dnsapi/dns_vscale.sh Executable file
View File

@ -0,0 +1,149 @@
#!/usr/bin/env sh
#This is the vscale.io api wrapper for acme.sh
#
#Author: Alex Loban
#Report Bugs here: https://github.com/LAV45/acme.sh
#VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
VSCALE_API_URL="https://api.vscale.io/v1"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_vscale_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$VSCALE_API_KEY" ]; then
VSCALE_API_KEY=""
_err "You didn't specify the VSCALE api key yet."
_err "Please create you key and try again."
return 1
fi
_saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}"
if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then
response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2)
if [ -z "$response" ]; then
_info "txt record updated success."
return 0
fi
fi
return 1
}
#fulldomain txtvalue
dns_vscale_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"
_vscale_rest GET "domains/$_domain_id/records/"
if [ -n "$response" ]; then
record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"")
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then
_info "txt record deleted success."
return 0
fi
_debug response "$response"
return 1
fi
return 1
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=12345
_get_root() {
domain=$1
i=2
p=1
if _vscale_rest GET "domains/"; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")"
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
fi
return 1
}
#method uri qstr data
_vscale_rest() {
mtd="$1"
ep="$2"
data="$3"
_debug mtd "$mtd"
_debug ep "$ep"
export _H1="Accept: application/json"
export _H2="Content-Type: application/json"
export _H3="X-Token: ${VSCALE_API_KEY}"
if [ "$mtd" != "GET" ]; then
# both POST and DELETE.
_debug data "$data"
response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")"
else
response="$(_get "$VSCALE_API_URL/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}