3 PROJECT
="https://github.com/Neilpang/le"
5 DEFAULT_CA
="https://acme-v01.api.letsencrypt.org"
6 DEFAULT_AGREEMENT
="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
8 DEFAULT_USER_AGENT
="le.sh client: $PROJECT"
10 STAGE_CA
="https://acme-staging.api.letsencrypt.org"
15 BEGIN_CSR
="-----BEGIN CERTIFICATE REQUEST-----"
16 END_CSR
="-----END CERTIFICATE REQUEST-----"
18 BEGIN_CERT
="-----BEGIN CERTIFICATE-----"
19 END_CERT
="-----END CERTIFICATE-----"
21 if [[ -z "$AGREEMENT" ]] ; then
22 AGREEMENT
="$DEFAULT_AGREEMENT"
27 if [[ -z "$2" ]] ; then
30 echo "[$(date)] $1"="'$2'"
40 if [[ -z "$DEBUG" ]] ; then
48 if [[ "$DEBUG" -ge "2" ]] ; then
56 if [[ -z "$cmd" ]] ; then
57 _err
"Usage: _exists cmd"
60 command -v $cmd >/dev
/null
2>&1
62 _debug2
"$cmd exists=$ret"
71 h
=$
(printf $hex | cut
-c $i-$j)
72 if [[ -z "$h" ]] ; then
85 if [[ -z "$filename" ]] ; then
86 _err
"Usage:_sed_i options filename"
90 if sed -h 2>&1 |
grep "\-i[SUFFIX]" ; then
94 _debug
"No -i support in sed"
95 text
="$(cat $filename)"
96 echo "$text" |
sed "$options" > "$filename"
100 #Usage: file startline endline
105 if [[ -z "$endline" ]] ; then
106 _err
"Usage: file startline endline"
110 i
="$(grep -n -- "$startline" $filename | cut -d : -f 1)"
111 if [[ -z "$i" ]] ; then
112 _err
"Can not find start line: $startline"
118 j
="$(grep -n -- "$endline" $filename | cut -d : -f 1)"
119 if [[ -z "$j" ]] ; then
120 _err
"Can not find end line: $endline"
126 sed -n $i,${j}p
"$filename"
135 openssl base64
-e |
tr -d '\r\n'
149 #Output Base64-encoded digest
152 if [[ -z "$alg" ]] ; then
153 _err
"Usage: _digest hashalg"
157 if [[ "$alg" == "sha256" ]] ; then
158 openssl dgst
-sha256 -binary | _base64
160 _err
"$alg is not supported yet"
166 #Usage: keyfile hashalg
167 #Output: Base64-encoded signature value
171 if [[ -z "$alg" ]] ; then
172 _err
"Usage: _sign keyfile hashalg"
176 if [[ "$alg" == "sha256" ]] ; then
177 openssl dgst
-sha256 -sign "$keyfile" | _base64
179 _err
"$alg is not supported yet"
188 if _exists
"ss" ; then
190 ss
-ntpl |
grep :$_port" "
194 if _exists
"netstat" ; then
195 _debug
"Using: netstat"
196 if netstat
-h 2>&1 |
grep "\-p proto" >/dev
/null
; then
197 #for windows version netstat tool
198 netstat
-anb -p tcp |
grep "LISTENING" |
grep :$_port" "
200 if netstat
-help 2>&1 |
grep "\-p protocol" >/dev
/null
; then
201 netstat
-an -p tcp |
grep LISTEN |
grep :$_port" "
203 netstat
-ntpl |
grep :$_port" "
215 if [[ -z "$domain" ]] ; then
216 echo "Usage: le.sh --toPkcs -d domain [--password pfx-password]"
222 if [[ "$pfxPassword" ]] ; then
223 openssl pkcs12
-export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
225 openssl pkcs12
-export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
228 if [[ "$?" == "0" ]] ; then
229 _info
"Success, Pfx is exported to: $CERT_PFX_PATH"
236 _info
"Creating account key"
237 if [[ -z "$1" ]] ; then
238 echo Usage
: le.sh
--createAccountKey -d domain.com
[--accountkeylength 2048]
245 if [[ "$length" == "ec-"* ]] ; then
249 if [[ -z "$2" ]] ; then
250 _info
"Use default length 2048"
255 if [[ -f "$ACCOUNT_KEY_PATH" ]] ; then
256 _info
"Account key exists, skip"
259 #generate account key
260 openssl genrsa
$length 2>/dev
/null
> "$ACCOUNT_KEY_PATH"
267 _info
"Creating domain key"
268 if [[ -z "$1" ]] ; then
269 echo Usage
: le.sh
--createDomainKey -d domain.com
[ --keylength 2048 ]
276 if [[ "$length" == "ec-"* ]] ; then
278 length
=$
(printf $length | cut
-d '-' -f 2-100)
282 if [[ -z "$length" ]] ; then
283 if [[ "$isec" ]] ; then
289 _info
"Use length $length"
291 if [[ "$isec" ]] ; then
292 if [[ "$length" == "256" ]] ; then
295 if [[ "$length" == "384" ]] ; then
298 if [[ "$length" == "521" ]] ; then
301 _info
"Using ec name: $eccname"
306 if [[ ! -f "$CERT_KEY_PATH" ]] ||
( [[ "$FORCE" ]] && ! [[ "$IS_RENEW" ]] ); then
307 #generate account key
308 if [[ "$isec" ]] ; then
309 openssl ecparam
-name $eccname -genkey 2>/dev
/null
> "$CERT_KEY_PATH"
311 openssl genrsa
$length 2>/dev
/null
> "$CERT_KEY_PATH"
314 if [[ "$IS_RENEW" ]] ; then
315 _info
"Domain key exists, skip"
318 _err
"Domain key exists, do you want to overwrite the key?"
319 _err
"Set FORCE=1, and try again."
329 if [[ -z "$1" ]] ; then
330 echo Usage
: le.sh
--createCSR -d domain1.com
[-d domain2.com
-d domain3.com ...
]
338 if [[ -f "$CSR_PATH" ]] && [[ "$IS_RENEW" ]] && [[ -z "$FORCE" ]]; then
339 _info
"CSR exists, skip"
343 if [[ -z "$domainlist" ]] ||
[[ "$domainlist" == "no" ]]; then
345 _info
"Single domain" $domain
346 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$DOMAIN_SSL_CONF"
347 openssl req
-new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
349 alt
="DNS:$(echo $domainlist | sed "s
/,/,DNS
:/g
")"
351 _info
"Multi domain" "$alt"
352 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt" > "$DOMAIN_SSL_CONF"
353 openssl req
-new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN
-config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
360 echo $__n |
tr '/+' '_-' |
tr -d '= '
365 if date -u -d@
$1 2>/dev
/null
; then
370 if date -u -r $1 2>/dev
/null
; then
378 if stat
-c '%U:%G' "$1" 2>/dev
/null
; then
383 if stat
-f '%Su:%Sg' "$1" 2>/dev
/null
; then
391 if [[ -z "$keyfile" ]] ; then
392 _err
"Usage: _calcjwk keyfile"
396 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev
/null
2>&1 ; then
398 pub_exp
=$
(openssl rsa
-in $keyfile -noout -text |
grep "^publicExponent:"| cut
-d '(' -f 2 | cut
-d 'x' -f 2 | cut
-d ')' -f 1)
399 if [[ "${#pub_exp}" == "5" ]] ; then
402 _debug2 pub_exp
"$pub_exp"
404 e
=$
(echo $pub_exp | _h2b | _base64
)
407 modulus
=$
(openssl rsa
-in $keyfile -modulus -noout | cut
-d '=' -f 2 )
408 n
=$
(echo $modulus| _h2b | _base64 | _urlencode
)
409 jwk
='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
412 HEADER
='{"alg": "RS256", "jwk": '$jwk'}'
413 HEADERPLACE
='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
414 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev
/null
2>&1 ; then
417 crv
="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE
:" | cut -d ":" -f 2 | tr -d " \r\n")"
420 pubi
="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
424 pubj
="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID
:" | cut -d : -f 1)"
428 pubtext
="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p
" | tr -d " \n\r")"
429 _debug2 pubtext
"$pubtext"
431 xlen
="$(printf "$pubtext" | tr -d ':' | wc -c)"
436 x
="$(printf $pubtext | cut -d : -f 2-$xend)"
439 x64
="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)"
443 y
="$(printf $pubtext | cut -d : -f $xend-10000)"
446 y64
="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)"
449 jwk
='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}'
452 HEADER
='{"alg": "ES256", "jwk": '$jwk'}'
453 HEADERPLACE
='{"nonce": "NONCE", "alg": "ES256", "jwk": '$jwk'}'
456 _err
"Only RSA or EC key is supported."
460 _debug2 HEADER
"$HEADER"
462 # body url [needbase64]
468 if _exists
"curl" ; then
469 CURL
="$CURL --dump-header $HTTP_HEADER "
470 if [[ "$needbase64" ]] ; then
471 response
="$($CURL -A "User-Agent
: $USER_AGENT" -X POST --data "$body" $url | _base64)"
473 response
="$($CURL -A "User-Agent
: $USER_AGENT" -X POST --data "$body" $url)"
476 if [[ "$needbase64" ]] ; then
477 response
="$($WGET -S -O - --user-agent="$USER_AGENT" --post-data="$body" $url 2>"$HTTP_HEADER" | _base64)"
479 response
="$($WGET -S -O - --user-agent="$USER_AGENT" --post-data="$body" $url 2>"$HTTP_HEADER")"
481 _sed_i
"s/^ *//g" "$HTTP_HEADER"
492 if _exists
"curl" ; then
493 if [[ "$onlyheader" ]] ; then
494 $CURL -I -A "User-Agent: $USER_AGENT" $url
496 $CURL -A "User-Agent: $USER_AGENT" $url
499 _debug
"WGET" "$WGET"
500 if [[ "$onlyheader" ]] ; then
501 eval $WGET --user-agent=\"$USER_AGENT\" -S -O /dev
/null
$url 2>&1 |
sed 's/^[ ]*//g'
503 eval $WGET --user-agent=\"$USER_AGENT\" -O - $url
510 # url payload needbase64 keyfile
511 _send_signed_request
() {
516 if [[ -z "$keyfile" ]] ; then
517 keyfile
="$ACCOUNT_KEY_PATH"
520 _debug payload
"$payload"
522 if ! _calcjwk
"$keyfile" ; then
526 payload64
=$
(echo -n $payload | _base64 | _urlencode
)
527 _debug2 payload64
$payload64
529 nonceurl
="$API/directory"
530 nonce
="$(_get $nonceurl "onlyheader
" | grep -o "Replay-Nonce
:.
*$
" | head -1 | tr -d "\r\n" | cut -d ' ' -f 2)"
532 _debug nonce
"$nonce"
534 protected
="$(printf "$HEADERPLACE" | sed "s
/NONCE
/$nonce/" )"
535 _debug2 protected
"$protected"
537 protected64
="$(printf "$protected" | _base64 | _urlencode)"
538 _debug2 protected64
"$protected64"
540 sig
=$
(echo -n "$protected64.$payload64" | _sign
"$keyfile" "sha256" | _urlencode
)
543 body
="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
547 response
="$(_post "$body" $url "$needbase64" )"
549 responseHeaders
="$(cat $HTTP_HEADER)"
551 _debug2 responseHeaders
"$responseHeaders"
552 _debug2 response
"$response"
553 code
="$(grep "^HTTP
" $HTTP_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
559 #setopt "file" "opt" "=" "value" [";"]
566 if [[ -z "$__opt" ]] ; then
567 echo usage
: _setopt
'"file" "opt" "=" "value" [";"]'
570 if [[ ! -f "$__conf" ]] ; then
574 if grep -H -n "^$__opt$__sep" "$__conf" > /dev
/null
; then
576 if [[ "$__val" == *"&"* ]] ; then
577 __val
="$(echo $__val | sed 's/&/\\&/g')"
579 text
="$(cat $__conf)"
580 echo "$text" |
sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
582 elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev
/null
; then
583 if [[ "$__val" == *"&"* ]] ; then
584 __val
="$(echo $__val | sed 's/&/\\&/g')"
586 text
="$(cat $__conf)"
587 echo "$text" |
sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
591 echo "$__opt$__sep$__val$__end" >> "$__conf"
593 _debug
"$(grep -H -n "^
$__opt$__sep" $__conf)"
596 #_savedomainconf key value
601 if [[ "$DOMAIN_CONF" ]] ; then
602 _setopt
$DOMAIN_CONF "$key" "=" "$value"
604 _err
"DOMAIN_CONF is empty, can not save $key=$value"
608 #_saveaccountconf key value
612 if [[ "$ACCOUNT_CONF_PATH" ]] ; then
613 _setopt
$ACCOUNT_CONF_PATH "$key" "=" "\"$value\""
615 _err
"ACCOUNT_CONF_PATH is empty, can not save $key=$value"
621 _debug
"startserver: $$"
622 nchelp
="$(nc -h 2>&1)"
624 if echo "$nchelp" |
grep "\-q[ ,]" >/dev
/null
; then
627 if echo "$nchelp" |
grep "GNU netcat" >/dev
/null
&& echo "$nchelp" |
grep "\-c, \-\-close" >/dev
/null
; then
629 elif echo "$nchelp" |
grep "\-N" |
grep "Shutdown the network socket after EOF on stdin" >/dev
/null
; then
638 if [[ "$DEBUG" ]] ; then
639 if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -p $Le_HTTPPort -vv ; then
640 echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC $Le_HTTPPort -vv ;
643 if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -p $Le_HTTPPort > /dev
/null
2>&1; then
644 echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC $Le_HTTPPort > /dev
/null
2>&1
647 if [[ "$?" != "0" ]] ; then
648 _err
"nc listen error."
657 if [[ -z "$pid" ]] ; then
661 if [[ "$(ps | grep "$pid" | grep "nc
")" ]] ; then
662 _debug
"Found nc process, kill it."
663 kill -s 9 $pid > /dev
/null
2>&1
666 _get
"http://localhost:$Le_HTTPPort" >/dev
/null
2>$1
672 if [[ -z "$LE_WORKING_DIR" ]] ; then
673 LE_WORKING_DIR
=$HOME/.le
676 _DEFAULT_ACCOUNT_CONF_PATH
="$LE_WORKING_DIR/account.conf"
678 if [[ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]] ; then
679 source "$_DEFAULT_ACCOUNT_CONF_PATH"
682 if [[ -z "$ACCOUNT_CONF_PATH" ]] ; then
683 ACCOUNT_CONF_PATH
="$_DEFAULT_ACCOUNT_CONF_PATH"
686 if [[ -f "$ACCOUNT_CONF_PATH" ]] ; then
687 source "$ACCOUNT_CONF_PATH"
690 if [[ "$IN_CRON" ]] ; then
691 if [[ ! "$_USER_PATH_EXPORTED" ]] ; then
692 _USER_PATH_EXPORTED
=1
693 export PATH
="$USER_PATH:$PATH"
697 if [[ -z "$API" ]] ; then
698 if [[ -z "$STAGE" ]] ; then
702 _info
"Using stage api:$API"
706 if [[ -z "$ACME_DIR" ]] ; then
707 ACME_DIR
="/home/.acme"
710 if [[ -z "$APACHE_CONF_BACKUP_DIR" ]] ; then
711 APACHE_CONF_BACKUP_DIR
="$LE_WORKING_DIR"
714 if [[ -z "$USER_AGENT" ]] ; then
715 USER_AGENT
="$DEFAULT_USER_AGENT"
718 HTTP_HEADER
="$LE_WORKING_DIR/http.header"
721 if [[ "$DEBUG" -ge "2" ]] ; then
725 dp
="$LE_WORKING_DIR/curl.dump"
726 CURL
="curl -L --silent"
727 if [[ "$DEBUG" -ge "2" ]] ; then
728 CURL
="$CURL -L --trace-ascii $dp "
733 if [[ -z "$ACCOUNT_KEY_PATH" ]] ; then
734 ACCOUNT_KEY_PATH
="$LE_WORKING_DIR/account.key"
737 if [[ -z "$domain" ]] ; then
741 domainhome
="$LE_WORKING_DIR/$domain"
742 mkdir
-p "$domainhome"
744 if [[ -z "$DOMAIN_PATH" ]] ; then
745 DOMAIN_PATH
="$domainhome"
747 if [[ -z "$DOMAIN_CONF" ]] ; then
748 DOMAIN_CONF
="$domainhome/$domain.conf"
751 if [[ -z "$DOMAIN_SSL_CONF" ]] ; then
752 DOMAIN_SSL_CONF
="$domainhome/$domain.ssl.conf"
755 if [[ -z "$CSR_PATH" ]] ; then
756 CSR_PATH
="$domainhome/$domain.csr"
758 if [[ -z "$CERT_KEY_PATH" ]] ; then
759 CERT_KEY_PATH
="$domainhome/$domain.key"
761 if [[ -z "$CERT_PATH" ]] ; then
762 CERT_PATH
="$domainhome/$domain.cer"
764 if [[ -z "$CA_CERT_PATH" ]] ; then
765 CA_CERT_PATH
="$domainhome/ca.cer"
767 if [[ -z "$CERT_FULLCHAIN_PATH" ]] ; then
768 CERT_FULLCHAIN_PATH
="$domainhome/fullchain.cer"
770 if [[ -z "$CERT_PFX_PATH" ]] ; then
771 CERT_PFX_PATH
="$domainhome/$domain.pfx"
777 httpdconfname
="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
778 if [[ "$httpdconfname" == '/'* ]] ; then
779 httpdconf="$httpdconfname"
780 httpdconfname="$(basename $httpdconfname)"
782 httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
783 httpdconf
="$httpdroot/$httpdconfname"
786 if [[ ! -f $httpdconf ]] ; then
787 _err
"Apache Config file not found" $httpdconf
794 if [[ -z "$usingApache" ]] ; then
798 if ! _apachePath
; then
802 if [[ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]] ; then
803 _debug
"No config file to restore."
807 cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
808 if ! apachectl
-t ; then
809 _err
"Sorry, restore apache config error, please contact me."
812 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
818 if ! _apachePath
; then
823 _debug
"Backup apache config file" $httpdconf
824 cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
825 _info
"JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
826 _info
"In case there is an error that can not be restored automatically, you may try restore it yourself."
827 _info
"The backup file will be deleted on sucess, just forget it."
831 apacheVer
="$(apachectl -V | grep "Server version
:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2 )"
832 _debug
"apacheVer" "$apacheVer"
833 apacheMajer
="$(echo "$apacheVer" | cut -d . -f 1)"
834 apacheMinor
="$(echo "$apacheVer" | cut -d . -f 2)"
836 if [[ "$apacheVer" ]] && [[ "$apacheMajer" -ge "2" ]] && [[ "$apacheMinor" -ge "4" ]] ; then
838 Alias /.well-known/acme-challenge $ACME_DIR
840 <Directory $ACME_DIR >
846 Alias /.well-known/acme-challenge $ACME_DIR
848 <Directory $ACME_DIR >
856 if ! apachectl
-t ; then
857 _err
"Sorry, apache config error, please contact me."
862 if [[ ! -d "$ACME_DIR" ]] ; then
864 chmod 755 "$ACME_DIR"
867 if ! apachectl graceful
; then
868 _err
"Sorry, apachectl graceful error, please contact me."
877 _stopserver
$serverproc
882 # webroot removelevel tokenfile
885 if [[ -z "$__webroot" ]] ; then
886 _debug
"no webroot specified, skip"
890 if [[ "$2" == '1' ]] ; then
891 _debug
"remove $__webroot/.well-known"
892 rm -rf "$__webroot/.well-known"
893 elif [[ "$2" == '2' ]] ; then
894 _debug
"remove $__webroot/.well-known/acme-challenge"
895 rm -rf "$__webroot/.well-known/acme-challenge"
896 elif [[ "$2" == '3' ]] ; then
897 _debug
"remove $__webroot/.well-known/acme-challenge/$3"
898 rm -rf "$__webroot/.well-known/acme-challenge/$3"
900 _info
"Skip for removelevel:$2"
908 if [[ -z "$2" ]] ; then
909 echo "Usage: le --issue -d a.com -w /path/to/webroot/a.com/ "
918 Le_RealCACertPath
="$7"
920 Le_RealFullChainPath
="$9"
924 if [[ -f "$DOMAIN_CONF" ]] ; then
925 Le_NextRenewTime
=$
(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut
-d '=' -f 2)
926 if [[ -z "$FORCE" ]] && [[ "$Le_NextRenewTime" ]] && [[ "$(date -u "+%s
" )" -lt "$Le_NextRenewTime" ]] ; then
927 _info
"Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr
" "$DOMAIN_CONF" | cut -d '=' -f 2)"
932 _setopt
"$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain"
933 _setopt
"$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt"
934 _setopt
"$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot"
935 _setopt
"$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength"
936 _setopt
"$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
937 _setopt
"$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
938 _setopt
"$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
939 _setopt
"$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
940 _setopt
"$DOMAIN_CONF" "Le_RealFullChainPath" "=" "\"$Le_RealFullChainPath\""
942 if [[ "$Le_Alt" == "no" ]] ; then
945 if [[ "$Le_Keylength" == "no" ]] ; then
948 if [[ "$Le_RealCertPath" == "no" ]] ; then
951 if [[ "$Le_RealKeyPath" == "no" ]] ; then
954 if [[ "$Le_RealCACertPath" == "no" ]] ; then
957 if [[ "$Le_ReloadCmd" == "no" ]] ; then
960 if [[ "$Le_RealFullChainPath" == "no" ]] ; then
961 Le_RealFullChainPath
=""
965 if [[ "$Le_Webroot" == *"no"* ]] ; then
966 _info
"Standalone mode."
967 if ! command -v "nc" > /dev
/null
; then
968 _err
"Please install netcat(nc) tools first."
972 if [[ -z "$Le_HTTPPort" ]] ; then
975 _setopt
"$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
977 netprc
="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")"
978 if [[ "$netprc" ]] ; then
980 _err
"tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
981 _err
"Please stop it first"
986 if [[ "$Le_Webroot" == *"apache"* ]] ; then
987 if ! _setApache
; then
988 _err
"set up apache error. Report error to me."
991 wellknown_path
="$ACME_DIR"
996 createAccountKey
$Le_Domain $Le_Keylength
998 if ! _calcjwk
"$ACCOUNT_KEY_PATH" ; then
1002 accountkey_json
=$
(echo -n "$jwk" |
tr -d ' ' )
1003 thumbprint
=$
(echo -n "$accountkey_json" | _digest
"sha256" | _urlencode
)
1005 accountkeyhash
="$(cat "$ACCOUNT_KEY_PATH" | _digest "sha256
" )"
1006 accountkeyhash
="$(echo $accountkeyhash$API | _digest "sha256
" )"
1007 if [[ "$accountkeyhash" != "$ACCOUNT_KEY_HASH" ]] ; then
1008 _info
"Registering account"
1009 regjson
='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
1010 if [[ "$ACCOUNT_EMAIL" ]] ; then
1011 regjson
='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
1013 _send_signed_request
"$API/acme/new-reg" "$regjson"
1015 if [[ "$code" == "" ]] ||
[[ "$code" == '201' ]] ; then
1017 echo $response > $LE_WORKING_DIR/account.json
1018 elif [[ "$code" == '409' ]] ; then
1019 _info
"Already registered"
1021 _err
"Register account Error: $response"
1025 ACCOUNT_KEY_HASH
="$accountkeyhash"
1026 _saveaccountconf
"ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH"
1028 _info
"Skip register account key"
1031 if ! createDomainKey
$Le_Domain $Le_Keylength ; then
1032 _err
"Create domain key error."
1036 if ! createCSR
$Le_Domain $Le_Alt ; then
1037 _err
"Create CSR error."
1042 # verify each domain
1043 _info
"Verify each domain"
1045 if [[ -z "$vlist" ]] ; then
1046 alldomains
=$
(echo "$Le_Domain,$Le_Alt" |
tr ',' ' ' )
1049 for d
in $alldomains
1051 _info
"Getting webroot for domain" $d
1052 _w
="$(echo $Le_Webroot | cut -d , -f $_index)"
1054 if [[ "$_w" ]] ; then
1057 _debug
"_currentRoot" "$_currentRoot"
1061 if [[ "$_currentRoot" == "dns"* ]] ; then
1064 _info
"Getting token for domain" $d
1065 _send_signed_request
"$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
1066 if [[ ! -z "$code" ]] && [[ ! "$code" == '201' ]] ; then
1067 _err
"new-authz error: $response"
1072 entry
="$(printf $response | egrep -o '\{[^{]*"type":"'$vtype'"[^}]*')"
1073 _debug entry
"$entry"
1075 token
="$(printf "$entry" | egrep -o '"token
":"[^
"]*' | cut -d : -f 2 | tr -d '"')"
1078 uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
1081 keyauthorization
="$token.$thumbprint"
1082 _debug keyauthorization
"$keyauthorization"
1084 dvlist
="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
1085 _debug dvlist
"$dvlist"
1087 vlist
="$vlist$dvlist,"
1093 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
1094 for ventry
in $ventries
1096 d
=$
(echo $ventry | cut
-d $sep -f 1)
1097 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
1098 vtype
=$
(echo $ventry | cut
-d $sep -f 4)
1099 _currentRoot
=$
(echo $ventry | cut
-d $sep -f 5)
1100 if [[ "$vtype" == "$VTYPE_DNS" ]] ; then
1102 txtdomain
="_acme-challenge.$d"
1103 _debug txtdomain
"$txtdomain"
1104 txt
="$(echo -e -n $keyauthorization | _digest "sha256
" | _urlencode)"
1109 if [[ -f "$LE_WORKING_DIR/$d/$_currentRoot" ]] ; then
1110 d_api
="$LE_WORKING_DIR/$d/$_currentRoot"
1111 elif [[ -f "$LE_WORKING_DIR/$d/$_currentRoot.sh" ]] ; then
1112 d_api
="$LE_WORKING_DIR/$d/$_currentRoot.sh"
1113 elif [[ -f "$LE_WORKING_DIR/$_currentRoot" ]] ; then
1114 d_api
="$LE_WORKING_DIR/$_currentRoot"
1115 elif [[ -f "$LE_WORKING_DIR/$_currentRoot.sh" ]] ; then
1116 d_api
="$LE_WORKING_DIR/$_currentRoot.sh"
1117 elif [[ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot" ]] ; then
1118 d_api
="$LE_WORKING_DIR/dnsapi/$_currentRoot"
1119 elif [[ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot.sh" ]] ; then
1120 d_api
="$LE_WORKING_DIR/dnsapi/$_currentRoot.sh"
1122 _debug d_api
"$d_api"
1124 if [[ "$d_api" ]] ; then
1125 _info
"Found domain api file: $d_api"
1127 _err
"Add the following TXT record:"
1128 _err
"Domain: $txtdomain"
1129 _err
"TXT value: $txt"
1130 _err
"Please be aware that you prepend _acme-challenge. before your domain"
1131 _err
"so the resulting subdomain will be: $txtdomain"
1136 if ! source $d_api ; then
1137 _err
"Load file $d_api error. Please check your api file and try again."
1141 addcommand
="$_currentRoot-add"
1142 if ! _exists
$addcommand ; then
1143 _err
"It seems that your api file is not correct, it must have a function named: $addcommand"
1147 if ! $addcommand $txtdomain $txt ; then
1148 _err
"Error add txt for domain:$txtdomain"
1153 if [[ "$?" != "0" ]] ; then
1160 if [[ "$dnsadded" == '0' ]] ; then
1161 _setopt
"$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\""
1162 _debug
"Dns record not added yet, so, save to $DOMAIN_CONF and exit."
1163 _err
"Please add the TXT records to the domains, and retry again."
1169 if [[ "$dnsadded" == '1' ]] ; then
1170 _info
"Sleep 60 seconds for the txt records to take effect"
1174 _debug
"ok, let's start to verify"
1176 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
1177 for ventry
in $ventries
1179 d
=$
(echo $ventry | cut
-d $sep -f 1)
1180 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
1181 uri
=$
(echo $ventry | cut
-d $sep -f 3)
1182 vtype
=$
(echo $ventry | cut
-d $sep -f 4)
1183 _currentRoot
=$
(echo $ventry | cut
-d $sep -f 5)
1184 _info
"Verifying:$d"
1186 _debug
"keyauthorization" "$keyauthorization"
1191 _debug
"_currentRoot" "$_currentRoot"
1194 if [[ "$vtype" == "$VTYPE_HTTP" ]] ; then
1195 if [[ "$_currentRoot" == "no" ]] ; then
1196 _info
"Standalone mode server"
1197 _startserver
"$keyauthorization" &
1198 if [[ "$?" != "0" ]] ; then
1203 _debug serverproc
$serverproc
1206 if [[ -z "$wellknown_path" ]] ; then
1207 wellknown_path
="$_currentRoot/.well-known/acme-challenge"
1209 _debug wellknown_path
"$wellknown_path"
1211 if [[ ! -d "$_currentRoot/.well-known" ]] ; then
1213 elif [[ ! -d "$_currentRoot/.well-known/acme-challenge" ]] ; then
1219 token
="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
1220 _debug
"writing token:$token to $wellknown_path/$token"
1222 mkdir
-p "$wellknown_path"
1223 echo -n "$keyauthorization" > "$wellknown_path/$token"
1224 if [[ ! "$usingApache" ]] ; then
1225 webroot_owner
=$
(_stat
$_currentRoot)
1226 _debug
"Changing owner/group of .well-known to $webroot_owner"
1227 chown
-R $webroot_owner "$_currentRoot/.well-known"
1233 _send_signed_request
$uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
1235 if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then
1236 _err
"$d:Challenge error: $response"
1237 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
1243 if [[ -z "$MAX_RETRY_TIMES" ]] ; then
1247 while [[ "1" ]] ; do
1249 if [[ "$waittimes" -ge "$MAX_RETRY_TIMES" ]] ; then
1251 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
1256 _debug
"sleep 5 secs to verify"
1259 response
="$(_get $uri)"
1260 if [[ "$?" != "0" ]] ; then
1261 _err
"$d:Verify error:$response"
1262 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
1267 status
=$
(echo $response |
egrep -o '"status":"[^"]*' | cut
-d : -f 2 |
tr -d '"')
1268 if [[ "$status" == "valid" ]] ; then
1270 _stopserver
$serverproc
1272 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
1276 if [[ "$status" == "invalid" ]] ; then
1277 error
=$
(echo $response |
egrep -o '"error":{[^}]*}' |
grep -o '"detail":"[^"]*"' | cut
-d '"' -f 4)
1278 _err
"$d:Verify error:$error"
1279 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
1284 if [[ "$status" == "pending" ]] ; then
1287 _err
"$d:Verify error:$response"
1288 _clearupwebbroot
"$_currentRoot" "$removelevel" "$token"
1298 _info
"Verify finished, start to sign."
1299 der
="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)"
1300 _send_signed_request
"$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
1303 Le_LinkCert
="$(grep -i -o '^Location.*$' $HTTP_HEADER | head -1 | tr -d "\r\n" | cut -d " " -f 2)"
1304 _setopt
"$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert"
1306 if [[ "$Le_LinkCert" ]] ; then
1307 echo "$BEGIN_CERT" > "$CERT_PATH"
1308 _get
"$Le_LinkCert" | _base64
"multiline" >> "$CERT_PATH"
1309 echo "$END_CERT" >> "$CERT_PATH"
1310 _info
"Cert success."
1313 _info
"Your cert is in $CERT_PATH"
1314 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
1316 if [[ ! "$USER_PATH" ]] ||
[[ ! "$IN_CRON" ]] ; then
1318 _saveaccountconf
"USER_PATH" "$USER_PATH"
1323 if [[ -z "$Le_LinkCert" ]] ; then
1324 response
="$(echo $response | _dbase64 "multiline
" )"
1325 _err
"Sign failed: $(echo "$response" | grep -o '"detail
":"[^
"]*"')"
1329 _setopt "$DOMAIN_CONF" 'Le_Vlist
' '=' "\"\""
1331 Le_LinkIssuer=$(grep -i '^Link
' $HTTP_HEADER | head -1 | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
1332 _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer"
1334 if [[ "$Le_LinkIssuer" ]] ; then
1335 echo "$BEGIN_CERT" > "$CA_CERT_PATH"
1336 _get "$Le_LinkIssuer" | _base64 "multiline" >> "$CA_CERT_PATH"
1337 echo "$END_CERT" >> "$CA_CERT_PATH"
1338 _info "The intermediate CA cert is in $CA_CERT_PATH"
1339 cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
1340 _info "And the full chain certs is there: $CERT_FULLCHAIN_PATH"
1343 Le_CertCreateTime=$(date -u "+%s")
1344 _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
1346 Le_CertCreateTimeStr=$(date -u )
1347 _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
1349 if [[ ! "$Le_RenewalDays" ]] ; then
1353 _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
1355 let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
1356 _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
1358 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
1359 _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
1362 installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
1368 if [[ -z "$Le_Domain" ]] ; then
1369 _err "Usage: le.sh --renew -d domain.com"
1373 _initpath $Le_Domain
1375 if [[ ! -f "$DOMAIN_CONF" ]] ; then
1376 _info "$Le_Domain is not a issued domain, skip."
1380 source "$DOMAIN_CONF"
1381 if [[ -z "$FORCE" ]] && [[ "$Le_NextRenewTime" ]] && [[ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ]] ; then
1382 _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
1387 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
1398 for d in $(ls -F ${LE_WORKING_DIR}/ | grep [^.].*[.].*/$ ) ; do
1399 d=$(echo $d | cut -d '/' -f 1)
1409 Le_CertCreateTime=""
1410 Le_CertCreateTimeStr=""
1413 Le_NextRenewTimeStr=""
1418 Le_RealCACertPath=""
1421 Le_RealFullChainPath=""
1431 CERT_FULLCHAIN_PATH=""
1443 if [[ -z "$Le_Domain" ]] ; then
1444 echo "Usage: le.sh --installcert -d domain.com [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
1448 Le_RealCertPath="$2"
1450 Le_RealCACertPath="$4"
1452 Le_RealFullChainPath="$6"
1454 _initpath $Le_Domain
1456 _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
1457 _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
1458 _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
1459 _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
1460 _setopt "$DOMAIN_CONF" "Le_RealFullChainPath" "=" "\"$Le_RealFullChainPath\""
1462 if [[ "$Le_RealCertPath" ]] ; then
1463 if [[ -f "$Le_RealCertPath" ]] ; then
1464 cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
1466 cat "$CERT_PATH" > "$Le_RealCertPath"
1469 if [[ "$Le_RealCACertPath" ]] ; then
1470 if [[ "$Le_RealCACertPath" == "$Le_RealCertPath" ]] ; then
1471 echo "" >> "$Le_RealCACertPath"
1472 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
1474 if [[ -f "$Le_RealCACertPath" ]] ; then
1475 cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
1477 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
1482 if [[ "$Le_RealKeyPath" ]] ; then
1483 if [[ -f "$Le_RealKeyPath" ]] ; then
1484 cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
1486 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
1489 if [[ "$Le_RealFullChainPath" ]] ; then
1490 if [[ -f "$Le_RealFullChainPath" ]] ; then
1491 cp -p "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
1493 cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
1496 if [[ "$Le_ReloadCmd" ]] ; then
1497 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
1498 (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd")
1505 if ! _exists "crontab" ; then
1506 _err "crontab doesn't exist
, so
, we can not
install cron
jobs.
"
1507 _err "All your certs will not be renewed automatically.
"
1508 _err "You must add your own cron job to call
'le.sh cron' everyday.
"
1512 _info "Installing cron job
"
1513 if ! crontab -l | grep 'le.sh cron' ; then
1514 if [[ -f "$LE_WORKING_DIR/le.sh
" ]] ; then
1515 lesh="\"$LE_WORKING_DIR\"/le.sh
"
1517 _err "Can not
install cronjob
, le.sh not found.
"
1520 crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR
=\"$LE_WORKING_DIR\" $lesh cron
> /dev
/null
"; } | crontab -
1522 if [[ "$?
" != "0" ]] ; then
1523 _err "Install cron job failed. You need to manually renew your certs.
"
1524 _err "Or you can add cronjob by yourself
:"
1525 _err "LE_WORKING_DIR
=\"$LE_WORKING_DIR\" $lesh cron
> /dev
/null
"
1530 uninstallcronjob() {
1531 if ! _exists "crontab
" ; then
1534 _info "Removing cron job
"
1535 cr="$
(crontab
-l |
grep 'le.sh cron')"
1536 if [[ "$cr" ]] ; then
1537 crontab -l | sed "/le.sh cron
/d
" | crontab -
1538 LE_WORKING_DIR="$
(echo "$cr" | cut
-d ' ' -f 6 | cut
-d '=' -f 2 |
tr -d '"')"
1539 _info LE_WORKING_DIR "$LE_WORKING_DIR"
1547 if [[ -z "$Le_Domain" ]] ; then
1548 echo "Usage
: le.sh
--revoke -d domain.com
"
1552 _initpath $Le_Domain
1553 if [[ ! -f "$DOMAIN_CONF" ]] ; then
1554 _err "$Le_Domain is not a issued domain
, skip.
"
1558 if [[ ! -f "$CERT_PATH" ]] ; then
1559 _err "Cert
for $Le_Domain $CERT_PATH is not found
, skip.
"
1563 cert="$
(_getfile
"${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"|
tr -d "\r\n" | _urlencode
)"
1565 if [[ -z "$cert" ]] ; then
1566 _err "Cert
for $Le_Domain is empty found
, skip.
"
1570 data="{\"resource
\": \"revoke-cert
\", \"certificate
\": \"$cert\"}"
1571 uri="$API/acme
/revoke-cert
"
1573 _info "Try domain key first.
"
1574 if _send_signed_request $uri "$data" "" "$CERT_KEY_PATH"; then
1575 if [[ -z "$response" ]] ; then
1576 _info "Revoke success.
"
1580 _err "Revoke error by domain key.
"
1585 _info "Then try account key.
"
1587 if _send_signed_request $uri "$data" "" "$ACCOUNT_KEY_PATH" ; then
1588 if [[ -z "$response" ]] ; then
1589 _info "Revoke success.
"
1593 _err "Revoke error.
"
1600 # Detect profile file if not specified as environment variable
1602 if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then
1607 local DETECTED_PROFILE
1610 SHELLTYPE="$
(basename "/$SHELL")"
1612 if [[ "$SHELLTYPE" = "bash
" ]] ; then
1613 if [[ -f "$HOME/.bashrc
" ]] ; then
1614 DETECTED_PROFILE="$HOME/.bashrc
"
1615 elif [[ -f "$HOME/.bash_profile
" ]] ; then
1616 DETECTED_PROFILE="$HOME/.bash_profile
"
1618 elif [[ "$SHELLTYPE" = "zsh
" ]] ; then
1619 DETECTED_PROFILE="$HOME/.zshrc
"
1622 if [[ -z "$DETECTED_PROFILE" ]] ; then
1623 if [[ -f "$HOME/.profile
" ]] ; then
1624 DETECTED_PROFILE="$HOME/.profile
"
1625 elif [[ -f "$HOME/.bashrc
" ]] ; then
1626 DETECTED_PROFILE="$HOME/.bashrc
"
1627 elif [[ -f "$HOME/.bash_profile
" ]] ; then
1628 DETECTED_PROFILE="$HOME/.bash_profile
"
1629 elif [[ -f "$HOME/.zshrc
" ]] ; then
1630 DETECTED_PROFILE="$HOME/.zshrc
"
1634 if [[ ! -z "$DETECTED_PROFILE" ]] ; then
1635 echo "$DETECTED_PROFILE"
1641 if [[ ! -f "$ACCOUNT_CONF_PATH" ]] ; then
1642 echo "#ACCOUNT_CONF_PATH=xxxx
1644 #Account configurations:
1645 #Here are the supported macros, uncomment them to make them take effect.
1647 #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
1648 #ACCOUNT_KEY_PATH=\"/path/to/account.key\"
1650 #STAGE=1 # Use the staging api
1651 #FORCE=1 # Force to issue cert
1652 #DEBUG=1 # Debug mode
1654 #ACCOUNT_KEY_HASH=account key hash
1656 USER_AGENT
=\"le.sh client
: $PROJECT\"
1661 #######################
1664 #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
1666 #CF_Email=\"xxxx@sss.com\"
1668 #######################
1673 #DP_Key=\"sADDsdasdgdsf\"
1675 #######################
1679 #CX_Secret=\"sADDsdasdgdsf\"
1681 " > $ACCOUNT_CONF_PATH
1686 if ! _exists "curl
" && ! _exists "wget
"; then
1687 _err "Please
install curl or wget first
, we need to access http resources.
"
1691 if ! _exists "crontab
" ; then
1692 _err "It is recommended to
install crontab first. try to
install 'cron, crontab, crontabs or vixie-cron'.
"
1693 _err "We need to
set cron job to renew the certs automatically.
"
1694 _err "Otherwise
, your certs will not be able to be renewed automatically.
"
1695 if [[ -z "$FORCE" ]] ; then
1696 _err "Please define
'FORCE=1' and try
install again to go without crontab.
"
1697 _err "FORCE
=1 .
/le.sh
install"
1702 if ! _exists "openssl
" ; then
1703 _err "Please
install openssl first.
"
1704 _err "We need openssl to generate keys.
"
1708 if ! _exists "nc
" ; then
1709 _err "It is recommended to
install nc first
, try to
install 'nc' or
'netcat'.
"
1710 _err "We use nc
for standalone server
if you use standalone mode.
"
1711 _err "If you don
't use standalone mode, just ignore this warning."
1718 if ! _initpath ; then
1719 _err "Install failed."
1723 if ! _precheck ; then
1724 _err "Pre-check failed, can not install."
1728 _info "Installing to $LE_WORKING_DIR"
1730 if ! mkdir -p "$LE_WORKING_DIR" ; then
1731 _err "Can not craete working dir: $LE_WORKING_DIR"
1735 cp le.sh "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/le.sh"
1737 if [[ "$?" != "0" ]] ; then
1738 _err "Install failed, can not copy le.sh"
1742 _info "Installed to $LE_WORKING_DIR/le.sh"
1744 _profile="$(_detect_profile)"
1745 if [[ "$_profile" ]] ; then
1746 _debug "Found profile: $_profile"
1748 echo "LE_WORKING_DIR=$LE_WORKING_DIR
1749 alias le=\"$LE_WORKING_DIR/le.sh\"
1750 alias le.sh=\"$LE_WORKING_DIR/le.sh\"
1751 " > "$LE_WORKING_DIR/le.env"
1752 echo "" >> "$_profile"
1753 _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
1754 _info "OK, Close and reopen your terminal to start using le"
1756 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
1759 mkdir -p $LE_WORKING_DIR/dnsapi
1760 cp dnsapi/* $LE_WORKING_DIR/dnsapi/
1762 #to keep compatible mv the .acc file to .key file
1763 if [[ -f "$LE_WORKING_DIR/account.acc" ]] ; then
1764 mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
1767 if [[ ! -f "$ACCOUNT_CONF_PATH" ]] ; then
1771 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
1772 _setopt "$ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
1783 _profile="$(_detect_profile)"
1784 if [[ "$_profile" ]] ; then
1785 text="$(cat $_profile)"
1786 echo "$text" | sed "s|^source.*le.env.*$||" > "$_profile"
1789 rm -f $LE_WORKING_DIR/le.sh
1790 _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
1807 echo "Usage: le.sh command ...[parameters]....
1809 --help, -h Show this help message.
1810 --version, -v Show version info.
1811 --install Install le.sh to your system.
1812 --uninstall Uninstall le.sh, and uninstall the cron job.
1813 --issue Issue a cert.
1814 --installcert Install the issued cert to apache/nginx or any other server.
1815 --renew, -r Renew a cert.
1816 --renewAll Renew all the certs
1817 --revoke Revoke a cert.
1818 --installcronjob Install the cron job to renew certs, you don't need to call this. The
'install' command can automatically
install the cron job.
1819 --uninstallcronjob Uninstall the cron job. The
'uninstall' command can
do this automatically.
1820 --cron Run cron job to renew all the certs.
1821 --toPkcs Export the certificate and key to a pfx
file.
1822 --createAccountKey, -cak Create an account private key
, professional use.
1823 --createDomainKey, -cdk Create an domain private key
, professional use.
1824 --createCSR, -ccsr Create CSR
, professional use.
1827 --domain, -d domain.tld Specifies a domain
, used to issue
, renew or revoke etc.
1828 --force, -f Used to force to
install or force to renew a cert immediately.
1829 --staging, --test Use staging server
, just
for test.
1830 --debug Output debug info.
1832 --webroot, -w /path
/to
/webroot Specifies the web root folder
for web root mode.
1833 --standalone Use standalone mode.
1834 --apache Use apache mode.
1835 --dns [dns-cf|dns-dp|dns-cx|
/path
/to
/api
/file] Use dns mode or dns api.
1837 --keylength, -k [2048] Specifies the domain key length
: 2048, 3072, 4096, 8192 or ec-256
, ec-384.
1838 --accountkeylength, -ak [2048] Specifies the account key length.
1840 These parameters are to
install the cert to nginx
/apache or anyother server after issue
/renew a cert
:
1842 --certpath /path
/to
/real
/cert
/file After issue
/renew
, the cert will be copied to this path.
1843 --keypath /path
/to
/real
/key
/file After issue
/renew
, the key will be copied to this path.
1844 --capath /path
/to
/real
/ca
/file After issue
/renew
, the intermediate cert will be copied to this path.
1845 --fullchainpath /path
/to
/fullchain
/file After issue
/renew
, the fullchain cert will be copied to this path.
1847 --reloadcmd \"service nginx reload
\" After issue
/renew
, it
's used to reload the server.
1849 --accountconf Specifies a customized account config file.
1850 --leworkingdir Specifies the home dir for le.sh
1856 _info "Installing from online archive."
1857 if [[ ! "$BRANCH" ]] ; then
1861 target="$PROJECT/archive/$BRANCH.tar.gz"
1862 _info "Downloading $target"
1863 localname="$BRANCH.tar.gz"
1864 if ! _get "$target" > $localname ; then
1865 _debug "Download error."
1868 _info "Extracting $localname"
1872 if ./le.sh install ; then
1873 _info "Install success!"
1888 _accountkeylength="no"
1895 while (( ${#} )); do
1921 --renewAll|-renewall)
1928 _CMD="installcronjob"
1931 _CMD="uninstallcronjob"
1939 --createAccountKey|--createaccountkey|-cak)
1940 _CMD="createAccountKey"
1942 --createDomainKey|--createdomainkey|-cdk)
1943 _CMD="createDomainKey"
1945 --createCSR|--createcsr|-ccr)
1953 if [[ -z "$_dvalue" ]] || [[ "$_dvalue" == "-"* ]] ; then
1954 _err "'$_dvalue' is not a valid domain for parameter '$1'"
1958 if [[ -z "$_domain" ]] ; then
1961 if [[ "$_altdomains" == "no" ]] ; then
1962 _altdomains="$_dvalue"
1964 _altdomains="$_altdomains,$_dvalue"
1977 if [[ "$2" == "-"* ]] || [[ -z "$2" ]]; then
1986 if [[ -z "$_webroot" ]] ; then
1989 _webroot="$_webroot,$wvalue"
1995 if [[ -z "$_webroot" ]] ; then
1998 _webroot="$_webroot,$wvalue"
2003 if [[ -z "$_webroot" ]] ; then
2006 _webroot="$_webroot,$wvalue"
2011 if [[ "$2" != "-"* ]] ; then
2015 if [[ -z "$_webroot" ]] ; then
2018 _webroot="$_webroot,$wvalue"
2023 accountkeylength="$2"
2026 --accountkeylength|-ak)
2027 accountkeylength="$2"
2056 ACCOUNT_CONF_PATH="$2"
2063 _err "Unknown parameter : $1"
2074 uninstall) uninstall ;;
2076 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keylength" "$_capath" "$_reloadcmd" "$_fullchainpath"
2079 installcert "$_domain" "$_certpath" "$_keylength" "$_capath" "$_reloadcmd" "$_fullchainpath"
2090 installcronjob) installcronjob ;;
2091 uninstallcronjob) uninstallcronjob ;;
2094 toPkcs "$_domain" "$_password"
2097 createAccountKey "$_domain" "$_accountkeylength"
2100 createDomainKey "$_domain" "$_keylength"
2103 createCSR "$_domain" "$_altdomains"
2107 _err "Invalid command: $_CMD"
2116 if [[ "$INSTALLONLINE" ]] ; then
2118 _installOnline $BRANCH
2122 if [[ -z "$1" ]] ; then
2125 if [[ "$1" == "-"* ]] ; then