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