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 STAGE_CA
="https://acme-staging.api.letsencrypt.org"
13 if [ -z "$AGREEMENT" ] ; then
14 AGREEMENT
="$DEFAULT_AGREEMENT"
19 if [ -z "$DEBUG" ] ; then
52 h
=$
(printf $hex | cut
-c $i-$j)
63 openssl base64
-e |
tr -d '\n'
68 _info
"Creating account key"
70 echo Usage
: createAccountKey account-domain
[2048]
77 if [[ "$length" == "ec-"* ]] ; then
82 _info
"Use default length 2048"
87 if [ -f "$ACCOUNT_KEY_PATH" ] ; then
88 _info
"Account key exists, skip"
92 openssl genrsa
$length > "$ACCOUNT_KEY_PATH"
99 _info
"Creating domain key"
100 if [ -z "$1" ] ; then
101 echo Usage
: createDomainKey domain
[2048]
108 if [[ "$length" == "ec-"* ]] ; then
110 length
=$
(printf $length | cut
-d '-' -f 2-100)
114 if [ -z "$length" ] ; then
115 if [ "$isec" ] ; then
121 _info
"Use length $length"
123 if [ "$isec" ] ; then
124 if [ "$length" == "256" ] ; then
127 if [ "$length" == "384" ] ; then
130 if [ "$length" == "521" ] ; then
133 _info
"Using ec name: $eccname"
138 if [ ! -f "$CERT_KEY_PATH" ] ||
( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
139 #generate account key
140 if [ "$isec" ] ; then
141 openssl ecparam
-name $eccname -genkey 2>/dev
/null
> "$CERT_KEY_PATH"
143 openssl genrsa
$length 2>/dev
/null
> "$CERT_KEY_PATH"
146 if [ "$IS_RENEW" ] ; then
147 _info
"Domain key exists, skip"
150 _err
"Domain key exists, do you want to overwrite the key?"
151 _err
"Set FORCE=1, and try again."
161 if [ -z "$1" ] ; then
162 echo Usage
: $0 domain
[domainlist
]
170 if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && ! [ "$FORCE" ]; then
171 _info
"CSR exists, skip"
175 if [ -z "$domainlist" ] ; then
177 _info
"Single domain" $domain
178 openssl req
-new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" > "$CSR_PATH"
180 alt
="DNS:$(echo $domainlist | sed "s
/,/,DNS
:/g
")"
182 _info
"Multi domain" "$alt"
183 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt" > "$DOMAIN_SSL_CONF"
184 openssl req
-new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN
-config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
191 echo $__n |
tr '/+' '_-' |
tr -d '= '
196 if date -u -d@
$1 2>/dev
/null
; then
201 if date -u -r $1 2>/dev
/null
; then
207 _send_signed_request
() {
213 _debug payload
"$payload"
215 CURL_HEADER
="$LE_WORKING_DIR/curl.header"
216 dp
="$LE_WORKING_DIR/curl.dump"
217 CURL
="curl --silent --dump-header $CURL_HEADER "
218 if [ "$DEBUG" ] ; then
219 CURL
="$CURL --trace-ascii $dp "
221 payload64
=$
(echo -n $payload | _base64 | _b64
)
222 _debug payload64
$payload64
224 nonceurl
="$API/directory"
225 nonce
="$($CURL -I $nonceurl | grep -o "^Replay-Nonce
:.
*$
" | tr -d "\r\n" | cut -d ' ' -f 2)"
227 _debug nonce
"$nonce"
229 protected
="$(printf "$HEADERPLACE" | sed "s
/NONCE
/$nonce/" )"
230 _debug protected
"$protected"
232 protected64
="$(printf "$protected" | _base64 | _b64)"
233 _debug protected64
"$protected64"
235 sig
=$
(echo -n "$protected64.$payload64" | openssl dgst
-sha256 -sign $ACCOUNT_KEY_PATH | _base64 | _b64
)
238 body
="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
241 if [ "$needbase64" ] ; then
242 response
="$($CURL -X POST --data "$body" $url | _base64)"
244 response
="$($CURL -X POST --data "$body" $url)"
247 responseHeaders
="$(cat $CURL_HEADER)"
249 _debug responseHeaders
"$responseHeaders"
250 _debug response
"$response"
251 code
="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
259 response
="$(curl --silent $url)"
261 _debug response
"$response"
262 code
="$(echo $response | grep -o '"status
":[0-9]\+' | cut -d : -f 2)"
267 #setopt "file" "opt" "=" "value" [";"]
274 if [ -z "$__opt" ] ; then
275 echo usage
: _setopt
'"file" "opt" "=" "value" [";"]'
278 if [ ! -f "$__conf" ] ; then
282 if grep -H -n "^$__opt$__sep" "$__conf" > /dev
/null
; then
284 if [[ "$__val" == *"&"* ]] ; then
285 __val
="$(echo $__val | sed 's/&/\\&/g')"
287 text
="$(cat $__conf)"
288 printf "$text" |
sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
290 elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev
/null
; then
291 if [[ "$__val" == *"&"* ]] ; then
292 __val
="$(echo $__val | sed 's/&/\\&/g')"
294 text
="$(cat $__conf)"
295 printf "$text" |
sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
300 echo "$__opt$__sep$__val$__end" >> "$__conf"
302 _debug
"$(grep -H -n "^
$__opt$__sep" $__conf)"
305 #_savedomainconf key value
310 if [ "$DOMAIN_CONF" ] ; then
311 _setopt
$DOMAIN_CONF "$key" "=" "$value"
313 _err
"DOMAIN_CONF is empty, can not save $key=$value"
317 #_saveaccountconf key value
321 if [ "$ACCOUNT_CONF_PATH" ] ; then
322 _setopt
$ACCOUNT_CONF_PATH "$key" "=" "$value"
324 _err
"ACCOUNT_CONF_PATH is empty, can not save $key=$value"
331 if nc
-h 2>&1 |
grep "nmap.org/ncat" >/dev
/null
; then
335 if [ "$DEBUG" ] ; then
336 echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -l -p $Le_HTTPPort -vv
338 echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -l -p $Le_HTTPPort > /dev
/null
350 if [ -z "$LE_WORKING_DIR" ]; then
351 LE_WORKING_DIR
=$HOME/.le
354 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
355 ACCOUNT_CONF_PATH
="$LE_WORKING_DIR/account.conf"
358 if [ -f "$ACCOUNT_CONF_PATH" ] ; then
359 source "$ACCOUNT_CONF_PATH"
362 if [ -z "$API" ] ; then
363 if [ -z "$STAGE" ] ; then
367 _info
"Using stage api:$API"
371 if [ -z "$ACME_DIR" ] ; then
372 ACME_DIR
="/home/.acme"
375 if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
376 APACHE_CONF_BACKUP_DIR
="$LE_WORKING_DIR/"
380 if ! mkdir
-p "$LE_WORKING_DIR" ; then
381 _err
"Can not craete working dir: $LE_WORKING_DIR"
385 if [ -z "$ACCOUNT_KEY_PATH" ] ; then
386 ACCOUNT_KEY_PATH
="$LE_WORKING_DIR/account.key"
389 if [ -z "$domain" ] ; then
393 domainhome
="$LE_WORKING_DIR/$domain"
394 mkdir
-p "$domainhome"
396 if [ -z "$DOMAIN_PATH" ] ; then
397 DOMAIN_PATH
="$domainhome"
399 if [ -z "$DOMAIN_CONF" ] ; then
400 DOMAIN_CONF
="$domainhome/$Le_Domain.conf"
403 if [ -z "$DOMAIN_SSL_CONF" ] ; then
404 DOMAIN_SSL_CONF
="$domainhome/$Le_Domain.ssl.conf"
407 if [ -z "$CSR_PATH" ] ; then
408 CSR_PATH
="$domainhome/$domain.csr"
410 if [ -z "$CERT_KEY_PATH" ] ; then
411 CERT_KEY_PATH
="$domainhome/$domain.key"
413 if [ -z "$CERT_PATH" ] ; then
414 CERT_PATH
="$domainhome/$domain.cer"
416 if [ -z "$CA_CERT_PATH" ] ; then
417 CA_CERT_PATH
="$domainhome/ca.cer"
424 httpdroot
="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
425 httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
426 httpdconf
="$httpdroot/$httpdconfname"
427 if [ ! -f $httpdconf ] ; then
428 _err
"Apache Config file not found" $httpdconf
435 if [ -z "$usingApache" ] ; then
439 if ! _apachePath
; then
443 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
444 _debug
"No config file to restore."
448 cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
449 if ! apachectl
-t ; then
450 _err
"Sorry, restore apache config error, please contact me."
453 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
459 if ! _apachePath
; then
464 _debug
"Backup apache config file" $httpdconf
465 cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
466 _info
"JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
467 _info
"In case there is an error that can not be restored automatically, you may try restore it yourself."
468 _info
"The backup file will be deleted on sucess, just forget it."
472 Alias /.well-known/acme-challenge $ACME_DIR
474 <Directory $ACME_DIR >
479 if ! apachectl
-t ; then
480 _err
"Sorry, apache config error, please contact me."
485 if [ ! -d "$ACME_DIR" ] ; then
487 chmod 755 "$ACME_DIR"
490 if ! apachectl graceful
; then
491 _err
"Sorry, apachectl graceful error, please contact me."
500 _stopserver
$serverproc
505 # webroot removelevel tokenfile
508 if [ -z "$__webroot" ] ; then
509 _debug
"no webroot specified, skip"
513 if [ "$2" == '1' ] ; then
514 _debug
"remove $__webroot/.well-known"
515 rm -rf "$__webroot/.well-known"
516 elif [ "$2" == '2' ] ; then
517 _debug
"remove $__webroot/.well-known/acme-challenge"
518 rm -rf "$__webroot/.well-known/acme-challenge"
519 elif [ "$2" == '3' ] ; then
520 _debug
"remove $__webroot/.well-known/acme-challenge/$3"
521 rm -rf "$__webroot/.well-known/acme-challenge/$3"
523 _info
"Skip for removelevel:$2"
531 if [ -z "$2" ] ; then
532 _err
"Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no"
541 Le_RealCACertPath
="$7"
547 if [ -f "$DOMAIN_CONF" ] ; then
548 Le_NextRenewTime
=$
(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut
-d '=' -f 2)
549 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s
" )" -lt "$Le_NextRenewTime" ] ; then
550 _info
"Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr
" "$DOMAIN_CONF" | cut -d '=' -f 2)"
555 if [ "$Le_Alt" == "no" ] ; then
558 if [ "$Le_Keylength" == "no" ] ; then
561 if [ "$Le_RealCertPath" == "no" ] ; then
564 if [ "$Le_RealKeyPath" == "no" ] ; then
567 if [ "$Le_RealCACertPath" == "no" ] ; then
570 if [ "$Le_ReloadCmd" == "no" ] ; then
574 _setopt
"$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain"
575 _setopt
"$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt"
576 _setopt
"$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot"
577 _setopt
"$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength"
578 _setopt
"$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
579 _setopt
"$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
580 _setopt
"$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
581 _setopt
"$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
583 if [ "$Le_Webroot" == "no" ] ; then
584 _info
"Standalone mode."
585 if ! command -v "nc" > /dev
/null
; then
586 _err
"Please install netcat(nc) tools first."
590 if [ -z "$Le_HTTPPort" ] ; then
593 _setopt
"$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
595 netprc
="$(ss -ntpl | grep :$Le_HTTPPort" ")"
596 if [ "$netprc" ] ; then
598 _err
"tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
599 _err
"Please stop it first"
604 if [ "$Le_Webroot" == "apache" ] ; then
605 if ! _setApache
; then
606 _err
"set up apache error. Report error to me."
609 wellknown_path
="$ACME_DIR"
614 createAccountKey
$Le_Domain $Le_Keylength
616 if ! createDomainKey
$Le_Domain $Le_Keylength ; then
617 _err
"Create domain key error."
621 if ! createCSR
$Le_Domain $Le_Alt ; then
622 _err
"Create CSR error."
626 pub_exp
=$
(openssl rsa
-in $ACCOUNT_KEY_PATH -noout -text |
grep "^publicExponent:"| cut
-d '(' -f 2 | cut
-d 'x' -f 2 | cut
-d ')' -f 1)
627 if [ "${#pub_exp}" == "5" ] ; then
630 _debug pub_exp
"$pub_exp"
632 e
=$
(echo $pub_exp | _h2b | _base64
)
635 modulus
=$
(openssl rsa
-in $ACCOUNT_KEY_PATH -modulus -noout | cut
-d '=' -f 2 )
636 n
=$
(echo $modulus| _h2b | _base64 | _b64
)
638 jwk
='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
640 HEADER
='{"alg": "RS256", "jwk": '$jwk'}'
641 HEADERPLACE
='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
642 _debug HEADER
"$HEADER"
644 accountkey_json
=$
(echo -n "$jwk" |
tr -d ' ' )
645 thumbprint
=$
(echo -n "$accountkey_json" | openssl dgst
-sha256 -binary | _base64 | _b64
)
648 _info
"Registering account"
649 regjson
='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
650 if [ "$ACCOUNT_EMAIL" ] ; then
651 regjson
='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
653 _send_signed_request
"$API/acme/new-reg" "$regjson"
655 if [ "$code" == "" ] ||
[ "$code" == '201' ] ; then
657 echo $response > $LE_WORKING_DIR/account.json
658 elif [ "$code" == '409' ] ; then
659 _info
"Already registered"
661 _err
"Register account Error."
667 if [[ "$Le_Webroot" == "dns"* ]] ; then
673 _info
"Verify each domain"
675 if [ -z "$vlist" ] ; then
676 alldomains
=$
(echo "$Le_Domain,$Le_Alt" |
tr ',' ' ' )
679 _info
"Getting token for domain" $d
680 _send_signed_request
"$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
681 if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
682 _err
"new-authz error: $response"
687 entry
="$(printf $response | egrep -o '{[^{]*"type":"'$vtype'"[^}]*')"
688 _debug entry
"$entry"
690 token
="$(printf "$entry" | egrep -o '"token
":"[^
"]*' | cut -d : -f 2 | tr -d '"')"
693 uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
696 keyauthorization
="$token.$thumbprint"
697 _debug keyauthorization
"$keyauthorization"
699 dvlist
="$d$sep$keyauthorization$sep$uri"
700 _debug dvlist
"$dvlist"
702 vlist
="$vlist$dvlist,"
708 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
709 for ventry
in $ventries
711 d
=$
(echo $ventry | cut
-d $sep -f 1)
712 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
714 if [ "$vtype" == "$VTYPE_DNS" ] ; then
716 txtdomain
="_acme-challenge.$d"
717 _debug txtdomain
"$txtdomain"
718 txt
="$(echo -e -n $keyauthorization | openssl dgst -sha256 -binary | _base64 | _b64)"
723 if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
724 d_api
="$LE_WORKING_DIR/$d/$Le_Webroot"
725 elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
726 d_api
="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
727 elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
728 d_api
="$LE_WORKING_DIR/$Le_Webroot"
729 elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
730 d_api
="$LE_WORKING_DIR/$Le_Webroot.sh"
731 elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
732 d_api
="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
733 elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
734 d_api
="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
736 _debug d_api
"$d_api"
738 if [ "$d_api" ]; then
739 _info
"Found domain api file: $d_api"
741 _err
"Add the following TXT record:"
742 _err
"Domain: $txtdomain"
743 _err
"TXT value: $txt"
744 _err
"Please be aware that you prepend _acme-challenge. before your domain"
745 _err
"so the resulting subdomain will be: $txtdomain"
749 if ! source $d_api ; then
750 _err
"Load file $d_api error. Please check your api file and try again."
754 addcommand
="$Le_Webroot-add"
755 if ! command -v $addcommand ; then
756 _err
"It seems that your api file is not correct, it must have a function named: $Le_Webroot"
760 if ! $addcommand $txtdomain $txt ; then
761 _err
"Error add txt for domain:$txtdomain"
768 if [ "$dnsadded" == '0' ] ; then
769 _setopt
"$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\""
770 _debug
"Dns record not added yet, so, save to $DOMAIN_CONF and exit."
771 _err
"Please add the TXT records to the domains, and retry again."
777 if [ "$dnsadded" == '1' ] ; then
778 _info
"Sleep 60 seconds for the txt records to take effect"
782 _debug
"ok, let's start to verify"
783 ventries
=$
(echo "$vlist" |
tr ',' ' ' )
784 for ventry
in $ventries
786 d
=$
(echo $ventry | cut
-d $sep -f 1)
787 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
788 uri
=$
(echo $ventry | cut
-d $sep -f 3)
791 _debug
"keyauthorization" "$keyauthorization"
795 if [ "$vtype" == "$VTYPE_HTTP" ] ; then
796 if [ "$Le_Webroot" == "no" ] ; then
797 _info
"Standalone mode server"
798 _startserver
"$keyauthorization" &
801 _debug serverproc
$serverproc
803 if [ -z "$wellknown_path" ] ; then
804 wellknown_path
="$Le_Webroot/.well-known/acme-challenge"
806 _debug wellknown_path
"$wellknown_path"
808 if [ ! -d "$Le_Webroot/.well-known" ] ; then
810 elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then
816 token
="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
817 _debug
"writing token:$token to $wellknown_path/$token"
819 mkdir
-p "$wellknown_path"
820 echo -n "$keyauthorization" > "$wellknown_path/$token"
822 webroot_owner
=$
(stat
-c '%U:%G' $Le_Webroot)
823 _debug
"Changing owner/group of .well-known to $webroot_owner"
824 chown
-R $webroot_owner "$Le_Webroot/.well-known"
829 _send_signed_request
$uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
831 if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
832 _err
"$d:Challenge error: $resource"
833 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
839 _debug
"sleep 5 secs to verify"
843 if ! _get
$uri ; then
844 _err
"$d:Verify error:$resource"
845 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
850 status
=$
(echo $response |
egrep -o '"status":"[^"]+"' | cut
-d : -f 2 |
tr -d '"')
851 if [ "$status" == "valid" ] ; then
853 _stopserver
$serverproc
855 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
859 if [ "$status" == "invalid" ] ; then
860 error
=$
(echo $response |
egrep -o '"error":{[^}]*}' |
grep -o '"detail":"[^"]*"' | cut
-d '"' -f 4)
861 _err
"$d:Verify error:$error"
862 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
867 if [ "$status" == "pending" ] ; then
870 _err
"$d:Verify error:$response"
871 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
881 _info
"Verify finished, start to sign."
882 der
="$(openssl req -in $CSR_PATH -outform DER | _base64 | _b64)"
883 _send_signed_request
"$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
886 Le_LinkCert
="$(grep -i -o '^Location.*$' $CURL_HEADER | tr -d "\r\n" | cut -d " " -f 2)"
887 _setopt
"$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert"
889 if [ "$Le_LinkCert" ] ; then
890 echo -----BEGIN CERTIFICATE-----
> "$CERT_PATH"
891 curl
--silent "$Le_LinkCert" | openssl base64
-e >> "$CERT_PATH"
892 echo -----END CERTIFICATE-----
>> "$CERT_PATH"
893 _info
"Cert success."
896 _info
"Your cert is in $CERT_PATH"
900 if [ -z "$Le_LinkCert" ] ; then
901 response
="$(echo $response | openssl base64 -d -A)"
902 _err
"Sign failed: $(echo "$response" | grep -o '"detail
":"[^
"]*"')"
906 _setopt "$DOMAIN_CONF" 'Le_Vlist
' '=' "\"\""
908 Le_LinkIssuer=$(grep -i '^Link
' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
909 _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer"
911 if [ "$Le_LinkIssuer" ] ; then
912 echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
913 curl --silent "$Le_LinkIssuer" | openssl base64 -e >> "$CA_CERT_PATH"
914 echo -----END CERTIFICATE----- >> "$CA_CERT_PATH"
915 _info "The intermediate CA cert is in $CA_CERT_PATH"
918 Le_CertCreateTime=$(date -u "+%s")
919 _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
921 Le_CertCreateTimeStr=$(date -u )
922 _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
924 if [ ! "$Le_RenewalDays" ] ; then
928 _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
930 let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
931 _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
933 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
934 _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
937 installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
943 if [ -z "$Le_Domain" ] ; then
944 _err "Usage: $0 domain.com"
950 if [ ! -f "$DOMAIN_CONF" ] ; then
951 _info "$Le_Domain is not a issued domain, skip."
955 source "$DOMAIN_CONF"
956 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
957 _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
962 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
973 for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
974 d=$(echo $d | cut -d '/' -f 1)
985 Le_CertCreateTimeStr=""
988 Le_NextRenewTimeStr=""
1015 if [ -z "$Le_Domain" ] ; then
1016 _err "Usage: $0 domain.com [cert-file-path]|no [key-file-path]|no [ca-cert-file-path]|no [reloadCmd]|no"
1020 Le_RealCertPath="$2"
1022 Le_RealCACertPath="$4"
1025 _initpath $Le_Domain
1027 _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
1028 _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
1029 _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
1030 _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
1032 if [ "$Le_RealCertPath" ] ; then
1033 if [ -f "$Le_RealCertPath" ] ; then
1034 cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
1036 cat "$CERT_PATH" > "$Le_RealCertPath"
1039 if [ "$Le_RealCACertPath" ] ; then
1040 if [ -f "$Le_RealCACertPath" ] ; then
1041 cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
1043 if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
1044 echo "" >> "$Le_RealCACertPath"
1045 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
1047 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
1052 if [ "$Le_RealKeyPath" ] ; then
1053 if [ -f "$Le_RealKeyPath" ] ; then
1054 cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
1056 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
1059 if [ "$Le_ReloadCmd" ] ; then
1060 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
1061 (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd")
1068 _info "Installing cron job"
1069 if ! crontab -l | grep 'le.sh cron
' ; then
1070 if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
1071 lesh="\"$LE_WORKING_DIR\"/le.sh"
1073 _err "Can not install cronjob, le.sh not found."
1076 crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
1078 if [ "$?" != "0" ] ; then
1079 _err "Install cron job failed. You need to manually renew your certs."
1080 _err "Or you can add cronjob by yourself:"
1081 _err "LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"
1086 uninstallcronjob() {
1087 _info "Removing cron job"
1088 cr="$(crontab -l | grep 'le.sh cron
')"
1090 crontab -l | sed "/le.sh cron/d" | crontab -
1091 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 6 | cut -d '=' -f 2 | tr -d '"')"
1092 _info LE_WORKING_DIR
"$LE_WORKING_DIR"
1099 # Detect profile file if not specified as environment variable
1101 if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
1106 local DETECTED_PROFILE
1109 SHELLTYPE
="$(basename "/$SHELL")"
1111 if [ "$SHELLTYPE" = "bash" ]; then
1112 if [ -f "$HOME/.bashrc" ]; then
1113 DETECTED_PROFILE
="$HOME/.bashrc"
1114 elif [ -f "$HOME/.bash_profile" ]; then
1115 DETECTED_PROFILE
="$HOME/.bash_profile"
1117 elif [ "$SHELLTYPE" = "zsh" ]; then
1118 DETECTED_PROFILE
="$HOME/.zshrc"
1121 if [ -z "$DETECTED_PROFILE" ]; then
1122 if [ -f "$HOME/.profile" ]; then
1123 DETECTED_PROFILE
="$HOME/.profile"
1124 elif [ -f "$HOME/.bashrc" ]; then
1125 DETECTED_PROFILE
="$HOME/.bashrc"
1126 elif [ -f "$HOME/.bash_profile" ]; then
1127 DETECTED_PROFILE
="$HOME/.bash_profile"
1128 elif [ -f "$HOME/.zshrc" ]; then
1129 DETECTED_PROFILE
="$HOME/.zshrc"
1133 if [ ! -z "$DETECTED_PROFILE" ]; then
1134 echo "$DETECTED_PROFILE"
1140 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
1141 echo "#Account configurations:
1142 #Here are the supported macros, uncomment them to make them take effect.
1143 #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
1145 #STAGE=1 # Use the staging api
1146 #FORCE=1 # Force to issue cert
1147 #DEBUG=1 # Debug mode
1150 #######################
1153 #CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje
"
1155 #CF_Email="xxxx@sss.com
"
1157 #######################
1162 #DP_Key="sADDsdasdgdsf
"
1164 #######################
1168 #CX_Secret="sADDsdasdgdsf
"
1170 " > $ACCOUNT_CONF_PATH
1175 if ! _initpath
; then
1176 _err
"Install failed."
1180 #check if there is sudo installed, AND if the current user is a sudoer.
1181 if command -v sudo
> /dev
/null
; then
1182 if [ "$(sudo -n uptime 2>&1|grep "load
"|wc -l)" != "0" ] ; then
1187 if command -v yum
> /dev
/null
; then
1189 INSTALL
="$SUDO yum install -y "
1190 elif command -v apt-get
> /dev
/null
; then
1191 INSTALL
="$SUDO apt-get install -y "
1194 if ! command -v "curl" > /dev
/null
; then
1195 _err
"Please install curl first."
1196 _err
"$INSTALL curl"
1200 if ! command -v "crontab" > /dev
/null
; then
1201 _err
"Please install crontab first."
1202 if [ "$YUM" ] ; then
1203 _err
"$INSTALL crontabs"
1205 _err
"$INSTALL crontab"
1210 if ! command -v "openssl" > /dev
/null
; then
1211 _err
"Please install openssl first."
1212 _err
"$INSTALL openssl"
1216 _info
"Installing to $LE_WORKING_DIR"
1218 cp le.sh
"$LE_WORKING_DIR/" && chmod +x
"$LE_WORKING_DIR/le.sh"
1220 if [ "$?" != "0" ] ; then
1221 _err
"Install failed, can not copy le.sh"
1225 _info
"Installed to $LE_WORKING_DIR/le.sh"
1227 _profile
="$(_detect_profile)"
1228 if [ "$_profile" ] ; then
1229 _debug
"Found profile: $_profile"
1231 echo "LE_WORKING_DIR=$LE_WORKING_DIR
1232 alias le=\"$LE_WORKING_DIR/le.sh\"
1233 alias le.sh=\"$LE_WORKING_DIR/le.sh\"
1234 " > "$LE_WORKING_DIR/le.env"
1236 _setopt
"$_profile" "source \"$LE_WORKING_DIR/le.env\""
1237 _info
"OK, Close and reopen your terminal to start using le"
1239 _info
"No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
1242 mkdir
-p $LE_WORKING_DIR/dnsapi
1243 cp dnsapi
/* $LE_WORKING_DIR/dnsapi
/
1245 #to keep compatible mv the .acc file to .key file
1246 if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
1247 mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
1252 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
1262 _profile
="$(_detect_profile)"
1263 if [ "$_profile" ] ; then
1264 sed -i /le.env
/d
"$_profile"
1267 rm -f $LE_WORKING_DIR/le.sh
1268 _info
"The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
1283 echo "Usage: le.sh [command] ...[args]....
1287 Install le.sh to your system.
1291 Install the issued cert to apache/nginx or any other server.
1295 Renew all the certs.
1297 Uninstall le.sh, and uninstall the cron job.
1301 Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
1303 Uninstall the cron job. The 'uninstall' command can do this automatically.
1305 Create an account private key, professional use.
1307 Create an domain private key, professional use.
1309 Create CSR , professional use.
1314 if [ -z "$1" ] ; then