]> git.proxmox.com Git - mirror_acme.sh.git/blob - le.sh
fd38622696388adc24a6e3366b5ab56f8debe595
[mirror_acme.sh.git] / le.sh
1 #!/usr/bin/env bash
2 VER=2.0.0
3 PROJECT="https://github.com/Neilpang/le"
4
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"
7
8 DEFAULT_USER_AGENT="le.sh client: $PROJECT"
9
10 STAGE_CA="https://acme-staging.api.letsencrypt.org"
11
12 VTYPE_HTTP="http-01"
13 VTYPE_DNS="dns-01"
14
15 BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
16 END_CSR="-----END CERTIFICATE REQUEST-----"
17
18 BEGIN_CERT="-----BEGIN CERTIFICATE-----"
19 END_CERT="-----END CERTIFICATE-----"
20
21 if [[ -z "$AGREEMENT" ]] ; then
22 AGREEMENT="$DEFAULT_AGREEMENT"
23 fi
24
25
26 _info() {
27 if [[ -z "$2" ]] ; then
28 echo "[$(date)] $1"
29 else
30 echo "[$(date)] $1"="'$2'"
31 fi
32 }
33
34 _err() {
35 _info "$@" >&2
36 return 1
37 }
38
39 _debug() {
40 if [[ -z "$DEBUG" ]] ; then
41 return
42 fi
43 _err "$@"
44 return 0
45 }
46
47 _debug2() {
48 if [[ "$DEBUG" -ge "2" ]] ; then
49 _debug "$@"
50 fi
51 return
52 }
53
54 _exists() {
55 cmd="$1"
56 if [[ -z "$cmd" ]] ; then
57 _err "Usage: _exists cmd"
58 return 1
59 fi
60 command -v $cmd >/dev/null 2>&1
61 ret="$?"
62 _debug2 "$cmd exists=$ret"
63 return $ret
64 }
65
66 _h2b() {
67 hex=$(cat)
68 i=1
69 j=2
70 while [ '1' ] ; do
71 h=$(printf $hex | cut -c $i-$j)
72 if [[ -z "$h" ]] ; then
73 break;
74 fi
75 printf "\x$h"
76 let "i+=2"
77 let "j+=2"
78 done
79 }
80
81 #options file
82 _sed_i() {
83 options="$1"
84 filename="$2"
85 if [[ -z "$filename" ]] ; then
86 _err "Usage:_sed_i options filename"
87 return 1
88 fi
89
90 if sed -h 2>&1 | grep "\-i[SUFFIX]" ; then
91 _debug "Using sed -i"
92 sed -i ""
93 else
94 _debug "No -i support in sed"
95 text="$(cat $filename)"
96 echo "$text" | sed "$options" > "$filename"
97 fi
98 }
99
100 #Usage: file startline endline
101 _getfile() {
102 filename="$1"
103 startline="$2"
104 endline="$3"
105 if [[ -z "$endline" ]] ; then
106 _err "Usage: file startline endline"
107 return 1
108 fi
109
110 i="$(grep -n -- "$startline" $filename | cut -d : -f 1)"
111 if [[ -z "$i" ]] ; then
112 _err "Can not find start line: $startline"
113 return 1
114 fi
115 let "i+=1"
116 _debug i $i
117
118 j="$(grep -n -- "$endline" $filename | cut -d : -f 1)"
119 if [[ -z "$j" ]] ; then
120 _err "Can not find end line: $endline"
121 return 1
122 fi
123 let "j-=1"
124 _debug j $j
125
126 sed -n $i,${j}p "$filename"
127
128 }
129
130 #Usage: multiline
131 _base64() {
132 if [[ "$1" ]] ; then
133 openssl base64 -e
134 else
135 openssl base64 -e | tr -d '\r\n'
136 fi
137 }
138
139 #Usage: multiline
140 _dbase64() {
141 if [[ "$1" ]] ; then
142 openssl base64 -d -A
143 else
144 openssl base64 -d
145 fi
146 }
147
148 #Usage: hashalg
149 #Output Base64-encoded digest
150 _digest() {
151 alg="$1"
152 if [[ -z "$alg" ]] ; then
153 _err "Usage: _digest hashalg"
154 return 1
155 fi
156
157 if [[ "$alg" == "sha256" ]] ; then
158 openssl dgst -sha256 -binary | _base64
159 else
160 _err "$alg is not supported yet"
161 return 1
162 fi
163
164 }
165
166 #Usage: keyfile hashalg
167 #Output: Base64-encoded signature value
168 _sign() {
169 keyfile="$1"
170 alg="$2"
171 if [[ -z "$alg" ]] ; then
172 _err "Usage: _sign keyfile hashalg"
173 return 1
174 fi
175
176 if [[ "$alg" == "sha256" ]] ; then
177 openssl dgst -sha256 -sign "$keyfile" | _base64
178 else
179 _err "$alg is not supported yet"
180 return 1
181 fi
182
183 }
184
185 _ss() {
186 _port="$1"
187
188 if _exists "ss" ; then
189 _debug "Using: ss"
190 ss -ntpl | grep :$_port" "
191 return 0
192 fi
193
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" "
199 else
200 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null ; then
201 netstat -an -p tcp | grep LISTEN | grep :$_port" "
202 else
203 netstat -ntpl | grep :$_port" "
204 fi
205 fi
206 return 0
207 fi
208
209 return 1
210 }
211
212 toPkcs() {
213 domain="$1"
214 pfxPassword="$2"
215 if [[ -z "$domain" ]] ; then
216 echo "Usage: le.sh --toPkcs -d domain [--password pfx-password]"
217 return 1
218 fi
219
220 _initpath "$domain"
221
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"
224 else
225 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
226 fi
227
228 if [[ "$?" == "0" ]] ; then
229 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
230 fi
231
232 }
233
234 #domain [2048]
235 createAccountKey() {
236 _info "Creating account key"
237 if [[ -z "$1" ]] ; then
238 echo Usage: le.sh --createAccountKey -d domain.com [--accountkeylength 2048]
239 return
240 fi
241
242 account=$1
243 length=$2
244
245 if [[ "$length" == "ec-"* ]] ; then
246 length=2048
247 fi
248
249 if [[ -z "$2" ]] ; then
250 _info "Use default length 2048"
251 length=2048
252 fi
253 _initpath
254
255 if [[ -f "$ACCOUNT_KEY_PATH" ]] ; then
256 _info "Account key exists, skip"
257 return
258 else
259 #generate account key
260 openssl genrsa $length 2>/dev/null > "$ACCOUNT_KEY_PATH"
261 fi
262
263 }
264
265 #domain length
266 createDomainKey() {
267 _info "Creating domain key"
268 if [[ -z "$1" ]] ; then
269 echo Usage: le.sh --createDomainKey -d domain.com [ --keylength 2048 ]
270 return
271 fi
272
273 domain=$1
274 length=$2
275 isec=""
276 if [[ "$length" == "ec-"* ]] ; then
277 isec="1"
278 length=$(printf $length | cut -d '-' -f 2-100)
279 eccname="$length"
280 fi
281
282 if [[ -z "$length" ]] ; then
283 if [[ "$isec" ]] ; then
284 length=256
285 else
286 length=2048
287 fi
288 fi
289 _info "Use length $length"
290
291 if [[ "$isec" ]] ; then
292 if [[ "$length" == "256" ]] ; then
293 eccname="prime256v1"
294 fi
295 if [[ "$length" == "384" ]] ; then
296 eccname="secp384r1"
297 fi
298 if [[ "$length" == "521" ]] ; then
299 eccname="secp521r1"
300 fi
301 _info "Using ec name: $eccname"
302 fi
303
304 _initpath $domain
305
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"
310 else
311 openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH"
312 fi
313 else
314 if [[ "$IS_RENEW" ]] ; then
315 _info "Domain key exists, skip"
316 return 0
317 else
318 _err "Domain key exists, do you want to overwrite the key?"
319 _err "Set FORCE=1, and try again."
320 return 1
321 fi
322 fi
323
324 }
325
326 # domain domainlist
327 createCSR() {
328 _info "Creating csr"
329 if [[ -z "$1" ]] ; then
330 echo Usage: le.sh --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]
331 return
332 fi
333 domain=$1
334 _initpath $domain
335
336 domainlist=$2
337
338 if [[ -f "$CSR_PATH" ]] && [[ "$IS_RENEW" ]] && [[ -z "$FORCE" ]]; then
339 _info "CSR exists, skip"
340 return
341 fi
342
343 if [[ -z "$domainlist" ]] || [[ "$domainlist" == "no" ]]; then
344 #single domain
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"
348 else
349 alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
350 #multi
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"
354 fi
355
356 }
357
358 _urlencode() {
359 __n=$(cat)
360 echo $__n | tr '/+' '_-' | tr -d '= '
361 }
362
363 _time2str() {
364 #BSD
365 if date -u -d@$1 2>/dev/null ; then
366 return
367 fi
368
369 #Linux
370 if date -u -r $1 2>/dev/null ; then
371 return
372 fi
373
374 }
375
376 _stat() {
377 #Linux
378 if stat -c '%U:%G' "$1" 2>/dev/null ; then
379 return
380 fi
381
382 #BSD
383 if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then
384 return
385 fi
386 }
387
388 #keyfile
389 _calcjwk() {
390 keyfile="$1"
391 if [[ -z "$keyfile" ]] ; then
392 _err "Usage: _calcjwk keyfile"
393 return 1
394 fi
395 EC_SIGN=""
396 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
397 _debug "RSA key"
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
400 pub_exp=0$pub_exp
401 fi
402 _debug2 pub_exp "$pub_exp"
403
404 e=$(echo $pub_exp | _h2b | _base64)
405 _debug2 e "$e"
406
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'"}'
410 _debug2 jwk "$jwk"
411
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
415 _debug "EC key"
416 EC_SIGN="1"
417 crv="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
418 _debug2 crv $crv
419
420 pubi="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
421 _debug2 pubi $pubi
422 let "pubi=pubi+1"
423
424 pubj="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
425 _debug2 pubj $pubj
426 let "pubj=pubj-1"
427
428 pubtext="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
429 _debug2 pubtext "$pubtext"
430
431 xlen="$(printf "$pubtext" | tr -d ':' | wc -c)"
432 let "xlen=xlen/4"
433 _debug2 xlen $xlen
434
435 let "xend=xlen+1"
436 x="$(printf $pubtext | cut -d : -f 2-$xend)"
437 _debug2 x $x
438
439 x64="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)"
440 _debug2 x64 $x64
441
442 let "xend+=1"
443 y="$(printf $pubtext | cut -d : -f $xend-10000)"
444 _debug2 y $y
445
446 y64="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)"
447 _debug2 y64 $y64
448
449 jwk='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}'
450 _debug2 jwk "$jwk"
451
452 HEADER='{"alg": "ES256", "jwk": '$jwk'}'
453 HEADERPLACE='{"nonce": "NONCE", "alg": "ES256", "jwk": '$jwk'}'
454
455 else
456 _err "Only RSA or EC key is supported."
457 return 1
458 fi
459
460 _debug2 HEADER "$HEADER"
461 }
462 # body url [needbase64]
463 _post() {
464 body="$1"
465 url="$2"
466 needbase64="$3"
467
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)"
472 else
473 response="$($CURL -A "User-Agent: $USER_AGENT" -X POST --data "$body" $url)"
474 fi
475 else
476 if [[ "$needbase64" ]] ; then
477 response="$($WGET -S -O - --user-agent="$USER_AGENT" --post-data="$body" $url 2>"$HTTP_HEADER" | _base64)"
478 else
479 response="$($WGET -S -O - --user-agent="$USER_AGENT" --post-data="$body" $url 2>"$HTTP_HEADER")"
480 fi
481 _sed_i "s/^ *//g" "$HTTP_HEADER"
482 fi
483 echo -n "$response"
484
485 }
486
487 # url getheader
488 _get() {
489 url="$1"
490 onlyheader="$2"
491 _debug url $url
492 if _exists "curl" ; then
493 if [[ "$onlyheader" ]] ; then
494 $CURL -I -A "User-Agent: $USER_AGENT" $url
495 else
496 $CURL -A "User-Agent: $USER_AGENT" $url
497 fi
498 else
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'
502 else
503 eval $WGET --user-agent=\"$USER_AGENT\" -O - $url
504 fi
505 fi
506 ret=$?
507 return $ret
508 }
509
510 # url payload needbase64 keyfile
511 _send_signed_request() {
512 url=$1
513 payload=$2
514 needbase64=$3
515 keyfile=$4
516 if [[ -z "$keyfile" ]] ; then
517 keyfile="$ACCOUNT_KEY_PATH"
518 fi
519 _debug url $url
520 _debug payload "$payload"
521
522 if ! _calcjwk "$keyfile" ; then
523 return 1
524 fi
525
526 payload64=$(echo -n $payload | _base64 | _urlencode)
527 _debug2 payload64 $payload64
528
529 nonceurl="$API/directory"
530 nonce="$(_get $nonceurl "onlyheader" | grep -o "Replay-Nonce:.*$" | head -1 | tr -d "\r\n" | cut -d ' ' -f 2)"
531
532 _debug nonce "$nonce"
533
534 protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )"
535 _debug2 protected "$protected"
536
537 protected64="$(printf "$protected" | _base64 | _urlencode)"
538 _debug2 protected64 "$protected64"
539
540 sig=$(echo -n "$protected64.$payload64" | _sign "$keyfile" "sha256" | _urlencode)
541 _debug2 sig "$sig"
542
543 body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
544 _debug2 body "$body"
545
546
547 response="$(_post "$body" $url "$needbase64" )"
548
549 responseHeaders="$(cat $HTTP_HEADER)"
550
551 _debug2 responseHeaders "$responseHeaders"
552 _debug2 response "$response"
553 code="$(grep "^HTTP" $HTTP_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
554 _debug code $code
555
556 }
557
558
559 #setopt "file" "opt" "=" "value" [";"]
560 _setopt() {
561 __conf="$1"
562 __opt="$2"
563 __sep="$3"
564 __val="$4"
565 __end="$5"
566 if [[ -z "$__opt" ]] ; then
567 echo usage: _setopt '"file" "opt" "=" "value" [";"]'
568 return
569 fi
570 if [[ ! -f "$__conf" ]] ; then
571 touch "$__conf"
572 fi
573
574 if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then
575 _debug2 OK
576 if [[ "$__val" == *"&"* ]] ; then
577 __val="$(echo $__val | sed 's/&/\\&/g')"
578 fi
579 text="$(cat $__conf)"
580 echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
581
582 elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
583 if [[ "$__val" == *"&"* ]] ; then
584 __val="$(echo $__val | sed 's/&/\\&/g')"
585 fi
586 text="$(cat $__conf)"
587 echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
588
589 else
590 _debug2 APP
591 echo "$__opt$__sep$__val$__end" >> "$__conf"
592 fi
593 _debug "$(grep -H -n "^$__opt$__sep" $__conf)"
594 }
595
596 #_savedomainconf key value
597 #save to domain.conf
598 _savedomainconf() {
599 key="$1"
600 value="$2"
601 if [[ "$DOMAIN_CONF" ]] ; then
602 _setopt $DOMAIN_CONF "$key" "=" "$value"
603 else
604 _err "DOMAIN_CONF is empty, can not save $key=$value"
605 fi
606 }
607
608 #_saveaccountconf key value
609 _saveaccountconf() {
610 key="$1"
611 value="$2"
612 if [[ "$ACCOUNT_CONF_PATH" ]] ; then
613 _setopt $ACCOUNT_CONF_PATH "$key" "=" "\"$value\""
614 else
615 _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
616 fi
617 }
618
619 _startserver() {
620 content="$1"
621 _debug "startserver: $$"
622 nchelp="$(nc -h 2>&1)"
623
624 if echo "$nchelp" | grep "\-q[ ,]" >/dev/null ; then
625 _NC="nc -q 1 -l"
626 else
627 if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null ; then
628 _NC="nc -c -l"
629 elif echo "$nchelp" | grep "\-N" |grep "Shutdown the network socket after EOF on stdin" >/dev/null ; then
630 _NC="nc -N -l"
631 else
632 _NC="nc -l"
633 fi
634 fi
635
636 _debug "_NC" "$_NC"
637 # while true ; do
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 ;
641 fi
642 else
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
645 fi
646 fi
647 if [[ "$?" != "0" ]] ; then
648 _err "nc listen error."
649 exit 1
650 fi
651 # done
652 }
653
654 _stopserver(){
655 pid="$1"
656 _debug "pid" "$pid"
657 if [[ -z "$pid" ]] ; then
658 return
659 fi
660
661 if [[ "$(ps | grep "$pid" | grep "nc")" ]] ; then
662 _debug "Found nc process, kill it."
663 kill -s 9 $pid > /dev/null 2>&1
664 fi
665
666 _get "http://localhost:$Le_HTTPPort" >/dev/null 2>$1
667
668 }
669
670 _initpath() {
671
672 if [[ -z "$LE_WORKING_DIR" ]] ; then
673 LE_WORKING_DIR=$HOME/.le
674 fi
675
676 _DEFAULT_ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
677
678 if [[ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]] ; then
679 source "$_DEFAULT_ACCOUNT_CONF_PATH"
680 fi
681
682 if [[ -z "$ACCOUNT_CONF_PATH" ]] ; then
683 ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
684 fi
685
686 if [[ -f "$ACCOUNT_CONF_PATH" ]] ; then
687 source "$ACCOUNT_CONF_PATH"
688 fi
689
690 if [[ "$IN_CRON" ]] ; then
691 if [[ ! "$_USER_PATH_EXPORTED" ]] ; then
692 _USER_PATH_EXPORTED=1
693 export PATH="$USER_PATH:$PATH"
694 fi
695 fi
696
697 if [[ -z "$API" ]] ; then
698 if [[ -z "$STAGE" ]] ; then
699 API="$DEFAULT_CA"
700 else
701 API="$STAGE_CA"
702 _info "Using stage api:$API"
703 fi
704 fi
705
706 if [[ -z "$ACME_DIR" ]] ; then
707 ACME_DIR="/home/.acme"
708 fi
709
710 if [[ -z "$APACHE_CONF_BACKUP_DIR" ]] ; then
711 APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR"
712 fi
713
714 if [[ -z "$USER_AGENT" ]] ; then
715 USER_AGENT="$DEFAULT_USER_AGENT"
716 fi
717
718 HTTP_HEADER="$LE_WORKING_DIR/http.header"
719
720 WGET="wget -q"
721 if [[ "$DEBUG" -ge "2" ]] ; then
722 WGET="$WGET -d "
723 fi
724
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 "
729 fi
730
731 domain="$1"
732
733 if [[ -z "$ACCOUNT_KEY_PATH" ]] ; then
734 ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
735 fi
736
737 if [[ -z "$domain" ]] ; then
738 return 0
739 fi
740
741 domainhome="$LE_WORKING_DIR/$domain"
742 mkdir -p "$domainhome"
743
744 if [[ -z "$DOMAIN_PATH" ]] ; then
745 DOMAIN_PATH="$domainhome"
746 fi
747 if [[ -z "$DOMAIN_CONF" ]] ; then
748 DOMAIN_CONF="$domainhome/$domain.conf"
749 fi
750
751 if [[ -z "$DOMAIN_SSL_CONF" ]] ; then
752 DOMAIN_SSL_CONF="$domainhome/$domain.ssl.conf"
753 fi
754
755 if [[ -z "$CSR_PATH" ]] ; then
756 CSR_PATH="$domainhome/$domain.csr"
757 fi
758 if [[ -z "$CERT_KEY_PATH" ]] ; then
759 CERT_KEY_PATH="$domainhome/$domain.key"
760 fi
761 if [[ -z "$CERT_PATH" ]] ; then
762 CERT_PATH="$domainhome/$domain.cer"
763 fi
764 if [[ -z "$CA_CERT_PATH" ]] ; then
765 CA_CERT_PATH="$domainhome/ca.cer"
766 fi
767 if [[ -z "$CERT_FULLCHAIN_PATH" ]] ; then
768 CERT_FULLCHAIN_PATH="$domainhome/fullchain.cer"
769 fi
770 if [[ -z "$CERT_PFX_PATH" ]] ; then
771 CERT_PFX_PATH="$domainhome/$domain.pfx"
772 fi
773 }
774
775
776 _apachePath() {
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)"
781 else
782 httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
783 httpdconf="$httpdroot/$httpdconfname"
784 fi
785
786 if [[ ! -f $httpdconf ]] ; then
787 _err "Apache Config file not found" $httpdconf
788 return 1
789 fi
790 return 0
791 }
792
793 _restoreApache() {
794 if [[ -z "$usingApache" ]] ; then
795 return 0
796 fi
797 _initpath
798 if ! _apachePath ; then
799 return 1
800 fi
801
802 if [[ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]] ; then
803 _debug "No config file to restore."
804 return 0
805 fi
806
807 cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
808 if ! apachectl -t ; then
809 _err "Sorry, restore apache config error, please contact me."
810 return 1;
811 fi
812 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
813 return 0
814 }
815
816 _setApache() {
817 _initpath
818 if ! _apachePath ; then
819 return 1
820 fi
821
822 #backup the conf
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."
828
829 #add alias
830
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)"
835
836 if [[ "$apacheVer" ]] && [[ "$apacheMajer" -ge "2" ]] && [[ "$apacheMinor" -ge "4" ]] ; then
837 echo "
838 Alias /.well-known/acme-challenge $ACME_DIR
839
840 <Directory $ACME_DIR >
841 Require all granted
842 </Directory>
843 " >> $httpdconf
844 else
845 echo "
846 Alias /.well-known/acme-challenge $ACME_DIR
847
848 <Directory $ACME_DIR >
849 Order allow,deny
850 Allow from all
851 </Directory>
852 " >> $httpdconf
853 fi
854
855
856 if ! apachectl -t ; then
857 _err "Sorry, apache config error, please contact me."
858 _restoreApache
859 return 1;
860 fi
861
862 if [[ ! -d "$ACME_DIR" ]] ; then
863 mkdir -p "$ACME_DIR"
864 chmod 755 "$ACME_DIR"
865 fi
866
867 if ! apachectl graceful ; then
868 _err "Sorry, apachectl graceful error, please contact me."
869 _restoreApache
870 return 1;
871 fi
872 usingApache="1"
873 return 0
874 }
875
876 _clearup () {
877 _stopserver $serverproc
878 serverproc=""
879 _restoreApache
880 }
881
882 # webroot removelevel tokenfile
883 _clearupwebbroot() {
884 __webroot="$1"
885 if [[ -z "$__webroot" ]] ; then
886 _debug "no webroot specified, skip"
887 return 0
888 fi
889
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"
899 else
900 _info "Skip for removelevel:$2"
901 fi
902
903 return 0
904
905 }
906
907 issue() {
908 if [[ -z "$2" ]] ; then
909 echo "Usage: le --issue -d a.com -w /path/to/webroot/a.com/ "
910 return 1
911 fi
912 Le_Webroot="$1"
913 Le_Domain="$2"
914 Le_Alt="$3"
915 Le_Keylength="$4"
916 Le_RealCertPath="$5"
917 Le_RealKeyPath="$6"
918 Le_RealCACertPath="$7"
919 Le_ReloadCmd="$8"
920 Le_RealFullChainPath="$9"
921
922 _initpath $Le_Domain
923
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)"
928 return 2
929 fi
930 fi
931
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\""
941
942 if [[ "$Le_Alt" == "no" ]] ; then
943 Le_Alt=""
944 fi
945 if [[ "$Le_Keylength" == "no" ]] ; then
946 Le_Keylength=""
947 fi
948 if [[ "$Le_RealCertPath" == "no" ]] ; then
949 Le_RealCertPath=""
950 fi
951 if [[ "$Le_RealKeyPath" == "no" ]] ; then
952 Le_RealKeyPath=""
953 fi
954 if [[ "$Le_RealCACertPath" == "no" ]] ; then
955 Le_RealCACertPath=""
956 fi
957 if [[ "$Le_ReloadCmd" == "no" ]] ; then
958 Le_ReloadCmd=""
959 fi
960 if [[ "$Le_RealFullChainPath" == "no" ]] ; then
961 Le_RealFullChainPath=""
962 fi
963
964
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."
969 return 1
970 fi
971
972 if [[ -z "$Le_HTTPPort" ]] ; then
973 Le_HTTPPort=80
974 fi
975 _setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
976
977 netprc="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")"
978 if [[ "$netprc" ]] ; then
979 _err "$netprc"
980 _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
981 _err "Please stop it first"
982 return 1
983 fi
984 fi
985
986 if [[ "$Le_Webroot" == *"apache"* ]] ; then
987 if ! _setApache ; then
988 _err "set up apache error. Report error to me."
989 return 1
990 fi
991 wellknown_path="$ACME_DIR"
992 else
993 usingApache=""
994 fi
995
996 createAccountKey $Le_Domain $Le_Keylength
997
998 if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then
999 return 1
1000 fi
1001
1002 accountkey_json=$(echo -n "$jwk" | tr -d ' ' )
1003 thumbprint=$(echo -n "$accountkey_json" | _digest "sha256" | _urlencode)
1004
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'"}'
1012 fi
1013 _send_signed_request "$API/acme/new-reg" "$regjson"
1014
1015 if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then
1016 _info "Registered"
1017 echo $response > $LE_WORKING_DIR/account.json
1018 elif [[ "$code" == '409' ]] ; then
1019 _info "Already registered"
1020 else
1021 _err "Register account Error: $response"
1022 _clearup
1023 return 1
1024 fi
1025 ACCOUNT_KEY_HASH="$accountkeyhash"
1026 _saveaccountconf "ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH"
1027 else
1028 _info "Skip register account key"
1029 fi
1030
1031 if ! createDomainKey $Le_Domain $Le_Keylength ; then
1032 _err "Create domain key error."
1033 return 1
1034 fi
1035
1036 if ! createCSR $Le_Domain $Le_Alt ; then
1037 _err "Create CSR error."
1038 return 1
1039 fi
1040
1041 vlist="$Le_Vlist"
1042 # verify each domain
1043 _info "Verify each domain"
1044 sep='#'
1045 if [[ -z "$vlist" ]] ; then
1046 alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
1047 _index=1
1048 _currentRoot=""
1049 for d in $alldomains
1050 do
1051 _info "Getting webroot for domain" $d
1052 _w="$(echo $Le_Webroot | cut -d , -f $_index)"
1053 _debug _w "$_w"
1054 if [[ "$_w" ]] ; then
1055 _currentRoot="$_w"
1056 fi
1057 _debug "_currentRoot" "$_currentRoot"
1058 let "_index+=1"
1059
1060 vtype="$VTYPE_HTTP"
1061 if [[ "$_currentRoot" == "dns"* ]] ; then
1062 vtype="$VTYPE_DNS"
1063 fi
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"
1068 _clearup
1069 return 1
1070 fi
1071
1072 entry="$(printf $response | egrep -o '\{[^{]*"type":"'$vtype'"[^}]*')"
1073 _debug entry "$entry"
1074
1075 token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
1076 _debug token $token
1077
1078 uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
1079 _debug uri $uri
1080
1081 keyauthorization="$token.$thumbprint"
1082 _debug keyauthorization "$keyauthorization"
1083
1084 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
1085 _debug dvlist "$dvlist"
1086
1087 vlist="$vlist$dvlist,"
1088
1089 done
1090
1091 #add entry
1092 dnsadded=""
1093 ventries=$(echo "$vlist" | tr ',' ' ' )
1094 for ventry in $ventries
1095 do
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
1101 dnsadded='0'
1102 txtdomain="_acme-challenge.$d"
1103 _debug txtdomain "$txtdomain"
1104 txt="$(echo -e -n $keyauthorization | _digest "sha256" | _urlencode)"
1105 _debug txt "$txt"
1106 #dns
1107 #1. check use api
1108 d_api=""
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"
1121 fi
1122 _debug d_api "$d_api"
1123
1124 if [[ "$d_api" ]] ; then
1125 _info "Found domain api file: $d_api"
1126 else
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"
1132 continue
1133 fi
1134
1135 (
1136 if ! source $d_api ; then
1137 _err "Load file $d_api error. Please check your api file and try again."
1138 return 1
1139 fi
1140
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"
1144 return 1
1145 fi
1146
1147 if ! $addcommand $txtdomain $txt ; then
1148 _err "Error add txt for domain:$txtdomain"
1149 return 1
1150 fi
1151 )
1152
1153 if [[ "$?" != "0" ]] ; then
1154 return 1
1155 fi
1156 dnsadded='1'
1157 fi
1158 done
1159
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."
1164 return 1
1165 fi
1166
1167 fi
1168
1169 if [[ "$dnsadded" == '1' ]] ; then
1170 _info "Sleep 60 seconds for the txt records to take effect"
1171 sleep 60
1172 fi
1173
1174 _debug "ok, let's start to verify"
1175
1176 ventries=$(echo "$vlist" | tr ',' ' ' )
1177 for ventry in $ventries
1178 do
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"
1185 _debug "d" "$d"
1186 _debug "keyauthorization" "$keyauthorization"
1187 _debug "uri" "$uri"
1188 removelevel=""
1189 token=""
1190
1191 _debug "_currentRoot" "$_currentRoot"
1192
1193
1194 if [[ "$vtype" == "$VTYPE_HTTP" ]] ; then
1195 if [[ "$_currentRoot" == "no" ]] ; then
1196 _info "Standalone mode server"
1197 _startserver "$keyauthorization" &
1198 if [[ "$?" != "0" ]] ; then
1199 return 1
1200 fi
1201 serverproc="$!"
1202 sleep 2
1203 _debug serverproc $serverproc
1204
1205 else
1206 if [[ -z "$wellknown_path" ]] ; then
1207 wellknown_path="$_currentRoot/.well-known/acme-challenge"
1208 fi
1209 _debug wellknown_path "$wellknown_path"
1210
1211 if [[ ! -d "$_currentRoot/.well-known" ]] ; then
1212 removelevel='1'
1213 elif [[ ! -d "$_currentRoot/.well-known/acme-challenge" ]] ; then
1214 removelevel='2'
1215 else
1216 removelevel='3'
1217 fi
1218
1219 token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
1220 _debug "writing token:$token to $wellknown_path/$token"
1221
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"
1228 fi
1229
1230 fi
1231 fi
1232
1233 _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
1234
1235 if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then
1236 _err "$d:Challenge error: $response"
1237 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1238 _clearup
1239 return 1
1240 fi
1241
1242 waittimes=0
1243 if [[ -z "$MAX_RETRY_TIMES" ]] ; then
1244 MAX_RETRY_TIMES=30
1245 fi
1246
1247 while [[ "1" ]] ; do
1248 let "waittimes+=1"
1249 if [[ "$waittimes" -ge "$MAX_RETRY_TIMES" ]] ; then
1250 _err "$d:Timeout"
1251 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1252 _clearup
1253 return 1
1254 fi
1255
1256 _debug "sleep 5 secs to verify"
1257 sleep 5
1258 _debug "checking"
1259 response="$(_get $uri)"
1260 if [[ "$?" != "0" ]] ; then
1261 _err "$d:Verify error:$response"
1262 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1263 _clearup
1264 return 1
1265 fi
1266
1267 status=$(echo $response | egrep -o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
1268 if [[ "$status" == "valid" ]] ; then
1269 _info "Success"
1270 _stopserver $serverproc
1271 serverproc=""
1272 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1273 break;
1274 fi
1275
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"
1280 _clearup
1281 return 1;
1282 fi
1283
1284 if [[ "$status" == "pending" ]] ; then
1285 _info "Pending"
1286 else
1287 _err "$d:Verify error:$response"
1288 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1289 _clearup
1290 return 1
1291 fi
1292
1293 done
1294
1295 done
1296
1297 _clearup
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"
1301
1302
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"
1305
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."
1311 cat "$CERT_PATH"
1312
1313 _info "Your cert is in $CERT_PATH"
1314 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
1315
1316 if [[ ! "$USER_PATH" ]] || [[ ! "$IN_CRON" ]] ; then
1317 USER_PATH="$PATH"
1318 _saveaccountconf "USER_PATH" "$USER_PATH"
1319 fi
1320 fi
1321
1322
1323 if [[ -z "$Le_LinkCert" ]] ; then
1324 response="$(echo $response | _dbase64 "multiline" )"
1325 _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
1326 return 1
1327 fi
1328
1329 _setopt "$DOMAIN_CONF" 'Le_Vlist' '=' "\"\""
1330
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"
1333
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"
1341 fi
1342
1343 Le_CertCreateTime=$(date -u "+%s")
1344 _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
1345
1346 Le_CertCreateTimeStr=$(date -u )
1347 _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
1348
1349 if [[ ! "$Le_RenewalDays" ]] ; then
1350 Le_RenewalDays=80
1351 fi
1352
1353 _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
1354
1355 let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
1356 _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
1357
1358 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
1359 _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
1360
1361
1362 installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
1363
1364 }
1365
1366 renew() {
1367 Le_Domain="$1"
1368 if [[ -z "$Le_Domain" ]] ; then
1369 _err "Usage: le.sh --renew -d domain.com"
1370 return 1
1371 fi
1372
1373 _initpath $Le_Domain
1374
1375 if [[ ! -f "$DOMAIN_CONF" ]] ; then
1376 _info "$Le_Domain is not a issued domain, skip."
1377 return 0;
1378 fi
1379
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"
1383 return 2
1384 fi
1385
1386 IS_RENEW="1"
1387 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
1388 local res=$?
1389 IS_RENEW=""
1390
1391 return $res
1392 }
1393
1394 renewAll() {
1395 _initpath
1396 _info "renewAll"
1397
1398 for d in $(ls -F ${LE_WORKING_DIR}/ | grep [^.].*[.].*/$ ) ; do
1399 d=$(echo $d | cut -d '/' -f 1)
1400 _info "renew $d"
1401
1402 Le_LinkCert=""
1403 Le_Domain=""
1404 Le_Alt="no"
1405 Le_Webroot=""
1406 Le_Keylength=""
1407 Le_LinkIssuer=""
1408
1409 Le_CertCreateTime=""
1410 Le_CertCreateTimeStr=""
1411 Le_RenewalDays=""
1412 Le_NextRenewTime=""
1413 Le_NextRenewTimeStr=""
1414
1415 Le_RealCertPath=""
1416 Le_RealKeyPath=""
1417
1418 Le_RealCACertPath=""
1419
1420 Le_ReloadCmd=""
1421 Le_RealFullChainPath=""
1422
1423 DOMAIN_PATH=""
1424 DOMAIN_CONF=""
1425 DOMAIN_SSL_CONF=""
1426 CSR_PATH=""
1427 CERT_KEY_PATH=""
1428 CERT_PATH=""
1429 CA_CERT_PATH=""
1430 CERT_PFX_PATH=""
1431 CERT_FULLCHAIN_PATH=""
1432 ACCOUNT_KEY_PATH=""
1433
1434 wellknown_path=""
1435
1436 renew "$d"
1437 done
1438
1439 }
1440
1441 installcert() {
1442 Le_Domain="$1"
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]"
1445 return 1
1446 fi
1447
1448 Le_RealCertPath="$2"
1449 Le_RealKeyPath="$3"
1450 Le_RealCACertPath="$4"
1451 Le_ReloadCmd="$5"
1452 Le_RealFullChainPath="$6"
1453
1454 _initpath $Le_Domain
1455
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\""
1461
1462 if [[ "$Le_RealCertPath" ]] ; then
1463 if [[ -f "$Le_RealCertPath" ]] ; then
1464 cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
1465 fi
1466 cat "$CERT_PATH" > "$Le_RealCertPath"
1467 fi
1468
1469 if [[ "$Le_RealCACertPath" ]] ; then
1470 if [[ "$Le_RealCACertPath" == "$Le_RealCertPath" ]] ; then
1471 echo "" >> "$Le_RealCACertPath"
1472 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
1473 else
1474 if [[ -f "$Le_RealCACertPath" ]] ; then
1475 cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
1476 fi
1477 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
1478 fi
1479 fi
1480
1481
1482 if [[ "$Le_RealKeyPath" ]] ; then
1483 if [[ -f "$Le_RealKeyPath" ]] ; then
1484 cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
1485 fi
1486 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
1487 fi
1488
1489 if [[ "$Le_RealFullChainPath" ]] ; then
1490 if [[ -f "$Le_RealFullChainPath" ]] ; then
1491 cp -p "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
1492 fi
1493 cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
1494 fi
1495
1496 if [[ "$Le_ReloadCmd" ]] ; then
1497 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
1498 (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd")
1499 fi
1500
1501 }
1502
1503 installcronjob() {
1504 _initpath
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."
1509 return 1
1510 fi
1511
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"
1516 else
1517 _err "Can not install cronjob, le.sh not found."
1518 return 1
1519 fi
1520 crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
1521 fi
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"
1526 return 1
1527 fi
1528 }
1529
1530 uninstallcronjob() {
1531 if ! _exists "crontab" ; then
1532 return
1533 fi
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"
1540 fi
1541 _initpath
1542
1543 }
1544
1545 revoke() {
1546 Le_Domain="$1"
1547 if [[ -z "$Le_Domain" ]] ; then
1548 echo "Usage: le.sh --revoke -d domain.com"
1549 return 1
1550 fi
1551
1552 _initpath $Le_Domain
1553 if [[ ! -f "$DOMAIN_CONF" ]] ; then
1554 _err "$Le_Domain is not a issued domain, skip."
1555 return 1;
1556 fi
1557
1558 if [[ ! -f "$CERT_PATH" ]] ; then
1559 _err "Cert for $Le_Domain $CERT_PATH is not found, skip."
1560 return 1
1561 fi
1562
1563 cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"| tr -d "\r\n" | _urlencode)"
1564
1565 if [[ -z "$cert" ]] ; then
1566 _err "Cert for $Le_Domain is empty found, skip."
1567 return 1
1568 fi
1569
1570 data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
1571 uri="$API/acme/revoke-cert"
1572
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."
1577 rm -f $CERT_PATH
1578 return 0
1579 else
1580 _err "Revoke error by domain key."
1581 _err "$resource"
1582 fi
1583 fi
1584
1585 _info "Then try account key."
1586
1587 if _send_signed_request $uri "$data" "" "$ACCOUNT_KEY_PATH" ; then
1588 if [[ -z "$response" ]] ; then
1589 _info "Revoke success."
1590 rm -f $CERT_PATH
1591 return 0
1592 else
1593 _err "Revoke error."
1594 _debug "$resource"
1595 fi
1596 fi
1597 return 1
1598 }
1599
1600 # Detect profile file if not specified as environment variable
1601 _detect_profile() {
1602 if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then
1603 echo "$PROFILE"
1604 return
1605 fi
1606
1607 local DETECTED_PROFILE
1608 DETECTED_PROFILE=''
1609 local SHELLTYPE
1610 SHELLTYPE="$(basename "/$SHELL")"
1611
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"
1617 fi
1618 elif [[ "$SHELLTYPE" = "zsh" ]] ; then
1619 DETECTED_PROFILE="$HOME/.zshrc"
1620 fi
1621
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"
1631 fi
1632 fi
1633
1634 if [[ ! -z "$DETECTED_PROFILE" ]] ; then
1635 echo "$DETECTED_PROFILE"
1636 fi
1637 }
1638
1639 _initconf() {
1640 _initpath
1641 if [[ ! -f "$ACCOUNT_CONF_PATH" ]] ; then
1642 echo "#ACCOUNT_CONF_PATH=xxxx
1643
1644 #Account configurations:
1645 #Here are the supported macros, uncomment them to make them take effect.
1646
1647 #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
1648 #ACCOUNT_KEY_PATH=\"/path/to/account.key\"
1649
1650 #STAGE=1 # Use the staging api
1651 #FORCE=1 # Force to issue cert
1652 #DEBUG=1 # Debug mode
1653
1654 #ACCOUNT_KEY_HASH=account key hash
1655
1656 USER_AGENT=\"le.sh client: $PROJECT\"
1657
1658 #USER_PATH=""
1659
1660 #dns api
1661 #######################
1662 #Cloudflare:
1663 #api key
1664 #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
1665 #account email
1666 #CF_Email=\"xxxx@sss.com\"
1667
1668 #######################
1669 #Dnspod.cn:
1670 #api key id
1671 #DP_Id=\"1234\"
1672 #api key
1673 #DP_Key=\"sADDsdasdgdsf\"
1674
1675 #######################
1676 #Cloudxns.com:
1677 #CX_Key=\"1234\"
1678 #
1679 #CX_Secret=\"sADDsdasdgdsf\"
1680
1681 " > $ACCOUNT_CONF_PATH
1682 fi
1683 }
1684
1685 _precheck() {
1686 if ! _exists "curl" && ! _exists "wget"; then
1687 _err "Please install curl or wget first, we need to access http resources."
1688 return 1
1689 fi
1690
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"
1698 return 1
1699 fi
1700 fi
1701
1702 if ! _exists "openssl" ; then
1703 _err "Please install openssl first."
1704 _err "We need openssl to generate keys."
1705 return 1
1706 fi
1707
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."
1712 fi
1713
1714 return 0
1715 }
1716
1717 install() {
1718 if ! _initpath ; then
1719 _err "Install failed."
1720 return 1
1721 fi
1722
1723 if ! _precheck ; then
1724 _err "Pre-check failed, can not install."
1725 return 1
1726 fi
1727
1728 _info "Installing to $LE_WORKING_DIR"
1729
1730 if ! mkdir -p "$LE_WORKING_DIR" ; then
1731 _err "Can not craete working dir: $LE_WORKING_DIR"
1732 return 1
1733 fi
1734
1735 cp le.sh "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/le.sh"
1736
1737 if [[ "$?" != "0" ]] ; then
1738 _err "Install failed, can not copy le.sh"
1739 return 1
1740 fi
1741
1742 _info "Installed to $LE_WORKING_DIR/le.sh"
1743
1744 _profile="$(_detect_profile)"
1745 if [[ "$_profile" ]] ; then
1746 _debug "Found profile: $_profile"
1747
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"
1755 else
1756 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
1757 fi
1758
1759 mkdir -p $LE_WORKING_DIR/dnsapi
1760 cp dnsapi/* $LE_WORKING_DIR/dnsapi/
1761
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"
1765 fi
1766
1767 if [[ ! -f "$ACCOUNT_CONF_PATH" ]] ; then
1768 _initconf
1769 fi
1770
1771 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
1772 _setopt "$ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
1773
1774 installcronjob
1775
1776 _info OK
1777 }
1778
1779 uninstall() {
1780 uninstallcronjob
1781 _initpath
1782
1783 _profile="$(_detect_profile)"
1784 if [[ "$_profile" ]] ; then
1785 text="$(cat $_profile)"
1786 echo "$text" | sed "s|^source.*le.env.*$||" > "$_profile"
1787 fi
1788
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."
1791
1792 }
1793
1794 cron() {
1795 IN_CRON=1
1796 renewAll
1797 IN_CRON=""
1798 }
1799
1800 version() {
1801 echo "$PROJECT"
1802 echo "v$VER"
1803 }
1804
1805 showhelp() {
1806 version
1807 echo "Usage: le.sh command ...[parameters]....
1808 Commands:
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.
1825
1826 Parameters:
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.
1831
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.
1836
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.
1839
1840 These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
1841
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.
1846
1847 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
1848
1849 --accountconf Specifies a customized account config file.
1850 --leworkingdir Specifies the home dir for le.sh
1851
1852 "
1853 }
1854
1855 _installOnline() {
1856 _info "Installing from online archive."
1857 if [[ ! "$BRANCH" ]] ; then
1858 BRANCH="master"
1859 fi
1860 _initpath
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."
1866 return 1
1867 fi
1868 _info "Extracting $localname"
1869 tar xzf $localname
1870 cd "le-$BRANCH"
1871 chmod +x le.sh
1872 if ./le.sh install ; then
1873 _info "Install success!"
1874 fi
1875
1876 cd ..
1877 rm -rf "le-$BRANCH"
1878 rm -f "$localname"
1879 }
1880
1881
1882 _process() {
1883 _CMD=""
1884 _domain=""
1885 _altdomains="no"
1886 _webroot=""
1887 _keylength="no"
1888 _accountkeylength="no"
1889 _certpath="no"
1890 _keypath="no"
1891 _capath="no"
1892 _fullchainpath="no"
1893 _reloadcmd="no"
1894 _password=""
1895 while (( ${#} )); do
1896 case "${1}" in
1897
1898 --help|-h)
1899 showhelp
1900 return
1901 ;;
1902 --version|-v)
1903 version
1904 return
1905 ;;
1906 --install)
1907 _CMD="install"
1908 ;;
1909 --uninstall)
1910 _CMD="uninstall"
1911 ;;
1912 --issue)
1913 _CMD="issue"
1914 ;;
1915 --installcert|-i)
1916 _CMD="installcert"
1917 ;;
1918 --renew|-r)
1919 _CMD="renew"
1920 ;;
1921 --renewAll|-renewall)
1922 _CMD="renewAll"
1923 ;;
1924 --revoke)
1925 _CMD="revoke"
1926 ;;
1927 --installcronjob)
1928 _CMD="installcronjob"
1929 ;;
1930 --uninstallcronjob)
1931 _CMD="uninstallcronjob"
1932 ;;
1933 --cron)
1934 _CMD="cron"
1935 ;;
1936 --toPkcs)
1937 _CMD="toPkcs"
1938 ;;
1939 --createAccountKey|--createaccountkey|-cak)
1940 _CMD="createAccountKey"
1941 ;;
1942 --createDomainKey|--createdomainkey|-cdk)
1943 _CMD="createDomainKey"
1944 ;;
1945 --createCSR|--createcsr|-ccr)
1946 _CMD="createCSR"
1947 ;;
1948
1949
1950 --domain|-d)
1951 _dvalue="$2"
1952
1953 if [[ -z "$_dvalue" ]] || [[ "$_dvalue" == "-"* ]] ; then
1954 _err "'$_dvalue' is not a valid domain for parameter '$1'"
1955 return 1
1956 fi
1957
1958 if [[ -z "$_domain" ]] ; then
1959 _domain="$_dvalue"
1960 else
1961 if [[ "$_altdomains" == "no" ]] ; then
1962 _altdomains="$_dvalue"
1963 else
1964 _altdomains="$_altdomains,$_dvalue"
1965 fi
1966 fi
1967 shift
1968 ;;
1969
1970 --force|-f)
1971 FORCE="1"
1972 ;;
1973 --staging|--test)
1974 STAGE="1"
1975 ;;
1976 --debug)
1977 if [[ "$2" == "-"* ]] || [[ -z "$2" ]]; then
1978 DEBUG="1"
1979 else
1980 DEBUG="$2"
1981 shift
1982 fi
1983 ;;
1984 --webroot|-w)
1985 wvalue="$2"
1986 if [[ -z "$_webroot" ]] ; then
1987 _webroot="$wvalue"
1988 else
1989 _webroot="$_webroot,$wvalue"
1990 fi
1991 shift
1992 ;;
1993 --standalone)
1994 wvalue="no"
1995 if [[ -z "$_webroot" ]] ; then
1996 _webroot="$wvalue"
1997 else
1998 _webroot="$_webroot,$wvalue"
1999 fi
2000 ;;
2001 --apache)
2002 wvalue="apache"
2003 if [[ -z "$_webroot" ]] ; then
2004 _webroot="$wvalue"
2005 else
2006 _webroot="$_webroot,$wvalue"
2007 fi
2008 ;;
2009 --dns)
2010 wvalue="dns"
2011 if [[ "$2" != "-"* ]] ; then
2012 wvalue="$2"
2013 shift
2014 fi
2015 if [[ -z "$_webroot" ]] ; then
2016 _webroot="$wvalue"
2017 else
2018 _webroot="$_webroot,$wvalue"
2019 fi
2020 ;;
2021 --keylength|-k)
2022 _keylength="$2"
2023 accountkeylength="$2"
2024 shift
2025 ;;
2026 --accountkeylength|-ak)
2027 accountkeylength="$2"
2028 shift
2029 ;;
2030
2031 --certpath)
2032 _certpath="$2"
2033 shift
2034 ;;
2035 --keypath)
2036 _keypath="$2"
2037 shift
2038 ;;
2039 --capath)
2040 _capath="$2"
2041 shift
2042 ;;
2043 --fullchainpath)
2044 _fullchainpath="$2"
2045 shift
2046 ;;
2047 --reloadcmd)
2048 _reloadcmd="$2"
2049 shift
2050 ;;
2051 --password)
2052 _password="$2"
2053 shift
2054 ;;
2055 --accountconf)
2056 ACCOUNT_CONF_PATH="$2"
2057 ;;
2058 --leworkingdir)
2059 LE_WORKING_DIR="$2"
2060 ;;
2061
2062 *)
2063 _err "Unknown parameter : $1"
2064 return 1
2065 ;;
2066 esac
2067
2068 shift 1
2069 done
2070
2071
2072 case "${_CMD}" in
2073 install) install ;;
2074 uninstall) uninstall ;;
2075 issue)
2076 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keylength" "$_capath" "$_reloadcmd" "$_fullchainpath"
2077 ;;
2078 installcert)
2079 installcert "$_domain" "$_certpath" "$_keylength" "$_capath" "$_reloadcmd" "$_fullchainpath"
2080 ;;
2081 renew)
2082 renew "$_domain"
2083 ;;
2084 renewAll)
2085 renewAll
2086 ;;
2087 revoke)
2088 revoke "$_domain"
2089 ;;
2090 installcronjob) installcronjob ;;
2091 uninstallcronjob) uninstallcronjob ;;
2092 cron) cron ;;
2093 toPkcs)
2094 toPkcs "$_domain" "$_password"
2095 ;;
2096 createAccountKey)
2097 createAccountKey "$_domain" "$_accountkeylength"
2098 ;;
2099 createDomainKey)
2100 createDomainKey "$_domain" "$_keylength"
2101 ;;
2102 createCSR)
2103 createCSR "$_domain" "$_altdomains"
2104 ;;
2105
2106 *)
2107 _err "Invalid command: $_CMD"
2108 showhelp;
2109 return 1
2110 ;;
2111 esac
2112
2113 }
2114
2115
2116 if [[ "$INSTALLONLINE" ]] ; then
2117 INSTALLONLINE=""
2118 _installOnline $BRANCH
2119 exit
2120 fi
2121
2122 if [[ -z "$1" ]] ; then
2123 showhelp
2124 else
2125 if [[ "$1" == "-"* ]] ; then
2126 _process "$@"
2127 else
2128 "$@"
2129 fi
2130 fi
2131
2132