diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 4abbb7ab..f7d4d1d7 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,4 +1,6 @@
Steps to reproduce
------------------
-
Debug log
-----------------
diff --git a/.travis.yml b/.travis.yml
index 8d6d30ad..7f7e120c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,14 +26,13 @@ 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
script:
- - echo "TEST_LOCAL=$TEST_LOCAL"
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
- command -V openssl && openssl version
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi
@@ -44,8 +43,8 @@ script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi
- cd ..
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
- - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
- - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi
+ - 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" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
matrix:
diff --git a/README.md b/README.md
index c6362ed7..fd867015 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,9 @@ https://github.com/Neilpang/acmetest
- Webroot mode
- Standalone mode
- Apache mode
+- Nginx mode ( Beta )
- DNS mode
+- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
# 1. How to install
@@ -146,7 +148,7 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT**
**Apache** example:
```bash
-acme.sh --installcert -d example.com \
+acme.sh --install-cert -d example.com \
--certpath /path/to/certfile/in/apache/cert.pem \
--keypath /path/to/keyfile/in/apache/key.pem \
--fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \
@@ -155,7 +157,7 @@ acme.sh --installcert -d example.com \
**Nginx** example:
```bash
-acme.sh --installcert -d example.com \
+acme.sh --install-cert -d example.com \
--keypath /path/to/keyfile/in/nginx/key.pem \
--fullchainpath /path/to/fullchain/nginx/cert.pem \
--reloadcmd "service nginx force-reload"
@@ -214,8 +216,27 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
+# 7. Use Nginx mode
-# 7. Use DNS mode:
+**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
+
+If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
+
+Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
+
+Just set string "nginx" as the second argument.
+
+It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
+
+So, the config is not changed.
+
+```
+acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
+```
+
+More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
+
+# 8. Use DNS mode:
Support the `dns-01` challenge.
@@ -246,7 +267,7 @@ acme.sh --renew -d example.com
Ok, it's finished.
-# 8. Automatic DNS API integration
+# 9. Automatic DNS API integration
If your DNS provider supports API access, we can use that API to automatically issue the certs.
@@ -271,6 +292,9 @@ You don't have to do anything manually!
1. Alwaysdata.com API
1. Linode.com API
1. FreeDNS (https://freedns.afraid.org/)
+1. cyon.ch
+1. Domain-Offensive/Resellerinterface/Domainrobot API
+1. Gandi LiveDNS API
**More APIs coming soon...**
@@ -279,7 +303,7 @@ If your DNS provider is not on the supported list above, you can write your own
For more details: [How to use DNS API](dnsapi)
-# 9. Issue ECC certificates
+# 10. Issue ECC certificates
`Let's Encrypt` can now issue **ECDSA** certificates.
@@ -310,7 +334,7 @@ Valid values are:
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
-# 10. How to renew the issued certs
+# 11. How to renew the issued certs
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
@@ -327,7 +351,7 @@ acme.sh --renew -d example.com --force --ecc
```
-# 11. How to upgrade `acme.sh`
+# 12. How to upgrade `acme.sh`
acme.sh is in constant development, so it's strongly recommended to use the latest code.
@@ -352,26 +376,26 @@ acme.sh --upgrade --auto-upgrade 0
```
-# 12. Issue a cert from an existing CSR
+# 13. Issue a cert from an existing CSR
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
-# Under the Hood
+# 14. Under the Hood
Speak ACME language using shell, directly to "Let's Encrypt".
TODO:
-# Acknowledgments
+# 15. Acknowledgments
1. Acme-tiny: https://github.com/diafygi/acme-tiny
2. ACME protocol: https://github.com/ietf-wg-acme/acme
3. Certbot: https://github.com/certbot/certbot
-# License & Others
+# 16. License & Others
License is GPLv3
@@ -380,8 +404,9 @@ Please Star and Fork me.
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
-# Donate
-
-1. PayPal: donate@acme.sh
+# 17. Donate
+Your donation makes **acme.sh** better:
+1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
+
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)
diff --git a/acme.sh b/acme.sh
index df4a0b4a..0313e0e1 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env sh
-VER=2.6.6
+VER=2.6.7
PROJECT_NAME="acme.sh"
@@ -41,8 +41,14 @@ NO_VALUE="no"
W_TLS="tls"
+MODE_STATELESS="stateless"
+
STATE_VERIFIED="verified_ok"
+NGINX="nginx:"
+NGINX_START="#ACME_NGINX_START"
+NGINX_END="#ACME_NGINX_END"
+
BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
END_CSR="-----END CERTIFICATE REQUEST-----"
@@ -59,8 +65,39 @@ LOG_LEVEL_2=2
LOG_LEVEL_3=3
DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
+DEBUG_LEVEL_1=1
+DEBUG_LEVEL_2=2
+DEBUG_LEVEL_3=3
+DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
+DEBUG_LEVEL_NONE=0
+
+HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)"
+
+SYSLOG_ERROR="user.error"
+SYSLOG_INFO="user.info"
+SYSLOG_DEBUG="user.debug"
+
+#error
+SYSLOG_LEVEL_ERROR=3
+#info
+SYSLOG_LEVEL_INFO=6
+#debug
+SYSLOG_LEVEL_DEBUG=7
+#debug2
+SYSLOG_LEVEL_DEBUG_2=8
+#debug3
+SYSLOG_LEVEL_DEBUG_3=9
+
+SYSLOG_LEVEL_DEFAULT=$SYSLOG_LEVEL_ERROR
+#none
+SYSLOG_LEVEL_NONE=0
+
_DEBUG_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh"
+_PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations"
+
+_STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode"
+
__INTERACTIVE=""
if [ -t 1 ]; then
__INTERACTIVE="1"
@@ -100,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:"
@@ -122,6 +159,16 @@ _dlg_versions() {
fi
}
+#class
+_syslog() {
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then
+ return
+ fi
+ _logclass="$1"
+ shift
+ logger -i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1
+}
+
_log() {
[ -z "$LOG_FILE" ] && return
_printargs "$@" >>"$LOG_FILE"
@@ -129,10 +176,14 @@ _log() {
_info() {
_log "$@"
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_INFO" ]; then
+ _syslog "$SYSLOG_INFO" "$@"
+ fi
_printargs "$@"
}
_err() {
+ _syslog "$SYSLOG_ERROR" "$@"
_log "$@"
if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
printf -- "%s" "[$(date)] " >&2
@@ -152,30 +203,99 @@ _usage() {
}
_debug() {
- if [ -z "$LOG_LEVEL" ] || [ "$LOG_LEVEL" -ge "$LOG_LEVEL_1" ]; then
+ if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
_log "$@"
fi
- if [ -z "$DEBUG" ]; then
- return
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
+ _syslog "$SYSLOG_DEBUG" "$@"
+ fi
+ if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
+ _printargs "$@" >&2
+ fi
+}
+
+#output the sensitive messages
+_secure_debug() {
+ if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
+ if [ "$OUTPUT_INSECURE" = "1" ]; then
+ _log "$@"
+ else
+ _log "$1" "$HIDDEN_VALUE"
+ fi
+ fi
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
+ _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
+ fi
+ if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
+ if [ "$OUTPUT_INSECURE" = "1" ]; then
+ _printargs "$@" >&2
+ else
+ _printargs "$1" "$HIDDEN_VALUE" >&2
+ fi
fi
- _printargs "$@" >&2
}
_debug2() {
- if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_2" ]; then
+ if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
_log "$@"
fi
- if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
- _debug "$@"
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
+ _syslog "$SYSLOG_DEBUG" "$@"
+ fi
+ if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
+ _printargs "$@" >&2
+ fi
+}
+
+_secure_debug2() {
+ if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
+ if [ "$OUTPUT_INSECURE" = "1" ]; then
+ _log "$@"
+ else
+ _log "$1" "$HIDDEN_VALUE"
+ fi
+ fi
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
+ _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
+ fi
+ if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
+ if [ "$OUTPUT_INSECURE" = "1" ]; then
+ _printargs "$@" >&2
+ else
+ _printargs "$1" "$HIDDEN_VALUE" >&2
+ fi
fi
}
_debug3() {
- if [ "$LOG_LEVEL" ] && [ "$LOG_LEVEL" -ge "$LOG_LEVEL_3" ]; then
+ if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
_log "$@"
fi
- if [ "$DEBUG" ] && [ "$DEBUG" -ge "3" ]; then
- _debug "$@"
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
+ _syslog "$SYSLOG_DEBUG" "$@"
+ fi
+ if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
+ _printargs "$@" >&2
+ fi
+}
+
+_secure_debug3() {
+ if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
+ if [ "$OUTPUT_INSECURE" = "1" ]; then
+ _log "$@"
+ else
+ _log "$1" "$HIDDEN_VALUE"
+ fi
+ fi
+ if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
+ _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
+ fi
+ if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
+ if [ "$OUTPUT_INSECURE" = "1" ]; then
+ _printargs "$@" >&2
+ else
+ _printargs "$1" "$HIDDEN_VALUE" >&2
+ fi
fi
}
@@ -358,8 +478,16 @@ _ascii_hex() {
#input:"abc"
#output: " 61 62 63"
_hex_dump() {
- #in wired some system, the od command is missing.
- if ! od -A n -v -t x1 | tr -d "\r\t" | tr -s " " | sed "s/ $//" | tr -d "\n" 2>/dev/null; then
+ if _exists od; then
+ od -A n -v -t x1 | tr -s " " | sed 's/ $//' | tr -d "\r\t\n"
+ elif _exists hexdump; then
+ _debug3 "using hexdump"
+ hexdump -v -e '/1 ""' -e '/1 " %02x" ""'
+ elif _exists xxd; then
+ _debug3 "using xxd"
+ xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " "
+ else
+ _debug3 "using _ascii_hex"
str=$(cat)
_ascii_hex "$str"
fi
@@ -652,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
}
@@ -681,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"
@@ -706,9 +834,9 @@ _hmac() {
if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
if [ "$outputhex" ]; then
- $OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" | 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
+ $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"
@@ -727,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
@@ -738,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"
@@ -799,12 +927,21 @@ _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"
- $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
@@ -890,7 +1027,11 @@ _createcsr() {
_csr_cn="$(_idn "$domain")"
_debug2 _csr_cn "$_csr_cn"
- $OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ if _contains "$(uname -a)" "MINGW"; then
+ $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ else
+ $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
+ fi
}
#_signcsr key csr conf cert
@@ -901,7 +1042,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
@@ -914,7 +1055,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
@@ -929,7 +1070,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
@@ -950,13 +1091,13 @@ _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 ' '
else
_debug "RSA CSR"
- echo "$_outcsr" | _egrep_o "^ *Public-Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
+ echo "$_outcsr" | _egrep_o "(^ *|^RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
fi
}
@@ -1004,9 +1145,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
@@ -1015,6 +1156,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"
@@ -1168,7 +1330,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
@@ -1177,7 +1339,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"
@@ -1190,12 +1352,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")
@@ -1215,15 +1377,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)"
@@ -1263,6 +1425,10 @@ _time() {
date -u "+%s"
}
+_utc_date() {
+ date -u "+%Y-%m-%d %H:%M:%S"
+}
+
_mktemp() {
if _exists mktemp; then
if mktemp 2>/dev/null; then
@@ -1319,6 +1485,11 @@ _inithttp() {
fi
fi
+ #from wget 1.14: do not skip body on 404 error
+ if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then
+ _ACME_WGET="$_ACME_WGET --content-on-error "
+ fi
+
__HTTP_INITIALIZED=1
}
@@ -1339,7 +1510,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 "
@@ -1380,7 +1551,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"
@@ -1406,7 +1577,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 "
@@ -1443,9 +1614,9 @@ _get() {
$_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
fi
ret=$?
- if [ "$_ret" = "8" ]; then
- _ret=0
- _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later."
+ if [ "$ret" = "8" ]; then
+ ret=0
+ _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"
@@ -1488,62 +1659,75 @@ _send_signed_request() {
payload64=$(printf "%s" "$payload" | _base64 | _url_replace)
_debug3 payload64 "$payload64"
- if [ -z "$_CACHED_NONCE" ]; then
- _debug2 "Get nonce."
- nonceurl="$API/directory"
- _headers="$(_get "$nonceurl" "onlyheader")"
+ MAX_REQUEST_RETRY_TIMES=5
+ _request_retry_times=0
+ while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
+ _debug3 _request_retry_times "$_request_retry_times"
+ if [ -z "$_CACHED_NONCE" ]; then
+ _debug2 "Get nonce."
+ nonceurl="$API/directory"
+ _headers="$(_get "$nonceurl" "onlyheader")"
- if [ "$?" != "0" ]; then
- _err "Can not connect to $nonceurl to get nonce."
+ if [ "$?" != "0" ]; then
+ _err "Can not connect to $nonceurl to get nonce."
+ return 1
+ fi
+
+ _debug2 _headers "$_headers"
+
+ _CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
+ _debug2 _CACHED_NONCE "$_CACHED_NONCE"
+ else
+ _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
+ fi
+ nonce="$_CACHED_NONCE"
+ _debug2 nonce "$nonce"
+
+ protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2"
+ _debug3 protected "$protected"
+
+ protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
+ _debug3 protected64 "$protected64"
+
+ if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
+ _err "Sign request failed."
return 1
fi
+ _debug3 _sig_t "$_sig_t"
- _debug2 _headers "$_headers"
+ sig="$(printf "%s" "$_sig_t" | _url_replace)"
+ _debug3 sig "$sig"
- _CACHED_NONCE="$(echo "$_headers" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
- _debug2 _CACHED_NONCE "$_CACHED_NONCE"
- else
- _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
- fi
- nonce="$_CACHED_NONCE"
- _debug2 nonce "$nonce"
+ body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
+ _debug3 body "$body"
- protected="$JWK_HEADERPLACE_PART1$nonce$JWK_HEADERPLACE_PART2"
- _debug3 protected "$protected"
+ response="$(_post "$body" "$url" "$needbase64")"
+ _CACHED_NONCE=""
- protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
- _debug3 protected64 "$protected64"
+ if [ "$?" != "0" ]; then
+ _err "Can not post to $url"
+ return 1
+ fi
+ _debug2 original "$response"
+ response="$(echo "$response" | _normalizeJson)"
- if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
- _err "Sign request failed."
- return 1
- fi
- _debug3 _sig_t "$_sig_t"
+ responseHeaders="$(cat "$HTTP_HEADER")"
- sig="$(printf "%s" "$_sig_t" | _url_replace)"
- _debug3 sig "$sig"
+ _debug2 responseHeaders "$responseHeaders"
+ _debug2 response "$response"
+ code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
+ _debug code "$code"
- body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
- _debug3 body "$body"
+ _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
- response="$(_post "$body" "$url" "$needbase64")"
- _CACHED_NONCE=""
- if [ "$?" != "0" ]; then
- _err "Can not post to $url"
- return 1
- fi
- _debug2 original "$response"
-
- response="$(echo "$response" | _normalizeJson)"
-
- responseHeaders="$(cat "$HTTP_HEADER")"
-
- _debug2 responseHeaders "$responseHeaders"
- _debug2 response "$response"
- code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
- _debug code "$code"
-
- _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
+ if _contains "$response" "JWS has invalid anti-replay nonce"; then
+ _info "It seems the CA server is busy now, let's wait and retry."
+ _request_retry_times=$(_math "$_request_retry_times" + 1)
+ _sleep 5
+ continue
+ fi
+ break
+ done
}
@@ -1568,20 +1752,20 @@ _setopt() {
__val="$(echo "$__val" | sed 's/&/\\&/g')"
fi
text="$(cat "$__conf")"
- echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
+ printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
elif grep -n "^#$__opt$__sep" "$__conf" >/dev/null; then
if _contains "$__val" "&"; then
__val="$(echo "$__val" | sed 's/&/\\&/g')"
fi
text="$(cat "$__conf")"
- echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
+ printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
else
_debug3 APP
echo "$__opt$__sep$__val$__end" >>"$__conf"
fi
- _debug2 "$(grep -n "^$__opt$__sep" "$__conf")"
+ _debug3 "$(grep -n "^$__opt$__sep" "$__conf")"
}
#_save_conf file key value
@@ -1684,6 +1868,14 @@ _startserver() {
_NC="$_NC -6"
fi
+ if [ "$Le_Listen_V4$Le_Listen_V6$ncaddr" ]; then
+ if ! _contains "$nchelp" "-4"; then
+ _err "The nc doesn't support '-4', '-6' or local-address, please install 'netcat-openbsd' and try again."
+ _err "See $(__green $_PREPARE_LINK)"
+ return 1
+ fi
+ fi
+
if echo "$nchelp" | grep "\-q[ ,]" >/dev/null; then
_NC="$_NC -q 1 -l $ncaddr"
else
@@ -1807,7 +1999,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
@@ -1986,8 +2178,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
@@ -2013,6 +2205,10 @@ _initpath() {
_debug DOMAIN_PATH "$DOMAIN_PATH"
fi
+ if [ -z "$DOMAIN_BACKUP_PATH" ]; then
+ DOMAIN_BACKUP_PATH="$DOMAIN_PATH/backup"
+ fi
+
if [ -z "$DOMAIN_CONF" ]; then
DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
fi
@@ -2039,6 +2235,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"
@@ -2227,10 +2426,228 @@ Allow from all
return 0
}
+#find the real nginx conf file
+#backup
+#set the nginx conf
+#returns the real nginx conf file
+_setNginx() {
+ _d="$1"
+ _croot="$2"
+ _thumbpt="$3"
+ if ! _exists "nginx"; then
+ _err "nginx command is not found."
+ return 1
+ fi
+ FOUND_REAL_NGINX_CONF=""
+ FOUND_REAL_NGINX_CONF_LN=""
+ BACKUP_NGINX_CONF=""
+ _debug _croot "$_croot"
+ _start_f="$(echo "$_croot" | cut -d : -f 2)"
+ _debug _start_f "$_start_f"
+ if [ -z "$_start_f" ]; then
+ _debug "find start conf from nginx command"
+ if [ -z "$NGINX_CONF" ]; then
+ NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
+ _debug NGINX_CONF "$NGINX_CONF"
+ NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
+ _debug NGINX_CONF "$NGINX_CONF"
+ if [ ! -f "$NGINX_CONF" ]; then
+ _err "'$NGINX_CONF' doesn't exist."
+ NGINX_CONF=""
+ return 1
+ fi
+ _debug "Found nginx conf file:$NGINX_CONF"
+ fi
+ _start_f="$NGINX_CONF"
+ fi
+ _debug "Start detect nginx conf for $_d from:$_start_f"
+ if ! _checkConf "$_d" "$_start_f"; then
+ "Can not find conf file for domain $d"
+ return 1
+ fi
+ _info "Found conf file: $FOUND_REAL_NGINX_CONF"
+
+ _ln=$FOUND_REAL_NGINX_CONF_LN
+ _debug "_ln" "$_ln"
+
+ _lnn=$(_math $_ln + 1)
+ _debug _lnn "$_lnn"
+ _start_tag="$(sed -n "$_lnn,${_lnn}p" "$FOUND_REAL_NGINX_CONF")"
+ _debug "_start_tag" "$_start_tag"
+ if [ "$_start_tag" = "$NGINX_START" ]; then
+ _info "The domain $_d is already configured, skip"
+ FOUND_REAL_NGINX_CONF=""
+ return 0
+ fi
+
+ mkdir -p "$DOMAIN_BACKUP_PATH"
+ _backup_conf="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
+ _debug _backup_conf "$_backup_conf"
+ BACKUP_NGINX_CONF="$_backup_conf"
+ _info "Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
+ if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
+ _err "backup error."
+ FOUND_REAL_NGINX_CONF=""
+ return 1
+ fi
+
+ _info "Check the nginx conf before setting up."
+ if ! _exec "nginx -t" >/dev/null; then
+ _exec_err
+ return 1
+ fi
+
+ _info "OK, Set up nginx config file"
+
+ if ! sed -n "1,${_ln}p" "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"; then
+ cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
+ _err "write nginx conf error, but don't worry, the file is restored to the original version."
+ return 1
+ fi
+
+ echo "$NGINX_START
+location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
+ default_type text/plain;
+ return 200 \"\$1.$_thumbpt\";
+}
+#NGINX_START
+" >>"$FOUND_REAL_NGINX_CONF"
+
+ if ! sed -n "${_lnn},99999p" "$_backup_conf" >>"$FOUND_REAL_NGINX_CONF"; then
+ cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
+ _err "write nginx conf error, but don't worry, the file is restored."
+ return 1
+ fi
+
+ _info "nginx conf is done, let's check it again."
+ if ! _exec "nginx -t" >/dev/null; then
+ _exec_err
+ _err "It seems that nginx conf was broken, let's restore."
+ cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
+ return 1
+ fi
+
+ _info "Reload nginx"
+ if ! _exec "nginx -s reload" >/dev/null; then
+ _exec_err
+ _err "It seems that nginx reload error, let's restore."
+ cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
+ return 1
+ fi
+
+ return 0
+}
+
+#d , conf
+_checkConf() {
+ _d="$1"
+ _c_file="$2"
+ _debug "Start _checkConf from:$_c_file"
+ if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
+ _debug "wildcard"
+ for _w_f in $2; do
+ if _checkConf "$1" "$_w_f"; then
+ return 0
+ fi
+ done
+ #not found
+ return 1
+ elif [ -f "$2" ]; then
+ _debug "single"
+ if _isRealNginxConf "$1" "$2"; then
+ _debug "$2 is found."
+ FOUND_REAL_NGINX_CONF="$2"
+ return 0
+ fi
+ if grep "^ *include *.*;" "$2" >/dev/null; then
+ _debug "Try include files"
+ for included in $(grep "^ *include *.*;" "$2" | sed "s/include //" | tr -d " ;"); do
+ _debug "check included $included"
+ if _checkConf "$1" "$included"; then
+ return 0
+ fi
+ done
+ fi
+ return 1
+ else
+ _debug "$2 not found."
+ return 1
+ fi
+ return 1
+}
+
+#d , conf
+_isRealNginxConf() {
+ _debug "_isRealNginxConf $1 $2"
+ if [ -f "$2" ]; then
+ for _fln in $(grep -n "^ *server_name.* $1" "$2" | cut -d : -f 1); do
+ _debug _fln "$_fln"
+ if [ "$_fln" ]; then
+ _start=$(cat "$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1)
+ _debug "_start" "$_start"
+ _start_n=$(echo "$_start" | cut -d : -f 1)
+ _start_nn=$(_math $_start_n + 1)
+ _debug "_start_n" "$_start_n"
+ _debug "_start_nn" "$_start_nn"
+
+ _left="$(sed -n "${_start_nn},99999p" "$2")"
+ _debug2 _left "$_left"
+ if echo "$_left" | grep -n "^ *server *{" >/dev/null; then
+ _end=$(echo "$_left" | grep -n "^ *server *{" | _head_n 1)
+ _debug "_end" "$_end"
+ _end_n=$(echo "$_end" | cut -d : -f 1)
+ _debug "_end_n" "$_end_n"
+ _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
+ else
+ _seg_n="$_left"
+ fi
+
+ _debug "_seg_n" "$_seg_n"
+
+ if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ]; then
+ _debug "ssl on, skip"
+ return 1
+ fi
+ FOUND_REAL_NGINX_CONF_LN=$_fln
+ return 0
+ fi
+ done
+ fi
+ return 1
+}
+
+#restore all the nginx conf
+_restoreNginx() {
+ if [ -z "$NGINX_RESTORE_VLIST" ]; then
+ _debug "No need to restore nginx, skip."
+ return
+ fi
+ _debug "_restoreNginx"
+ _debug "NGINX_RESTORE_VLIST" "$NGINX_RESTORE_VLIST"
+
+ for ng_entry in $(echo "$NGINX_RESTORE_VLIST" | tr "$dvsep" ' '); do
+ _debug "ng_entry" "$ng_entry"
+ _nd=$(echo "$ng_entry" | cut -d "$sep" -f 1)
+ _ngconf=$(echo "$ng_entry" | cut -d "$sep" -f 2)
+ _ngbackupconf=$(echo "$ng_entry" | cut -d "$sep" -f 3)
+ _info "Restoring from $_ngbackupconf to $_ngconf"
+ cat "$_ngbackupconf" >"$_ngconf"
+ done
+
+ _info "Reload nginx"
+ if ! _exec "nginx -s reload" >/dev/null; then
+ _exec_err
+ _err "It seems that nginx reload error, please report bug."
+ return 1
+ fi
+ return 0
+}
+
_clearup() {
_stopserver "$serverproc"
serverproc=""
_restoreApache
+ _restoreNginx
_clearupdns
if [ -z "$DEBUG" ]; then
rm -f "$TLS_CONF"
@@ -2256,7 +2673,7 @@ _clearupdns() {
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
- _info "$d is already verified, skip $vtype."
+ _debug "$d is already verified, skip $vtype."
continue
fi
@@ -2328,34 +2745,39 @@ _clearupwebbroot() {
}
_on_before_issue() {
+ _chk_web_roots="$1"
+ _chk_main_domain="$2"
+ _chk_alt_domains="$3"
+ _chk_pre_hook="$4"
+ _chk_local_addr="$5"
_debug _on_before_issue
#run pre hook
- if [ "$Le_PreHook" ]; then
- _info "Run pre hook:'$Le_PreHook'"
+ if [ "$_chk_pre_hook" ]; then
+ _info "Run pre hook:'$_chk_pre_hook'"
if ! (
- cd "$DOMAIN_PATH" && eval "$Le_PreHook"
+ cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
); then
_err "Error when run pre hook."
return 1
fi
fi
- if _hasfield "$Le_Webroot" "$NO_VALUE"; then
+ if _hasfield "$_chk_web_roots" "$NO_VALUE"; then
if ! _exists "nc"; then
_err "Please install netcat(nc) tools first."
return 1
fi
fi
- _debug Le_LocalAddress "$Le_LocalAddress"
+ _debug Le_LocalAddress "$_chk_local_addr"
- alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ')
+ alldomains=$(echo "$_chk_main_domain,$_chk_alt_domains" | tr ',' ' ')
_index=1
_currentRoot=""
_addrIndex=1
for d in $alldomains; do
_debug "Check for domain" "$d"
- _currentRoot="$(_getfield "$Le_Webroot" $_index)"
+ _currentRoot="$(_getfield "$_chk_web_roots" $_index)"
_debug "_currentRoot" "$_currentRoot"
_index=$(_math $_index + 1)
_checkport=""
@@ -2379,7 +2801,7 @@ _on_before_issue() {
if [ "$_checkport" ]; then
_debug _checkport "$_checkport"
- _checkaddr="$(_getfield "$Le_LocalAddress" $_addrIndex)"
+ _checkaddr="$(_getfield "$_chk_local_addr" $_addrIndex)"
_debug _checkaddr "$_checkaddr"
_addrIndex="$(_math $_addrIndex + 1)"
@@ -2398,7 +2820,7 @@ _on_before_issue() {
fi
done
- if _hasfield "$Le_Webroot" "apache"; then
+ if _hasfield "$_chk_web_roots" "apache"; then
if ! _setApache; then
_err "set up apache error. Report error to me."
return 1
@@ -2410,6 +2832,8 @@ _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"
@@ -2418,29 +2842,49 @@ _on_issue_err() {
_err "See: $_DEBUG_WIKI"
fi
- if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
- _debug "$(_dlg_versions)"
- fi
-
#run the post hook
- if [ "$Le_PostHook" ]; then
- _info "Run post hook:'$Le_PostHook'"
+ if [ "$_chk_post_hook" ]; then
+ _info "Run post hook:'$_chk_post_hook'"
if ! (
- cd "$DOMAIN_PATH" && eval "$Le_PostHook"
+ cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
); then
_err "Error when run post hook."
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() {
+ _chk_post_hook="$1"
+ _chk_renew_hook="$2"
_debug _on_issue_success
#run the post hook
- if [ "$Le_PostHook" ]; then
- _info "Run post hook:'$Le_PostHook'"
+ if [ "$_chk_post_hook" ]; then
+ _info "Run post hook:'$_chk_post_hook'"
if ! (
- cd "$DOMAIN_PATH" && eval "$Le_PostHook"
+ cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
); then
_err "Error when run post hook."
return 1
@@ -2448,10 +2892,10 @@ _on_issue_success() {
fi
#run renew hook
- if [ "$IS_RENEW" ] && [ "$Le_RenewHook" ]; then
- _info "Run renew hook:'$Le_RenewHook'"
+ if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
+ _info "Run renew hook:'$_chk_renew_hook'"
if ! (
- cd "$DOMAIN_PATH" && eval "$Le_RenewHook"
+ cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
); then
_err "Error when run renew hook."
return 1
@@ -2475,6 +2919,10 @@ __calcAccountKeyHash() {
[ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256 <"$ACCOUNT_KEY_PATH"
}
+__calc_account_thumbprint() {
+ printf "%s" "$jwk" | tr -d ' ' | _digest "sha256" | _url_replace
+}
+
#keylength
_regAccount() {
_initpath
@@ -2565,6 +3013,8 @@ _regAccount() {
return 1
fi
fi
+ ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
+ _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
return 0
done
@@ -2636,44 +3086,54 @@ __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
_usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
return 1
fi
- Le_Webroot="$1"
- Le_Domain="$2"
- Le_Alt="$3"
- if _contains "$Le_Domain" ","; then
- Le_Domain=$(echo "$2,$3" | cut -d , -f 1)
- Le_Alt=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
+ _web_roots="$1"
+ _main_domain="$2"
+ _alt_domains="$3"
+ if _contains "$_main_domain" ","; then
+ _main_domain=$(echo "$2,$3" | cut -d , -f 1)
+ _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
fi
- Le_Keylength="$4"
- Le_RealCertPath="$5"
- Le_RealKeyPath="$6"
- Le_RealCACertPath="$7"
- Le_ReloadCmd="$8"
- Le_RealFullChainPath="$9"
- Le_PreHook="${10}"
- Le_PostHook="${11}"
- Le_RenewHook="${12}"
- Le_LocalAddress="${13}"
+ _key_length="$4"
+ _real_cert="$5"
+ _real_key="$6"
+ _real_ca="$7"
+ _reload_cmd="$8"
+ _real_fullchain="$9"
+ _pre_hook="${10}"
+ _post_hook="${11}"
+ _renew_hook="${12}"
+ _local_addr="${13}"
#remove these later.
- if [ "$Le_Webroot" = "dns-cf" ]; then
- Le_Webroot="dns_cf"
+ if [ "$_web_roots" = "dns-cf" ]; then
+ _web_roots="dns_cf"
fi
- if [ "$Le_Webroot" = "dns-dp" ]; then
- Le_Webroot="dns_dp"
+ if [ "$_web_roots" = "dns-dp" ]; then
+ _web_roots="dns_dp"
fi
- if [ "$Le_Webroot" = "dns-cx" ]; then
- Le_Webroot="dns_cx"
+ if [ "$_web_roots" = "dns-cx" ]; then
+ _web_roots="dns_cx"
fi
_debug "Using api: $API"
if [ ! "$IS_RENEW" ]; then
- _initpath "$Le_Domain" "$Le_Keylength"
+ _initpath "$_main_domain" "$_key_length"
mkdir -p "$DOMAIN_PATH"
fi
@@ -2685,7 +3145,7 @@ issue() {
_debug _saved_domain "$_saved_domain"
_saved_alt=$(_readdomainconf Le_Alt)
_debug _saved_alt "$_saved_alt"
- if [ "$_saved_domain,$_saved_alt" = "$Le_Domain,$Le_Alt" ]; then
+ if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
_info "Domains not changed."
_info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
_info "Add '$(__red '--force')' to force to renew."
@@ -2696,16 +3156,16 @@ issue() {
fi
fi
- _savedomainconf "Le_Domain" "$Le_Domain"
- _savedomainconf "Le_Alt" "$Le_Alt"
- _savedomainconf "Le_Webroot" "$Le_Webroot"
+ _savedomainconf "Le_Domain" "$_main_domain"
+ _savedomainconf "Le_Alt" "$_alt_domains"
+ _savedomainconf "Le_Webroot" "$_web_roots"
- _savedomainconf "Le_PreHook" "$Le_PreHook"
- _savedomainconf "Le_PostHook" "$Le_PostHook"
- _savedomainconf "Le_RenewHook" "$Le_RenewHook"
+ _savedomainconf "Le_PreHook" "$_pre_hook"
+ _savedomainconf "Le_PostHook" "$_post_hook"
+ _savedomainconf "Le_RenewHook" "$_renew_hook"
- if [ "$Le_LocalAddress" ]; then
- _savedomainconf "Le_LocalAddress" "$Le_LocalAddress"
+ if [ "$_local_addr" ]; then
+ _savedomainconf "Le_LocalAddress" "$_local_addr"
else
_cleardomainconf "Le_LocalAddress"
fi
@@ -2713,15 +3173,15 @@ issue() {
Le_API="$API"
_savedomainconf "Le_API" "$Le_API"
- if [ "$Le_Alt" = "$NO_VALUE" ]; then
- Le_Alt=""
+ if [ "$_alt_domains" = "$NO_VALUE" ]; then
+ _alt_domains=""
fi
- if [ "$Le_Keylength" = "$NO_VALUE" ]; then
- Le_Keylength=""
+ if [ "$_key_length" = "$NO_VALUE" ]; then
+ _key_length=""
fi
- if ! _on_before_issue; then
+ if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
_err "_on_before_issue."
return 1
fi
@@ -2731,7 +3191,7 @@ issue() {
if [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then
if ! _regAccount "$_accountkeylength"; then
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
else
@@ -2743,37 +3203,38 @@ issue() {
else
_key=$(_readdomainconf Le_Keylength)
_debug "Read key length:$_key"
- if [ ! -f "$CERT_KEY_PATH" ] || [ "$Le_Keylength" != "$_key" ]; then
- if ! createDomainKey "$Le_Domain" "$Le_Keylength"; then
+ if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ]; then
+ if ! createDomainKey "$_main_domain" "$_key_length"; then
_err "Create domain key error."
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
fi
- if ! _createcsr "$Le_Domain" "$Le_Alt" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
+ if ! _createcsr "$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
_err "Create CSR error."
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
fi
- _savedomainconf "Le_Keylength" "$Le_Keylength"
+ _savedomainconf "Le_Keylength" "$_key_length"
vlist="$Le_Vlist"
_info "Getting domain auth token for each domain"
sep='#'
+ dvsep=','
if [ -z "$vlist" ]; then
- alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ')
+ alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ')
_index=1
_currentRoot=""
for d in $alldomains; do
_info "Getting webroot for domain" "$d"
- _w="$(echo $Le_Webroot | cut -d , -f $_index)"
- _info _w "$_w"
+ _w="$(echo $_web_roots | cut -d , -f $_index)"
+ _debug _w "$_w"
if [ "$_w" ]; then
_currentRoot="$_w"
fi
@@ -2791,13 +3252,12 @@ issue() {
if ! __get_domain_new_authz "$d"; then
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
if [ -z "$thumbprint" ]; then
- accountkey_json=$(printf "%s" "$jwk" | tr -d ' ')
- thumbprint=$(printf "%s" "$accountkey_json" | _digest "sha256" | _url_replace)
+ thumbprint="$(__calc_account_thumbprint)"
fi
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
@@ -2805,7 +3265,7 @@ issue() {
if [ -z "$entry" ]; then
_err "Error, can not get domain token $d"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
@@ -2818,7 +3278,7 @@ issue() {
_debug keyauthorization "$keyauthorization"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
- _info "$d is already verified, skip."
+ _debug "$d is already verified, skip."
keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization"
fi
@@ -2826,13 +3286,13 @@ issue() {
dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
_debug dvlist "$dvlist"
- vlist="$vlist$dvlist,"
+ vlist="$vlist$dvlist$dvsep"
done
-
+ _debug vlist "$vlist"
#add entry
dnsadded=""
- ventries=$(echo "$vlist" | tr ',' ' ')
+ ventries=$(echo "$vlist" | tr "$dvsep" ' ')
for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
@@ -2840,7 +3300,7 @@ issue() {
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
- _info "$d is already verified, skip $vtype."
+ _debug "$d is already verified, skip $vtype."
continue
fi
@@ -2886,7 +3346,7 @@ issue() {
if [ "$?" != "0" ]; then
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
dnsadded='1'
@@ -2898,7 +3358,7 @@ issue() {
_debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
_err "Please add the TXT records to the domains, and retry again."
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
@@ -2915,10 +3375,11 @@ issue() {
_sleep "$Le_DNSSleep"
fi
+ NGINX_RESTORE_VLIST=""
_debug "ok, let's start to verify"
_ncIndex=1
- ventries=$(echo "$vlist" | tr ',' ' ')
+ ventries=$(echo "$vlist" | tr "$dvsep" ' ')
for ventry in $ventries; do
d=$(echo "$ventry" | cut -d "$sep" -f 1)
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
@@ -2943,18 +3404,38 @@ issue() {
if [ "$vtype" = "$VTYPE_HTTP" ]; then
if [ "$_currentRoot" = "$NO_VALUE" ]; then
_info "Standalone mode server"
- _ncaddr="$(_getfield "$Le_LocalAddress" "$_ncIndex")"
+ _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math $_ncIndex + 1)"
_startserver "$keyauthorization" "$_ncaddr" &
if [ "$?" != "0" ]; then
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
serverproc="$!"
sleep 1
_debug serverproc "$serverproc"
+ elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
+ _info "Stateless mode for domain:$d"
+ _sleep 1
+ elif _startswith "$_currentRoot" "$NGINX"; then
+ _info "Nginx mode for domain:$d"
+ #set up nginx server
+ FOUND_REAL_NGINX_CONF=""
+ BACKUP_NGINX_CONF=""
+ if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
+ _clearup
+ _on_issue_err "$_post_hook" "$vlist"
+ return 1
+ fi
+ if [ "$FOUND_REAL_NGINX_CONF" ]; then
+ _realConf="$FOUND_REAL_NGINX_CONF"
+ _backup="$BACKUP_NGINX_CONF"
+ _debug _realConf "$_realConf"
+ NGINX_RESTORE_VLIST="$d$sep$_realConf$sep$_backup$dvsep$NGINX_RESTORE_VLIST"
+ fi
+ _sleep 1
else
if [ "$_currentRoot" = "apache" ]; then
wellknown_path="$ACME_DIR"
@@ -2979,7 +3460,7 @@ issue() {
_err "$d:Can not write token to file : $wellknown_path/$token"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
@@ -3018,22 +3499,22 @@ issue() {
_SAN_B="$_x.$_y.acme.invalid"
_debug2 _SAN_B "$_SAN_B"
- _ncaddr="$(_getfield "$Le_LocalAddress" "$_ncIndex")"
+ _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math "$_ncIndex" + 1)"
if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" "$_ncaddr"; then
_err "Start tls server error."
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _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
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
@@ -3041,7 +3522,7 @@ issue() {
_err "$d:Challenge error: $response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
@@ -3056,7 +3537,7 @@ issue() {
_err "$d:Timeout"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
@@ -3068,7 +3549,7 @@ issue() {
_err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
_debug2 original "$response"
@@ -3103,7 +3584,7 @@ issue() {
fi
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
@@ -3113,7 +3594,7 @@ issue() {
_err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
- _on_issue_err
+ _on_issue_err "$_post_hook" "$vlist"
return 1
fi
@@ -3127,7 +3608,7 @@ issue() {
if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"; then
_err "Sign failed."
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
@@ -3169,7 +3650,7 @@ issue() {
if [ -z "$Le_LinkCert" ]; then
response="$(echo "$response" | _dbase64 "multiline" | _normalizeJson)"
_err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
- _on_issue_err
+ _on_issue_err "$_post_hook"
return 1
fi
@@ -3231,10 +3712,15 @@ issue() {
Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
- _on_issue_success
+ _on_issue_success "$_post_hook" "$_renew_hook"
- if [ "$Le_RealCertPath$Le_RealKeyPath$Le_RealCACertPath$Le_ReloadCmd$Le_RealFullChainPath" ]; then
- _installcert
+ if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
+ _savedomainconf "Le_RealCertPath" "$_real_cert"
+ _savedomainconf "Le_RealCACertPath" "$_real_ca"
+ _savedomainconf "Le_RealKeyPath" "$_real_key"
+ _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
+ _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
+ _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
fi
}
@@ -3287,7 +3773,7 @@ renew() {
fi
if [ "$Le_DeployHook" ]; then
- deploy "$Le_Domain" "$Le_DeployHook" "$Le_Keylength"
+ _deploy "$Le_Domain" "$Le_DeployHook"
res="$?"
fi
@@ -3459,151 +3945,169 @@ list() {
}
+_deploy() {
+ _d="$1"
+ _hooks="$2"
+
+ for _d_api in $(echo "$_hooks" | tr ',' " "); do
+ _deployApi="$(_findHook "$_d" deploy "$_d_api")"
+ if [ -z "$_deployApi" ]; then
+ _err "The deploy hook $_d_api is not found."
+ return 1
+ fi
+ _debug _deployApi "$_deployApi"
+
+ if ! (
+ if ! . "$_deployApi"; then
+ _err "Load file $_deployApi error. Please check your api file and try again."
+ return 1
+ fi
+
+ d_command="${_d_api}_deploy"
+ if ! _exists "$d_command"; then
+ _err "It seems that your api file is not correct, it must have a function named: $d_command"
+ return 1
+ fi
+
+ if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
+ _err "Error deploy for domain:$_d"
+ return 1
+ fi
+ ); then
+ _err "Deploy error."
+ return 1
+ else
+ _info "$(__green Success)"
+ fi
+ done
+}
+
+#domain hooks
deploy() {
- Le_Domain="$1"
- Le_DeployHook="$2"
+ _d="$1"
+ _hooks="$2"
_isEcc="$3"
- if [ -z "$Le_DeployHook" ]; then
+ if [ -z "$_hooks" ]; then
_usage "Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
return 1
fi
- _initpath "$Le_Domain" "$_isEcc"
+ _initpath "$_d" "$_isEcc"
if [ ! -d "$DOMAIN_PATH" ]; then
- _err "Domain is not valid:'$Le_Domain'"
+ _err "Domain is not valid:'$_d'"
return 1
fi
- _deployApi="$(_findHook "$Le_Domain" deploy "$Le_DeployHook")"
- if [ -z "$_deployApi" ]; then
- _err "The deploy hook $Le_DeployHook is not found."
- return 1
- fi
- _debug _deployApi "$_deployApi"
+ . "$DOMAIN_CONF"
- _savedomainconf Le_DeployHook "$Le_DeployHook"
-
- if ! (
- if ! . "$_deployApi"; then
- _err "Load file $_deployApi error. Please check your api file and try again."
- return 1
- fi
-
- d_command="${Le_DeployHook}_deploy"
- if ! _exists "$d_command"; then
- _err "It seems that your api file is not correct, it must have a function named: $d_command"
- return 1
- fi
-
- if ! $d_command "$Le_Domain" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
- _err "Error deploy for domain:$Le_Domain"
- _on_issue_err
- return 1
- fi
- ); then
- _err "Deploy error."
- return 1
- else
- _info "$(__green Success)"
- fi
+ _savedomainconf Le_DeployHook "$_hooks"
+ _deploy "$_d" "$_hooks"
}
installcert() {
- Le_Domain="$1"
- if [ -z "$Le_Domain" ]; then
+ _main_domain="$1"
+ if [ -z "$_main_domain" ]; then
_usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
return 1
fi
- Le_RealCertPath="$2"
- Le_RealKeyPath="$3"
- Le_RealCACertPath="$4"
- Le_ReloadCmd="$5"
- Le_RealFullChainPath="$6"
+ _real_cert="$2"
+ _real_key="$3"
+ _real_ca="$4"
+ _reload_cmd="$5"
+ _real_fullchain="$6"
_isEcc="$7"
- _initpath "$Le_Domain" "$_isEcc"
+ _initpath "$_main_domain" "$_isEcc"
if [ ! -d "$DOMAIN_PATH" ]; then
- _err "Domain is not valid:'$Le_Domain'"
+ _err "Domain is not valid:'$_main_domain'"
return 1
fi
- _installcert
+ _savedomainconf "Le_RealCertPath" "$_real_cert"
+ _savedomainconf "Le_RealCACertPath" "$_real_ca"
+ _savedomainconf "Le_RealKeyPath" "$_real_key"
+ _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
+ _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
+
+ _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
}
+#domain cert key ca fullchain reloadcmd backup-prefix
_installcert() {
- _savedomainconf "Le_RealCertPath" "$Le_RealCertPath"
- _savedomainconf "Le_RealCACertPath" "$Le_RealCACertPath"
- _savedomainconf "Le_RealKeyPath" "$Le_RealKeyPath"
- _savedomainconf "Le_ReloadCmd" "$Le_ReloadCmd"
- _savedomainconf "Le_RealFullChainPath" "$Le_RealFullChainPath"
+ _main_domain="$1"
+ _real_cert="$2"
+ _real_key="$3"
+ _real_ca="$4"
+ _real_fullchain="$5"
+ _reload_cmd="$6"
+ _backup_prefix="$7"
- if [ "$Le_RealCertPath" = "$NO_VALUE" ]; then
- Le_RealCertPath=""
+ if [ "$_real_cert" = "$NO_VALUE" ]; then
+ _real_cert=""
fi
- if [ "$Le_RealKeyPath" = "$NO_VALUE" ]; then
- Le_RealKeyPath=""
+ if [ "$_real_key" = "$NO_VALUE" ]; then
+ _real_key=""
fi
- if [ "$Le_RealCACertPath" = "$NO_VALUE" ]; then
- Le_RealCACertPath=""
+ if [ "$_real_ca" = "$NO_VALUE" ]; then
+ _real_ca=""
fi
- if [ "$Le_ReloadCmd" = "$NO_VALUE" ]; then
- Le_ReloadCmd=""
+ if [ "$_reload_cmd" = "$NO_VALUE" ]; then
+ _reload_cmd=""
fi
- if [ "$Le_RealFullChainPath" = "$NO_VALUE" ]; then
- Le_RealFullChainPath=""
+ if [ "$_real_fullchain" = "$NO_VALUE" ]; then
+ _real_fullchain=""
fi
- if [ "$Le_RealCertPath" ]; then
+ _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix"
+ mkdir -p "$_backup_path"
- _info "Installing cert to:$Le_RealCertPath"
- if [ -f "$Le_RealCertPath" ] && [ ! "$IS_RENEW" ]; then
- cp "$Le_RealCertPath" "$Le_RealCertPath".bak
+ if [ "$_real_cert" ]; then
+ _info "Installing cert to:$_real_cert"
+ if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then
+ cp "$_real_cert" "$_backup_path/cert.bak"
fi
- cat "$CERT_PATH" >"$Le_RealCertPath"
+ cat "$CERT_PATH" >"$_real_cert"
fi
- if [ "$Le_RealCACertPath" ]; then
-
- _info "Installing CA to:$Le_RealCACertPath"
- if [ "$Le_RealCACertPath" = "$Le_RealCertPath" ]; then
- echo "" >>"$Le_RealCACertPath"
- cat "$CA_CERT_PATH" >>"$Le_RealCACertPath"
+ if [ "$_real_ca" ]; then
+ _info "Installing CA to:$_real_ca"
+ if [ "$_real_ca" = "$_real_cert" ]; then
+ echo "" >>"$_real_ca"
+ cat "$CA_CERT_PATH" >>"$_real_ca"
else
- if [ -f "$Le_RealCACertPath" ] && [ ! "$IS_RENEW" ]; then
- cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak
+ if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then
+ cp "$_real_ca" "$_backup_path/ca.bak"
fi
- cat "$CA_CERT_PATH" >"$Le_RealCACertPath"
+ cat "$CA_CERT_PATH" >"$_real_ca"
fi
fi
- if [ "$Le_RealKeyPath" ]; then
-
- _info "Installing key to:$Le_RealKeyPath"
- if [ -f "$Le_RealKeyPath" ] && [ ! "$IS_RENEW" ]; then
- cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
+ if [ "$_real_key" ]; then
+ _info "Installing key to:$_real_key"
+ if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
+ cp "$_real_key" "$_backup_path/key.bak"
fi
- cat "$CERT_KEY_PATH" >"$Le_RealKeyPath"
+ cat "$CERT_KEY_PATH" >"$_real_key"
fi
- if [ "$Le_RealFullChainPath" ]; then
-
- _info "Installing full chain to:$Le_RealFullChainPath"
- if [ -f "$Le_RealFullChainPath" ] && [ ! "$IS_RENEW" ]; then
- cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
+ if [ "$_real_fullchain" ]; then
+ _info "Installing full chain to:$_real_fullchain"
+ if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then
+ cp "$_real_fullchain" "$_backup_path/fullchain.bak"
fi
- cat "$CERT_FULLCHAIN_PATH" >"$Le_RealFullChainPath"
+ cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain"
fi
- if [ "$Le_ReloadCmd" ]; then
- _info "Run Le_ReloadCmd: $Le_ReloadCmd"
+ if [ "$_reload_cmd" ]; then
+ _info "Run reload cmd: $_reload_cmd"
if (
export CERT_PATH
export CERT_KEY_PATH
export CA_CERT_PATH
export CERT_FULLCHAIN_PATH
- cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd"
+ cd "$DOMAIN_PATH" && eval "$_reload_cmd"
); then
_info "$(__green "Reload success")"
else
@@ -3896,12 +4400,7 @@ _detect_profile() {
_initconf() {
_initpath
if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
- echo "#ACCOUNT_CONF_PATH=xxxx
-
-#ACCOUNT_EMAIL=aaa@example.com # the account email used to register account.
-#ACCOUNT_KEY_PATH=\"/path/to/account.key\"
-#CERT_HOME=\"/path/to/cert/home\"
-
+ echo "
#LOG_FILE=\"$DEFAULT_LOG_FILE\"
#LOG_LEVEL=1
@@ -3909,12 +4408,6 @@ _initconf() {
#AUTO_UPGRADE=\"1\"
#NO_TIMESTAMP=1
-#OPENSSL_BIN=openssl
-
-#USER_AGENT=\"$USER_AGENT\"
-
-#USER_PATH=
-
" >"$ACCOUNT_CONF_PATH"
fi
@@ -3942,8 +4435,8 @@ _precheck() {
fi
fi
- if ! _exists "$OPENSSL_BIN"; then
- _err "Please install openssl first."
+ 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
@@ -4220,7 +4713,7 @@ Commands:
--version, -v Show version info.
--install Install $PROJECT_NAME to your system.
--uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
- --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT .
+ --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT.
--issue Issue a cert.
--signcsr Issue a cert from an existing csr.
--deploy Deploy the cert to your server.
@@ -4235,10 +4728,11 @@ 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.
- --createAccountKey, -cak Create an account private key, professional use.
- --createDomainKey, -cdk Create an domain private key, professional use.
+ --create-account-key Create an account private key, professional use.
+ --create-domain-key Create an domain private key, professional use.
--createCSR, -ccsr Create CSR , professional use.
--deactivate Deactivate the domain authz, professional use.
@@ -4247,9 +4741,10 @@ Parameters:
--force, -f Used to force to install or force to renew a cert immediately.
--staging, --test Use staging server, just for test.
--debug Output debug info.
-
+ --output-insecure Output all the sensitive messages. By default all the credentials/sensitive messages are hidden from the output/debug/log for secure.
--webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
--standalone Use standalone mode.
+ --stateless Use stateless mode, see: $_STATELESS_WIKI
--tls Use standalone tls mode.
--apache Use apache mode.
--dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
@@ -4259,6 +4754,7 @@ Parameters:
--accountkeylength, -ak [2048] Specifies the account key length.
--log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
--log-level 1|2 Specifies the log level, default is 1.
+ --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
@@ -4296,6 +4792,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.
"
}
@@ -4363,9 +4860,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
@@ -4374,6 +4871,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() {
@@ -4417,6 +4920,8 @@ _process() {
_listen_v4=""
_listen_v6=""
_openssl_bin=""
+ _syslog=""
+ _use_wget=""
while [ ${#} -gt 0 ]; do
case "${1}" in
@@ -4479,10 +4984,13 @@ _process() {
--toPkcs)
_CMD="toPkcs"
;;
- --createAccountKey | --createaccountkey | -cak)
+ --toPkcs8)
+ _CMD="toPkcs8"
+ ;;
+ --createAccountKey | --createaccountkey | -cak | --create-account-key)
_CMD="createAccountKey"
;;
- --createDomainKey | --createdomainkey | -cdk)
+ --createDomainKey | --createdomainkey | -cdk | --create-domain-key)
_CMD="createDomainKey"
;;
--createCSR | --createcsr | -ccr)
@@ -4532,12 +5040,15 @@ _process() {
;;
--debug)
if [ -z "$2" ] || _startswith "$2" "-"; then
- DEBUG="1"
+ DEBUG="$DEBUG_LEVEL_DEFAULT"
else
DEBUG="$2"
shift
fi
;;
+ --output-insecure)
+ export OUTPUT_INSECURE=1
+ ;;
--webroot | -w)
wvalue="$2"
if [ -z "$_webroot" ]; then
@@ -4555,6 +5066,14 @@ _process() {
_webroot="$_webroot,$wvalue"
fi
;;
+ --stateless)
+ wvalue="$MODE_STATELESS"
+ if [ -z "$_webroot" ]; then
+ _webroot="$wvalue"
+ else
+ _webroot="$_webroot,$wvalue"
+ fi
+ ;;
--local-address)
lvalue="$2"
_local_address="$_local_address$lvalue,"
@@ -4568,6 +5087,14 @@ _process() {
_webroot="$_webroot,$wvalue"
fi
;;
+ --nginx)
+ wvalue="$NGINX"
+ if [ -z "$_webroot" ]; then
+ _webroot="$wvalue"
+ else
+ _webroot="$_webroot,$wvalue"
+ fi
+ ;;
--tls)
wvalue="$W_TLS"
if [ -z "$_webroot" ]; then
@@ -4715,7 +5242,11 @@ _process() {
shift
;;
--deploy-hook)
- _deploy_hook="$2"
+ if [ -z "$2" ] || _startswith "$2" "-"; then
+ _usage "Please specify a value for '--deploy-hook'"
+ return 1
+ fi
+ _deploy_hook="$_deploy_hook$2,"
shift
;;
--ocsp-must-staple | --ocsp)
@@ -4739,6 +5270,15 @@ _process() {
LOG_LEVEL="$_log_level"
shift
;;
+ --syslog)
+ if ! _startswith "$2" '-'; then
+ _syslog="$2"
+ shift
+ fi
+ if [ -z "$_syslog" ]; then
+ _syslog="$SYSLOG_LEVEL_DEFAULT"
+ fi
+ ;;
--auto-upgrade)
_auto_upgrade="$2"
if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-'; then
@@ -4758,7 +5298,12 @@ _process() {
;;
--openssl-bin)
_openssl_bin="$2"
- OPENSSL_BIN="$_openssl_bin"
+ ACME_OPENSSL_BIN="$_openssl_bin"
+ shift
+ ;;
+ --use-wget)
+ _use_wget="1"
+ ACME_USE_WGET="1"
;;
*)
_err "Unknown parameter : $1"
@@ -4786,6 +5331,21 @@ _process() {
LOG_LEVEL="$_log_level"
fi
+ if [ "$_syslog" ]; then
+ if _exists logger; then
+ if [ "$_syslog" = "0" ]; then
+ _clearaccountconf "SYS_LOG"
+ else
+ _saveaccountconf "SYS_LOG" "$_syslog"
+ fi
+ SYS_LOG="$_syslog"
+ else
+ _err "The 'logger' command is not found, can not enable syslog."
+ _clearaccountconf "SYS_LOG"
+ SYS_LOG=""
+ fi
+ fi
+
_processAccountConf
fi
@@ -4844,6 +5404,9 @@ _process() {
toPkcs)
toPkcs "$_domain" "$_password" "$_ecc"
;;
+ toPkcs8)
+ toPkcs8 "$_domain" "$_ecc"
+ ;;
createAccountKey)
createAccountKey "$_accountkeylength"
;;
@@ -4878,6 +5441,21 @@ _process() {
if [ "$_log_level" ]; then
_saveaccountconf "LOG_LEVEL" "$_log_level"
fi
+
+ if [ "$_syslog" ]; then
+ if _exists logger; then
+ if [ "$_syslog" = "0" ]; then
+ _clearaccountconf "SYS_LOG"
+ else
+ _saveaccountconf "SYS_LOG" "$_syslog"
+ fi
+ else
+ _err "The 'logger' command is not found, can not enable syslog."
+ _clearaccountconf "SYS_LOG"
+ SYS_LOG=""
+ fi
+ fi
+
_processAccountConf
fi
diff --git a/deploy/README.md b/deploy/README.md
index 580eaac8..4a13e096 100644
--- a/deploy/README.md
+++ b/deploy/README.md
@@ -1 +1,74 @@
-#Using deploy api
+# 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.)
+
+
+
+Then you can deploy now:
+
+```sh
+export DEPLOY_CPANEL_USER=myusername
+export DEPLOY_CPANEL_PASSWORD=PASSWORD
+acme.sh --deploy -d example.com --deploy-hook cpanel
+```
+
+## 2. Deploy ssl cert on kong proxy engine based on api.
+
+Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
+
+(TODO)
+
+## 3. Deploy the cert to remote server through SSH access.
+
+(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
+```
+
+## 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/apache.sh b/deploy/apache.sh
new file mode 100644
index 00000000..b6c1fbc2
--- /dev/null
+++ b/deploy/apache.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to dovecot server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+apache_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "Deploy cert to apache server, Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/cpanel.sh b/deploy/cpanel.sh
new file mode 100644
index 00000000..bf1332ff
--- /dev/null
+++ b/deploy/cpanel.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env sh
+
+#Here is the script to deploy the cert to your cpanel account by the cpanel APIs.
+
+#returns 0 means success, otherwise error.
+
+#export DEPLOY_CPANEL_USER=myusername
+#export DEPLOY_CPANEL_PASSWORD=PASSWORD
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+cpanel_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/dovecot.sh b/deploy/dovecot.sh
new file mode 100644
index 00000000..3baf23d9
--- /dev/null
+++ b/deploy/dovecot.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to dovecot server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+dovecot_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/exim4.sh b/deploy/exim4.sh
new file mode 100644
index 00000000..bf92b438
--- /dev/null
+++ b/deploy/exim4.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to exim4 server.
+
+#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
+exim4_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _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
+
+}
diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh
new file mode 100644
index 00000000..34efbb1f
--- /dev/null
+++ b/deploy/haproxy.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to haproxy server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+haproxy_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "deploy cert to haproxy server, Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/kong.sh b/deploy/kong.sh
old mode 100644
new mode 100755
diff --git a/deploy/myapi.sh b/deploy/myapi.sh
old mode 100644
new mode 100755
diff --git a/deploy/mysqld.sh b/deploy/mysqld.sh
new file mode 100644
index 00000000..8778843e
--- /dev/null
+++ b/deploy/mysqld.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to mysqld server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+mysqld_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "deploy cert to mysqld server, Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/nginx.sh b/deploy/nginx.sh
new file mode 100644
index 00000000..952b27f3
--- /dev/null
+++ b/deploy/nginx.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to nginx server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+nginx_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "deploy cert to nginx server, Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/opensshd.sh b/deploy/opensshd.sh
new file mode 100644
index 00000000..9001b97c
--- /dev/null
+++ b/deploy/opensshd.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to opensshd server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+opensshd_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "deploy cert to opensshd server, Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/pureftpd.sh b/deploy/pureftpd.sh
new file mode 100644
index 00000000..3d803601
--- /dev/null
+++ b/deploy/pureftpd.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to pureftpd server.
+
+#returns 0 means success, otherwise error.
+
+######## Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+pureftpd_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _err "deploy cert to pureftpd server, Not implemented yet"
+ return 1
+
+}
diff --git a/deploy/vsftpd.sh b/deploy/vsftpd.sh
new file mode 100644
index 00000000..1c6410a6
--- /dev/null
+++ b/deploy/vsftpd.sh
@@ -0,0 +1,110 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to vsftpd server.
+
+#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
+vsftpd_deploy() {
+ _cdomain="$1"
+ _ckey="$2"
+ _ccert="$3"
+ _cca="$4"
+ _cfullchain="$5"
+
+ _debug _cdomain "$_cdomain"
+ _debug _ckey "$_ckey"
+ _debug _ccert "$_ccert"
+ _debug _cca "$_cca"
+ _debug _cfullchain "$_cfullchain"
+
+ _ssl_path="/etc/acme.sh/vsftpd"
+ 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/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
+
+ DEFAULT_VSFTPD_RELOAD="service vsftpd restart"
+ _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}"
+
+ 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
+ _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 "Reload error, restoring"
+ 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 0
+}
diff --git a/dnsapi/README.md b/dnsapi/README.md
index 6a86bf4c..18c1ca9f 100644
--- a/dnsapi/README.md
+++ b/dnsapi/README.md
@@ -305,6 +305,50 @@ Note that you cannot use acme.sh automatic DNS validation for FreeDNS public dom
you create under a FreeDNS public domain. You must own the top level domain in order to automaitcally
validate with acme.sh at FreeDNS.
+## 16. Use cyon.ch
+
+You only need to set your cyon.ch login credentials.
+If you also have 2 Factor Authentication (OTP) enabled, you need to set your secret token too and have `oathtool` installed.
+
+```
+export CY_Username="your_cyon_username"
+export CY_Password="your_cyon_password"
+export CY_OTP_Secret="your_otp_secret" # Only required if using 2FA
+```
+
+To issue a cert:
+```
+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
+```
+
+## 18. Use Gandi LiveDNS API
+
+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"
+```
+
+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_ad.sh b/dnsapi/dns_ad.sh
old mode 100644
new mode 100755
diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh
old mode 100644
new mode 100755
diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh
old mode 100644
new mode 100755
index 555bd70b..84aa28d3
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -93,7 +93,7 @@ _get_root() {
fi
if _contains "$response" "$h."; then
- hostedzone="$(echo "$response" | _egrep_o "[^<]*<.Id>$h.<.Name>.*<.HostedZone>")"
+ hostedzone="$(echo "$response" | sed 's//#&/g' | tr '#' '\n' | _egrep_o "[^<]*<.Id>$h.<.Name>.*<.HostedZone>")"
_debug hostedzone "$hostedzone"
if [ -z "$hostedzone" ]; then
_err "Error, can not get hostedzone."
@@ -181,10 +181,10 @@ aws_rest() {
#kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
- _debug2 kSecret "$kSecret"
+ _secure_debug2 kSecret "$kSecret"
kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
- _debug2 kSecretH "$kSecretH"
+ _secure_debug2 kSecretH "$kSecretH"
kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
_debug2 kDateH "$kDateH"
diff --git a/dnsapi/dns_cyon.sh b/dnsapi/dns_cyon.sh
new file mode 100644
index 00000000..c096d8b0
--- /dev/null
+++ b/dnsapi/dns_cyon.sh
@@ -0,0 +1,328 @@
+#!/usr/bin/env sh
+
+########
+# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh)
+#
+# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
+#
+# Dependencies:
+# -------------
+# - oathtool (When using 2 Factor Authentication)
+#
+# Issues:
+# -------
+# Any issues / questions / suggestions can be posted here:
+# https://github.com/noplanman/cyon-api/issues
+#
+# Author: Armando Lüscher
+########
+
+dns_cyon_add() {
+ _cyon_load_credentials \
+ && _cyon_load_parameters "$@" \
+ && _cyon_print_header "add" \
+ && _cyon_login \
+ && _cyon_change_domain_env \
+ && _cyon_add_txt \
+ && _cyon_logout
+}
+
+dns_cyon_rm() {
+ _cyon_load_credentials \
+ && _cyon_load_parameters "$@" \
+ && _cyon_print_header "delete" \
+ && _cyon_login \
+ && _cyon_change_domain_env \
+ && _cyon_delete_txt \
+ && _cyon_logout
+}
+
+#########################
+### PRIVATE FUNCTIONS ###
+#########################
+
+_cyon_load_credentials() {
+ # Convert loaded password to/from base64 as needed.
+ if [ "${CY_Password_B64}" ]; then
+ CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
+ elif [ "${CY_Password}" ]; then
+ CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
+ fi
+
+ if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then
+ # Dummy entries to satify script checker.
+ CY_Username=""
+ CY_Password=""
+ CY_OTP_Secret=""
+
+ _err ""
+ _err "You haven't set your cyon.ch login credentials yet."
+ _err "Please set the required cyon environment variables."
+ _err ""
+ return 1
+ fi
+
+ # Save the login credentials to the account.conf file.
+ _debug "Save credentials to account.conf"
+ _saveaccountconf CY_Username "${CY_Username}"
+ _saveaccountconf CY_Password_B64 "$CY_Password_B64"
+ if [ ! -z "${CY_OTP_Secret}" ]; then
+ _saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
+ else
+ _clearaccountconf CY_OTP_Secret
+ fi
+}
+
+_cyon_is_idn() {
+ _idn_temp="$(printf "%s" "${1}" | tr -d "0-9a-zA-Z.,-_")"
+ _idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")"
+ [ "$_idn_temp" ] || [ "$_idn_temp2" ]
+}
+
+_cyon_load_parameters() {
+ # Read the required parameters to add the TXT entry.
+ # shellcheck disable=SC2018,SC2019
+ fulldomain="$(printf "%s" "${1}" | tr "A-Z" "a-z")"
+ fulldomain_idn="${fulldomain}"
+
+ # Special case for IDNs, as cyon needs a domain environment change,
+ # which uses the "pretty" instead of the punycode version.
+ if _cyon_is_idn "${fulldomain}"; then
+ if ! _exists idn; then
+ _err "Please install idn to process IDN names."
+ _err ""
+ return 1
+ fi
+
+ fulldomain="$(idn -u "${fulldomain}")"
+ fulldomain_idn="$(idn -a "${fulldomain}")"
+ fi
+
+ _debug fulldomain "${fulldomain}"
+ _debug fulldomain_idn "${fulldomain_idn}"
+
+ txtvalue="${2}"
+ _debug txtvalue "${txtvalue}"
+
+ # This header is required for curl calls.
+ _H1="X-Requested-With: XMLHttpRequest"
+ export _H1
+}
+
+_cyon_print_header() {
+ if [ "${1}" = "add" ]; then
+ _info ""
+ _info "+---------------------------------------------+"
+ _info "| Adding DNS TXT entry to your cyon.ch domain |"
+ _info "+---------------------------------------------+"
+ _info ""
+ _info " * Full Domain: ${fulldomain}"
+ _info " * TXT Value: ${txtvalue}"
+ _info ""
+ elif [ "${1}" = "delete" ]; then
+ _info ""
+ _info "+-------------------------------------------------+"
+ _info "| Deleting DNS TXT entry from your cyon.ch domain |"
+ _info "+-------------------------------------------------+"
+ _info ""
+ _info " * Full Domain: ${fulldomain}"
+ _info ""
+ fi
+}
+
+_cyon_get_cookie_header() {
+ printf "Cookie: %s" "$(grep "cyon=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'cyon=[^;]*;' | tr -d ';')"
+}
+
+_cyon_login() {
+ _info " - Logging in..."
+
+ username_encoded="$(printf "%s" "${CY_Username}" | _url_encode)"
+ password_encoded="$(printf "%s" "${CY_Password}" | _url_encode)"
+
+ login_url="https://my.cyon.ch/auth/index/dologin-async"
+ login_data="$(printf "%s" "username=${username_encoded}&password=${password_encoded}&pathname=%2F")"
+
+ login_response="$(_post "$login_data" "$login_url")"
+ _debug login_response "${login_response}"
+
+ # Bail if login fails.
+ if [ "$(printf "%s" "${login_response}" | _cyon_get_response_success)" != "success" ]; then
+ _err " $(printf "%s" "${login_response}" | _cyon_get_response_message)"
+ _err ""
+ return 1
+ fi
+
+ _info " success"
+
+ # NECESSARY!! Load the main page after login, to get the new cookie.
+ _H2="$(_cyon_get_cookie_header)"
+ export _H2
+
+ _get "https://my.cyon.ch/" >/dev/null
+
+ # todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
+
+ # 2FA authentication with OTP?
+ if [ ! -z "${CY_OTP_Secret}" ]; then
+ _info " - Authorising with OTP code..."
+
+ if ! _exists oathtool; then
+ _err "Please install oathtool to use 2 Factor Authentication."
+ _err ""
+ return 1
+ fi
+
+ # Get OTP code with the defined secret.
+ otp_code="$(oathtool --base32 --totp "${CY_OTP_Secret}" 2>/dev/null)"
+
+ login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async"
+ login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0"
+
+ login_otp_response="$(_post "$login_otp_data" "$login_otp_url")"
+ _debug login_otp_response "${login_otp_response}"
+
+ # Bail if OTP authentication fails.
+ if [ "$(printf "%s" "${login_otp_response}" | _cyon_get_response_success)" != "success" ]; then
+ _err " $(printf "%s" "${login_otp_response}" | _cyon_get_response_message)"
+ _err ""
+ return 1
+ fi
+
+ _info " success"
+ fi
+
+ _info ""
+}
+
+_cyon_logout() {
+ _info " - Logging out..."
+
+ _get "https://my.cyon.ch/auth/index/dologout" >/dev/null
+
+ _info " success"
+ _info ""
+}
+
+_cyon_change_domain_env() {
+ _info " - Changing domain environment..."
+
+ # Get the "example.com" part of the full domain name.
+ domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')"
+ _debug "Changing domain environment to ${domain_env}"
+
+ gloo_item_key="$(_get "https://my.cyon.ch/domain/" | tr '\n' ' ' | sed -E -e "s/.*data-domain=\"${domain_env}\"[^<]*data-itemkey=\"([^\"]*).*/\1/")"
+ _debug gloo_item_key "${gloo_item_key}"
+
+ domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/${gloo_item_key}"
+
+ domain_env_response="$(_get "${domain_env_url}")"
+ _debug domain_env_response "${domain_env_response}"
+
+ if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
+
+ domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
+
+ # Bail if domain environment change fails.
+ if [ "${domain_env_success}" != "true" ]; then
+ _err " $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
+ _err ""
+ return 1
+ fi
+
+ _info " success"
+ _info ""
+}
+
+_cyon_add_txt() {
+ _info " - Adding DNS TXT entry..."
+
+ add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
+ add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
+
+ add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
+ _debug add_txt_response "${add_txt_response}"
+
+ if ! _cyon_check_if_2fa_missed "${add_txt_response}"; then return 1; fi
+
+ add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
+ add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
+
+ # Bail if adding TXT entry fails.
+ if [ "${add_txt_status}" != "true" ]; then
+ _err " ${add_txt_message}"
+ _err ""
+ return 1
+ fi
+
+ _info " success (TXT|${fulldomain_idn}.|${txtvalue})"
+ _info ""
+}
+
+_cyon_delete_txt() {
+ _info " - Deleting DNS TXT entry..."
+
+ list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async"
+
+ list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')"
+ _debug list_txt_response "${list_txt_response}"
+
+ if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi
+
+ # Find and delete all acme challenge entries for the $fulldomain.
+ _dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')"
+
+ printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do
+ dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)"
+ dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)"
+
+ if [ "${dns_type}" != "TXT" ] || [ "${dns_domain}" != "${fulldomain_idn}." ]; then
+ continue
+ fi
+
+ hash_encoded="$(printf "%s" "${_hash}" | _url_encode)"
+ identifier_encoded="$(printf "%s" "${_identifier}" | _url_encode)"
+
+ delete_txt_url="https://my.cyon.ch/domain/dnseditor/delete-record-async"
+ delete_txt_data="$(printf "%s" "hash=${hash_encoded}&identifier=${identifier_encoded}")"
+
+ delete_txt_response="$(_post "$delete_txt_data" "$delete_txt_url")"
+ _debug delete_txt_response "${delete_txt_response}"
+
+ if ! _cyon_check_if_2fa_missed "${delete_txt_response}"; then return 1; fi
+
+ delete_txt_message="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_message)"
+ delete_txt_status="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_status)"
+
+ # Skip if deleting TXT entry fails.
+ if [ "${delete_txt_status}" != "true" ]; then
+ _err " ${delete_txt_message} (${_identifier})"
+ else
+ _info " success (${_identifier})"
+ fi
+ done
+
+ _info " done"
+ _info ""
+}
+
+_cyon_get_response_message() {
+ _egrep_o '"message":"[^"]*"' | cut -d : -f 2 | tr -d '"'
+}
+
+_cyon_get_response_status() {
+ _egrep_o '"status":\w*' | cut -d : -f 2
+}
+
+_cyon_get_response_success() {
+ _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
+}
+
+_cyon_check_if_2fa_missed() {
+ # Did we miss the 2FA?
+ if test "${1#*multi_factor_form}" != "${1}"; then
+ _err " Missed OTP authentication!"
+ _err ""
+ return 1
+ fi
+}
diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh
new file mode 100755
index 00000000..3a2f8f49
--- /dev/null
+++ b/dnsapi/dns_do.sh
@@ -0,0 +1,148 @@
+#!/usr/bin/env sh
+
+# 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"
+
+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
+ 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
+ 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 $_dns_do_had_error
+ 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, are DO_PID and DO_PW set correctly?"
+ 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 ">$(_regexcape "$fulldomain")" \
+ | sed -e 's/<\/item>/\n/g' \
+ | grep '>id[0-9]{1,16}<' \
+ | tr -d '><')"
+ [ "${_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$_k>"
+ done
+ body="$body"
+ _debug2 "SOAP request ${body}"
+
+ # build SOAP XML
+ _xml='
+
+ '"$body"'
+'
+
+ # set SOAP headers
+ export _H1="SOAPAction: ${DO_URL}#${func}"
+
+ if ! response="$(_post "${_xml}" "${DO_URL}")"; then
+ _err "Error <$1>"
+ return 1
+ fi
+ _debug2 "SOAP response $response"
+
+ # retrieve cookie header
+ _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
+ export _H2
+
+ return 0
+}
+
+_get_root() {
+ domain=$1
+ i=1
+
+ _dns_do_soap getDomainList
+ _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)
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
+ _domain="$h"
+ return 0
+ fi
+
+ i=$(_math $i + 1)
+ done
+ _debug "$domain not found"
+
+ return 1
+}
+
+_regexcape() {
+ echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
+}
diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
new file mode 100755
index 00000000..41f42980
--- /dev/null
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -0,0 +1,123 @@
+#!/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\"]}" \
+ && _contains "$response" '{"message": "Zone Record Created"}' \
+ && _info "Add $(__green "success")"
+}
+
+#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" ""
+
+}
+
+#################### 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)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _gandi_livedns_rest GET "domains/$h"; then
+ return 1
+ fi
+
+ 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)
+ _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 [ "$m" = "GET" ]; then
+ response="$(_get "$GANDI_LIVEDNS_API/$ep")"
+ else
+ _debug data "$data"
+ response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh
index 1abeeacf..f2dd1fd5 100755
--- a/dnsapi/dns_gd.sh
+++ b/dnsapi/dns_gd.sh
@@ -40,7 +40,7 @@ dns_gd_add() {
if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
if [ "$response" = "{}" ]; then
_info "Added, sleeping 10 seconds"
- sleep 10
+ _sleep 10
#todo: check if the record takes effect
return 0
else
diff --git a/dnsapi/dns_lexicon.sh b/dnsapi/dns_lexicon.sh
index c38ff3e3..c09f16fd 100755
--- a/dnsapi/dns_lexicon.sh
+++ b/dnsapi/dns_lexicon.sh
@@ -34,7 +34,7 @@ dns_lexicon_add() {
# shellcheck disable=SC2018,SC2019
Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z')
Lx_name_v=$(eval echo \$"$Lx_name")
- _debug "$Lx_name" "$Lx_name_v"
+ _secure_debug "$Lx_name" "$Lx_name_v"
if [ "$Lx_name_v" ]; then
_saveaccountconf "$Lx_name" "$Lx_name_v"
eval export "$Lx_name"
@@ -43,7 +43,7 @@ dns_lexicon_add() {
# shellcheck disable=SC2018,SC2019
Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z')
Lx_token_v=$(eval echo \$"$Lx_token")
- _debug "$Lx_token" "$Lx_token_v"
+ _secure_debug "$Lx_token" "$Lx_token_v"
if [ "$Lx_token_v" ]; then
_saveaccountconf "$Lx_token" "$Lx_token_v"
eval export "$Lx_token"
@@ -52,7 +52,7 @@ dns_lexicon_add() {
# shellcheck disable=SC2018,SC2019
Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z')
Lx_password_v=$(eval echo \$"$Lx_password")
- _debug "$Lx_password" "$Lx_password_v"
+ _secure_debug "$Lx_password" "$Lx_password_v"
if [ "$Lx_password_v" ]; then
_saveaccountconf "$Lx_password" "$Lx_password_v"
eval export "$Lx_password"
@@ -61,7 +61,7 @@ dns_lexicon_add() {
# shellcheck disable=SC2018,SC2019
Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z')
Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
- _debug "$Lx_domaintoken" "$Lx_domaintoken_v"
+ _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v"
if [ "$Lx_domaintoken_v" ]; then
eval export "$Lx_domaintoken"
_saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v"
diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh
index 8833c0a1..faf5b42b 100755
--- a/dnsapi/dns_ovh.sh
+++ b/dnsapi/dns_ovh.sh
@@ -207,7 +207,7 @@ _ovh_authentication() {
_err "Unable to get consumerKey"
return 1
fi
- _debug consumerKey "$consumerKey"
+ _secure_debug consumerKey "$consumerKey"
OVH_CK="$consumerKey"
_saveaccountconf OVH_CK "$OVH_CK"
@@ -269,7 +269,7 @@ _ovh_rest() {
_ovh_t="$(_ovh_timestamp)"
_debug2 _ovh_t "$_ovh_t"
_ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t"
- _debug _ovh_p "$_ovh_p"
+ _secure_debug _ovh_p "$_ovh_p"
_ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)"
_debug2 _ovh_hex "$_ovh_hex"