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