]> git.proxmox.com Git - mirror_acme.sh.git/blame - acme.sh
minor format output
[mirror_acme.sh.git] / acme.sh
CommitLineData
0a7c9364 1#!/usr/bin/env sh
bfdf1f48 2
fac1e367 3VER=2.4.1
a7b7355d 4
6cc11ffb 5PROJECT_NAME="acme.sh"
a7b7355d 6
6cc11ffb 7PROJECT_ENTRY="acme.sh"
8
9PROJECT="https://github.com/Neilpang/$PROJECT_NAME"
4c3b3608 10
11DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
c93ec933 12DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
4c3b3608 13
08928b48 14DEFAULT_USER_AGENT="$PROJECT_ENTRY client v$VER : $PROJECT"
bbbdcb09 15
4c3b3608 16STAGE_CA="https://acme-staging.api.letsencrypt.org"
17
18VTYPE_HTTP="http-01"
19VTYPE_DNS="dns-01"
e22bcf7c 20VTYPE_TLS="tls-sni-01"
21VTYPE_TLS2="tls-sni-02"
22
523c7682 23MAX_RENEW=80
24
4a4dacb5 25DEFAULT_DNS_SLEEP=120
26
e22bcf7c 27W_TLS="tls"
4c3b3608 28
ec603bee 29STATE_VERIFIED="verified_ok"
30
88fab7d6 31BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
32END_CSR="-----END CERTIFICATE REQUEST-----"
33
34BEGIN_CERT="-----BEGIN CERTIFICATE-----"
35END_CERT="-----END CERTIFICATE-----"
36
cc179731 37RENEW_SKIP=2
38
43822d37 39ECC_SEP="_"
40ECC_SUFFIX="${ECC_SEP}ecc"
41
8663fb7e 42if [ -z "$AGREEMENT" ] ; then
4c3b3608 43 AGREEMENT="$DEFAULT_AGREEMENT"
44fi
45
4c3b3608 46
00a50605 47
43822d37 48__green() {
49 printf '\033[1;31;32m'
50 printf -- "$1"
51 printf '\033[0m'
52}
53
54__red() {
55 printf '\033[1;31;40m'
56 printf -- "$1"
57 printf '\033[0m'
58}
00a50605 59
4c3b3608 60_info() {
8663fb7e 61 if [ -z "$2" ] ; then
43822d37 62 printf -- "[$(date)] $1"
63 else
64 printf -- "[$(date)] $1='$2'"
65 fi
66 printf "\n"
67}
68
69
43822d37 70_err_e() {
71 if [ -z "$2" ] ; then
72 __red "$1" >&2
4c3b3608 73 else
43822d37 74 __red "$1='$2'" >&2
4c3b3608 75 fi
fac1e367 76 printf "\n" >&2
4c3b3608 77}
78
79_err() {
43822d37 80 printf -- "[$(date)] " >&2
fac1e367 81 _err_e "$@"
4c3b3608 82 return 1
83}
84
43822d37 85_usage() {
86 version
87 _err_e "$@"
43822d37 88}
89
c60883ef 90_debug() {
8663fb7e 91 if [ -z "$DEBUG" ] ; then
c60883ef 92 return
93 fi
43822d37 94
95 if [ -z "$2" ] ; then
96 printf -- "[$(date)] $1" >&2
97 else
98 printf -- "[$(date)] $1='$2'" >&2
99 fi
100
31a5487c 101 printf "\n" >&2
c60883ef 102 return 0
103}
104
a63b05a9 105_debug2() {
8663fb7e 106 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
a63b05a9 107 _debug "$@"
108 fi
109 return
110}
111
22ea4004 112_debug3() {
113 if [ "$DEBUG" ] && [ "$DEBUG" -ge "3" ] ; then
114 _debug "$@"
115 fi
116 return
117}
118
dceb3aca 119_startswith(){
120 _str="$1"
121 _sub="$2"
19539575 122 echo "$_str" | grep "^$_sub" >/dev/null 2>&1
dceb3aca 123}
124
43822d37 125_endswith(){
126 _str="$1"
127 _sub="$2"
128 echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
129}
130
dceb3aca 131_contains(){
132 _str="$1"
133 _sub="$2"
43822d37 134 echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
dceb3aca 135}
136
c53da1ef 137_hasfield() {
138 _str="$1"
139 _field="$2"
140 _sep="$3"
141 if [ -z "$_field" ] ; then
43822d37 142 _usage "Usage: str field [sep]"
c53da1ef 143 return 1
144 fi
145
146 if [ -z "$_sep" ] ; then
147 _sep=","
148 fi
149
150 for f in $(echo "$_str" | tr ',' ' ') ; do
151 if [ "$f" = "$_field" ] ; then
152 _debug "'$_str' contains '$_field'"
153 return 0 #contains ok
154 fi
155 done
156 _debug "'$_str' does not contain '$_field'"
157 return 1 #not contains
158}
159
dceb3aca 160_exists(){
c60883ef 161 cmd="$1"
8663fb7e 162 if [ -z "$cmd" ] ; then
43822d37 163 _usage "Usage: _exists cmd"
c60883ef 164 return 1
165 fi
eac18b1c 166 if type command >/dev/null 2>&1 ; then
19539575 167 command -v "$cmd" >/dev/null 2>&1
eac18b1c 168 else
19539575 169 type "$cmd" >/dev/null 2>&1
eac18b1c 170 fi
c60883ef 171 ret="$?"
a63b05a9 172 _debug2 "$cmd exists=$ret"
c60883ef 173 return $ret
174}
175
00a50605 176#a + b
177_math(){
178 expr "$@"
179}
180
181_h_char_2_dec() {
182 _ch=$1
183 case "${_ch}" in
184 a|A)
19539575 185 printf "10"
00a50605 186 ;;
187 b|B)
19539575 188 printf "11"
00a50605 189 ;;
190 c|C)
19539575 191 printf "12"
00a50605 192 ;;
193 d|D)
19539575 194 printf "13"
00a50605 195 ;;
196 e|E)
19539575 197 printf "14"
00a50605 198 ;;
199 f|F)
19539575 200 printf "15"
00a50605 201 ;;
202 *)
19539575 203 printf "%s" "$_ch"
00a50605 204 ;;
19539575 205 esac
00a50605 206
207}
208
fac1e367 209
210_URGLY_PRINTF=""
211if [ "$(printf '\x41')" != 'A' ] ; then
212 _URGLY_PRINTF=1
213fi
214
4c3b3608 215_h2b() {
216 hex=$(cat)
217 i=1
218 j=2
00a50605 219 if _exists let ; then
220 uselet="1"
221 fi
7270f277 222 _debug2 uselet "$uselet"
223 _debug2 _URGLY_PRINTF "$_URGLY_PRINTF"
19539575 224 while true ; do
00a50605 225 if [ -z "$_URGLY_PRINTF" ] ; then
19539575 226 h="$(printf $hex | cut -c $i-$j)"
00a50605 227 if [ -z "$h" ] ; then
228 break;
229 fi
230 printf "\x$h"
231 else
19539575 232 ic="$(printf $hex | cut -c $i)"
233 jc="$(printf $hex | cut -c $j)"
00a50605 234 if [ -z "$ic$jc" ] ; then
235 break;
236 fi
19539575 237 ic="$(_h_char_2_dec "$ic")"
238 jc="$(_h_char_2_dec "$jc")"
00a50605 239 printf '\'"$(printf %o "$(_math $ic \* 16 + $jc)")"
4c3b3608 240 fi
00a50605 241 if [ "$uselet" ] ; then
f4312b44 242 let "i+=2" >/dev/null
243 let "j+=2" >/dev/null
00a50605 244 else
245 i="$(_math $i + 2)"
246 j="$(_math $j + 2)"
247 fi
4c3b3608 248 done
249}
250
c60883ef 251#options file
252_sed_i() {
253 options="$1"
254 filename="$2"
8663fb7e 255 if [ -z "$filename" ] ; then
43822d37 256 _usage "Usage:_sed_i options filename"
c60883ef 257 return 1
258 fi
14f3dbb7 259 _debug2 options "$options"
260 if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
c60883ef 261 _debug "Using sed -i"
14f3dbb7 262 sed -i "$options" "$filename"
c60883ef 263 else
264 _debug "No -i support in sed"
19539575 265 text="$(cat "$filename")"
c60883ef 266 echo "$text" | sed "$options" > "$filename"
267 fi
268}
269
22ea4004 270_egrep_o() {
271 if _contains "$(egrep -o 2>&1)" "egrep: illegal option -- o" ; then
272 sed -n 's/.*\('"$1"'\).*/\1/p'
273 else
274 egrep -o "$1"
275 fi
276}
277
88fab7d6 278#Usage: file startline endline
279_getfile() {
280 filename="$1"
281 startline="$2"
282 endline="$3"
8663fb7e 283 if [ -z "$endline" ] ; then
43822d37 284 _usage "Usage: file startline endline"
88fab7d6 285 return 1
286 fi
287
19539575 288 i="$(grep -n -- "$startline" "$filename" | cut -d : -f 1)"
8663fb7e 289 if [ -z "$i" ] ; then
88fab7d6 290 _err "Can not find start line: $startline"
291 return 1
292 fi
19539575 293 i="$(_math "$i" + 1)"
294 _debug i "$i"
88fab7d6 295
19539575 296 j="$(grep -n -- "$endline" "$filename" | cut -d : -f 1)"
8663fb7e 297 if [ -z "$j" ] ; then
88fab7d6 298 _err "Can not find end line: $endline"
299 return 1
300 fi
19539575 301 j="$(_math "$j" - 1)"
302 _debug j "$j"
88fab7d6 303
19539575 304 sed -n "$i,${j}p" "$filename"
88fab7d6 305
306}
307
308#Usage: multiline
4c3b3608 309_base64() {
8663fb7e 310 if [ "$1" ] ; then
88fab7d6 311 openssl base64 -e
312 else
313 openssl base64 -e | tr -d '\r\n'
314 fi
315}
316
317#Usage: multiline
318_dbase64() {
8663fb7e 319 if [ "$1" ] ; then
88fab7d6 320 openssl base64 -d -A
321 else
322 openssl base64 -d
323 fi
324}
325
e22bcf7c 326#Usage: hashalg [outputhex]
88fab7d6 327#Output Base64-encoded digest
328_digest() {
329 alg="$1"
8663fb7e 330 if [ -z "$alg" ] ; then
43822d37 331 _usage "Usage: _digest hashalg"
88fab7d6 332 return 1
333 fi
334
e22bcf7c 335 outputhex="$2"
336
8663fb7e 337 if [ "$alg" = "sha256" ] ; then
e22bcf7c 338 if [ "$outputhex" ] ; then
339 echo $(openssl dgst -sha256 -hex | cut -d = -f 2)
340 else
341 openssl dgst -sha256 -binary | _base64
342 fi
88fab7d6 343 else
344 _err "$alg is not supported yet"
345 return 1
346 fi
347
348}
349
350#Usage: keyfile hashalg
351#Output: Base64-encoded signature value
352_sign() {
353 keyfile="$1"
354 alg="$2"
8663fb7e 355 if [ -z "$alg" ] ; then
43822d37 356 _usage "Usage: _sign keyfile hashalg"
88fab7d6 357 return 1
358 fi
359
8663fb7e 360 if [ "$alg" = "sha256" ] ; then
88fab7d6 361 openssl dgst -sha256 -sign "$keyfile" | _base64
362 else
363 _err "$alg is not supported yet"
364 return 1
fac1e367 365 fi
88fab7d6 366
4c3b3608 367}
368
43822d37 369#keylength
370_isEccKey() {
371 _length="$1"
372
373 if [ -z "$_length" ] ;then
374 return 1
375 fi
376
377 [ "$_length" != "1024" ] \
378 && [ "$_length" != "2048" ] \
379 && [ "$_length" != "3172" ] \
380 && [ "$_length" != "4096" ] \
381 && [ "$_length" != "8192" ]
382}
383
e22bcf7c 384# _createkey 2048|ec-256 file
385_createkey() {
386 length="$1"
387 f="$2"
43822d37 388 eccname="$length"
e22bcf7c 389 if _startswith "$length" "ec-" ; then
e22bcf7c 390 length=$(printf $length | cut -d '-' -f 2-100)
e22bcf7c 391
e22bcf7c 392 if [ "$length" = "256" ] ; then
393 eccname="prime256v1"
394 fi
395 if [ "$length" = "384" ] ; then
396 eccname="secp384r1"
397 fi
398 if [ "$length" = "521" ] ; then
399 eccname="secp521r1"
400 fi
43822d37 401
e22bcf7c 402 fi
403
43822d37 404 if [ -z "$length" ] ; then
405 length=2048
406 fi
407
408 _info "Use length $length"
409
410 if _isEccKey "$length" ; then
411 _info "Using ec name: $eccname"
e22bcf7c 412 openssl ecparam -name $eccname -genkey 2>/dev/null > "$f"
413 else
43822d37 414 _info "Using RSA: $length"
e22bcf7c 415 openssl genrsa $length 2>/dev/null > "$f"
416 fi
43822d37 417
418 if [ "$?" != "0" ] ; then
419 _err "Create key error."
420 return 1
421 fi
e22bcf7c 422}
423
424#_createcsr cn san_list keyfile csrfile conf
425_createcsr() {
426 _debug _createcsr
427 domain="$1"
428 domainlist="$2"
429 key="$3"
430 csr="$4"
431 csrconf="$5"
432 _debug2 domain "$domain"
433 _debug2 domainlist "$domainlist"
434 if [ -z "$domainlist" ] || [ "$domainlist" = "no" ]; then
435 #single domain
436 _info "Single domain" "$domain"
437 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$csrconf"
438 openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
439 else
440 if _contains "$domainlist" "," ; then
441 alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
442 else
443 alt="DNS:$domainlist"
444 fi
445 #multi
446 _info "Multi domain" "$alt"
43822d37 447 printf -- "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\nsubjectAltName=$alt" > "$csrconf"
e22bcf7c 448 openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
449 fi
450}
451
452#_signcsr key csr conf cert
453_signcsr() {
454 key="$1"
455 csr="$2"
456 conf="$3"
457 cert="$4"
5aa146a5 458 _debug "_signcsr"
e22bcf7c 459
5aa146a5 460 _msg="$(openssl x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
461 _ret="$?"
462 _debug "$_msg"
463 return $_ret
e22bcf7c 464}
465
34c27e09 466_ss() {
467 _port="$1"
edf08da6 468
469 if _exists "ss" ; then
470 _debug "Using: ss"
19539575 471 ss -ntpl | grep ":$_port "
edf08da6 472 return 0
473 fi
474
475 if _exists "netstat" ; then
251fc37c 476 _debug "Using: netstat"
ccb96535 477 if netstat -h 2>&1 | grep "\-p proto" >/dev/null ; then
478 #for windows version netstat tool
19539575 479 netstat -anb -p tcp | grep "LISTENING" | grep ":$_port "
ccb96535 480 else
14165e5a 481 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null ; then
19539575 482 netstat -an -p tcp | grep LISTEN | grep ":$_port "
22ea4004 483 elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null ; then
484 #for solaris
e3c66532 485 netstat -an -P tcp | grep "\.$_port " | grep "LISTEN"
edf08da6 486 else
19539575 487 netstat -ntpl | grep ":$_port "
edf08da6 488 fi
ccb96535 489 fi
34c27e09 490 return 0
491 fi
edf08da6 492
34c27e09 493 return 1
494}
495
43822d37 496#domain [password] [isEcc]
ac2d5123 497toPkcs() {
498 domain="$1"
499 pfxPassword="$2"
8663fb7e 500 if [ -z "$domain" ] ; then
43822d37 501 _usage "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
ac2d5123 502 return 1
503 fi
504
43822d37 505 _isEcc="$3"
ac2d5123 506
43822d37 507 _initpath "$domain" "$_isEcc"
508
8663fb7e 509 if [ "$pfxPassword" ] ; then
ac2d5123 510 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
511 else
512 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
513 fi
514
8663fb7e 515 if [ "$?" = "0" ] ; then
ac2d5123 516 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
517 fi
518
519}
520
4c3b3608 521#domain [2048]
522createAccountKey() {
523 _info "Creating account key"
8663fb7e 524 if [ -z "$1" ] ; then
43822d37 525 _usage "Usage: $PROJECT_ENTRY --createAccountKey -d domain.com [--accountkeylength 2048]"
4c3b3608 526 return
527 fi
528
529 account=$1
530 length=$2
b5eb4b90 531 _debug account "$account"
532 _debug length "$length"
dceb3aca 533 if _startswith "$length" "ec-" ; then
4c3b3608 534 length=2048
535 fi
536
8663fb7e 537 if [ -z "$2" ] || [ "$2" = "no" ] ; then
4c3b3608 538 _info "Use default length 2048"
539 length=2048
540 fi
541 _initpath
542
8663fb7e 543 if [ -f "$ACCOUNT_KEY_PATH" ] ; then
4c3b3608 544 _info "Account key exists, skip"
545 return
546 else
547 #generate account key
31a5487c 548 _createkey "$length" "$ACCOUNT_KEY_PATH"
4c3b3608 549 fi
550
551}
552
43822d37 553#domain [length]
4c3b3608 554createDomainKey() {
555 _info "Creating domain key"
8663fb7e 556 if [ -z "$1" ] ; then
43822d37 557 _usage "Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]"
4c3b3608 558 return
559 fi
560
561 domain=$1
e22bcf7c 562 length=$2
43822d37 563
564 _initpath $domain "$length"
e22bcf7c 565
8663fb7e 566 if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
e22bcf7c 567 _createkey "$length" "$CERT_KEY_PATH"
4c3b3608 568 else
8663fb7e 569 if [ "$IS_RENEW" ] ; then
4c3b3608 570 _info "Domain key exists, skip"
571 return 0
572 else
573 _err "Domain key exists, do you want to overwrite the key?"
41e3eafa 574 _err "Add '--force', and try again."
4c3b3608 575 return 1
576 fi
577 fi
578
579}
580
43822d37 581# domain domainlist isEcc
4c3b3608 582createCSR() {
583 _info "Creating csr"
8663fb7e 584 if [ -z "$1" ] ; then
43822d37 585 _usage "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
4c3b3608 586 return
587 fi
4c3b3608 588
43822d37 589 domain="$1"
590 domainlist="$2"
591 _isEcc="$3"
592
593 _initpath "$domain" "$_isEcc"
4c3b3608 594
8663fb7e 595 if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && [ -z "$FORCE" ]; then
4c3b3608 596 _info "CSR exists, skip"
597 return
598 fi
599
43822d37 600 if [ ! -f "$CERT_KEY_PATH" ] ; then
601 _err "The key file is not found: $CERT_KEY_PATH"
602 _err "Please create the key file first."
603 return 1
604 fi
e22bcf7c 605 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
606
4c3b3608 607}
608
166096dc 609_urlencode() {
4c3b3608 610 __n=$(cat)
611 echo $__n | tr '/+' '_-' | tr -d '= '
612}
613
614_time2str() {
615 #BSD
616 if date -u -d@$1 2>/dev/null ; then
617 return
618 fi
619
620 #Linux
621 if date -u -r $1 2>/dev/null ; then
622 return
623 fi
624
22ea4004 625 #Soaris
626 if _exists adb ; then
627 echo $(echo "0t${1}=Y" | adb)
628 fi
629
4c3b3608 630}
631
eae29099 632_normalizeJson() {
633 sed "s/\" *: *\([\"{\[]\)/\":\1/g" | sed "s/^ *\([^ ]\)/\1/" | tr -d "\r\n"
634}
635
44df2967 636_stat() {
637 #Linux
638 if stat -c '%U:%G' "$1" 2>/dev/null ; then
639 return
640 fi
641
642 #BSD
643 if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then
644 return
645 fi
3ad08e95
TB
646
647 return 1; #error, 'stat' not found
44df2967 648}
649
166096dc 650#keyfile
651_calcjwk() {
652 keyfile="$1"
8663fb7e 653 if [ -z "$keyfile" ] ; then
43822d37 654 _usage "Usage: _calcjwk keyfile"
166096dc 655 return 1
656 fi
657 EC_SIGN=""
658 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
659 _debug "RSA key"
660 pub_exp=$(openssl rsa -in $keyfile -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
8663fb7e 661 if [ "${#pub_exp}" = "5" ] ; then
166096dc 662 pub_exp=0$pub_exp
663 fi
22ea4004 664 _debug3 pub_exp "$pub_exp"
166096dc 665
666 e=$(echo $pub_exp | _h2b | _base64)
22ea4004 667 _debug3 e "$e"
166096dc 668
669 modulus=$(openssl rsa -in $keyfile -modulus -noout | cut -d '=' -f 2 )
22ea4004 670 _debug3 modulus "$modulus"
19539575 671 n="$(printf "%s" "$modulus"| _h2b | _base64 | _urlencode )"
166096dc 672 jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
22ea4004 673 _debug3 jwk "$jwk"
166096dc 674
675 HEADER='{"alg": "RS256", "jwk": '$jwk'}'
22ea4004 676 HEADERPLACE_PART1='{"nonce": "'
677 HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
166096dc 678 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
679 _debug "EC key"
680 EC_SIGN="1"
681 crv="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
22ea4004 682 _debug3 crv "$crv"
166096dc 683
684 pubi="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
00a50605 685 pubi=$(_math $pubi + 1)
22ea4004 686 _debug3 pubi "$pubi"
166096dc 687
688 pubj="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
00a50605 689 pubj=$(_math $pubj + 1)
22ea4004 690 _debug3 pubj "$pubj"
166096dc 691
692 pubtext="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
22ea4004 693 _debug3 pubtext "$pubtext"
166096dc 694
695 xlen="$(printf "$pubtext" | tr -d ':' | wc -c)"
00a50605 696 xlen=$(_math $xlen / 4)
22ea4004 697 _debug3 xlen "$xlen"
00a50605 698
19539575 699 xend=$(_math "$xend" + 1)
166096dc 700 x="$(printf $pubtext | cut -d : -f 2-$xend)"
22ea4004 701 _debug3 x "$x"
166096dc 702
703 x64="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)"
22ea4004 704 _debug3 x64 "$x64"
00a50605 705
19539575 706 xend=$(_math "$xend" + 1)
166096dc 707 y="$(printf $pubtext | cut -d : -f $xend-10000)"
22ea4004 708 _debug3 y "$y"
166096dc 709
710 y64="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)"
22ea4004 711 _debug3 y64 "$y64"
166096dc 712
713 jwk='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}'
22ea4004 714 _debug3 jwk "$jwk"
166096dc 715
716 HEADER='{"alg": "ES256", "jwk": '$jwk'}'
22ea4004 717 HEADERPLACE_PART1='{"nonce": "'
718 HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}'
166096dc 719 else
720 _err "Only RSA or EC key is supported."
721 return 1
722 fi
723
22ea4004 724 _debug3 HEADER "$HEADER"
166096dc 725}
fac1e367 726
727
728_mktemp() {
729 if _exists mktemp ; then
730 mktemp
731 fi
732}
733
734_inithttp() {
735
a8df88ab 736 if [ -z "$HTTP_HEADER" ] || ! touch "HTTP_HEADER" ; then
fac1e367 737 HTTP_HEADER="$(_mktemp)"
738 _debug2 HTTP_HEADER "$HTTP_HEADER"
739 fi
740
741 if [ -z "$CURL" ] ; then
742 CURL="curl -L --silent --dump-header $HTTP_HEADER "
743 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
744 _CURL_DUMP="$(_mktemp)"
745 CURL="$CURL --trace-ascii $_CURL_DUMP "
746 fi
747
748 if [ "$HTTPS_INSECURE" ] ; then
749 CURL="$CURL --insecure "
750 fi
751 fi
752
753 if [ -z "$WGET" ] ; then
754 WGET="wget -q"
755 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
756 WGET="$WGET -d "
757 fi
758 if [ "$HTTPS_INSECURE" ] ; then
759 WGET="$WGET --no-check-certificate "
760 fi
761 fi
762
763}
764
765
c839b2b0 766# body url [needbase64] [POST|PUT]
c60883ef 767_post() {
768 body="$1"
769 url="$2"
770 needbase64="$3"
a4270efa 771 httpmethod="$4"
c60883ef 772
a4270efa 773 if [ -z "$httpmethod" ] ; then
774 httpmethod="POST"
775 fi
776 _debug $httpmethod
484d9d2a 777 _debug "url" "$url"
30de13b4 778 _debug2 "body" "$body"
fac1e367 779
780 _inithttp
781
c60883ef 782 if _exists "curl" ; then
fac1e367 783 _CURL="$CURL"
ec9fc8cb 784 _debug "_CURL" "$_CURL"
8663fb7e 785 if [ "$needbase64" ] ; then
63a195e5 786 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" --data "$body" "$url" | _base64)"
c60883ef 787 else
63a195e5 788 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" --data "$body" "$url" )"
c60883ef 789 fi
16679b57 790 _ret="$?"
687cfcc2 791 if [ "$_ret" != "0" ] ; then
87ab2d90 792 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
793 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
794 _err "Here is the curl dump log:"
795 _err "$(cat "$_CURL_DUMP")"
796 fi
687cfcc2 797 fi
d0b748a4 798 elif _exists "wget" ; then
ec9fc8cb 799 _debug "WGET" "$WGET"
8663fb7e 800 if [ "$needbase64" ] ; then
8fb9a709 801 if [ "$httpmethod"="POST" ] ; then
802 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
803 else
804 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
805 fi
c60883ef 806 else
8fb9a709 807 if [ "$httpmethod"="POST" ] ; then
808 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")"
809 else
810 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")"
811 fi
c60883ef 812 fi
16679b57 813 _ret="$?"
687cfcc2 814 if [ "$_ret" != "0" ] ; then
815 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
816 fi
c60883ef 817 _sed_i "s/^ *//g" "$HTTP_HEADER"
d0b748a4 818 else
819 _ret="$?"
820 _err "Neither curl nor wget is found, can not do $httpmethod."
c60883ef 821 fi
16679b57 822 _debug "_ret" "$_ret"
19539575 823 printf "%s" "$response"
16679b57 824 return $_ret
c60883ef 825}
826
d529eb6d 827
75da0713 828# url getheader timeout
c60883ef 829_get() {
a4270efa 830 _debug GET
c60883ef 831 url="$1"
832 onlyheader="$2"
75da0713 833 t="$3"
c60883ef 834 _debug url $url
75da0713 835 _debug "timeout" "$t"
fac1e367 836
837 _inithttp
838
c60883ef 839 if _exists "curl" ; then
75da0713 840 _CURL="$CURL"
841 if [ "$t" ] ; then
842 _CURL="$_CURL --connect-timeout $t"
843 fi
844 _debug "_CURL" "$_CURL"
8663fb7e 845 if [ "$onlyheader" ] ; then
75da0713 846 $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" $url
c60883ef 847 else
75da0713 848 $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" $url
c60883ef 849 fi
9aaf36cd 850 ret=$?
fac1e367 851 if [ "$ret" != "0" ] ; then
d529eb6d 852 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $ret"
fac1e367 853 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
854 _err "Here is the curl dump log:"
855 _err "$(cat "$_CURL_DUMP")"
856 fi
857 fi
d0b748a4 858 elif _exists "wget" ; then
75da0713 859 _WGET="$WGET"
860 if [ "$t" ] ; then
861 _WGET="$_WGET --timeout=$t"
862 fi
863 _debug "_WGET" "$_WGET"
8663fb7e 864 if [ "$onlyheader" ] ; then
75da0713 865 $_WGET --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null $url 2>&1 | sed 's/^[ ]*//g'
c60883ef 866 else
75da0713 867 $_WGET --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - $url
c60883ef 868 fi
9aaf36cd 869 ret=$?
fac1e367 870 if [ "$ret" != "0" ] ; then
d529eb6d 871 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
fac1e367 872 fi
d0b748a4 873 else
874 ret=$?
875 _err "Neither curl nor wget is found, can not do GET."
9aaf36cd 876 fi
ec9fc8cb 877 _debug "ret" "$ret"
c60883ef 878 return $ret
879}
166096dc 880
fac1e367 881
166096dc 882# url payload needbase64 keyfile
4c3b3608 883_send_signed_request() {
884 url=$1
885 payload=$2
886 needbase64=$3
166096dc 887 keyfile=$4
8663fb7e 888 if [ -z "$keyfile" ] ; then
166096dc 889 keyfile="$ACCOUNT_KEY_PATH"
890 fi
4c3b3608 891 _debug url $url
892 _debug payload "$payload"
893
166096dc 894 if ! _calcjwk "$keyfile" ; then
895 return 1
896 fi
c60883ef 897
22ea4004 898 payload64=$(printf "%s" "$payload" | _base64 | _urlencode)
899 _debug3 payload64 $payload64
4c3b3608 900
901 nonceurl="$API/directory"
a272ee4f 902 _headers="$(_get $nonceurl "onlyheader")"
903
7012b91f 904 if [ "$?" != "0" ] ; then
905 _err "Can not connect to $nonceurl to get nonce."
906 return 1
907 fi
a272ee4f 908
22ea4004 909 _debug3 _headers "$_headers"
a272ee4f 910
911 nonce="$( echo "$_headers" | grep "Replay-Nonce:" | head -1 | tr -d "\r\n " | cut -d ':' -f 2)"
912
22ea4004 913 _debug3 nonce "$nonce"
4c3b3608 914
22ea4004 915 protected="$HEADERPLACE_PART1$nonce$HEADERPLACE_PART2"
916 _debug3 protected "$protected"
4c3b3608 917
166096dc 918 protected64="$(printf "$protected" | _base64 | _urlencode)"
22ea4004 919 _debug3 protected64 "$protected64"
166096dc 920
22ea4004 921 sig=$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256" | _urlencode)
922 _debug3 sig "$sig"
4c3b3608 923
924 body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
22ea4004 925 _debug3 body "$body"
4c3b3608 926
bbbdcb09 927
eae29099 928 response="$(_post "$body" $url "$needbase64")"
7012b91f 929 if [ "$?" != "0" ] ; then
930 _err "Can not post to $url."
931 return 1
932 fi
eae29099 933 _debug2 original "$response"
934
935 response="$( echo "$response" | _normalizeJson )"
4c3b3608 936
c60883ef 937 responseHeaders="$(cat $HTTP_HEADER)"
4c3b3608 938
a63b05a9 939 _debug2 responseHeaders "$responseHeaders"
940 _debug2 response "$response"
c60883ef 941 code="$(grep "^HTTP" $HTTP_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
4c3b3608 942 _debug code $code
943
944}
945
4c3b3608 946
947#setopt "file" "opt" "=" "value" [";"]
948_setopt() {
949 __conf="$1"
950 __opt="$2"
951 __sep="$3"
952 __val="$4"
953 __end="$5"
8663fb7e 954 if [ -z "$__opt" ] ; then
43822d37 955 _usage usage: _setopt '"file" "opt" "=" "value" [";"]'
4c3b3608 956 return
957 fi
8663fb7e 958 if [ ! -f "$__conf" ] ; then
4c3b3608 959 touch "$__conf"
960 fi
961
22ea4004 962 if grep -n "^$__opt$__sep" "$__conf" > /dev/null ; then
963 _debug3 OK
dceb3aca 964 if _contains "$__val" "&" ; then
4c3b3608 965 __val="$(echo $__val | sed 's/&/\\&/g')"
966 fi
967 text="$(cat $__conf)"
6dfaaa70 968 echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
4c3b3608 969
22ea4004 970 elif grep -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
dceb3aca 971 if _contains "$__val" "&" ; then
4c3b3608 972 __val="$(echo $__val | sed 's/&/\\&/g')"
973 fi
974 text="$(cat $__conf)"
6dfaaa70 975 echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
4c3b3608 976
977 else
22ea4004 978 _debug3 APP
4c3b3608 979 echo "$__opt$__sep$__val$__end" >> "$__conf"
980 fi
22ea4004 981 _debug2 "$(grep -n "^$__opt$__sep" $__conf)"
4c3b3608 982}
983
984#_savedomainconf key value
985#save to domain.conf
986_savedomainconf() {
987 key="$1"
988 value="$2"
8663fb7e 989 if [ "$DOMAIN_CONF" ] ; then
4d2f38b0 990 _setopt "$DOMAIN_CONF" "$key" "=" "\"$value\""
991 else
992 _err "DOMAIN_CONF is empty, can not save $key=$value"
993 fi
994}
995
996#_cleardomainconf key
997_cleardomainconf() {
998 key="$1"
999 if [ "$DOMAIN_CONF" ] ; then
1000 _sed_i "s/^$key.*$//" "$DOMAIN_CONF"
4c3b3608 1001 else
1002 _err "DOMAIN_CONF is empty, can not save $key=$value"
1003 fi
1004}
1005
61623d22 1006#_readdomainconf key
1007_readdomainconf() {
1008 key="$1"
1009 if [ "$DOMAIN_CONF" ] ; then
1010 (
1011 eval $(grep "^$key *=" "$DOMAIN_CONF")
1012 eval "printf \"%s\" \"\$$key\""
1013 )
1014 else
1015 _err "DOMAIN_CONF is empty, can not read $key"
1016 fi
1017}
1018
4c3b3608 1019#_saveaccountconf key value
1020_saveaccountconf() {
1021 key="$1"
1022 value="$2"
8663fb7e 1023 if [ "$ACCOUNT_CONF_PATH" ] ; then
4d2f38b0 1024 _setopt "$ACCOUNT_CONF_PATH" "$key" "=" "\"$value\""
4c3b3608 1025 else
1026 _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
1027 fi
1028}
1029
fac1e367 1030#_clearaccountconf key
1031_clearaccountconf() {
1032 key="$1"
1033 if [ "$ACCOUNT_CONF_PATH" ] ; then
1034 _sed_i "s/^$key.*$//" "$ACCOUNT_CONF_PATH"
1035 else
1036 _err "ACCOUNT_CONF_PATH is empty, can not clear $key"
1037 fi
1038}
1039
4c3b3608 1040_startserver() {
1041 content="$1"
6fc1447f 1042 _debug "startserver: $$"
1b2e940d 1043 nchelp="$(nc -h 2>&1)"
850c1128 1044
399306a1 1045 if echo "$nchelp" | grep "\-q[ ,]" >/dev/null ; then
850c1128 1046 _NC="nc -q 1 -l"
1047 else
f76eb452 1048 if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null ; then
1049 _NC="nc -c -l"
6d60f288 1050 elif echo "$nchelp" | grep "\-N" |grep "Shutdown the network socket after EOF on stdin" >/dev/null ; then
1051 _NC="nc -N -l"
f76eb452 1052 else
1053 _NC="nc -l"
1054 fi
4c3b3608 1055 fi
1b2e940d 1056
f76eb452 1057 _debug "_NC" "$_NC"
39c8f79f 1058 _debug Le_HTTPPort "$Le_HTTPPort"
4c3b3608 1059# while true ; do
8663fb7e 1060 if [ "$DEBUG" ] ; then
2c554a4b 1061 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort ; then
1062 printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ;
3aff11f6 1063 fi
4c3b3608 1064 else
2c554a4b 1065 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then
1066 printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1
3aff11f6 1067 fi
4c3b3608 1068 fi
8663fb7e 1069 if [ "$?" != "0" ] ; then
051c706d 1070 _err "nc listen error."
6fc1447f 1071 exit 1
051c706d 1072 fi
4c3b3608 1073# done
1074}
1075
6fc1447f 1076_stopserver(){
4c3b3608 1077 pid="$1"
6fc1447f 1078 _debug "pid" "$pid"
8663fb7e 1079 if [ -z "$pid" ] ; then
6fc1447f 1080 return
1081 fi
e22bcf7c 1082
dcf9cb58 1083 _debug2 "Le_HTTPPort" "$Le_HTTPPort"
1084 if [ "$Le_HTTPPort" ] ; then
1085 if [ "$DEBUG" ] ; then
22ea4004 1086 _get "http://localhost:$Le_HTTPPort" "" 1
dcf9cb58 1087 else
22ea4004 1088 _get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1
dcf9cb58 1089 fi
1090 fi
1091
1092 _debug2 "Le_TLSPort" "$Le_TLSPort"
1093 if [ "$Le_TLSPort" ] ; then
1094 if [ "$DEBUG" ] ; then
75da0713 1095 _get "https://localhost:$Le_TLSPort" "" 1
1096 _get "https://localhost:$Le_TLSPort" "" 1
dcf9cb58 1097 else
75da0713 1098 _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
1099 _get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
dcf9cb58 1100 fi
1101 fi
4c3b3608 1102}
1103
e22bcf7c 1104
1105# _starttlsserver san_a san_b port content
1106_starttlsserver() {
1107 _info "Starting tls server."
1108 san_a="$1"
1109 san_b="$2"
1110 port="$3"
1111 content="$4"
1112
1113 _debug san_a "$san_a"
1114 _debug san_b "$san_b"
1115 _debug port "$port"
1116
1117 #create key TLS_KEY
1118 if ! _createkey "2048" "$TLS_KEY" ; then
1119 _err "Create tls validation key error."
1120 return 1
1121 fi
1122
1123 #create csr
1124 alt="$san_a"
1125 if [ "$san_b" ] ; then
1126 alt="$alt,$san_b"
1127 fi
1128 if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" ; then
1129 _err "Create tls validation csr error."
1130 return 1
1131 fi
1132
1133 #self signed
1134 if ! _signcsr "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT" ; then
1135 _err "Create tls validation cert error."
1136 return 1
1137 fi
1138
1139 #start openssl
bdbf323f 1140 _debug "openssl s_server -cert \"$TLS_CERT\" -key \"$TLS_KEY\" -accept $port -naccept 1 -tlsextdebug"
fbad6a39 1141 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
75da0713 1142 (printf "HTTP/1.1 200 OK\r\n\r\n$content" | openssl s_server -cert "$TLS_CERT" -key "$TLS_KEY" -accept $port -tlsextdebug ) &
331c4bb6 1143 else
75da0713 1144 (printf "HTTP/1.1 200 OK\r\n\r\n$content" | openssl s_server -cert "$TLS_CERT" -key "$TLS_KEY" -accept $port >/dev/null 2>&1) &
331c4bb6 1145 fi
1146
e22bcf7c 1147 serverproc="$!"
1148 sleep 2
1149 _debug serverproc $serverproc
1150}
1151
43822d37 1152#[domain] [keylength]
4c3b3608 1153_initpath() {
1154
8663fb7e 1155 if [ -z "$LE_WORKING_DIR" ] ; then
6cc11ffb 1156 LE_WORKING_DIR=$HOME/.$PROJECT_NAME
4c3b3608 1157 fi
1158
d53289d7 1159 _DEFAULT_ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
1160
8663fb7e 1161 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
1162 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ] ; then
1163 . "$_DEFAULT_ACCOUNT_CONF_PATH"
635695ec 1164 fi
d53289d7 1165 fi
1166
8663fb7e 1167 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
d53289d7 1168 ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
4c3b3608 1169 fi
1170
8663fb7e 1171 if [ -f "$ACCOUNT_CONF_PATH" ] ; then
1172 . "$ACCOUNT_CONF_PATH"
4c3b3608 1173 fi
1174
8663fb7e 1175 if [ "$IN_CRON" ] ; then
1176 if [ ! "$_USER_PATH_EXPORTED" ] ; then
281aa349 1177 _USER_PATH_EXPORTED=1
1178 export PATH="$USER_PATH:$PATH"
1179 fi
1180 fi
1181
8663fb7e 1182 if [ -z "$API" ] ; then
1183 if [ -z "$STAGE" ] ; then
4c3b3608 1184 API="$DEFAULT_CA"
1185 else
1186 API="$STAGE_CA"
1187 _info "Using stage api:$API"
1188 fi
1189 fi
1190
8663fb7e 1191 if [ -z "$ACME_DIR" ] ; then
4c3b3608 1192 ACME_DIR="/home/.acme"
1193 fi
1194
8663fb7e 1195 if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
8a144f4d 1196 APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR"
4c3b3608 1197 fi
1198
8663fb7e 1199 if [ -z "$USER_AGENT" ] ; then
bbbdcb09 1200 USER_AGENT="$DEFAULT_USER_AGENT"
1201 fi
1202
933c169d 1203 if [ -z "$HTTP_HEADER" ] ; then
1204 HTTP_HEADER="$LE_WORKING_DIR/http.header"
1205 fi
b2817897 1206
1207 _DEFAULT_ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
8663fb7e 1208 if [ -z "$ACCOUNT_KEY_PATH" ] ; then
b2817897 1209 ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
4c3b3608 1210 fi
b2817897 1211
a79b26af
RD
1212 _DEFAULT_CERT_HOME="$LE_WORKING_DIR"
1213 if [ -z "$CERT_HOME" ] ; then
1214 CERT_HOME="$_DEFAULT_CERT_HOME"
1215 fi
1216
b2817897 1217 domain="$1"
43822d37 1218 length="$2"
8663fb7e 1219 if [ -z "$domain" ] ; then
4c3b3608 1220 return 0
1221 fi
1222
4c3b3608 1223
8663fb7e 1224 if [ -z "$DOMAIN_PATH" ] ; then
43822d37 1225 domainhome="$CERT_HOME/$domain"
1226 domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
1227
4c3b3608 1228 DOMAIN_PATH="$domainhome"
43822d37 1229
1230 if _isEccKey "$length" ; then
1231 DOMAIN_PATH="$domainhomeecc"
1232 else
1233 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ] ; then
1234 _info "The domain '$domain' seems to be a ECC domain, please add '$(__red "--ecc")' parameter next time."
1235 DOMAIN_PATH="$domainhomeecc"
1236 fi
1237 fi
1238 _debug DOMAIN_PATH "$DOMAIN_PATH"
4c3b3608 1239 fi
43822d37 1240
933c169d 1241 if [ ! -d "$DOMAIN_PATH" ] ; then
1242 if ! mkdir -p "$DOMAIN_PATH" ; then
1243 _err "Can not create domain path: $DOMAIN_PATH"
1244 return 1
1245 fi
1246 fi
1247
8663fb7e 1248 if [ -z "$DOMAIN_CONF" ] ; then
43822d37 1249 DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
4c3b3608 1250 fi
1251
8663fb7e 1252 if [ -z "$DOMAIN_SSL_CONF" ] ; then
43822d37 1253 DOMAIN_SSL_CONF="$DOMAIN_PATH/$domain.ssl.conf"
4c3b3608 1254 fi
1255
8663fb7e 1256 if [ -z "$CSR_PATH" ] ; then
43822d37 1257 CSR_PATH="$DOMAIN_PATH/$domain.csr"
4c3b3608 1258 fi
8663fb7e 1259 if [ -z "$CERT_KEY_PATH" ] ; then
43822d37 1260 CERT_KEY_PATH="$DOMAIN_PATH/$domain.key"
4c3b3608 1261 fi
8663fb7e 1262 if [ -z "$CERT_PATH" ] ; then
43822d37 1263 CERT_PATH="$DOMAIN_PATH/$domain.cer"
4c3b3608 1264 fi
8663fb7e 1265 if [ -z "$CA_CERT_PATH" ] ; then
43822d37 1266 CA_CERT_PATH="$DOMAIN_PATH/ca.cer"
4c3b3608 1267 fi
8663fb7e 1268 if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
43822d37 1269 CERT_FULLCHAIN_PATH="$DOMAIN_PATH/fullchain.cer"
caf1fc10 1270 fi
8663fb7e 1271 if [ -z "$CERT_PFX_PATH" ] ; then
43822d37 1272 CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
ac2d5123 1273 fi
e22bcf7c 1274
1275 if [ -z "$TLS_CONF" ] ; then
43822d37 1276 TLS_CONF="$DOMAIN_PATH/tls.valdation.conf"
e22bcf7c 1277 fi
1278 if [ -z "$TLS_CERT" ] ; then
43822d37 1279 TLS_CERT="$DOMAIN_PATH/tls.valdation.cert"
e22bcf7c 1280 fi
1281 if [ -z "$TLS_KEY" ] ; then
43822d37 1282 TLS_KEY="$DOMAIN_PATH/tls.valdation.key"
e22bcf7c 1283 fi
1284 if [ -z "$TLS_CSR" ] ; then
43822d37 1285 TLS_CSR="$DOMAIN_PATH/tls.valdation.csr"
e22bcf7c 1286 fi
1287
4c3b3608 1288}
1289
1290
1291_apachePath() {
c3dd3ef0 1292 _APACHECTL="apachectl"
80a0a7b5 1293 if ! _exists apachectl ; then
e4a19585 1294 if _exists apache2ctl ; then
c3dd3ef0 1295 _APACHECTL="apache2ctl"
e4a19585 1296 else
bc96082f 1297 _err "'apachectl not found. It seems that apache is not installed, or you are not root user.'"
e4a19585 1298 _err "Please use webroot mode to try again."
1299 return 1
1300 fi
80a0a7b5 1301 fi
c3dd3ef0 1302 httpdconfname="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
78768e98 1303 _debug httpdconfname "$httpdconfname"
dceb3aca 1304 if _startswith "$httpdconfname" '/' ; then
d62ee940 1305 httpdconf="$httpdconfname"
c456d954 1306 httpdconfname="$(basename $httpdconfname)"
d62ee940 1307 else
c3dd3ef0 1308 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
78768e98 1309 _debug httpdroot "$httpdroot"
d62ee940 1310 httpdconf="$httpdroot/$httpdconfname"
8f63baf7 1311 httpdconfname="$(basename $httpdconfname)"
d62ee940 1312 fi
78768e98 1313 _debug httpdconf "$httpdconf"
8f63baf7 1314 _debug httpdconfname "$httpdconfname"
78768e98 1315 if [ ! -f "$httpdconf" ] ; then
1316 _err "Apache Config file not found" "$httpdconf"
4c3b3608 1317 return 1
1318 fi
1319 return 0
1320}
1321
1322_restoreApache() {
8663fb7e 1323 if [ -z "$usingApache" ] ; then
4c3b3608 1324 return 0
1325 fi
1326 _initpath
1327 if ! _apachePath ; then
1328 return 1
1329 fi
1330
8663fb7e 1331 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
4c3b3608 1332 _debug "No config file to restore."
1333 return 0
1334 fi
1335
ff3bce32 1336 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" > "$httpdconf"
5ef501c5 1337 _debug "Restored: $httpdconf."
c3dd3ef0 1338 if ! $_APACHECTL -t >/dev/null 2>&1 ; then
4c3b3608 1339 _err "Sorry, restore apache config error, please contact me."
1340 return 1;
1341 fi
5ef501c5 1342 _debug "Restored successfully."
4c3b3608 1343 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
1344 return 0
1345}
1346
1347_setApache() {
1348 _initpath
1349 if ! _apachePath ; then
1350 return 1
1351 fi
1352
5fc5016d 1353 #test the conf first
869578ce 1354 _info "Checking if there is an error in the apache config file before starting."
c3dd3ef0 1355 _msg="$($_APACHECTL -t 2>&1 )"
5fc5016d 1356 if [ "$?" != "0" ] ; then
869578ce 1357 _err "Sorry, apache config file has error, please fix it first, then try again."
1358 _err "Don't worry, there is nothing changed to your system."
5fc5016d 1359 _err "$_msg"
1360 return 1;
1361 else
1362 _info "OK"
1363 fi
1364
4c3b3608 1365 #backup the conf
5778811a 1366 _debug "Backup apache config file" "$httpdconf"
8f63baf7 1367 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/" ; then
869578ce 1368 _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
8f63baf7 1369 _err "This might be a bug of $PROJECT_NAME , pleae report issue: $PROJECT"
1370 return 1
1371 fi
4c3b3608 1372 _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
1373 _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
1374 _info "The backup file will be deleted on sucess, just forget it."
1375
1376 #add alias
b09d597c 1377
c3dd3ef0 1378 apacheVer="$($_APACHECTL -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2 )"
b09d597c 1379 _debug "apacheVer" "$apacheVer"
1380 apacheMajer="$(echo "$apacheVer" | cut -d . -f 1)"
1381 apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)"
1382
5778811a 1383 if [ "$apacheVer" ] && [ "$apacheMajer$apacheMinor" -ge "24" ] ; then
b09d597c 1384 echo "
4c3b3608 1385Alias /.well-known/acme-challenge $ACME_DIR
1386
1387<Directory $ACME_DIR >
1388Require all granted
b09d597c 1389</Directory>
5778811a 1390 " >> "$httpdconf"
b09d597c 1391 else
1392 echo "
1393Alias /.well-known/acme-challenge $ACME_DIR
1394
1395<Directory $ACME_DIR >
1396Order allow,deny
1397Allow from all
4c3b3608 1398</Directory>
5778811a 1399 " >> "$httpdconf"
b09d597c 1400 fi
1401
c3dd3ef0 1402 _msg="$($_APACHECTL -t 2>&1 )"
5fc5016d 1403 if [ "$?" != "0" ] ; then
1404 _err "Sorry, apache config error"
1405 if _restoreApache ; then
869578ce 1406 _err "The apache config file is restored."
5fc5016d 1407 else
869578ce 1408 _err "Sorry, The apache config file can not be restored, please report bug."
5fc5016d 1409 fi
4c3b3608 1410 return 1;
1411 fi
1412
8663fb7e 1413 if [ ! -d "$ACME_DIR" ] ; then
4c3b3608 1414 mkdir -p "$ACME_DIR"
1415 chmod 755 "$ACME_DIR"
1416 fi
1417
c3dd3ef0 1418 if ! $_APACHECTL graceful ; then
1419 _err "Sorry, $_APACHECTL graceful error, please contact me."
4c3b3608 1420 _restoreApache
1421 return 1;
1422 fi
1423 usingApache="1"
1424 return 0
1425}
1426
5ef501c5 1427_clearup() {
4c3b3608 1428 _stopserver $serverproc
1429 serverproc=""
1430 _restoreApache
e22bcf7c 1431 if [ -z "$DEBUG" ] ; then
1432 rm -f "$TLS_CONF"
1433 rm -f "$TLS_CERT"
1434 rm -f "$TLS_KEY"
1435 rm -f "$TLS_CSR"
1436 fi
4c3b3608 1437}
1438
1439# webroot removelevel tokenfile
1440_clearupwebbroot() {
1441 __webroot="$1"
8663fb7e 1442 if [ -z "$__webroot" ] ; then
4c3b3608 1443 _debug "no webroot specified, skip"
1444 return 0
1445 fi
1446
dcf9cb58 1447 _rmpath=""
8663fb7e 1448 if [ "$2" = '1' ] ; then
dcf9cb58 1449 _rmpath="$__webroot/.well-known"
8663fb7e 1450 elif [ "$2" = '2' ] ; then
dcf9cb58 1451 _rmpath="$__webroot/.well-known/acme-challenge"
8663fb7e 1452 elif [ "$2" = '3' ] ; then
dcf9cb58 1453 _rmpath="$__webroot/.well-known/acme-challenge/$3"
4c3b3608 1454 else
cc179731 1455 _debug "Skip for removelevel:$2"
4c3b3608 1456 fi
1457
dcf9cb58 1458 if [ "$_rmpath" ] ; then
1459 if [ "$DEBUG" ] ; then
1460 _debug "Debugging, skip removing: $_rmpath"
1461 else
1462 rm -rf "$_rmpath"
1463 fi
1464 fi
1465
4c3b3608 1466 return 0
1467
1468}
1469
1470issue() {
8663fb7e 1471 if [ -z "$2" ] ; then
43822d37 1472 _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
4c3b3608 1473 return 1
1474 fi
1475 Le_Webroot="$1"
1476 Le_Domain="$2"
1477 Le_Alt="$3"
1478 Le_Keylength="$4"
1479 Le_RealCertPath="$5"
1480 Le_RealKeyPath="$6"
1481 Le_RealCACertPath="$7"
1482 Le_ReloadCmd="$8"
a63b05a9 1483 Le_RealFullChainPath="$9"
4c3b3608 1484
eccec5f6 1485 #remove these later.
8663fb7e 1486 if [ "$Le_Webroot" = "dns-cf" ] ; then
eccec5f6 1487 Le_Webroot="dns_cf"
1488 fi
8663fb7e 1489 if [ "$Le_Webroot" = "dns-dp" ] ; then
eccec5f6 1490 Le_Webroot="dns_dp"
1491 fi
8663fb7e 1492 if [ "$Le_Webroot" = "dns-cx" ] ; then
eccec5f6 1493 Le_Webroot="dns_cx"
1494 fi
4c3b3608 1495
43822d37 1496 if [ ! "$IS_RENEW" ] ; then
1497 _initpath $Le_Domain "$Le_Keylength"
1498 mkdir -p "$DOMAIN_PATH"
1499 fi
eccec5f6 1500
8663fb7e 1501 if [ -f "$DOMAIN_CONF" ] ; then
61623d22 1502 Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
a4270efa 1503 _debug Le_NextRenewTime "$Le_NextRenewTime"
1504 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ $(date -u "+%s" ) -lt $Le_NextRenewTime ] ; then
4c3b3608 1505 _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
cc179731 1506 return $RENEW_SKIP
4c3b3608 1507 fi
1508 fi
96a46cfc 1509
4d2f38b0 1510 _savedomainconf "Le_Domain" "$Le_Domain"
1511 _savedomainconf "Le_Alt" "$Le_Alt"
1512 _savedomainconf "Le_Webroot" "$Le_Webroot"
4c3b3608 1513
61623d22 1514
8663fb7e 1515 if [ "$Le_Alt" = "no" ] ; then
4c3b3608 1516 Le_Alt=""
1517 fi
61623d22 1518
c53da1ef 1519 if _hasfield "$Le_Webroot" "no" ; then
4c3b3608 1520 _info "Standalone mode."
5ef501c5 1521 if ! _exists "nc" ; then
4c3b3608 1522 _err "Please install netcat(nc) tools first."
1523 return 1
1524 fi
1525
8663fb7e 1526 if [ -z "$Le_HTTPPort" ] ; then
4c3b3608 1527 Le_HTTPPort=80
054cb72e 1528 else
1529 _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
1530 fi
4c3b3608 1531
251fc37c 1532 netprc="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")"
8663fb7e 1533 if [ "$netprc" ] ; then
4c3b3608 1534 _err "$netprc"
1535 _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
1536 _err "Please stop it first"
1537 return 1
1538 fi
1539 fi
1540
e22bcf7c 1541 if _hasfield "$Le_Webroot" "$W_TLS" ; then
1542 _info "Standalone tls mode."
1543
1544 if [ -z "$Le_TLSPort" ] ; then
1545 Le_TLSPort=443
1546 else
1547 _savedomainconf "Le_TLSPort" "$Le_TLSPort"
1548 fi
1549
1550 netprc="$(_ss "$Le_TLSPort" | grep "$Le_TLSPort")"
1551 if [ "$netprc" ] ; then
1552 _err "$netprc"
1553 _err "tcp port $Le_TLSPort is already used by $(echo "$netprc" | cut -d : -f 4)"
1554 _err "Please stop it first"
1555 return 1
1556 fi
1557 fi
1558
c53da1ef 1559 if _hasfield "$Le_Webroot" "apache" ; then
4c3b3608 1560 if ! _setApache ; then
1561 _err "set up apache error. Report error to me."
1562 return 1
1563 fi
4c3b3608 1564 else
1565 usingApache=""
1566 fi
1567
8663fb7e 1568 if [ ! -f "$ACCOUNT_KEY_PATH" ] ; then
41e3eafa 1569 if ! createAccountKey $Le_Domain $Le_Keylength ; then
1570 _err "Create account key error."
8663fb7e 1571 if [ "$usingApache" ] ; then
5ef501c5 1572 _restoreApache
1573 fi
41e3eafa 1574 return 1
1575 fi
1576 fi
1577
166096dc 1578 if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then
8663fb7e 1579 if [ "$usingApache" ] ; then
5ef501c5 1580 _restoreApache
1581 fi
166096dc 1582 return 1
1583 fi
1584
22ea4004 1585 accountkey_json=$(printf "%s" "$jwk" | tr -d ' ' )
1586 thumbprint=$(printf "%s" "$accountkey_json" | _digest "sha256" | _urlencode)
4c3b3608 1587
f574e581 1588 regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
1589 if [ "$ACCOUNT_EMAIL" ] ; then
1590 regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
1591 fi
1592
88fab7d6 1593 accountkeyhash="$(cat "$ACCOUNT_KEY_PATH" | _digest "sha256" )"
f574e581 1594 accountkeyhash="$(echo $accountkeyhash$API$regjson | _digest "sha256" )"
8663fb7e 1595 if [ "$accountkeyhash" != "$ACCOUNT_KEY_HASH" ] ; then
f574e581 1596 _info "Registering account"
1597 _send_signed_request "$API/acme/new-reg" "$regjson"
8663fb7e 1598 if [ "$code" = "" ] || [ "$code" = '201' ] ; then
166096dc 1599 _info "Registered"
f574e581 1600 echo "$response" > $LE_WORKING_DIR/account.json
8663fb7e 1601 elif [ "$code" = '409' ] ; then
166096dc 1602 _info "Already registered"
1603 else
1604 _err "Register account Error: $response"
1605 _clearup
1606 return 1
1607 fi
1608 ACCOUNT_KEY_HASH="$accountkeyhash"
1609 _saveaccountconf "ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH"
1610 else
1611 _info "Skip register account key"
1612 fi
1613
58f41a19 1614 if [ "$Le_Keylength" = "no" ] ; then
1615 Le_Keylength=""
1616 fi
1617
61623d22 1618 _key=$(_readdomainconf Le_Keylength)
1619 _debug "Read key length:$_key"
d8d10bc4 1620 if [ ! -f "$CERT_KEY_PATH" ] || [ "$Le_Keylength" != "$_key" ] ; then
41e3eafa 1621 if ! createDomainKey $Le_Domain $Le_Keylength ; then
1622 _err "Create domain key error."
5ef501c5 1623 _clearup
41e3eafa 1624 return 1
1625 fi
4c3b3608 1626 fi
1627
61623d22 1628 _savedomainconf "Le_Keylength" "$Le_Keylength"
58f41a19 1629
61623d22 1630
43822d37 1631 if ! _createcsr "$Le_Domain" "$Le_Alt" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF" ; then
4c3b3608 1632 _err "Create CSR error."
5ef501c5 1633 _clearup
4c3b3608 1634 return 1
1635 fi
a63b05a9 1636
4c3b3608 1637 vlist="$Le_Vlist"
1638 # verify each domain
1639 _info "Verify each domain"
1640 sep='#'
8663fb7e 1641 if [ -z "$vlist" ] ; then
4c3b3608 1642 alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
a63b05a9 1643 _index=1
1644 _currentRoot=""
4c3b3608 1645 for d in $alldomains
a63b05a9 1646 do
1647 _info "Getting webroot for domain" $d
1648 _w="$(echo $Le_Webroot | cut -d , -f $_index)"
1649 _debug _w "$_w"
8663fb7e 1650 if [ "$_w" ] ; then
a63b05a9 1651 _currentRoot="$_w"
1652 fi
1653 _debug "_currentRoot" "$_currentRoot"
00a50605 1654 _index=$(_math $_index + 1)
a63b05a9 1655
1656 vtype="$VTYPE_HTTP"
dceb3aca 1657 if _startswith "$_currentRoot" "dns" ; then
a63b05a9 1658 vtype="$VTYPE_DNS"
1659 fi
e22bcf7c 1660
1661 if [ "$_currentRoot" = "$W_TLS" ] ; then
1662 vtype="$VTYPE_TLS"
1663 fi
1664
4c3b3608 1665 _info "Getting token for domain" $d
c4d8fd83 1666
1667 if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}" ; then
1668 _err "Can not get domain token."
1669 _clearup
1670 return 1
1671 fi
1672
8663fb7e 1673 if [ ! -z "$code" ] && [ ! "$code" = '201' ] ; then
4c3b3608 1674 _err "new-authz error: $response"
1675 _clearup
1676 return 1
1677 fi
1678
22ea4004 1679 entry="$(printf "%s\n" "$response" | _egrep_o '[^{]*"type":"'$vtype'"[^}]*')"
4c3b3608 1680 _debug entry "$entry"
19539575 1681 if [ -z "$entry" ] ; then
1682 _err "Error, can not get domain token $d"
1683 _clearup
1684 return 1
1685 fi
22ea4004 1686 token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
4c3b3608 1687 _debug token $token
1688
22ea4004 1689 uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
4c3b3608 1690 _debug uri $uri
1691
1692 keyauthorization="$token.$thumbprint"
1693 _debug keyauthorization "$keyauthorization"
1694
d35bf517 1695
1696 if printf "$response" | grep '"status":"valid"' >/dev/null 2>&1 ; then
1697 _info "$d is already verified, skip."
1698 keyauthorization=$STATE_VERIFIED
1699 _debug keyauthorization "$keyauthorization"
ec603bee 1700 fi
1701
d35bf517 1702
a63b05a9 1703 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
4c3b3608 1704 _debug dvlist "$dvlist"
1705
1706 vlist="$vlist$dvlist,"
1707
1708 done
1709
1710 #add entry
1711 dnsadded=""
1712 ventries=$(echo "$vlist" | tr ',' ' ' )
1713 for ventry in $ventries
1714 do
1715 d=$(echo $ventry | cut -d $sep -f 1)
1716 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
a63b05a9 1717 vtype=$(echo $ventry | cut -d $sep -f 4)
1718 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
ec603bee 1719
bd5e57d8 1720 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
ec603bee 1721 _info "$d is already verified, skip $vtype."
1722 continue
1723 fi
1724
8663fb7e 1725 if [ "$vtype" = "$VTYPE_DNS" ] ; then
4c3b3608 1726 dnsadded='0'
1727 txtdomain="_acme-challenge.$d"
1728 _debug txtdomain "$txtdomain"
22ea4004 1729 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _urlencode)"
4c3b3608 1730 _debug txt "$txt"
1731 #dns
1732 #1. check use api
1733 d_api=""
8663fb7e 1734 if [ -f "$LE_WORKING_DIR/$d/$_currentRoot" ] ; then
a63b05a9 1735 d_api="$LE_WORKING_DIR/$d/$_currentRoot"
8663fb7e 1736 elif [ -f "$LE_WORKING_DIR/$d/$_currentRoot.sh" ] ; then
a63b05a9 1737 d_api="$LE_WORKING_DIR/$d/$_currentRoot.sh"
8663fb7e 1738 elif [ -f "$LE_WORKING_DIR/$_currentRoot" ] ; then
a63b05a9 1739 d_api="$LE_WORKING_DIR/$_currentRoot"
8663fb7e 1740 elif [ -f "$LE_WORKING_DIR/$_currentRoot.sh" ] ; then
a63b05a9 1741 d_api="$LE_WORKING_DIR/$_currentRoot.sh"
8663fb7e 1742 elif [ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot" ] ; then
a63b05a9 1743 d_api="$LE_WORKING_DIR/dnsapi/$_currentRoot"
8663fb7e 1744 elif [ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot.sh" ] ; then
a63b05a9 1745 d_api="$LE_WORKING_DIR/dnsapi/$_currentRoot.sh"
4c3b3608 1746 fi
1747 _debug d_api "$d_api"
1748
8663fb7e 1749 if [ "$d_api" ] ; then
4c3b3608 1750 _info "Found domain api file: $d_api"
1751 else
1752 _err "Add the following TXT record:"
1753 _err "Domain: $txtdomain"
1754 _err "TXT value: $txt"
1755 _err "Please be aware that you prepend _acme-challenge. before your domain"
1756 _err "so the resulting subdomain will be: $txtdomain"
1757 continue
1758 fi
4c3b3608 1759
73b8b120 1760 (
8663fb7e 1761 if ! . $d_api ; then
73b8b120 1762 _err "Load file $d_api error. Please check your api file and try again."
1763 return 1
1764 fi
1765
158f22f7 1766 addcommand="${_currentRoot}_add"
d53289d7 1767 if ! _exists $addcommand ; then
73b8b120 1768 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
1769 return 1
1770 fi
1771
1772 if ! $addcommand $txtdomain $txt ; then
1773 _err "Error add txt for domain:$txtdomain"
1774 return 1
1775 fi
1776 )
4c3b3608 1777
8663fb7e 1778 if [ "$?" != "0" ] ; then
5ef501c5 1779 _clearup
4c3b3608 1780 return 1
1781 fi
1782 dnsadded='1'
1783 fi
1784 done
1785
8663fb7e 1786 if [ "$dnsadded" = '0' ] ; then
4d2f38b0 1787 _savedomainconf "Le_Vlist" "$vlist"
4c3b3608 1788 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
1789 _err "Please add the TXT records to the domains, and retry again."
5ef501c5 1790 _clearup
4c3b3608 1791 return 1
1792 fi
1793
1794 fi
1795
8663fb7e 1796 if [ "$dnsadded" = '1' ] ; then
0e38c60d 1797 if [ -z "$Le_DNSSleep" ] ; then
4a4dacb5 1798 Le_DNSSleep=$DEFAULT_DNS_SLEEP
0e38c60d 1799 else
1800 _savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
1801 fi
1802
1803 _info "Sleep $Le_DNSSleep seconds for the txt records to take effect"
1804 sleep $Le_DNSSleep
4c3b3608 1805 fi
1806
1807 _debug "ok, let's start to verify"
a63b05a9 1808
4c3b3608 1809 ventries=$(echo "$vlist" | tr ',' ' ' )
1810 for ventry in $ventries
1811 do
1812 d=$(echo $ventry | cut -d $sep -f 1)
1813 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
1814 uri=$(echo $ventry | cut -d $sep -f 3)
a63b05a9 1815 vtype=$(echo $ventry | cut -d $sep -f 4)
1816 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
ec603bee 1817
bd5e57d8 1818 if [ "$keyauthorization" = "$STATE_VERIFIED" ] ; then
ec603bee 1819 _info "$d is already verified, skip $vtype."
1820 continue
1821 fi
1822
4c3b3608 1823 _info "Verifying:$d"
1824 _debug "d" "$d"
1825 _debug "keyauthorization" "$keyauthorization"
1826 _debug "uri" "$uri"
1827 removelevel=""
e22bcf7c 1828 token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
a63b05a9 1829
1830 _debug "_currentRoot" "$_currentRoot"
1831
1832
8663fb7e 1833 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
1834 if [ "$_currentRoot" = "no" ] ; then
4c3b3608 1835 _info "Standalone mode server"
1836 _startserver "$keyauthorization" &
8663fb7e 1837 if [ "$?" != "0" ] ; then
5ef501c5 1838 _clearup
6fc1447f 1839 return 1
1840 fi
4c3b3608 1841 serverproc="$!"
1842 sleep 2
1843 _debug serverproc $serverproc
6fc1447f 1844
4c3b3608 1845 else
8663fb7e 1846 if [ "$_currentRoot" = "apache" ] ; then
6f930641 1847 wellknown_path="$ACME_DIR"
1848 else
a63b05a9 1849 wellknown_path="$_currentRoot/.well-known/acme-challenge"
8663fb7e 1850 if [ ! -d "$_currentRoot/.well-known" ] ; then
6f930641 1851 removelevel='1'
8663fb7e 1852 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ] ; then
6f930641 1853 removelevel='2'
1854 else
1855 removelevel='3'
1856 fi
4c3b3608 1857 fi
6f930641 1858
4c3b3608 1859 _debug wellknown_path "$wellknown_path"
6f930641 1860
4c3b3608 1861 _debug "writing token:$token to $wellknown_path/$token"
1862
1863 mkdir -p "$wellknown_path"
7939b419 1864 printf "%s" "$keyauthorization" > "$wellknown_path/$token"
8663fb7e 1865 if [ ! "$usingApache" ] ; then
32fdc196
TB
1866 if webroot_owner=$(_stat $_currentRoot) ; then
1867 _debug "Changing owner/group of .well-known to $webroot_owner"
1868 chown -R $webroot_owner "$_currentRoot/.well-known"
1869 else
1870 _debug "not chaning owner/group of webroot";
1871 fi
df886ffa 1872 fi
4c3b3608 1873
1874 fi
e22bcf7c 1875
1876 elif [ "$vtype" = "$VTYPE_TLS" ] ; then
1877 #create A
1878 #_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
1879 #_debug2 _hash_A "$_hash_A"
1880 #_x="$(echo $_hash_A | cut -c 1-32)"
1881 #_debug2 _x "$_x"
1882 #_y="$(echo $_hash_A | cut -c 33-64)"
1883 #_debug2 _y "$_y"
1884 #_SAN_A="$_x.$_y.token.acme.invalid"
1885 #_debug2 _SAN_A "$_SAN_A"
1886
1887 #create B
1888 _hash_B="$(printf "%s" $keyauthorization | _digest "sha256" "hex" )"
1889 _debug2 _hash_B "$_hash_B"
1890 _x="$(echo $_hash_B | cut -c 1-32)"
1891 _debug2 _x "$_x"
1892 _y="$(echo $_hash_B | cut -c 33-64)"
1893 _debug2 _y "$_y"
1894
1895 #_SAN_B="$_x.$_y.ka.acme.invalid"
1896
1897 _SAN_B="$_x.$_y.acme.invalid"
1898 _debug2 _SAN_B "$_SAN_B"
1899
1900 if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" ; then
1901 _err "Start tls server error."
1902 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1903 _clearup
1904 return 1
1905 fi
4c3b3608 1906 fi
1907
c4d8fd83 1908 if ! _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" ; then
1909 _err "$d:Can not get challenge: $response"
1910 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1911 _clearup
1912 return 1
1913 fi
4c3b3608 1914
8663fb7e 1915 if [ ! -z "$code" ] && [ ! "$code" = '202' ] ; then
c60883ef 1916 _err "$d:Challenge error: $response"
a63b05a9 1917 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1918 _clearup
1919 return 1
1920 fi
1921
6fc1447f 1922 waittimes=0
8663fb7e 1923 if [ -z "$MAX_RETRY_TIMES" ] ; then
6fc1447f 1924 MAX_RETRY_TIMES=30
1925 fi
1926
2ee5d873 1927 while true ; do
00a50605 1928 waittimes=$(_math $waittimes + 1)
8663fb7e 1929 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ] ; then
6fc1447f 1930 _err "$d:Timeout"
1931 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1932 _clearup
1933 return 1
1934 fi
1935
4c3b3608 1936 _debug "sleep 5 secs to verify"
1937 sleep 5
1938 _debug "checking"
9aaf36cd 1939 response="$(_get $uri)"
8663fb7e 1940 if [ "$?" != "0" ] ; then
c60883ef 1941 _err "$d:Verify error:$response"
a63b05a9 1942 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1943 _clearup
1944 return 1
1945 fi
9aaf36cd 1946 _debug2 original "$response"
1947
1948 response="$(echo "$response" | _normalizeJson )"
7012b91f 1949 _debug2 response "$response"
4c3b3608 1950
22ea4004 1951 status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
8663fb7e 1952 if [ "$status" = "valid" ] ; then
4c3b3608 1953 _info "Success"
1954 _stopserver $serverproc
1955 serverproc=""
a63b05a9 1956 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1957 break;
1958 fi
1959
8663fb7e 1960 if [ "$status" = "invalid" ] ; then
22ea4004 1961 error="$(echo "$response" | _egrep_o '"error":\{[^}]*}')"
b7ec6789 1962 _debug2 error "$error"
22ea4004 1963 errordetail="$(echo $error | _egrep_o '"detail": *"[^"]*"' | cut -d '"' -f 4)"
b7ec6789 1964 _debug2 errordetail "$errordetail"
1965 if [ "$errordetail" ] ; then
1966 _err "$d:Verify error:$errordetail"
1967 else
1968 _err "$d:Verify error:$error"
1969 fi
dcf9cb58 1970 if [ "$DEBUG" ] ; then
1971 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
1972 _get "http://$d/.well-known/acme-challenge/$token"
1973 fi
1974 fi
a63b05a9 1975 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1976 _clearup
1977 return 1;
1978 fi
1979
8663fb7e 1980 if [ "$status" = "pending" ] ; then
4c3b3608 1981 _info "Pending"
1982 else
1983 _err "$d:Verify error:$response"
a63b05a9 1984 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1985 _clearup
1986 return 1
1987 fi
1988
1989 done
1990
1991 done
1992
1993 _clearup
1994 _info "Verify finished, start to sign."
fa8311dc 1995 der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)"
c4d8fd83 1996
1997 if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" ; then
1998 _err "Sign failed."
1999 return 1
2000 fi
4c3b3608 2001
2002
22ea4004 2003 Le_LinkCert="$(grep -i '^Location.*$' $HTTP_HEADER | head -1 | tr -d "\r\n" | cut -d " " -f 2)"
4d2f38b0 2004 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
4c3b3608 2005
8663fb7e 2006 if [ "$Le_LinkCert" ] ; then
88fab7d6 2007 echo "$BEGIN_CERT" > "$CERT_PATH"
c60883ef 2008 _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH"
88fab7d6 2009 echo "$END_CERT" >> "$CERT_PATH"
43822d37 2010 _info "$(__green "Cert success.")"
4c3b3608 2011 cat "$CERT_PATH"
2012
66f08eb2 2013 _info "Your cert is in $( __green " $CERT_PATH ")"
2014 _info "Your cert key is in $( __green " $CERT_KEY_PATH ")"
caf1fc10 2015 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
281aa349 2016
8663fb7e 2017 if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ] ; then
281aa349 2018 USER_PATH="$PATH"
2019 _saveaccountconf "USER_PATH" "$USER_PATH"
2020 fi
4c3b3608 2021 fi
2022
2023
8663fb7e 2024 if [ -z "$Le_LinkCert" ] ; then
eae29099 2025 response="$(echo $response | _dbase64 "multiline" | _normalizeJson )"
22ea4004 2026 _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
4c3b3608 2027 return 1
2028 fi
2029
4d2f38b0 2030 _cleardomainconf "Le_Vlist"
4c3b3608 2031
bbbdcb09 2032 Le_LinkIssuer=$(grep -i '^Link' $HTTP_HEADER | head -1 | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
fac1e367 2033 if ! _contains "$Le_LinkIssuer" ":" ; then
2034 Le_LinkIssuer="$API$Le_LinkIssuer"
2035 fi
2036
4d2f38b0 2037 _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
4c3b3608 2038
8663fb7e 2039 if [ "$Le_LinkIssuer" ] ; then
88fab7d6 2040 echo "$BEGIN_CERT" > "$CA_CERT_PATH"
c60883ef 2041 _get "$Le_LinkIssuer" | _base64 "multiline" >> "$CA_CERT_PATH"
88fab7d6 2042 echo "$END_CERT" >> "$CA_CERT_PATH"
66f08eb2 2043 _info "The intermediate CA cert is in $( __green " $CA_CERT_PATH ")"
caf1fc10 2044 cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
66f08eb2 2045 _info "And the full chain certs is there: $( __green " $CERT_FULLCHAIN_PATH ")"
4c3b3608 2046 fi
2047
2048 Le_CertCreateTime=$(date -u "+%s")
4d2f38b0 2049 _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
4c3b3608 2050
2051 Le_CertCreateTimeStr=$(date -u )
4d2f38b0 2052 _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
4c3b3608 2053
523c7682 2054 if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ] ; then
2055 Le_RenewalDays=$MAX_RENEW
054cb72e 2056 else
2057 _savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
13d7cae9 2058 fi
2059
fac1e367 2060 if [ "$HTTPS_INSECURE" ] ; then
2061 _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE"
2062 else
2063 _clearaccountconf "HTTPS_INSECURE"
13d7cae9 2064 fi
00a50605 2065
2066 Le_NextRenewTime=$(_math $Le_CertCreateTime + $Le_RenewalDays \* 24 \* 60 \* 60)
4d2f38b0 2067 _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
4c3b3608 2068
2069 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
4d2f38b0 2070 _savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
4c3b3608 2071
2072
4c0d3f1b 2073 if [ "$Le_RealCertPath$Le_RealKeyPath$Le_RealCACertPath$Le_ReloadCmd$Le_RealFullChainPath" ] ; then
43822d37 2074 _installcert
01f54558 2075 fi
4c0d3f1b 2076
4c3b3608 2077}
2078
43822d37 2079#domain [isEcc]
4c3b3608 2080renew() {
2081 Le_Domain="$1"
8663fb7e 2082 if [ -z "$Le_Domain" ] ; then
43822d37 2083 _usage "Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
4c3b3608 2084 return 1
2085 fi
2086
43822d37 2087 _isEcc="$2"
2088
2089 _initpath $Le_Domain "$_isEcc"
2090
2091 _info "Renew: '$Le_Domain'"
8663fb7e 2092 if [ ! -f "$DOMAIN_CONF" ] ; then
43822d37 2093 _info "'$Le_Domain' is not a issued domain, skip."
4c3b3608 2094 return 0;
2095 fi
2096
1e6b68f5 2097 if [ "$Le_RenewalDays" ] ; then
2098 _savedomainconf Le_RenewalDays "$Le_RenewalDays"
2099 fi
2100
8663fb7e 2101 . "$DOMAIN_CONF"
2102 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
4c3b3608 2103 _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
cc179731 2104 return $RENEW_SKIP
4c3b3608 2105 fi
2106
2107 IS_RENEW="1"
a63b05a9 2108 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
22ea4004 2109 res=$?
4c3b3608 2110 IS_RENEW=""
2111
2112 return $res
2113}
2114
cc179731 2115#renewAll [stopRenewOnError]
4c3b3608 2116renewAll() {
2117 _initpath
cc179731 2118 _stopRenewOnError="$1"
2119 _debug "_stopRenewOnError" "$_stopRenewOnError"
2120 _ret="0"
43822d37 2121
b2817897 2122 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
4c3b3608 2123 d=$(echo $d | cut -d '/' -f 1)
43822d37 2124 (
2125 if _endswith $d "$ECC_SUFFIX" ; then
2126 _isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
2127 d=$(echo $d | cut -d "$ECC_SEP" -f 1)
2128 fi
2129 renew "$d" "$_isEcc"
4d2f38b0 2130 )
cc179731 2131 rc="$?"
2132 _debug "Return code: $rc"
2133 if [ "$rc" != "0" ] ; then
2134 if [ "$rc" = "$RENEW_SKIP" ] ; then
2135 _info "Skipped $d"
2136 elif [ "$_stopRenewOnError" ] ; then
2137 _err "Error renew $d, stop now."
2138 return $rc
2139 else
2140 _ret="$rc"
2141 _err "Error renew $d, Go ahead to next one."
2142 fi
2143 fi
4c3b3608 2144 done
cc179731 2145 return $_ret
4c3b3608 2146}
2147
dcf4f8f6 2148
6d7eda3e 2149list() {
22ea4004 2150 _raw="$1"
6d7eda3e 2151 _initpath
dcf4f8f6 2152
2153 _sep="|"
2154 if [ "$_raw" ] ; then
43822d37 2155 printf "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew\n"
dcf4f8f6 2156 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
2157 d=$(echo $d | cut -d '/' -f 1)
2158 (
43822d37 2159 if _endswith $d "$ECC_SUFFIX" ; then
2160 _isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
2161 d=$(echo $d | cut -d "$ECC_SEP" -f 1)
2162 fi
2163 _initpath $d "$_isEcc"
dcf4f8f6 2164 if [ -f "$DOMAIN_CONF" ] ; then
2165 . "$DOMAIN_CONF"
43822d37 2166 printf "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr\n"
dcf4f8f6 2167 fi
2168 )
2169 done
2170 else
22ea4004 2171 if _exists column ; then
2172 list "raw" | column -t -s "$_sep"
2173 else
43822d37 2174 list "raw" | tr "$_sep" '\t'
22ea4004 2175 fi
dcf4f8f6 2176 fi
6d7eda3e 2177
2178
2179}
2180
4c3b3608 2181installcert() {
2182 Le_Domain="$1"
8663fb7e 2183 if [ -z "$Le_Domain" ] ; then
43822d37 2184 _usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
4c3b3608 2185 return 1
2186 fi
2187
2188 Le_RealCertPath="$2"
2189 Le_RealKeyPath="$3"
2190 Le_RealCACertPath="$4"
2191 Le_ReloadCmd="$5"
a63b05a9 2192 Le_RealFullChainPath="$6"
43822d37 2193 _isEcc="$7"
2194
2195 _initpath $Le_Domain "$_isEcc"
2196 if [ ! -d "$DOMAIN_PATH" ] ; then
2197 _err "Domain is not valid:'$Le_Domain'"
2198 return 1
2199 fi
2200
2201 _installcert
2202}
4c3b3608 2203
43822d37 2204
2205_installcert() {
4c3b3608 2206
4d2f38b0 2207 _savedomainconf "Le_RealCertPath" "$Le_RealCertPath"
2208 _savedomainconf "Le_RealCACertPath" "$Le_RealCACertPath"
2209 _savedomainconf "Le_RealKeyPath" "$Le_RealKeyPath"
2210 _savedomainconf "Le_ReloadCmd" "$Le_ReloadCmd"
2211 _savedomainconf "Le_RealFullChainPath" "$Le_RealFullChainPath"
4c3b3608 2212
4d2f38b0 2213 if [ "$Le_RealCertPath" = "no" ] ; then
2214 Le_RealCertPath=""
2215 fi
2216 if [ "$Le_RealKeyPath" = "no" ] ; then
2217 Le_RealKeyPath=""
2218 fi
2219 if [ "$Le_RealCACertPath" = "no" ] ; then
2220 Le_RealCACertPath=""
2221 fi
2222 if [ "$Le_ReloadCmd" = "no" ] ; then
2223 Le_ReloadCmd=""
2224 fi
2225 if [ "$Le_RealFullChainPath" = "no" ] ; then
2226 Le_RealFullChainPath=""
2227 fi
2228
2229 _installed="0"
8663fb7e 2230 if [ "$Le_RealCertPath" ] ; then
4d2f38b0 2231 _installed=1
2232 _info "Installing cert to:$Le_RealCertPath"
43822d37 2233 if [ -f "$Le_RealCertPath" ] && [ ! "$IS_RENEW" ] ; then
ff3bce32 2234 cp "$Le_RealCertPath" "$Le_RealCertPath".bak
4c3b3608 2235 fi
2236 cat "$CERT_PATH" > "$Le_RealCertPath"
2237 fi
2238
8663fb7e 2239 if [ "$Le_RealCACertPath" ] ; then
4d2f38b0 2240 _installed=1
2241 _info "Installing CA to:$Le_RealCACertPath"
8663fb7e 2242 if [ "$Le_RealCACertPath" = "$Le_RealCertPath" ] ; then
4c3b3608 2243 echo "" >> "$Le_RealCACertPath"
2244 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
2245 else
43822d37 2246 if [ -f "$Le_RealCACertPath" ] && [ ! "$IS_RENEW" ] ; then
ff3bce32 2247 cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak
78552b18 2248 fi
4c3b3608 2249 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
2250 fi
2251 fi
2252
2253
8663fb7e 2254 if [ "$Le_RealKeyPath" ] ; then
4d2f38b0 2255 _installed=1
2256 _info "Installing key to:$Le_RealKeyPath"
43822d37 2257 if [ -f "$Le_RealKeyPath" ] && [ ! "$IS_RENEW" ] ; then
ff3bce32 2258 cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
4c3b3608 2259 fi
2260 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
2261 fi
a63b05a9 2262
8663fb7e 2263 if [ "$Le_RealFullChainPath" ] ; then
4d2f38b0 2264 _installed=1
2265 _info "Installing full chain to:$Le_RealFullChainPath"
43822d37 2266 if [ -f "$Le_RealFullChainPath" ] && [ ! "$IS_RENEW" ] ; then
ff3bce32 2267 cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
a63b05a9 2268 fi
2269 cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
2270 fi
4c3b3608 2271
8663fb7e 2272 if [ "$Le_ReloadCmd" ] ; then
4d2f38b0 2273 _installed=1
4c3b3608 2274 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
4d2f38b0 2275 if (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd") ; then
43822d37 2276 _info "$(__green "Reload success")"
4d2f38b0 2277 else
2278 _err "Reload error for :$Le_Domain"
2279 fi
2280 fi
2281
4c3b3608 2282
2283}
2284
2285installcronjob() {
2286 _initpath
77546ea5 2287 if ! _exists "crontab" ; then
2288 _err "crontab doesn't exist, so, we can not install cron jobs."
2289 _err "All your certs will not be renewed automatically."
a7b7355d 2290 _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
77546ea5 2291 return 1
2292 fi
2293
4c3b3608 2294 _info "Installing cron job"
a7b7355d 2295 if ! crontab -l | grep "$PROJECT_ENTRY --cron" ; then
8663fb7e 2296 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ] ; then
a7b7355d 2297 lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
4c3b3608 2298 else
a7b7355d 2299 _err "Can not install cronjob, $PROJECT_ENTRY not found."
4c3b3608 2300 return 1
2301 fi
22ea4004 2302 if _exists uname && uname -a | grep solaris >/dev/null ; then
2303 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab --
2304 else
2305 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab -
2306 fi
4c3b3608 2307 fi
8663fb7e 2308 if [ "$?" != "0" ] ; then
4c3b3608 2309 _err "Install cron job failed. You need to manually renew your certs."
2310 _err "Or you can add cronjob by yourself:"
a7b7355d 2311 _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
4c3b3608 2312 return 1
2313 fi
2314}
2315
2316uninstallcronjob() {
37db5b81 2317 if ! _exists "crontab" ; then
2318 return
2319 fi
4c3b3608 2320 _info "Removing cron job"
a7b7355d 2321 cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")"
8663fb7e 2322 if [ "$cr" ] ; then
22ea4004 2323 if _exists uname && uname -a | grep solaris >/dev/null ; then
2324 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab --
2325 else
2326 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -
2327 fi
a7b7355d 2328 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
4c3b3608 2329 _info LE_WORKING_DIR "$LE_WORKING_DIR"
2330 fi
2331 _initpath
a7b7355d 2332
4c3b3608 2333}
2334
6cb415f5 2335revoke() {
2336 Le_Domain="$1"
8663fb7e 2337 if [ -z "$Le_Domain" ] ; then
43822d37 2338 _usage "Usage: $PROJECT_ENTRY --revoke -d domain.com"
6cb415f5 2339 return 1
2340 fi
2341
43822d37 2342 _isEcc="$2"
2343
2344 _initpath $Le_Domain "$_isEcc"
8663fb7e 2345 if [ ! -f "$DOMAIN_CONF" ] ; then
6cb415f5 2346 _err "$Le_Domain is not a issued domain, skip."
2347 return 1;
2348 fi
2349
8663fb7e 2350 if [ ! -f "$CERT_PATH" ] ; then
6cb415f5 2351 _err "Cert for $Le_Domain $CERT_PATH is not found, skip."
2352 return 1
2353 fi
2354
2355 cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"| tr -d "\r\n" | _urlencode)"
2356
8663fb7e 2357 if [ -z "$cert" ] ; then
6cb415f5 2358 _err "Cert for $Le_Domain is empty found, skip."
2359 return 1
2360 fi
2361
2362 data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
2363 uri="$API/acme/revoke-cert"
2364
2365 _info "Try domain key first."
2366 if _send_signed_request $uri "$data" "" "$CERT_KEY_PATH"; then
8663fb7e 2367 if [ -z "$response" ] ; then
6cb415f5 2368 _info "Revoke success."
2369 rm -f $CERT_PATH
2370 return 0
2371 else
2372 _err "Revoke error by domain key."
c9c31c04 2373 _err "$response"
6cb415f5 2374 fi
2375 fi
2376
2377 _info "Then try account key."
2378
2379 if _send_signed_request $uri "$data" "" "$ACCOUNT_KEY_PATH" ; then
8663fb7e 2380 if [ -z "$response" ] ; then
6cb415f5 2381 _info "Revoke success."
2382 rm -f $CERT_PATH
2383 return 0
2384 else
2385 _err "Revoke error."
c9c31c04 2386 _debug "$response"
6cb415f5 2387 fi
2388 fi
2389 return 1
2390}
4c3b3608 2391
2392# Detect profile file if not specified as environment variable
2393_detect_profile() {
a63b05a9 2394 if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then
4c3b3608 2395 echo "$PROFILE"
2396 return
2397 fi
2398
4c3b3608 2399 DETECTED_PROFILE=''
4c3b3608 2400 SHELLTYPE="$(basename "/$SHELL")"
2401
8663fb7e 2402 if [ "$SHELLTYPE" = "bash" ] ; then
2403 if [ -f "$HOME/.bashrc" ] ; then
4c3b3608 2404 DETECTED_PROFILE="$HOME/.bashrc"
8663fb7e 2405 elif [ -f "$HOME/.bash_profile" ] ; then
4c3b3608 2406 DETECTED_PROFILE="$HOME/.bash_profile"
2407 fi
8663fb7e 2408 elif [ "$SHELLTYPE" = "zsh" ] ; then
4c3b3608 2409 DETECTED_PROFILE="$HOME/.zshrc"
2410 fi
2411
8663fb7e 2412 if [ -z "$DETECTED_PROFILE" ] ; then
2413 if [ -f "$HOME/.profile" ] ; then
4c3b3608 2414 DETECTED_PROFILE="$HOME/.profile"
8663fb7e 2415 elif [ -f "$HOME/.bashrc" ] ; then
4c3b3608 2416 DETECTED_PROFILE="$HOME/.bashrc"
8663fb7e 2417 elif [ -f "$HOME/.bash_profile" ] ; then
4c3b3608 2418 DETECTED_PROFILE="$HOME/.bash_profile"
8663fb7e 2419 elif [ -f "$HOME/.zshrc" ] ; then
4c3b3608 2420 DETECTED_PROFILE="$HOME/.zshrc"
2421 fi
2422 fi
2423
8663fb7e 2424 if [ ! -z "$DETECTED_PROFILE" ] ; then
4c3b3608 2425 echo "$DETECTED_PROFILE"
2426 fi
2427}
2428
2429_initconf() {
2430 _initpath
8663fb7e 2431 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
d53289d7 2432 echo "#ACCOUNT_CONF_PATH=xxxx
2433
2434#Account configurations:
4c3b3608 2435#Here are the supported macros, uncomment them to make them take effect.
d53289d7 2436
4c3b3608 2437#ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
5fd3f21b 2438#ACCOUNT_KEY_PATH=\"/path/to/account.key\"
b2817897 2439#CERT_HOME=\"/path/to/cert/home\"
4c3b3608 2440
2441#STAGE=1 # Use the staging api
2442#FORCE=1 # Force to issue cert
2443#DEBUG=1 # Debug mode
2444
166096dc 2445#ACCOUNT_KEY_HASH=account key hash
2446
8814a348 2447#USER_AGENT=\"$USER_AGENT\"
281aa349 2448
2449#USER_PATH=""
2450
4c3b3608 2451#dns api
2452#######################
2453#Cloudflare:
2454#api key
3d49985a 2455#CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
4c3b3608 2456#account email
3d49985a 2457#CF_Email=\"xxxx@sss.com\"
4c3b3608 2458
2459#######################
2460#Dnspod.cn:
2461#api key id
3d49985a 2462#DP_Id=\"1234\"
4c3b3608 2463#api key
3d49985a 2464#DP_Key=\"sADDsdasdgdsf\"
4c3b3608 2465
2466#######################
2467#Cloudxns.com:
3d49985a 2468#CX_Key=\"1234\"
4c3b3608 2469#
3d49985a 2470#CX_Secret=\"sADDsdasdgdsf\"
30de13b4 2471
2472#######################
2473#Godaddy.com:
2474#GD_Key=\"sdfdsgdgdfdasfds\"
2475#
2476#GD_Secret=\"sADDsdasdfsdfdssdgdsf\"
4c3b3608 2477
2478 " > $ACCOUNT_CONF_PATH
2479 fi
2480}
2481
c8e9a31e 2482# nocron
c60883ef 2483_precheck() {
c8e9a31e 2484 _nocron="$1"
2485
c60883ef 2486 if ! _exists "curl" && ! _exists "wget"; then
2487 _err "Please install curl or wget first, we need to access http resources."
4c3b3608 2488 return 1
2489 fi
2490
c8e9a31e 2491 if [ -z "$_nocron" ] ; then
2492 if ! _exists "crontab" ; then
2493 _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
2494 _err "We need to set cron job to renew the certs automatically."
2495 _err "Otherwise, your certs will not be able to be renewed automatically."
2496 if [ -z "$FORCE" ] ; then
2497 _err "Please add '--force' and try install again to go without crontab."
2498 _err "./$PROJECT_ENTRY --install --force"
2499 return 1
2500 fi
77546ea5 2501 fi
4c3b3608 2502 fi
2503
c60883ef 2504 if ! _exists "openssl" ; then
2505 _err "Please install openssl first."
2506 _err "We need openssl to generate keys."
4c3b3608 2507 return 1
2508 fi
2509
c60883ef 2510 if ! _exists "nc" ; then
2511 _err "It is recommended to install nc first, try to install 'nc' or 'netcat'."
2512 _err "We use nc for standalone server if you use standalone mode."
2513 _err "If you don't use standalone mode, just ignore this warning."
2514 fi
2515
2516 return 0
2517}
2518
0a7c9364 2519_setShebang() {
2520 _file="$1"
2521 _shebang="$2"
2522 if [ -z "$_shebang" ] ; then
43822d37 2523 _usage "Usage: file shebang"
0a7c9364 2524 return 1
2525 fi
2526 cp "$_file" "$_file.tmp"
2527 echo "$_shebang" > "$_file"
2528 sed -n 2,99999p "$_file.tmp" >> "$_file"
2529 rm -f "$_file.tmp"
2530}
2531
94dc5f33 2532_installalias() {
2533 _initpath
2534
2535 _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
2536 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ] ; then
2537 echo "$(cat $_envfile)" | sed "s|^LE_WORKING_DIR.*$||" > "$_envfile"
2538 echo "$(cat $_envfile)" | sed "s|^alias le.*$||" > "$_envfile"
2539 echo "$(cat $_envfile)" | sed "s|^alias le.sh.*$||" > "$_envfile"
2540 fi
2541
1786a5e5 2542 _setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
94dc5f33 2543 _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
2544
2545 _profile="$(_detect_profile)"
2546 if [ "$_profile" ] ; then
2547 _debug "Found profile: $_profile"
2548 _setopt "$_profile" ". \"$_envfile\""
2549 _info "OK, Close and reopen your terminal to start using $PROJECT_NAME"
2550 else
2551 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
2552 fi
2553
2554
2555 #for csh
2556 _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
94dc5f33 2557 _csh_profile="$HOME/.cshrc"
2558 if [ -f "$_csh_profile" ] ; then
6626371d 2559 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
2560 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
94dc5f33 2561 _setopt "$_csh_profile" "source \"$_cshfile\""
2562 fi
acafa585 2563
2564 #for tcsh
2565 _tcsh_profile="$HOME/.tcshrc"
2566 if [ -f "$_tcsh_profile" ] ; then
2567 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
2568 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
2569 _setopt "$_tcsh_profile" "source \"$_cshfile\""
2570 fi
94dc5f33 2571
2572}
2573
c8e9a31e 2574# nocron
c60883ef 2575install() {
c8e9a31e 2576 _nocron="$1"
c60883ef 2577 if ! _initpath ; then
2578 _err "Install failed."
4c3b3608 2579 return 1
2580 fi
52677b0a 2581 if [ "$_nocron" ] ; then
2582 _debug "Skip install cron job"
2583 fi
2584
c8e9a31e 2585 if ! _precheck "$_nocron" ; then
c60883ef 2586 _err "Pre-check failed, can not install."
4c3b3608 2587 return 1
2588 fi
c60883ef 2589
6cc11ffb 2590 #convert from le
8663fb7e 2591 if [ -d "$HOME/.le" ] ; then
6cc11ffb 2592 for envfile in "le.env" "le.sh.env"
2593 do
8663fb7e 2594 if [ -f "$HOME/.le/$envfile" ] ; then
6cc11ffb 2595 if grep "le.sh" "$HOME/.le/$envfile" >/dev/null ; then
2596 _upgrading="1"
2597 _info "You are upgrading from le.sh"
2598 _info "Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
2599 mv "$HOME/.le" "$LE_WORKING_DIR"
2600 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
2601 break;
2602 fi
2603 fi
2604 done
2605 fi
2606
4c3b3608 2607 _info "Installing to $LE_WORKING_DIR"
635695ec 2608
4a0f23e2 2609 if ! mkdir -p "$LE_WORKING_DIR" ; then
90035252 2610 _err "Can not create working dir: $LE_WORKING_DIR"
4a0f23e2 2611 return 1
2612 fi
2613
762978f8 2614 chmod 700 "$LE_WORKING_DIR"
2615
a7b7355d 2616 cp $PROJECT_ENTRY "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
4c3b3608 2617
8663fb7e 2618 if [ "$?" != "0" ] ; then
a7b7355d 2619 _err "Install failed, can not copy $PROJECT_ENTRY"
4c3b3608 2620 return 1
2621 fi
2622
a7b7355d 2623 _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
4c3b3608 2624
94dc5f33 2625 _installalias
4c3b3608 2626
8663fb7e 2627 if [ -d "dnsapi" ] ; then
6ed1c718 2628 mkdir -p $LE_WORKING_DIR/dnsapi
2629 cp dnsapi/* $LE_WORKING_DIR/dnsapi/
2630 fi
d53289d7 2631
8663fb7e 2632 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
4c3b3608 2633 _initconf
2634 fi
6cc11ffb 2635
8663fb7e 2636 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ] ; then
635695ec 2637 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
6cc11ffb 2638 fi
2639
8663fb7e 2640 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ] ; then
b2817897 2641 _saveaccountconf "CERT_HOME" "$CERT_HOME"
2642 fi
2643
8663fb7e 2644 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ] ; then
b2817897 2645 _saveaccountconf "ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
2646 fi
2647
c8e9a31e 2648 if [ -z "$_nocron" ] ; then
2649 installcronjob
2650 fi
0a7c9364 2651
641989fd 2652 if [ -z "$NO_DETECT_SH" ] ; then
2653 #Modify shebang
2654 if _exists bash ; then
2655 _info "Good, bash is installed, change the shebang to use bash as prefered."
2656 _shebang='#!/usr/bin/env bash'
2657 _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
2658 if [ -d "$LE_WORKING_DIR/dnsapi" ] ; then
2659 for _apifile in $(ls "$LE_WORKING_DIR/dnsapi/"*.sh) ; do
2660 _setShebang "$_apifile" "$_shebang"
2661 done
2662 fi
0a7c9364 2663 fi
2664 fi
2665
4c3b3608 2666 _info OK
2667}
2668
52677b0a 2669# nocron
4c3b3608 2670uninstall() {
52677b0a 2671 _nocron="$1"
2672 if [ -z "$_nocron" ] ; then
2673 uninstallcronjob
2674 fi
4c3b3608 2675 _initpath
2676
2677 _profile="$(_detect_profile)"
8663fb7e 2678 if [ "$_profile" ] ; then
7203a1c1 2679 text="$(cat $_profile)"
94dc5f33 2680 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" > "$_profile"
4c3b3608 2681 fi
2682
94dc5f33 2683 _csh_profile="$HOME/.cshrc"
2684 if [ -f "$_csh_profile" ] ; then
2685 text="$(cat $_csh_profile)"
2686 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_csh_profile"
2687 fi
2688
acafa585 2689 _tcsh_profile="$HOME/.tcshrc"
2690 if [ -f "$_tcsh_profile" ] ; then
2691 text="$(cat $_tcsh_profile)"
2692 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_tcsh_profile"
2693 fi
2694
a7b7355d 2695 rm -f $LE_WORKING_DIR/$PROJECT_ENTRY
4c3b3608 2696 _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
2697
2698}
2699
2700cron() {
281aa349 2701 IN_CRON=1
4c3b3608 2702 renewAll
cc179731 2703 _ret="$?"
281aa349 2704 IN_CRON=""
cc179731 2705 return $_ret
4c3b3608 2706}
2707
2708version() {
a63b05a9 2709 echo "$PROJECT"
2710 echo "v$VER"
4c3b3608 2711}
2712
2713showhelp() {
2714 version
a7b7355d 2715 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
a63b05a9 2716Commands:
2717 --help, -h Show this help message.
2718 --version, -v Show version info.
a7b7355d 2719 --install Install $PROJECT_NAME to your system.
2720 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
52677b0a 2721 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT
a63b05a9 2722 --issue Issue a cert.
2723 --installcert Install the issued cert to apache/nginx or any other server.
2724 --renew, -r Renew a cert.
2725 --renewAll Renew all the certs
2726 --revoke Revoke a cert.
6d7eda3e 2727 --list List all the certs
a63b05a9 2728 --installcronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
2729 --uninstallcronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
2730 --cron Run cron job to renew all the certs.
2731 --toPkcs Export the certificate and key to a pfx file.
2732 --createAccountKey, -cak Create an account private key, professional use.
2733 --createDomainKey, -cdk Create an domain private key, professional use.
2734 --createCSR, -ccsr Create CSR , professional use.
2735
2736Parameters:
2737 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
2738 --force, -f Used to force to install or force to renew a cert immediately.
2739 --staging, --test Use staging server, just for test.
2740 --debug Output debug info.
2741
2742 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
2743 --standalone Use standalone mode.
e22bcf7c 2744 --tls Use standalone tls mode.
a63b05a9 2745 --apache Use apache mode.
eccec5f6 2746 --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
4a4dacb5 2747 --dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds.
a63b05a9 2748
2749 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384.
2750 --accountkeylength, -ak [2048] Specifies the account key length.
2751
2752 These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
2753
2754 --certpath /path/to/real/cert/file After issue/renew, the cert will be copied to this path.
2755 --keypath /path/to/real/key/file After issue/renew, the key will be copied to this path.
2756 --capath /path/to/real/ca/file After issue/renew, the intermediate cert will be copied to this path.
2757 --fullchainpath /path/to/fullchain/file After issue/renew, the fullchain cert will be copied to this path.
2758
2759 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
2760
2761 --accountconf Specifies a customized account config file.
635695ec 2762 --home Specifies the home dir for $PROJECT_NAME .
39c8f79f 2763 --certhome Specifies the home dir to save all the certs, only valid for '--install' command.
635695ec 2764 --useragent Specifies the user agent string. it will be saved for future use too.
b5eb4b90 2765 --accountemail Specifies the account email for registering, Only valid for the '--install' command.
06625071 2766 --accountkey Specifies the account key path, Only valid for the '--install' command.
523c7682 2767 --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
39c8f79f 2768 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
e22bcf7c 2769 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
dcf4f8f6 2770 --listraw Only used for '--list' command, list the certs in raw format.
c8e9a31e 2771 --stopRenewOnError, -se Only valid for '--renewall' command. Stop if one cert has error in renewal.
13d7cae9 2772 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
bc96082f 2773 --nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically.
43822d37 2774 --ecc Specifies to use the ECC cert. Valid for '--installcert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
4c3b3608 2775 "
2776}
2777
52677b0a 2778# nocron
4a0f23e2 2779_installOnline() {
2780 _info "Installing from online archive."
52677b0a 2781 _nocron="$1"
8663fb7e 2782 if [ ! "$BRANCH" ] ; then
4a0f23e2 2783 BRANCH="master"
2784 fi
a8df88ab 2785
4a0f23e2 2786 target="$PROJECT/archive/$BRANCH.tar.gz"
2787 _info "Downloading $target"
2788 localname="$BRANCH.tar.gz"
2789 if ! _get "$target" > $localname ; then
df9547ae 2790 _err "Download error."
4a0f23e2 2791 return 1
2792 fi
0bbe6eef 2793 (
4a0f23e2 2794 _info "Extracting $localname"
2795 tar xzf $localname
0bbe6eef 2796
6cc11ffb 2797 cd "$PROJECT_NAME-$BRANCH"
a7b7355d 2798 chmod +x $PROJECT_ENTRY
52677b0a 2799 if ./$PROJECT_ENTRY install "$_nocron" ; then
4a0f23e2 2800 _info "Install success!"
2801 fi
2802
2803 cd ..
0bbe6eef 2804
6cc11ffb 2805 rm -rf "$PROJECT_NAME-$BRANCH"
4a0f23e2 2806 rm -f "$localname"
0bbe6eef 2807 )
4a0f23e2 2808}
2809
52677b0a 2810upgrade() {
2811 if (
d0b748a4 2812 cd "$LE_WORKING_DIR"
52677b0a 2813 _installOnline "nocron"
2814 ) ; then
2815 _info "Upgrade success!"
096d8992 2816 exit 0
52677b0a 2817 else
2818 _err "Upgrade failed!"
096d8992 2819 exit 1
52677b0a 2820 fi
2821}
a63b05a9 2822
2823_process() {
2824 _CMD=""
2825 _domain=""
2826 _altdomains="no"
2827 _webroot=""
bdbf323f 2828 _keylength=""
2829 _accountkeylength=""
2830 _certpath=""
2831 _keypath=""
2832 _capath=""
2833 _fullchainpath=""
4d2f38b0 2834 _reloadcmd=""
a63b05a9 2835 _password=""
635695ec 2836 _accountconf=""
2837 _useragent=""
b5eb4b90 2838 _accountemail=""
2839 _accountkey=""
b2817897 2840 _certhome=""
39c8f79f 2841 _httpport=""
e22bcf7c 2842 _tlsport=""
0e38c60d 2843 _dnssleep=""
dcf4f8f6 2844 _listraw=""
cc179731 2845 _stopRenewOnError=""
13d7cae9 2846 _insecure=""
c8e9a31e 2847 _nocron=""
43822d37 2848 _ecc=""
8663fb7e 2849 while [ ${#} -gt 0 ] ; do
a63b05a9 2850 case "${1}" in
2851
2852 --help|-h)
2853 showhelp
2854 return
2855 ;;
2856 --version|-v)
2857 version
2858 return
2859 ;;
2860 --install)
2861 _CMD="install"
2862 ;;
2863 --uninstall)
2864 _CMD="uninstall"
2865 ;;
52677b0a 2866 --upgrade)
2867 _CMD="upgrade"
2868 ;;
a63b05a9 2869 --issue)
2870 _CMD="issue"
2871 ;;
2872 --installcert|-i)
2873 _CMD="installcert"
2874 ;;
2875 --renew|-r)
2876 _CMD="renew"
2877 ;;
4d2f38b0 2878 --renewAll|--renewall)
a63b05a9 2879 _CMD="renewAll"
2880 ;;
2881 --revoke)
2882 _CMD="revoke"
2883 ;;
6d7eda3e 2884 --list)
2885 _CMD="list"
2886 ;;
a63b05a9 2887 --installcronjob)
2888 _CMD="installcronjob"
2889 ;;
2890 --uninstallcronjob)
2891 _CMD="uninstallcronjob"
2892 ;;
2893 --cron)
2894 _CMD="cron"
2895 ;;
2896 --toPkcs)
2897 _CMD="toPkcs"
2898 ;;
2899 --createAccountKey|--createaccountkey|-cak)
2900 _CMD="createAccountKey"
2901 ;;
2902 --createDomainKey|--createdomainkey|-cdk)
2903 _CMD="createDomainKey"
2904 ;;
2905 --createCSR|--createcsr|-ccr)
2906 _CMD="createCSR"
2907 ;;
2908
2909
2910 --domain|-d)
2911 _dvalue="$2"
2912
ee1737a5 2913 if [ "$_dvalue" ] ; then
2914 if _startswith "$_dvalue" "-" ; then
2915 _err "'$_dvalue' is not a valid domain for parameter '$1'"
2916 return 1
2917 fi
2918
2919 if [ -z "$_domain" ] ; then
2920 _domain="$_dvalue"
a63b05a9 2921 else
ee1737a5 2922 if [ "$_altdomains" = "no" ] ; then
2923 _altdomains="$_dvalue"
2924 else
2925 _altdomains="$_altdomains,$_dvalue"
2926 fi
a63b05a9 2927 fi
2928 fi
ee1737a5 2929
a63b05a9 2930 shift
2931 ;;
2932
2933 --force|-f)
2934 FORCE="1"
2935 ;;
2936 --staging|--test)
2937 STAGE="1"
2938 ;;
2939 --debug)
8663fb7e 2940 if [ -z "$2" ] || _startswith "$2" "-" ; then
a63b05a9 2941 DEBUG="1"
2942 else
2943 DEBUG="$2"
2944 shift
6fc1447f 2945 fi
a63b05a9 2946 ;;
a63b05a9 2947 --webroot|-w)
2948 wvalue="$2"
8663fb7e 2949 if [ -z "$_webroot" ] ; then
a63b05a9 2950 _webroot="$wvalue"
2951 else
2952 _webroot="$_webroot,$wvalue"
2953 fi
2954 shift
2955 ;;
2956 --standalone)
2957 wvalue="no"
8663fb7e 2958 if [ -z "$_webroot" ] ; then
a63b05a9 2959 _webroot="$wvalue"
2960 else
2961 _webroot="$_webroot,$wvalue"
2962 fi
2963 ;;
2964 --apache)
2965 wvalue="apache"
8663fb7e 2966 if [ -z "$_webroot" ] ; then
a63b05a9 2967 _webroot="$wvalue"
2968 else
2969 _webroot="$_webroot,$wvalue"
2970 fi
2971 ;;
e22bcf7c 2972 --tls)
2973 wvalue="$W_TLS"
2974 if [ -z "$_webroot" ] ; then
2975 _webroot="$wvalue"
2976 else
2977 _webroot="$_webroot,$wvalue"
2978 fi
2979 ;;
a63b05a9 2980 --dns)
2981 wvalue="dns"
dceb3aca 2982 if ! _startswith "$2" "-" ; then
a63b05a9 2983 wvalue="$2"
2984 shift
2985 fi
8663fb7e 2986 if [ -z "$_webroot" ] ; then
a63b05a9 2987 _webroot="$wvalue"
2988 else
2989 _webroot="$_webroot,$wvalue"
2990 fi
2991 ;;
0e38c60d 2992 --dnssleep)
2993 _dnssleep="$2"
2994 Le_DNSSleep="$_dnssleep"
2995 shift
2996 ;;
2997
a63b05a9 2998 --keylength|-k)
2999 _keylength="$2"
2ce87fe2 3000 if [ "$_accountkeylength" = "no" ] ; then
3001 _accountkeylength="$2"
3002 fi
a63b05a9 3003 shift
3004 ;;
3005 --accountkeylength|-ak)
2ce87fe2 3006 _accountkeylength="$2"
a63b05a9 3007 shift
3008 ;;
3009
3010 --certpath)
3011 _certpath="$2"
3012 shift
3013 ;;
3014 --keypath)
3015 _keypath="$2"
3016 shift
3017 ;;
3018 --capath)
3019 _capath="$2"
3020 shift
3021 ;;
3022 --fullchainpath)
3023 _fullchainpath="$2"
3024 shift
3025 ;;
635695ec 3026 --reloadcmd|--reloadCmd)
a63b05a9 3027 _reloadcmd="$2"
3028 shift
3029 ;;
3030 --password)
3031 _password="$2"
3032 shift
3033 ;;
3034 --accountconf)
635695ec 3035 _accountconf="$2"
3036 ACCOUNT_CONF_PATH="$_accountconf"
a7b7355d 3037 shift
a63b05a9 3038 ;;
a7b7355d 3039 --home)
a63b05a9 3040 LE_WORKING_DIR="$2"
a7b7355d 3041 shift
a63b05a9 3042 ;;
b2817897 3043 --certhome)
3044 _certhome="$2"
3045 CERT_HOME="$_certhome"
3046 shift
3047 ;;
635695ec 3048 --useragent)
3049 _useragent="$2"
3050 USER_AGENT="$_useragent"
3051 shift
3052 ;;
b5eb4b90 3053 --accountemail )
3054 _accountemail="$2"
3055 ACCOUNT_EMAIL="$_accountemail"
3056 shift
3057 ;;
3058 --accountkey )
3059 _accountkey="$2"
3060 ACCOUNT_KEY_PATH="$_accountkey"
3061 shift
3062 ;;
06625071 3063 --days )
3064 _days="$2"
3065 Le_RenewalDays="$_days"
3066 shift
3067 ;;
39c8f79f 3068 --httpport )
3069 _httpport="$2"
3070 Le_HTTPPort="$_httpport"
3071 shift
3072 ;;
e22bcf7c 3073 --tlsport )
3074 _tlsport="$2"
3075 Le_TLSPort="$_tlsport"
3076 shift
3077 ;;
3078
dcf4f8f6 3079 --listraw )
3080 _listraw="raw"
3081 ;;
cc179731 3082 --stopRenewOnError|--stoprenewonerror|-se )
3083 _stopRenewOnError="1"
3084 ;;
13d7cae9 3085 --insecure)
3086 _insecure="1"
fac1e367 3087 HTTPS_INSECURE="1"
13d7cae9 3088 ;;
c8e9a31e 3089 --nocron)
3090 _nocron="1"
3091 ;;
43822d37 3092 --ecc)
3093 _ecc="isEcc"
3094 ;;
3095
a63b05a9 3096 *)
3097 _err "Unknown parameter : $1"
3098 return 1
3099 ;;
3100 esac
3101
3102 shift 1
3103 done
3104
dcf9cb58 3105 if [ "$DEBUG" ] ; then
3106 version
3107 fi
a63b05a9 3108
3109 case "${_CMD}" in
c8e9a31e 3110 install) install "$_nocron" ;;
bc96082f 3111 uninstall) uninstall "$_nocron" ;;
52677b0a 3112 upgrade) upgrade ;;
a63b05a9 3113 issue)
70a55875 3114 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath"
a63b05a9 3115 ;;
3116 installcert)
43822d37 3117 installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_ecc"
a63b05a9 3118 ;;
3119 renew)
43822d37 3120 renew "$_domain" "$_ecc"
a63b05a9 3121 ;;
3122 renewAll)
cc179731 3123 renewAll "$_stopRenewOnError"
a63b05a9 3124 ;;
3125 revoke)
43822d37 3126 revoke "$_domain" "$_ecc"
a63b05a9 3127 ;;
6d7eda3e 3128 list)
dcf4f8f6 3129 list "$_listraw"
6d7eda3e 3130 ;;
a63b05a9 3131 installcronjob) installcronjob ;;
3132 uninstallcronjob) uninstallcronjob ;;
3133 cron) cron ;;
3134 toPkcs)
43822d37 3135 toPkcs "$_domain" "$_password" "$_ecc"
a63b05a9 3136 ;;
3137 createAccountKey)
3138 createAccountKey "$_domain" "$_accountkeylength"
3139 ;;
3140 createDomainKey)
3141 createDomainKey "$_domain" "$_keylength"
3142 ;;
3143 createCSR)
43822d37 3144 createCSR "$_domain" "$_altdomains" "$_ecc"
a63b05a9 3145 ;;
3146
3147 *)
3148 _err "Invalid command: $_CMD"
3149 showhelp;
3150 return 1
3151 ;;
3152 esac
d3595686 3153 _ret="$?"
3154 if [ "$_ret" != "0" ] ; then
3155 return $_ret
3156 fi
a63b05a9 3157
8663fb7e 3158 if [ "$_useragent" ] ; then
635695ec 3159 _saveaccountconf "USER_AGENT" "$_useragent"
3160 fi
8663fb7e 3161 if [ "$_accountemail" ] ; then
b5eb4b90 3162 _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
3163 fi
b2817897 3164
635695ec 3165
a63b05a9 3166}
3167
3168
8663fb7e 3169if [ "$INSTALLONLINE" ] ; then
d1f97fc8 3170 INSTALLONLINE=""
4a0f23e2 3171 _installOnline $BRANCH
3172 exit
3173fi
4c3b3608 3174
8663fb7e 3175if [ -z "$1" ] ; then
4c3b3608 3176 showhelp
3177else
036e9d10 3178 if echo "$1" | grep "^-" >/dev/null 2>&1 ; then
a63b05a9 3179 _process "$@"
3180 else
3181 "$@"
3182 fi
4c3b3608 3183fi
a63b05a9 3184
3185