mirror of
				https://github.com/hiskang/acme.sh
				synced 2025-10-31 02:17:18 +00:00 
			
		
		
		
	Merge branch 'dev' into master
This commit is contained in:
		
						commit
						9201e0a5b9
					
				
							
								
								
									
										59
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| FROM alpine | ||||
| 
 | ||||
| RUN apk update -f \ | ||||
|   && apk --no-cache add -f \ | ||||
|   openssl \ | ||||
|   curl \ | ||||
|   netcat-openbsd | ||||
| 
 | ||||
| ENV LE_CONFIG_HOME /acme.sh | ||||
| 
 | ||||
| ENV AUTO_UPGRADE 1 | ||||
| 
 | ||||
| #Install | ||||
| RUN mkdir -p /install_acme.sh/ | ||||
| ADD ./ /install_acme.sh/ | ||||
| RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) | ||||
| RUN rm -rf /install_acme.sh/ | ||||
| 
 | ||||
| RUN ln -s  /root/.acme.sh/acme.sh  /usr/local/bin/acme.sh | ||||
| 
 | ||||
| RUN for verb in help \  | ||||
|   version \ | ||||
|   install \ | ||||
|   uninstall \ | ||||
|   upgrade \ | ||||
|   issue \ | ||||
|   signcsr \ | ||||
|   deploy \ | ||||
|   install-cert \ | ||||
|   renew \ | ||||
|   renew-all \ | ||||
|   revoke \ | ||||
|   remove \ | ||||
|   list \ | ||||
|   showcsr \ | ||||
|   install-cronjob \ | ||||
|   uninstall-cronjob \ | ||||
|   cron \ | ||||
|   toPkcs \ | ||||
|   toPkcs8 \ | ||||
|   update-account \ | ||||
|   register-account \ | ||||
|   create-account-key \ | ||||
|   create-domain-key \ | ||||
|   createCSR \ | ||||
|   deactivate \ | ||||
|   ; do \ | ||||
|     printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ | ||||
|   ; done | ||||
| 
 | ||||
| RUN printf "%b" '#!'"/usr/bin/env sh\n \ | ||||
| if [ \"\$1\" = \"daemon\" ];  then \n \ | ||||
|  crond; tail -f /dev/null;\n \ | ||||
| else \n \ | ||||
|  /root/.acme.sh/acme.sh --config-home /acme.sh \"\$@\"\n \ | ||||
| fi" >/entry.sh && chmod +x /entry.sh | ||||
| 
 | ||||
| ENTRYPOINT ["/entry.sh"] | ||||
| CMD ["--help"] | ||||
							
								
								
									
										40
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README.md
									
									
									
									
									
								
							| @ -7,11 +7,13 @@ | ||||
| - Purely written in Shell with no dependencies on python or the official Let's Encrypt client. | ||||
| - Just one script to issue, renew and install your certificates automatically. | ||||
| - DOES NOT require `root/sudoer` access. | ||||
| - Docker friendly | ||||
| 
 | ||||
| It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. | ||||
| It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. | ||||
| 
 | ||||
| Wiki: https://github.com/Neilpang/acme.sh/wiki | ||||
| 
 | ||||
| For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) | ||||
| 
 | ||||
| Twitter: [@neilpangxa](https://twitter.com/neilpangxa) | ||||
| 
 | ||||
| @ -29,6 +31,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) | ||||
| - [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html) | ||||
| - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) | ||||
| - [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) | ||||
| - [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) | ||||
| - [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) | ||||
| 
 | ||||
| # Tested OS | ||||
| @ -133,13 +136,25 @@ root@v1:~# acme.sh -h | ||||
| acme.sh --issue -d example.com -w /home/wwwroot/example.com | ||||
| ``` | ||||
| 
 | ||||
| or: | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -d example.com -w /home/username/public_html | ||||
| ``` | ||||
| 
 | ||||
| or: | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -d example.com -w /var/www/html | ||||
| ``` | ||||
| 
 | ||||
| **Example 2:** Multiple domains in the same cert. | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com | ||||
| ``` | ||||
| 
 | ||||
| The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder. | ||||
| The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder. | ||||
| 
 | ||||
| Second argument **"example.com"** is the main domain you want to issue the cert for. | ||||
| You must have at least one domain there. | ||||
| @ -161,17 +176,17 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT** | ||||
| **Apache** example: | ||||
| ```bash | ||||
| 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 \ | ||||
| --cert-file      /path/to/certfile/in/apache/cert.pem  \ | ||||
| --key-file       /path/to/keyfile/in/apache/key.pem  \ | ||||
| --fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \ | ||||
| --reloadcmd     "service apache2 force-reload" | ||||
| ``` | ||||
| 
 | ||||
| **Nginx** example: | ||||
| ```bash | ||||
| acme.sh --install-cert -d example.com \ | ||||
| --keypath       /path/to/keyfile/in/nginx/key.pem  \ | ||||
| --fullchainpath /path/to/fullchain/nginx/cert.pem \ | ||||
| --key-file       /path/to/keyfile/in/nginx/key.pem  \ | ||||
| --fullchain-file /path/to/fullchain/nginx/cert.pem \ | ||||
| --reloadcmd     "service nginx force-reload" | ||||
| ``` | ||||
| 
 | ||||
| @ -289,6 +304,7 @@ You don't have to do anything manually! | ||||
| 
 | ||||
| 1. CloudFlare.com API | ||||
| 1. DNSPod.cn API | ||||
| 1. DNSimple API | ||||
| 1. CloudXNS.com API | ||||
| 1. GoDaddy.com API | ||||
| 1. OVH, kimsufi, soyoustart and runabove API | ||||
| @ -308,7 +324,13 @@ You don't have to do anything manually! | ||||
| 1. Domain-Offensive/Resellerinterface/Domainrobot API | ||||
| 1. Gandi LiveDNS API | ||||
| 1. Knot DNS API | ||||
| 1. NS1. API | ||||
| 1. NS1.com API | ||||
| 1. DigitalOcean API (native) | ||||
| 1. ClouDNS.net API | ||||
| 1. Infoblox NIOS API (https://www.infoblox.com/) | ||||
| 1. VSCALE (https://vscale.io/) | ||||
| 1. Dynu API (https://www.dynu.com) | ||||
| 
 | ||||
| 
 | ||||
| **More APIs coming soon...** | ||||
| 
 | ||||
| @ -327,7 +349,7 @@ Just set the `length` parameter with a prefix `ec-`. | ||||
| 
 | ||||
| For example: | ||||
| 
 | ||||
| ### Single domain ECC cerfiticate | ||||
| ### Single domain ECC certificate | ||||
| 
 | ||||
| ```bash | ||||
| acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 | ||||
|  | ||||
							
								
								
									
										290
									
								
								acme.sh
									
									
									
									
									
								
							
							
						
						
									
										290
									
								
								acme.sh
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| VER=2.6.7 | ||||
| VER=2.6.9 | ||||
| 
 | ||||
| PROJECT_NAME="acme.sh" | ||||
| 
 | ||||
| @ -107,7 +107,7 @@ __green() { | ||||
|   if [ "$__INTERACTIVE" ]; then | ||||
|     printf '\033[1;31;32m' | ||||
|   fi | ||||
|   printf -- "$1" | ||||
|   printf -- "%b" "$1" | ||||
|   if [ "$__INTERACTIVE" ]; then | ||||
|     printf '\033[0m' | ||||
|   fi | ||||
| @ -117,7 +117,7 @@ __red() { | ||||
|   if [ "$__INTERACTIVE" ]; then | ||||
|     printf '\033[1;31;40m' | ||||
|   fi | ||||
|   printf -- "$1" | ||||
|   printf -- "%b" "$1" | ||||
|   if [ "$__INTERACTIVE" ]; then | ||||
|     printf '\033[0m' | ||||
|   fi | ||||
| @ -138,8 +138,8 @@ _printargs() { | ||||
| _dlg_versions() { | ||||
|   echo "Diagnosis versions: " | ||||
|   echo "openssl:$ACME_OPENSSL_BIN" | ||||
|   if _exists "$ACME_OPENSSL_BIN"; then | ||||
|     $ACME_OPENSSL_BIN version 2>&1 | ||||
|   if _exists "${ACME_OPENSSL_BIN:-openssl}"; then | ||||
|     ${ACME_OPENSSL_BIN:-openssl} version 2>&1 | ||||
|   else | ||||
|     echo "$ACME_OPENSSL_BIN doesn't exists." | ||||
|   fi | ||||
| @ -166,7 +166,14 @@ _syslog() { | ||||
|   fi | ||||
|   _logclass="$1" | ||||
|   shift | ||||
|   logger -i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1 | ||||
|   if [ -z "$__logger_i" ]; then | ||||
|     if _contains "$(logger --help 2>&1)" "-i"; then | ||||
|       __logger_i="logger -i" | ||||
|     else | ||||
|       __logger_i="logger" | ||||
|     fi | ||||
|   fi | ||||
|   $__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1 | ||||
| } | ||||
| 
 | ||||
| _log() { | ||||
| @ -299,6 +306,16 @@ _secure_debug3() { | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| _upper_case() { | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   tr 'a-z' 'A-Z' | ||||
| } | ||||
| 
 | ||||
| _lower_case() { | ||||
|   # shellcheck disable=SC2018,SC2019 | ||||
|   tr 'A-Z' 'a-z' | ||||
| } | ||||
| 
 | ||||
| _startswith() { | ||||
|   _str="$1" | ||||
|   _sub="$2" | ||||
| @ -330,14 +347,14 @@ _hasfield() { | ||||
|     _sep="," | ||||
|   fi | ||||
| 
 | ||||
|   for f in $(echo "$_str" | tr ',' ' '); do | ||||
|   for f in $(echo "$_str" | tr "$_sep" ' '); do | ||||
|     if [ "$f" = "$_field" ]; then | ||||
|       _debug2 "'$_str' contains '$_field'" | ||||
|       return 0 #contains ok | ||||
|     fi | ||||
|   done | ||||
|   _debug2 "'$_str' does not contain '$_field'" | ||||
|   return 1 #not contains  | ||||
|   return 1 #not contains | ||||
| } | ||||
| 
 | ||||
| _getfield() { | ||||
| @ -712,7 +729,7 @@ _url_encode() { | ||||
|       "7e") | ||||
|         printf "%s" "~" | ||||
|         ;; | ||||
|       #other hex   | ||||
|       #other hex | ||||
|       *) | ||||
|         printf '%%%s' "$_hex_code" | ||||
|         ;; | ||||
| @ -780,19 +797,19 @@ _base64() { | ||||
|   [ "" ] #urgly | ||||
|   if [ "$1" ]; then | ||||
|     _debug3 "base64 multiline:'$1'" | ||||
|     $ACME_OPENSSL_BIN base64 -e | ||||
|     ${ACME_OPENSSL_BIN:-openssl} base64 -e | ||||
|   else | ||||
|     _debug3 "base64 single line." | ||||
|     $ACME_OPENSSL_BIN base64 -e | tr -d '\r\n' | ||||
|     ${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n' | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| #Usage: multiline | ||||
| _dbase64() { | ||||
|   if [ "$1" ]; then | ||||
|     $ACME_OPENSSL_BIN base64 -d -A | ||||
|     ${ACME_OPENSSL_BIN:-openssl} base64 -d -A | ||||
|   else | ||||
|     $ACME_OPENSSL_BIN base64 -d | ||||
|     ${ACME_OPENSSL_BIN:-openssl} base64 -d | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| @ -809,9 +826,9 @@ _digest() { | ||||
| 
 | ||||
|   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then | ||||
|     if [ "$outputhex" ]; then | ||||
|       $ACME_OPENSSL_BIN dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' ' | ||||
|       ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' ' | ||||
|     else | ||||
|       $ACME_OPENSSL_BIN dgst -"$alg" -binary | _base64 | ||||
|       ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64 | ||||
|     fi | ||||
|   else | ||||
|     _err "$alg is not supported yet" | ||||
| @ -834,9 +851,9 @@ _hmac() { | ||||
| 
 | ||||
|   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then | ||||
|     if [ "$outputhex" ]; then | ||||
|       ($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 ' ' | ||||
|       (${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' ' | ||||
|     else | ||||
|       $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 | ||||
|       ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary | ||||
|     fi | ||||
|   else | ||||
|     _err "$alg is not supported yet" | ||||
| @ -855,7 +872,7 @@ _sign() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _sign_openssl="$ACME_OPENSSL_BIN   dgst -sign $keyfile " | ||||
|   _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile " | ||||
|   if [ "$alg" = "sha256" ]; then | ||||
|     _sign_openssl="$_sign_openssl -$alg" | ||||
|   else | ||||
| @ -866,10 +883,10 @@ _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 | $ACME_OPENSSL_BIN asn1parse -inform DER)"; then | ||||
|     if ! _signedECText="$($_sign_openssl | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then | ||||
|       _err "Sign failed: $_sign_openssl" | ||||
|       _err "Key file: $keyfile" | ||||
|       _err "Key content:$(wc -l <"$keyfile") lises" | ||||
|       _err "Key content:$(wc -l <"$keyfile") lines" | ||||
|       return 1 | ||||
|     fi | ||||
|     _debug3 "_signedECText" "$_signedECText" | ||||
| @ -938,10 +955,10 @@ _createkey() { | ||||
| 
 | ||||
|   if _isEccKey "$length"; then | ||||
|     _debug "Using ec name: $eccname" | ||||
|     $ACME_OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f" | ||||
|     ${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null >"$f" | ||||
|   else | ||||
|     _debug "Using RSA: $length" | ||||
|     $ACME_OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f" | ||||
|     ${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null >"$f" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
| @ -1015,7 +1032,7 @@ _createcsr() { | ||||
|     else | ||||
|       alt="DNS:$domainlist" | ||||
|     fi | ||||
|     #multi  | ||||
|     #multi | ||||
|     _info "Multi domain" "$alt" | ||||
|     printf -- "\nsubjectAltName=$alt" >>"$csrconf" | ||||
|   fi | ||||
| @ -1028,9 +1045,9 @@ _createcsr() { | ||||
|   _csr_cn="$(_idn "$domain")" | ||||
|   _debug2 _csr_cn "$_csr_cn" | ||||
|   if _contains "$(uname -a)" "MINGW"; then | ||||
|     $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr" | ||||
|     ${ACME_OPENSSL_BIN:-openssl} 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" | ||||
|     ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr" | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| @ -1042,7 +1059,7 @@ _signcsr() { | ||||
|   cert="$4" | ||||
|   _debug "_signcsr" | ||||
| 
 | ||||
|   _msg="$($ACME_OPENSSL_BIN x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)" | ||||
|   _msg="$(${ACME_OPENSSL_BIN:-openssl} x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)" | ||||
|   _ret="$?" | ||||
|   _debug "$_msg" | ||||
|   return $_ret | ||||
| @ -1055,7 +1072,7 @@ _readSubjectFromCSR() { | ||||
|     _usage "_readSubjectFromCSR mycsr.csr" | ||||
|     return 1 | ||||
|   fi | ||||
|   $ACME_OPENSSL_BIN req -noout -in "$_csrfile" -subject | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n' | ||||
|   ${ACME_OPENSSL_BIN:-openssl} req -noout -in "$_csrfile" -subject | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n' | ||||
| } | ||||
| 
 | ||||
| #_csrfile | ||||
| @ -1070,7 +1087,7 @@ _readSubjectAltNamesFromCSR() { | ||||
|   _csrsubj="$(_readSubjectFromCSR "$_csrfile")" | ||||
|   _debug _csrsubj "$_csrsubj" | ||||
| 
 | ||||
|   _dnsAltnames="$($ACME_OPENSSL_BIN req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')" | ||||
|   _dnsAltnames="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')" | ||||
|   _debug _dnsAltnames "$_dnsAltnames" | ||||
| 
 | ||||
|   if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then | ||||
| @ -1083,7 +1100,7 @@ _readSubjectAltNamesFromCSR() { | ||||
|   printf "%s" "$_dnsAltnames" | sed "s/DNS://g" | ||||
| } | ||||
| 
 | ||||
| #_csrfile  | ||||
| #_csrfile | ||||
| _readKeyLengthFromCSR() { | ||||
|   _csrfile="$1" | ||||
|   if [ -z "$_csrfile" ]; then | ||||
| @ -1091,13 +1108,14 @@ _readKeyLengthFromCSR() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _outcsr="$($ACME_OPENSSL_BIN req -noout -text -in "$_csrfile")" | ||||
|   _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")" | ||||
|   _debug2 _outcsr "$_outcsr" | ||||
|   if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then | ||||
|     _debug "ECC CSR" | ||||
|     echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' | ||||
|     echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' ' | ||||
|   else | ||||
|     _debug "RSA CSR" | ||||
|     echo "$_outcsr" | _egrep_o "(^ *|^RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1 | ||||
|     echo "$_outcsr" | tr "\t" " " | _egrep_o "(^ *|RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| @ -1121,8 +1139,12 @@ _ss() { | ||||
|       elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null; then | ||||
|         #for solaris | ||||
|         netstat -an -P tcp | grep "\.$_port " | grep "LISTEN" | ||||
|       else | ||||
|       elif netstat -help 2>&1 | grep "\-p" >/dev/null; then | ||||
|         #for full linux | ||||
|         netstat -ntpl | grep ":$_port " | ||||
|       else | ||||
|         #for busybox (embedded linux; no pid support) | ||||
|         netstat -ntl 2>/dev/null | grep ":$_port " | ||||
|       fi | ||||
|     fi | ||||
|     return 0 | ||||
| @ -1145,9 +1167,9 @@ toPkcs() { | ||||
|   _initpath "$domain" "$_isEcc" | ||||
| 
 | ||||
|   if [ "$pfxPassword" ]; then | ||||
|     $ACME_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:-openssl} pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword" | ||||
|   else | ||||
|     $ACME_OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" | ||||
|     ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" = "0" ]; then | ||||
| @ -1169,7 +1191,7 @@ toPkcs8() { | ||||
| 
 | ||||
|   _initpath "$domain" "$_isEcc" | ||||
| 
 | ||||
|   $ACME_OPENSSL_BIN pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH" | ||||
|   ${ACME_OPENSSL_BIN:-openssl} pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH" | ||||
| 
 | ||||
|   if [ "$?" = "0" ]; then | ||||
|     _info "Success, $CERT_PKCS8_PATH" | ||||
| @ -1177,7 +1199,7 @@ toPkcs8() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #[2048]   | ||||
| #[2048] | ||||
| createAccountKey() { | ||||
|   _info "Creating account key" | ||||
|   if [ -z "$1" ]; then | ||||
| @ -1330,7 +1352,7 @@ _calcjwk() { | ||||
| 
 | ||||
|   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then | ||||
|     _debug "RSA key" | ||||
|     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) | ||||
|     pub_exp=$(${ACME_OPENSSL_BIN:-openssl} 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 | ||||
| @ -1339,7 +1361,7 @@ _calcjwk() { | ||||
|     e=$(echo "$pub_exp" | _h2b | _base64) | ||||
|     _debug3 e "$e" | ||||
| 
 | ||||
|     modulus=$($ACME_OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2) | ||||
|     modulus=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2) | ||||
|     _debug3 modulus "$modulus" | ||||
|     n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)" | ||||
|     _debug3 n "$n" | ||||
| @ -1352,12 +1374,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="$($ACME_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:-openssl} 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="$($ACME_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:-openssl} 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") | ||||
| @ -1377,15 +1399,15 @@ _calcjwk() { | ||||
|       _debug3 crv "$crv" | ||||
|     fi | ||||
| 
 | ||||
|     pubi="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)" | ||||
|     pubi="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)" | ||||
|     pubi=$(_math "$pubi" + 1) | ||||
|     _debug3 pubi "$pubi" | ||||
| 
 | ||||
|     pubj="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)" | ||||
|     pubj="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)" | ||||
|     pubj=$(_math "$pubj" - 1) | ||||
|     _debug3 pubj "$pubj" | ||||
| 
 | ||||
|     pubtext="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")" | ||||
|     pubtext="$(${ACME_OPENSSL_BIN:-openssl} 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)" | ||||
| @ -1469,7 +1491,9 @@ _inithttp() { | ||||
|       _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP " | ||||
|     fi | ||||
| 
 | ||||
|     if [ "$CA_BUNDLE" ]; then | ||||
|     if [ "$CA_PATH" ]; then | ||||
|       _ACME_CURL="$_ACME_CURL --capath $CA_PATH " | ||||
|     elif [ "$CA_BUNDLE" ]; then | ||||
|       _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE " | ||||
|     fi | ||||
| 
 | ||||
| @ -1480,8 +1504,10 @@ _inithttp() { | ||||
|     if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then | ||||
|       _ACME_WGET="$_ACME_WGET -d " | ||||
|     fi | ||||
|     if [ "$CA_BUNDLE" ]; then | ||||
|       _ACME_WGET="$_ACME_WGET --ca-certificate $CA_BUNDLE " | ||||
|     if [ "$CA_PATH" ]; then | ||||
|       _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH " | ||||
|     elif [ "$CA_BUNDLE" ]; then | ||||
|       _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE " | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
| @ -1828,6 +1854,24 @@ _saveaccountconf() { | ||||
|   _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2" | ||||
| } | ||||
| 
 | ||||
| #key  value | ||||
| _saveaccountconf_mutable() { | ||||
|   _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2" | ||||
|   #remove later | ||||
|   _clearaccountconf "$1" | ||||
| } | ||||
| 
 | ||||
| #key | ||||
| _readaccountconf() { | ||||
|   _read_conf "$ACCOUNT_CONF_PATH" "$1" | ||||
| } | ||||
| 
 | ||||
| #key | ||||
| _readaccountconf_mutable() { | ||||
|   _rac_key="$1" | ||||
|   _readaccountconf "SAVED_$_rac_key" | ||||
| } | ||||
| 
 | ||||
| #_clearaccountconf   key | ||||
| _clearaccountconf() { | ||||
|   _clear_conf "$ACCOUNT_CONF_PATH" "$1" | ||||
| @ -1999,7 +2043,7 @@ _starttlsserver() { | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   __S_OPENSSL="$ACME_OPENSSL_BIN s_server -cert $TLS_CERT  -key $TLS_KEY " | ||||
|   __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -cert $TLS_CERT  -key $TLS_KEY " | ||||
|   if [ "$opaddr" ]; then | ||||
|     __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" | ||||
|   else | ||||
| @ -2240,16 +2284,16 @@ _initpath() { | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$TLS_CONF" ]; then | ||||
|     TLS_CONF="$DOMAIN_PATH/tls.valdation.conf" | ||||
|     TLS_CONF="$DOMAIN_PATH/tls.validation.conf" | ||||
|   fi | ||||
|   if [ -z "$TLS_CERT" ]; then | ||||
|     TLS_CERT="$DOMAIN_PATH/tls.valdation.cert" | ||||
|     TLS_CERT="$DOMAIN_PATH/tls.validation.cert" | ||||
|   fi | ||||
|   if [ -z "$TLS_KEY" ]; then | ||||
|     TLS_KEY="$DOMAIN_PATH/tls.valdation.key" | ||||
|     TLS_KEY="$DOMAIN_PATH/tls.validation.key" | ||||
|   fi | ||||
|   if [ -z "$TLS_CSR" ]; then | ||||
|     TLS_CSR="$DOMAIN_PATH/tls.valdation.csr" | ||||
|     TLS_CSR="$DOMAIN_PATH/tls.validation.csr" | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| @ -2367,7 +2411,7 @@ _setApache() { | ||||
|   _debug "Backup apache config file" "$httpdconf" | ||||
|   if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then | ||||
|     _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed." | ||||
|     _err "This might be a bug of $PROJECT_NAME , pleae report issue: $PROJECT" | ||||
|     _err "This might be a bug of $PROJECT_NAME , please report issue: $PROJECT" | ||||
|     return 1 | ||||
|   fi | ||||
|   _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname" | ||||
| @ -2509,7 +2553,7 @@ _setNginx() { | ||||
| location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" { | ||||
|   default_type text/plain; | ||||
|   return 200 \"\$1.$_thumbpt\"; | ||||
| }   | ||||
| } | ||||
| #NGINX_START | ||||
| " >>"$FOUND_REAL_NGINX_CONF" | ||||
| 
 | ||||
| @ -2546,7 +2590,7 @@ _checkConf() { | ||||
|   if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then | ||||
|     _debug "wildcard" | ||||
|     for _w_f in $2; do | ||||
|       if [ -f "$_w_f"] && _checkConf "$1" "$_w_f"; then | ||||
|       if [ -f "$_w_f" ] && _checkConf "$1" "$_w_f"; then | ||||
|         return 0 | ||||
|       fi | ||||
|     done | ||||
| @ -2580,10 +2624,10 @@ _checkConf() { | ||||
| _isRealNginxConf() { | ||||
|   _debug "_isRealNginxConf $1 $2" | ||||
|   if [ -f "$2" ]; then | ||||
|     for _fln in $(grep -n "^ *server_name.* $1" "$2" | cut -d : -f 1); do | ||||
|     for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do | ||||
|       _debug _fln "$_fln" | ||||
|       if [ "$_fln" ]; then | ||||
|         _start=$(cat "$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1) | ||||
|         _start=$(tr "\t" ' ' <"$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) | ||||
| @ -2592,8 +2636,8 @@ _isRealNginxConf() { | ||||
| 
 | ||||
|         _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) | ||||
|         if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then | ||||
|           _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1) | ||||
|           _debug "_end" "$_end" | ||||
|           _end_n=$(echo "$_end" | cut -d : -f 1) | ||||
|           _debug "_end_n" "$_end_n" | ||||
| @ -2865,7 +2909,7 @@ _on_issue_err() { | ||||
|         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" | ||||
|         __trigger_validation "$uri" "$keyauthorization" | ||||
|       done | ||||
|     ) | ||||
|   fi | ||||
| @ -3087,7 +3131,7 @@ __get_domain_new_authz() { | ||||
| } | ||||
| 
 | ||||
| #uri keyAuthorization | ||||
| __trigger_validaton() { | ||||
| __trigger_validation() { | ||||
|   _debug2 "tigger domain validation." | ||||
|   _t_url="$1" | ||||
|   _debug2 _t_url "$_t_url" | ||||
| @ -3096,12 +3140,16 @@ __trigger_validaton() { | ||||
|   _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}" | ||||
| } | ||||
| 
 | ||||
| #webroot, domain domainlist  keylength  | ||||
| #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 | ||||
|   if [ -z "$1" ]; then | ||||
|     _usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc." | ||||
|     return 1 | ||||
|   fi | ||||
|   _web_roots="$1" | ||||
|   _main_domain="$2" | ||||
|   _alt_domains="$3" | ||||
| @ -3467,9 +3515,12 @@ issue() { | ||||
|         if [ ! "$usingApache" ]; then | ||||
|           if webroot_owner=$(_stat "$_currentRoot"); then | ||||
|             _debug "Changing owner/group of .well-known to $webroot_owner" | ||||
|             chown -R "$webroot_owner" "$_currentRoot/.well-known" | ||||
|             if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then | ||||
|               _debug "$(cat "$_EXEC_TEMP_ERR")" | ||||
|               _exec_err >/dev/null 2>&1 | ||||
|             fi | ||||
|           else | ||||
|             _debug "not chaning owner/group of webroot" | ||||
|             _debug "not changing owner/group of webroot" | ||||
|           fi | ||||
|         fi | ||||
| 
 | ||||
| @ -3510,7 +3561,7 @@ issue() { | ||||
|       fi | ||||
|     fi | ||||
| 
 | ||||
|     if ! __trigger_validaton "$uri" "$keyauthorization"; then | ||||
|     if ! __trigger_validation "$uri" "$keyauthorization"; then | ||||
|       _err "$d:Can not get challenge: $response" | ||||
|       _clearupwebbroot "$_currentRoot" "$removelevel" "$token" | ||||
|       _clearup | ||||
| @ -3614,6 +3665,7 @@ issue() { | ||||
| 
 | ||||
|   _rcert="$response" | ||||
|   Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" | ||||
|   _debug "Le_LinkCert" "$Le_LinkCert" | ||||
|   _savedomainconf "Le_LinkCert" "$Le_LinkCert" | ||||
| 
 | ||||
|   if [ "$Le_LinkCert" ]; then | ||||
| @ -3621,7 +3673,7 @@ issue() { | ||||
| 
 | ||||
|     #if ! _get "$Le_LinkCert" | _base64 "multiline"  >> "$CERT_PATH" ; then | ||||
|     #  _debug "Get cert failed. Let's try last response." | ||||
|     #  printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"  | ||||
|     #  printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH" | ||||
|     #fi | ||||
| 
 | ||||
|     if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then | ||||
| @ -3660,16 +3712,34 @@ issue() { | ||||
|   if ! _contains "$Le_LinkIssuer" ":"; then | ||||
|     Le_LinkIssuer="$API$Le_LinkIssuer" | ||||
|   fi | ||||
| 
 | ||||
|   _debug Le_LinkIssuer "$Le_LinkIssuer" | ||||
|   _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" | ||||
| 
 | ||||
|   if [ "$Le_LinkIssuer" ]; then | ||||
|     echo "$BEGIN_CERT" >"$CA_CERT_PATH" | ||||
|     _get "$Le_LinkIssuer" | _base64 "multiline" >>"$CA_CERT_PATH" | ||||
|     echo "$END_CERT" >>"$CA_CERT_PATH" | ||||
|     _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" | ||||
|     cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" | ||||
|     _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" | ||||
|     _link_issuer_retry=0 | ||||
|     _MAX_ISSUER_RETRY=5 | ||||
|     while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do | ||||
|       _debug _link_issuer_retry "$_link_issuer_retry" | ||||
|       if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then | ||||
|         echo "$BEGIN_CERT" >"$CA_CERT_PATH" | ||||
|         _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH" | ||||
|         echo "$END_CERT" >>"$CA_CERT_PATH" | ||||
| 
 | ||||
|         _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")" | ||||
|         cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH" | ||||
|         _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")" | ||||
| 
 | ||||
|         rm -f "$CA_CERT_PATH.der" | ||||
|         break | ||||
|       fi | ||||
|       _link_issuer_retry=$(_math $_link_issuer_retry + 1) | ||||
|       _sleep "$_link_issuer_retry" | ||||
|     done | ||||
|     if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then | ||||
|       _err "Max retry for issuer ca cert is reached." | ||||
|     fi | ||||
|   else | ||||
|     _debug "No Le_LinkIssuer header found." | ||||
|   fi | ||||
| 
 | ||||
|   Le_CertCreateTime=$(_time) | ||||
| @ -3690,6 +3760,12 @@ issue() { | ||||
|     _clearaccountconf "CA_BUNDLE" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$CA_PATH" ]; then | ||||
|     _saveaccountconf CA_PATH "$CA_PATH" | ||||
|   else | ||||
|     _clearaccountconf "CA_PATH" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$HTTPS_INSECURE" ]; then | ||||
|     _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE" | ||||
|   else | ||||
| @ -3814,7 +3890,7 @@ renewAll() { | ||||
|         return "$rc" | ||||
|       else | ||||
|         _ret="$rc" | ||||
|         _err "Error renew $d, Go ahead to next one." | ||||
|         _err "Error renew $d." | ||||
|       fi | ||||
|     fi | ||||
|   done | ||||
| @ -4008,7 +4084,7 @@ deploy() { | ||||
| installcert() { | ||||
|   _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]" | ||||
|     _usage "Usage: $PROJECT_ENTRY --installcert -d domain.com  [--ecc] [--cert-file cert-file-path]  [--key-file key-file-path]  [--ca-file ca-cert-file-path]   [ --reloadCmd reloadCmd] [--fullchain-file fullchain-path]" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
| @ -4107,6 +4183,7 @@ _installcert() { | ||||
|       export CERT_KEY_PATH | ||||
|       export CA_CERT_PATH | ||||
|       export CERT_FULLCHAIN_PATH | ||||
|       export Le_Domain | ||||
|       cd "$DOMAIN_PATH" && eval "$_reload_cmd" | ||||
|     ); then | ||||
|       _info "$(__green "Reload success")" | ||||
| @ -4435,7 +4512,7 @@ _precheck() { | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   if ! _exists "$ACME_OPENSSL_BIN"; then | ||||
|   if ! _exists "${ACME_OPENSSL_BIN:-openssl}"; then | ||||
|     _err "Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN" | ||||
|     _err "We need openssl to generate keys." | ||||
|     return 1 | ||||
| @ -4618,7 +4695,7 @@ install() { | ||||
|     #Modify shebang | ||||
|     if _exists bash; then | ||||
|       _info "Good, bash is found, so change the shebang to use bash as preferred." | ||||
|       _shebang='#!/usr/bin/env bash' | ||||
|       _shebang='#!'"$(env bash -c "command -v bash")" | ||||
|       _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang" | ||||
|       for subf in $_SUB_FOLDERS; do | ||||
|         if [ -d "$LE_WORKING_DIR/$subf" ]; then | ||||
| @ -4677,6 +4754,7 @@ _uninstallalias() { | ||||
| cron() { | ||||
|   IN_CRON=1 | ||||
|   _initpath | ||||
|   _info "$(__green "===Starting cron===")" | ||||
|   if [ "$AUTO_UPGRADE" = "1" ]; then | ||||
|     export LE_WORKING_DIR | ||||
|     ( | ||||
| @ -4696,6 +4774,7 @@ cron() { | ||||
|   renewAll | ||||
|   _ret="$?" | ||||
|   IN_CRON="" | ||||
|   _info "$(__green "===End cron===")" | ||||
|   exit $_ret | ||||
| } | ||||
| 
 | ||||
| @ -4735,7 +4814,7 @@ Commands: | ||||
|   --create-domain-key      Create an domain private key, professional use. | ||||
|   --createCSR, -ccsr       Create CSR , professional use. | ||||
|   --deactivate             Deactivate the domain authz, professional use. | ||||
|    | ||||
| 
 | ||||
| Parameters: | ||||
|   --domain, -d   domain.tld         Specifies a domain, used to issue, renew or revoke etc. | ||||
|   --force, -f                       Used to force to install or force to renew a cert immediately. | ||||
| @ -4749,20 +4828,20 @@ Parameters: | ||||
|   --apache                          Use apache mode. | ||||
|   --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file]   Use dns mode or dns api. | ||||
|   --dnssleep  [$DEFAULT_DNS_SLEEP]                  The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds. | ||||
|    | ||||
| 
 | ||||
|   --keylength, -k [2048]            Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384. | ||||
|   --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: | ||||
|    | ||||
|   --certpath /path/to/real/cert/file  After issue/renew, the cert will be copied to this path. | ||||
|   --keypath /path/to/real/key/file  After issue/renew, the key will be copied to this path. | ||||
|   --capath /path/to/real/ca/file    After issue/renew, the intermediate cert will be copied to this path. | ||||
|   --fullchainpath /path/to/fullchain/file After issue/renew, the fullchain cert will be copied to this path. | ||||
|    | ||||
| 
 | ||||
|   --cert-file                       After issue/renew, the cert will be copied to this path. | ||||
|   --key-file                        After issue/renew, the key will be copied to this path. | ||||
|   --ca-file                         After issue/renew, the intermediate cert will be copied to this path. | ||||
|   --fullchain-file                  After issue/renew, the fullchain cert will be copied to this path. | ||||
| 
 | ||||
|   --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server. | ||||
| 
 | ||||
|   --accountconf                     Specifies a customized account config file. | ||||
| @ -4779,12 +4858,13 @@ Parameters: | ||||
|   --listraw                         Only used for '--list' command, list the certs in raw format. | ||||
|   --stopRenewOnError, -se           Only valid for '--renew-all' command. Stop if one cert has error in renewal. | ||||
|   --insecure                        Do not check the server certificate, in some devices, the api server's certificate may not be trusted. | ||||
|   --ca-bundle                       Specifices the path to the CA certificate bundle to verify api server's certificate. | ||||
|   --ca-bundle                       Specifies the path to the CA certificate bundle to verify api server's certificate. | ||||
|   --ca-path                         Specifies directory containing CA certificates in PEM format, used by wget or curl. | ||||
|   --nocron                          Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically. | ||||
|   --ecc                             Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR' | ||||
|   --csr                             Specifies the input csr. | ||||
|   --pre-hook                        Command to be run before obtaining any certificates. | ||||
|   --post-hook                       Command to be run after attempting to obtain/renew certificates. No matter the obain/renew is success or failed. | ||||
|   --post-hook                       Command to be run after attempting to obtain/renew certificates. No matter the obtain/renew is success or failed. | ||||
|   --renew-hook                      Command to be run once for each successfully renewed certificate. | ||||
|   --deploy-hook                     The hook file to deploy cert | ||||
|   --ocsp-must-staple, --ocsp        Generate ocsp must Staple extension. | ||||
| @ -4886,10 +4966,10 @@ _process() { | ||||
|   _webroot="" | ||||
|   _keylength="" | ||||
|   _accountkeylength="" | ||||
|   _certpath="" | ||||
|   _keypath="" | ||||
|   _capath="" | ||||
|   _fullchainpath="" | ||||
|   _cert_file="" | ||||
|   _key_file="" | ||||
|   _ca_file="" | ||||
|   _fullchain_file="" | ||||
|   _reloadcmd="" | ||||
|   _password="" | ||||
|   _accountconf="" | ||||
| @ -4905,6 +4985,7 @@ _process() { | ||||
|   _stopRenewOnError="" | ||||
|   #_insecure="" | ||||
|   _ca_bundle="" | ||||
|   _ca_path="" | ||||
|   _nocron="" | ||||
|   _ecc="" | ||||
|   _csr="" | ||||
| @ -5130,20 +5211,20 @@ _process() { | ||||
|         shift | ||||
|         ;; | ||||
| 
 | ||||
|       --certpath) | ||||
|         _certpath="$2" | ||||
|       --cert-file | --certpath) | ||||
|         _cert_file="$2" | ||||
|         shift | ||||
|         ;; | ||||
|       --keypath) | ||||
|         _keypath="$2" | ||||
|       --key-file | --keypath) | ||||
|         _key_file="$2" | ||||
|         shift | ||||
|         ;; | ||||
|       --capath) | ||||
|         _capath="$2" | ||||
|       --ca-file | --capath) | ||||
|         _ca_file="$2" | ||||
|         shift | ||||
|         ;; | ||||
|       --fullchainpath) | ||||
|         _fullchainpath="$2" | ||||
|       --fullchain-file | --fullchainpath) | ||||
|         _fullchain_file="$2" | ||||
|         shift | ||||
|         ;; | ||||
|       --reloadcmd | --reloadCmd) | ||||
| @ -5219,6 +5300,11 @@ _process() { | ||||
|         CA_BUNDLE="$_ca_bundle" | ||||
|         shift | ||||
|         ;; | ||||
|       --ca-path) | ||||
|         _ca_path="$2" | ||||
|         CA_PATH="$_ca_path" | ||||
|         shift | ||||
|         ;; | ||||
|       --nocron) | ||||
|         _nocron="1" | ||||
|         ;; | ||||
| @ -5360,7 +5446,7 @@ _process() { | ||||
|     uninstall) uninstall "$_nocron" ;; | ||||
|     upgrade) upgrade ;; | ||||
|     issue) | ||||
|       issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" | ||||
|       issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" | ||||
|       ;; | ||||
|     deploy) | ||||
|       deploy "$_domain" "$_deploy_hook" "$_ecc" | ||||
| @ -5372,7 +5458,7 @@ _process() { | ||||
|       showcsr "$_csr" "$_domain" | ||||
|       ;; | ||||
|     installcert) | ||||
|       installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_ecc" | ||||
|       installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc" | ||||
|       ;; | ||||
|     renew) | ||||
|       renew "$_domain" "$_ecc" | ||||
|  | ||||
| @ -21,8 +21,11 @@ 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). | ||||
| Currently supports Kong-v0.10.x. | ||||
| 
 | ||||
| (TODO) | ||||
| ```sh | ||||
| acme.sh --deploy -d ftp.example.com --deploy-hook kong | ||||
| ``` | ||||
| 
 | ||||
| ## 3. Deploy the cert to remote server through SSH access. | ||||
| 
 | ||||
|  | ||||
| @ -79,7 +79,7 @@ exim4_deploy() { | ||||
|         _info "Restore conf success" | ||||
|         eval "$_reload" | ||||
|       else | ||||
|         _err "Opps, error restore exim4 conf, please report bug to us." | ||||
|         _err "Oops, error restore exim4 conf, please report bug to us." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
| @ -105,7 +105,7 @@ exim4_deploy() { | ||||
|       _info "Restore conf success" | ||||
|       eval "$_reload" | ||||
|     else | ||||
|       _err "Opps, error restore exim4 conf, please report bug to us." | ||||
|       _err "Oops, error restore exim4 conf, please report bug to us." | ||||
|     fi | ||||
|     return 1 | ||||
|   fi | ||||
|  | ||||
| @ -1,13 +1,7 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # This deploy hook will deploy ssl cert on kong proxy engine based on api request_host parameter. | ||||
| # Note that ssl plugin should be available on Kong instance | ||||
| # The hook will match cdomain to request_host, in case of multiple domain it will always take the first | ||||
| # one (acme.sh behaviour). | ||||
| # If ssl config already exist it will update only cert and key not touching other parameter | ||||
| # If ssl config doesn't exist it will only upload cert and key and not set other parameter | ||||
| # Not that we deploy full chain | ||||
| # See https://getkong.org/plugins/dynamic-ssl/ for other options | ||||
| # If certificate already exist it will update only cert and key not touching other parameter | ||||
| # If certificate  doesn't exist it will only upload cert and key and not set other parameter | ||||
| # Note that we deploy full chain | ||||
| # Written by Geoffroi Genot <ggenot@voxbone.com> | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| @ -31,29 +25,32 @@ kong_deploy() { | ||||
|   _debug _cca "$_cca" | ||||
|   _debug _cfullchain "$_cfullchain" | ||||
| 
 | ||||
|   #Get uuid linked to the domain | ||||
|   uuid=$(_get "$KONG_URL/apis?request_host=$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') | ||||
|   if [ -z "$uuid" ]; then | ||||
|     _err "Unable to get Kong uuid for domain $_cdomain" | ||||
|     _err "Make sure that KONG_URL is correctly configured" | ||||
|     _err "Make sure that a Kong api request_host match the domain" | ||||
|     _err "Kong url: $KONG_URL" | ||||
|     return 1 | ||||
|   #Get ssl_uuid linked to the domain | ||||
|   ssl_uuid=$(_get "$KONG_URL/certificates/$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     _debug "Unable to get Kong ssl_uuid for domain $_cdomain" | ||||
|     _debug "Make sure that KONG_URL is correctly configured" | ||||
|     _debug "Make sure that a Kong certificate match the sni" | ||||
|     _debug "Kong url: $KONG_URL" | ||||
|     _info "No existing certificate, creating..." | ||||
|     #return 1 | ||||
|   fi | ||||
|   #Save kong url if it's succesful (First run case) | ||||
|   _saveaccountconf KONG_URL "$KONG_URL" | ||||
|   #Generate DEIM | ||||
|   delim="-----MultipartDelimeter$(date "+%s%N")" | ||||
|   delim="-----MultipartDelimiter$(date "+%s%N")" | ||||
|   nl="\015\012" | ||||
|   #Set Header | ||||
|   _H1="Content-Type: multipart/form-data; boundary=$delim" | ||||
|   #Generate data for request (Multipart/form-data with mixed content) | ||||
|   #set name to ssl | ||||
|   content="--$delim${nl}Content-Disposition: form-data; name=\"name\"${nl}${nl}ssl" | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     #set sni to domain | ||||
|     content="--$delim${nl}Content-Disposition: form-data; name=\"snis\"${nl}${nl}$_cdomain" | ||||
|   fi | ||||
|   #add key | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")" | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")" | ||||
|   #Add cert | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")" | ||||
|   content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")" | ||||
|   #Close multipart | ||||
|   content="$content${nl}--$delim--${nl}" | ||||
|   #Convert CRLF | ||||
| @ -61,18 +58,17 @@ kong_deploy() { | ||||
|   #DEBUG | ||||
|   _debug header "$_H1" | ||||
|   _debug content "$content" | ||||
|   #Check if ssl plugins is aready enabled (if not => POST else => PATCH) | ||||
|   ssl_uuid=$(_get "$KONG_URL/apis/$uuid/plugins" | _egrep_o '"id":"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"[a-zA-Z0-9\-\,\"_\:]*"name":"ssl"' | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') | ||||
|   _debug ssl_uuid "$ssl_uuid" | ||||
|   #Check if sslcreated (if not => POST else => PATCH) | ||||
| 
 | ||||
|   if [ -z "$ssl_uuid" ]; then | ||||
|     #Post certificate to Kong | ||||
|     response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins" "" "POST") | ||||
|     response=$(_post "$content" "$KONG_URL/certificates" "" "POST") | ||||
|   else | ||||
|     #patch | ||||
|     response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins/$ssl_uuid" "" "PATCH") | ||||
|     response=$(_post "$content" "$KONG_URL/certificates/$ssl_uuid" "" "PATCH") | ||||
|   fi | ||||
|   if ! [ "$(echo "$response" | _egrep_o "ssl")" = "ssl" ]; then | ||||
|     _err "An error occured with cert upload. Check response:" | ||||
|   if ! [ "$(echo "$response" | _egrep_o "created_at")" = "created_at" ]; then | ||||
|     _err "An error occurred with cert upload. Check response:" | ||||
|     _err "$response" | ||||
|     return 1 | ||||
|   fi | ||||
|  | ||||
| @ -76,7 +76,7 @@ vsftpd_deploy() { | ||||
|         _info "Restore conf success" | ||||
|         eval "$_reload" | ||||
|       else | ||||
|         _err "Opps, error restore vsftpd conf, please report bug to us." | ||||
|         _err "Oops, error restore vsftpd conf, please report bug to us." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
| @ -102,7 +102,7 @@ vsftpd_deploy() { | ||||
|       _info "Restore conf success" | ||||
|       eval "$_reload" | ||||
|     else | ||||
|       _err "Opps, error restore vsftpd conf, please report bug to us." | ||||
|       _err "Oops, error restore vsftpd conf, please report bug to us." | ||||
|     fi | ||||
|     return 1 | ||||
|   fi | ||||
|  | ||||
							
								
								
									
										106
									
								
								dnsapi/README.md
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								dnsapi/README.md
									
									
									
									
									
								
							| @ -302,7 +302,7 @@ acme.sh --issue --dns dns_freedns -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| Note that you cannot use acme.sh automatic DNS validation for FreeDNS public domains or for a subdomain that | ||||
| you create under a FreeDNS public domain.  You must own the top level domain in order to automaitcally | ||||
| you create under a FreeDNS public domain.  You must own the top level domain in order to automatically | ||||
| validate with acme.sh at FreeDNS. | ||||
| 
 | ||||
| ## 16. Use cyon.ch | ||||
| @ -394,7 +394,8 @@ acme.sh --issue --dns dns_knot -d example.com -d www.example.com | ||||
| 
 | ||||
| The `KNOT_SERVER` and `KNOT_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 20. Use NS1. API | ||||
| 
 | ||||
| ## 20. Use NS1.com API | ||||
| 
 | ||||
| ``` | ||||
| export NS1_Key="fdmlfsdklmfdkmqsdfk" | ||||
| @ -405,6 +406,107 @@ Ok, let's issue a cert now: | ||||
| acme.sh --issue --dns dns_nsone -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 20. Use DigitalOcean API (native) | ||||
| 
 | ||||
| You need to obtain a read and write capable API key from your DigitalOcean account. See: https://www.digitalocean.com/help/api/ | ||||
| 
 | ||||
| ``` | ||||
| export DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dgon -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 21. Use ClouDNS.net API | ||||
| 
 | ||||
| You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/ | ||||
| 
 | ||||
| ``` | ||||
| export CLOUDNS_AUTH_ID=XXXXX | ||||
| export CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ## 22. Use Infoblox API | ||||
| 
 | ||||
| First you need to create/obtain API credentials on your Infoblox appliance. | ||||
| 
 | ||||
| ``` | ||||
| export Infoblox_Creds="username:password" | ||||
| export Infoblox_Server="ip or fqdn of infoblox appliance" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_infoblox -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| Note: This script will automatically create and delete the ephemeral txt record. | ||||
| The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| 
 | ||||
| ## 23. Use VSCALE API | ||||
| 
 | ||||
| First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/). | ||||
| 
 | ||||
| ``` | ||||
| VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_vscale -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| ##  24. Use Dynu API | ||||
| 
 | ||||
| First you need to create/obtain API credentials from your Dynu account. See: https://www.dynu.com/resources/api/documentation | ||||
| 
 | ||||
| ``` | ||||
| export Dynu_ClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||||
| export Dynu_Secret="yyyyyyyyyyyyyyyyyyyyyyyyy" | ||||
| ``` | ||||
| 
 | ||||
| Ok, let's issue a cert now: | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dynu -d example.com -d www.example.com | ||||
| ``` | ||||
| 
 | ||||
| The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. | ||||
| 
 | ||||
| ## 25. Use DNSimple API | ||||
| 
 | ||||
| First you need to login to your DNSimple account and generate a new oauth token. | ||||
| 
 | ||||
| https://dnsimple.com/a/{your account id}/account/access_tokens | ||||
| 
 | ||||
| Note that this is an _account_ token and not a user token. The account token is | ||||
| needed to infer the `account_id` used in requests. A user token will not be able | ||||
| to determine the correct account to use. | ||||
| 
 | ||||
| ``` | ||||
| export DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| ``` | ||||
| 
 | ||||
| To issue the cert just specify the `dns_dnsimple` API. | ||||
| 
 | ||||
| ``` | ||||
| acme.sh --issue --dns dns_dnsimple -d example.com | ||||
| ``` | ||||
| 
 | ||||
| The `DNSimple_OAUTH_TOKEN` will be saved in `~/.acme.sh/account.conf` and will | ||||
| be reused when needed. | ||||
| 
 | ||||
| If you have any issues with this integration please report them to | ||||
| https://github.com/pho3nixf1re/acme.sh/issues. | ||||
| 
 | ||||
| 
 | ||||
| # Use custom API | ||||
| 
 | ||||
| If your API is not supported yet, you can write your own DNS API. | ||||
|  | ||||
| @ -88,12 +88,25 @@ _get_root() { | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       if [ -z "$h" ]; then | ||||
|         if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then | ||||
|           _debug "IsTruncated" | ||||
|           _nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)" | ||||
|           _debug "NextMarker" "$_nextMarker" | ||||
|           if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then | ||||
|             _debug "Truncated request OK" | ||||
|             i=2 | ||||
|             p=1 | ||||
|             continue | ||||
|           else | ||||
|             _err "Truncated request error." | ||||
|           fi | ||||
|         fi | ||||
|         #not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       if _contains "$response" "<Name>$h.</Name>"; then | ||||
|         hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<.HostedZone>")" | ||||
|         hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")" | ||||
|         _debug hostedzone "$hostedzone" | ||||
|         if [ -z "$hostedzone" ]; then | ||||
|           _err "Error, can not get hostedzone." | ||||
| @ -143,7 +156,7 @@ aws_rest() { | ||||
|   CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n" | ||||
|   SignedHeaders="host;x-amz-date" | ||||
|   if [ -n "$AWS_SESSION_TOKEN" ]; then | ||||
|     export _H2="x-amz-security-token: $AWS_SESSION_TOKEN" | ||||
|     export _H3="x-amz-security-token: $AWS_SESSION_TOKEN" | ||||
|     CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n" | ||||
|     SignedHeaders="${SignedHeaders};x-amz-security-token" | ||||
|   fi | ||||
| @ -204,10 +217,13 @@ aws_rest() { | ||||
|   Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" | ||||
|   _debug2 Authorization "$Authorization" | ||||
| 
 | ||||
|   _H3="Authorization: $Authorization" | ||||
|   _debug _H3 "$_H3" | ||||
|   _H2="Authorization: $Authorization" | ||||
|   _debug _H2 "$_H2" | ||||
| 
 | ||||
|   url="$AWS_URL/$ep" | ||||
|   if [ "$qsr" ]; then | ||||
|     url="$AWS_URL/$ep?$qsr" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$mtd" = "GET" ]; then | ||||
|     response="$(_get "$url")" | ||||
|  | ||||
| @ -14,6 +14,8 @@ dns_cf_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" | ||||
|   CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" | ||||
|   if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then | ||||
|     CF_Key="" | ||||
|     CF_Email="" | ||||
| @ -29,8 +31,8 @@ dns_cf_add() { | ||||
|   fi | ||||
| 
 | ||||
|   #save the api key and email to the account conf file. | ||||
|   _saveaccountconf CF_Key "$CF_Key" | ||||
|   _saveaccountconf CF_Email "$CF_Email" | ||||
|   _saveaccountconf_mutable CF_Key "$CF_Key" | ||||
|   _saveaccountconf_mutable CF_Email "$CF_Email" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
| @ -83,6 +85,17 @@ dns_cf_add() { | ||||
| dns_cf_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" | ||||
|   CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" | ||||
|   if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then | ||||
|     CF_Key="" | ||||
|     CF_Email="" | ||||
|     _err "You don't specify cloudflare api key and email yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|  | ||||
							
								
								
									
										170
									
								
								dnsapi/dns_cloudns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										170
									
								
								dnsapi/dns_cloudns.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,170 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # Author: Boyan Peychev <boyan at cloudns dot net> | ||||
| # Repository: https://github.com/ClouDNS/acme.sh/ | ||||
| 
 | ||||
| #CLOUDNS_AUTH_ID=XXXXX | ||||
| #CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" | ||||
| CLOUDNS_API="https://api.cloudns.net" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_cloudns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_cloudns_add() { | ||||
|   _info "Using cloudns" | ||||
| 
 | ||||
|   if ! _dns_cloudns_init_check; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   zone="$(_dns_cloudns_get_zone_name "$1")" | ||||
|   if [ -z "$zone" ]; then | ||||
|     _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   host="$(echo "$1" | sed "s/\.$zone\$//")" | ||||
|   record=$2 | ||||
|   record_id=$(_dns_cloudns_get_record_id "$zone" "$host") | ||||
| 
 | ||||
|   _debug zone "$zone" | ||||
|   _debug host "$host" | ||||
|   _debug record "$record" | ||||
|   _debug record_id "$record_id" | ||||
| 
 | ||||
|   if [ -z "$record_id" ]; then | ||||
|     _info "Adding the TXT record for $1" | ||||
|     _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60" | ||||
|     if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|       _err "Record cannot be added." | ||||
|       return 1 | ||||
|     fi | ||||
|     _info "Added." | ||||
|   else | ||||
|     _info "Updating the TXT record for $1" | ||||
|     _dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60" | ||||
|     if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|       _err "The TXT record for $1 cannot be updated." | ||||
|       return 1 | ||||
|     fi | ||||
|     _info "Updated." | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: dns_cloudns_rm   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_cloudns_rm() { | ||||
|   _info "Using cloudns" | ||||
| 
 | ||||
|   if ! _dns_cloudns_init_check; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$zone" ]; then | ||||
|     zone="$(_dns_cloudns_get_zone_name "$1")" | ||||
|     if [ -z "$zone" ]; then | ||||
|       _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup." | ||||
|       return 1 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   host="$(echo "$1" | sed "s/\.$zone\$//")" | ||||
|   record=$2 | ||||
|   record_id=$(_dns_cloudns_get_record_id "$zone" "$host") | ||||
| 
 | ||||
|   _debug zone "$zone" | ||||
|   _debug host "$host" | ||||
|   _debug record "$record" | ||||
|   _debug record_id "$record_id" | ||||
| 
 | ||||
|   if [ ! -z "$record_id" ]; then | ||||
|     _info "Deleting the TXT record for $1" | ||||
|     _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id" | ||||
|     if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|       _err "The TXT record for $1 cannot be deleted." | ||||
|       return 1 | ||||
|     fi | ||||
|     _info "Deleted." | ||||
|   fi | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| _dns_cloudns_init_check() { | ||||
|   if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$CLOUDNS_AUTH_ID" ]; then | ||||
|     _err "CLOUDNS_AUTH_ID is not configured" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then | ||||
|     _err "CLOUDNS_AUTH_PASSWORD is not configured" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _dns_cloudns_http_api_call "dns/login.json" "" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"status\":\"Success\""; then | ||||
|     _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   CLOUDNS_INIT_CHECK_COMPLETED=1 | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _dns_cloudns_get_zone_name() { | ||||
|   i=2 | ||||
|   while true; do | ||||
|     zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100) | ||||
| 
 | ||||
|     if [ -z "$zoneForCheck" ]; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     _debug zoneForCheck "$zoneForCheck" | ||||
| 
 | ||||
|     _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck" | ||||
| 
 | ||||
|     if ! _contains "$response" "\"status\":\"Failed\""; then | ||||
|       echo "$zoneForCheck" | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _dns_cloudns_get_record_id() { | ||||
|   _dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT" | ||||
|   if _contains "$response" "\"id\":"; then | ||||
|     echo "$response" | cut -d '"' -f 2 | ||||
|     return 0 | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| _dns_cloudns_http_api_call() { | ||||
|   method=$1 | ||||
| 
 | ||||
|   _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" | ||||
|   _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" | ||||
| 
 | ||||
|   if [ -z "$2" ]; then | ||||
|     data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" | ||||
|   else | ||||
|     data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" | ||||
|   fi | ||||
| 
 | ||||
|   response="$(_get "$CLOUDNS_API/$method?$data")" | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| @ -209,8 +209,7 @@ _rest() { | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   if ! _contains "$response" '"message":"success"'; then | ||||
|     return 1 | ||||
|   fi | ||||
|   return 0 | ||||
| 
 | ||||
|   _contains "$response" '"code":1' | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -50,7 +50,7 @@ _cyon_load_credentials() { | ||||
|   fi | ||||
| 
 | ||||
|   if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then | ||||
|     # Dummy entries to satify script checker. | ||||
|     # Dummy entries to satisfy script checker. | ||||
|     CY_Username="" | ||||
|     CY_Password="" | ||||
|     CY_OTP_Secret="" | ||||
|  | ||||
							
								
								
									
										205
									
								
								dnsapi/dns_dgon.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										205
									
								
								dnsapi/dns_dgon.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,205 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ## Will be called by acme.sh to add the txt record to your api system. | ||||
| ## returns 0 means success, otherwise error. | ||||
| 
 | ||||
| ## Author: thewer <github at thewer.com> | ||||
| ## GitHub: https://github.com/gitwer/acme.sh | ||||
| 
 | ||||
| ## | ||||
| ## Environment Variables Required: | ||||
| ## | ||||
| ## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc" | ||||
| ## | ||||
| 
 | ||||
| #####################  Public functions  ##################### | ||||
| 
 | ||||
| ## Create the text record for validation. | ||||
| ## Usage: fulldomain txtvalue | ||||
| ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs" | ||||
| dns_dgon_add() { | ||||
|   fulldomain="$(echo "$1" | _lower_case)" | ||||
|   txtvalue=$2 | ||||
|   _info "Using digitalocean dns validation - add record" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## save the env vars (key and domain split location) for later automated use | ||||
|   _saveaccountconf DO_API_KEY "$DO_API_KEY" | ||||
| 
 | ||||
|   ## split the domain for DO API | ||||
|   if ! _get_base_domain "$fulldomain"; then | ||||
|     _err "domain not found in your account for addition" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   ## Set the header with our post type and key auth key | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Authorization: Bearer $DO_API_KEY" | ||||
|   PURL='https://api.digitalocean.com/v2/domains/'$_domain'/records' | ||||
|   PBODY='{"type":"TXT","name":"'$_sub_domain'","data":"'$txtvalue'"}' | ||||
| 
 | ||||
|   _debug PURL "$PURL" | ||||
|   _debug PBODY "$PBODY" | ||||
| 
 | ||||
|   ## the create request - post | ||||
|   ## args: BODY, URL, [need64, httpmethod] | ||||
|   response="$(_post "$PBODY" "$PURL")" | ||||
| 
 | ||||
|   ## check response | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error in response: $response" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
| 
 | ||||
|   ## finished correctly | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ## Remove the txt record after validation. | ||||
| ## Usage: fulldomain txtvalue | ||||
| ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs" | ||||
| dns_dgon_rm() { | ||||
|   fulldomain="$(echo "$1" | _lower_case)" | ||||
|   txtvalue=$2 | ||||
|   _info "Using digitalocean dns validation - remove record" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## split the domain for DO API | ||||
|   if ! _get_base_domain "$fulldomain"; then | ||||
|     _err "domain not found in your account for removal" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   ## Set the header with our post type and key auth key | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Authorization: Bearer $DO_API_KEY" | ||||
|   ## get URL for the list of domains | ||||
|   ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}} | ||||
|   GURL="https://api.digitalocean.com/v2/domains/$_domain/records" | ||||
| 
 | ||||
|   ## while we dont have a record ID we keep going | ||||
|   while [ -z "$record" ]; do | ||||
|     ## 1) get the URL | ||||
|     ## the create request - get | ||||
|     ## args: URL, [onlyheader, timeout] | ||||
|     domain_list="$(_get "$GURL")" | ||||
|     ## 2) find record | ||||
|     ## check for what we are looing for: "type":"A","name":"$_sub_domain" | ||||
|     record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*\d+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" | ||||
|     ## 3) check record and get next page | ||||
|     if [ -z "$record" ]; then | ||||
|       ## find the next page if we dont have a match | ||||
|       nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=\d+")" | ||||
|       if [ -z "$nextpage" ]; then | ||||
|         _err "no record and no nextpage in digital ocean DNS removal" | ||||
|         return 1 | ||||
|       fi | ||||
|       _debug2 nextpage "$nextpage" | ||||
|       GURL="$nextpage" | ||||
|     fi | ||||
|     ## we break out of the loop when we have a record | ||||
|   done | ||||
| 
 | ||||
|   ## we found the record | ||||
|   rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*\d+" | _egrep_o "\d+")" | ||||
|   _debug rec_id "$rec_id" | ||||
| 
 | ||||
|   ## delete the record | ||||
|   ## delete URL for removing the one we dont want | ||||
|   DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id" | ||||
| 
 | ||||
|   ## the create request - delete | ||||
|   ## args: BODY, URL, [need64, httpmethod] | ||||
|   response="$(_post "" "$DURL" "" "DELETE")" | ||||
| 
 | ||||
|   ## check response (sort of) | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error in remove response: $response" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
| 
 | ||||
|   ## finished correctly | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #####################  Private functions below  ##################### | ||||
| 
 | ||||
| ## Split the domain provided into the "bade domain" and the "start prefix". | ||||
| ## This function searches for the longest subdomain in your account | ||||
| ## for the full domain given and splits it into the base domain (zone) | ||||
| ## and the prefix/record to be added/removed | ||||
| ## USAGE: fulldomain | ||||
| ## EG: "_acme-challenge.two.three.four.domain.com" | ||||
| ## returns | ||||
| ## _sub_domain="_acme-challenge.two" | ||||
| ## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists | ||||
| ## if only "domain.com" exists it will return | ||||
| ## _sub_domain="_acme-challenge.two.three.four" | ||||
| ## _domain="domain.com" | ||||
| _get_base_domain() { | ||||
|   # args | ||||
|   fulldomain="$(echo "$1" | tr '[:upper:]' '[:lower:]')" | ||||
|   _debug fulldomain "$fulldomain" | ||||
| 
 | ||||
|   # domain max legal length = 253 | ||||
|   MAX_DOM=255 | ||||
| 
 | ||||
|   ## get a list of domains for the account to check thru | ||||
|   ## Set the headers | ||||
|   export _H1="Content-Type: application/json" | ||||
|   export _H2="Authorization: Bearer $DO_API_KEY" | ||||
|   _debug DO_API_KEY "$DO_API_KEY" | ||||
|   ## get URL for the list of domains | ||||
|   ## havent seen this request paginated, tested with 18 domains (more requires manual requests with DO) | ||||
|   DOMURL="https://api.digitalocean.com/v2/domains" | ||||
| 
 | ||||
|   ## get the domain list (DO gives basically a full XFER!) | ||||
|   domain_list="$(_get "$DOMURL")" | ||||
| 
 | ||||
|   ## check response | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error in domain_list response: $domain_list" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 domain_list "$domain_list" | ||||
| 
 | ||||
|   ## for each shortening of our $fulldomain, check if it exists in the $domain_list | ||||
|   ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge" | ||||
|   i=2 | ||||
|   while [ $i -gt 0 ]; do | ||||
|     ## get next longest domain | ||||
|     _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM") | ||||
|     ## check we got something back from our cut (or are we at the end) | ||||
|     if [ -z "$_domain" ]; then | ||||
|       ## we got to the end of the domain - invalid domain | ||||
|       _err "domain not found in DigitalOcean account" | ||||
|       return 1 | ||||
|     fi | ||||
|     ## we got part of a domain back - grep it out | ||||
|     found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")" | ||||
|     ## check if it exists | ||||
|     if [ ! -z "$found" ]; then | ||||
|       ## exists - exit loop returning the parts | ||||
|       sub_point=$(_math $i - 1) | ||||
|       _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point") | ||||
|       _debug _domain "$_domain" | ||||
|       _debug _sub_domain "$_sub_domain" | ||||
|       return 0 | ||||
|     fi | ||||
|     ## increment cut point $i | ||||
|     i=$(_math $i + 1) | ||||
|   done | ||||
| 
 | ||||
|   ## we went through the entire domain zone list and dint find one that matched | ||||
|   ## doesnt look like we can add in the record | ||||
|   _err "domain not found in DigitalOcean account, but we should never get here" | ||||
|   return 1 | ||||
| } | ||||
							
								
								
									
										215
									
								
								dnsapi/dns_dnsimple.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								dnsapi/dns_dnsimple.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # DNSimple domain api | ||||
| # https://github.com/pho3nixf1re/acme.sh/issues | ||||
| # | ||||
| # This is your oauth token which can be acquired on the account page. Please | ||||
| # note that this must be an _account_ token and not a _user_ token. | ||||
| # https://dnsimple.com/a/<your account id>/account/access_tokens | ||||
| # DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| 
 | ||||
| DNSimple_API="https://api.dnsimple.com/v2" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| # Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_dnsimple_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$DNSimple_OAUTH_TOKEN" ]; then | ||||
|     DNSimple_OAUTH_TOKEN="" | ||||
|     _err "You have not set the dnsimple oauth token yet." | ||||
|     _err "Please visit https://dnsimple.com/user to generate it." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   # save the oauth token for later | ||||
|   _saveaccountconf DNSimple_OAUTH_TOKEN "$DNSimple_OAUTH_TOKEN" | ||||
| 
 | ||||
|   if ! _get_account_id; then | ||||
|     _err "failed to retrive account id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _get_records "$_account_id" "$_domain" "$_sub_domain" | ||||
| 
 | ||||
|   if [ "$_records_count" = "0" ]; then | ||||
|     _info "Adding record" | ||||
|     if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then | ||||
|       if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then | ||||
|         _info "Added" | ||||
|         return 0 | ||||
|       else | ||||
|         _err "Unexpected response while adding text record." | ||||
|         return 1 | ||||
|       fi | ||||
|     fi | ||||
|     _err "Add txt record error." | ||||
|   else | ||||
|     _info "Updating record" | ||||
|     _extract_record_id "$_records" "$_sub_domain" | ||||
| 
 | ||||
|     if _dnsimple_rest \ | ||||
|       PATCH \ | ||||
|       "$_account_id/zones/$_domain/records/$_record_id" \ | ||||
|       "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then | ||||
| 
 | ||||
|       _info "Updated!" | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     _err "Update error" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| # fulldomain | ||||
| dns_dnsimple_rm() { | ||||
|   fulldomain=$1 | ||||
| 
 | ||||
|   if ! _get_account_id; then | ||||
|     _err "failed to retrive account id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _get_records "$_account_id" "$_domain" "$_sub_domain" | ||||
|   _extract_record_id "$_records" "$_sub_domain" | ||||
| 
 | ||||
|   if [ "$_record_id" ]; then | ||||
| 
 | ||||
|     if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$_record_id"; then | ||||
|       _info "removed record" "$_record_id" | ||||
|       return 0 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _err "failed to remove record" "$_record_id" | ||||
|   return 1 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions bellow ################################## | ||||
| # _acme-challenge.www.domain.com | ||||
| # returns | ||||
| #   _sub_domain=_acme-challenge.www | ||||
| #   _domain=domain.com | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   previous=1 | ||||
|   while true; do | ||||
|     h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|     if [ -z "$h" ]; then | ||||
|       # not valid | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if ! _dnsimple_rest GET "$_account_id/zones/$h"; then | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     if _contains "$response" 'not found'; then | ||||
|       _debug "$h not found" | ||||
|     else | ||||
|       _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous) | ||||
|       _domain="$h" | ||||
| 
 | ||||
|       _debug _domain "$_domain" | ||||
|       _debug _sub_domain "$_sub_domain" | ||||
| 
 | ||||
|       return 0 | ||||
|     fi | ||||
| 
 | ||||
|     previous="$i" | ||||
|     i=$(_math "$i" + 1) | ||||
|   done | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| # returns _account_id | ||||
| _get_account_id() { | ||||
|   _debug "retrive account id" | ||||
|   if ! _dnsimple_rest GET "whoami"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$response" "\"account\":null"; then | ||||
|     _err "no account associated with this token" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if _contains "$response" "timeout"; then | ||||
|     _err "timeout retrieving account id" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _account_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"email\":" | cut -d: -f2 | cut -d, -f1) | ||||
|   _debug _account_id "$_account_id" | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| # returns | ||||
| #   _records | ||||
| #   _records_count | ||||
| _get_records() { | ||||
|   account_id=$1 | ||||
|   domain=$2 | ||||
|   sub_domain=$3 | ||||
| 
 | ||||
|   _debug "fetching txt records" | ||||
|   _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100" | ||||
| 
 | ||||
|   if ! _contains "$response" "\"id\":"; then | ||||
|     _err "failed to retrieve records" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _records_count=$(printf "%s" "$response" | _egrep_o "\"name\":\"$sub_domain\"" | wc -l | _egrep_o "[0-9]+") | ||||
|   _records=$response | ||||
|   _debug _records_count "$_records_count" | ||||
| } | ||||
| 
 | ||||
| # returns _record_id | ||||
| _extract_record_id() { | ||||
|   _record_id=$(printf "%s" "$_records" | _egrep_o "\"id\":[^,]*,\"zone_id\":\"[^,]*\",\"parent_id\":null,\"name\":\"$_sub_domain\"" | cut -d: -f2 | cut -d, -f1) | ||||
|   _debug "_record_id" "$_record_id" | ||||
| } | ||||
| 
 | ||||
| # returns response | ||||
| _dnsimple_rest() { | ||||
|   method=$1 | ||||
|   path="$2" | ||||
|   data="$3" | ||||
|   request_url="$DNSimple_API/$path" | ||||
|   _debug "$path" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Authorization: Bearer $DNSimple_OAUTH_TOKEN" | ||||
| 
 | ||||
|   if [ "$data" ] || [ "$method" = "DELETE" ]; then | ||||
|     _H1="Content-Type: application/json" | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$request_url" "" "$method")" | ||||
|   else | ||||
|     response="$(_get "$request_url" "" "" "$method")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $request_url" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
							
								
								
									
										216
									
								
								dnsapi/dns_dynu.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								dnsapi/dns_dynu.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,216 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Client ID | ||||
| #Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d" | ||||
| # | ||||
| #Secret | ||||
| #Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9" | ||||
| # | ||||
| #Token | ||||
| Dynu_Token="" | ||||
| # | ||||
| #Endpoint | ||||
| Dynu_EndPoint="https://api.dynu.com/v1" | ||||
| # | ||||
| #Author: Dynu Systems, Inc. | ||||
| #Report Bugs here: https://github.com/shar0119/acme.sh | ||||
| # | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_dynu_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then | ||||
|     Dynu_ClientId="" | ||||
|     Dynu_Secret="" | ||||
|     _err "Dynu client id and secret is not specified." | ||||
|     _err "Please create you API client id and secret and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the client id and secret to the account conf file. | ||||
|   _saveaccountconf Dynu_ClientId "$Dynu_ClientId" | ||||
|   _saveaccountconf Dynu_Secret "$Dynu_Secret" | ||||
| 
 | ||||
|   if [ -z "$Dynu_Token" ]; then | ||||
|     _info "Getting Dynu token." | ||||
|     if ! _dynu_authentication; then | ||||
|       _err "Can not get token." | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Detect root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Invalid domain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug _node "$_node" | ||||
|   _debug _domain_name "$_domain_name" | ||||
| 
 | ||||
|   _info "Creating TXT record." | ||||
|   if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "text_data"; then | ||||
|     _err "Could not add TXT record." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| #Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_dynu_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then | ||||
|     Dynu_ClientId="" | ||||
|     Dynu_Secret="" | ||||
|     _err "Dynu client id and secret is not specified." | ||||
|     _err "Please create you API client id and secret and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   #save the client id and secret to the account conf file. | ||||
|   _saveaccountconf Dynu_ClientId "$Dynu_ClientId" | ||||
|   _saveaccountconf Dynu_Secret "$Dynu_Secret" | ||||
| 
 | ||||
|   if [ -z "$Dynu_Token" ]; then | ||||
|     _info "Getting Dynu token." | ||||
|     if ! _dynu_authentication; then | ||||
|       _err "Can not get token." | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   _debug "Detect root zone." | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "Invalid domain." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _debug _node "$_node" | ||||
|   _debug _domain_name "$_domain_name" | ||||
| 
 | ||||
|   _info "Checking for TXT record." | ||||
|   if ! _get_recordid "$fulldomain" "$txtvalue"; then | ||||
|     _err "Could not get TXT record id." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$_dns_record_id" = "" ]; then | ||||
|     _err "TXT record not found." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _info "Removing TXT record." | ||||
|   if ! _delete_txt_record "$_dns_record_id"; then | ||||
|     _err "Could not remove TXT record $_dns_record_id." | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| ########  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _node=_acme-challenge.www | ||||
| # _domain_name=domain.com | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   if ! _dynu_rest GET "dns/getroot/$domain"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "domain_name"; then | ||||
|     _debug "Domain name not found." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _domain_name=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2) | ||||
|   _node=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 3 | cut -d : -f 2 | cut -d '"' -f 2) | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _get_recordid() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "$txtvalue"; then | ||||
|     _dns_record_id=0 | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   _dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | _egrep_o ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _delete_txt_record() { | ||||
|   _dns_record_id=$1 | ||||
| 
 | ||||
|   if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   if ! _contains "$response" "true"; then | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _dynu_rest() { | ||||
|   m=$1 | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
|   _debug "$ep" | ||||
| 
 | ||||
|   export _H1="Authorization: Bearer $Dynu_Token" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   if [ "$data" ]; then | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" | ||||
|   else | ||||
|     _info "Getting $Dynu_EndPoint/$ep" | ||||
|     response="$(_get "$Dynu_EndPoint/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| 
 | ||||
| _dynu_authentication() { | ||||
|   realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)" | ||||
| 
 | ||||
|   export _H1="Authorization: Basic $realm" | ||||
|   export _H2="Content-Type: application/json" | ||||
| 
 | ||||
|   response="$(_get "$Dynu_EndPoint/oauth2/token")" | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "Authentication failed." | ||||
|     return 1 | ||||
|   fi | ||||
|   if _contains "$response" "accessToken"; then | ||||
|     Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2) | ||||
|   fi | ||||
|   if _contains "$Dynu_Token" "null"; then | ||||
|     Dynu_Token="" | ||||
|   fi | ||||
| 
 | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
| @ -10,7 +10,7 @@ | ||||
| # | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| # Export FreeDNS userid and password in folowing variables... | ||||
| # Export FreeDNS userid and password in following variables... | ||||
| #  FREEDNS_User=username | ||||
| #  FREEDNS_Password=password | ||||
| # login cookie is saved in acme account config file so userid / pw | ||||
| @ -53,7 +53,7 @@ dns_freedns_add() { | ||||
|   i="$(_math "$i" - 1)" | ||||
|   sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" | ||||
| 
 | ||||
|   # Sometimes FreeDNS does not reurn the subdomain page but rather  | ||||
|   # Sometimes FreeDNS does not return the subdomain page but rather | ||||
|   # returns a page regarding becoming a premium member.  This usually | ||||
|   # happens after a period of inactivity.  Immediately trying again | ||||
|   # returns the correct subdomain page.  So, we will try twice to | ||||
| @ -65,14 +65,14 @@ dns_freedns_add() { | ||||
|     htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" | ||||
|     if [ "$?" != "0" ]; then | ||||
|       if [ "$using_cached_cookies" = "true" ]; then | ||||
|         _err "Has your FreeDNS username and password channged?  If so..." | ||||
|         _err "Has your FreeDNS username and password changed?  If so..." | ||||
|         _err "Please export as FREEDNS_User / FREEDNS_Password and try again." | ||||
|       fi | ||||
|       return 1 | ||||
|     fi | ||||
| 
 | ||||
|     # Now convert the tables in the HTML to CSV.  This litte gem from | ||||
|     # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv     | ||||
|     # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv | ||||
|     subdomain_csv="$(echo "$htmlpage" \ | ||||
|       | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \ | ||||
|       | sed 's/^[\ \t]*//g' \ | ||||
| @ -112,7 +112,7 @@ dns_freedns_add() { | ||||
|           # not produce accurate results as the value field is truncated | ||||
|           # on this webpage. To get full value we would need to load | ||||
|           # another page. However we don't really need this so long as | ||||
|           # there is only one TXT record for the acme chalenge subdomain. | ||||
|           # there is only one TXT record for the acme challenge subdomain. | ||||
|           DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')" | ||||
|           if [ $found != 0 ]; then | ||||
|             break | ||||
| @ -192,11 +192,11 @@ dns_freedns_rm() { | ||||
| 
 | ||||
|   # Need to read cookie from conf file again in case new value set | ||||
|   # during login to FreeDNS when TXT record was created. | ||||
|   # acme.sh does not have a _readaccountconf() fuction | ||||
|   # acme.sh does not have a _readaccountconf() function | ||||
|   FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" | ||||
|   _debug "FreeDNS login cookies: $FREEDNS_COOKIE" | ||||
| 
 | ||||
|   # Sometimes FreeDNS does not reurn the subdomain page but rather  | ||||
|   # Sometimes FreeDNS does not return the subdomain page but rather | ||||
|   # returns a page regarding becoming a premium member.  This usually | ||||
|   # happens after a period of inactivity.  Immediately trying again | ||||
|   # returns the correct subdomain page.  So, we will try twice to | ||||
| @ -302,12 +302,12 @@ _freedns_retrieve_subdomain_page() { | ||||
|   export _H2="Accept-Language:en-US" | ||||
|   url="https://freedns.afraid.org/subdomain/" | ||||
| 
 | ||||
|   _debug "Retrieve subdmoain page from FreeDNS" | ||||
|   _debug "Retrieve subdomain page from FreeDNS" | ||||
| 
 | ||||
|   htmlpage="$(_get "$url")" | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "FreeDNS retrieve subdomins failed bad RC from _get" | ||||
|     _err "FreeDNS retrieve subdomains failed bad RC from _get" | ||||
|     return 1 | ||||
|   elif [ -z "$htmlpage" ]; then | ||||
|     _err "FreeDNS returned empty subdomain page" | ||||
| @ -341,7 +341,7 @@ _freedns_add_txt_record() { | ||||
|     return 1 | ||||
|   elif _contains "$htmlpage" "security code was incorrect"; then | ||||
|     _debug "$htmlpage" | ||||
|     _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested seurity code" | ||||
|     _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code" | ||||
|     _err "Note that you cannot use automatic DNS validation for FreeDNS public domains" | ||||
|     return 1 | ||||
|   fi | ||||
|  | ||||
| @ -19,7 +19,7 @@ dns_gandi_livedns_add() { | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$GANDI_LIVEDNS_KEY" ]; then | ||||
|     _err "No API key specifed for Gandi LiveDNS." | ||||
|     _err "No API key specified for Gandi LiveDNS." | ||||
|     _err "Create your key and export it as GANDI_LIVEDNS_KEY" | ||||
|     return 1 | ||||
|   fi | ||||
|  | ||||
							
								
								
									
										97
									
								
								dnsapi/dns_infoblox.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								dnsapi/dns_infoblox.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| ## Infoblox API integration by Jason Keller and Elijah Tenai | ||||
| ## | ||||
| ## Report any bugs via https://github.com/jasonkeller/acme.sh | ||||
| 
 | ||||
| dns_infoblox_add() { | ||||
| 
 | ||||
|   ## Nothing to see here, just some housekeeping | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
|   baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue" | ||||
| 
 | ||||
|   _info "Using Infoblox API" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## Check for the credentials | ||||
|   if [ -z "$Infoblox_Creds" ] || [ -z "$Infoblox_Server" ]; then | ||||
|     Infoblox_Creds="" | ||||
|     Infoblox_Server="" | ||||
|     _err "You didn't specify the credentials or server yet (Infoblox_Creds and Infoblox_Server)." | ||||
|     _err "Please set them via EXPORT ([username:password] and [ip or hostname]) and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   ## Save the credentials to the account file | ||||
|   _saveaccountconf Infoblox_Creds "$Infoblox_Creds" | ||||
|   _saveaccountconf Infoblox_Server "$Infoblox_Server" | ||||
| 
 | ||||
|   ## Base64 encode the credentials | ||||
|   Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64) | ||||
| 
 | ||||
|   ## Construct the HTTP Authorization header | ||||
|   export _H1="Accept-Language:en-US" | ||||
|   export _H2="Authorization: Basic $Infoblox_CredsEncoded" | ||||
| 
 | ||||
|   ## Add the challenge record to the Infoblox grid member | ||||
|   result=$(_post "" "$baseurlnObject" "" "POST") | ||||
| 
 | ||||
|   ## Let's see if we get something intelligible back from the unit | ||||
|   if echo "$result" | egrep 'record:txt/.*:.*/default'; then | ||||
|     _info "Successfully created the txt record" | ||||
|     return 0 | ||||
|   else | ||||
|     _err "Error encountered during record addition" | ||||
|     _err "$result" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| dns_infoblox_rm() { | ||||
| 
 | ||||
|   ## Nothing to see here, just some housekeeping | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _info "Using Infoblox API" | ||||
|   _debug fulldomain "$fulldomain" | ||||
|   _debug txtvalue "$txtvalue" | ||||
| 
 | ||||
|   ## Base64 encode the credentials | ||||
|   Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64) | ||||
| 
 | ||||
|   ## Construct the HTTP Authorization header | ||||
|   export _H1="Accept-Language:en-US" | ||||
|   export _H2="Authorization: Basic $Infoblox_CredsEncoded" | ||||
| 
 | ||||
|   ## Does the record exist?  Let's check. | ||||
|   baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&_return_type=xml-pretty" | ||||
|   result=$(_get "$baseurlnObject") | ||||
| 
 | ||||
|   ## Let's see if we get something intelligible back from the grid | ||||
|   if echo "$result" | egrep 'record:txt/.*:.*/default'; then | ||||
|     ## Extract the object reference | ||||
|     objRef=$(printf "%b" "$result" | _egrep_o 'record:txt/.*:.*/default') | ||||
|     objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef" | ||||
|     ## Delete them! All the stale records! | ||||
|     rmResult=$(_post "" "$objRmUrl" "" "DELETE") | ||||
|     ## Let's see if that worked | ||||
|     if echo "$rmResult" | egrep 'record:txt/.*:.*/default'; then | ||||
|       _info "Successfully deleted $objRef" | ||||
|       return 0 | ||||
|     else | ||||
|       _err "Error occurred during txt record delete" | ||||
|       _err "$rmResult" | ||||
|       return 1 | ||||
|     fi | ||||
|   else | ||||
|     _err "Record to delete didn't match an existing record" | ||||
|     _err "$result" | ||||
|     return 1 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| @ -1,6 +1,6 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #Applcation Key | ||||
| #Application Key | ||||
| #OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| # | ||||
| #Application Secret | ||||
| @ -14,7 +14,7 @@ | ||||
| #'ovh-eu' | ||||
| OVH_EU='https://eu.api.ovh.com/1.0' | ||||
| 
 | ||||
| #'ovh-ca':  | ||||
| #'ovh-ca': | ||||
| OVH_CA='https://ca.api.ovh.com/1.0' | ||||
| 
 | ||||
| #'kimsufi-eu' | ||||
| @ -119,7 +119,7 @@ dns_ovh_add() { | ||||
| 
 | ||||
|   _info "Checking authentication" | ||||
| 
 | ||||
|   response="$(_ovh_rest GET "domain/")" | ||||
|   response="$(_ovh_rest GET "domain")" | ||||
|   if _contains "$response" "INVALID_CREDENTIAL"; then | ||||
|     _err "The consumer key is invalid: $OVH_CK" | ||||
|     _err "Please retry to create a new one." | ||||
| @ -191,7 +191,7 @@ _ovh_authentication() { | ||||
|   _H3="" | ||||
|   _H4="" | ||||
| 
 | ||||
|   _ovhdata='{"accessRules": [{"method": "GET","path": "/*"},{"method": "POST","path": "/*"},{"method": "PUT","path": "/*"},{"method": "DELETE","path": "/*"}],"redirection":"'$ovh_success'"}' | ||||
|   _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}' | ||||
| 
 | ||||
|   response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" | ||||
|   _debug3 response "$response" | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #PowerDNS Emdedded API | ||||
| #PowerDNS Embedded API | ||||
| #https://doc.powerdns.com/md/httpapi/api_spec/ | ||||
| # | ||||
| #PDNS_Url="http://ns.example.com:8081" | ||||
|  | ||||
							
								
								
									
										149
									
								
								dnsapi/dns_vscale.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										149
									
								
								dnsapi/dns_vscale.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,149 @@ | ||||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| #This is the vscale.io api wrapper for acme.sh | ||||
| # | ||||
| #Author: Alex Loban | ||||
| #Report Bugs here: https://github.com/LAV45/acme.sh | ||||
| 
 | ||||
| #VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje" | ||||
| VSCALE_API_URL="https://api.vscale.io/v1" | ||||
| 
 | ||||
| ########  Public functions ##################### | ||||
| 
 | ||||
| #Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" | ||||
| dns_vscale_add() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   if [ -z "$VSCALE_API_KEY" ]; then | ||||
|     VSCALE_API_KEY="" | ||||
|     _err "You didn't specify the VSCALE api key yet." | ||||
|     _err "Please create you key and try again." | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   _saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY" | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _domain_id "$_domain_id" | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   _vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}" | ||||
| 
 | ||||
|   if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then | ||||
|     response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2) | ||||
|     if [ -z "$response" ]; then | ||||
|       _info "txt record updated success." | ||||
|       return 0 | ||||
|     fi | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #fulldomain txtvalue | ||||
| dns_vscale_rm() { | ||||
|   fulldomain=$1 | ||||
|   txtvalue=$2 | ||||
| 
 | ||||
|   _debug "First detect the root zone" | ||||
|   if ! _get_root "$fulldomain"; then | ||||
|     _err "invalid domain" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug _domain_id "$_domain_id" | ||||
|   _debug _sub_domain "$_sub_domain" | ||||
|   _debug _domain "$_domain" | ||||
| 
 | ||||
|   _debug "Getting txt records" | ||||
|   _vscale_rest GET "domains/$_domain_id/records/" | ||||
| 
 | ||||
|   if [ -n "$response" ]; then | ||||
|     record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"") | ||||
|     _debug record_id "$record_id" | ||||
|     if [ -z "$record_id" ]; then | ||||
|       _err "Can not get record id to remove." | ||||
|       return 1 | ||||
|     fi | ||||
|     if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then | ||||
|       _info "txt record deleted success." | ||||
|       return 0 | ||||
|     fi | ||||
|     _debug response "$response" | ||||
|     return 1 | ||||
|   fi | ||||
| 
 | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| ####################  Private functions below ################################## | ||||
| #_acme-challenge.www.domain.com | ||||
| #returns | ||||
| # _sub_domain=_acme-challenge.www | ||||
| # _domain=domain.com | ||||
| # _domain_id=12345 | ||||
| _get_root() { | ||||
|   domain=$1 | ||||
|   i=2 | ||||
|   p=1 | ||||
| 
 | ||||
|   if _vscale_rest GET "domains/"; then | ||||
|     response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')" | ||||
|     while true; do | ||||
|       h=$(printf "%s" "$domain" | cut -d . -f $i-100) | ||||
|       _debug h "$h" | ||||
|       if [ -z "$h" ]; then | ||||
|         #not valid | ||||
|         return 1 | ||||
|       fi | ||||
| 
 | ||||
|       hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")" | ||||
|       if [ "$hostedzone" ]; then | ||||
|         _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ ) | ||||
|         if [ "$_domain_id" ]; then | ||||
|           _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) | ||||
|           _domain=$h | ||||
|           return 0 | ||||
|         fi | ||||
|         return 1 | ||||
|       fi | ||||
|       p=$i | ||||
|       i=$(_math "$i" + 1) | ||||
|     done | ||||
|   fi | ||||
|   return 1 | ||||
| } | ||||
| 
 | ||||
| #method uri qstr data | ||||
| _vscale_rest() { | ||||
|   mtd="$1" | ||||
|   ep="$2" | ||||
|   data="$3" | ||||
| 
 | ||||
|   _debug mtd "$mtd" | ||||
|   _debug ep "$ep" | ||||
| 
 | ||||
|   export _H1="Accept: application/json" | ||||
|   export _H2="Content-Type: application/json" | ||||
|   export _H3="X-Token: ${VSCALE_API_KEY}" | ||||
| 
 | ||||
|   if [ "$mtd" != "GET" ]; then | ||||
|     # both POST and DELETE. | ||||
|     _debug data "$data" | ||||
|     response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")" | ||||
|   else | ||||
|     response="$(_get "$VSCALE_API_URL/$ep")" | ||||
|   fi | ||||
| 
 | ||||
|   if [ "$?" != "0" ]; then | ||||
|     _err "error $ep" | ||||
|     return 1 | ||||
|   fi | ||||
|   _debug2 response "$response" | ||||
|   return 0 | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user