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 openssl req
-new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN
-config <(printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt") -out "$CSR_PATH"
190 echo $__n |
tr '/+' '_-' |
tr -d '= '
193 _send_signed_request
() {
199 _debug payload
"$payload"
201 CURL_HEADER
="$LE_WORKING_DIR/curl.header"
202 dp
="$LE_WORKING_DIR/curl.dump"
203 CURL
="curl --silent --dump-header $CURL_HEADER "
204 if [ "$DEBUG" ] ; then
205 CURL
="$CURL --trace-ascii $dp "
207 payload64
=$
(echo -n $payload | _base64 | _b64
)
208 _debug payload64
$payload64
210 nonceurl
="$API/directory"
211 nonce
=$
($CURL -I $nonceurl |
grep "^Replay-Nonce:" |
sed s
/\\r
//|
sed s
/\\n
//| cut
-d ' ' -f 2)
215 protected
=$
(echo -n "$HEADERPLACE" |
sed "s/NONCE/$nonce/" )
216 _debug protected
"$protected"
218 protected64
=$
( echo -n $protected | _base64 | _b64
)
219 _debug protected64
"$protected64"
221 sig
=$
(echo -n "$protected64.$payload64" | openssl dgst
-sha256 -sign $ACCOUNT_KEY_PATH | _base64 | _b64
)
224 body
="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
227 if [ "$needbase64" ] ; then
228 response
="$($CURL -X POST --data "$body" $url | _base64)"
230 response
="$($CURL -X POST --data "$body" $url)"
233 responseHeaders
="$(sed 's/\r//g' $CURL_HEADER)"
235 _debug responseHeaders
"$responseHeaders"
236 _debug response
"$response"
237 code
="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2)"
245 response
="$(curl --silent $url)"
247 _debug response
"$response"
248 code
="$(echo $response | grep -o '"status
":[0-9]\+' | cut -d : -f 2)"
253 #setopt "file" "opt" "=" "value" [";"]
260 if [ -z "$__opt" ] ; then
261 echo usage
: $0 '"file" "opt" "=" "value" [";"]'
264 if [ ! -f "$__conf" ] ; then
267 if grep -H -n "^$__opt$__sep" "$__conf" > /dev
/null
; then
269 if [[ "$__val" == *"&"* ]] ; then
270 __val
="$(echo $__val | sed 's/&/\\&/g')"
272 sed -i "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" "$__conf"
275 echo "$__opt$__sep$__val$__end" >> "$__conf"
277 _debug
"$(grep -H -n "^
$__opt$__sep" $__conf)"
280 #_savedomainconf key value
285 if [ "$DOMAIN_CONF" ] ; then
286 _setopt
$DOMAIN_CONF "$key" "=" "$value"
288 _err
"DOMAIN_CONF is empty, can not save $key=$value"
292 #_saveaccountconf key value
296 if [ "$ACCOUNT_CONF_PATH" ] ; then
297 _setopt
$ACCOUNT_CONF_PATH "$key" "=" "$value"
299 _err
"ACCOUNT_CONF_PATH is empty, can not save $key=$value"
306 if nc
-h 2>&1 |
grep "nmap.org/ncat" >/dev
/null
; then
310 if [ "$DEBUG" ] ; then
311 echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -l -p 80 -vv
313 echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" |
$_NC -l -p 80 > /dev
/null
325 #check if there is sudo installed, AND if the current user is a sudoer.
326 if command -v sudo
> /dev
/null
; then
327 if [ "$(sudo -n uptime 2>&1|grep "load
"|wc -l)" != "0" ] ; then
332 if [ -z "$LE_WORKING_DIR" ]; then
333 LE_WORKING_DIR
=$HOME/.le
336 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
337 ACCOUNT_CONF_PATH
="$LE_WORKING_DIR/account.conf"
340 if [ -f "$ACCOUNT_CONF_PATH" ] ; then
341 source "$ACCOUNT_CONF_PATH"
344 if [ -z "$API" ] ; then
345 if [ -z "$STAGE" ] ; then
349 _info
"Using stage api:$API"
353 if [ -z "$ACME_DIR" ] ; then
354 ACME_DIR
="/home/.acme"
357 if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
358 APACHE_CONF_BACKUP_DIR
="$LE_WORKING_DIR/"
362 mkdir
-p "$LE_WORKING_DIR"
364 if [ -z "$ACCOUNT_KEY_PATH" ] ; then
365 ACCOUNT_KEY_PATH
="$LE_WORKING_DIR/account.key"
368 if [ -z "$domain" ] ; then
372 domainhome
="$LE_WORKING_DIR/$domain"
373 mkdir
-p "$domainhome"
375 if [ -z "$DOMAIN_CONF" ] ; then
376 DOMAIN_CONF
="$domainhome/$Le_Domain.conf"
379 if [ -z "$CSR_PATH" ] ; then
380 CSR_PATH
="$domainhome/$domain.csr"
382 if [ -z "$CERT_KEY_PATH" ] ; then
383 CERT_KEY_PATH
="$domainhome/$domain.key"
385 if [ -z "$CERT_PATH" ] ; then
386 CERT_PATH
="$domainhome/$domain.cer"
388 if [ -z "$CA_CERT_PATH" ] ; then
389 CA_CERT_PATH
="$domainhome/ca.cer"
396 httpdroot
="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | sed s/\"//g)"
397 httpdconfname
="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | sed s/\"//g)"
398 httpdconf
="$httpdroot/$httpdconfname"
399 if [ ! -f $httpdconf ] ; then
400 _err
"Apache Config file not found" $httpdconf
407 if [ -z "$usingApache" ] ; then
411 if ! _apachePath
; then
415 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
416 _debug
"No config file to restore."
420 cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
421 if ! apachectl
-t ; then
422 _err
"Sorry, restore apache config error, please contact me."
425 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
431 if ! _apachePath
; then
436 _debug
"Backup apache config file" $httpdconf
437 cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
438 _info
"JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
439 _info
"In case there is an error that can not be restored automatically, you may try restore it yourself."
440 _info
"The backup file will be deleted on sucess, just forget it."
444 Alias /.well-known/acme-challenge $ACME_DIR
446 <Directory $ACME_DIR >
451 if ! apachectl
-t ; then
452 _err
"Sorry, apache config error, please contact me."
457 if [ ! -d "$ACME_DIR" ] ; then
459 chmod 755 "$ACME_DIR"
462 if ! apachectl graceful
; then
463 _err
"Sorry, apachectl graceful error, please contact me."
472 _stopserver
$serverproc
477 # webroot removelevel tokenfile
480 if [ -z "$__webroot" ] ; then
481 _debug
"no webroot specified, skip"
485 if [ "$2" == '1' ] ; then
486 _debug
"remove $__webroot/.well-known"
487 rm -rf "$__webroot/.well-known"
488 elif [ "$2" == '2' ] ; then
489 _debug
"remove $__webroot/.well-known/acme-challenge"
490 rm -rf "$__webroot/.well-known/acme-challenge"
491 elif [ "$2" == '3' ] ; then
492 _debug
"remove $__webroot/.well-known/acme-challenge/$3"
493 rm -rf "$__webroot/.well-known/acme-challenge/$3"
495 _info
"Skip for removelevel:$2"
503 if [ -z "$2" ] ; then
504 _err
"Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no"
513 Le_RealCACertPath
="$7"
519 if [ -f "$DOMAIN_CONF" ] ; then
520 Le_NextRenewTime
=$
(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut
-d '=' -f 2)
521 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s
" )" -lt "$Le_NextRenewTime" ] ; then
522 _info
"Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr
" "$DOMAIN_CONF" | cut -d '=' -f 2)"
527 if [ "$Le_Alt" == "no" ] ; then
530 if [ "$Le_Keylength" == "no" ] ; then
533 if [ "$Le_RealCertPath" == "no" ] ; then
536 if [ "$Le_RealKeyPath" == "no" ] ; then
539 if [ "$Le_RealCACertPath" == "no" ] ; then
542 if [ "$Le_ReloadCmd" == "no" ] ; then
546 _setopt
"$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain"
547 _setopt
"$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt"
548 _setopt
"$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot"
549 _setopt
"$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength"
550 _setopt
"$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
551 _setopt
"$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
552 _setopt
"$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
553 _setopt
"$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
555 if [ "$Le_Webroot" == "no" ] ; then
556 _info
"Standalone mode."
557 if ! command -v "nc" > /dev
/null
; then
558 _err
"Please install netcat(nc) tools first."
562 netprc
="$(ss -ntpl | grep ':80 ')"
563 if [ "$netprc" ] ; then
565 _err
"tcp port 80 is already used by $(echo "$netprc" | cut -d : -f 4)"
566 _err
"Please stop it first"
571 if [ "$Le_Webroot" == "apache" ] ; then
572 if ! _setApache
; then
573 _err
"set up apache error. Report error to me."
576 wellknown_path
="$ACME_DIR"
581 createAccountKey
$Le_Domain $Le_Keylength
583 if ! createDomainKey
$Le_Domain $Le_Keylength ; then
584 _err
"Create domain key error."
588 if ! createCSR
$Le_Domain $Le_Alt ; then
589 _err
"Create CSR error."
593 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)
594 if [ "${#pub_exp}" == "5" ] ; then
597 _debug pub_exp
"$pub_exp"
599 e
=$
(echo $pub_exp | _h2b | _base64
)
602 modulus
=$
(openssl rsa
-in $ACCOUNT_KEY_PATH -modulus -noout | cut
-d '=' -f 2 )
603 n
=$
(echo $modulus| _h2b | _base64 | _b64
)
605 jwk
='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
607 HEADER
='{"alg": "RS256", "jwk": '$jwk'}'
608 HEADERPLACE
='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
609 _debug HEADER
"$HEADER"
611 accountkey_json
=$
(echo -n "$jwk" |
sed "s/ //g")
612 thumbprint
=$
(echo -n "$accountkey_json" | openssl dgst
-sha256 -binary | _base64 | _b64
)
615 _info
"Registering account"
616 regjson
='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
617 if [ "$ACCOUNT_EMAIL" ] ; then
618 regjson
='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
620 _send_signed_request
"$API/acme/new-reg" "$regjson"
622 if [ "$code" == "" ] ||
[ "$code" == '201' ] ; then
624 echo $response > $LE_WORKING_DIR/account.json
625 elif [ "$code" == '409' ] ; then
626 _info
"Already registered"
628 _err
"Register account Error."
634 if [[ "$Le_Webroot" == "dns"* ]] ; then
640 _info
"Verify each domain"
642 if [ -z "$vlist" ] ; then
643 alldomains
=$
(echo "$Le_Domain,$Le_Alt" |
sed "s/,/ /g")
646 _info
"Geting token for domain" $d
647 _send_signed_request
"$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
648 if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
649 _err
"new-authz error: $response"
654 entry
=$
(echo $response |
egrep -o '{[^{]*"type":"'$vtype'"[^}]*')
655 _debug entry
"$entry"
657 token
=$
(echo "$entry" |
sed 's/,/\n'/g|
grep '"token":'| cut
-d : -f 2|
sed 's/"//g')
660 uri
=$
(echo "$entry" |
sed 's/,/\n'/g|
grep '"uri":'| cut
-d : -f 2,3|
sed 's/"//g')
663 keyauthorization
="$token.$thumbprint"
664 _debug keyauthorization
"$keyauthorization"
666 dvlist
="$d$sep$keyauthorization$sep$uri"
667 _debug dvlist
"$dvlist"
669 vlist
="$vlist$dvlist,"
675 ventries
=$
(echo "$vlist" |
sed "s/,/ /g")
676 for ventry
in $ventries
678 d
=$
(echo $ventry | cut
-d $sep -f 1)
679 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
681 if [ "$vtype" == "$VTYPE_DNS" ] ; then
683 txtdomain
="_acme-challenge.$d"
684 _debug txtdomain
"$txtdomain"
685 txt
="$(echo -e -n $keyauthorization | openssl sha -sha256 -binary | _base64 | _b64)"
690 if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
691 d_api
="$LE_WORKING_DIR/$d/$Le_Webroot"
692 elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
693 d_api
="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
694 elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
695 d_api
="$LE_WORKING_DIR/$Le_Webroot"
696 elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
697 d_api
="$LE_WORKING_DIR/$Le_Webroot.sh"
698 elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
699 d_api
="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
700 elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
701 d_api
="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
703 _debug d_api
"$d_api"
705 if [ "$d_api" ]; then
706 _info
"Found domain api file: $d_api"
708 _err
"Add the following TXT record:"
709 _err
"Domain: $txtdomain"
710 _err
"TXT value: $txt"
711 _err
"Please be aware that you prepend _acme-challenge. before your domain"
712 _err
"so the resulting subdomain will be: $txtdomain"
716 if ! source $d_api ; then
717 _err
"Load file $d_api error. Please check your api file and try again."
721 addcommand
="$Le_Webroot-add"
722 if ! command -v $addcommand ; then
723 _err
"It seems that your api file is not correct, it must have a function named: $Le_Webroot"
727 if ! $addcommand $txtdomain $txt ; then
728 _err
"Error add txt for domain:$txtdomain"
735 if [ "$dnsadded" == '0' ] ; then
736 _setopt
"$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\""
737 _debug
"Dns record not added yet, so, save to $DOMAIN_CONF and exit."
738 _err
"Please add the TXT records to the domains, and retry again."
744 if [ "$dnsadded" == '1' ] ; then
745 _info
"Sleep 60 seconds for the txt records to take effect"
749 _debug
"ok, let's start to verify"
750 ventries
=$
(echo "$vlist" |
sed "s/,/ /g")
751 for ventry
in $ventries
753 d
=$
(echo $ventry | cut
-d $sep -f 1)
754 keyauthorization
=$
(echo $ventry | cut
-d $sep -f 2)
755 uri
=$
(echo $ventry | cut
-d $sep -f 3)
758 _debug
"keyauthorization" "$keyauthorization"
762 if [ "$vtype" == "$VTYPE_HTTP" ] ; then
763 if [ "$Le_Webroot" == "no" ] ; then
764 _info
"Standalone mode server"
765 _startserver
"$keyauthorization" &
768 _debug serverproc
$serverproc
770 if [ -z "$wellknown_path" ] ; then
771 wellknown_path
="$Le_Webroot/.well-known/acme-challenge"
773 _debug wellknown_path
"$wellknown_path"
775 if [ ! -d "$Le_Webroot/.well-known" ] ; then
777 elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then
783 token
="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
784 _debug
"writing token:$token to $wellknown_path/$token"
786 mkdir
-p "$wellknown_path"
787 echo -n "$keyauthorization" > "$wellknown_path/$token"
789 webroot_owner
=$
(stat
-c '%U:%G' $Le_Webroot)
790 _debug
"Changing owner/group of .well-known to $webroot_owner"
791 chown
-R $webroot_owner "$Le_Webroot/.well-known"
796 _send_signed_request
$uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
798 if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
799 _err
"$d:Challenge error: $resource"
800 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
806 _debug
"sleep 5 secs to verify"
810 if ! _get
$uri ; then
811 _err
"$d:Verify error:$resource"
812 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
817 status
=$
(echo $response |
egrep -o '"status":"[^"]+"' | cut
-d : -f 2 |
sed 's/"//g')
818 if [ "$status" == "valid" ] ; then
820 _stopserver
$serverproc
822 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
826 if [ "$status" == "invalid" ] ; then
827 error
=$
(echo $response |
egrep -o '"error":{[^}]*}' |
grep -o '"detail":"[^"]*"' | cut
-d '"' -f 4)
828 _err
"$d:Verify error:$error"
829 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
834 if [ "$status" == "pending" ] ; then
837 _err
"$d:Verify error:$response"
838 _clearupwebbroot
"$Le_Webroot" "$removelevel" "$token"
848 _info
"Verify finished, start to sign."
849 der
="$(openssl req -in $CSR_PATH -outform DER | _base64 | _b64)"
850 _send_signed_request
"$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
853 Le_LinkCert
="$(grep -i -o '^Location.*' $CURL_HEADER |sed 's/\r//g'| cut -d " " -f 2)"
854 _setopt
"$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert"
856 if [ "$Le_LinkCert" ] ; then
857 echo -----BEGIN CERTIFICATE-----
> "$CERT_PATH"
858 curl
--silent "$Le_LinkCert" | openssl base64
-e >> "$CERT_PATH"
859 echo -----END CERTIFICATE-----
>> "$CERT_PATH"
860 _info
"Cert success."
863 _info
"Your cert is in $CERT_PATH"
867 if [ -z "$Le_LinkCert" ] ; then
868 response
="$(echo $response | openssl base64 -d -A)"
869 _err
"Sign failed: $(echo "$response" | grep -o '"detail
":"[^
"]*"')"
873 _setopt "$DOMAIN_CONF" 'Le_Vlist
' '=' "\"\""
875 Le_LinkIssuer=$(grep -i '^Link
' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | sed 's
/<//g
' | sed 's
/>//g
')
876 _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer"
878 if [ "$Le_LinkIssuer" ] ; then
879 echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
880 curl --silent "$Le_LinkIssuer" | openssl base64 -e >> "$CA_CERT_PATH"
881 echo -----END CERTIFICATE----- >> "$CA_CERT_PATH"
882 _info "The intermediate CA cert is in $CA_CERT_PATH"
885 Le_CertCreateTime=$(date -u "+%s")
886 _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
888 Le_CertCreateTimeStr=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
889 _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
891 if [ ! "$Le_RenewalDays" ] ; then
895 _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
897 Le_NextRenewTime=$(date -u -d "+$Le_RenewalDays day" "+%s")
898 _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
900 Le_NextRenewTimeStr=$(date -u -d "+$Le_RenewalDays day" "+%Y-%m-%d %H:%M:%S UTC")
901 _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
904 installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
910 if [ -z "$Le_Domain" ] ; then
911 _err "Usage: $0 domain.com"
917 if [ ! -f "$DOMAIN_CONF" ] ; then
918 _info "$Le_Domain is not a issued domain, skip."
922 source "$DOMAIN_CONF"
923 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
924 _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
929 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
940 for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
941 d=$(echo $d | cut -d '/' -f 1)
952 Le_CertCreateTimeStr=""
955 Le_NextRenewTimeStr=""
980 if [ -z "$Le_Domain" ] ; then
981 _err "Usage: $0 domain.com [cert-file-path]|no [key-file-path]|no [ca-cert-file-path]|no [reloadCmd]|no"
987 Le_RealCACertPath="$4"
992 _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
993 _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
994 _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
995 _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
997 if [ "$Le_RealCertPath" ] ; then
998 if [ -f "$Le_RealCertPath" ] ; then
999 cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
1001 cat "$CERT_PATH" > "$Le_RealCertPath"
1004 if [ "$Le_RealCACertPath" ] ; then
1005 if [ -f "$Le_RealCACertPath" ] ; then
1006 cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
1008 if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
1009 echo "" >> "$Le_RealCACertPath"
1010 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
1012 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
1017 if [ "$Le_RealKeyPath" ] ; then
1018 if [ -f "$Le_RealKeyPath" ] ; then
1019 cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
1021 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
1024 if [ "$Le_ReloadCmd" ] ; then
1025 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
1033 _info "Installing cron job"
1034 if ! crontab -l | grep 'le.sh cron
' ; then
1035 if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
1036 lesh="\"$LE_WORKING_DIR\"/le.sh"
1038 _err "Can not install cronjob, le.sh not found."
1041 crontab -l | { cat; echo "0 0 * * * $SUDO LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
1046 uninstallcronjob() {
1047 _info "Removing cron job"
1048 cr="$(crontab -l | grep 'le.sh cron
')"
1050 crontab -l | sed "/le.sh cron/d" | crontab -
1051 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 7 | cut -d '=' -f 2 | tr -d '"')"
1052 _info LE_WORKING_DIR
"$LE_WORKING_DIR"
1059 # Detect profile file if not specified as environment variable
1061 if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
1066 local DETECTED_PROFILE
1069 SHELLTYPE
="$(basename "/$SHELL")"
1071 if [ "$SHELLTYPE" = "bash" ]; then
1072 if [ -f "$HOME/.bashrc" ]; then
1073 DETECTED_PROFILE
="$HOME/.bashrc"
1074 elif [ -f "$HOME/.bash_profile" ]; then
1075 DETECTED_PROFILE
="$HOME/.bash_profile"
1077 elif [ "$SHELLTYPE" = "zsh" ]; then
1078 DETECTED_PROFILE
="$HOME/.zshrc"
1081 if [ -z "$DETECTED_PROFILE" ]; then
1082 if [ -f "$HOME/.profile" ]; then
1083 DETECTED_PROFILE
="$HOME/.profile"
1084 elif [ -f "$HOME/.bashrc" ]; then
1085 DETECTED_PROFILE
="$HOME/.bashrc"
1086 elif [ -f "$HOME/.bash_profile" ]; then
1087 DETECTED_PROFILE
="$HOME/.bash_profile"
1088 elif [ -f "$HOME/.zshrc" ]; then
1089 DETECTED_PROFILE
="$HOME/.zshrc"
1093 if [ ! -z "$DETECTED_PROFILE" ]; then
1094 echo "$DETECTED_PROFILE"
1100 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
1101 echo "#Account configurations:
1102 #Here are the supported macros, uncomment them to make them take effect.
1103 #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
1105 #STAGE=1 # Use the staging api
1106 #FORCE=1 # Force to issue cert
1107 #DEBUG=1 # Debug mode
1110 #######################
1113 #CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje
"
1115 #CF_Email="xxxx@sss.com
"
1117 #######################
1122 #DP_Key="sADDsdasdgdsf
"
1124 #######################
1128 #CX_Secret="sADDsdasdgdsf
"
1130 " > $ACCOUNT_CONF_PATH
1137 if command -v yum
> /dev
/null
; then
1139 INSTALL
="$SUDO yum install -y "
1140 elif command -v apt-get
> /dev
/null
; then
1141 INSTALL
="$SUDO apt-get install -y "
1144 if ! command -v "curl" > /dev
/null
; then
1145 _err
"Please install curl first."
1146 _err
"$INSTALL curl"
1150 if ! command -v "crontab" > /dev
/null
; then
1151 _err
"Please install crontab first."
1152 if [ "$YUM" ] ; then
1153 _err
"$INSTALL crontabs"
1155 _err
"$INSTALL crontab"
1160 if ! command -v "openssl" > /dev
/null
; then
1161 _err
"Please install openssl first."
1162 _err
"$INSTALL openssl"
1166 _info
"Installing to $LE_WORKING_DIR"
1168 _info
"Installed to $LE_WORKING_DIR/le.sh"
1169 cp le.sh
$LE_WORKING_DIR/
1170 chmod +x
$LE_WORKING_DIR/le.sh
1172 _profile
="$(_detect_profile)"
1173 if [ "$_profile" ] ; then
1174 _debug
"Found profile: $_profile"
1176 echo "LE_WORKING_DIR=$LE_WORKING_DIR
1177 alias le=\"$LE_WORKING_DIR/le.sh\"
1178 alias le.sh=\"$LE_WORKING_DIR/le.sh\"
1179 " > "$LE_WORKING_DIR/le.env"
1181 _setopt
"$_profile" "source \"$LE_WORKING_DIR/le.env\""
1182 _info
"OK, Close and reopen your terminal to start using le"
1184 _info
"No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
1187 mkdir
-p $LE_WORKING_DIR/dnsapi
1188 cp dnsapi
/* $LE_WORKING_DIR/dnsapi
/
1190 #to keep compatible mv the .acc file to .key file
1191 if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
1192 mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
1197 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
1207 _profile
="$(_detect_profile)"
1208 if [ "$_profile" ] ; then
1209 sed -i /le.env
/d
"$_profile"
1212 rm -f $LE_WORKING_DIR/le.sh
1213 _info
"The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
1228 echo "Usage: le.sh [command] ...[args]....
1232 Install le.sh to your system.
1236 Install the issued cert to apache/nginx or any other server.
1240 Renew all the certs.
1242 Uninstall le.sh, and uninstall the cron job.
1246 Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
1248 Uninstall the cron job. The 'uninstall' command can do this automatically.
1250 Create an account private key, professional use.
1252 Create an domain private key, professional use.
1254 Create CSR , professional use.
1259 if [ -z "$1" ] ; then
1262 "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"