]> git.proxmox.com Git - mirror_acme.sh.git/blame - acme.sh
support multiple intermediate CA matching for `--preferred-chain`
[mirror_acme.sh.git] / acme.sh
CommitLineData
0a7c9364 1#!/usr/bin/env sh
bfdf1f48 2
9044adec 3VER=2.8.7
a7b7355d 4
6cc11ffb 5PROJECT_NAME="acme.sh"
a7b7355d 6
6cc11ffb 7PROJECT_ENTRY="acme.sh"
8
09f74a9a 9PROJECT="https://github.com/acmesh-official/$PROJECT_NAME"
4c3b3608 10
f3e4cea3 11DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME"
77f96b38 12
13_WINDOWS_SCHEDULER_NAME="$PROJECT_NAME.cron"
14
f3e4cea3 15_SCRIPT_="$0"
16
b50e701c 17_SUB_FOLDER_NOTIFY="notify"
18_SUB_FOLDER_DNSAPI="dnsapi"
19_SUB_FOLDER_DEPLOY="deploy"
20
21_SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY"
f3e4cea3 22
c1151b0d 23LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory"
24LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory"
25
737e9e48 26CA_LETSENCRYPT_V2="https://acme-v02.api.letsencrypt.org/directory"
27CA_LETSENCRYPT_V2_TEST="https://acme-staging-v02.api.letsencrypt.org/directory"
c1151b0d 28
737e9e48 29CA_BUYPASS="https://api.buypass.com/acme/directory"
30CA_BUYPASS_TEST="https://api.test4.buypass.no/acme/directory"
31
32CA_ZEROSSL="https://acme.zerossl.com/v2/DV90"
389518e1 33_ZERO_EAB_ENDPOINT="http://api.zerossl.com/acme/eab-credentials-email"
34
737e9e48 35DEFAULT_CA=$CA_LETSENCRYPT_V2
36DEFAULT_STAGING_CA=$CA_LETSENCRYPT_V2_TEST
37
38CA_NAMES="
269847d1 39Letsencrypt.org,letsencrypt
40Letsencrypt.org_test,letsencrypt_test,letsencrypttest
41BuyPass.com,buypass
42BuyPass.com_test,buypass_test,buypasstest
43ZeroSSL.com,zerossl
737e9e48 44"
45
46CA_SERVERS="$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_ZEROSSL"
f87890cb 47
07af4247 48DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
bbbdcb09 49
57e58ce7 50DEFAULT_ACCOUNT_KEY_LENGTH=2048
51DEFAULT_DOMAIN_KEY_LENGTH=2048
52
a746139c 53DEFAULT_OPENSSL_BIN="openssl"
54
4c3b3608 55VTYPE_HTTP="http-01"
56VTYPE_DNS="dns-01"
08681f4a 57VTYPE_ALPN="tls-alpn-01"
e22bcf7c 58
0463b5d6 59LOCAL_ANY_ADDRESS="0.0.0.0"
60
ec67a1b2 61DEFAULT_RENEW=60
523c7682 62
3f4513b3 63NO_VALUE="no"
64
3881f221 65W_DNS="dns"
08681f4a 66W_ALPN="alpn"
64821ad4 67DNS_ALIAS_PREFIX="="
4c3b3608 68
0e44f587 69MODE_STATELESS="stateless"
70
ec603bee 71STATE_VERIFIED="verified_ok"
72
9d725af6 73NGINX="nginx:"
03f8d6e9 74NGINX_START="#ACME_NGINX_START"
75NGINX_END="#ACME_NGINX_END"
9d725af6 76
88fab7d6 77BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
78END_CSR="-----END CERTIFICATE REQUEST-----"
79
80BEGIN_CERT="-----BEGIN CERTIFICATE-----"
81END_CERT="-----END CERTIFICATE-----"
82
45e386b2 83CONTENT_TYPE_JSON="application/jose+json"
cc179731 84RENEW_SKIP=2
85
7690f73e 86B64CONF_START="__ACME_BASE64__START_"
87B64CONF_END="__ACME_BASE64__END_"
88
43822d37 89ECC_SEP="_"
90ECC_SUFFIX="${ECC_SEP}ecc"
91
a73c5b33 92LOG_LEVEL_1=1
93LOG_LEVEL_2=2
94LOG_LEVEL_3=3
95DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
96
fc6cf4d9 97DEBUG_LEVEL_1=1
98DEBUG_LEVEL_2=2
99DEBUG_LEVEL_3=3
100DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
101DEBUG_LEVEL_NONE=0
102
10eec7d4 103DOH_CLOUDFLARE=1
104DOH_GOOGLE=2
105
e6e85b0c 106HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)"
107
e2edf208 108SYSLOG_ERROR="user.error"
fc6cf4d9 109SYSLOG_INFO="user.info"
e2edf208 110SYSLOG_DEBUG="user.debug"
111
fc6cf4d9 112#error
113089be 113SYSLOG_LEVEL_ERROR=3
fc6cf4d9 114#info
113089be 115SYSLOG_LEVEL_INFO=6
fc6cf4d9 116#debug
113089be 117SYSLOG_LEVEL_DEBUG=7
fc6cf4d9 118#debug2
113089be 119SYSLOG_LEVEL_DEBUG_2=8
fc6cf4d9 120#debug3
113089be 121SYSLOG_LEVEL_DEBUG_3=9
fc6cf4d9 122
113089be 123SYSLOG_LEVEL_DEFAULT=$SYSLOG_LEVEL_ERROR
fc6cf4d9 124#none
125SYSLOG_LEVEL_NONE=0
126
b50e701c 127NOTIFY_LEVEL_DISABLE=0
128NOTIFY_LEVEL_ERROR=1
129NOTIFY_LEVEL_RENEW=2
130NOTIFY_LEVEL_SKIP=3
131
132NOTIFY_LEVEL_DEFAULT=$NOTIFY_LEVEL_RENEW
133
134NOTIFY_MODE_BULK=0
135NOTIFY_MODE_CERT=1
136
137NOTIFY_MODE_DEFAULT=$NOTIFY_MODE_BULK
138
09f74a9a 139_DEBUG_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh"
4c3b3608 140
09f74a9a 141_PREPARE_LINK="https://github.com/acmesh-official/acme.sh/wiki/Install-preparations"
562a4c05 142
09f74a9a 143_STATELESS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode"
0e44f587 144
09f74a9a 145_DNS_ALIAS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode"
875625b1 146
09f74a9a 147_DNS_MANUAL_WIKI="https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode"
a0923622 148
e3ebd582 149_DNS_API_WIKI="https://github.com/acmesh-official/acme.sh/wiki/dnsapi"
150
09f74a9a 151_NOTIFY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/notify"
b50e701c 152
09f74a9a 153_SUDO_WIKI="https://github.com/acmesh-official/acme.sh/wiki/sudo"
5bdfdfef 154
1041c9f9 155_REVOKE_WIKI="https://github.com/acmesh-official/acme.sh/wiki/revokecert"
156
737e9e48 157_ZEROSSL_WIKI="https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA"
158
d83d8552 159_SERVER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Server"
160
e3ebd582 161_PREFERRED_CHAIN_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Preferred-Chain"
162
309bec47 163_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
164
165_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
166
a0923622 167_DNS_MANUAL_ERROR="It seems that you are using dns manual mode. Read this link first: $_DNS_MANUAL_WIKI"
168
08ee072f 169__INTERACTIVE=""
4c2a3841 170if [ -t 1 ]; then
08ee072f 171 __INTERACTIVE="1"
172fi
00a50605 173
43822d37 174__green() {
137dc1ea 175 if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then
671edc33 176 printf '\33[1;32m%b\33[0m' "$1"
137dc1ea 177 return
2d12b689 178 fi
3576754c 179 printf -- "%b" "$1"
43822d37 180}
181
182__red() {
137dc1ea 183 if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then
671edc33 184 printf '\33[1;31m%b\33[0m' "$1"
137dc1ea 185 return
2d12b689 186 fi
3576754c 187 printf -- "%b" "$1"
43822d37 188}
00a50605 189
a73c5b33 190_printargs() {
65a7d569 191 _exitstatus="$?"
569d6c55 192 if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
193 printf -- "%s" "[$(date)] "
194 fi
4c2a3841 195 if [ -z "$2" ]; then
569d6c55 196 printf -- "%s" "$1"
43822d37 197 else
569d6c55 198 printf -- "%s" "$1='$2'"
43822d37 199 fi
a73c5b33 200 printf "\n"
ac9f6e3a 201 # return the saved exit status
52351d7d 202 return "$_exitstatus"
43822d37 203}
204
9d548d81 205_dlg_versions() {
206 echo "Diagnosis versions: "
851fedf7 207 echo "openssl:$ACME_OPENSSL_BIN"
d8ba26e6 208 if _exists "${ACME_OPENSSL_BIN:-openssl}"; then
209 ${ACME_OPENSSL_BIN:-openssl} version 2>&1
9d548d81 210 else
eca57bee 211 echo "$ACME_OPENSSL_BIN doesn't exist."
9d548d81 212 fi
4c2a3841 213
9d548d81 214 echo "apache:"
4c2a3841 215 if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
e735d8d4 216 $_APACHECTL -V 2>&1
9d548d81 217 else
eca57bee 218 echo "apache doesn't exist."
9d548d81 219 fi
4c2a3841 220
326c386b 221 echo "nginx:"
222 if _exists "nginx"; then
223 nginx -V 2>&1
224 else
eca57bee 225 echo "nginx doesn't exist."
326c386b 226 fi
227
3794b5cb 228 echo "socat:"
229 if _exists "socat"; then
463df9e4 230 socat -V 2>&1
9d548d81 231 else
eca57bee 232 _debug "socat doesn't exist."
9d548d81 233 fi
234}
43822d37 235
e2edf208 236#class
237_syslog() {
65a7d569 238 _exitstatus="$?"
fc6cf4d9 239 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" = "$SYSLOG_LEVEL_NONE" ]; then
e2edf208 240 return
241 fi
242 _logclass="$1"
243 shift
5b3e3d9c 244 if [ -z "$__logger_i" ]; then
245 if _contains "$(logger --help 2>&1)" "-i"; then
246 __logger_i="logger -i"
247 else
248 __logger_i="logger"
249 fi
250 fi
251 $__logger_i -t "$PROJECT_NAME" -p "$_logclass" "$(_printargs "$@")" >/dev/null 2>&1
65a7d569 252 return "$_exitstatus"
e2edf208 253}
254
a73c5b33 255_log() {
256 [ -z "$LOG_FILE" ] && return
95e06de5 257 _printargs "$@" >>"$LOG_FILE"
a73c5b33 258}
259
260_info() {
fc6cf4d9 261 _log "$@"
113089be 262 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_INFO" ]; then
fc6cf4d9 263 _syslog "$SYSLOG_INFO" "$@"
264 fi
a73c5b33 265 _printargs "$@"
4c3b3608 266}
267
268_err() {
fc6cf4d9 269 _syslog "$SYSLOG_ERROR" "$@"
270 _log "$@"
569d6c55 271 if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
272 printf -- "%s" "[$(date)] " >&2
273 fi
4c2a3841 274 if [ -z "$2" ]; then
65de3110 275 __red "$1" >&2
276 else
277 __red "$1='$2'" >&2
278 fi
b19ba13a 279 printf "\n" >&2
4c3b3608 280 return 1
281}
282
43822d37 283_usage() {
4c2a3841 284 __red "$@" >&2
65de3110 285 printf "\n" >&2
43822d37 286}
287
bba5376a
JV
288__debug_bash_helper() {
289 # At this point only do for --debug 3
290 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -lt "$DEBUG_LEVEL_3" ]; then
bba5376a
JV
291 return
292 fi
293 # Return extra debug info when running with bash, otherwise return empty
294 # string.
295 if [ -z "${BASH_VERSION}" ]; then
bba5376a
JV
296 return
297 fi
298 # We are a bash shell at this point, return the filename, function name, and
299 # line number as a string
300 _dbh_saveIFS=$IFS
301 IFS=" "
adce8f52
JV
302 # Must use eval or syntax error happens under dash. The eval should use
303 # single quotes as older versions of busybox had a bug with double quotes and
304 # eval.
bba5376a
JV
305 # Use 'caller 1' as we want one level up the stack as we should be called
306 # by one of the _debug* functions
adce8f52 307 eval '_dbh_called=($(caller 1))'
bba5376a 308 IFS=$_dbh_saveIFS
adce8f52 309 eval '_dbh_file=${_dbh_called[2]}'
bba5376a
JV
310 if [ -n "${_script_home}" ]; then
311 # Trim off the _script_home directory name
adce8f52 312 eval '_dbh_file=${_dbh_file#$_script_home/}'
bba5376a 313 fi
adce8f52
JV
314 eval '_dbh_function=${_dbh_called[1]}'
315 eval '_dbh_lineno=${_dbh_called[0]}'
bba5376a
JV
316 printf "%-40s " "$_dbh_file:${_dbh_function}:${_dbh_lineno}"
317}
318
c60883ef 319_debug() {
fc6cf4d9 320 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
321 _log "$@"
a73c5b33 322 fi
113089be 323 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
fc6cf4d9 324 _syslog "$SYSLOG_DEBUG" "$@"
325 fi
326 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
bba5376a
JV
327 _bash_debug=$(__debug_bash_helper)
328 _printargs "${_bash_debug}$@" >&2
c60883ef 329 fi
c60883ef 330}
331
e6e85b0c 332#output the sensitive messages
333_secure_debug() {
334 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then
335 if [ "$OUTPUT_INSECURE" = "1" ]; then
336 _log "$@"
337 else
338 _log "$1" "$HIDDEN_VALUE"
339 fi
340 fi
341 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG" ]; then
342 _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
343 fi
344 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then
345 if [ "$OUTPUT_INSECURE" = "1" ]; then
346 _printargs "$@" >&2
347 else
348 _printargs "$1" "$HIDDEN_VALUE" >&2
349 fi
350 fi
351}
352
a63b05a9 353_debug2() {
fc6cf4d9 354 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
355 _log "$@"
a73c5b33 356 fi
113089be 357 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
fc6cf4d9 358 _syslog "$SYSLOG_DEBUG" "$@"
359 fi
360 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
bba5376a
JV
361 _bash_debug=$(__debug_bash_helper)
362 _printargs "${_bash_debug}$@" >&2
a63b05a9 363 fi
a63b05a9 364}
365
e6e85b0c 366_secure_debug2() {
367 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_2" ]; then
368 if [ "$OUTPUT_INSECURE" = "1" ]; then
369 _log "$@"
370 else
371 _log "$1" "$HIDDEN_VALUE"
372 fi
373 fi
374 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_2" ]; then
375 _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
376 fi
377 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
378 if [ "$OUTPUT_INSECURE" = "1" ]; then
379 _printargs "$@" >&2
380 else
381 _printargs "$1" "$HIDDEN_VALUE" >&2
382 fi
383 fi
384}
385
22ea4004 386_debug3() {
fc6cf4d9 387 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
388 _log "$@"
389 fi
113089be 390 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
fc6cf4d9 391 _syslog "$SYSLOG_DEBUG" "$@"
a73c5b33 392 fi
fc6cf4d9 393 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
bba5376a
JV
394 _bash_debug=$(__debug_bash_helper)
395 _printargs "${_bash_debug}$@" >&2
22ea4004 396 fi
22ea4004 397}
398
e6e85b0c 399_secure_debug3() {
400 if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_3" ]; then
401 if [ "$OUTPUT_INSECURE" = "1" ]; then
402 _log "$@"
403 else
404 _log "$1" "$HIDDEN_VALUE"
405 fi
406 fi
407 if [ "${SYS_LOG:-$SYSLOG_LEVEL_NONE}" -ge "$SYSLOG_LEVEL_DEBUG_3" ]; then
408 _syslog "$SYSLOG_DEBUG" "$1" "$HIDDEN_VALUE"
409 fi
410 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then
411 if [ "$OUTPUT_INSECURE" = "1" ]; then
412 _printargs "$@" >&2
413 else
414 _printargs "$1" "$HIDDEN_VALUE" >&2
415 fi
416 fi
417}
418
c4bf5eef 419_upper_case() {
420 # shellcheck disable=SC2018,SC2019
421 tr 'a-z' 'A-Z'
422}
423
424_lower_case() {
425 # shellcheck disable=SC2018,SC2019
426 tr 'A-Z' 'a-z'
427}
428
4c2a3841 429_startswith() {
dceb3aca 430 _str="$1"
431 _sub="$2"
19539575 432 echo "$_str" | grep "^$_sub" >/dev/null 2>&1
dceb3aca 433}
434
4c2a3841 435_endswith() {
43822d37 436 _str="$1"
437 _sub="$2"
438 echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
439}
440
4c2a3841 441_contains() {
dceb3aca 442 _str="$1"
443 _sub="$2"
43822d37 444 echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
dceb3aca 445}
446
c53da1ef 447_hasfield() {
448 _str="$1"
449 _field="$2"
450 _sep="$3"
4c2a3841 451 if [ -z "$_field" ]; then
43822d37 452 _usage "Usage: str field [sep]"
c53da1ef 453 return 1
454 fi
4c2a3841 455
456 if [ -z "$_sep" ]; then
c53da1ef 457 _sep=","
458 fi
4c2a3841 459
6cf7be4b 460 for f in $(echo "$_str" | tr "$_sep" ' '); do
4c2a3841 461 if [ "$f" = "$_field" ]; then
0c9546cc 462 _debug2 "'$_str' contains '$_field'"
c53da1ef 463 return 0 #contains ok
464 fi
465 done
0c9546cc 466 _debug2 "'$_str' does not contain '$_field'"
3c07f57a 467 return 1 #not contains
c53da1ef 468}
469
422dd1fa 470# str index [sep]
4c2a3841 471_getfield() {
0463b5d6 472 _str="$1"
473 _findex="$2"
474 _sep="$3"
4c2a3841 475
476 if [ -z "$_findex" ]; then
0463b5d6 477 _usage "Usage: str field [sep]"
478 return 1
479 fi
4c2a3841 480
481 if [ -z "$_sep" ]; then
0463b5d6 482 _sep=","
483 fi
484
201aa244 485 _ffi="$_findex"
4c2a3841 486 while [ "$_ffi" -gt "0" ]; do
201aa244 487 _fv="$(echo "$_str" | cut -d "$_sep" -f "$_ffi")"
4c2a3841 488 if [ "$_fv" ]; then
0463b5d6 489 printf -- "%s" "$_fv"
490 return 0
491 fi
95e06de5 492 _ffi="$(_math "$_ffi" - 1)"
0463b5d6 493 done
4c2a3841 494
0463b5d6 495 printf -- "%s" "$_str"
496
497}
498
4c2a3841 499_exists() {
c60883ef 500 cmd="$1"
4c2a3841 501 if [ -z "$cmd" ]; then
43822d37 502 _usage "Usage: _exists cmd"
c60883ef 503 return 1
504 fi
82dc2244 505
506 if eval type type >/dev/null 2>&1; then
507 eval type "$cmd" >/dev/null 2>&1
508 elif command >/dev/null 2>&1; then
19539575 509 command -v "$cmd" >/dev/null 2>&1
ce4be4e9 510 else
e591d5cf 511 which "$cmd" >/dev/null 2>&1
eac18b1c 512 fi
c60883ef 513 ret="$?"
690a5e20 514 _debug3 "$cmd exists=$ret"
c60883ef 515 return $ret
516}
517
00a50605 518#a + b
4c2a3841 519_math() {
be68fbd4 520 _m_opts="$@"
521 printf "%s" "$(($_m_opts))"
00a50605 522}
523
524_h_char_2_dec() {
525 _ch=$1
526 case "${_ch}" in
4c2a3841 527 a | A)
19539575 528 printf "10"
4c2a3841 529 ;;
530 b | B)
19539575 531 printf "11"
4c2a3841 532 ;;
533 c | C)
19539575 534 printf "12"
4c2a3841 535 ;;
536 d | D)
19539575 537 printf "13"
4c2a3841 538 ;;
539 e | E)
19539575 540 printf "14"
4c2a3841 541 ;;
542 f | F)
19539575 543 printf "15"
4c2a3841 544 ;;
00a50605 545 *)
19539575 546 printf "%s" "$_ch"
4c2a3841 547 ;;
19539575 548 esac
00a50605 549
550}
551
fac1e367 552_URGLY_PRINTF=""
4c2a3841 553if [ "$(printf '\x41')" != 'A' ]; then
fac1e367 554 _URGLY_PRINTF=1
555fi
556
f8bcfeb2 557_ESCAPE_XARGS=""
841b7627 558if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
f8bcfeb2 559 _ESCAPE_XARGS=1
560fi
561
4c3b3608 562_h2b() {
b615cce9 563 if _exists xxd && xxd -r -p 2>/dev/null; then
b420ec6c 564 return
565 fi
566
4c3b3608 567 hex=$(cat)
fa93d68b 568 ic=""
569 jc=""
b420ec6c 570 _debug2 _URGLY_PRINTF "$_URGLY_PRINTF"
571 if [ -z "$_URGLY_PRINTF" ]; then
f8bcfeb2 572 if [ "$_ESCAPE_XARGS" ] && _exists xargs; then
fa93d68b 573 _debug2 "xargs"
ded4469e 574 echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf
fa93d68b 575 else
ded4469e 576 for h in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/ \1/g'); do
fa93d68b 577 if [ -z "$h" ]; then
578 break
579 fi
580 printf "\x$h%s"
581 done
582 fi
b420ec6c 583 else
ded4469e 584 for c in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\)/ \1/g'); do
fa93d68b 585 if [ -z "$ic" ]; then
586 ic=$c
587 continue
00a50605 588 fi
fa93d68b 589 jc=$c
19539575 590 ic="$(_h_char_2_dec "$ic")"
591 jc="$(_h_char_2_dec "$jc")"
e51bef6d 592 printf '\'"$(printf "%o" "$(_math "$ic" \* 16 + $jc)")""%s"
fa93d68b 593 ic=""
594 jc=""
b420ec6c 595 done
596 fi
e591d5cf 597
4c3b3608 598}
599
542d7977 600_is_solaris() {
601 _contains "${__OS__:=$(uname -a)}" "solaris" || _contains "${__OS__:=$(uname -a)}" "SunOS"
602}
603
9bdb799b 604#_ascii_hex str
605#this can only process ascii chars, should only be used when od command is missing as a backup way.
606_ascii_hex() {
607 _debug2 "Using _ascii_hex"
608 _str="$1"
609 _str_len=${#_str}
610 _h_i=1
611 while [ "$_h_i" -le "$_str_len" ]; do
612 _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")"
613 printf " %02x" "'$_str_c"
614 _h_i="$(_math "$_h_i" + 1)"
615 done
616}
617
542d7977 618#stdin output hexstr splited by one space
619#input:"abc"
620#output: " 61 62 63"
621_hex_dump() {
4e4a6d83 622 if _exists od; then
623 od -A n -v -t x1 | tr -s " " | sed 's/ $//' | tr -d "\r\t\n"
624 elif _exists hexdump; then
625 _debug3 "using hexdump"
626 hexdump -v -e '/1 ""' -e '/1 " %02x" ""'
627 elif _exists xxd; then
628 _debug3 "using xxd"
629 xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " "
630 else
631 _debug3 "using _ascii_hex"
9bdb799b 632 str=$(cat)
633 _ascii_hex "$str"
634 fi
542d7977 635}
636
637#url encode, no-preserved chars
638#A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
639#41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a
640
641#a b c d e f g h i j k l m n o p q r s t u v w x y z
642#61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a
643
644#0 1 2 3 4 5 6 7 8 9 - _ . ~
645#30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e
646
647#stdin stdout
648_url_encode() {
649 _hex_str=$(_hex_dump)
650 _debug3 "_url_encode"
651 _debug3 "_hex_str" "$_hex_str"
652 for _hex_code in $_hex_str; do
653 #upper case
654 case "${_hex_code}" in
c3b1eb08 655 "41")
656 printf "%s" "A"
657 ;;
658 "42")
659 printf "%s" "B"
660 ;;
661 "43")
662 printf "%s" "C"
663 ;;
664 "44")
665 printf "%s" "D"
666 ;;
667 "45")
668 printf "%s" "E"
669 ;;
670 "46")
671 printf "%s" "F"
672 ;;
673 "47")
674 printf "%s" "G"
675 ;;
676 "48")
677 printf "%s" "H"
678 ;;
679 "49")
680 printf "%s" "I"
681 ;;
682 "4a")
683 printf "%s" "J"
684 ;;
685 "4b")
686 printf "%s" "K"
687 ;;
688 "4c")
689 printf "%s" "L"
690 ;;
691 "4d")
692 printf "%s" "M"
693 ;;
694 "4e")
695 printf "%s" "N"
696 ;;
697 "4f")
698 printf "%s" "O"
699 ;;
700 "50")
701 printf "%s" "P"
702 ;;
703 "51")
704 printf "%s" "Q"
705 ;;
706 "52")
707 printf "%s" "R"
708 ;;
709 "53")
710 printf "%s" "S"
711 ;;
712 "54")
713 printf "%s" "T"
714 ;;
715 "55")
716 printf "%s" "U"
717 ;;
718 "56")
719 printf "%s" "V"
720 ;;
721 "57")
722 printf "%s" "W"
723 ;;
724 "58")
725 printf "%s" "X"
726 ;;
727 "59")
728 printf "%s" "Y"
729 ;;
730 "5a")
731 printf "%s" "Z"
732 ;;
733
734 #lower case
735 "61")
736 printf "%s" "a"
737 ;;
738 "62")
739 printf "%s" "b"
740 ;;
741 "63")
742 printf "%s" "c"
743 ;;
744 "64")
745 printf "%s" "d"
746 ;;
747 "65")
748 printf "%s" "e"
749 ;;
750 "66")
751 printf "%s" "f"
752 ;;
753 "67")
754 printf "%s" "g"
755 ;;
756 "68")
757 printf "%s" "h"
758 ;;
759 "69")
760 printf "%s" "i"
761 ;;
762 "6a")
763 printf "%s" "j"
764 ;;
765 "6b")
766 printf "%s" "k"
767 ;;
768 "6c")
769 printf "%s" "l"
770 ;;
771 "6d")
772 printf "%s" "m"
773 ;;
774 "6e")
775 printf "%s" "n"
776 ;;
777 "6f")
778 printf "%s" "o"
779 ;;
780 "70")
781 printf "%s" "p"
782 ;;
783 "71")
784 printf "%s" "q"
785 ;;
786 "72")
787 printf "%s" "r"
788 ;;
789 "73")
790 printf "%s" "s"
791 ;;
792 "74")
793 printf "%s" "t"
794 ;;
795 "75")
796 printf "%s" "u"
797 ;;
798 "76")
799 printf "%s" "v"
800 ;;
801 "77")
802 printf "%s" "w"
803 ;;
804 "78")
805 printf "%s" "x"
806 ;;
807 "79")
808 printf "%s" "y"
809 ;;
810 "7a")
811 printf "%s" "z"
812 ;;
813 #numbers
814 "30")
815 printf "%s" "0"
816 ;;
817 "31")
818 printf "%s" "1"
819 ;;
820 "32")
821 printf "%s" "2"
822 ;;
823 "33")
824 printf "%s" "3"
825 ;;
826 "34")
827 printf "%s" "4"
828 ;;
829 "35")
830 printf "%s" "5"
831 ;;
832 "36")
833 printf "%s" "6"
834 ;;
835 "37")
836 printf "%s" "7"
837 ;;
838 "38")
839 printf "%s" "8"
840 ;;
841 "39")
842 printf "%s" "9"
843 ;;
844 "2d")
845 printf "%s" "-"
846 ;;
847 "5f")
848 printf "%s" "_"
849 ;;
850 "2e")
851 printf "%s" "."
852 ;;
853 "7e")
854 printf "%s" "~"
855 ;;
3c07f57a 856 #other hex
542d7977 857 *)
c3b1eb08 858 printf '%%%s' "$_hex_code"
859 ;;
542d7977 860 esac
e009ec8b 861 done
862}
863
b50e701c 864_json_encode() {
865 _j_str="$(sed 's/"/\\"/g' | sed "s/\r/\\r/g")"
866 _debug3 "_json_encode"
867 _debug3 "_j_str" "$_j_str"
868 echo "$_j_str" | _hex_dump | _lower_case | sed 's/0a/5c 6e/g' | tr -d ' ' | _h2b | tr -d "\r\n"
869}
870
93de1e49 871#from: http:\/\/ to http://
872_json_decode() {
873 _j_str="$(sed 's#\\/#/#g')"
874 _debug3 "_json_decode"
875 _debug3 "_j_str" "$_j_str"
876 echo "$_j_str"
877}
878
c60883ef 879#options file
880_sed_i() {
881 options="$1"
882 filename="$2"
4c2a3841 883 if [ -z "$filename" ]; then
43822d37 884 _usage "Usage:_sed_i options filename"
c60883ef 885 return 1
886 fi
14f3dbb7 887 _debug2 options "$options"
888 if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
c60883ef 889 _debug "Using sed -i"
14f3dbb7 890 sed -i "$options" "$filename"
c60883ef 891 else
892 _debug "No -i support in sed"
19539575 893 text="$(cat "$filename")"
4c2a3841 894 echo "$text" | sed "$options" >"$filename"
c60883ef 895 fi
896}
897
22ea4004 898_egrep_o() {
a3c0c754 899 if ! egrep -o "$1" 2>/dev/null; then
22ea4004 900 sed -n 's/.*\('"$1"'\).*/\1/p'
22ea4004 901 fi
902}
903
88fab7d6 904#Usage: file startline endline
905_getfile() {
906 filename="$1"
907 startline="$2"
908 endline="$3"
4c2a3841 909 if [ -z "$endline" ]; then
43822d37 910 _usage "Usage: file startline endline"
88fab7d6 911 return 1
912 fi
4c2a3841 913
914 i="$(grep -n -- "$startline" "$filename" | cut -d : -f 1)"
915 if [ -z "$i" ]; then
88fab7d6 916 _err "Can not find start line: $startline"
917 return 1
918 fi
19539575 919 i="$(_math "$i" + 1)"
920 _debug i "$i"
4c2a3841 921
922 j="$(grep -n -- "$endline" "$filename" | cut -d : -f 1)"
923 if [ -z "$j" ]; then
88fab7d6 924 _err "Can not find end line: $endline"
925 return 1
926 fi
19539575 927 j="$(_math "$j" - 1)"
928 _debug j "$j"
4c2a3841 929
930 sed -n "$i,${j}p" "$filename"
88fab7d6 931
932}
933
934#Usage: multiline
4c3b3608 935_base64() {
ec9975c3 936 [ "" ] #urgly
4c2a3841 937 if [ "$1" ]; then
24d2a8b9 938 _debug3 "base64 multiline:'$1'"
d8ba26e6 939 ${ACME_OPENSSL_BIN:-openssl} base64 -e
88fab7d6 940 else
4d8b99a3 941 _debug3 "base64 single line."
d8ba26e6 942 ${ACME_OPENSSL_BIN:-openssl} base64 -e | tr -d '\r\n'
88fab7d6 943 fi
944}
945
946#Usage: multiline
947_dbase64() {
4c2a3841 948 if [ "$1" ]; then
d8ba26e6 949 ${ACME_OPENSSL_BIN:-openssl} base64 -d -A
88fab7d6 950 else
d8ba26e6 951 ${ACME_OPENSSL_BIN:-openssl} base64 -d
88fab7d6 952 fi
953}
954
183063a2 955#file
956_checkcert() {
957 _cf="$1"
958 if [ "$DEBUG" ]; then
7e381f8e 959 openssl x509 -noout -text -in "$_cf"
183063a2 960 else
7e381f8e 961 openssl x509 -noout -text -in "$_cf" >/dev/null 2>&1
183063a2 962 fi
963}
964
e22bcf7c 965#Usage: hashalg [outputhex]
88fab7d6 966#Output Base64-encoded digest
967_digest() {
968 alg="$1"
4c2a3841 969 if [ -z "$alg" ]; then
43822d37 970 _usage "Usage: _digest hashalg"
88fab7d6 971 return 1
972 fi
4c2a3841 973
e22bcf7c 974 outputhex="$2"
4c2a3841 975
c7b16249 976 if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
4c2a3841 977 if [ "$outputhex" ]; then
d8ba26e6 978 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
e22bcf7c 979 else
d8ba26e6 980 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -binary | _base64
b001840d 981 fi
982 else
983 _err "$alg is not supported yet"
984 return 1
985 fi
986
987}
988
e009ec8b 989#Usage: hashalg secret_hex [outputhex]
990#Output binary hmac
b001840d 991_hmac() {
992 alg="$1"
e009ec8b 993 secret_hex="$2"
b001840d 994 outputhex="$3"
4c2a3841 995
e009ec8b 996 if [ -z "$secret_hex" ]; then
4c2a3841 997 _usage "Usage: _hmac hashalg secret [outputhex]"
b001840d 998 return 1
999 fi
1000
a6014bf0 1001 if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
4c2a3841 1002 if [ "$outputhex" ]; then
d8ba26e6 1003 (${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' '
e22bcf7c 1004 else
d8ba26e6 1005 ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || ${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary
e22bcf7c 1006 fi
88fab7d6 1007 else
1008 _err "$alg is not supported yet"
1009 return 1
1010 fi
1011
1012}
1013
1014#Usage: keyfile hashalg
1015#Output: Base64-encoded signature value
1016_sign() {
1017 keyfile="$1"
1018 alg="$2"
4c2a3841 1019 if [ -z "$alg" ]; then
43822d37 1020 _usage "Usage: _sign keyfile hashalg"
88fab7d6 1021 return 1
1022 fi
4c2a3841 1023
d8ba26e6 1024 _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
4c2a3841 1025
63031fb2 1026 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || grep "BEGIN PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
f87890cb 1027 $_sign_openssl -$alg | _base64
4c2a3841 1028 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
f87890cb 1029 if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
67184d7b 1030 _err "Sign failed: $_sign_openssl"
1031 _err "Key file: $keyfile"
357b514b 1032 _err "Key content:$(wc -l <"$keyfile") lines"
67184d7b 1033 return 1
1034 fi
998783eb 1035 _debug3 "_signedECText" "$_signedECText"
1036 _ec_r="$(echo "$_signedECText" | _head_n 2 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
998783eb 1037 _ec_s="$(echo "$_signedECText" | _head_n 3 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")"
a3295476 1038 if [ "$__ECC_KEY_LEN" -eq "256" ]; then
1039 while [ "${#_ec_r}" -lt "64" ]; do
f31debc0 1040 _ec_r="0${_ec_r}"
6654d7a9 1041 done
a3295476 1042 while [ "${#_ec_s}" -lt "64" ]; do
f31debc0 1043 _ec_s="0${_ec_s}"
a3295476 1044 done
1045 fi
1046 if [ "$__ECC_KEY_LEN" -eq "384" ]; then
1047 while [ "${#_ec_r}" -lt "96" ]; do
f31debc0 1048 _ec_r="0${_ec_r}"
6654d7a9 1049 done
a3295476 1050 while [ "${#_ec_s}" -lt "96" ]; do
f31debc0 1051 _ec_s="0${_ec_s}"
a3295476 1052 done
1053 fi
1054 if [ "$__ECC_KEY_LEN" -eq "512" ]; then
1055 while [ "${#_ec_r}" -lt "132" ]; do
f31debc0 1056 _ec_r="0${_ec_r}"
6654d7a9 1057 done
a3295476 1058 while [ "${#_ec_s}" -lt "132" ]; do
f31debc0 1059 _ec_s="0${_ec_s}"
a3295476 1060 done
1061 fi
e2a5af1c 1062 _debug3 "_ec_r" "$_ec_r"
998783eb 1063 _debug3 "_ec_s" "$_ec_s"
1064 printf "%s" "$_ec_r$_ec_s" | _h2b | _base64
1065 else
1066 _err "Unknown key file format."
1067 return 1
1068 fi
4c2a3841 1069
4c3b3608 1070}
1071
3281043e 1072#keylength or isEcc flag (empty str => not ecc)
43822d37 1073_isEccKey() {
1074 _length="$1"
1075
4c2a3841 1076 if [ -z "$_length" ]; then
43822d37 1077 return 1
1078 fi
1079
1080 [ "$_length" != "1024" ] \
4c2a3841 1081 && [ "$_length" != "2048" ] \
1082 && [ "$_length" != "3072" ] \
1083 && [ "$_length" != "4096" ] \
1084 && [ "$_length" != "8192" ]
43822d37 1085}
1086
e22bcf7c 1087# _createkey 2048|ec-256 file
1088_createkey() {
1089 length="$1"
1090 f="$2"
c4236e58 1091 _debug2 "_createkey for file:$f"
43822d37 1092 eccname="$length"
4c2a3841 1093 if _startswith "$length" "ec-"; then
f9a6988e 1094 length=$(printf "%s" "$length" | cut -d '-' -f 2-100)
e22bcf7c 1095
4c2a3841 1096 if [ "$length" = "256" ]; then
e22bcf7c 1097 eccname="prime256v1"
1098 fi
4c2a3841 1099 if [ "$length" = "384" ]; then
e22bcf7c 1100 eccname="secp384r1"
1101 fi
4c2a3841 1102 if [ "$length" = "521" ]; then
e22bcf7c 1103 eccname="secp521r1"
1104 fi
43822d37 1105
e22bcf7c 1106 fi
1107
4c2a3841 1108 if [ -z "$length" ]; then
1109 length=2048
43822d37 1110 fi
4c2a3841 1111
cbcd7e0f 1112 _debug "Use length $length"
43822d37 1113
81532f37 1114 if ! touch "$f" >/dev/null 2>&1; then
1115 _f_path="$(dirname "$f")"
1116 _debug _f_path "$_f_path"
1117 if ! mkdir -p "$_f_path"; then
1118 _err "Can not create path: $_f_path"
1119 return 1
1120 fi
1121 fi
1122
4c2a3841 1123 if _isEccKey "$length"; then
cbcd7e0f 1124 _debug "Using ec name: $eccname"
e6df1828 1125 if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null)"; then
1126 echo "$_opkey" >"$f"
1127 else
1128 _err "error ecc key name: $eccname"
1129 return 1
1130 fi
e22bcf7c 1131 else
cbcd7e0f 1132 _debug "Using RSA: $length"
e6df1828 1133 if _opkey="$(${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null)"; then
1134 echo "$_opkey" >"$f"
1135 else
1136 _err "error rsa key: $length"
1137 return 1
1138 fi
e22bcf7c 1139 fi
43822d37 1140
4c2a3841 1141 if [ "$?" != "0" ]; then
43822d37 1142 _err "Create key error."
1143 return 1
1144 fi
e22bcf7c 1145}
1146
9774b01b 1147#domain
1148_is_idn() {
1149 _is_idn_d="$1"
049be104 1150 _debug2 _is_idn_d "$_is_idn_d"
d10f40f1 1151 _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-_')
049be104 1152 _debug2 _idn_temp "$_idn_temp"
1153 [ "$_idn_temp" ]
9774b01b 1154}
1155
1156#aa.com
1157#aa.com,bb.com,cc.com
1158_idn() {
1159 __idn_d="$1"
4c2a3841 1160 if ! _is_idn "$__idn_d"; then
9774b01b 1161 printf "%s" "$__idn_d"
1162 return 0
1163 fi
4c2a3841 1164
1165 if _exists idn; then
1166 if _contains "$__idn_d" ','; then
9774b01b 1167 _i_first="1"
4c2a3841 1168 for f in $(echo "$__idn_d" | tr ',' ' '); do
9774b01b 1169 [ -z "$f" ] && continue
4c2a3841 1170 if [ -z "$_i_first" ]; then
9774b01b 1171 printf "%s" ","
1172 else
1173 _i_first=""
1174 fi
2a1e06f8 1175 idn --quiet "$f" | tr -d "\r\n"
9774b01b 1176 done
1177 else
1178 idn "$__idn_d" | tr -d "\r\n"
1179 fi
1180 else
1181 _err "Please install idn to process IDN names."
1182 fi
1183}
1184
08681f4a 1185#_createcsr cn san_list keyfile csrfile conf acmeValidationv1
e22bcf7c 1186_createcsr() {
1187 _debug _createcsr
1188 domain="$1"
1189 domainlist="$2"
0c9546cc 1190 csrkey="$3"
e22bcf7c 1191 csr="$4"
1192 csrconf="$5"
08681f4a 1193 acmeValidationv1="$6"
e22bcf7c 1194 _debug2 domain "$domain"
1195 _debug2 domainlist "$domainlist"
0c9546cc 1196 _debug2 csrkey "$csrkey"
1197 _debug2 csr "$csr"
1198 _debug2 csrconf "$csrconf"
4c2a3841 1199
1200 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf"
1201
08681f4a 1202 if [ "$acmeValidationv1" ]; then
4962cc3d 1203 domainlist="$(_idn "$domainlist")"
08681f4a 1204 printf -- "\nsubjectAltName=DNS:$domainlist" >>"$csrconf"
1205 elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
e22bcf7c 1206 #single domain
1207 _info "Single domain" "$domain"
0093dc3d 1208 printf -- "\nsubjectAltName=DNS:$(_idn "$domain")" >>"$csrconf"
e22bcf7c 1209 else
f9a6988e 1210 domainlist="$(_idn "$domainlist")"
9774b01b 1211 _debug2 domainlist "$domainlist"
4c2a3841 1212 if _contains "$domainlist" ","; then
0093dc3d 1213 alt="DNS:$(_idn "$domain"),DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")"
e22bcf7c 1214 else
0093dc3d 1215 alt="DNS:$(_idn "$domain"),DNS:$domainlist"
e22bcf7c 1216 fi
3c07f57a 1217 #multi
e22bcf7c 1218 _info "Multi domain" "$alt"
4c2a3841 1219 printf -- "\nsubjectAltName=$alt" >>"$csrconf"
0c9546cc 1220 fi
6ba1eda9 1221 if [ "$Le_OCSP_Staple" = "1" ]; then
96db9362 1222 _savedomainconf Le_OCSP_Staple "$Le_OCSP_Staple"
4c2a3841 1223 printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >>"$csrconf"
e22bcf7c 1224 fi
4c2a3841 1225
08681f4a 1226 if [ "$acmeValidationv1" ]; then
ad613e24 1227 printf "\n1.3.6.1.5.5.7.1.31=critical,DER:04:20:${acmeValidationv1}" >>"${csrconf}"
08681f4a 1228 fi
1229
9774b01b 1230 _csr_cn="$(_idn "$domain")"
1231 _debug2 _csr_cn "$_csr_cn"
34f25fa5 1232 if _contains "$(uname -a)" "MINGW"; then
d8ba26e6 1233 ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
34f25fa5 1234 else
d8ba26e6 1235 ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
34f25fa5 1236 fi
e22bcf7c 1237}
1238
1239#_signcsr key csr conf cert
1240_signcsr() {
1241 key="$1"
1242 csr="$2"
1243 conf="$3"
1244 cert="$4"
5aa146a5 1245 _debug "_signcsr"
4c2a3841 1246
d8ba26e6 1247 _msg="$(${ACME_OPENSSL_BIN:-openssl} x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
5aa146a5 1248 _ret="$?"
1249 _debug "$_msg"
1250 return $_ret
e22bcf7c 1251}
1252
10afcaca 1253#_csrfile
1254_readSubjectFromCSR() {
1255 _csrfile="$1"
4c2a3841 1256 if [ -z "$_csrfile" ]; then
10afcaca 1257 _usage "_readSubjectFromCSR mycsr.csr"
1258 return 1
1259 fi
b963dadc 1260 ${ACME_OPENSSL_BIN:-openssl} req -noout -in "$_csrfile" -subject | tr ',' "\n" | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d ' \n'
10afcaca 1261}
1262
1263#_csrfile
1264#echo comma separated domain list
1265_readSubjectAltNamesFromCSR() {
1266 _csrfile="$1"
4c2a3841 1267 if [ -z "$_csrfile" ]; then
10afcaca 1268 _usage "_readSubjectAltNamesFromCSR mycsr.csr"
1269 return 1
1270 fi
4c2a3841 1271
10afcaca 1272 _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
1273 _debug _csrsubj "$_csrsubj"
4c2a3841 1274
d8ba26e6 1275 _dnsAltnames="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
10afcaca 1276 _debug _dnsAltnames "$_dnsAltnames"
4c2a3841 1277
1278 if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then
10afcaca 1279 _debug "AltNames contains subject"
2b9ebd66 1280 _excapedAlgnames="$(echo "$_dnsAltnames" | tr '*' '#')"
1281 _debug _excapedAlgnames "$_excapedAlgnames"
1282 _escapedSubject="$(echo "$_csrsubj" | tr '*' '#')"
1283 _debug _escapedSubject "$_escapedSubject"
1284 _dnsAltnames="$(echo "$_excapedAlgnames," | sed "s/DNS:$_escapedSubject,//g" | tr '#' '*' | sed "s/,\$//g")"
1285 _debug _dnsAltnames "$_dnsAltnames"
10afcaca 1286 else
1287 _debug "AltNames doesn't contain subject"
1288 fi
4c2a3841 1289
2b9ebd66 1290 echo "$_dnsAltnames" | sed "s/DNS://g"
10afcaca 1291}
1292
3c07f57a 1293#_csrfile
10afcaca 1294_readKeyLengthFromCSR() {
1295 _csrfile="$1"
4c2a3841 1296 if [ -z "$_csrfile" ]; then
1643b476 1297 _usage "_readKeyLengthFromCSR mycsr.csr"
10afcaca 1298 return 1
1299 fi
4c2a3841 1300
d8ba26e6 1301 _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
7df062b7 1302 _debug2 _outcsr "$_outcsr"
4c2a3841 1303 if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
10afcaca 1304 _debug "ECC CSR"
482cb737 1305 echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
10afcaca 1306 else
1307 _debug "RSA CSR"
eb0ef6bd 1308 _rkl="$(echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1)"
1309 if [ "$_rkl" ]; then
1310 echo "$_rkl"
1311 else
1312 echo "$_outcsr" | tr "\t" " " | _egrep_o "RSA Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
1313 fi
10afcaca 1314 fi
1315}
1316
34c27e09 1317_ss() {
1318 _port="$1"
4c2a3841 1319
1320 if _exists "ss"; then
edf08da6 1321 _debug "Using: ss"
14d7bfda 1322 ss -ntpl 2>/dev/null | grep ":$_port "
edf08da6 1323 return 0
1324 fi
1325
4c2a3841 1326 if _exists "netstat"; then
251fc37c 1327 _debug "Using: netstat"
97147b59 1328 if netstat -help 2>&1 | grep "\-p proto" >/dev/null; then
ccb96535 1329 #for windows version netstat tool
0463b5d6 1330 netstat -an -p tcp | grep "LISTENING" | grep ":$_port "
ccb96535 1331 else
4c2a3841 1332 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null; then
19539575 1333 netstat -an -p tcp | grep LISTEN | grep ":$_port "
4c2a3841 1334 elif netstat -help 2>&1 | grep -- '-P protocol' >/dev/null; then
22ea4004 1335 #for solaris
e3c66532 1336 netstat -an -P tcp | grep "\.$_port " | grep "LISTEN"
f19f2100 1337 elif netstat -help 2>&1 | grep "\-p" >/dev/null; then
f21dd911 1338 #for full linux
19539575 1339 netstat -ntpl | grep ":$_port "
f21dd911 1340 else
1341 #for busybox (embedded linux; no pid support)
1342 netstat -ntl 2>/dev/null | grep ":$_port "
edf08da6 1343 fi
ccb96535 1344 fi
34c27e09 1345 return 0
1346 fi
edf08da6 1347
34c27e09 1348 return 1
1349}
1350
8eab77f3
BC
1351#outfile key cert cacert [password [name [caname]]]
1352_toPkcs() {
1353 _cpfx="$1"
1354 _ckey="$2"
1355 _ccert="$3"
1356 _cca="$4"
1357 pfxPassword="$5"
1358 pfxName="$6"
1359 pfxCaname="$7"
1360
1361 if [ "$pfxCaname" ]; then
1362 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" -caname "$pfxCaname"
1363 elif [ "$pfxName" ]; then
1364 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName"
1365 elif [ "$pfxPassword" ]; then
1366 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword"
1367 else
1368 ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca"
1369 fi
1370
1371}
1372
43822d37 1373#domain [password] [isEcc]
ac2d5123 1374toPkcs() {
1375 domain="$1"
1376 pfxPassword="$2"
4c2a3841 1377 if [ -z "$domain" ]; then
43822d37 1378 _usage "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
ac2d5123 1379 return 1
1380 fi
1381
43822d37 1382 _isEcc="$3"
4c2a3841 1383
43822d37 1384 _initpath "$domain" "$_isEcc"
1385
8eab77f3 1386 _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$pfxPassword"
4c2a3841 1387
1388 if [ "$?" = "0" ]; then
ac2d5123 1389 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
1390 fi
1391
1392}
1393
4410226d 1394#domain [isEcc]
1395toPkcs8() {
1396 domain="$1"
1397
1398 if [ -z "$domain" ]; then
1399 _usage "Usage: $PROJECT_ENTRY --toPkcs8 -d domain [--ecc]"
1400 return 1
1401 fi
1402
1403 _isEcc="$2"
1404
1405 _initpath "$domain" "$_isEcc"
1406
d8ba26e6 1407 ${ACME_OPENSSL_BIN:-openssl} pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH"
4410226d 1408
1409 if [ "$?" = "0" ]; then
1410 _info "Success, $CERT_PKCS8_PATH"
1411 fi
1412
1413}
1414
3c07f57a 1415#[2048]
4c3b3608 1416createAccountKey() {
1417 _info "Creating account key"
4c2a3841 1418 if [ -z "$1" ]; then
5fbc47eb 1419 _usage "Usage: $PROJECT_ENTRY --createAccountKey --accountkeylength 2048"
4c3b3608 1420 return
1421 fi
4c2a3841 1422
5fbc47eb 1423 length=$1
57e58ce7 1424 _create_account_key "$length"
1425
1426}
1427
1428_create_account_key() {
1429
5fbc47eb 1430 length=$1
4c2a3841 1431
1432 if [ -z "$length" ] || [ "$length" = "$NO_VALUE" ]; then
57e58ce7 1433 _debug "Use default length $DEFAULT_ACCOUNT_KEY_LENGTH"
1434 length="$DEFAULT_ACCOUNT_KEY_LENGTH"
4c3b3608 1435 fi
4c2a3841 1436
5fbc47eb 1437 _debug length "$length"
4c3b3608 1438 _initpath
5fbc47eb 1439
57e58ce7 1440 mkdir -p "$CA_DIR"
4f1888d2 1441 if [ -s "$ACCOUNT_KEY_PATH" ]; then
4c3b3608 1442 _info "Account key exists, skip"
4f1888d2 1443 return 0
4c3b3608 1444 else
1445 #generate account key
4f1888d2 1446 if _createkey "$length" "$ACCOUNT_KEY_PATH"; then
1447 chmod 600 "$ACCOUNT_KEY_PATH"
1448 _info "Create account key ok."
1449 return 0
1450 else
1451 _err "Create account key error."
1452 return 1
1453 fi
4c3b3608 1454 fi
1455
1456}
1457
43822d37 1458#domain [length]
4c3b3608 1459createDomainKey() {
1460 _info "Creating domain key"
4c2a3841 1461 if [ -z "$1" ]; then
43822d37 1462 _usage "Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]"
4c3b3608 1463 return
1464 fi
4c2a3841 1465
4c3b3608 1466 domain=$1
2844d73d 1467 _cdl=$2
e22bcf7c 1468
2844d73d 1469 if [ -z "$_cdl" ]; then
57e58ce7 1470 _debug "Use DEFAULT_DOMAIN_KEY_LENGTH=$DEFAULT_DOMAIN_KEY_LENGTH"
2844d73d 1471 _cdl="$DEFAULT_DOMAIN_KEY_LENGTH"
57e58ce7 1472 fi
e22bcf7c 1473
2844d73d 1474 _initpath "$domain" "$_cdl"
4c2a3841 1475
bd04638d 1476 if [ ! -f "$CERT_KEY_PATH" ] || [ ! -s "$CERT_KEY_PATH" ] || ([ "$FORCE" ] && ! [ "$_ACME_IS_RENEW" ]) || [ "$Le_ForceNewDomainKey" = "1" ]; then
2844d73d 1477 if _createkey "$_cdl" "$CERT_KEY_PATH"; then
1478 _savedomainconf Le_Keylength "$_cdl"
1479 _info "The domain key is here: $(__green $CERT_KEY_PATH)"
7aeb113c 1480 return 0
4f1888d2 1481 else
a7420ca3 1482 _err "Can not create domain key"
4f1888d2 1483 return 1
2844d73d 1484 fi
4c3b3608 1485 else
bd04638d 1486 if [ "$_ACME_IS_RENEW" ]; then
4c3b3608 1487 _info "Domain key exists, skip"
1488 return 0
1489 else
1490 _err "Domain key exists, do you want to overwrite the key?"
41e3eafa 1491 _err "Add '--force', and try again."
4c3b3608 1492 return 1
1493 fi
1494 fi
1495
1496}
1497
43822d37 1498# domain domainlist isEcc
4c3b3608 1499createCSR() {
1500 _info "Creating csr"
4c2a3841 1501 if [ -z "$1" ]; then
43822d37 1502 _usage "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
4c3b3608 1503 return
1504 fi
4c2a3841 1505
43822d37 1506 domain="$1"
1507 domainlist="$2"
1508 _isEcc="$3"
4c2a3841 1509
43822d37 1510 _initpath "$domain" "$_isEcc"
4c2a3841 1511
bd04638d 1512 if [ -f "$CSR_PATH" ] && [ "$_ACME_IS_RENEW" ] && [ -z "$FORCE" ]; then
4c3b3608 1513 _info "CSR exists, skip"
1514 return
1515 fi
4c2a3841 1516
1517 if [ ! -f "$CERT_KEY_PATH" ]; then
43822d37 1518 _err "The key file is not found: $CERT_KEY_PATH"
1519 _err "Please create the key file first."
1520 return 1
1521 fi
e22bcf7c 1522 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
4c2a3841 1523
4c3b3608 1524}
1525
11927a76 1526_url_replace() {
f9a6988e 1527 tr '/+' '_-' | tr -d '= '
4c3b3608 1528}
1529
f96d91cb 1530#base64 string
1531_durl_replace_base64() {
1532 _l=$((${#1} % 4))
1533 if [ $_l -eq 2 ]; then
1534 _s="$1"'=='
1535 elif [ $_l -eq 3 ]; then
1536 _s="$1"'='
1537 else
1538 _s="$1"
1539 fi
1540 echo "$_s" | tr '_-' '/+'
1541}
1542
4c3b3608 1543_time2str() {
cb115809
JM
1544 #BSD
1545 if date -u -r "$1" 2>/dev/null; then
4c3b3608 1546 return
1547 fi
4c2a3841 1548
cb115809
JM
1549 #Linux
1550 if date -u -d@"$1" 2>/dev/null; then
4c3b3608 1551 return
1552 fi
4c2a3841 1553
cb115809 1554 #Solaris
4c2a3841 1555 if _exists adb; then
031e885e 1556 _t_s_a=$(echo "0t${1}=Y" | adb)
1557 echo "$_t_s_a"
22ea4004 1558 fi
4c2a3841 1559
a07395fb
M
1560 #Busybox
1561 if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then
1562 return
1563 fi
4c3b3608 1564}
1565
eae29099 1566_normalizeJson() {
1567 sed "s/\" *: *\([\"{\[]\)/\":\1/g" | sed "s/^ *\([^ ]\)/\1/" | tr -d "\r\n"
1568}
1569
44df2967 1570_stat() {
1571 #Linux
4c2a3841 1572 if stat -c '%U:%G' "$1" 2>/dev/null; then
44df2967 1573 return
1574 fi
4c2a3841 1575
44df2967 1576 #BSD
4c2a3841 1577 if stat -f '%Su:%Sg' "$1" 2>/dev/null; then
44df2967 1578 return
1579 fi
4c2a3841 1580
1581 return 1 #error, 'stat' not found
44df2967 1582}
1583
166096dc 1584#keyfile
1585_calcjwk() {
1586 keyfile="$1"
4c2a3841 1587 if [ -z "$keyfile" ]; then
43822d37 1588 _usage "Usage: _calcjwk keyfile"
166096dc 1589 return 1
1590 fi
4c2a3841 1591
1592 if [ "$JWK_HEADER" ] && [ "$__CACHED_JWK_KEY_FILE" = "$keyfile" ]; then
ae2db62f 1593 _debug2 "Use cached jwk for file: $__CACHED_JWK_KEY_FILE"
1594 return 0
1595 fi
4c2a3841 1596
4c2a3841 1597 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
166096dc 1598 _debug "RSA key"
d8ba26e6 1599 pub_exp=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
4c2a3841 1600 if [ "${#pub_exp}" = "5" ]; then
166096dc 1601 pub_exp=0$pub_exp
1602 fi
22ea4004 1603 _debug3 pub_exp "$pub_exp"
4c2a3841 1604
f9a6988e 1605 e=$(echo "$pub_exp" | _h2b | _base64)
22ea4004 1606 _debug3 e "$e"
4c2a3841 1607
d8ba26e6 1608 modulus=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
22ea4004 1609 _debug3 modulus "$modulus"
11927a76 1610 n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
4d8b99a3 1611 _debug3 n "$n"
1612
166096dc 1613 jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
22ea4004 1614 _debug3 jwk "$jwk"
4c2a3841 1615
5982f4bc 1616 JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
1617 JWK_HEADERPLACE_PART1='{"nonce": "'
c1151b0d 1618 JWK_HEADERPLACE_PART2='", "alg": "RS256"'
4c2a3841 1619 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
166096dc 1620 _debug "EC key"
d8ba26e6 1621 crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
22ea4004 1622 _debug3 crv "$crv"
f87890cb 1623 __ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2)
1624 if [ "$__ECC_KEY_LEN" = "521" ]; then
1625 __ECC_KEY_LEN=512
1626 fi
1627 _debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN"
4c2a3841 1628 if [ -z "$crv" ]; then
d22b7938 1629 _debug "Let's try ASN1 OID"
d8ba26e6 1630 crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
cae9cee2 1631 _debug3 crv_oid "$crv_oid"
d22b7938 1632 case "${crv_oid}" in
1633 "prime256v1")
4c2a3841 1634 crv="P-256"
f87890cb 1635 __ECC_KEY_LEN=256
4c2a3841 1636 ;;
d22b7938 1637 "secp384r1")
4c2a3841 1638 crv="P-384"
f87890cb 1639 __ECC_KEY_LEN=384
4c2a3841 1640 ;;
d22b7938 1641 "secp521r1")
4c2a3841 1642 crv="P-521"
f87890cb 1643 __ECC_KEY_LEN=512
4c2a3841 1644 ;;
d22b7938 1645 *)
4c2a3841 1646 _err "ECC oid : $crv_oid"
1647 return 1
1648 ;;
067d586c 1649 esac
d22b7938 1650 _debug3 crv "$crv"
1651 fi
4c2a3841 1652
d8ba26e6 1653 pubi="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
79a267ab 1654 pubi=$(_math "$pubi" + 1)
22ea4004 1655 _debug3 pubi "$pubi"
4c2a3841 1656
d8ba26e6 1657 pubj="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
79a267ab 1658 pubj=$(_math "$pubj" - 1)
22ea4004 1659 _debug3 pubj "$pubj"
4c2a3841 1660
d8ba26e6 1661 pubtext="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
22ea4004 1662 _debug3 pubtext "$pubtext"
4c2a3841 1663
95e06de5 1664 xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)"
79a267ab 1665 xlen=$(_math "$xlen" / 4)
22ea4004 1666 _debug3 xlen "$xlen"
00a50605 1667
998783eb 1668 xend=$(_math "$xlen" + 1)
f9a6988e 1669 x="$(printf "%s" "$pubtext" | cut -d : -f 2-"$xend")"
22ea4004 1670 _debug3 x "$x"
4c2a3841 1671
11927a76 1672 x64="$(printf "%s" "$x" | tr -d : | _h2b | _base64 | _url_replace)"
22ea4004 1673 _debug3 x64 "$x64"
00a50605 1674
19539575 1675 xend=$(_math "$xend" + 1)
f9a6988e 1676 y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)"
22ea4004 1677 _debug3 y "$y"
4c2a3841 1678
11927a76 1679 y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _url_replace)"
22ea4004 1680 _debug3 y64 "$y64"
4c2a3841 1681
ae2db62f 1682 jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
22ea4004 1683 _debug3 jwk "$jwk"
4c2a3841 1684
f87890cb 1685 JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
5982f4bc 1686 JWK_HEADERPLACE_PART1='{"nonce": "'
c1151b0d 1687 JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"'
166096dc 1688 else
238990a2 1689 _err "Only RSA or EC key is supported. keyfile=$keyfile"
1690 _debug2 "$(cat "$keyfile")"
166096dc 1691 return 1
1692 fi
1693
5982f4bc 1694 _debug3 JWK_HEADER "$JWK_HEADER"
ae2db62f 1695 __CACHED_JWK_KEY_FILE="$keyfile"
166096dc 1696}
fac1e367 1697
3aae1ae3 1698_time() {
1699 date -u "+%s"
1700}
fac1e367 1701
5d2c5b01 1702_utc_date() {
1703 date -u "+%Y-%m-%d %H:%M:%S"
1704}
1705
fac1e367 1706_mktemp() {
4c2a3841 1707 if _exists mktemp; then
1708 if mktemp 2>/dev/null; then
610e0f21 1709 return 0
4c2a3841 1710 elif _contains "$(mktemp 2>&1)" "-t prefix" && mktemp -t "$PROJECT_NAME" 2>/dev/null; then
5c48e139 1711 #for Mac osx
610e0f21 1712 return 0
b19ba13a 1713 fi
fac1e367 1714 fi
4c2a3841 1715 if [ -d "/tmp" ]; then
3aae1ae3 1716 echo "/tmp/${PROJECT_NAME}wefADf24sf.$(_time).tmp"
1717 return 0
4c2a3841 1718 elif [ "$LE_TEMP_DIR" ] && mkdir -p "$LE_TEMP_DIR"; then
610e0f21 1719 echo "/$LE_TEMP_DIR/wefADf24sf.$(_time).tmp"
1720 return 0
3aae1ae3 1721 fi
1722 _err "Can not create temp file."
fac1e367 1723}
1724
1725_inithttp() {
1726
4c2a3841 1727 if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
fac1e367 1728 HTTP_HEADER="$(_mktemp)"
1729 _debug2 HTTP_HEADER "$HTTP_HEADER"
1730 fi
1731
4c2a3841 1732 if [ "$__HTTP_INITIALIZED" ]; then
1733 if [ "$_ACME_CURL$_ACME_WGET" ]; then
1befee5a 1734 _debug2 "Http already initialized."
1735 return 0
1736 fi
1737 fi
4c2a3841 1738
1739 if [ -z "$_ACME_CURL" ] && _exists "curl"; then
1befee5a 1740 _ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER "
4c2a3841 1741 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
fac1e367 1742 _CURL_DUMP="$(_mktemp)"
1befee5a 1743 _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
fac1e367 1744 fi
1745
2aa75f03 1746 if [ "$CA_PATH" ]; then
1747 _ACME_CURL="$_ACME_CURL --capath $CA_PATH "
1748 elif [ "$CA_BUNDLE" ]; then
1befee5a 1749 _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
78009539
PS
1750 fi
1751
6ca5f3d8 1752 if _contains "$(curl --help 2>&1)" "--globoff"; then
1753 _ACME_CURL="$_ACME_CURL -g "
1754 fi
fac1e367 1755 fi
4c2a3841 1756
1befee5a 1757 if [ -z "$_ACME_WGET" ] && _exists "wget"; then
1758 _ACME_WGET="wget -q"
4c2a3841 1759 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
1befee5a 1760 _ACME_WGET="$_ACME_WGET -d "
fac1e367 1761 fi
2aa75f03 1762 if [ "$CA_PATH" ]; then
1763 _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
1764 elif [ "$CA_BUNDLE" ]; then
1765 _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
78009539 1766 fi
fac1e367 1767 fi
1768
177b57e1 1769 #from wget 1.14: do not skip body on 404 error
58ef6d83 1770 if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then
177b57e1 1771 _ACME_WGET="$_ACME_WGET --content-on-error "
1772 fi
1773
1befee5a 1774 __HTTP_INITIALIZED=1
fac1e367 1775
fac1e367 1776}
fac1e367 1777
9cecd525 1778# body url [needbase64] [POST|PUT|DELETE] [ContentType]
c60883ef 1779_post() {
1780 body="$1"
c1151b0d 1781 _post_url="$2"
c60883ef 1782 needbase64="$3"
a4270efa 1783 httpmethod="$4"
45e386b2 1784 _postContentType="$5"
c60883ef 1785
4c2a3841 1786 if [ -z "$httpmethod" ]; then
a4270efa 1787 httpmethod="POST"
1788 fi
1789 _debug $httpmethod
c1151b0d 1790 _debug "_post_url" "$_post_url"
30de13b4 1791 _debug2 "body" "$body"
6a66ba8a 1792 _debug2 "_postContentType" "$_postContentType"
4c2a3841 1793
fac1e367 1794 _inithttp
4c2a3841 1795
9b124070 1796 if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
1befee5a 1797 _CURL="$_ACME_CURL"
7834c252 1798 if [ "$HTTPS_INSECURE" ]; then
1799 _CURL="$_CURL --insecure "
1800 fi
5723fd11 1801 if [ "$httpmethod" = "HEAD" ]; then
1802 _CURL="$_CURL -I "
1803 fi
ec9fc8cb 1804 _debug "_CURL" "$_CURL"
4c2a3841 1805 if [ "$needbase64" ]; then
1ba4ab2b 1806 if [ "$body" ]; then
1807 if [ "$_postContentType" ]; then
1808 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)"
1809 else
1810 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)"
1811 fi
39852662 1812 else
1ba4ab2b 1813 if [ "$_postContentType" ]; then
1814 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64)"
1815 else
1816 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64)"
1817 fi
39852662 1818 fi
c60883ef 1819 else
1ba4ab2b 1820 if [ "$body" ]; then
1821 if [ "$_postContentType" ]; then
1822 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
1823 else
1824 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
1825 fi
39852662 1826 else
1ba4ab2b 1827 if [ "$_postContentType" ]; then
1828 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")"
1829 else
1830 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")"
1831 fi
39852662 1832 fi
c60883ef 1833 fi
16679b57 1834 _ret="$?"
4c2a3841 1835 if [ "$_ret" != "0" ]; then
87ab2d90 1836 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
4c2a3841 1837 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
87ab2d90 1838 _err "Here is the curl dump log:"
1839 _err "$(cat "$_CURL_DUMP")"
1840 fi
687cfcc2 1841 fi
4c2a3841 1842 elif [ "$_ACME_WGET" ]; then
7834c252 1843 _WGET="$_ACME_WGET"
1844 if [ "$HTTPS_INSECURE" ]; then
1845 _WGET="$_WGET --no-check-certificate "
1846 fi
51b4a9e3 1847 if [ "$httpmethod" = "HEAD" ]; then
1848 _WGET="$_WGET --read-timeout=3.0 --tries=2 "
1849 fi
7834c252 1850 _debug "_WGET" "$_WGET"
4c2a3841 1851 if [ "$needbase64" ]; then
1852 if [ "$httpmethod" = "POST" ]; then
ef871775 1853 if [ "$_postContentType" ]; then
1854 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
1855 else
1856 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
1857 fi
8fb9a709 1858 else
ef871775 1859 if [ "$_postContentType" ]; then
1860 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
1861 else
1862 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
1863 fi
8fb9a709 1864 fi
c60883ef 1865 else
4c2a3841 1866 if [ "$httpmethod" = "POST" ]; then
ef871775 1867 if [ "$_postContentType" ]; then
1868 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1869 else
1870 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1871 fi
51b4a9e3 1872 elif [ "$httpmethod" = "HEAD" ]; then
1873 if [ "$_postContentType" ]; then
1874 response="$($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1875 else
1876 response="$($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1877 fi
8fb9a709 1878 else
ef871775 1879 if [ "$_postContentType" ]; then
1880 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1881 else
1882 response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
1883 fi
8fb9a709 1884 fi
c60883ef 1885 fi
16679b57 1886 _ret="$?"
4c2a3841 1887 if [ "$_ret" = "8" ]; then
9f43c270 1888 _ret=0
810c129c 1889 _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
9f43c270 1890 fi
4c2a3841 1891 if [ "$_ret" != "0" ]; then
1892 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
687cfcc2 1893 fi
c60883ef 1894 _sed_i "s/^ *//g" "$HTTP_HEADER"
d0b748a4 1895 else
1896 _ret="$?"
1897 _err "Neither curl nor wget is found, can not do $httpmethod."
c60883ef 1898 fi
16679b57 1899 _debug "_ret" "$_ret"
19539575 1900 printf "%s" "$response"
16679b57 1901 return $_ret
c60883ef 1902}
1903
75da0713 1904# url getheader timeout
c60883ef 1905_get() {
a4270efa 1906 _debug GET
c60883ef 1907 url="$1"
1908 onlyheader="$2"
75da0713 1909 t="$3"
79a267ab 1910 _debug url "$url"
72f54ca6 1911 _debug "timeout=$t"
fac1e367 1912
1913 _inithttp
1914
9b124070 1915 if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
1befee5a 1916 _CURL="$_ACME_CURL"
7834c252 1917 if [ "$HTTPS_INSECURE" ]; then
1918 _CURL="$_CURL --insecure "
1919 fi
4c2a3841 1920 if [ "$t" ]; then
75da0713 1921 _CURL="$_CURL --connect-timeout $t"
1922 fi
1923 _debug "_CURL" "$_CURL"
4c2a3841 1924 if [ "$onlyheader" ]; then
f9a6988e 1925 $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url"
c60883ef 1926 else
f9a6988e 1927 $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url"
c60883ef 1928 fi
9aaf36cd 1929 ret=$?
4c2a3841 1930 if [ "$ret" != "0" ]; then
d529eb6d 1931 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $ret"
4c2a3841 1932 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
fac1e367 1933 _err "Here is the curl dump log:"
1934 _err "$(cat "$_CURL_DUMP")"
1935 fi
1936 fi
4c2a3841 1937 elif [ "$_ACME_WGET" ]; then
1befee5a 1938 _WGET="$_ACME_WGET"
7834c252 1939 if [ "$HTTPS_INSECURE" ]; then
1940 _WGET="$_WGET --no-check-certificate "
1941 fi
4c2a3841 1942 if [ "$t" ]; then
75da0713 1943 _WGET="$_WGET --timeout=$t"
1944 fi
1945 _debug "_WGET" "$_WGET"
4c2a3841 1946 if [ "$onlyheader" ]; then
f9a6988e 1947 $_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 1948 else
f9a6988e 1949 $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
c60883ef 1950 fi
9aaf36cd 1951 ret=$?
f731a4c7 1952 if [ "$ret" = "8" ]; then
39a1f1ef 1953 ret=0
810c129c 1954 _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
9f43c270 1955 fi
4c2a3841 1956 if [ "$ret" != "0" ]; then
1957 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
fac1e367 1958 fi
d0b748a4 1959 else
1960 ret=$?
1961 _err "Neither curl nor wget is found, can not do GET."
9aaf36cd 1962 fi
ec9fc8cb 1963 _debug "ret" "$ret"
c60883ef 1964 return $ret
1965}
166096dc 1966
c2c8f320 1967_head_n() {
79a267ab 1968 head -n "$1"
c2c8f320 1969}
1970
1971_tail_n() {
f9a6988e 1972 if ! tail -n "$1" 2>/dev/null; then
19ab2a29 1973 #fix for solaris
f9a6988e 1974 tail -"$1"
19ab2a29 1975 fi
c2c8f320 1976}
fac1e367 1977
166096dc 1978# url payload needbase64 keyfile
4c3b3608 1979_send_signed_request() {
1980 url=$1
1981 payload=$2
1982 needbase64=$3
166096dc 1983 keyfile=$4
4c2a3841 1984 if [ -z "$keyfile" ]; then
166096dc 1985 keyfile="$ACCOUNT_KEY_PATH"
1986 fi
f9a6988e 1987 _debug url "$url"
4c3b3608 1988 _debug payload "$payload"
4c2a3841 1989
1990 if ! _calcjwk "$keyfile"; then
166096dc 1991 return 1
1992 fi
c60883ef 1993
cc2d5946 1994 __request_conent_type="$CONTENT_TYPE_JSON"
1995
11927a76 1996 payload64=$(printf "%s" "$payload" | _base64 | _url_replace)
f9a6988e 1997 _debug3 payload64 "$payload64"
4c2a3841 1998
709a3fb0 1999 MAX_REQUEST_RETRY_TIMES=20
2000 _sleep_retry_sec=1
0bc745f6 2001 _request_retry_times=0
2002 while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
aad309ee 2003 _request_retry_times=$(_math "$_request_retry_times" + 1)
b7924ce5 2004 _debug3 _request_retry_times "$_request_retry_times"
0bc745f6 2005 if [ -z "$_CACHED_NONCE" ]; then
8f01919f 2006 _headers=""
8bd12ed0
K
2007 if [ "$ACME_NEW_NONCE" ]; then
2008 _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE"
cae50e16 2009 nonceurl="$ACME_NEW_NONCE"
1ba4ab2b 2010 if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type" >/dev/null; then
cae50e16 2011 _headers="$(cat "$HTTP_HEADER")"
e7f7e96d 2012 _debug2 _headers "$_headers"
2013 _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
cae50e16 2014 fi
2015 fi
e7f7e96d 2016 if [ -z "$_CACHED_NONCE" ]; then
33226307 2017 _debug2 "Get nonce with GET. ACME_DIRECTORY" "$ACME_DIRECTORY"
cae50e16 2018 nonceurl="$ACME_DIRECTORY"
2019 _headers="$(_get "$nonceurl" "onlyheader")"
e7f7e96d 2020 _debug2 _headers "$_headers"
2021 _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
cae50e16 2022 fi
e7f7e96d 2023 if [ -z "$_CACHED_NONCE" ] && [ "$ACME_NEW_NONCE" ]; then
2024 _debug2 "Get nonce with GET. ACME_NEW_NONCE" "$ACME_NEW_NONCE"
2025 nonceurl="$ACME_NEW_NONCE"
2026 _headers="$(_get "$nonceurl" "onlyheader")"
2027 _debug2 _headers "$_headers"
2028 _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
2029 fi
2030 _debug2 _CACHED_NONCE "$_CACHED_NONCE"
0bc745f6 2031 if [ "$?" != "0" ]; then
2032 _err "Can not connect to $nonceurl to get nonce."
2033 return 1
2034 fi
0bc745f6 2035 else
2036 _debug2 "Use _CACHED_NONCE" "$_CACHED_NONCE"
2037 fi
2038 nonce="$_CACHED_NONCE"
2039 _debug2 nonce "$nonce"
aad309ee 2040 if [ -z "$nonce" ]; then
2041 _info "Could not get nonce, let's try again."
2042 _sleep 2
2043 continue
2044 fi
c1151b0d 2045 if [ "$ACME_VERSION" = "2" ]; then
114f2a14 2046 if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then
2047 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
2048 elif [ "$url" = "$ACME_REVOKE_CERT" ] && [ "$keyfile" != "$ACCOUNT_KEY_PATH" ]; then
c1151b0d 2049 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
2050 else
6b798b01 2051 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}'
c1151b0d 2052 fi
2053 else
2054 protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
2055 fi
0bc745f6 2056 _debug3 protected "$protected"
a272ee4f 2057
0bc745f6 2058 protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
2059 _debug3 protected64 "$protected64"
4c2a3841 2060
0bc745f6 2061 if ! _sig_t="$(printf "%s" "$protected64.$payload64" | _sign "$keyfile" "sha256")"; then
2062 _err "Sign request failed."
2063 return 1
2064 fi
2065 _debug3 _sig_t "$_sig_t"
166096dc 2066
0bc745f6 2067 sig="$(printf "%s" "$_sig_t" | _url_replace)"
2068 _debug3 sig "$sig"
4c2a3841 2069
8bd12ed0 2070 body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
0bc745f6 2071 _debug3 body "$body"
4c2a3841 2072
6a66ba8a 2073 response="$(_post "$body" "$url" "$needbase64" "POST" "$__request_conent_type")"
0bc745f6 2074 _CACHED_NONCE=""
bbbdcb09 2075
0bc745f6 2076 if [ "$?" != "0" ]; then
2077 _err "Can not post to $url"
2078 return 1
2079 fi
4c3b3608 2080
64802502 2081 responseHeaders="$(cat "$HTTP_HEADER")"
0bc745f6 2082 _debug2 responseHeaders "$responseHeaders"
f2acdd27 2083
0bc745f6 2084 code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
2085 _debug code "$code"
4c2a3841 2086
f2acdd27 2087 _debug2 original "$response"
0712e989 2088 if echo "$responseHeaders" | grep -i "Content-Type: *application/json" >/dev/null 2>&1; then
f2acdd27 2089 response="$(echo "$response" | _normalizeJson)"
8148bfea 2090 fi
f2acdd27 2091 _debug2 response "$response"
36309e6d 2092
f2acdd27 2093 _CACHED_NONCE="$(echo "$responseHeaders" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
2094
a0ec5b18 2095 if ! _startswith "$code" "2"; then
f2acdd27 2096 _body="$response"
2097 if [ "$needbase64" ]; then
2098 _body="$(echo "$_body" | _dbase64 multiline)"
2099 _debug3 _body "$_body"
2100 fi
2101
2102 if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then
2103 _info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds."
2104 _CACHED_NONCE=""
2105 _sleep $_sleep_retry_sec
2106 continue
2107 fi
0bc745f6 2108 fi
a44ea0dd 2109 return 0
0bc745f6 2110 done
a44ea0dd 2111 _info "Giving up sending to CA server after $MAX_REQUEST_RETRY_TIMES retries."
2112 return 1
4c3b3608 2113
4c3b3608 2114}
4c3b3608 2115
2116#setopt "file" "opt" "=" "value" [";"]
2117_setopt() {
2118 __conf="$1"
2119 __opt="$2"
2120 __sep="$3"
2121 __val="$4"
2122 __end="$5"
4c2a3841 2123 if [ -z "$__opt" ]; then
2124 _usage usage: _setopt '"file" "opt" "=" "value" [";"]'
4c3b3608 2125 return
2126 fi
4c2a3841 2127 if [ ! -f "$__conf" ]; then
4c3b3608 2128 touch "$__conf"
2129 fi
2130
4c2a3841 2131 if grep -n "^$__opt$__sep" "$__conf" >/dev/null; then
22ea4004 2132 _debug3 OK
4c2a3841 2133 if _contains "$__val" "&"; then
79a267ab 2134 __val="$(echo "$__val" | sed 's/&/\\&/g')"
4c3b3608 2135 fi
79a267ab 2136 text="$(cat "$__conf")"
52f8b787 2137 printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
4c3b3608 2138
4c2a3841 2139 elif grep -n "^#$__opt$__sep" "$__conf" >/dev/null; then
2140 if _contains "$__val" "&"; then
79a267ab 2141 __val="$(echo "$__val" | sed 's/&/\\&/g')"
4c3b3608 2142 fi
79a267ab 2143 text="$(cat "$__conf")"
52f8b787 2144 printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
4c3b3608 2145
2146 else
22ea4004 2147 _debug3 APP
4c2a3841 2148 echo "$__opt$__sep$__val$__end" >>"$__conf"
4c3b3608 2149 fi
1efb2085 2150 _debug3 "$(grep -n "^$__opt$__sep" "$__conf")"
4c3b3608 2151}
2152
7690f73e 2153#_save_conf file key value base64encode
8a29fbc8 2154#save to conf
2155_save_conf() {
2156 _s_c_f="$1"
2157 _sdkey="$2"
2158 _sdvalue="$3"
7690f73e 2159 _b64encode="$4"
53c01882 2160 if [ "$_sdvalue" ] && [ "$_b64encode" ]; then
7690f73e 2161 _sdvalue="${B64CONF_START}$(printf "%s" "${_sdvalue}" | _base64)${B64CONF_END}"
2162 fi
4c2a3841 2163 if [ "$_s_c_f" ]; then
8a29fbc8 2164 _setopt "$_s_c_f" "$_sdkey" "=" "'$_sdvalue'"
4d2f38b0 2165 else
8a29fbc8 2166 _err "config file is empty, can not save $_sdkey=$_sdvalue"
4d2f38b0 2167 fi
2168}
2169
8a29fbc8 2170#_clear_conf file key
2171_clear_conf() {
2172 _c_c_f="$1"
2173 _sdkey="$2"
4c2a3841 2174 if [ "$_c_c_f" ]; then
20ea8591 2175 _conf_data="$(cat "$_c_c_f")"
fa574fe8 2176 echo "$_conf_data" | sed "s/^$_sdkey *=.*$//" >"$_c_c_f"
4c3b3608 2177 else
8a29fbc8 2178 _err "config file is empty, can not clear"
4c3b3608 2179 fi
2180}
2181
8a29fbc8 2182#_read_conf file key
2183_read_conf() {
2184 _r_c_f="$1"
2185 _sdkey="$2"
4c2a3841 2186 if [ -f "$_r_c_f" ]; then
0cfeee4d 2187 _sdv="$(
2188 eval "$(grep "^$_sdkey *=" "$_r_c_f")"
2189 eval "printf \"%s\" \"\$$_sdkey\""
c97e43dc 2190 )"
7690f73e 2191 if _startswith "$_sdv" "${B64CONF_START}" && _endswith "$_sdv" "${B64CONF_END}"; then
2192 _sdv="$(echo "$_sdv" | sed "s/${B64CONF_START}//" | sed "s/${B64CONF_END}//" | _dbase64)"
2193 fi
2194 printf "%s" "$_sdv"
61623d22 2195 else
57e58ce7 2196 _debug "config file is empty, can not read $_sdkey"
4c3b3608 2197 fi
4c3b3608 2198}
2199
7690f73e 2200#_savedomainconf key value base64encode
4c3b3608 2201#save to domain.conf
2202_savedomainconf() {
7690f73e 2203 _save_conf "$DOMAIN_CONF" "$@"
4d2f38b0 2204}
2205
2206#_cleardomainconf key
2207_cleardomainconf() {
8a29fbc8 2208 _clear_conf "$DOMAIN_CONF" "$1"
4c3b3608 2209}
2210
61623d22 2211#_readdomainconf key
2212_readdomainconf() {
8a29fbc8 2213 _read_conf "$DOMAIN_CONF" "$1"
61623d22 2214}
2215
aec66362 2216#key value base64encode
2217_savedeployconf() {
2218 _savedomainconf "SAVED_$1" "$2" "$3"
2219 #remove later
dc5eda7e 2220 _cleardomainconf "$1"
aec66362 2221}
2222
2223#key
2224_getdeployconf() {
2225 _rac_key="$1"
2e3ddd3a 2226 _rac_value="$(eval echo \$"$_rac_key")"
2227 if [ "$_rac_value" ]; then
2228 if _startswith "$_rac_value" '"' && _endswith "$_rac_value" '"'; then
2229 _debug2 "trim quotation marks"
465ece5d 2230 eval "export $_rac_key=$_rac_value"
2e3ddd3a 2231 fi
aec66362 2232 return 0 # do nothing
2233 fi
2234 _saved=$(_readdomainconf "SAVED_$_rac_key")
22f9a3b4 2235 eval "export $_rac_key=\"$_saved\""
aec66362 2236}
2237
7690f73e 2238#_saveaccountconf key value base64encode
4c3b3608 2239_saveaccountconf() {
7690f73e 2240 _save_conf "$ACCOUNT_CONF_PATH" "$@"
4c3b3608 2241}
2242
7690f73e 2243#key value base64encode
fcdf41ba 2244_saveaccountconf_mutable() {
7690f73e 2245 _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2" "$3"
fcdf41ba 2246 #remove later
2247 _clearaccountconf "$1"
2248}
2249
2250#key
2251_readaccountconf() {
2252 _read_conf "$ACCOUNT_CONF_PATH" "$1"
2253}
2254
2255#key
2256_readaccountconf_mutable() {
2257 _rac_key="$1"
2258 _readaccountconf "SAVED_$_rac_key"
2259}
2260
fac1e367 2261#_clearaccountconf key
2262_clearaccountconf() {
8a29fbc8 2263 _clear_conf "$ACCOUNT_CONF_PATH" "$1"
2264}
2265
2266#_savecaconf key value
2267_savecaconf() {
2268 _save_conf "$CA_CONF" "$1" "$2"
2269}
2270
2271#_readcaconf key
2272_readcaconf() {
2273 _read_conf "$CA_CONF" "$1"
2274}
2275
2276#_clearaccountconf key
2277_clearcaconf() {
2278 _clear_conf "$CA_CONF" "$1"
fac1e367 2279}
2280
0463b5d6 2281# content localaddress
4c3b3608 2282_startserver() {
2283 content="$1"
0463b5d6 2284 ncaddr="$2"
e7f7e96d 2285 _debug "content" "$content"
0463b5d6 2286 _debug "ncaddr" "$ncaddr"
2287
6fc1447f 2288 _debug "startserver: $$"
4c2a3841 2289
39c8f79f 2290 _debug Le_HTTPPort "$Le_HTTPPort"
6ae0f7f5 2291 _debug Le_Listen_V4 "$Le_Listen_V4"
2292 _debug Le_Listen_V6 "$Le_Listen_V6"
4c2a3841 2293
3794b5cb 2294 _NC="socat"
4c2a3841 2295 if [ "$Le_Listen_V4" ]; then
6ae0f7f5 2296 _NC="$_NC -4"
4c2a3841 2297 elif [ "$Le_Listen_V6" ]; then
6ae0f7f5 2298 _NC="$_NC -6"
2299 fi
4c2a3841 2300
9ad7ac63 2301 if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
5c568d69 2302 _NC="$_NC -d -d -v"
2303 fi
2304
9134b6ea
HC
2305 SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork
2306
2307 #Adding bind to local-address
d84665cb 2308 if [ "$ncaddr" ]; then
9ad7ac63 2309 SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}"
9134b6ea
HC
2310 fi
2311
e7f7e96d 2312 _content_len="$(printf "%s" "$content" | wc -c)"
2313 _debug _content_len "$_content_len"
5c568d69 2314 _debug "_NC" "$_NC $SOCAT_OPTIONS"
e7f7e96d 2315 $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; \
2316echo 'HTTP/1.0 200 OK'; \
2317echo 'Content-Length\: $_content_len'; \
2318echo ''; \
2ffd8637 2319printf -- '$content';" &
3794b5cb 2320 serverproc="$!"
4c3b3608 2321}
2322
4c2a3841 2323_stopserver() {
4c3b3608 2324 pid="$1"
6fc1447f 2325 _debug "pid" "$pid"
4c2a3841 2326 if [ -z "$pid" ]; then
6fc1447f 2327 return
2328 fi
e22bcf7c 2329
3794b5cb 2330 kill $pid
2331
4c3b3608 2332}
2333
fdcb6b72 2334# sleep sec
2335_sleep() {
2336 _sleep_sec="$1"
4c2a3841 2337 if [ "$__INTERACTIVE" ]; then
fdcb6b72 2338 _sleep_c="$_sleep_sec"
4c2a3841 2339 while [ "$_sleep_c" -ge "0" ]; do
c583d6bb 2340 printf "\r \r"
fdcb6b72 2341 __green "$_sleep_c"
79a267ab 2342 _sleep_c="$(_math "$_sleep_c" - 1)"
fdcb6b72 2343 sleep 1
2344 done
c583d6bb 2345 printf "\r"
fdcb6b72 2346 else
2347 sleep "$_sleep_sec"
2348 fi
2349}
e22bcf7c 2350
08681f4a 2351# _starttlsserver san_a san_b port content _ncaddr acmeValidationv1
e22bcf7c 2352_starttlsserver() {
2353 _info "Starting tls server."
2354 san_a="$1"
2355 san_b="$2"
2356 port="$3"
2357 content="$4"
6ae0f7f5 2358 opaddr="$5"
08681f4a 2359 acmeValidationv1="$6"
4c2a3841 2360
e22bcf7c 2361 _debug san_a "$san_a"
2362 _debug san_b "$san_b"
2363 _debug port "$port"
08681f4a 2364 _debug acmeValidationv1 "$acmeValidationv1"
4c2a3841 2365
e22bcf7c 2366 #create key TLS_KEY
4c2a3841 2367 if ! _createkey "2048" "$TLS_KEY"; then
e22bcf7c 2368 _err "Create tls validation key error."
2369 return 1
2370 fi
4c2a3841 2371
e22bcf7c 2372 #create csr
2373 alt="$san_a"
4c2a3841 2374 if [ "$san_b" ]; then
e22bcf7c 2375 alt="$alt,$san_b"
2376 fi
08681f4a 2377 if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$acmeValidationv1"; then
e22bcf7c 2378 _err "Create tls validation csr error."
2379 return 1
2380 fi
4c2a3841 2381
e22bcf7c 2382 #self signed
4c2a3841 2383 if ! _signcsr "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT"; then
e22bcf7c 2384 _err "Create tls validation cert error."
2385 return 1
2386 fi
4c2a3841 2387
5f6e3da7 2388 __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY "
2389 if [ "$opaddr" ]; then
2390 __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
2391 else
2392 __S_OPENSSL="$__S_OPENSSL -accept $port"
2393 fi
6ae0f7f5 2394
2395 _debug Le_Listen_V4 "$Le_Listen_V4"
2396 _debug Le_Listen_V6 "$Le_Listen_V6"
4c2a3841 2397 if [ "$Le_Listen_V4" ]; then
6ae0f7f5 2398 __S_OPENSSL="$__S_OPENSSL -4"
4c2a3841 2399 elif [ "$Le_Listen_V6" ]; then
6ae0f7f5 2400 __S_OPENSSL="$__S_OPENSSL -6"
2401 fi
4c2a3841 2402
08681f4a 2403 if [ "$acmeValidationv1" ]; then
2404 __S_OPENSSL="$__S_OPENSSL -alpn acme-tls/1"
2405 fi
2406
6ae0f7f5 2407 _debug "$__S_OPENSSL"
5f6e3da7 2408 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
2409 $__S_OPENSSL -tlsextdebug &
2410 else
2411 $__S_OPENSSL >/dev/null 2>&1 &
2412 fi
331c4bb6 2413
e22bcf7c 2414 serverproc="$!"
5dbf664a 2415 sleep 1
d5ec5f80 2416 _debug serverproc "$serverproc"
e22bcf7c 2417}
2418
18e46962 2419#file
2420_readlink() {
2421 _rf="$1"
2422 if ! readlink -f "$_rf" 2>/dev/null; then
6c4cc357 2423 if _startswith "$_rf" "/"; then
2424 echo "$_rf"
7da50703 2425 return 0
2426 fi
6c4cc357 2427 echo "$(pwd)/$_rf" | _conapath
18e46962 2428 fi
2429}
2430
6c4cc357 2431_conapath() {
2432 sed "s#/\./#/#g"
2433}
2434
5ea6e9c9 2435__initHome() {
4c2a3841 2436 if [ -z "$_SCRIPT_HOME" ]; then
2437 if _exists readlink && _exists dirname; then
66990cf8 2438 _debug "Lets find script dir."
f3e4cea3 2439 _debug "_SCRIPT_" "$_SCRIPT_"
18e46962 2440 _script="$(_readlink "$_SCRIPT_")"
f3e4cea3 2441 _debug "_script" "$_script"
2442 _script_home="$(dirname "$_script")"
2443 _debug "_script_home" "$_script_home"
4c2a3841 2444 if [ -d "$_script_home" ]; then
f3e4cea3 2445 _SCRIPT_HOME="$_script_home"
2446 else
2447 _err "It seems the script home is not correct:$_script_home"
2448 fi
2449 fi
2450 fi
2451
219e9115 2452 # if [ -z "$LE_WORKING_DIR" ]; then
2453 # if [ -f "$DEFAULT_INSTALL_HOME/account.conf" ]; then
2454 # _debug "It seems that $PROJECT_NAME is already installed in $DEFAULT_INSTALL_HOME"
2455 # LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
2456 # else
2457 # LE_WORKING_DIR="$_SCRIPT_HOME"
2458 # fi
2459 # fi
4c2a3841 2460
2461 if [ -z "$LE_WORKING_DIR" ]; then
f3e4cea3 2462 _debug "Using default home:$DEFAULT_INSTALL_HOME"
2463 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
2464 fi
7da50703 2465 export LE_WORKING_DIR
f3e4cea3 2466
f5b546b3 2467 if [ -z "$LE_CONFIG_HOME" ]; then
2468 LE_CONFIG_HOME="$LE_WORKING_DIR"
27dbe77f 2469 fi
f5b546b3 2470 _debug "Using config home:$LE_CONFIG_HOME"
2471 export LE_CONFIG_HOME
27dbe77f 2472
f5b546b3 2473 _DEFAULT_ACCOUNT_CONF_PATH="$LE_CONFIG_HOME/account.conf"
d53289d7 2474
4c2a3841 2475 if [ -z "$ACCOUNT_CONF_PATH" ]; then
2476 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]; then
8663fb7e 2477 . "$_DEFAULT_ACCOUNT_CONF_PATH"
635695ec 2478 fi
d53289d7 2479 fi
4c2a3841 2480
2481 if [ -z "$ACCOUNT_CONF_PATH" ]; then
d53289d7 2482 ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
4c3b3608 2483 fi
f8f53a6b 2484 _debug3 ACCOUNT_CONF_PATH "$ACCOUNT_CONF_PATH"
f5b546b3 2485 DEFAULT_LOG_FILE="$LE_CONFIG_HOME/$PROJECT_NAME.log"
4c2a3841 2486
f5b546b3 2487 DEFAULT_CA_HOME="$LE_CONFIG_HOME/ca"
4c2a3841 2488
2489 if [ -z "$LE_TEMP_DIR" ]; then
f5b546b3 2490 LE_TEMP_DIR="$LE_CONFIG_HOME/tmp"
610e0f21 2491 fi
5ea6e9c9 2492}
2493
48d9a8c1 2494#server
2495_initAPI() {
2496 _api_server="${1:-$ACME_DIRECTORY}"
2497 _debug "_init api for server: $_api_server"
4cee14f3 2498
cae50e16 2499 if [ -z "$ACME_NEW_ACCOUNT" ]; then
48d9a8c1 2500 response=$(_get "$_api_server")
2501 if [ "$?" != "0" ]; then
2502 _debug2 "response" "$response"
2503 _err "Can not init api."
2504 return 1
2505 fi
2506 _debug2 "response" "$response"
2507
2508 ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3)
c1151b0d 2509 if [ -z "$ACME_KEY_CHANGE" ]; then
2510 ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3)
2511 fi
48d9a8c1 2512 export ACME_KEY_CHANGE
2513
2514 ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3)
c1151b0d 2515 if [ -z "$ACME_NEW_AUTHZ" ]; then
2516 ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3)
2517 fi
48d9a8c1 2518 export ACME_NEW_AUTHZ
2519
cae50e16 2520 ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3)
a71eba07 2521 ACME_NEW_ORDER_RES="new-cert"
cae50e16 2522 if [ -z "$ACME_NEW_ORDER" ]; then
2523 ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3)
a71eba07 2524 ACME_NEW_ORDER_RES="new-order"
c1151b0d 2525 if [ -z "$ACME_NEW_ORDER" ]; then
2526 ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3)
2527 fi
cae50e16 2528 fi
2529 export ACME_NEW_ORDER
a71eba07 2530 export ACME_NEW_ORDER_RES
48d9a8c1 2531
cae50e16 2532 ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-reg" *: *"[^"]*"' | cut -d '"' -f 3)
a71eba07 2533 ACME_NEW_ACCOUNT_RES="new-reg"
cae50e16 2534 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2535 ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3)
a71eba07 2536 ACME_NEW_ACCOUNT_RES="new-account"
c1151b0d 2537 if [ -z "$ACME_NEW_ACCOUNT" ]; then
2538 ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3)
2539 if [ "$ACME_NEW_ACCOUNT" ]; then
2540 export ACME_VERSION=2
2541 fi
2542 fi
cae50e16 2543 fi
2544 export ACME_NEW_ACCOUNT
a71eba07 2545 export ACME_NEW_ACCOUNT_RES
48d9a8c1 2546
2547 ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3)
c1151b0d 2548 if [ -z "$ACME_REVOKE_CERT" ]; then
2549 ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3)
2550 fi
48d9a8c1 2551 export ACME_REVOKE_CERT
2552
cae50e16 2553 ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3)
c1151b0d 2554 if [ -z "$ACME_NEW_NONCE" ]; then
2555 ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3)
2556 fi
cae50e16 2557 export ACME_NEW_NONCE
4249e13e 2558
f87890cb 2559 ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3)
c1151b0d 2560 if [ -z "$ACME_AGREEMENT" ]; then
2561 ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3)
2562 fi
f87890cb 2563 export ACME_AGREEMENT
cae50e16 2564
f87890cb 2565 _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
2566 _debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
2567 _debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
2568 _debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
2569 _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
2570 _debug "ACME_AGREEMENT" "$ACME_AGREEMENT"
c1151b0d 2571 _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE"
2572 _debug "ACME_VERSION" "$ACME_VERSION"
48d9a8c1 2573
f87890cb 2574 fi
48d9a8c1 2575}
2576
3281043e 2577#[domain] [keylength or isEcc flag]
5ea6e9c9 2578_initpath() {
cd9fb3b6 2579 domain="$1"
2580 _ilength="$2"
5ea6e9c9 2581
2582 __initHome
2583
4c2a3841 2584 if [ -f "$ACCOUNT_CONF_PATH" ]; then
8663fb7e 2585 . "$ACCOUNT_CONF_PATH"
4c3b3608 2586 fi
2587
bd04638d 2588 if [ "$_ACME_IN_CRON" ]; then
4c2a3841 2589 if [ ! "$_USER_PATH_EXPORTED" ]; then
281aa349 2590 _USER_PATH_EXPORTED=1
2591 export PATH="$USER_PATH:$PATH"
2592 fi
2593 fi
4c2a3841 2594
2595 if [ -z "$CA_HOME" ]; then
5c48e139 2596 CA_HOME="$DEFAULT_CA_HOME"
2597 fi
281aa349 2598
48d9a8c1 2599 if [ -z "$ACME_DIRECTORY" ]; then
b3a801df 2600 if [ "$STAGE" ]; then
2601 ACME_DIRECTORY="$DEFAULT_STAGING_CA"
bd04638d 2602 _info "Using ACME_DIRECTORY: $ACME_DIRECTORY"
4c3b3608 2603 else
b3a801df 2604 default_acme_server=$(_readaccountconf "DEFAULT_ACME_SERVER")
2605 _debug default_acme_server "$default_acme_server"
2606 if [ "$default_acme_server" ]; then
2607 ACME_DIRECTORY="$default_acme_server"
737e9e48 2608 else
b3a801df 2609 ACME_DIRECTORY="$DEFAULT_CA"
737e9e48 2610 fi
4c2a3841 2611 fi
4c3b3608 2612 fi
4c2a3841 2613
66444663 2614 _debug ACME_DIRECTORY "$ACME_DIRECTORY"
98394f99 2615 _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
48d9a8c1 2616 _debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
2617
2618 CA_DIR="$CA_HOME/$_ACME_SERVER_HOST"
4c2a3841 2619
5c48e139 2620 _DEFAULT_CA_CONF="$CA_DIR/ca.conf"
4c2a3841 2621
2622 if [ -z "$CA_CONF" ]; then
5c48e139 2623 CA_CONF="$_DEFAULT_CA_CONF"
2624 fi
c4236e58 2625 _debug3 CA_CONF "$CA_CONF"
4c2a3841 2626
2627 if [ -f "$CA_CONF" ]; then
5c48e139 2628 . "$CA_CONF"
2629 fi
2630
4c2a3841 2631 if [ -z "$ACME_DIR" ]; then
4c3b3608 2632 ACME_DIR="/home/.acme"
2633 fi
4c2a3841 2634
2635 if [ -z "$APACHE_CONF_BACKUP_DIR" ]; then
f5b546b3 2636 APACHE_CONF_BACKUP_DIR="$LE_CONFIG_HOME"
4c3b3608 2637 fi
4c2a3841 2638
2639 if [ -z "$USER_AGENT" ]; then
bbbdcb09 2640 USER_AGENT="$DEFAULT_USER_AGENT"
2641 fi
4c2a3841 2642
2643 if [ -z "$HTTP_HEADER" ]; then
f5b546b3 2644 HTTP_HEADER="$LE_CONFIG_HOME/http.header"
933c169d 2645 fi
b2817897 2646
5c48e139 2647 _OLD_ACCOUNT_KEY="$LE_WORKING_DIR/account.key"
2648 _OLD_ACCOUNT_JSON="$LE_WORKING_DIR/account.json"
4c2a3841 2649
5c48e139 2650 _DEFAULT_ACCOUNT_KEY_PATH="$CA_DIR/account.key"
2651 _DEFAULT_ACCOUNT_JSON_PATH="$CA_DIR/account.json"
4c2a3841 2652 if [ -z "$ACCOUNT_KEY_PATH" ]; then
b2817897 2653 ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
4c3b3608 2654 fi
4c2a3841 2655
2656 if [ -z "$ACCOUNT_JSON_PATH" ]; then
5c48e139 2657 ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH"
2658 fi
4c2a3841 2659
f5b546b3 2660 _DEFAULT_CERT_HOME="$LE_CONFIG_HOME"
4c2a3841 2661 if [ -z "$CERT_HOME" ]; then
a79b26af
RD
2662 CERT_HOME="$_DEFAULT_CERT_HOME"
2663 fi
2664
77f1ea40 2665 if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ]; then
851fedf7 2666 ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
a746139c 2667 fi
2668
cd9fb3b6 2669 if [ -z "$domain" ]; then
4c3b3608 2670 return 0
2671 fi
4c2a3841 2672
4c2a3841 2673 if [ -z "$DOMAIN_PATH" ]; then
43822d37 2674 domainhome="$CERT_HOME/$domain"
2675 domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
4c2a3841 2676
4c3b3608 2677 DOMAIN_PATH="$domainhome"
4c2a3841 2678
2679 if _isEccKey "$_ilength"; then
43822d37 2680 DOMAIN_PATH="$domainhomeecc"
2681 else
4c2a3841 2682 if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ]; then
6d4e903b 2683 _info "The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert."
43822d37 2684 fi
2685 fi
2686 _debug DOMAIN_PATH "$DOMAIN_PATH"
4c3b3608 2687 fi
4c2a3841 2688
fd72cced 2689 if [ -z "$DOMAIN_BACKUP_PATH" ]; then
d88f8e86 2690 DOMAIN_BACKUP_PATH="$DOMAIN_PATH/backup"
fd72cced 2691 fi
2692
4c2a3841 2693 if [ -z "$DOMAIN_CONF" ]; then
43822d37 2694 DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
4c3b3608 2695 fi
4c2a3841 2696
2697 if [ -z "$DOMAIN_SSL_CONF" ]; then
0c9546cc 2698 DOMAIN_SSL_CONF="$DOMAIN_PATH/$domain.csr.conf"
4c3b3608 2699 fi
4c2a3841 2700
2701 if [ -z "$CSR_PATH" ]; then
43822d37 2702 CSR_PATH="$DOMAIN_PATH/$domain.csr"
4c3b3608 2703 fi
4c2a3841 2704 if [ -z "$CERT_KEY_PATH" ]; then
43822d37 2705 CERT_KEY_PATH="$DOMAIN_PATH/$domain.key"
4c3b3608 2706 fi
4c2a3841 2707 if [ -z "$CERT_PATH" ]; then
43822d37 2708 CERT_PATH="$DOMAIN_PATH/$domain.cer"
4c3b3608 2709 fi
4c2a3841 2710 if [ -z "$CA_CERT_PATH" ]; then
43822d37 2711 CA_CERT_PATH="$DOMAIN_PATH/ca.cer"
4c3b3608 2712 fi
4c2a3841 2713 if [ -z "$CERT_FULLCHAIN_PATH" ]; then
43822d37 2714 CERT_FULLCHAIN_PATH="$DOMAIN_PATH/fullchain.cer"
caf1fc10 2715 fi
4c2a3841 2716 if [ -z "$CERT_PFX_PATH" ]; then
43822d37 2717 CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
ac2d5123 2718 fi
4410226d 2719 if [ -z "$CERT_PKCS8_PATH" ]; then
2720 CERT_PKCS8_PATH="$DOMAIN_PATH/$domain.pkcs8"
2721 fi
4c2a3841 2722
2723 if [ -z "$TLS_CONF" ]; then
f94433e5 2724 TLS_CONF="$DOMAIN_PATH/tls.validation.conf"
e22bcf7c 2725 fi
4c2a3841 2726 if [ -z "$TLS_CERT" ]; then
f94433e5 2727 TLS_CERT="$DOMAIN_PATH/tls.validation.cert"
e22bcf7c 2728 fi
4c2a3841 2729 if [ -z "$TLS_KEY" ]; then
f94433e5 2730 TLS_KEY="$DOMAIN_PATH/tls.validation.key"
e22bcf7c 2731 fi
4c2a3841 2732 if [ -z "$TLS_CSR" ]; then
f94433e5 2733 TLS_CSR="$DOMAIN_PATH/tls.validation.csr"
e22bcf7c 2734 fi
4c2a3841 2735
4c3b3608 2736}
2737
610e0f21 2738_exec() {
4c2a3841 2739 if [ -z "$_EXEC_TEMP_ERR" ]; then
610e0f21 2740 _EXEC_TEMP_ERR="$(_mktemp)"
2741 fi
2742
4c2a3841 2743 if [ "$_EXEC_TEMP_ERR" ]; then
3e5b1024 2744 eval "$@ 2>>$_EXEC_TEMP_ERR"
610e0f21 2745 else
3e5b1024 2746 eval "$@"
610e0f21 2747 fi
2748}
2749
2750_exec_err() {
3e5b1024 2751 [ "$_EXEC_TEMP_ERR" ] && _err "$(cat "$_EXEC_TEMP_ERR")" && echo "" >"$_EXEC_TEMP_ERR"
610e0f21 2752}
4c3b3608 2753
2754_apachePath() {
c3dd3ef0 2755 _APACHECTL="apachectl"
4c2a3841 2756 if ! _exists apachectl; then
2757 if _exists apache2ctl; then
2758 _APACHECTL="apache2ctl"
e4a19585 2759 else
bc96082f 2760 _err "'apachectl not found. It seems that apache is not installed, or you are not root user.'"
e4a19585 2761 _err "Please use webroot mode to try again."
2762 return 1
2763 fi
80a0a7b5 2764 fi
4c2a3841 2765
2766 if ! _exec $_APACHECTL -V >/dev/null; then
610e0f21 2767 _exec_err
2768 return 1
2769 fi
4c2a3841 2770
2771 if [ "$APACHE_HTTPD_CONF" ]; then
5be1449d 2772 _saveaccountconf APACHE_HTTPD_CONF "$APACHE_HTTPD_CONF"
2773 httpdconf="$APACHE_HTTPD_CONF"
79a267ab 2774 httpdconfname="$(basename "$httpdconfname")"
d62ee940 2775 else
4c2a3841 2776 httpdconfname="$($_APACHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"')"
5be1449d 2777 _debug httpdconfname "$httpdconfname"
4c2a3841 2778
2779 if [ -z "$httpdconfname" ]; then
5be1449d 2780 _err "Can not read apache config file."
2781 return 1
2782 fi
4c2a3841 2783
2784 if _startswith "$httpdconfname" '/'; then
5be1449d 2785 httpdconf="$httpdconfname"
79a267ab 2786 httpdconfname="$(basename "$httpdconfname")"
5be1449d 2787 else
4c2a3841 2788 httpdroot="$($_APACHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"')"
5be1449d 2789 _debug httpdroot "$httpdroot"
2790 httpdconf="$httpdroot/$httpdconfname"
79a267ab 2791 httpdconfname="$(basename "$httpdconfname")"
5be1449d 2792 fi
d62ee940 2793 fi
78768e98 2794 _debug httpdconf "$httpdconf"
8f63baf7 2795 _debug httpdconfname "$httpdconfname"
4c2a3841 2796 if [ ! -f "$httpdconf" ]; then
78768e98 2797 _err "Apache Config file not found" "$httpdconf"
4c3b3608 2798 return 1
2799 fi
2800 return 0
2801}
2802
2803_restoreApache() {
4c2a3841 2804 if [ -z "$usingApache" ]; then
4c3b3608 2805 return 0
2806 fi
2807 _initpath
4c2a3841 2808 if ! _apachePath; then
4c3b3608 2809 return 1
2810 fi
4c2a3841 2811
2812 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]; then
4c3b3608 2813 _debug "No config file to restore."
2814 return 0
2815 fi
4c2a3841 2816
2817 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf"
5ef501c5 2818 _debug "Restored: $httpdconf."
4c2a3841 2819 if ! _exec $_APACHECTL -t; then
610e0f21 2820 _exec_err
4c3b3608 2821 _err "Sorry, restore apache config error, please contact me."
4c2a3841 2822 return 1
4c3b3608 2823 fi
5ef501c5 2824 _debug "Restored successfully."
4c3b3608 2825 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
4c2a3841 2826 return 0
4c3b3608 2827}
2828
2829_setApache() {
2830 _initpath
4c2a3841 2831 if ! _apachePath; then
4c3b3608 2832 return 1
2833 fi
2834
5fc5016d 2835 #test the conf first
869578ce 2836 _info "Checking if there is an error in the apache config file before starting."
4c2a3841 2837
44edb2bd 2838 if ! _exec "$_APACHECTL" -t >/dev/null; then
610e0f21 2839 _exec_err
2840 _err "The apache config file has error, please fix it first, then try again."
869578ce 2841 _err "Don't worry, there is nothing changed to your system."
4c2a3841 2842 return 1
5fc5016d 2843 else
2844 _info "OK"
2845 fi
4c2a3841 2846
4c3b3608 2847 #backup the conf
5778811a 2848 _debug "Backup apache config file" "$httpdconf"
4c2a3841 2849 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/"; then
869578ce 2850 _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
61a48a5b 2851 _err "This might be a bug of $PROJECT_NAME , please report issue: $PROJECT"
8f63baf7 2852 return 1
2853 fi
4c3b3608 2854 _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
2855 _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
329174b6 2856 _info "The backup file will be deleted on success, just forget it."
4c2a3841 2857
4c3b3608 2858 #add alias
4c2a3841 2859
2860 apacheVer="$($_APACHECTL -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2)"
b09d597c 2861 _debug "apacheVer" "$apacheVer"
94787d53 2862 apacheMajor="$(echo "$apacheVer" | cut -d . -f 1)"
b09d597c 2863 apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)"
2864
94787d53 2865 if [ "$apacheVer" ] && [ "$apacheMajor$apacheMinor" -ge "24" ]; then
b09d597c 2866 echo "
4c3b3608 2867Alias /.well-known/acme-challenge $ACME_DIR
2868
2869<Directory $ACME_DIR >
2870Require all granted
b09d597c 2871</Directory>
4c2a3841 2872 " >>"$httpdconf"
b09d597c 2873 else
2874 echo "
2875Alias /.well-known/acme-challenge $ACME_DIR
2876
2877<Directory $ACME_DIR >
2878Order allow,deny
2879Allow from all
4c3b3608 2880</Directory>
4c2a3841 2881 " >>"$httpdconf"
b09d597c 2882 fi
2883
4c2a3841 2884 _msg="$($_APACHECTL -t 2>&1)"
2885 if [ "$?" != "0" ]; then
5fc5016d 2886 _err "Sorry, apache config error"
4c2a3841 2887 if _restoreApache; then
869578ce 2888 _err "The apache config file is restored."
5fc5016d 2889 else
869578ce 2890 _err "Sorry, The apache config file can not be restored, please report bug."
5fc5016d 2891 fi
4c2a3841 2892 return 1
4c3b3608 2893 fi
4c2a3841 2894
2895 if [ ! -d "$ACME_DIR" ]; then
4c3b3608 2896 mkdir -p "$ACME_DIR"
2897 chmod 755 "$ACME_DIR"
2898 fi
4c2a3841 2899
44edb2bd 2900 if ! _exec "$_APACHECTL" graceful; then
4c2a3841 2901 _exec_err
610e0f21 2902 _err "$_APACHECTL graceful error, please contact me."
4c3b3608 2903 _restoreApache
4c2a3841 2904 return 1
4c3b3608 2905 fi
2906 usingApache="1"
2907 return 0
2908}
2909
9d725af6 2910#find the real nginx conf file
2911#backup
2912#set the nginx conf
2913#returns the real nginx conf file
2914_setNginx() {
2915 _d="$1"
2916 _croot="$2"
2917 _thumbpt="$3"
37f39c08 2918
9d725af6 2919 FOUND_REAL_NGINX_CONF=""
9f90618a 2920 FOUND_REAL_NGINX_CONF_LN=""
9d725af6 2921 BACKUP_NGINX_CONF=""
2922 _debug _croot "$_croot"
2923 _start_f="$(echo "$_croot" | cut -d : -f 2)"
2924 _debug _start_f "$_start_f"
2925 if [ -z "$_start_f" ]; then
2926 _debug "find start conf from nginx command"
2927 if [ -z "$NGINX_CONF" ]; then
37f39c08 2928 if ! _exists "nginx"; then
2929 _err "nginx command is not found."
2930 return 1
2931 fi
9d725af6 2932 NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
2933 _debug NGINX_CONF "$NGINX_CONF"
2934 NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
2935 _debug NGINX_CONF "$NGINX_CONF"
1081d98b 2936 if [ -z "$NGINX_CONF" ]; then
2937 _err "Can not find nginx conf."
2938 NGINX_CONF=""
2939 return 1
2940 fi
9d725af6 2941 if [ ! -f "$NGINX_CONF" ]; then
2942 _err "'$NGINX_CONF' doesn't exist."
2943 NGINX_CONF=""
2944 return 1
2945 fi
2946 _debug "Found nginx conf file:$NGINX_CONF"
2947 fi
2948 _start_f="$NGINX_CONF"
2949 fi
03f8d6e9 2950 _debug "Start detect nginx conf for $_d from:$_start_f"
9d725af6 2951 if ! _checkConf "$_d" "$_start_f"; then
5378d9ca 2952 _err "Can not find conf file for domain $d"
9d725af6 2953 return 1
2954 fi
2955 _info "Found conf file: $FOUND_REAL_NGINX_CONF"
2956
9f90618a 2957 _ln=$FOUND_REAL_NGINX_CONF_LN
03f8d6e9 2958 _debug "_ln" "$_ln"
2959
2960 _lnn=$(_math $_ln + 1)
2961 _debug _lnn "$_lnn"
2962 _start_tag="$(sed -n "$_lnn,${_lnn}p" "$FOUND_REAL_NGINX_CONF")"
2963 _debug "_start_tag" "$_start_tag"
2964 if [ "$_start_tag" = "$NGINX_START" ]; then
2965 _info "The domain $_d is already configured, skip"
2966 FOUND_REAL_NGINX_CONF=""
2967 return 0
2968 fi
2969
9d725af6 2970 mkdir -p "$DOMAIN_BACKUP_PATH"
2971 _backup_conf="$DOMAIN_BACKUP_PATH/$_d.nginx.conf"
2972 _debug _backup_conf "$_backup_conf"
2973 BACKUP_NGINX_CONF="$_backup_conf"
2974 _info "Backup $FOUND_REAL_NGINX_CONF to $_backup_conf"
2975 if ! cp "$FOUND_REAL_NGINX_CONF" "$_backup_conf"; then
2976 _err "backup error."
2977 FOUND_REAL_NGINX_CONF=""
2978 return 1
2979 fi
2980
37f39c08 2981 if ! _exists "nginx"; then
2982 _err "nginx command is not found."
2983 return 1
2984 fi
9d725af6 2985 _info "Check the nginx conf before setting up."
2986 if ! _exec "nginx -t" >/dev/null; then
2987 _exec_err
2988 return 1
2989 fi
2990
2991 _info "OK, Set up nginx config file"
9d725af6 2992
302c41ed 2993 if ! sed -n "1,${_ln}p" "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"; then
03f8d6e9 2994 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
9d725af6 2995 _err "write nginx conf error, but don't worry, the file is restored to the original version."
2996 return 1
2997 fi
2998
03f8d6e9 2999 echo "$NGINX_START
9d725af6 3000location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
3001 default_type text/plain;
3002 return 200 \"\$1.$_thumbpt\";
3c07f57a 3003}
03f8d6e9 3004#NGINX_START
3005" >>"$FOUND_REAL_NGINX_CONF"
9d725af6 3006
03f8d6e9 3007 if ! sed -n "${_lnn},99999p" "$_backup_conf" >>"$FOUND_REAL_NGINX_CONF"; then
3008 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
9d725af6 3009 _err "write nginx conf error, but don't worry, the file is restored."
3010 return 1
3011 fi
df711b0e 3012 _debug3 "Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
9d725af6 3013 _info "nginx conf is done, let's check it again."
3014 if ! _exec "nginx -t" >/dev/null; then
3015 _exec_err
3016 _err "It seems that nginx conf was broken, let's restore."
302c41ed 3017 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
9d725af6 3018 return 1
3019 fi
3020
3021 _info "Reload nginx"
3022 if ! _exec "nginx -s reload" >/dev/null; then
3023 _exec_err
3024 _err "It seems that nginx reload error, let's restore."
302c41ed 3025 cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
9d725af6 3026 return 1
3027 fi
3028
3029 return 0
3030}
3031
3032#d , conf
3033_checkConf() {
3034 _d="$1"
3035 _c_file="$2"
3036 _debug "Start _checkConf from:$_c_file"
3037 if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
3038 _debug "wildcard"
3039 for _w_f in $2; do
7f618e7e 3040 if [ -f "$_w_f" ] && _checkConf "$1" "$_w_f"; then
9d725af6 3041 return 0
3042 fi
3043 done
3044 #not found
3045 return 1
3046 elif [ -f "$2" ]; then
3047 _debug "single"
3048 if _isRealNginxConf "$1" "$2"; then
3049 _debug "$2 is found."
3050 FOUND_REAL_NGINX_CONF="$2"
3051 return 0
3052 fi
f08a79d3 3053 if cat "$2" | tr "\t" " " | grep "^ *include *.*;" >/dev/null; then
9d725af6 3054 _debug "Try include files"
f08a79d3 3055 for included in $(cat "$2" | tr "\t" " " | grep "^ *include *.*;" | sed "s/include //" | tr -d " ;"); do
9d725af6 3056 _debug "check included $included"
3057 if _checkConf "$1" "$included"; then
3058 return 0
3059 fi
3060 done
3061 fi
3062 return 1
3063 else
3064 _debug "$2 not found."
3065 return 1
3066 fi
3067 return 1
3068}
3069
3070#d , conf
3071_isRealNginxConf() {
3072 _debug "_isRealNginxConf $1 $2"
302c41ed 3073 if [ -f "$2" ]; then
3f1a76d9 3074 for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do
302c41ed 3075 _debug _fln "$_fln"
3076 if [ "$_fln" ]; then
04a609b5 3077 _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *" | grep -v server_name | _tail_n 1)
9f90618a 3078 _debug "_start" "$_start"
3079 _start_n=$(echo "$_start" | cut -d : -f 1)
3080 _start_nn=$(_math $_start_n + 1)
3081 _debug "_start_n" "$_start_n"
3082 _debug "_start_nn" "$_start_nn"
3083
3084 _left="$(sed -n "${_start_nn},99999p" "$2")"
3085 _debug2 _left "$_left"
012dd698 3086 _end="$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | grep -v server_name | _head_n 1)"
3087 _debug "_end" "$_end"
3088 if [ "$_end" ]; then
9f90618a 3089 _end_n=$(echo "$_end" | cut -d : -f 1)
3090 _debug "_end_n" "$_end_n"
3091 _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
3092 else
3093 _seg_n="$_left"
3094 fi
3095
3096 _debug "_seg_n" "$_seg_n"
3097
04a609b5 3098 _skip_ssl=1
d1067c60 3099 for _listen_i in $(echo "$_seg_n" | tr "\t" ' ' | grep "^ *listen" | tr -d " "); do
04a609b5 3100 if [ "$_listen_i" ]; then
9841063d 3101 if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl")" ]; then
04a609b5 3102 _debug2 "$_listen_i is ssl"
3103 else
3104 _debug2 "$_listen_i is plain text"
3105 _skip_ssl=""
c05eb0b1 3106 break
3107 fi
04a609b5 3108 fi
3109 done
3110
3111 if [ "$_skip_ssl" = "1" ]; then
9f90618a 3112 _debug "ssl on, skip"
241cfc43 3113 else
3114 FOUND_REAL_NGINX_CONF_LN=$_fln
3115 _debug3 "found FOUND_REAL_NGINX_CONF_LN" "$FOUND_REAL_NGINX_CONF_LN"
3116 return 0
450efea1 3117 fi
302c41ed 3118 fi
3119 done
9d725af6 3120 fi
302c41ed 3121 return 1
9d725af6 3122}
3123
3124#restore all the nginx conf
3125_restoreNginx() {
5d943a35 3126 if [ -z "$NGINX_RESTORE_VLIST" ]; then
9d725af6 3127 _debug "No need to restore nginx, skip."
3128 return
3129 fi
3130 _debug "_restoreNginx"
5d943a35 3131 _debug "NGINX_RESTORE_VLIST" "$NGINX_RESTORE_VLIST"
9d725af6 3132
5d943a35 3133 for ng_entry in $(echo "$NGINX_RESTORE_VLIST" | tr "$dvsep" ' '); do
9d725af6 3134 _debug "ng_entry" "$ng_entry"
3135 _nd=$(echo "$ng_entry" | cut -d "$sep" -f 1)
3136 _ngconf=$(echo "$ng_entry" | cut -d "$sep" -f 2)
3137 _ngbackupconf=$(echo "$ng_entry" | cut -d "$sep" -f 3)
3138 _info "Restoring from $_ngbackupconf to $_ngconf"
302c41ed 3139 cat "$_ngbackupconf" >"$_ngconf"
9d725af6 3140 done
3141
3142 _info "Reload nginx"
3143 if ! _exec "nginx -s reload" >/dev/null; then
3144 _exec_err
3145 _err "It seems that nginx reload error, please report bug."
3146 return 1
3147 fi
3148 return 0
3149}
3150
5ef501c5 3151_clearup() {
44edb2bd 3152 _stopserver "$serverproc"
4c3b3608 3153 serverproc=""
3154 _restoreApache
9d725af6 3155 _restoreNginx
800e3f45 3156 _clearupdns
4c2a3841 3157 if [ -z "$DEBUG" ]; then
e22bcf7c 3158 rm -f "$TLS_CONF"
3159 rm -f "$TLS_CERT"
3160 rm -f "$TLS_KEY"
3161 rm -f "$TLS_CSR"
3162 fi
4c3b3608 3163}
3164
800e3f45 3165_clearupdns() {
3166 _debug "_clearupdns"
b5ca9bba 3167 _debug "dns_entries" "$dns_entries"
3168
3169 if [ -z "$dns_entries" ]; then
65b22b49 3170 _debug "skip dns."
800e3f45 3171 return
3172 fi
875625b1 3173 _info "Removing DNS records."
800e3f45 3174
b5ca9bba 3175 for entry in $dns_entries; do
3176 d=$(_getfield "$entry" 1)
3177 txtdomain=$(_getfield "$entry" 2)
3178 aliasDomain=$(_getfield "$entry" 3)
9a733a57 3179 _currentRoot=$(_getfield "$entry" 4)
b5ca9bba 3180 txt=$(_getfield "$entry" 5)
3181 d_api=$(_getfield "$entry" 6)
3182 _debug "d" "$d"
3183 _debug "txtdomain" "$txtdomain"
3184 _debug "aliasDomain" "$aliasDomain"
9a733a57 3185 _debug "_currentRoot" "$_currentRoot"
b5ca9bba 3186 _debug "txt" "$txt"
3187 _debug "d_api" "$d_api"
3188 if [ "$d_api" = "$txt" ]; then
3189 d_api=""
800e3f45 3190 fi
4c2a3841 3191
4c2a3841 3192 if [ -z "$d_api" ]; then
800e3f45 3193 _info "Not Found domain api file: $d_api"
3194 continue
3195 fi
4c2a3841 3196
b5ca9bba 3197 if [ "$aliasDomain" ]; then
3198 txtdomain="$aliasDomain"
3199 fi
3200
800e3f45 3201 (
d5ec5f80 3202 if ! . "$d_api"; then
800e3f45 3203 _err "Load file $d_api error. Please check your api file and try again."
3204 return 1
3205 fi
4c2a3841 3206
800e3f45 3207 rmcommand="${_currentRoot}_rm"
d5ec5f80 3208 if ! _exists "$rmcommand"; then
800e3f45 3209 _err "It seems that your api file doesn't define $rmcommand"
3210 return 1
3211 fi
a180b95c 3212 _info "Removing txt: $txt for domain: $txtdomain"
21f201e3 3213 if ! $rmcommand "$txtdomain" "$txt"; then
800e3f45 3214 _err "Error removing txt for domain:$txtdomain"
3215 return 1
3216 fi
a180b95c 3217 _info "Removed: Success"
800e3f45 3218 )
4c2a3841 3219
800e3f45 3220 done
3221}
3222
4c3b3608 3223# webroot removelevel tokenfile
3224_clearupwebbroot() {
3225 __webroot="$1"
4c2a3841 3226 if [ -z "$__webroot" ]; then
4c3b3608 3227 _debug "no webroot specified, skip"
3228 return 0
3229 fi
4c2a3841 3230
dcf9cb58 3231 _rmpath=""
4c2a3841 3232 if [ "$2" = '1' ]; then
dcf9cb58 3233 _rmpath="$__webroot/.well-known"
4c2a3841 3234 elif [ "$2" = '2' ]; then
dcf9cb58 3235 _rmpath="$__webroot/.well-known/acme-challenge"
4c2a3841 3236 elif [ "$2" = '3' ]; then
dcf9cb58 3237 _rmpath="$__webroot/.well-known/acme-challenge/$3"
4c3b3608 3238 else
cc179731 3239 _debug "Skip for removelevel:$2"
4c3b3608 3240 fi
4c2a3841 3241
3242 if [ "$_rmpath" ]; then
3243 if [ "$DEBUG" ]; then
dcf9cb58 3244 _debug "Debugging, skip removing: $_rmpath"
3245 else
3246 rm -rf "$_rmpath"
3247 fi
3248 fi
4c2a3841 3249
4c3b3608 3250 return 0
3251
3252}
3253
b0070f03 3254_on_before_issue() {
af1cc3b3 3255 _chk_web_roots="$1"
02140ce7 3256 _chk_main_domain="$2"
3257 _chk_alt_domains="$3"
85e1f4ea 3258 _chk_pre_hook="$4"
3259 _chk_local_addr="$5"
30c2d84c 3260 _debug _on_before_issue
38f1b4d2 3261 _debug _chk_main_domain "$_chk_main_domain"
3262 _debug _chk_alt_domains "$_chk_alt_domains"
d0f7c309 3263 #run pre hook
85e1f4ea 3264 if [ "$_chk_pre_hook" ]; then
3265 _info "Run pre hook:'$_chk_pre_hook'"
d0f7c309 3266 if ! (
85e1f4ea 3267 cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
d0f7c309 3268 ); then
3269 _err "Error when run pre hook."
3270 return 1
3271 fi
3272 fi
3273
af1cc3b3 3274 if _hasfield "$_chk_web_roots" "$NO_VALUE"; then
3794b5cb 3275 if ! _exists "socat"; then
3276 _err "Please install socat tools first."
0463b5d6 3277 return 1
3278 fi
0463b5d6 3279 fi
3280
85e1f4ea 3281 _debug Le_LocalAddress "$_chk_local_addr"
4c2a3841 3282
0463b5d6 3283 _index=1
3284 _currentRoot=""
3285 _addrIndex=1
38f1b4d2 3286 _w_index=1
931d19ee 3287 while true; do
931d19ee 3288 d="$(echo "$_chk_main_domain,$_chk_alt_domains," | cut -d , -f "$_w_index")"
3289 _w_index="$(_math "$_w_index" + 1)"
3290 _debug d "$d"
3291 if [ -z "$d" ]; then
3292 break
3293 fi
d5ec5f80 3294 _debug "Check for domain" "$d"
af1cc3b3 3295 _currentRoot="$(_getfield "$_chk_web_roots" $_index)"
0463b5d6 3296 _debug "_currentRoot" "$_currentRoot"
3297 _index=$(_math $_index + 1)
3298 _checkport=""
4c2a3841 3299 if [ "$_currentRoot" = "$NO_VALUE" ]; then
0463b5d6 3300 _info "Standalone mode."
4c2a3841 3301 if [ -z "$Le_HTTPPort" ]; then
0463b5d6 3302 Le_HTTPPort=80
e7f7e96d 3303 _cleardomainconf "Le_HTTPPort"
0463b5d6 3304 else
4c2a3841 3305 _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
0463b5d6 3306 fi
3307 _checkport="$Le_HTTPPort"
43ff787b 3308 elif [ "$_currentRoot" = "$W_ALPN" ]; then
3309 _info "Standalone alpn mode."
4c2a3841 3310 if [ -z "$Le_TLSPort" ]; then
0463b5d6 3311 Le_TLSPort=443
3312 else
4c2a3841 3313 _savedomainconf "Le_TLSPort" "$Le_TLSPort"
0463b5d6 3314 fi
3315 _checkport="$Le_TLSPort"
3316 fi
4c2a3841 3317
3318 if [ "$_checkport" ]; then
0463b5d6 3319 _debug _checkport "$_checkport"
85e1f4ea 3320 _checkaddr="$(_getfield "$_chk_local_addr" $_addrIndex)"
0463b5d6 3321 _debug _checkaddr "$_checkaddr"
4c2a3841 3322
0463b5d6 3323 _addrIndex="$(_math $_addrIndex + 1)"
4c2a3841 3324
0463b5d6 3325 _netprc="$(_ss "$_checkport" | grep "$_checkport")"
3326 netprc="$(echo "$_netprc" | grep "$_checkaddr")"
4c2a3841 3327 if [ -z "$netprc" ]; then
0463b5d6 3328 netprc="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")"
3329 fi
4c2a3841 3330 if [ "$netprc" ]; then
0463b5d6 3331 _err "$netprc"
4c2a3841 3332 _err "tcp port $_checkport is already used by $(echo "$netprc" | cut -d : -f 4)"
0463b5d6 3333 _err "Please stop it first"
3334 return 1
3335 fi
3336 fi
3337 done
3338
af1cc3b3 3339 if _hasfield "$_chk_web_roots" "apache"; then
4c2a3841 3340 if ! _setApache; then
0463b5d6 3341 _err "set up apache error. Report error to me."
3342 return 1
3343 fi
3344 else
3345 usingApache=""
3346 fi
3347
b0070f03 3348}
3349
3350_on_issue_err() {
85e1f4ea 3351 _chk_post_hook="$1"
58e4d337 3352 _chk_vlist="$2"
30c2d84c 3353 _debug _on_issue_err
cd8fc359 3354
4c2a3841 3355 if [ "$LOG_FILE" ]; then
a73c5b33 3356 _err "Please check log file for more details: $LOG_FILE"
3357 else
54ae008d 3358 _err "Please add '--debug' or '--log' to check more details."
a73c5b33 3359 _err "See: $_DEBUG_WIKI"
3360 fi
4c2a3841 3361
b0070f03 3362 #run the post hook
85e1f4ea 3363 if [ "$_chk_post_hook" ]; then
3364 _info "Run post hook:'$_chk_post_hook'"
b0070f03 3365 if ! (
85e1f4ea 3366 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
4c2a3841 3367 ); then
b0070f03 3368 _err "Error when run post hook."
3369 return 1
3370 fi
3371 fi
58e4d337 3372
3373 #trigger the validation to flush the pending authz
ea722da3 3374 _debug2 "_chk_vlist" "$_chk_vlist"
58e4d337 3375 if [ "$_chk_vlist" ]; then
3376 (
c719a61e 3377 _debug2 "start to deactivate authz"
3378 ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ')
3379 for ventry in $ventries; do
3380 d=$(echo "$ventry" | cut -d "$sep" -f 1)
3381 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
3382 uri=$(echo "$ventry" | cut -d "$sep" -f 3)
3383 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
3384 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
f94433e5 3385 __trigger_validation "$uri" "$keyauthorization"
c719a61e 3386 done
58e4d337 3387 )
3388 fi
3389
bd04638d 3390 if [ "$_ACME_IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "$W_DNS"; then
309bec47 3391 _err "$_DNS_MANUAL_ERR"
3392 fi
3393
58e4d337 3394 if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
3395 _debug "$(_dlg_versions)"
3396 fi
3397
b0070f03 3398}
3399
3400_on_issue_success() {
85e1f4ea 3401 _chk_post_hook="$1"
3402 _chk_renew_hook="$2"
30c2d84c 3403 _debug _on_issue_success
c6b68551 3404
b0070f03 3405 #run the post hook
85e1f4ea 3406 if [ "$_chk_post_hook" ]; then
3407 _info "Run post hook:'$_chk_post_hook'"
b0070f03 3408 if ! (
c7849a43 3409 export CERT_PATH
3410 export CERT_KEY_PATH
3411 export CA_CERT_PATH
3412 export CERT_FULLCHAIN_PATH
3413 export Le_Domain="$_main_domain"
85e1f4ea 3414 cd "$DOMAIN_PATH" && eval "$_chk_post_hook"
4c2a3841 3415 ); then
b0070f03 3416 _err "Error when run post hook."
3417 return 1
3418 fi
3419 fi
4c2a3841 3420
b0070f03 3421 #run renew hook
bd04638d 3422 if [ "$_ACME_IS_RENEW" ] && [ "$_chk_renew_hook" ]; then
85e1f4ea 3423 _info "Run renew hook:'$_chk_renew_hook'"
b0070f03 3424 if ! (
c7849a43 3425 export CERT_PATH
3426 export CERT_KEY_PATH
3427 export CA_CERT_PATH
3428 export CERT_FULLCHAIN_PATH
3429 export Le_Domain="$_main_domain"
85e1f4ea 3430 cd "$DOMAIN_PATH" && eval "$_chk_renew_hook"
4c2a3841 3431 ); then
b0070f03 3432 _err "Error when run renew hook."
3433 return 1
3434 fi
4c2a3841 3435 fi
3436
867ec010 3437 if _hasfield "$Le_Webroot" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
309bec47 3438 _err "$_DNS_MANUAL_WARN"
3439 fi
3440
b0070f03 3441}
3442
f96d91cb 3443#account_key_length eab-kid eab-hmac-key
eb59817e 3444registeraccount() {
f96d91cb 3445 _account_key_length="$1"
3446 _eab_id="$2"
3447 _eab_hmac_key="$3"
eb59817e 3448 _initpath
f96d91cb 3449 _regAccount "$_account_key_length" "$_eab_id" "$_eab_hmac_key"
eb59817e 3450}
d404e92d 3451
8a29fbc8 3452__calcAccountKeyHash() {
ca7202eb 3453 [ -f "$ACCOUNT_KEY_PATH" ] && _digest sha256 <"$ACCOUNT_KEY_PATH"
8a29fbc8 3454}
3455
339a8ad6 3456__calc_account_thumbprint() {
3457 printf "%s" "$jwk" | tr -d ' ' | _digest "sha256" | _url_replace
3458}
3459
389518e1 3460_getAccountEmail() {
3461 if [ "$ACCOUNT_EMAIL" ]; then
3462 echo "$ACCOUNT_EMAIL"
3463 return 0
3464 fi
3465 if [ -z "$CA_EMAIL" ]; then
3466 CA_EMAIL="$(_readcaconf CA_EMAIL)"
3467 fi
3468 if [ "$CA_EMAIL" ]; then
3469 echo "$CA_EMAIL"
3470 return 0
3471 fi
3472 _readaccountconf "ACCOUNT_EMAIL"
3473}
3474
57e58ce7 3475#keylength
d404e92d 3476_regAccount() {
3477 _initpath
57e58ce7 3478 _reg_length="$1"
f96d91cb 3479 _eab_id="$2"
3480 _eab_hmac_key="$3"
f87890cb 3481 _debug3 _regAccount "$_regAccount"
c1151b0d 3482 _initAPI
3483
1bbc33a0 3484 mkdir -p "$CA_DIR"
5c48e139 3485 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3486 _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3487 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3488 fi
4c2a3841 3489
5c48e139 3490 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3491 _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3492 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3493 fi
4c2a3841 3494
3495 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3496 if ! _create_account_key "$_reg_length"; then
d404e92d 3497 _err "Create account key error."
3498 return 1
3499 fi
3500 fi
4c2a3841 3501
3502 if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
d404e92d 3503 return 1
3504 fi
f96d91cb 3505 if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
3506 _savecaconf CA_EAB_KEY_ID "$_eab_id"
3507 _savecaconf CA_EAB_HMAC_KEY "$_eab_hmac_key"
3508 fi
3509 _eab_id=$(_readcaconf "CA_EAB_KEY_ID")
3510 _eab_hmac_key=$(_readcaconf "CA_EAB_HMAC_KEY")
3511 _secure_debug3 _eab_id "$_eab_id"
3512 _secure_debug3 _eab_hmac_key "$_eab_hmac_key"
389518e1 3513 _email="$(_getAccountEmail)"
3514 if [ "$_email" ]; then
3515 _savecaconf "CA_EMAIL" "$_email"
3516 fi
c1151b0d 3517 if [ "$ACME_VERSION" = "2" ]; then
389518e1 3518 if [ "$ACME_DIRECTORY" = "$CA_ZEROSSL" ]; then
3519 if [ -z "$_eab_id" ] || [ -z "$_eab_hmac_key" ]; then
3520 _info "No EAB credentials found for ZeroSSL, let's get one"
3521 if [ -z "$_email" ]; then
578c338d 3522 _err "Please provide a email address for ZeroSSL account."
3523 _err "See ZeroSSL usage: $_ZEROSSL_WIKI"
389518e1 3524 return 1
3525 fi
3526 _eabresp=$(_post "email=$_email" $_ZERO_EAB_ENDPOINT)
3527 if [ "$?" != "0" ]; then
3528 _debug2 "$_eabresp"
578c338d 3529 _err "Can not get EAB credentials from ZeroSSL."
389518e1 3530 return 1
3531 fi
3532 _eab_id="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_kid"' | cut -d : -f 2 | tr -d '"')"
3533 if [ -z "$_eab_id" ]; then
365aa69a 3534 _err "Can not resolve _eab_id"
389518e1 3535 return 1
3536 fi
3537 _eab_hmac_key="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_hmac_key"' | cut -d : -f 2 | tr -d '"')"
3538 if [ -z "$_eab_hmac_key" ]; then
365aa69a 3539 _err "Can not resolve _eab_hmac_key"
389518e1 3540 return 1
3541 fi
3542 _savecaconf CA_EAB_KEY_ID "$_eab_id"
3543 _savecaconf CA_EAB_HMAC_KEY "$_eab_hmac_key"
3544 fi
3545 fi
f96d91cb 3546 if [ "$_eab_id" ] && [ "$_eab_hmac_key" ]; then
3547 eab_protected="{\"alg\":\"HS256\",\"kid\":\"$_eab_id\",\"url\":\"${ACME_NEW_ACCOUNT}\"}"
3548 _debug3 eab_protected "$eab_protected"
3549
3550 eab_protected64=$(printf "%s" "$eab_protected" | _base64 | _url_replace)
3551 _debug3 eab_protected64 "$eab_protected64"
3552
3553 eab_payload64=$(printf "%s" "$jwk" | _base64 | _url_replace)
3554 _debug3 eab_payload64 "$eab_payload64"
3555
3556 eab_sign_t="$eab_protected64.$eab_payload64"
3557 _debug3 eab_sign_t "$eab_sign_t"
3558
d42ff227 3559 key_hex="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 | _hex_dump | tr -d ' ')"
f96d91cb 3560 _debug3 key_hex "$key_hex"
3561
d42ff227 3562 eab_signature=$(printf "%s" "$eab_sign_t" | _hmac sha256 $key_hex | _base64 | _url_replace)
f96d91cb 3563 _debug3 eab_signature "$eab_signature"
3564
3565 externalBinding=",\"externalAccountBinding\":{\"protected\":\"$eab_protected64\", \"payload\":\"$eab_payload64\", \"signature\":\"$eab_signature\"}"
3566 _debug3 externalBinding "$externalBinding"
3567 fi
389518e1 3568 if [ "$_email" ]; then
3569 email_sg="\"contact\": [\"mailto:$_email\"], "
c1151b0d 3570 fi
f96d91cb 3571 regjson="{$email_sg\"termsOfServiceAgreed\": true$externalBinding}"
c1151b0d 3572 else
3573 _reg_res="$ACME_NEW_ACCOUNT_RES"
3574 regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
389518e1 3575 if [ "$_email" ]; then
3576 regjson='{"resource": "'$_reg_res'", "contact": ["mailto:'$_email'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
c1151b0d 3577 fi
f87890cb 3578 fi
4c2a3841 3579
389518e1 3580 _info "Registering account: $ACME_DIRECTORY"
d404e92d 3581
f87890cb 3582 if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
3583 _err "Register account Error: $response"
3584 return 1
3585 fi
d404e92d 3586
389518e1 3587 _eabAlreadyBound=""
f87890cb 3588 if [ "$code" = "" ] || [ "$code" = '201' ]; then
3589 echo "$response" >"$ACCOUNT_JSON_PATH"
3590 _info "Registered"
7df20e50 3591 elif [ "$code" = '409' ] || [ "$code" = '200' ]; then
f87890cb 3592 _info "Already registered"
389518e1 3593 elif [ "$code" = '400' ] && _contains "$response" 'The account is not awaiting external account binding'; then
3594 _info "Already register EAB."
3595 _eabAlreadyBound=1
f87890cb 3596 else
3597 _err "Register account Error: $response"
3598 return 1
3599 fi
d404e92d 3600
389518e1 3601 if [ -z "$_eabAlreadyBound" ]; then
3602 _debug2 responseHeaders "$responseHeaders"
3603 _accUri="$(echo "$responseHeaders" | grep -i "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n ")"
3604 _debug "_accUri" "$_accUri"
3605 if [ -z "$_accUri" ]; then
3606 _err "Can not find account id url."
3607 _err "$responseHeaders"
3608 return 1
3609 fi
3610 _savecaconf "ACCOUNT_URL" "$_accUri"
3611 else
3612 ACCOUNT_URL="$(_readcaconf ACCOUNT_URL)"
7e0b334b 3613 fi
7e0b334b 3614 export ACCOUNT_URL="$_accUri"
d404e92d 3615
f87890cb 3616 CA_KEY_HASH="$(__calcAccountKeyHash)"
3617 _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
3618 _savecaconf CA_KEY_HASH "$CA_KEY_HASH"
d404e92d 3619
f87890cb 3620 if [ "$code" = '403' ]; then
3621 _err "It seems that the account key is already deactivated, please use a new account key."
3622 return 1
3623 fi
4c2a3841 3624
f87890cb 3625 ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
3626 _info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
d404e92d 3627}
3628
79e2f8a2 3629#implement updateaccount
3630updateaccount() {
3631 _initpath
3632
3633 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
3634 _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3635 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3636 fi
3637
3638 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
3639 _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3640 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3641 fi
3642
3643 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3644 _err "Account key is not found at: $ACCOUNT_KEY_PATH"
3645 return 1
3646 fi
3647
3648 _accUri=$(_readcaconf "ACCOUNT_URL")
3649 _debug _accUri "$_accUri"
3650
3651 if [ -z "$_accUri" ]; then
3652 _err "The account url is empty, please run '--update-account' first to update the account info first,"
3653 _err "Then try again."
3654 return 1
3655 fi
3656
3657 if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
3658 return 1
3659 fi
3660 _initAPI
3661
389518e1 3662 _email="$(_getAccountEmail)"
79e2f8a2 3663 if [ "$ACME_VERSION" = "2" ]; then
3664 if [ "$ACCOUNT_EMAIL" ]; then
389518e1 3665 updjson='{"contact": ["mailto:'$_email'"]}'
2febdfc3
SW
3666 else
3667 updjson='{"contact": []}'
79e2f8a2 3668 fi
3669 else
3670 # ACMEv1: Updates happen the same way a registration is done.
3671 # https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-6.3
3672 _regAccount
3673 return
3674 fi
3675
3676 # this part handles ACMEv2 account updates.
3677 _send_signed_request "$_accUri" "$updjson"
3678
3679 if [ "$code" = '200' ]; then
72e1a1b2 3680 echo "$response" >"$ACCOUNT_JSON_PATH"
79e2f8a2 3681 _info "account update success for $_accUri."
3682 else
3683 _info "Error. The account was not updated."
3684 return 1
3685 fi
3686}
3687
422dd1fa 3688#Implement deactivate account
3689deactivateaccount() {
3690 _initpath
3691
3692 if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
422dd1fa 3693 _info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
3694 mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
3695 fi
3696
3697 if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
422dd1fa 3698 _info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
3699 mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
3700 fi
3701
3702 if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
3703 _err "Account key is not found at: $ACCOUNT_KEY_PATH"
3704 return 1
3705 fi
3706
3707 _accUri=$(_readcaconf "ACCOUNT_URL")
3708 _debug _accUri "$_accUri"
3709
3710 if [ -z "$_accUri" ]; then
3711 _err "The account url is empty, please run '--update-account' first to update the account info first,"
3712 _err "Then try again."
3713 return 1
3714 fi
3715
3716 if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
3717 return 1
3718 fi
3719 _initAPI
3720
d2cde379 3721 if [ "$ACME_VERSION" = "2" ]; then
3722 _djson="{\"status\":\"deactivated\"}"
3723 else
3724 _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}"
3725 fi
3726 if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then
422dd1fa 3727 _info "Deactivate account success for $_accUri."
3728 _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,')
3729 elif [ "$code" = "403" ]; then
3730 _info "The account is already deactivated."
3731 _accid=$(_getfield "$_accUri" "999" "/")
3732 else
3733 _err "Deactivate: account failed for $_accUri."
3734 return 1
3735 fi
3736
3737 _debug "Account id: $_accid"
3738 if [ "$_accid" ]; then
3739 _deactivated_account_path="$CA_DIR/deactivated/$_accid"
3740 _debug _deactivated_account_path "$_deactivated_account_path"
3741 if mkdir -p "$_deactivated_account_path"; then
3742 _info "Moving deactivated account info to $_deactivated_account_path/"
3743 mv "$CA_CONF" "$_deactivated_account_path/"
3744 mv "$ACCOUNT_JSON_PATH" "$_deactivated_account_path/"
3745 mv "$ACCOUNT_KEY_PATH" "$_deactivated_account_path/"
3746 else
3747 _err "Can not create dir: $_deactivated_account_path, try to remove the deactivated account key."
3748 rm -f "$CA_CONF"
3749 rm -f "$ACCOUNT_JSON_PATH"
3750 rm -f "$ACCOUNT_KEY_PATH"
3751 fi
3752 fi
3753}
3754
a61fe418 3755# domain folder file
3756_findHook() {
3757 _hookdomain="$1"
3758 _hookcat="$2"
3759 _hookname="$3"
3760
c7b16249 3761 if [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname" ]; then
3762 d_api="$_SCRIPT_HOME/$_hookcat/$_hookname"
3763 elif [ -f "$_SCRIPT_HOME/$_hookcat/$_hookname.sh" ]; then
3764 d_api="$_SCRIPT_HOME/$_hookcat/$_hookname.sh"
b50e701c 3765 elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname" ]; then
a61fe418 3766 d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname"
b50e701c 3767 elif [ "$_hookdomain" ] && [ -f "$LE_WORKING_DIR/$_hookdomain/$_hookname.sh" ]; then
a61fe418 3768 d_api="$LE_WORKING_DIR/$_hookdomain/$_hookname.sh"
4c2a3841 3769 elif [ -f "$LE_WORKING_DIR/$_hookname" ]; then
a61fe418 3770 d_api="$LE_WORKING_DIR/$_hookname"
4c2a3841 3771 elif [ -f "$LE_WORKING_DIR/$_hookname.sh" ]; then
a61fe418 3772 d_api="$LE_WORKING_DIR/$_hookname.sh"
4c2a3841 3773 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname" ]; then
a61fe418 3774 d_api="$LE_WORKING_DIR/$_hookcat/$_hookname"
4c2a3841 3775 elif [ -f "$LE_WORKING_DIR/$_hookcat/$_hookname.sh" ]; then
a61fe418 3776 d_api="$LE_WORKING_DIR/$_hookcat/$_hookname.sh"
3777 fi
3778
3779 printf "%s" "$d_api"
3780}
3781
f940b2a5 3782#domain
3783__get_domain_new_authz() {
3784 _gdnd="$1"
3785 _info "Getting new-authz for domain" "$_gdnd"
40ef86f4 3786 _initAPI
f940b2a5 3787 _Max_new_authz_retry_times=5
3788 _authz_i=0
4c2a3841 3789 while [ "$_authz_i" -lt "$_Max_new_authz_retry_times" ]; do
efd96153 3790 _debug "Try new-authz for the $_authz_i time."
48d9a8c1 3791 if ! _send_signed_request "${ACME_NEW_AUTHZ}" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_gdnd")\"}}"; then
f940b2a5 3792 _err "Can not get domain new authz."
3793 return 1
3794 fi
5413bf87 3795 if _contains "$response" "No registration exists matching provided key"; then
3796 _err "It seems there is an error, but it's recovered now, please try again."
3797 _err "If you see this message for a second time, please report bug: $(__green "$PROJECT")"
3798 _clearcaconf "CA_KEY_HASH"
3799 break
3800 fi
4c2a3841 3801 if ! _contains "$response" "An error occurred while processing your request"; then
f940b2a5 3802 _info "The new-authz request is ok."
3803 break
3804 fi
3805 _authz_i="$(_math "$_authz_i" + 1)"
9e45ac93 3806 _info "The server is busy, Sleep $_authz_i to retry."
f940b2a5 3807 _sleep "$_authz_i"
4c2a3841 3808 done
f940b2a5 3809
4c2a3841 3810 if [ "$_authz_i" = "$_Max_new_authz_retry_times" ]; then
efd96153 3811 _err "new-authz retry reach the max $_Max_new_authz_retry_times times."
f940b2a5 3812 fi
4c2a3841 3813
78915896 3814 if [ "$code" ] && [ "$code" != '201' ]; then
f940b2a5 3815 _err "new-authz error: $response"
3816 return 1
3817 fi
3818
3819}
3820
58e4d337 3821#uri keyAuthorization
f94433e5 3822__trigger_validation() {
8bd12ed0 3823 _debug2 "Trigger domain validation."
58e4d337 3824 _t_url="$1"
3825 _debug2 _t_url "$_t_url"
3826 _t_key_authz="$2"
3827 _debug2 _t_key_authz "$_t_key_authz"
920cab6f
K
3828 _t_vtype="$3"
3829 _debug2 _t_vtype "$_t_vtype"
c1151b0d 3830 if [ "$ACME_VERSION" = "2" ]; then
e7f7e96d 3831 _send_signed_request "$_t_url" "{}"
c1151b0d 3832 else
8bd12ed0 3833 _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}"
c1151b0d 3834 fi
58e4d337 3835}
3836
b5ca9bba 3837#endpoint domain type
10eec7d4 3838_ns_lookup_impl() {
b5ca9bba 3839 _ns_ep="$1"
3840 _ns_domain="$2"
3841 _ns_type="$3"
3842 _debug2 "_ns_ep" "$_ns_ep"
3843 _debug2 "_ns_domain" "$_ns_domain"
3844 _debug2 "_ns_type" "$_ns_type"
3845
3846 response="$(_H1="accept: application/dns-json" _get "$_ns_ep?name=$_ns_domain&type=$_ns_type")"
3847 _ret=$?
3848 _debug2 "response" "$response"
3849 if [ "$_ret" != "0" ]; then
3850 return $_ret
3851 fi
3852 _answers="$(echo "$response" | tr '{}' '<>' | _egrep_o '"Answer":\[[^]]*]' | tr '<>' '\n\n')"
3853 _debug2 "_answers" "$_answers"
3854 echo "$_answers"
3855}
3856
3857#domain, type
3858_ns_lookup_cf() {
3859 _cf_ld="$1"
3860 _cf_ld_type="$2"
3861 _cf_ep="https://cloudflare-dns.com/dns-query"
10eec7d4 3862 _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
b5ca9bba 3863}
3864
3865#domain, type
3866_ns_purge_cf() {
3867 _cf_d="$1"
3868 _cf_d_type="$2"
3869 _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d"
b9b2cd27 3870 _cf_purl="https://cloudflare-dns.com/api/v1/purge?domain=$_cf_d&type=$_cf_d_type"
b5ca9bba 3871 response="$(_post "" "$_cf_purl")"
3872 _debug2 response "$response"
3873}
3874
10eec7d4 3875#checks if cf server is available
3876_ns_is_available_cf() {
1e7534b9 3877 if _get "https://cloudflare-dns.com" >/dev/null 2>&1; then
10eec7d4 3878 return 0
3879 else
3880 return 1
3881 fi
3882}
3883
3884#domain, type
3885_ns_lookup_google() {
3886 _cf_ld="$1"
3887 _cf_ld_type="$2"
3888 _cf_ep="https://dns.google/resolve"
3889 _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
3890}
3891
3892#domain, type
3893_ns_lookup() {
3894 if [ -z "$DOH_USE" ]; then
3895 _debug "Detect dns server first."
3896 if _ns_is_available_cf; then
3897 _debug "Use cloudflare doh server"
3898 export DOH_USE=$DOH_CLOUDFLARE
3899 else
3900 _debug "Use google doh server"
3901 export DOH_USE=$DOH_GOOGLE
3902 fi
3903 fi
3904
3905 if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then
3906 _ns_lookup_cf "$@"
3907 else
3908 _ns_lookup_google "$@"
3909 fi
3910
3911}
3912
b5ca9bba 3913#txtdomain, alias, txt
3914__check_txt() {
3915 _c_txtdomain="$1"
3916 _c_aliasdomain="$2"
3917 _c_txt="$3"
3918 _debug "_c_txtdomain" "$_c_txtdomain"
3919 _debug "_c_aliasdomain" "$_c_aliasdomain"
3920 _debug "_c_txt" "$_c_txt"
10eec7d4 3921 _answers="$(_ns_lookup "$_c_aliasdomain" TXT)"
b5ca9bba 3922 _contains "$_answers" "$_c_txt"
3923
3924}
3925
3926#txtdomain
3927__purge_txt() {
3928 _p_txtdomain="$1"
3929 _debug _p_txtdomain "$_p_txtdomain"
10eec7d4 3930 if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then
3931 _ns_purge_cf "$_p_txtdomain" "TXT"
3932 else
3933 _debug "no purge api for google dns api, just sleep 5 secs"
3934 _sleep 5
3935 fi
3936
b5ca9bba 3937}
3938
3939#wait and check each dns entries
3940_check_dns_entries() {
3941 _success_txt=","
3942 _end_time="$(_time)"
3943 _end_time="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes.
3944
3945 while [ "$(_time)" -le "$_end_time" ]; do
3946 _left=""
3947 for entry in $dns_entries; do
3948 d=$(_getfield "$entry" 1)
3949 txtdomain=$(_getfield "$entry" 2)
0093dc3d 3950 txtdomain=$(_idn "$txtdomain")
b5ca9bba 3951 aliasDomain=$(_getfield "$entry" 3)
0093dc3d 3952 aliasDomain=$(_idn "$aliasDomain")
b5ca9bba 3953 txt=$(_getfield "$entry" 5)
3954 d_api=$(_getfield "$entry" 6)
3955 _debug "d" "$d"
3956 _debug "txtdomain" "$txtdomain"
3957 _debug "aliasDomain" "$aliasDomain"
3958 _debug "txt" "$txt"
3959 _debug "d_api" "$d_api"
3960 _info "Checking $d for $aliasDomain"
3961 if _contains "$_success_txt" ",$txt,"; then
3962 _info "Already success, continue next one."
3963 continue
3964 fi
3965
3966 if __check_txt "$txtdomain" "$aliasDomain" "$txt"; then
3967 _info "Domain $d '$aliasDomain' success."
3968 _success_txt="$_success_txt,$txt,"
3969 continue
3970 fi
3971 _left=1
3972 _info "Not valid yet, let's wait 10 seconds and check next one."
b5ca9bba 3973 __purge_txt "$txtdomain"
3974 if [ "$txtdomain" != "$aliasDomain" ]; then
3975 __purge_txt "$aliasDomain"
3976 fi
b9b2cd27 3977 _sleep 10
b5ca9bba 3978 done
3979 if [ "$_left" ]; then
3980 _info "Let's wait 10 seconds and check again".
3981 _sleep 10
3982 else
3983 _info "All success, let's return"
a44ea0dd 3984 return 0
b5ca9bba 3985 fi
3986 done
a44ea0dd 3987 _info "Timed out waiting for DNS."
3988 return 1
b5ca9bba 3989
3990}
3991
e3ebd582 3992#file
d5d38b33 3993_get_cert_issuers() {
e3ebd582 3994 _cfile="$1"
d5d38b33 3995 if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7"; then
3996 ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | openssl pkcs7 -print_certs -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2
3997 else
3998 ${ACME_OPENSSL_BIN:-openssl} x509 -in $_cfile -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2
3999 fi
e3ebd582 4000}
4001
4002#cert issuer
4003_match_issuer() {
4004 _cfile="$1"
4005 _missuer="$2"
d5d38b33 4006 _fissuers="$(_get_cert_issuers $_cfile)"
4007 _debug2 _fissuers "$_fissuers"
4008 _contains "$_fissuers" "$_missuer"
e3ebd582 4009}
4010
3c07f57a 4011#webroot, domain domainlist keylength
4c3b3608 4012issue() {
4c2a3841 4013 if [ -z "$2" ]; then
43822d37 4014 _usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
4c3b3608 4015 return 1
4016 fi
49d75a0c 4017 if [ -z "$1" ]; then
4018 _usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
4019 return 1
4020 fi
af1cc3b3 4021 _web_roots="$1"
4022 _main_domain="$2"
02140ce7 4023 _alt_domains="$3"
d2cde379 4024
af1cc3b3 4025 if _contains "$_main_domain" ","; then
4026 _main_domain=$(echo "$2,$3" | cut -d , -f 1)
02140ce7 4027 _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
2aff36e7 4028 fi
674b5088 4029 _debug _main_domain "$_main_domain"
4030 _debug _alt_domains "$_alt_domains"
4031
d9c9114b 4032 _key_length="$4"
85e1f4ea 4033 _real_cert="$5"
4034 _real_key="$6"
4035 _real_ca="$7"
4036 _reload_cmd="$8"
4037 _real_fullchain="$9"
4038 _pre_hook="${10}"
4039 _post_hook="${11}"
4040 _renew_hook="${12}"
4041 _local_addr="${13}"
875625b1 4042 _challenge_alias="${14}"
e3ebd582 4043 _preferred_chain="${15}"
4c2a3841 4044
bd04638d 4045 if [ -z "$_ACME_IS_RENEW" ]; then
d9c9114b 4046 _initpath "$_main_domain" "$_key_length"
43822d37 4047 mkdir -p "$DOMAIN_PATH"
4048 fi
eccec5f6 4049
a0923622 4050 if _hasfield "$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
4051 _err "$_DNS_MANUAL_ERROR"
4052 return 1
4053 fi
4054
48d9a8c1 4055 _debug "Using ACME_DIRECTORY: $ACME_DIRECTORY"
4056
4057 _initAPI
4058
4c2a3841 4059 if [ -f "$DOMAIN_CONF" ]; then
61623d22 4060 Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
a4270efa 4061 _debug Le_NextRenewTime "$Le_NextRenewTime"
95e06de5 4062 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
bb25febd 4063 _saved_domain=$(_readdomainconf Le_Domain)
4064 _debug _saved_domain "$_saved_domain"
4065 _saved_alt=$(_readdomainconf Le_Alt)
4066 _debug _saved_alt "$_saved_alt"
02140ce7 4067 if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then
bb25febd 4068 _info "Domains not changed."
4069 _info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
4c2a3841 4070 _info "Add '$(__red '--force')' to force to renew."
bb25febd 4071 return $RENEW_SKIP
4072 else
4073 _info "Domains have changed."
4074 fi
4c3b3608 4075 fi
4076 fi
96a46cfc 4077
af1cc3b3 4078 _savedomainconf "Le_Domain" "$_main_domain"
02140ce7 4079 _savedomainconf "Le_Alt" "$_alt_domains"
af1cc3b3 4080 _savedomainconf "Le_Webroot" "$_web_roots"
4c2a3841 4081
c7257bcf 4082 _savedomainconf "Le_PreHook" "$_pre_hook" "base64"
4083 _savedomainconf "Le_PostHook" "$_post_hook" "base64"
4084 _savedomainconf "Le_RenewHook" "$_renew_hook" "base64"
4c2a3841 4085
85e1f4ea 4086 if [ "$_local_addr" ]; then
4087 _savedomainconf "Le_LocalAddress" "$_local_addr"
72518d48 4088 else
4089 _cleardomainconf "Le_LocalAddress"
4090 fi
875625b1 4091 if [ "$_challenge_alias" ]; then
4092 _savedomainconf "Le_ChallengeAlias" "$_challenge_alias"
4093 else
4094 _cleardomainconf "Le_ChallengeAlias"
4095 fi
e3ebd582 4096 if [ "$_preferred_chain" ]; then
4097 _savedomainconf "Le_Preferred_Chain" "$_preferred_chain" "base64"
4098 else
4099 _cleardomainconf "Le_Preferred_Chain"
4100 fi
6ae0f7f5 4101
a6d22e3b 4102 Le_API="$ACME_DIRECTORY"
4103 _savedomainconf "Le_API" "$Le_API"
4104
389518e1 4105 _info "Using CA: $ACME_DIRECTORY"
02140ce7 4106 if [ "$_alt_domains" = "$NO_VALUE" ]; then
4107 _alt_domains=""
4c3b3608 4108 fi
4c2a3841 4109
d9c9114b 4110 if [ "$_key_length" = "$NO_VALUE" ]; then
4111 _key_length=""
d404e92d 4112 fi
4c2a3841 4113
85e1f4ea 4114 if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
0463b5d6 4115 _err "_on_before_issue."
4116 return 1
4c3b3608 4117 fi
0463b5d6 4118
8a29fbc8 4119 _saved_account_key_hash="$(_readcaconf "CA_KEY_HASH")"
4120 _debug2 _saved_account_key_hash "$_saved_account_key_hash"
4c2a3841 4121
e8b54a50 4122 if [ -z "$ACCOUNT_URL" ] || [ -z "$_saved_account_key_hash" ] || [ "$_saved_account_key_hash" != "$(__calcAccountKeyHash)" ]; then
57e58ce7 4123 if ! _regAccount "$_accountkeylength"; then
85e1f4ea 4124 _on_issue_err "$_post_hook"
8a29fbc8 4125 return 1
4126 fi
57e58ce7 4127 else
4128 _debug "_saved_account_key_hash is not changed, skip register account."
166096dc 4129 fi
166096dc 4130
4c2a3841 4131 if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then
10afcaca 4132 _info "Signing from existing CSR."
4133 else
4134 _key=$(_readdomainconf Le_Keylength)
4135 _debug "Read key length:$_key"
c4b2e582 4136 if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ] || [ "$Le_ForceNewDomainKey" = "1" ]; then
d9c9114b 4137 if ! createDomainKey "$_main_domain" "$_key_length"; then
10afcaca 4138 _err "Create domain key error."
4139 _clearup
85e1f4ea 4140 _on_issue_err "$_post_hook"
10afcaca 4141 return 1
4142 fi
4143 fi
4144
02140ce7 4145 if ! _createcsr "$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
10afcaca 4146 _err "Create CSR error."
5ef501c5 4147 _clearup
85e1f4ea 4148 _on_issue_err "$_post_hook"
41e3eafa 4149 return 1
4150 fi
4c3b3608 4151 fi
10afcaca 4152
d9c9114b 4153 _savedomainconf "Le_Keylength" "$_key_length"
4c2a3841 4154
4c3b3608 4155 vlist="$Le_Vlist"
882ac74a 4156 _cleardomainconf "Le_Vlist"
cae203be 4157 _info "Getting domain auth token for each domain"
4c3b3608 4158 sep='#'
9d725af6 4159 dvsep=','
4c2a3841 4160 if [ -z "$vlist" ]; then
d2cde379 4161 if [ "$ACME_VERSION" = "2" ]; then
c1151b0d 4162 #make new order request
0093dc3d 4163 _identifiers="{\"type\":\"dns\",\"value\":\"$(_idn "$_main_domain")\"}"
38f1b4d2 4164 _w_index=1
674b5088 4165 while true; do
dd17124e 4166 d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")"
674b5088 4167 _w_index="$(_math "$_w_index" + 1)"
4168 _debug d "$d"
4169 if [ -z "$d" ]; then
4170 break
c1151b0d 4171 fi
0093dc3d 4172 _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn "$d")\"}"
c1151b0d 4173 done
4174 _debug2 _identifiers "$_identifiers"
4175 if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
4176 _err "Create new order error."
4177 _clearup
4178 _on_issue_err "$_post_hook"
4179 return 1
4180 fi
05aa26e6 4181 Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
fbdc5a0e 4182 _debug Le_LinkOrder "$Le_LinkOrder"
dbc44c08 4183 Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
d2cde379 4184 _debug Le_OrderFinalize "$Le_OrderFinalize"
4185 if [ -z "$Le_OrderFinalize" ]; then
78915896 4186 _err "Create new order error. Le_OrderFinalize not found. $response"
c1151b0d 4187 _clearup
4188 _on_issue_err "$_post_hook"
4189 return 1
4190 fi
4191
4192 #for dns manual mode
d2cde379 4193 _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize"
c1151b0d 4194
93de1e49 4195 _authorizations_seg="$(echo "$response" | _json_decode | _egrep_o '"authorizations" *: *\[[^\[]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
c1151b0d 4196 _debug2 _authorizations_seg "$_authorizations_seg"
4197 if [ -z "$_authorizations_seg" ]; then
4198 _err "_authorizations_seg not found."
4199 _clearup
4200 _on_issue_err "$_post_hook"
4201 return 1
4202 fi
4203
4204 #domain and authz map
4205 _authorizations_map=""
f8d22c48 4206 for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
c1151b0d 4207 _debug2 "_authz_url" "$_authz_url"
0483d841 4208 if ! _send_signed_request "$_authz_url"; then
c1151b0d 4209 _err "get to authz error."
263c38ca 4210 _err "_authorizations_seg" "$_authorizations_seg"
4211 _err "_authz_url" "$_authz_url"
c1151b0d 4212 _clearup
4213 _on_issue_err "$_post_hook"
4214 return 1
4215 fi
4216
4217 response="$(echo "$response" | _normalizeJson)"
4218 _debug2 response "$response"
4219 _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')"
72f54ca6 4220 if _contains "$response" "\"wildcard\" *: *true"; then
4221 _d="*.$_d"
4222 fi
c1151b0d 4223 _debug2 _d "$_d"
4224 _authorizations_map="$_d,$response
4225$_authorizations_map"
4226 done
4227 _debug2 _authorizations_map "$_authorizations_map"
4228 fi
4229
c1151b0d 4230 _index=0
a63b05a9 4231 _currentRoot=""
38f1b4d2 4232 _w_index=1
88bbe55b 4233 while true; do
88bbe55b 4234 d="$(echo "$_main_domain,$_alt_domains," | cut -d , -f "$_w_index")"
4235 _w_index="$(_math "$_w_index" + 1)"
4236 _debug d "$d"
4237 if [ -z "$d" ]; then
4238 break
4239 fi
ca7202eb 4240 _info "Getting webroot for domain" "$d"
c1151b0d 4241 _index=$(_math $_index + 1)
af1cc3b3 4242 _w="$(echo $_web_roots | cut -d , -f $_index)"
9d725af6 4243 _debug _w "$_w"
4c2a3841 4244 if [ "$_w" ]; then
a63b05a9 4245 _currentRoot="$_w"
4246 fi
4247 _debug "_currentRoot" "$_currentRoot"
4c2a3841 4248
a63b05a9 4249 vtype="$VTYPE_HTTP"
c1151b0d 4250 #todo, v2 wildcard force to use dns
3881f221 4251 if _startswith "$_currentRoot" "$W_DNS"; then
a63b05a9 4252 vtype="$VTYPE_DNS"
4253 fi
4c2a3841 4254
08681f4a 4255 if [ "$_currentRoot" = "$W_ALPN" ]; then
4256 vtype="$VTYPE_ALPN"
4257 fi
4258
c1151b0d 4259 if [ "$ACME_VERSION" = "2" ]; then
d04c6dd3 4260 _idn_d="$(_idn "$d")"
dbc43550
L
4261 _candidates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
4262 _debug2 _candidates "$_candidates"
4263 if [ "$(echo "$_candidates" | wc -l)" -gt 1 ]; then
4264 for _can in $_candidates; do
d04c6dd3 4265 if _startswith "$(echo "$_can" | tr '.' '|')" "$(echo "$_idn_d" | tr '.' '|'),"; then
dbc43550 4266 _candidates="$_can"
d04c6dd3 4267 break
4268 fi
4269 done
4270 fi
dbc43550 4271 response="$(echo "$_candidates" | sed "s/$_idn_d,//")"
c1151b0d 4272 _debug2 "response" "$response"
4273 if [ -z "$response" ]; then
4274 _err "get to authz error."
263c38ca 4275 _err "_authorizations_map" "$_authorizations_map"
c1151b0d 4276 _clearup
4277 _on_issue_err "$_post_hook"
4278 return 1
4279 fi
4280 else
4281 if ! __get_domain_new_authz "$d"; then
4282 _clearup
4283 _on_issue_err "$_post_hook"
4284 return 1
4285 fi
c4d8fd83 4286 fi
4287
4c2a3841 4288 if [ -z "$thumbprint" ]; then
339a8ad6 4289 thumbprint="$(__calc_account_thumbprint)"
4c3b3608 4290 fi
4291
dbc44c08 4292 entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
4c3b3608 4293 _debug entry "$entry"
9541ea6a 4294 keyauthorization=""
4c2a3841 4295 if [ -z "$entry" ]; then
9541ea6a 4296 if ! _startswith "$d" '*.'; then
4297 _debug "Not a wildcard domain, lets check whether the validation is already valid."
4298 if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
4299 _debug "$d is already valid."
4300 keyauthorization="$STATE_VERIFIED"
4301 _debug keyauthorization "$keyauthorization"
4302 fi
4303 fi
4304 if [ -z "$keyauthorization" ]; then
fc3a1817 4305 _err "Error, can not get domain token entry $d for $vtype"
9541ea6a 4306 _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
4307 if [ "$_supported_vtypes" ]; then
4308 _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
4309 fi
4310 _clearup
4311 _on_issue_err "$_post_hook"
4312 return 1
b51ed9bb 4313 fi
c1151b0d 4314 fi
f8b225e7 4315
9541ea6a 4316 if [ -z "$keyauthorization" ]; then
4317 token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
4318 _debug token "$token"
cae203be 4319
9541ea6a 4320 if [ -z "$token" ]; then
4321 _err "Error, can not get domain token $entry"
4322 _clearup
4323 _on_issue_err "$_post_hook"
4324 return 1
4325 fi
4326 if [ "$ACME_VERSION" = "2" ]; then
4327 uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
4328 else
4329 uri="$(echo "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
4330 fi
4331 _debug uri "$uri"
4c3b3608 4332
9541ea6a 4333 if [ -z "$uri" ]; then
4334 _err "Error, can not get domain uri. $entry"
4335 _clearup
4336 _on_issue_err "$_post_hook"
4337 return 1
4338 fi
4339 keyauthorization="$token.$thumbprint"
d35bf517 4340 _debug keyauthorization "$keyauthorization"
9541ea6a 4341
4342 if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
4343 _debug "$d is already verified."
4344 keyauthorization="$STATE_VERIFIED"
4345 _debug keyauthorization "$keyauthorization"
4346 fi
ec603bee 4347 fi
4348
a63b05a9 4349 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
4c3b3608 4350 _debug dvlist "$dvlist"
4c2a3841 4351
9d725af6 4352 vlist="$vlist$dvlist$dvsep"
4c3b3608 4353
4354 done
9d725af6 4355 _debug vlist "$vlist"
4c3b3608 4356 #add entry
b5ca9bba 4357 dns_entries=""
4c3b3608 4358 dnsadded=""
9d725af6 4359 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
1f7df33e 4360 _alias_index=1
4c2a3841 4361 for ventry in $ventries; do
ca7202eb 4362 d=$(echo "$ventry" | cut -d "$sep" -f 1)
4363 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
4364 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
4365 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
72f54ca6 4366 _debug d "$d"
4c2a3841 4367 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
69212114 4368 _debug "$d is already verified, skip $vtype."
fd536d37 4369 _alias_index="$(_math "$_alias_index" + 1)"
ec603bee 4370 continue
4371 fi
4372
4c2a3841 4373 if [ "$vtype" = "$VTYPE_DNS" ]; then
4c3b3608 4374 dnsadded='0'
72f54ca6 4375 _dns_root_d="$d"
4376 if _startswith "$_dns_root_d" "*."; then
4377 _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
4378 fi
875625b1 4379 _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
4380 _alias_index="$(_math "$_alias_index" + 1)"
4381 _debug "_d_alias" "$_d_alias"
4382 if [ "$_d_alias" ]; then
64821ad4 4383 if _startswith "$_d_alias" "$DNS_ALIAS_PREFIX"; then
4384 txtdomain="$(echo "$_d_alias" | sed "s/$DNS_ALIAS_PREFIX//")"
4385 else
4386 txtdomain="_acme-challenge.$_d_alias"
4387 fi
82b0ebb7 4388 dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot"
875625b1 4389 else
4390 txtdomain="_acme-challenge.$_dns_root_d"
82b0ebb7 4391 dns_entry="${_dns_root_d}${dvsep}_acme-challenge.$_dns_root_d$dvsep$dvsep$_currentRoot"
875625b1 4392 fi
82b0ebb7 4393
4c3b3608 4394 _debug txtdomain "$txtdomain"
11927a76 4395 txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
4c3b3608 4396 _debug txt "$txt"
a61fe418 4397
b50e701c 4398 d_api="$(_findHook "$_dns_root_d" $_SUB_FOLDER_DNSAPI "$_currentRoot")"
4c3b3608 4399 _debug d_api "$d_api"
82b0ebb7 4400
4401 dns_entry="$dns_entry$dvsep$txt${dvsep}$d_api"
4402 _debug2 dns_entry "$dns_entry"
4c2a3841 4403 if [ "$d_api" ]; then
a180b95c 4404 _debug "Found domain api file: $d_api"
4c3b3608 4405 else
3881f221 4406 if [ "$_currentRoot" != "$W_DNS" ]; then
4407 _err "Can not find dns api hook for: $_currentRoot"
4408 _info "You need to add the txt record manually."
4409 fi
5f8b60a0 4410 _info "$(__red "Add the following TXT record:")"
81772fb7 4411 _info "$(__red "Domain: '$(__green "$txtdomain")'")"
4412 _info "$(__red "TXT value: '$(__green "$txt")'")"
4413 _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")"
4414 _info "$(__red "so the resulting subdomain will be: $txtdomain")"
4c3b3608 4415 continue
4416 fi
4c2a3841 4417
73b8b120 4418 (
ca7202eb 4419 if ! . "$d_api"; then
73b8b120 4420 _err "Load file $d_api error. Please check your api file and try again."
4421 return 1
4422 fi
4c2a3841 4423
158f22f7 4424 addcommand="${_currentRoot}_add"
ca7202eb 4425 if ! _exists "$addcommand"; then
73b8b120 4426 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
4427 return 1
4428 fi
a180b95c 4429 _info "Adding txt value: $txt for domain: $txtdomain"
ca7202eb 4430 if ! $addcommand "$txtdomain" "$txt"; then
73b8b120 4431 _err "Error add txt for domain:$txtdomain"
4432 return 1
4433 fi
a180b95c 4434 _info "The txt record is added: Success."
73b8b120 4435 )
4c2a3841 4436
4437 if [ "$?" != "0" ]; then
ea722da3 4438 _on_issue_err "$_post_hook" "$vlist"
545f2355 4439 _clearup
4c3b3608 4440 return 1
4441 fi
82b0ebb7 4442 dns_entries="$dns_entries$dns_entry
4443"
4444 _debug2 "$dns_entries"
4c3b3608 4445 dnsadded='1'
4446 fi
4447 done
4448
4c2a3841 4449 if [ "$dnsadded" = '0' ]; then
4450 _savedomainconf "Le_Vlist" "$vlist"
4c3b3608 4451 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
a8b62261 4452 _err "Please add the TXT records to the domains, and re-run with --renew."
5f8b60a0 4453 _on_issue_err "$_post_hook"
545f2355 4454 _clearup
4c3b3608 4455 return 1
4456 fi
4c2a3841 4457
4c3b3608 4458 fi
4c2a3841 4459
b5ca9bba 4460 if [ "$dns_entries" ]; then
4c2a3841 4461 if [ -z "$Le_DNSSleep" ]; then
427c2780 4462 _info "Let's check each DNS record now. Sleep 20 seconds first."
b5ca9bba 4463 _sleep 20
4464 if ! _check_dns_entries; then
4465 _err "check dns error."
4466 _on_issue_err "$_post_hook"
4467 _clearup
4468 return 1
4469 fi
0e38c60d 4470 else
4c2a3841 4471 _savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
b5ca9bba 4472 _info "Sleep $(__green $Le_DNSSleep) seconds for the txt records to take effect"
4473 _sleep "$Le_DNSSleep"
0e38c60d 4474 fi
4c3b3608 4475 fi
4c2a3841 4476
5d943a35 4477 NGINX_RESTORE_VLIST=""
4c3b3608 4478 _debug "ok, let's start to verify"
a63b05a9 4479
0463b5d6 4480 _ncIndex=1
9d725af6 4481 ventries=$(echo "$vlist" | tr "$dvsep" ' ')
4c2a3841 4482 for ventry in $ventries; do
ca7202eb 4483 d=$(echo "$ventry" | cut -d "$sep" -f 1)
4484 keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
4485 uri=$(echo "$ventry" | cut -d "$sep" -f 3)
4486 vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
4487 _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
ec603bee 4488
4c2a3841 4489 if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
ec603bee 4490 _info "$d is already verified, skip $vtype."
4491 continue
4492 fi
4493
dd068467 4494 _info "Verifying: $d"
4c3b3608 4495 _debug "d" "$d"
4496 _debug "keyauthorization" "$keyauthorization"
4497 _debug "uri" "$uri"
4498 removelevel=""
e22bcf7c 4499 token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
a63b05a9 4500
4501 _debug "_currentRoot" "$_currentRoot"
4502
4c2a3841 4503 if [ "$vtype" = "$VTYPE_HTTP" ]; then
4504 if [ "$_currentRoot" = "$NO_VALUE" ]; then
4c3b3608 4505 _info "Standalone mode server"
85e1f4ea 4506 _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
0463b5d6 4507 _ncIndex="$(_math $_ncIndex + 1)"
3794b5cb 4508 _startserver "$keyauthorization" "$_ncaddr"
4c2a3841 4509 if [ "$?" != "0" ]; then
5ef501c5 4510 _clearup
58e4d337 4511 _on_issue_err "$_post_hook" "$vlist"
6fc1447f 4512 return 1
4513 fi
5dbf664a 4514 sleep 1
ca7202eb 4515 _debug serverproc "$serverproc"
0e44f587 4516 elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
4517 _info "Stateless mode for domain:$d"
4518 _sleep 1
9d725af6 4519 elif _startswith "$_currentRoot" "$NGINX"; then
4520 _info "Nginx mode for domain:$d"
4521 #set up nginx server
4522 FOUND_REAL_NGINX_CONF=""
4523 BACKUP_NGINX_CONF=""
4524 if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
4525 _clearup
58e4d337 4526 _on_issue_err "$_post_hook" "$vlist"
9d725af6 4527 return 1
03f8d6e9 4528 fi
302c41ed 4529
03f8d6e9 4530 if [ "$FOUND_REAL_NGINX_CONF" ]; then
9d725af6 4531 _realConf="$FOUND_REAL_NGINX_CONF"
4532 _backup="$BACKUP_NGINX_CONF"
4533 _debug _realConf "$_realConf"
5d943a35 4534 NGINX_RESTORE_VLIST="$d$sep$_realConf$sep$_backup$dvsep$NGINX_RESTORE_VLIST"
9d725af6 4535 fi
4536 _sleep 1
4c3b3608 4537 else
4c2a3841 4538 if [ "$_currentRoot" = "apache" ]; then
6f930641 4539 wellknown_path="$ACME_DIR"
4540 else
a63b05a9 4541 wellknown_path="$_currentRoot/.well-known/acme-challenge"
4c2a3841 4542 if [ ! -d "$_currentRoot/.well-known" ]; then
6f930641 4543 removelevel='1'
4c2a3841 4544 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ]; then
6f930641 4545 removelevel='2'
4546 else
4547 removelevel='3'
4548 fi
4c3b3608 4549 fi
6f930641 4550
4c3b3608 4551 _debug wellknown_path "$wellknown_path"
6f930641 4552
4c3b3608 4553 _debug "writing token:$token to $wellknown_path/$token"
4554
4555 mkdir -p "$wellknown_path"
93fc48a2 4556
4c2a3841 4557 if ! printf "%s" "$keyauthorization" >"$wellknown_path/$token"; then
93fc48a2 4558 _err "$d:Can not write token to file : $wellknown_path/$token"
4559 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4560 _clearup
58e4d337 4561 _on_issue_err "$_post_hook" "$vlist"
93fc48a2 4562 return 1
4563 fi
4564
4c2a3841 4565 if [ ! "$usingApache" ]; then
44edb2bd 4566 if webroot_owner=$(_stat "$_currentRoot"); then
32fdc196 4567 _debug "Changing owner/group of .well-known to $webroot_owner"
c87cd0de 4568 if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
4569 _debug "$(cat "$_EXEC_TEMP_ERR")"
4570 _exec_err >/dev/null 2>&1
4571 fi
32fdc196 4572 else
b54ce310 4573 _debug "not changing owner/group of webroot"
32fdc196 4574 fi
df886ffa 4575 fi
4c2a3841 4576
4c3b3608 4577 fi
08681f4a 4578 elif [ "$vtype" = "$VTYPE_ALPN" ]; then
4579 acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
4580 _debug acmevalidationv1 "$acmevalidationv1"
4581 if ! _starttlsserver "$d" "" "$Le_TLSPort" "$keyauthorization" "$_ncaddr" "$acmevalidationv1"; then
4582 _err "Start tls server error."
4583 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4584 _clearup
4585 _on_issue_err "$_post_hook" "$vlist"
4586 return 1
4587 fi
4c3b3608 4588 fi
4c2a3841 4589
920cab6f 4590 if ! __trigger_validation "$uri" "$keyauthorization" "$vtype"; then
c4d8fd83 4591 _err "$d:Can not get challenge: $response"
4592 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4593 _clearup
58e4d337 4594 _on_issue_err "$_post_hook" "$vlist"
c4d8fd83 4595 return 1
4596 fi
4c2a3841 4597
c1151b0d 4598 if [ "$code" ] && [ "$code" != '202' ]; then
8bd12ed0 4599 if [ "$code" = '200' ]; then
c1151b0d 4600 _debug "trigger validation code: $code"
4601 else
8bd12ed0
K
4602 _err "$d:Challenge error: $response"
4603 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4604 _clearup
4605 _on_issue_err "$_post_hook" "$vlist"
4606 return 1
c1151b0d 4607 fi
4c3b3608 4608 fi
4c2a3841 4609
6fc1447f 4610 waittimes=0
4c2a3841 4611 if [ -z "$MAX_RETRY_TIMES" ]; then
6fc1447f 4612 MAX_RETRY_TIMES=30
4613 fi
4c2a3841 4614
4615 while true; do
0c538f75 4616 waittimes=$(_math "$waittimes" + 1)
4c2a3841 4617 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then
6fc1447f 4618 _err "$d:Timeout"
4619 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4620 _clearup
58e4d337 4621 _on_issue_err "$_post_hook" "$vlist"
6fc1447f 4622 return 1
4623 fi
4c2a3841 4624
5dbf664a 4625 _debug "sleep 2 secs to verify"
4626 sleep 2
4c3b3608 4627 _debug "checking"
0483d841 4628 if [ "$ACME_VERSION" = "2" ]; then
4629 _send_signed_request "$uri"
4630 else
4631 response="$(_get "$uri")"
4632 fi
4c2a3841 4633 if [ "$?" != "0" ]; then
c60883ef 4634 _err "$d:Verify error:$response"
a63b05a9 4635 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 4636 _clearup
58e4d337 4637 _on_issue_err "$_post_hook" "$vlist"
4c3b3608 4638 return 1
4639 fi
9aaf36cd 4640 _debug2 original "$response"
4c2a3841 4641
4642 response="$(echo "$response" | _normalizeJson)"
7012b91f 4643 _debug2 response "$response"
4c2a3841 4644
4645 status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
4646 if [ "$status" = "valid" ]; then
93f3098a 4647 _info "$(__green Success)"
ca7202eb 4648 _stopserver "$serverproc"
4c3b3608 4649 serverproc=""
a63b05a9 4650 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c2a3841 4651 break
4c3b3608 4652 fi
4c2a3841 4653
4654 if [ "$status" = "invalid" ]; then
d0d74907 4655 error="$(echo "$response" | _egrep_o '"error":\{[^\}]*')"
4c2a3841 4656 _debug2 error "$error"
4657 errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
4658 _debug2 errordetail "$errordetail"
4659 if [ "$errordetail" ]; then
4660 _err "$d:Verify error:$errordetail"
4661 else
4662 _err "$d:Verify error:$error"
4663 fi
4664 if [ "$DEBUG" ]; then
4665 if [ "$vtype" = "$VTYPE_HTTP" ]; then
4666 _debug "Debug: get token url."
4667 _get "http://$d/.well-known/acme-challenge/$token" "" 1
4668 fi
4669 fi
a63b05a9 4670 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 4671 _clearup
58e4d337 4672 _on_issue_err "$_post_hook" "$vlist"
4c2a3841 4673 return 1
4c3b3608 4674 fi
4c2a3841 4675
4676 if [ "$status" = "pending" ]; then
4c3b3608 4677 _info "Pending"
93740c99 4678 elif [ "$status" = "processing" ]; then
4679 _info "Processing"
4c3b3608 4680 else
4c2a3841 4681 _err "$d:Verify error:$response"
a63b05a9 4682 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 4683 _clearup
58e4d337 4684 _on_issue_err "$_post_hook" "$vlist"
4c3b3608 4685 return 1
4686 fi
4c2a3841 4687
4c3b3608 4688 done
4c2a3841 4689
4c3b3608 4690 done
4691
4692 _clearup
4693 _info "Verify finished, start to sign."
11927a76 4694 der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
4c2a3841 4695
c1151b0d 4696 if [ "$ACME_VERSION" = "2" ]; then
bd04638d 4697 _info "Lets finalize the order."
4698 _info "Le_OrderFinalize" "$Le_OrderFinalize"
d2cde379 4699 if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then
c1151b0d 4700 _err "Sign failed."
4701 _on_issue_err "$_post_hook"
4702 return 1
4703 fi
4704 if [ "$code" != "200" ]; then
e7f7e96d 4705 _err "Sign failed, finalize code is not 200."
668c43ab 4706 _err "$response"
c1151b0d 4707 _on_issue_err "$_post_hook"
4708 return 1
4709 fi
e7f7e96d 4710 if [ -z "$Le_LinkOrder" ]; then
0712e989 4711 Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n" | cut -d ":" -f 2-)"
e7f7e96d 4712 fi
fbdc5a0e 4713
e7f7e96d 4714 _savedomainconf "Le_LinkOrder" "$Le_LinkOrder"
4715
4716 _link_cert_retry=0
ff9be30f 4717 _MAX_CERT_RETRY=30
a3d8b993 4718 while [ "$_link_cert_retry" -lt "$_MAX_CERT_RETRY" ]; do
e7f7e96d 4719 if _contains "$response" "\"status\":\"valid\""; then
4720 _debug "Order status is valid."
d0d74907 4721 Le_LinkCert="$(echo "$response" | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
e7f7e96d 4722 _debug Le_LinkCert "$Le_LinkCert"
4723 if [ -z "$Le_LinkCert" ]; then
4724 _err "Sign error, can not find Le_LinkCert"
4725 _err "$response"
4726 _on_issue_err "$_post_hook"
4727 return 1
4728 fi
4729 break
4730 elif _contains "$response" "\"processing\""; then
4731 _info "Order status is processing, lets sleep and retry."
1fe8235a 4732 _retryafter=$(echo "$responseHeaders" | grep -i "^Retry-After *:" | cut -d : -f 2 | tr -d ' ' | tr -d '\r')
4733 _debug "_retryafter" "$_retryafter"
4734 if [ "$_retryafter" ]; then
4735 _info "Retry after: $_retryafter"
4736 _sleep $_retryafter
4737 else
4738 _sleep 2
4739 fi
e7f7e96d 4740 else
4741 _err "Sign error, wrong status"
4742 _err "$response"
4743 _on_issue_err "$_post_hook"
4744 return 1
4745 fi
fbdc5a0e 4746 #the order is processing, so we are going to poll order status
4747 if [ -z "$Le_LinkOrder" ]; then
4748 _err "Sign error, can not get order link location header"
4749 _err "responseHeaders" "$responseHeaders"
4750 _on_issue_err "$_post_hook"
4751 return 1
4752 fi
4753 _info "Polling order status: $Le_LinkOrder"
e7f7e96d 4754 if ! _send_signed_request "$Le_LinkOrder"; then
4755 _err "Sign failed, can not post to Le_LinkOrder cert:$Le_LinkOrder."
4756 _err "$response"
4757 _on_issue_err "$_post_hook"
4758 return 1
4759 fi
4760 _link_cert_retry="$(_math $_link_cert_retry + 1)"
4761 done
4c2a3841 4762
e7f7e96d 4763 if [ -z "$Le_LinkCert" ]; then
4764 _err "Sign failed, can not get Le_LinkCert, retry time limit."
4765 _err "$response"
4766 _on_issue_err "$_post_hook"
4767 return 1
4768 fi
bd04638d 4769 _info "Downloading cert."
4770 _info "Le_LinkCert" "$Le_LinkCert"
f2acdd27 4771 if ! _send_signed_request "$Le_LinkCert"; then
668c43ab 4772 _err "Sign failed, can not download cert:$Le_LinkCert."
4773 _err "$response"
c1151b0d 4774 _on_issue_err "$_post_hook"
4775 return 1
4776 fi
4c3b3608 4777
f2acdd27 4778 echo "$response" >"$CERT_PATH"
e3ebd582 4779 _split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH"
4780
d5d38b33 4781 if [ "$_preferred_chain" ] && [ -f "$CERT_FULLCHAIN_PATH" ]; then
4782 if ! _match_issuer "$CERT_FULLCHAIN_PATH" "$_preferred_chain"; then
e3ebd582 4783 rels="$(echo "$responseHeaders" | tr -d ' <>' | grep -i "^link:" | grep -i 'rel="alternate"' | cut -d : -f 2- | cut -d ';' -f 1)"
4784 _debug2 "rels" "$rels"
4785 for rel in $rels; do
4786 _info "Try rel: $rel"
4787 if ! _send_signed_request "$rel"; then
4788 _err "Sign failed, can not download cert:$rel"
4789 _err "$response"
4790 continue
4791 fi
4792 _relcert="$CERT_PATH.alt"
4793 _relfullchain="$CERT_FULLCHAIN_PATH.alt"
4794 _relca="$CA_CERT_PATH.alt"
4795 echo "$response" >"$_relcert"
4796 _split_cert_chain "$_relcert" "$_relfullchain" "$_relca"
d5d38b33 4797 if _match_issuer "$_relfullchain" "$_preferred_chain"; then
e3ebd582 4798 _info "Matched issuer in: $rel"
4799 cat $_relcert >"$CERT_PATH"
4800 cat $_relfullchain >"$CERT_FULLCHAIN_PATH"
4801 cat $_relca >"$CA_CERT_PATH"
4802 break
4803 fi
4804 done
4805 fi
1c35f46b 4806 fi
c1151b0d 4807 else
4808 if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
323febe8 4809 _err "Sign failed. $response"
c1151b0d 4810 _on_issue_err "$_post_hook"
4811 return 1
4812 fi
4813 _rcert="$response"
36a7a840 4814 Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
4c2a3841 4815 echo "$BEGIN_CERT" >"$CERT_PATH"
4c3b3608 4816
72518d48 4817 #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
4818 # _debug "Get cert failed. Let's try last response."
3c07f57a 4819 # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
72518d48 4820 #fi
4c2a3841 4821
4822 if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
72518d48 4823 _debug "Try cert link."
4c2a3841 4824 _get "$Le_LinkCert" | _base64 "multiline" >>"$CERT_PATH"
d404e92d 4825 fi
4826
4c2a3841 4827 echo "$END_CERT" >>"$CERT_PATH"
c1151b0d 4828 fi
4829
4830 _debug "Le_LinkCert" "$Le_LinkCert"
4831 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
4832
183063a2 4833 if [ -z "$Le_LinkCert" ] || ! _checkcert "$CERT_PATH"; then
716f7277 4834 response="$(echo "$response" | _dbase64 "multiline" | tr -d '\0' | _normalizeJson)"
183063a2 4835 _err "Sign failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
4836 _on_issue_err "$_post_hook"
4837 return 1
4838 fi
4839
c1151b0d 4840 if [ "$Le_LinkCert" ]; then
43822d37 4841 _info "$(__green "Cert success.")"
4c3b3608 4842 cat "$CERT_PATH"
5980ebc7 4843
4c2a3841 4844 _info "Your cert is in $(__green " $CERT_PATH ")"
4845
4846 if [ -f "$CERT_KEY_PATH" ]; then
4847 _info "Your cert key is in $(__green " $CERT_KEY_PATH ")"
5980ebc7 4848 fi
4849
bd04638d 4850 if [ ! "$USER_PATH" ] || [ ! "$_ACME_IN_CRON" ]; then
281aa349 4851 USER_PATH="$PATH"
4852 _saveaccountconf "USER_PATH" "$USER_PATH"
4853 fi
4c3b3608 4854 fi
4c3b3608 4855
1c35f46b 4856 if [ "$ACME_VERSION" = "2" ]; then
4857 _debug "v2 chain."
4858 else
183063a2 4859 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
1c35f46b 4860 Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
d8ba26e6 4861
1c35f46b 4862 if [ "$Le_LinkIssuer" ]; then
4863 if ! _contains "$Le_LinkIssuer" ":"; then
4864 _info "$(__red "Relative issuer link found.")"
4865 Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer"
d8ba26e6 4866 fi
1c35f46b 4867 _debug Le_LinkIssuer "$Le_LinkIssuer"
4868 _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
d8ba26e6 4869
1c35f46b 4870 _link_issuer_retry=0
4871 _MAX_ISSUER_RETRY=5
4872 while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
4873 _debug _link_issuer_retry "$_link_issuer_retry"
4874 if [ "$ACME_VERSION" = "2" ]; then
0483d841 4875 if _send_signed_request "$Le_LinkIssuer"; then
4876 echo "$response" >"$CA_CERT_PATH"
1c35f46b 4877 break
4878 fi
4879 else
4880 if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
4881 echo "$BEGIN_CERT" >"$CA_CERT_PATH"
4882 _base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
4883 echo "$END_CERT" >>"$CA_CERT_PATH"
0f120c41 4884 if ! _checkcert "$CA_CERT_PATH"; then
183063a2 4885 _err "Can not get the ca cert."
4886 break
4887 fi
1c35f46b 4888 cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
4889 rm -f "$CA_CERT_PATH.der"
4890 break
4891 fi
c1151b0d 4892 fi
1c35f46b 4893 _link_issuer_retry=$(_math $_link_issuer_retry + 1)
4894 _sleep "$_link_issuer_retry"
4895 done
4896 if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
4897 _err "Max retry for issuer ca cert is reached."
d8ba26e6 4898 fi
1c35f46b 4899 else
4900 _debug "No Le_LinkIssuer header found."
d8ba26e6 4901 fi
4c3b3608 4902 fi
1c35f46b 4903 [ -f "$CA_CERT_PATH" ] && _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
4904 [ -f "$CERT_FULLCHAIN_PATH" ] && _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
4c2a3841 4905
3aae1ae3 4906 Le_CertCreateTime=$(_time)
4c2a3841 4907 _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
4908
4909 Le_CertCreateTimeStr=$(date -u)
4910 _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
4911
ec67a1b2 4912 if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then
4913 Le_RenewalDays="$DEFAULT_RENEW"
054cb72e 4914 else
4c2a3841 4915 _savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
13d7cae9 4916 fi
4c2a3841 4917
4918 if [ "$CA_BUNDLE" ]; then
78009539
PS
4919 _saveaccountconf CA_BUNDLE "$CA_BUNDLE"
4920 else
4921 _clearaccountconf "CA_BUNDLE"
4922 fi
4923
2aa75f03 4924 if [ "$CA_PATH" ]; then
4925 _saveaccountconf CA_PATH "$CA_PATH"
4926 else
4927 _clearaccountconf "CA_PATH"
4928 fi
78009539 4929
4c2a3841 4930 if [ "$HTTPS_INSECURE" ]; then
fac1e367 4931 _saveaccountconf HTTPS_INSECURE "$HTTPS_INSECURE"
4932 else
4c2a3841 4933 _clearaccountconf "HTTPS_INSECURE"
13d7cae9 4934 fi
00a50605 4935
4c2a3841 4936 if [ "$Le_Listen_V4" ]; then
4937 _savedomainconf "Le_Listen_V4" "$Le_Listen_V4"
50827188 4938 _cleardomainconf Le_Listen_V6
4c2a3841 4939 elif [ "$Le_Listen_V6" ]; then
4940 _savedomainconf "Le_Listen_V6" "$Le_Listen_V6"
50827188 4941 _cleardomainconf Le_Listen_V4
4942 fi
f6dcd989 4943
c4b2e582 4944 if [ "$Le_ForceNewDomainKey" = "1" ]; then
4945 _savedomainconf "Le_ForceNewDomainKey" "$Le_ForceNewDomainKey"
4946 else
4947 _cleardomainconf Le_ForceNewDomainKey
4948 fi
4949
ca7202eb 4950 Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
4c2a3841 4951
ca7202eb 4952 Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
4c2a3841 4953 _savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
4954
ca7202eb 4955 Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
4c2a3841 4956 _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
f6dcd989 4957
85e1f4ea 4958 if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
4959 _savedomainconf "Le_RealCertPath" "$_real_cert"
4960 _savedomainconf "Le_RealCACertPath" "$_real_ca"
4961 _savedomainconf "Le_RealKeyPath" "$_real_key"
7690f73e 4962 _savedomainconf "Le_ReloadCmd" "$_reload_cmd" "base64"
85e1f4ea 4963 _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
ce8dca7a 4964 if ! _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"; then
4965 return 1
4966 fi
01f54558 4967 fi
4c0d3f1b 4968
ce8dca7a 4969 if ! _on_issue_success "$_post_hook" "$_renew_hook"; then
4970 _err "Call hook error."
4971 return 1
4972 fi
4c3b3608 4973}
4974
e3ebd582 4975#in_out_cert out_fullchain out out_ca
4976_split_cert_chain() {
4977 _certf="$1"
4978 _fullchainf="$2"
4979 _caf="$3"
4980 if [ "$(grep -- "$BEGIN_CERT" "$_certf" | wc -l)" -gt "1" ]; then
4981 _debug "Found cert chain"
4982 cat "$_certf" >"$_fullchainf"
4983 _end_n="$(grep -n -- "$END_CERT" "$_fullchainf" | _head_n 1 | cut -d : -f 1)"
4984 _debug _end_n "$_end_n"
4985 sed -n "1,${_end_n}p" "$_fullchainf" >"$_certf"
4986 _end_n="$(_math $_end_n + 1)"
4987 sed -n "${_end_n},9999p" "$_fullchainf" >"$_caf"
4988 fi
4989}
4990
43822d37 4991#domain [isEcc]
4c3b3608 4992renew() {
4993 Le_Domain="$1"
4c2a3841 4994 if [ -z "$Le_Domain" ]; then
43822d37 4995 _usage "Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
4c3b3608 4996 return 1
4997 fi
4998
43822d37 4999 _isEcc="$2"
5000
e799ef29 5001 _initpath "$Le_Domain" "$_isEcc"
43822d37 5002
e2053b22 5003 _info "$(__green "Renew: '$Le_Domain'")"
4c2a3841 5004 if [ ! -f "$DOMAIN_CONF" ]; then
43822d37 5005 _info "'$Le_Domain' is not a issued domain, skip."
acae0ac2 5006 return $RENEW_SKIP
4c3b3608 5007 fi
4c2a3841 5008
5009 if [ "$Le_RenewalDays" ]; then
1e6b68f5 5010 _savedomainconf Le_RenewalDays "$Le_RenewalDays"
5011 fi
5012
8663fb7e 5013 . "$DOMAIN_CONF"
c5f1cca3 5014 _debug Le_API "$Le_API"
f2add8de 5015
5016 if [ "$Le_API" = "$LETSENCRYPT_CA_V1" ]; then
5017 _cleardomainconf Le_API
5018 Le_API="$DEFAULT_CA"
5019 fi
5020 if [ "$Le_API" = "$LETSENCRYPT_STAGING_CA_V1" ]; then
5021 _cleardomainconf Le_API
5022 Le_API="$DEFAULT_STAGING_CA"
5023 fi
5024
4c2a3841 5025 if [ "$Le_API" ]; then
48d9a8c1 5026 export ACME_DIRECTORY="$Le_API"
c4236e58 5027 #reload ca configs
5028 ACCOUNT_KEY_PATH=""
5029 ACCOUNT_JSON_PATH=""
5030 CA_CONF=""
5031 _debug3 "initpath again."
5032 _initpath "$Le_Domain" "$_isEcc"
5c48e139 5033 fi
4c2a3841 5034
5035 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
e2053b22 5036 _info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
5037 _info "Add '$(__red '--force')' to force to renew."
e799ef29 5038 return "$RENEW_SKIP"
4c3b3608 5039 fi
4c2a3841 5040
bd04638d 5041 if [ "$_ACME_IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then
c4d0aec5 5042 _info "Skip invalid cert for: $Le_Domain"
acae0ac2 5043 return $RENEW_SKIP
c4d0aec5 5044 fi
5045
bd04638d 5046 _ACME_IS_RENEW="1"
7690f73e 5047 Le_ReloadCmd="$(_readdomainconf Le_ReloadCmd)"
c7257bcf 5048 Le_PreHook="$(_readdomainconf Le_PreHook)"
5049 Le_PostHook="$(_readdomainconf Le_PostHook)"
5050 Le_RenewHook="$(_readdomainconf Le_RenewHook)"
e3ebd582 5051 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" "$Le_ChallengeAlias" "$Le_Preferred_Chain"
e799ef29 5052 res="$?"
4c2a3841 5053 if [ "$res" != "0" ]; then
e799ef29 5054 return "$res"
a61fe418 5055 fi
4c2a3841 5056
5057 if [ "$Le_DeployHook" ]; then
93bce1b2 5058 _deploy "$Le_Domain" "$Le_DeployHook"
e799ef29 5059 res="$?"
a61fe418 5060 fi
4c2a3841 5061
bd04638d 5062 _ACME_IS_RENEW=""
4c3b3608 5063
e799ef29 5064 return "$res"
4c3b3608 5065}
5066
cc179731 5067#renewAll [stopRenewOnError]
4c3b3608 5068renewAll() {
5069 _initpath
cc179731 5070 _stopRenewOnError="$1"
5071 _debug "_stopRenewOnError" "$_stopRenewOnError"
5072 _ret="0"
b50e701c 5073 _success_msg=""
5074 _error_msg=""
5075 _skipped_msg=""
c6b68551 5076 _error_level=$NOTIFY_LEVEL_SKIP
5077 _notify_code=$RENEW_SKIP
f803c6c0 5078 _set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
5079 _debug "_set_level" "$_set_level"
e591d5cf 5080 for di in "${CERT_HOME}"/*.*/; do
5081 _debug di "$di"
44483dba 5082 if ! [ -d "$di" ]; then
3498a585 5083 _debug "Not directory, skip: $di"
5084 continue
5085 fi
e591d5cf 5086 d=$(basename "$di")
201aa244 5087 _debug d "$d"
43822d37 5088 (
201aa244 5089 if _endswith "$d" "$ECC_SUFFIX"; then
5090 _isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2)
5091 d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
43822d37 5092 fi
5093 renew "$d" "$_isEcc"
4d2f38b0 5094 )
cc179731 5095 rc="$?"
5096 _debug "Return code: $rc"
c6b68551 5097 if [ "$rc" = "0" ]; then
5098 if [ $_error_level -gt $NOTIFY_LEVEL_RENEW ]; then
5099 _error_level="$NOTIFY_LEVEL_RENEW"
5100 _notify_code=0
5101 fi
bd04638d 5102 if [ "$_ACME_IN_CRON" ]; then
f803c6c0 5103 if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then
c6b68551 5104 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5105 _send_notify "Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0
5106 fi
5107 fi
5108 fi
5109 _success_msg="${_success_msg} $d
b50e701c 5110"
c6b68551 5111 elif [ "$rc" = "$RENEW_SKIP" ]; then
5112 if [ $_error_level -gt $NOTIFY_LEVEL_SKIP ]; then
5113 _error_level="$NOTIFY_LEVEL_SKIP"
5114 _notify_code=$RENEW_SKIP
5115 fi
bd04638d 5116 if [ "$_ACME_IN_CRON" ]; then
f803c6c0 5117 if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then
c6b68551 5118 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5119 _send_notify "Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP"
5120 fi
b50e701c 5121 fi
cc179731 5122 fi
c6b68551 5123 _info "Skipped $d"
5124 _skipped_msg="${_skipped_msg} $d
5125"
b50e701c 5126 else
c6b68551 5127 if [ $_error_level -gt $NOTIFY_LEVEL_ERROR ]; then
5128 _error_level="$NOTIFY_LEVEL_ERROR"
5129 _notify_code=1
5130 fi
bd04638d 5131 if [ "$_ACME_IN_CRON" ]; then
f803c6c0 5132 if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then
c6b68551 5133 if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
5134 _send_notify "Renew $d error" "There is an error." "$NOTIFY_HOOK" 1
5135 fi
5136 fi
5137 fi
5138 _error_msg="${_error_msg} $d
b50e701c 5139"
c6b68551 5140 if [ "$_stopRenewOnError" ]; then
5141 _err "Error renew $d, stop now."
5142 _ret="$rc"
5143 break
5144 else
5145 _ret="$rc"
5146 _err "Error renew $d."
5147 fi
cc179731 5148 fi
4c3b3608 5149 done
c6b68551 5150 _debug _error_level "$_error_level"
a2738e85 5151 _debug _set_level "$_set_level"
bd04638d 5152 if [ "$_ACME_IN_CRON" ] && [ $_error_level -le $_set_level ]; then
b50e701c 5153 if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then
5154 _msg_subject="Renew"
5155 if [ "$_error_msg" ]; then
5156 _msg_subject="${_msg_subject} Error"
c6b68551 5157 _msg_data="Error certs:
5158${_error_msg}
5159"
b50e701c 5160 fi
5161 if [ "$_success_msg" ]; then
5162 _msg_subject="${_msg_subject} Success"
c6b68551 5163 _msg_data="${_msg_data}Success certs:
5164${_success_msg}
5165"
b50e701c 5166 fi
5167 if [ "$_skipped_msg" ]; then
5168 _msg_subject="${_msg_subject} Skipped"
c6b68551 5169 _msg_data="${_msg_data}Skipped certs:
5170${_skipped_msg}
b50e701c 5171"
c6b68551 5172 fi
5173
5174 _send_notify "$_msg_subject" "$_msg_data" "$NOTIFY_HOOK" "$_notify_code"
b50e701c 5175 fi
5176 fi
5177
201aa244 5178 return "$_ret"
4c3b3608 5179}
5180
10afcaca 5181#csr webroot
4c2a3841 5182signcsr() {
10afcaca 5183 _csrfile="$1"
5184 _csrW="$2"
5185 if [ -z "$_csrfile" ] || [ -z "$_csrW" ]; then
5186 _usage "Usage: $PROJECT_ENTRY --signcsr --csr mycsr.csr -w /path/to/webroot/a.com/ "
5187 return 1
5188 fi
5189
875625b1 5190 _real_cert="$3"
5191 _real_key="$4"
5192 _real_ca="$5"
5193 _reload_cmd="$6"
5194 _real_fullchain="$7"
5195 _pre_hook="${8}"
5196 _post_hook="${9}"
5197 _renew_hook="${10}"
5198 _local_addr="${11}"
5199 _challenge_alias="${12}"
5200
10afcaca 5201 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
4c2a3841 5202 if [ "$?" != "0" ]; then
10afcaca 5203 _err "Can not read subject from csr: $_csrfile"
5204 return 1
5205 fi
ad752b31 5206 _debug _csrsubj "$_csrsubj"
2c9ed4c5 5207 if _contains "$_csrsubj" ' ' || ! _contains "$_csrsubj" '.'; then
5208 _info "It seems that the subject: $_csrsubj is not a valid domain name. Drop it."
5209 _csrsubj=""
5210 fi
10afcaca 5211
5212 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
4c2a3841 5213 if [ "$?" != "0" ]; then
10afcaca 5214 _err "Can not read domain list from csr: $_csrfile"
5215 return 1
5216 fi
5217 _debug "_csrdomainlist" "$_csrdomainlist"
4c2a3841 5218
5219 if [ -z "$_csrsubj" ]; then
ad752b31 5220 _csrsubj="$(_getfield "$_csrdomainlist" 1)"
5221 _debug _csrsubj "$_csrsubj"
5222 _csrdomainlist="$(echo "$_csrdomainlist" | cut -d , -f 2-)"
5223 _debug "_csrdomainlist" "$_csrdomainlist"
5224 fi
4c2a3841 5225
5226 if [ -z "$_csrsubj" ]; then
ad752b31 5227 _err "Can not read subject from csr: $_csrfile"
5228 return 1
5229 fi
4c2a3841 5230
10afcaca 5231 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
4c2a3841 5232 if [ "$?" != "0" ] || [ -z "$_csrkeylength" ]; then
10afcaca 5233 _err "Can not read key length from csr: $_csrfile"
5234 return 1
5235 fi
4c2a3841 5236
cd9fb3b6 5237 if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then
5238 export ACME_VERSION=2
5239 fi
10afcaca 5240 _initpath "$_csrsubj" "$_csrkeylength"
5241 mkdir -p "$DOMAIN_PATH"
4c2a3841 5242
10afcaca 5243 _info "Copy csr to: $CSR_PATH"
5244 cp "$_csrfile" "$CSR_PATH"
4c2a3841 5245
875625b1 5246 issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias"
4c2a3841 5247
10afcaca 5248}
5249
5250showcsr() {
4c2a3841 5251 _csrfile="$1"
10afcaca 5252 _csrd="$2"
5253 if [ -z "$_csrfile" ] && [ -z "$_csrd" ]; then
5254 _usage "Usage: $PROJECT_ENTRY --showcsr --csr mycsr.csr"
5255 return 1
5256 fi
5257
5258 _initpath
4c2a3841 5259
10afcaca 5260 _csrsubj=$(_readSubjectFromCSR "$_csrfile")
4c2a3841 5261 if [ "$?" != "0" ] || [ -z "$_csrsubj" ]; then
10afcaca 5262 _err "Can not read subject from csr: $_csrfile"
5263 return 1
5264 fi
4c2a3841 5265
10afcaca 5266 _info "Subject=$_csrsubj"
5267
5268 _csrdomainlist=$(_readSubjectAltNamesFromCSR "$_csrfile")
4c2a3841 5269 if [ "$?" != "0" ]; then
10afcaca 5270 _err "Can not read domain list from csr: $_csrfile"
5271 return 1
5272 fi
5273 _debug "_csrdomainlist" "$_csrdomainlist"
5274
5275 _info "SubjectAltNames=$_csrdomainlist"
5276
10afcaca 5277 _csrkeylength=$(_readKeyLengthFromCSR "$_csrfile")
4c2a3841 5278 if [ "$?" != "0" ] || [ -z "$_csrkeylength" ]; then
10afcaca 5279 _err "Can not read key length from csr: $_csrfile"
5280 return 1
5281 fi
5282 _info "KeyLength=$_csrkeylength"
5283}
5284
95ef046d 5285#listraw domain
6d7eda3e 5286list() {
22ea4004 5287 _raw="$1"
95ef046d 5288 _domain="$2"
6d7eda3e 5289 _initpath
4c2a3841 5290
dcf4f8f6 5291 _sep="|"
4c2a3841 5292 if [ "$_raw" ]; then
95ef046d 5293 if [ -z "$_domain" ]; then
5294 printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}CA${_sep}Created${_sep}Renew"
5295 fi
e591d5cf 5296 for di in "${CERT_HOME}"/*.*/; do
5297 d=$(basename "$di")
201aa244 5298 _debug d "$d"
dcf4f8f6 5299 (
201aa244 5300 if _endswith "$d" "$ECC_SUFFIX"; then
be0df07d 5301 _isEcc="ecc"
201aa244 5302 d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
43822d37 5303 fi
be0df07d 5304 DOMAIN_CONF="$di/$d.conf"
4c2a3841 5305 if [ -f "$DOMAIN_CONF" ]; then
dcf4f8f6 5306 . "$DOMAIN_CONF"
269847d1 5307 _ca="$(_getCAShortName "$Le_API")"
95ef046d 5308 if [ -z "$_domain" ]; then
5309 printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$_ca${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr"
5310 else
5311 if [ "$_domain" = "$d" ]; then
5312 cat "$DOMAIN_CONF"
5313 fi
5314 fi
dcf4f8f6 5315 fi
5316 )
5317 done
5318 else
4c2a3841 5319 if _exists column; then
95ef046d 5320 list "raw" "$_domain" | column -t -s "$_sep"
22ea4004 5321 else
95ef046d 5322 list "raw" "$_domain" | tr "$_sep" '\t'
22ea4004 5323 fi
dcf4f8f6 5324 fi
6d7eda3e 5325
6d7eda3e 5326}
5327
93bce1b2 5328_deploy() {
5329 _d="$1"
5330 _hooks="$2"
5331
5332 for _d_api in $(echo "$_hooks" | tr ',' " "); do
b50e701c 5333 _deployApi="$(_findHook "$_d" $_SUB_FOLDER_DEPLOY "$_d_api")"
93bce1b2 5334 if [ -z "$_deployApi" ]; then
5335 _err "The deploy hook $_d_api is not found."
5336 return 1
5337 fi
5338 _debug _deployApi "$_deployApi"
5339
5340 if ! (
5341 if ! . "$_deployApi"; then
5342 _err "Load file $_deployApi error. Please check your api file and try again."
5343 return 1
5344 fi
5345
5346 d_command="${_d_api}_deploy"
5347 if ! _exists "$d_command"; then
5348 _err "It seems that your api file is not correct, it must have a function named: $d_command"
5349 return 1
5350 fi
5351
5352 if ! $d_command "$_d" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$CERT_FULLCHAIN_PATH"; then
5353 _err "Error deploy for domain:$_d"
5354 return 1
5355 fi
5356 ); then
5357 _err "Deploy error."
5358 return 1
5359 else
5360 _info "$(__green Success)"
5361 fi
5362 done
5363}
5364
5365#domain hooks
a61fe418 5366deploy() {
93bce1b2 5367 _d="$1"
5368 _hooks="$2"
a61fe418 5369 _isEcc="$3"
93bce1b2 5370 if [ -z "$_hooks" ]; then
a61fe418 5371 _usage "Usage: $PROJECT_ENTRY --deploy -d domain.com --deploy-hook cpanel [--ecc] "
5372 return 1
5373 fi
5374
93bce1b2 5375 _initpath "$_d" "$_isEcc"
4c2a3841 5376 if [ ! -d "$DOMAIN_PATH" ]; then
9672c6b8 5377 _err "The domain '$_d' is not a cert name. You must use the cert name to specify the cert to install."
5378 _err "Can not find path:'$DOMAIN_PATH'"
a61fe418 5379 return 1
5380 fi
4c2a3841 5381
93bce1b2 5382 . "$DOMAIN_CONF"
4c2a3841 5383
93bce1b2 5384 _savedomainconf Le_DeployHook "$_hooks"
4c2a3841 5385
93bce1b2 5386 _deploy "$_d" "$_hooks"
a61fe418 5387}
5388
4c3b3608 5389installcert() {
85e1f4ea 5390 _main_domain="$1"
5391 if [ -z "$_main_domain" ]; then
5c539af7 5392 _usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--cert-file cert-file-path] [--key-file key-file-path] [--ca-file ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchain-file fullchain-path]"
4c3b3608 5393 return 1
5394 fi
5395
85e1f4ea 5396 _real_cert="$2"
5397 _real_key="$3"
5398 _real_ca="$4"
5399 _reload_cmd="$5"
5400 _real_fullchain="$6"
43822d37 5401 _isEcc="$7"
5402
85e1f4ea 5403 _initpath "$_main_domain" "$_isEcc"
4c2a3841 5404 if [ ! -d "$DOMAIN_PATH" ]; then
9672c6b8 5405 _err "The domain '$_main_domain' is not a cert name. You must use the cert name to specify the cert to install."
5406 _err "Can not find path:'$DOMAIN_PATH'"
43822d37 5407 return 1
5408 fi
5409
85e1f4ea 5410 _savedomainconf "Le_RealCertPath" "$_real_cert"
5411 _savedomainconf "Le_RealCACertPath" "$_real_ca"
5412 _savedomainconf "Le_RealKeyPath" "$_real_key"
7690f73e 5413 _savedomainconf "Le_ReloadCmd" "$_reload_cmd" "base64"
85e1f4ea 5414 _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
5415
044da37c 5416 _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
43822d37 5417}
4c3b3608 5418
044da37c 5419#domain cert key ca fullchain reloadcmd backup-prefix
43822d37 5420_installcert() {
85e1f4ea 5421 _main_domain="$1"
5422 _real_cert="$2"
5423 _real_key="$3"
5424 _real_ca="$4"
044da37c 5425 _real_fullchain="$5"
5426 _reload_cmd="$6"
5427 _backup_prefix="$7"
4c3b3608 5428
85e1f4ea 5429 if [ "$_real_cert" = "$NO_VALUE" ]; then
5430 _real_cert=""
4d2f38b0 5431 fi
85e1f4ea 5432 if [ "$_real_key" = "$NO_VALUE" ]; then
5433 _real_key=""
4d2f38b0 5434 fi
85e1f4ea 5435 if [ "$_real_ca" = "$NO_VALUE" ]; then
5436 _real_ca=""
4d2f38b0 5437 fi
85e1f4ea 5438 if [ "$_reload_cmd" = "$NO_VALUE" ]; then
5439 _reload_cmd=""
4d2f38b0 5440 fi
85e1f4ea 5441 if [ "$_real_fullchain" = "$NO_VALUE" ]; then
5442 _real_fullchain=""
4d2f38b0 5443 fi
4c2a3841 5444
044da37c 5445 _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix"
5446 mkdir -p "$_backup_path"
5447
85e1f4ea 5448 if [ "$_real_cert" ]; then
5449 _info "Installing cert to:$_real_cert"
bd04638d 5450 if [ -f "$_real_cert" ] && [ ! "$_ACME_IS_RENEW" ]; then
044da37c 5451 cp "$_real_cert" "$_backup_path/cert.bak"
4c3b3608 5452 fi
206be3c1 5453 cat "$CERT_PATH" >"$_real_cert" || return 1
4c3b3608 5454 fi
4c2a3841 5455
85e1f4ea 5456 if [ "$_real_ca" ]; then
5457 _info "Installing CA to:$_real_ca"
5458 if [ "$_real_ca" = "$_real_cert" ]; then
5459 echo "" >>"$_real_ca"
206be3c1 5460 cat "$CA_CERT_PATH" >>"$_real_ca" || return 1
4c3b3608 5461 else
bd04638d 5462 if [ -f "$_real_ca" ] && [ ! "$_ACME_IS_RENEW" ]; then
044da37c 5463 cp "$_real_ca" "$_backup_path/ca.bak"
78552b18 5464 fi
206be3c1 5465 cat "$CA_CERT_PATH" >"$_real_ca" || return 1
4c3b3608 5466 fi
5467 fi
5468
85e1f4ea 5469 if [ "$_real_key" ]; then
5470 _info "Installing key to:$_real_key"
bd04638d 5471 if [ -f "$_real_key" ] && [ ! "$_ACME_IS_RENEW" ]; then
044da37c 5472 cp "$_real_key" "$_backup_path/key.bak"
4c3b3608 5473 fi
82014583 5474 if [ -f "$_real_key" ]; then
206be3c1 5475 cat "$CERT_KEY_PATH" >"$_real_key" || return 1
82014583 5476 else
206be3c1 5477 cat "$CERT_KEY_PATH" >"$_real_key" || return 1
7b92371a 5478 chmod 600 "$_real_key"
82014583 5479 fi
4c3b3608 5480 fi
4c2a3841 5481
85e1f4ea 5482 if [ "$_real_fullchain" ]; then
5483 _info "Installing full chain to:$_real_fullchain"
bd04638d 5484 if [ -f "$_real_fullchain" ] && [ ! "$_ACME_IS_RENEW" ]; then
044da37c 5485 cp "$_real_fullchain" "$_backup_path/fullchain.bak"
a63b05a9 5486 fi
206be3c1 5487 cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" || return 1
4c2a3841 5488 fi
4c3b3608 5489
85e1f4ea 5490 if [ "$_reload_cmd" ]; then
5491 _info "Run reload cmd: $_reload_cmd"
25555b8c 5492 if (
839bf0e2 5493 export CERT_PATH
5494 export CERT_KEY_PATH
5495 export CA_CERT_PATH
5496 export CERT_FULLCHAIN_PATH
b3f61297 5497 export Le_Domain="$_main_domain"
85e1f4ea 5498 cd "$DOMAIN_PATH" && eval "$_reload_cmd"
839bf0e2 5499 ); then
43822d37 5500 _info "$(__green "Reload success")"
4d2f38b0 5501 else
5502 _err "Reload error for :$Le_Domain"
5503 fi
5504 fi
5505
4c3b3608 5506}
5507
77f96b38 5508__read_password() {
5509 unset _pp
5510 prompt="Enter Password:"
0b04a7f1 5511 while IFS= read -p "$prompt" -r -s -n 1 char; do
5512 if [ "$char" = $'\0' ]; then
4ebad105 5513 break
0b04a7f1 5514 fi
5515 prompt='*'
5516 _pp="$_pp$char"
77f96b38 5517 done
5518 echo "$_pp"
5519}
5520
5521_install_win_taskscheduler() {
5522 _lesh="$1"
5523 _centry="$2"
5524 _randomminute="$3"
5525 if ! _exists cygpath; then
5526 _err "cygpath not found"
5527 return 1
5528 fi
5529 if ! _exists schtasks; then
5530 _err "schtasks.exe is not found, are you on Windows?"
5531 return 1
5532 fi
5533 _winbash="$(cygpath -w $(which bash))"
5534 _debug _winbash "$_winbash"
5535 if [ -z "$_winbash" ]; then
5536 _err "can not find bash path"
5537 return 1
5538 fi
5539 _myname="$(whoami)"
5540 _debug "_myname" "$_myname"
5541 if [ -z "$_myname" ]; then
5542 _err "can not find my user name"
5543 return 1
5544 fi
5545 _debug "_lesh" "$_lesh"
5546
5547 _info "To install scheduler task in your Windows account, you must input your windows password."
5548 _info "$PROJECT_NAME doesn't save your password."
5549 _info "Please input your Windows password for: $(__green "$_myname")"
5550 _password="$(__read_password)"
5551 #SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'" >/dev/null
5552 echo SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "\"$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'\"" | cmd.exe >/dev/null
5553 echo
5554
5555}
5556
5557_uninstall_win_taskscheduler() {
5558 if ! _exists schtasks; then
5559 _err "schtasks.exe is not found, are you on Windows?"
5560 return 1
5561 fi
5562 if ! echo SCHTASKS /query /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null; then
5563 _debug "scheduler $_WINDOWS_SCHEDULER_NAME is not found."
5564 else
5565 _info "Removing $_WINDOWS_SCHEDULER_NAME"
0b04a7f1 5566 echo SCHTASKS /delete /f /tn "$_WINDOWS_SCHEDULER_NAME" | cmd.exe >/dev/null
77f96b38 5567 fi
5568}
5569
27dbe77f 5570#confighome
4c3b3608 5571installcronjob() {
27dbe77f 5572 _c_home="$1"
4c3b3608 5573 _initpath
415f375c 5574 _CRONTAB="crontab"
77f96b38 5575 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
5576 lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
5577 else
5578 _err "Can not install cronjob, $PROJECT_ENTRY not found."
5579 return 1
5580 fi
5581 if [ "$_c_home" ]; then
5582 _c_entry="--config-home \"$_c_home\" "
5583 fi
5584 _t=$(_time)
5585 random_minute=$(_math $_t % 60)
5586
415f375c 5587 if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
5588 _CRONTAB="fcrontab"
5589 fi
77f96b38 5590
415f375c 5591 if ! _exists "$_CRONTAB"; then
77f96b38 5592 if _exists cygpath && _exists schtasks.exe; then
5593 _info "It seems you are on Windows, let's install Windows scheduler task."
5594 if _install_win_taskscheduler "$lesh" "$_c_entry" "$random_minute"; then
5595 _info "Install Windows scheduler task success."
5596 return 0
5597 else
5598 _err "Install Windows scheduler task failed."
5599 return 1
5600 fi
5601 fi
415f375c 5602 _err "crontab/fcrontab doesn't exist, so, we can not install cron jobs."
77546ea5 5603 _err "All your certs will not be renewed automatically."
a7b7355d 5604 _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
77546ea5 5605 return 1
5606 fi
4c3b3608 5607 _info "Installing cron job"
415f375c 5608 if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
e2c939fb 5609 if _exists uname && uname -a | grep SunOS >/dev/null; then
415f375c 5610 $_CRONTAB -l | {
4c2a3841 5611 cat
0533bde9 5612 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
415f375c 5613 } | $_CRONTAB --
22ea4004 5614 else
415f375c 5615 $_CRONTAB -l | {
4c2a3841 5616 cat
0533bde9 5617 echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
415f375c 5618 } | $_CRONTAB -
22ea4004 5619 fi
4c3b3608 5620 fi
4c2a3841 5621 if [ "$?" != "0" ]; then
4c3b3608 5622 _err "Install cron job failed. You need to manually renew your certs."
5623 _err "Or you can add cronjob by yourself:"
a7b7355d 5624 _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
4c3b3608 5625 return 1
5626 fi
5627}
5628
5629uninstallcronjob() {
415f375c 5630 _CRONTAB="crontab"
5631 if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
5632 _CRONTAB="fcrontab"
5633 fi
5634
5635 if ! _exists "$_CRONTAB"; then
77f96b38 5636 if _exists cygpath && _exists schtasks.exe; then
5637 _info "It seems you are on Windows, let's uninstall Windows scheduler task."
5638 if _uninstall_win_taskscheduler; then
5639 _info "Uninstall Windows scheduler task success."
5640 return 0
5641 else
5642 _err "Uninstall Windows scheduler task failed."
5643 return 1
5644 fi
5645 fi
37db5b81 5646 return
5647 fi
4c3b3608 5648 _info "Removing cron job"
415f375c 5649 cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
4c2a3841 5650 if [ "$cr" ]; then
5651 if _exists uname && uname -a | grep solaris >/dev/null; then
415f375c 5652 $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB --
22ea4004 5653 else
415f375c 5654 $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -
22ea4004 5655 fi
a7b7355d 5656 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
4c3b3608 5657 _info LE_WORKING_DIR "$LE_WORKING_DIR"
27dbe77f 5658 if _contains "$cr" "--config-home"; then
f5b546b3 5659 LE_CONFIG_HOME="$(echo "$cr" | cut -d ' ' -f 11 | tr -d '"')"
5660 _debug LE_CONFIG_HOME "$LE_CONFIG_HOME"
27dbe77f 5661 fi
4c2a3841 5662 fi
4c3b3608 5663 _initpath
a7b7355d 5664
4c3b3608 5665}
5666
1041c9f9 5667#domain isECC revokeReason
6cb415f5 5668revoke() {
5669 Le_Domain="$1"
4c2a3841 5670 if [ -z "$Le_Domain" ]; then
78f0201d 5671 _usage "Usage: $PROJECT_ENTRY --revoke -d domain.com [--ecc]"
6cb415f5 5672 return 1
5673 fi
4c2a3841 5674
43822d37 5675 _isEcc="$2"
1041c9f9 5676 _reason="$3"
5677 if [ -z "$_reason" ]; then
5678 _reason="0"
5679 fi
c4a375b3 5680 _initpath "$Le_Domain" "$_isEcc"
4c2a3841 5681 if [ ! -f "$DOMAIN_CONF" ]; then
6cb415f5 5682 _err "$Le_Domain is not a issued domain, skip."
4c2a3841 5683 return 1
6cb415f5 5684 fi
4c2a3841 5685
5686 if [ ! -f "$CERT_PATH" ]; then
6cb415f5 5687 _err "Cert for $Le_Domain $CERT_PATH is not found, skip."
5688 return 1
5689 fi
6cb415f5 5690
11927a76 5691 cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
4c2a3841 5692
5693 if [ -z "$cert" ]; then
6cb415f5 5694 _err "Cert for $Le_Domain is empty found, skip."
5695 return 1
5696 fi
4c2a3841 5697
48d9a8c1 5698 _initAPI
5699
d2cde379 5700 if [ "$ACME_VERSION" = "2" ]; then
1041c9f9 5701 data="{\"certificate\": \"$cert\",\"reason\":$_reason}"
d2cde379 5702 else
5703 data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
5704 fi
48d9a8c1 5705 uri="${ACME_REVOKE_CERT}"
6cb415f5 5706
4c2a3841 5707 if [ -f "$CERT_KEY_PATH" ]; then
1befee5a 5708 _info "Try domain key first."
c4a375b3 5709 if _send_signed_request "$uri" "$data" "" "$CERT_KEY_PATH"; then
4c2a3841 5710 if [ -z "$response" ]; then
1befee5a 5711 _info "Revoke success."
c4a375b3 5712 rm -f "$CERT_PATH"
1befee5a 5713 return 0
4c2a3841 5714 else
1befee5a 5715 _err "Revoke error by domain key."
5716 _err "$response"
5717 fi
6cb415f5 5718 fi
4c2a3841 5719 else
eca57bee 5720 _info "Domain key file doesn't exist."
6cb415f5 5721 fi
6cb415f5 5722
1befee5a 5723 _info "Try account key."
6cb415f5 5724
c4a375b3 5725 if _send_signed_request "$uri" "$data" "" "$ACCOUNT_KEY_PATH"; then
4c2a3841 5726 if [ -z "$response" ]; then
6cb415f5 5727 _info "Revoke success."
c4a375b3 5728 rm -f "$CERT_PATH"
6cb415f5 5729 return 0
4c2a3841 5730 else
6cb415f5 5731 _err "Revoke error."
c9c31c04 5732 _debug "$response"
6cb415f5 5733 fi
5734 fi
5735 return 1
5736}
4c3b3608 5737
78f0201d 5738#domain ecc
5739remove() {
5740 Le_Domain="$1"
5741 if [ -z "$Le_Domain" ]; then
5742 _usage "Usage: $PROJECT_ENTRY --remove -d domain.com [--ecc]"
5743 return 1
5744 fi
5745
5746 _isEcc="$2"
5747
5748 _initpath "$Le_Domain" "$_isEcc"
5749 _removed_conf="$DOMAIN_CONF.removed"
5750 if [ ! -f "$DOMAIN_CONF" ]; then
5751 if [ -f "$_removed_conf" ]; then
5752 _err "$Le_Domain is already removed, You can remove the folder by yourself: $DOMAIN_PATH"
5753 else
5754 _err "$Le_Domain is not a issued domain, skip."
5755 fi
5756 return 1
5757 fi
5758
5759 if mv "$DOMAIN_CONF" "$_removed_conf"; then
68aea3af 5760 _info "$Le_Domain is removed, the key and cert files are in $(__green $DOMAIN_PATH)"
78f0201d 5761 _info "You can remove them by yourself."
5762 return 0
5763 else
5764 _err "Remove $Le_Domain failed."
5765 return 1
5766 fi
5767}
5768
0c00e870 5769#domain vtype
5770_deactivate() {
5771 _d_domain="$1"
5772 _d_type="$2"
5773 _initpath
4c2a3841 5774
d2cde379 5775 if [ "$ACME_VERSION" = "2" ]; then
5776 _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}"
5777 if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
5778 _err "Can not get domain new order."
5779 return 1
5780 fi
d0d74907 5781 _authorizations_seg="$(echo "$response" | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
d2cde379 5782 _debug2 _authorizations_seg "$_authorizations_seg"
5783 if [ -z "$_authorizations_seg" ]; then
5784 _err "_authorizations_seg not found."
5785 _clearup
5786 _on_issue_err "$_post_hook"
5787 return 1
5788 fi
4c2a3841 5789
d2cde379 5790 authzUri="$_authorizations_seg"
5791 _debug2 "authzUri" "$authzUri"
0483d841 5792 if ! _send_signed_request "$authzUri"; then
d2cde379 5793 _err "get to authz error."
263c38ca 5794 _err "_authorizations_seg" "$_authorizations_seg"
5795 _err "authzUri" "$authzUri"
d2cde379 5796 _clearup
5797 _on_issue_err "$_post_hook"
5798 return 1
5799 fi
4c2a3841 5800
d2cde379 5801 response="$(echo "$response" | _normalizeJson)"
5802 _debug2 response "$response"
5803 _URL_NAME="url"
5804 else
5805 if ! __get_domain_new_authz "$_d_domain"; then
5806 _err "Can not get domain new authz token."
5807 return 1
5808 fi
5809
0712e989 5810 authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
d2cde379 5811 _debug "authzUri" "$authzUri"
5812 if [ "$code" ] && [ ! "$code" = '201' ]; then
5813 _err "new-authz error: $response"
5814 return 1
5815 fi
5816 _URL_NAME="uri"
14d7bfda 5817 fi
0c00e870 5818
4f3f4e23 5819 entries="$(echo "$response" | _egrep_o "[^{]*\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
14d7bfda 5820 if [ -z "$entries" ]; then
5821 _info "No valid entries found."
5822 if [ -z "$thumbprint" ]; then
5823 thumbprint="$(__calc_account_thumbprint)"
5824 fi
5825 _debug "Trigger validation."
d2cde379 5826 vtype="$VTYPE_DNS"
d0d74907 5827 entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
14d7bfda 5828 _debug entry "$entry"
5829 if [ -z "$entry" ]; then
5830 _err "Error, can not get domain token $d"
0c00e870 5831 return 1
5832 fi
d0d74907 5833 token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
14d7bfda 5834 _debug token "$token"
4c2a3841 5835
d0d74907 5836 uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
14d7bfda 5837 _debug uri "$uri"
5838
5839 keyauthorization="$token.$thumbprint"
5840 _debug keyauthorization "$keyauthorization"
5841 __trigger_validation "$uri" "$keyauthorization"
5842
5843 fi
5844
5845 _d_i=0
5846 _d_max_retry=$(echo "$entries" | wc -l)
5847 while [ "$_d_i" -lt "$_d_max_retry" ]; do
5848 _info "Deactivate: $_d_domain"
5849 _d_i="$(_math $_d_i + 1)"
5850 entry="$(echo "$entries" | sed -n "${_d_i}p")"
0c00e870 5851 _debug entry "$entry"
4c2a3841 5852
5853 if [ -z "$entry" ]; then
fb2029e7 5854 _info "No more valid entry found."
0c00e870 5855 break
5856 fi
4c2a3841 5857
d0d74907 5858 _vtype="$(echo "$entry" | _egrep_o '"type": *"[^"]*"' | cut -d : -f 2 | tr -d '"')"
c4a375b3 5859 _debug _vtype "$_vtype"
0c00e870 5860 _info "Found $_vtype"
5861
d0d74907 5862 uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
c4a375b3 5863 _debug uri "$uri"
4c2a3841 5864
5865 if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
0c00e870 5866 _info "Skip $_vtype"
5867 continue
5868 fi
4c2a3841 5869
0c00e870 5870 _info "Deactivate: $_vtype"
4c2a3841 5871
d2cde379 5872 if [ "$ACME_VERSION" = "2" ]; then
5873 _djson="{\"status\":\"deactivated\"}"
5874 else
5875 _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}"
5876 fi
5877
5878 if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then
14d7bfda 5879 _info "Deactivate: $_vtype success."
5880 else
0c00e870 5881 _err "Can not deactivate $_vtype."
14d7bfda 5882 break
0c00e870 5883 fi
4c2a3841 5884
0c00e870 5885 done
5886 _debug "$_d_i"
14d7bfda 5887 if [ "$_d_i" -eq "$_d_max_retry" ]; then
0c00e870 5888 _info "Deactivated success!"
5889 else
5890 _err "Deactivate failed."
5891 fi
5892
5893}
5894
5895deactivate() {
3f4513b3 5896 _d_domain_list="$1"
0c00e870 5897 _d_type="$2"
5898 _initpath
a3bdaa85 5899 _initAPI
3f4513b3 5900 _debug _d_domain_list "$_d_domain_list"
4c2a3841 5901 if [ -z "$(echo $_d_domain_list | cut -d , -f 1)" ]; then
3f4513b3 5902 _usage "Usage: $PROJECT_ENTRY --deactivate -d domain.com [-d domain.com]"
0c00e870 5903 return 1
5904 fi
4c2a3841 5905 for _d_dm in $(echo "$_d_domain_list" | tr ',' ' '); do
5906 if [ -z "$_d_dm" ] || [ "$_d_dm" = "$NO_VALUE" ]; then
3f4513b3 5907 continue
5908 fi
c4a375b3 5909 if ! _deactivate "$_d_dm" "$_d_type"; then
86c017ec 5910 return 1
5911 fi
3f4513b3 5912 done
0c00e870 5913}
5914
4c3b3608 5915# Detect profile file if not specified as environment variable
5916_detect_profile() {
4c2a3841 5917 if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
4c3b3608 5918 echo "$PROFILE"
5919 return
5920 fi
5921
4c3b3608 5922 DETECTED_PROFILE=''
4c3b3608 5923 SHELLTYPE="$(basename "/$SHELL")"
5924
4c2a3841 5925 if [ "$SHELLTYPE" = "bash" ]; then
5926 if [ -f "$HOME/.bashrc" ]; then
4c3b3608 5927 DETECTED_PROFILE="$HOME/.bashrc"
4c2a3841 5928 elif [ -f "$HOME/.bash_profile" ]; then
4c3b3608 5929 DETECTED_PROFILE="$HOME/.bash_profile"
5930 fi
4c2a3841 5931 elif [ "$SHELLTYPE" = "zsh" ]; then
4c3b3608 5932 DETECTED_PROFILE="$HOME/.zshrc"
5933 fi
5934
4c2a3841 5935 if [ -z "$DETECTED_PROFILE" ]; then
5936 if [ -f "$HOME/.profile" ]; then
4c3b3608 5937 DETECTED_PROFILE="$HOME/.profile"
4c2a3841 5938 elif [ -f "$HOME/.bashrc" ]; then
4c3b3608 5939 DETECTED_PROFILE="$HOME/.bashrc"
4c2a3841 5940 elif [ -f "$HOME/.bash_profile" ]; then
4c3b3608 5941 DETECTED_PROFILE="$HOME/.bash_profile"
4c2a3841 5942 elif [ -f "$HOME/.zshrc" ]; then
4c3b3608 5943 DETECTED_PROFILE="$HOME/.zshrc"
5944 fi
5945 fi
5946
1be222f6 5947 echo "$DETECTED_PROFILE"
4c3b3608 5948}
5949
5950_initconf() {
5951 _initpath
4c2a3841 5952 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
0ca5b799 5953 echo "
d404e92d 5954
d0871bda 5955#LOG_FILE=\"$DEFAULT_LOG_FILE\"
6b500036 5956#LOG_LEVEL=1
5ea6e9c9 5957
251d1c5c 5958#AUTO_UPGRADE=\"1\"
89002ed2 5959
569d6c55 5960#NO_TIMESTAMP=1
5b771039 5961
d5ec5f80 5962 " >"$ACCOUNT_CONF_PATH"
4c3b3608 5963 fi
5964}
5965
c8e9a31e 5966# nocron
c60883ef 5967_precheck() {
c8e9a31e 5968 _nocron="$1"
4c2a3841 5969
5970 if ! _exists "curl" && ! _exists "wget"; then
c60883ef 5971 _err "Please install curl or wget first, we need to access http resources."
4c3b3608 5972 return 1
5973 fi
4c2a3841 5974
5975 if [ -z "$_nocron" ]; then
415f375c 5976 if ! _exists "crontab" && ! _exists "fcrontab"; then
77f96b38 5977 if _exists cygpath && _exists schtasks.exe; then
5978 _info "It seems you are on Windows, we will install Windows scheduler task."
5979 else
5980 _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
5981 _err "We need to set cron job to renew the certs automatically."
5982 _err "Otherwise, your certs will not be able to be renewed automatically."
5983 if [ -z "$FORCE" ]; then
5984 _err "Please add '--force' and try install again to go without crontab."
5985 _err "./$PROJECT_ENTRY --install --force"
5986 return 1
5987 fi
c8e9a31e 5988 fi
77546ea5 5989 fi
4c3b3608 5990 fi
4c2a3841 5991
d8ba26e6 5992 if ! _exists "${ACME_OPENSSL_BIN:-openssl}"; then
851fedf7 5993 _err "Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN"
c60883ef 5994 _err "We need openssl to generate keys."
4c3b3608 5995 return 1
5996 fi
4c2a3841 5997
3794b5cb 5998 if ! _exists "socat"; then
5999 _err "It is recommended to install socat first."
6000 _err "We use socat for standalone server if you use standalone mode."
c60883ef 6001 _err "If you don't use standalone mode, just ignore this warning."
6002 fi
4c2a3841 6003
c60883ef 6004 return 0
6005}
6006
0a7c9364 6007_setShebang() {
6008 _file="$1"
6009 _shebang="$2"
4c2a3841 6010 if [ -z "$_shebang" ]; then
43822d37 6011 _usage "Usage: file shebang"
0a7c9364 6012 return 1
6013 fi
6014 cp "$_file" "$_file.tmp"
4c2a3841 6015 echo "$_shebang" >"$_file"
6016 sed -n 2,99999p "$_file.tmp" >>"$_file"
6017 rm -f "$_file.tmp"
0a7c9364 6018}
6019
27dbe77f 6020#confighome
94dc5f33 6021_installalias() {
27dbe77f 6022 _c_home="$1"
94dc5f33 6023 _initpath
6024
6025 _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
4c2a3841 6026 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ]; then
44edb2bd 6027 echo "$(cat "$_envfile")" | sed "s|^LE_WORKING_DIR.*$||" >"$_envfile"
6028 echo "$(cat "$_envfile")" | sed "s|^alias le.*$||" >"$_envfile"
6029 echo "$(cat "$_envfile")" | sed "s|^alias le.sh.*$||" >"$_envfile"
94dc5f33 6030 fi
6031
27dbe77f 6032 if [ "$_c_home" ]; then
be83a6a3 6033 _c_entry=" --config-home '$_c_home'"
27dbe77f 6034 fi
6035
1786a5e5 6036 _setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
f5b546b3 6037 if [ "$_c_home" ]; then
6038 _setopt "$_envfile" "export LE_CONFIG_HOME" "=" "\"$LE_CONFIG_HOME\""
d04434e3 6039 else
6040 _sed_i "/^export LE_CONFIG_HOME/d" "$_envfile"
f5b546b3 6041 fi
be83a6a3 6042 _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
94dc5f33 6043
6044 _profile="$(_detect_profile)"
4c2a3841 6045 if [ "$_profile" ]; then
94dc5f33 6046 _debug "Found profile: $_profile"
aba5c634 6047 _info "Installing alias to '$_profile'"
94dc5f33 6048 _setopt "$_profile" ". \"$_envfile\""
6049 _info "OK, Close and reopen your terminal to start using $PROJECT_NAME"
6050 else
6051 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
6052 fi
94dc5f33 6053
6054 #for csh
6055 _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
94dc5f33 6056 _csh_profile="$HOME/.cshrc"
4c2a3841 6057 if [ -f "$_csh_profile" ]; then
aba5c634 6058 _info "Installing alias to '$_csh_profile'"
6626371d 6059 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
f5b546b3 6060 if [ "$_c_home" ]; then
6061 _setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
d04434e3 6062 else
6063 _sed_i "/^setenv LE_CONFIG_HOME/d" "$_cshfile"
f5b546b3 6064 fi
be83a6a3 6065 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
4c2a3841 6066 _setopt "$_csh_profile" "source \"$_cshfile\""
94dc5f33 6067 fi
4c2a3841 6068
acafa585 6069 #for tcsh
6070 _tcsh_profile="$HOME/.tcshrc"
4c2a3841 6071 if [ -f "$_tcsh_profile" ]; then
aba5c634 6072 _info "Installing alias to '$_tcsh_profile'"
acafa585 6073 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
f5b546b3 6074 if [ "$_c_home" ]; then
6075 _setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
6076 fi
be83a6a3 6077 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
4c2a3841 6078 _setopt "$_tcsh_profile" "source \"$_cshfile\""
acafa585 6079 fi
94dc5f33 6080
6081}
6082
86ef0a26 6083# nocron confighome noprofile
c60883ef 6084install() {
f3e4cea3 6085
4c2a3841 6086 if [ -z "$LE_WORKING_DIR" ]; then
f3e4cea3 6087 LE_WORKING_DIR="$DEFAULT_INSTALL_HOME"
6088 fi
4c2a3841 6089
c8e9a31e 6090 _nocron="$1"
27dbe77f 6091 _c_home="$2"
86ef0a26 6092 _noprofile="$3"
4c2a3841 6093 if ! _initpath; then
c60883ef 6094 _err "Install failed."
4c3b3608 6095 return 1
6096 fi
4c2a3841 6097 if [ "$_nocron" ]; then
52677b0a 6098 _debug "Skip install cron job"
6099 fi
4c2a3841 6100
bd04638d 6101 if [ "$_ACME_IN_CRON" != "1" ]; then
4356eefb 6102 if ! _precheck "$_nocron"; then
6103 _err "Pre-check failed, can not install."
6104 return 1
6105 fi
4c3b3608 6106 fi
4c2a3841 6107
8e845d9f 6108 if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
6109 _info "Using config home: $LE_CONFIG_HOME"
6110 _c_home="$LE_CONFIG_HOME"
6111 fi
6112
6cc11ffb 6113 #convert from le
4c2a3841 6114 if [ -d "$HOME/.le" ]; then
6115 for envfile in "le.env" "le.sh.env"; do
6116 if [ -f "$HOME/.le/$envfile" ]; then
6117 if grep "le.sh" "$HOME/.le/$envfile" >/dev/null; then
6118 _upgrading="1"
6119 _info "You are upgrading from le.sh"
6120 _info "Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
6121 mv "$HOME/.le" "$LE_WORKING_DIR"
6122 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
6123 break
6cc11ffb 6124 fi
6125 fi
6126 done
6127 fi
6128
4c3b3608 6129 _info "Installing to $LE_WORKING_DIR"
635695ec 6130
d04434e3 6131 if [ ! -d "$LE_WORKING_DIR" ]; then
6132 if ! mkdir -p "$LE_WORKING_DIR"; then
6133 _err "Can not create working dir: $LE_WORKING_DIR"
6134 return 1
6135 fi
6136
6137 chmod 700 "$LE_WORKING_DIR"
4a0f23e2 6138 fi
4c2a3841 6139
d04434e3 6140 if [ ! -d "$LE_CONFIG_HOME" ]; then
6141 if ! mkdir -p "$LE_CONFIG_HOME"; then
6142 _err "Can not create config dir: $LE_CONFIG_HOME"
6143 return 1
6144 fi
762978f8 6145
d04434e3 6146 chmod 700 "$LE_CONFIG_HOME"
27dbe77f 6147 fi
6148
d5ec5f80 6149 cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
4c3b3608 6150
4c2a3841 6151 if [ "$?" != "0" ]; then
a7b7355d 6152 _err "Install failed, can not copy $PROJECT_ENTRY"
4c3b3608 6153 return 1
6154 fi
6155
a7b7355d 6156 _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
4c3b3608 6157
bd04638d 6158 if [ "$_ACME_IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then
4356eefb 6159 _installalias "$_c_home"
6160 fi
4c3b3608 6161
4c2a3841 6162 for subf in $_SUB_FOLDERS; do
6163 if [ -d "$subf" ]; then
d5ec5f80 6164 mkdir -p "$LE_WORKING_DIR/$subf"
6165 cp "$subf"/* "$LE_WORKING_DIR"/"$subf"/
a61fe418 6166 fi
6167 done
6168
4c2a3841 6169 if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
4c3b3608 6170 _initconf
6171 fi
6cc11ffb 6172
4c2a3841 6173 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ]; then
635695ec 6174 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
6cc11ffb 6175 fi
6176
4c2a3841 6177 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ]; then
b2817897 6178 _saveaccountconf "CERT_HOME" "$CERT_HOME"
6179 fi
6180
4c2a3841 6181 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ]; then
b2817897 6182 _saveaccountconf "ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
6183 fi
4c2a3841 6184
6185 if [ -z "$_nocron" ]; then
27dbe77f 6186 installcronjob "$_c_home"
c8e9a31e 6187 fi
0a7c9364 6188
4c2a3841 6189 if [ -z "$NO_DETECT_SH" ]; then
641989fd 6190 #Modify shebang
4c2a3841 6191 if _exists bash; then
694af4ae 6192 _bash_path="$(bash -c "command -v bash 2>/dev/null")"
6193 if [ -z "$_bash_path" ]; then
6194 _bash_path="$(bash -c 'echo $SHELL')"
6195 fi
6196 fi
6197 if [ "$_bash_path" ]; then
329174b6 6198 _info "Good, bash is found, so change the shebang to use bash as preferred."
694af4ae 6199 _shebang='#!'"$_bash_path"
641989fd 6200 _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
4c2a3841 6201 for subf in $_SUB_FOLDERS; do
6202 if [ -d "$LE_WORKING_DIR/$subf" ]; then
6203 for _apifile in "$LE_WORKING_DIR/$subf/"*.sh; do
a61fe418 6204 _setShebang "$_apifile" "$_shebang"
6205 done
6206 fi
6207 done
0a7c9364 6208 fi
6209 fi
6210
4c3b3608 6211 _info OK
6212}
6213
52677b0a 6214# nocron
4c3b3608 6215uninstall() {
52677b0a 6216 _nocron="$1"
4c2a3841 6217 if [ -z "$_nocron" ]; then
52677b0a 6218 uninstallcronjob
6219 fi
4c3b3608 6220 _initpath
6221
9aa3be7f 6222 _uninstallalias
4c2a3841 6223
d5ec5f80 6224 rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
f5b546b3 6225 _info "The keys and certs are in \"$(__green "$LE_CONFIG_HOME")\", you can remove them by yourself."
9aa3be7f 6226
6227}
6228
6229_uninstallalias() {
6230 _initpath
6231
4c3b3608 6232 _profile="$(_detect_profile)"
4c2a3841 6233 if [ "$_profile" ]; then
9aa3be7f 6234 _info "Uninstalling alias from: '$_profile'"
d5ec5f80 6235 text="$(cat "$_profile")"
4c2a3841 6236 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" >"$_profile"
4c3b3608 6237 fi
6238
94dc5f33 6239 _csh_profile="$HOME/.cshrc"
4c2a3841 6240 if [ -f "$_csh_profile" ]; then
9aa3be7f 6241 _info "Uninstalling alias from: '$_csh_profile'"
d5ec5f80 6242 text="$(cat "$_csh_profile")"
4c2a3841 6243 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_csh_profile"
94dc5f33 6244 fi
4c2a3841 6245
acafa585 6246 _tcsh_profile="$HOME/.tcshrc"
4c2a3841 6247 if [ -f "$_tcsh_profile" ]; then
9aa3be7f 6248 _info "Uninstalling alias from: '$_csh_profile'"
d5ec5f80 6249 text="$(cat "$_tcsh_profile")"
4c2a3841 6250 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" >"$_tcsh_profile"
acafa585 6251 fi
4c3b3608 6252
6253}
6254
6255cron() {
bd04638d 6256 export _ACME_IN_CRON=1
89002ed2 6257 _initpath
d8ba26e6 6258 _info "$(__green "===Starting cron===")"
4c2a3841 6259 if [ "$AUTO_UPGRADE" = "1" ]; then
89002ed2 6260 export LE_WORKING_DIR
6261 (
4c2a3841 6262 if ! upgrade; then
6263 _err "Cron:Upgrade failed!"
6264 return 1
6265 fi
89002ed2 6266 )
d5ec5f80 6267 . "$LE_WORKING_DIR/$PROJECT_ENTRY" >/dev/null
1ab63043 6268
4c2a3841 6269 if [ -t 1 ]; then
1ab63043 6270 __INTERACTIVE="1"
6271 fi
4c2a3841 6272
89002ed2 6273 _info "Auto upgraded to: $VER"
6274 fi
4c3b3608 6275 renewAll
cc179731 6276 _ret="$?"
bd04638d 6277 _ACME_IN_CRON=""
d8ba26e6 6278 _info "$(__green "===End cron===")"
0ba95a3d 6279 exit $_ret
4c3b3608 6280}
6281
6282version() {
a63b05a9 6283 echo "$PROJECT"
6284 echo "v$VER"
4c3b3608 6285}
6286
b50e701c 6287# subject content hooks code
6288_send_notify() {
6289 _nsubject="$1"
6290 _ncontent="$2"
6291 _nhooks="$3"
6292 _nerror="$4"
6293
6294 if [ "$NOTIFY_LEVEL" = "$NOTIFY_LEVEL_DISABLE" ]; then
6295 _debug "The NOTIFY_LEVEL is $NOTIFY_LEVEL, disabled, just return."
6296 return 0
6297 fi
6298
6299 if [ -z "$_nhooks" ]; then
6300 _debug "The NOTIFY_HOOK is empty, just return."
6301 return 0
6302 fi
6303
6304 _send_err=0
6305 for _n_hook in $(echo "$_nhooks" | tr ',' " "); do
6306 _n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")"
143eac09 6307 _info "Sending via: $_n_hook"
6308 _debug "Found $_n_hook_file for $_n_hook"
75191e71 6309 if [ -z "$_n_hook_file" ]; then
6310 _err "Can not find the hook file for $_n_hook"
6311 continue
6312 fi
b50e701c 6313 if ! (
6314 if ! . "$_n_hook_file"; then
6315 _err "Load file $_n_hook_file error. Please check your api file and try again."
6316 return 1
6317 fi
6318
6319 d_command="${_n_hook}_send"
6320 if ! _exists "$d_command"; then
6321 _err "It seems that your api file is not correct, it must have a function named: $d_command"
6322 return 1
6323 fi
6324
6325 if ! $d_command "$_nsubject" "$_ncontent" "$_nerror"; then
6326 _err "Error send message by $d_command"
6327 return 1
6328 fi
6329
6330 return 0
6331 ); then
6332 _err "Set $_n_hook_file error."
6333 _send_err=1
6334 else
6335 _info "$_n_hook $(__green Success)"
6336 fi
6337 done
6338 return $_send_err
6339
6340}
6341
6342# hook
6343_set_notify_hook() {
6344 _nhooks="$1"
6345
5698bec6 6346 _test_subject="Hello, this is a notification from $PROJECT_NAME"
143eac09 6347 _test_content="If you receive this message, your notification works."
b50e701c 6348
6349 _send_notify "$_test_subject" "$_test_content" "$_nhooks" 0
6350
6351}
6352
6353#[hook] [level] [mode]
6354setnotify() {
6355 _nhook="$1"
6356 _nlevel="$2"
6357 _nmode="$3"
6358
6359 _initpath
6360
6361 if [ -z "$_nhook$_nlevel$_nmode" ]; then
6362 _usage "Usage: $PROJECT_ENTRY --set-notify [--notify-hook mailgun] [--notify-level $NOTIFY_LEVEL_DEFAULT] [--notify-mode $NOTIFY_MODE_DEFAULT]"
6363 _usage "$_NOTIFY_WIKI"
6364 return 1
6365 fi
6366
6367 if [ "$_nlevel" ]; then
6368 _info "Set notify level to: $_nlevel"
6369 export "NOTIFY_LEVEL=$_nlevel"
6370 _saveaccountconf "NOTIFY_LEVEL" "$NOTIFY_LEVEL"
6371 fi
6372
6373 if [ "$_nmode" ]; then
6374 _info "Set notify mode to: $_nmode"
6375 export "NOTIFY_MODE=$_nmode"
6376 _saveaccountconf "NOTIFY_MODE" "$NOTIFY_MODE"
6377 fi
6378
6379 if [ "$_nhook" ]; then
6380 _info "Set notify hook to: $_nhook"
6381 if [ "$_nhook" = "$NO_VALUE" ]; then
6382 _info "Clear notify hook"
6383 _clearaccountconf "NOTIFY_HOOK"
6384 else
6385 if _set_notify_hook "$_nhook"; then
6386 export NOTIFY_HOOK="$_nhook"
6387 _saveaccountconf "NOTIFY_HOOK" "$NOTIFY_HOOK"
6388 return 0
6389 else
6390 _err "Can not set notify hook to: $_nhook"
6391 return 1
6392 fi
6393 fi
6394 fi
6395
6396}
6397
4c3b3608 6398showhelp() {
d0871bda 6399 _initpath
4c3b3608 6400 version
a7b7355d 6401 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
a63b05a9 6402Commands:
6403 --help, -h Show this help message.
6404 --version, -v Show version info.
a7b7355d 6405 --install Install $PROJECT_NAME to your system.
6406 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
d8beaf72 6407 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT.
a63b05a9 6408 --issue Issue a cert.
10afcaca 6409 --signcsr Issue a cert from an existing csr.
a61fe418 6410 --deploy Deploy the cert to your server.
27dbe77f 6411 --install-cert Install the issued cert to apache/nginx or any other server.
a63b05a9 6412 --renew, -r Renew a cert.
27dbe77f 6413 --renew-all Renew all the certs.
a63b05a9 6414 --revoke Revoke a cert.
47b49f1b 6415 --remove Remove the cert from list of certs known to $PROJECT_NAME.
10afcaca 6416 --list List all the certs.
6417 --showcsr Show the content of a csr.
27dbe77f 6418 --install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
6419 --uninstall-cronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
a63b05a9 6420 --cron Run cron job to renew all the certs.
6421 --toPkcs Export the certificate and key to a pfx file.
4410226d 6422 --toPkcs8 Convert to pkcs8 format.
27dbe77f 6423 --update-account Update account info.
6424 --register-account Register account key.
422dd1fa 6425 --deactivate-account Deactivate the account.
0984585d 6426 --create-account-key Create an account private key, professional use.
6427 --create-domain-key Create an domain private key, professional use.
a63b05a9 6428 --createCSR, -ccsr Create CSR , professional use.
0c00e870 6429 --deactivate Deactivate the domain authz, professional use.
b50e701c 6430 --set-notify Set the cron notification hook, level or mode.
737e9e48 6431 --set-default-ca Used with '--server' , to set the default CA to use to use.
b50e701c 6432
3c07f57a 6433
a63b05a9 6434Parameters:
6435 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
e3ebd582 6436 --challenge-alias domain.tld The challenge domain alias for DNS alias mode.
6437 See: $_DNS_ALIAS_WIKI
6438
6439 --domain-alias domain.tld The domain alias for DNS alias mode.
6440 See: $_DNS_ALIAS_WIKI
6441
6442 --preferred-chain CHAIN If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
6443 If no match, the default offered chain will be used. (default: empty)
6444 See: $_PREFERRED_CHAIN_WIKI
6445
a63b05a9 6446 --force, -f Used to force to install or force to renew a cert immediately.
6447 --staging, --test Use staging server, just for test.
6448 --debug Output debug info.
e3ebd582 6449 --output-insecure Output all the sensitive messages.
6450 By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
6451
a63b05a9 6452 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
6453 --standalone Use standalone mode.
08681f4a 6454 --alpn Use standalone alpn mode.
e3ebd582 6455 --stateless Use stateless mode.
6456 See: $_STATELESS_WIKI
6457
a63b05a9 6458 --apache Use apache mode.
e3ebd582 6459 --dns [dns_hook] Use dns mode or dns api.
6460 See: $_DNS_API_WIKI
6461
6462 --dnssleep 300 The time in seconds to wait for all the txt records to propagate in dns api mode.
6463 It's not necessary to use this by default, $PROJECT_NAME polls dns status by DOH automatically.
3c07f57a 6464
3c98fae4 6465 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
b6552aff 6466 --accountkeylength, -ak [2048] Specifies the account key length: 2048, 3072, 4096
d0871bda 6467 --log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
a73c5b33 6468 --log-level 1|2 Specifies the log level, default is 1.
52765466 6469 --syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
3c07f57a 6470
f96d91cb 6471 --eab-kid EAB_KID Key Identifier for External Account Binding.
6472 --eab-hmac-key EAB_HMAC_KEY HMAC key for External Account Binding.
6473
6474
7903fcb4 6475 These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
3c07f57a 6476
13fe54c9 6477 --cert-file After issue/renew, the cert will be copied to this path.
6478 --key-file After issue/renew, the key will be copied to this path.
6479 --ca-file After issue/renew, the intermediate cert will be copied to this path.
6480 --fullchain-file After issue/renew, the fullchain cert will be copied to this path.
3c07f57a 6481
a63b05a9 6482 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
6483
e3ebd582 6484 --server SERVER ACME Directory Resource URI. (default: $DEFAULT_CA)
6485 See: $_SERVER_WIKI
6486
a63b05a9 6487 --accountconf Specifies a customized account config file.
5fee82ce 6488 --home Specifies the home dir for $PROJECT_NAME.
27dbe77f 6489 --cert-home Specifies the home dir to save all the certs, only valid for '--install' command.
6490 --config-home Specifies the home dir to save all the configurations.
635695ec 6491 --useragent Specifies the user agent string. it will be saved for future use too.
737e9e48 6492 --accountemail, -m Specifies the account email, only valid for the '--install' and '--update-account' command.
9082862b 6493 --accountkey Specifies the account key path, only valid for the '--install' command.
ec67a1b2 6494 --days Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days.
39c8f79f 6495 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
79a0a66f 6496 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
6ae0f7f5 6497 --local-address Specifies the standalone/tls server listening address, in case you have multiple ip addresses.
dcf4f8f6 6498 --listraw Only used for '--list' command, list the certs in raw format.
27dbe77f 6499 --stopRenewOnError, -se Only valid for '--renew-all' command. Stop if one cert has error in renewal.
13d7cae9 6500 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
8f73e241 6501 --ca-bundle Specifies the path to the CA certificate bundle to verify api server's certificate.
13fe54c9 6502 --ca-path Specifies directory containing CA certificates in PEM format, used by wget or curl.
e3ebd582 6503 --nocron Only valid for '--install' command, which means: do not install the default cron job.
6504 In this case, the certs will not be renewed automatically.
6505
61556a54 6506 --noprofile Only valid for '--install' command, which means: do not install aliases to user profile.
08b4e1a7 6507 --no-color Do not output color text.
e32b3aac 6508 --force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
27dbe77f 6509 --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
10afcaca 6510 --csr Specifies the input csr.
b0070f03 6511 --pre-hook Command to be run before obtaining any certificates.
84a6730b 6512 --post-hook Command to be run after attempting to obtain/renew certificates. No matter the obtain/renew is success or failed.
b0070f03 6513 --renew-hook Command to be run once for each successfully renewed certificate.
a61fe418 6514 --deploy-hook The hook file to deploy cert
0c9546cc 6515 --ocsp-must-staple, --ocsp Generate ocsp must Staple extension.
c4b2e582 6516 --always-force-new-domain-key Generate new domain key when renewal. Otherwise, the domain key is not changed by default.
6bf281f9 6517 --auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future.
6ae0f7f5 6518 --listen-v4 Force standalone/tls server to listen at ipv4.
6519 --listen-v6 Force standalone/tls server to listen at ipv6.
a746139c 6520 --openssl-bin Specifies a custom openssl bin location.
9b124070 6521 --use-wget Force to use wget, if you have both curl and wget installed.
e3ebd582 6522 --yes-I-know-dns-manual-mode-enough-go-ahead-please Force to use dns manual mode.
6523 See: $_DNS_MANUAL_WIKI
6524
66686de4 6525 --branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to.
b50e701c 6526
6527 --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT.
ac9f6e3a 6528 0: disabled, no notification will be sent.
c6b68551 6529 1: send notifications only when there is an error.
6530 2: send notifications when a cert is successfully renewed, or there is an error.
6531 3: send notifications when a cert is skipped, renewed, or error.
e3ebd582 6532
b50e701c 6533 --notify-mode 0|1 Set notification mode. Default value is $NOTIFY_MODE_DEFAULT.
c6b68551 6534 0: Bulk mode. Send all the domain's notifications in one message(mail).
b50e701c 6535 1: Cert mode. Send a message for every single cert.
e3ebd582 6536
b50e701c 6537 --notify-hook [hookname] Set the notify hook
e3ebd582 6538 --revoke-reason [0-10] The reason for '--revoke' command.
6539 See: $_REVOKE_WIKI
6540
b50e701c 6541
6542"
4c3b3608 6543}
6544
86ef0a26 6545# nocron noprofile
4a0f23e2 6546_installOnline() {
6547 _info "Installing from online archive."
52677b0a 6548 _nocron="$1"
86ef0a26 6549 _noprofile="$2"
4c2a3841 6550 if [ ! "$BRANCH" ]; then
4a0f23e2 6551 BRANCH="master"
6552 fi
a8df88ab 6553
4a0f23e2 6554 target="$PROJECT/archive/$BRANCH.tar.gz"
6555 _info "Downloading $target"
6556 localname="$BRANCH.tar.gz"
4c2a3841 6557 if ! _get "$target" >$localname; then
df9547ae 6558 _err "Download error."
4a0f23e2 6559 return 1
6560 fi
0bbe6eef 6561 (
4c2a3841 6562 _info "Extracting $localname"
3a3b0dd5 6563 if ! (tar xzf $localname || gtar xzf $localname); then
6564 _err "Extraction error."
6565 exit 1
6566 fi
4c2a3841 6567
6568 cd "$PROJECT_NAME-$BRANCH"
6569 chmod +x $PROJECT_ENTRY
86ef0a26 6570 if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
4c2a3841 6571 _info "Install success!"
ac3667c7 6572 _initpath
cb7e3857 6573 _saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
4c2a3841 6574 fi
6575
6576 cd ..
6577
6578 rm -rf "$PROJECT_NAME-$BRANCH"
6579 rm -f "$localname"
0bbe6eef 6580 )
4a0f23e2 6581}
6582
cb7e3857
OB
6583_getRepoHash() {
6584 _hash_path=$1
6585 shift
6586 _hash_url="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/$_hash_path"
6587 _get $_hash_url | tr -d "\r\n" | tr '{},' '\n' | grep '"sha":' | cut -d '"' -f 4
6588}
6589
6590_getUpgradeHash() {
f716f606 6591 _b="$BRANCH"
6592 if [ -z "$_b" ]; then
6593 _b="master"
6594 fi
cb7e3857
OB
6595 _hash=$(_getRepoHash "heads/$_b")
6596 if [ -z "$_hash" ]; then _hash=$(_getRepoHash "tags/$_b"); fi
6597 echo $_hash
7a3c61b7 6598}
6599
52677b0a 6600upgrade() {
6601 if (
267f283a 6602 _initpath
cb7e3857 6603 [ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already uptodate!" && exit 0
267f283a 6604 export LE_WORKING_DIR
d0b748a4 6605 cd "$LE_WORKING_DIR"
86ef0a26 6606 _installOnline "nocron" "noprofile"
4c2a3841 6607 ); then
52677b0a 6608 _info "Upgrade success!"
096d8992 6609 exit 0
52677b0a 6610 else
6611 _err "Upgrade failed!"
096d8992 6612 exit 1
52677b0a 6613 fi
6614}
a63b05a9 6615
5ea6e9c9 6616_processAccountConf() {
4c2a3841 6617 if [ "$_useragent" ]; then
5ea6e9c9 6618 _saveaccountconf "USER_AGENT" "$_useragent"
4c2a3841 6619 elif [ "$USER_AGENT" ] && [ "$USER_AGENT" != "$DEFAULT_USER_AGENT" ]; then
d0871bda 6620 _saveaccountconf "USER_AGENT" "$USER_AGENT"
5ea6e9c9 6621 fi
4c2a3841 6622
a746139c 6623 if [ "$_openssl_bin" ]; then
851fedf7 6624 _saveaccountconf "ACME_OPENSSL_BIN" "$_openssl_bin"
6625 elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
6626 _saveaccountconf "ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN"
a746139c 6627 fi
6628
4c2a3841 6629 if [ "$_auto_upgrade" ]; then
6bf281f9 6630 _saveaccountconf "AUTO_UPGRADE" "$_auto_upgrade"
4c2a3841 6631 elif [ "$AUTO_UPGRADE" ]; then
6bf281f9 6632 _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
6633 fi
4c2a3841 6634
9b124070 6635 if [ "$_use_wget" ]; then
6636 _saveaccountconf "ACME_USE_WGET" "$_use_wget"
6637 elif [ "$ACME_USE_WGET" ]; then
6638 _saveaccountconf "ACME_USE_WGET" "$ACME_USE_WGET"
6639 fi
6640
5ea6e9c9 6641}
6642
5bdfdfef 6643_checkSudo() {
6644 if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then
6645 if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then
6646 #it's root using sudo, no matter it's using sudo or not, just fine
6647 return 0
6648 fi
79ad0ff5 6649 if [ -n "$SUDO_COMMAND" ]; then
815a3be4 6650 #it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`
79ad0ff5 6651 _endswith "$SUDO_COMMAND" /bin/su || grep "^$SUDO_COMMAND\$" /etc/shells >/dev/null 2>&1
6652 return $?
5bdfdfef 6653 fi
6654 #otherwise
6655 return 1
6656 fi
6657 return 0
6658}
6659
737e9e48 6660#server
6661_selectServer() {
6662 _server="$1"
6663 _server_lower="$(echo "$_server" | _lower_case)"
6664 _sindex=0
6665 for snames in $CA_NAMES; do
6666 snames="$(echo "$snames" | _lower_case)"
6667 _sindex="$(_math $_sindex + 1)"
6668 _debug2 "_selectServer try snames" "$snames"
6669 for sname in $(echo "$snames" | tr ',' ' '); do
6670 if [ "$_server_lower" = "$sname" ]; then
6671 _debug2 "_selectServer match $sname"
6672 _serverdir="$(_getfield "$CA_SERVERS" $_sindex)"
6673 _debug "Selected server: $_serverdir"
6674 ACME_DIRECTORY="$_serverdir"
6675 export ACME_DIRECTORY
6676 return
6677 fi
6678 done
6679 done
6680 ACME_DIRECTORY="$_server"
6681 export ACME_DIRECTORY
6682}
6683
269847d1 6684#url
6685_getCAShortName() {
6686 caurl="$1"
95ef046d 6687 if [ -z "$caurl" ]; then
6688 caurl="$DEFAULT_CA"
6689 fi
269847d1 6690 caurl_lower="$(echo $caurl | _lower_case)"
6691 _sindex=0
6692 for surl in $(echo "$CA_SERVERS" | _lower_case | tr , ' '); do
6693 _sindex="$(_math $_sindex + 1)"
6694 if [ "$caurl_lower" = "$surl" ]; then
6695 _nindex=0
6696 for snames in $CA_NAMES; do
6697 _nindex="$(_math $_nindex + 1)"
6698 if [ $_nindex -ge $_sindex ]; then
6699 _getfield "$snames" 1
6700 return
6701 fi
6702 done
6703 fi
6704 done
6705 echo "$caurl"
6706}
6707
737e9e48 6708#set default ca to $ACME_DIRECTORY
6709setdefaultca() {
6710 if [ -z "$ACME_DIRECTORY" ]; then
6711 _err "Please give a --server parameter."
6712 return 1
6713 fi
6714 _saveaccountconf "DEFAULT_ACME_SERVER" "$ACME_DIRECTORY"
df22f680 6715 _info "Changed default CA to: $(__green "$ACME_DIRECTORY")"
737e9e48 6716}
6717
a63b05a9 6718_process() {
6719 _CMD=""
6720 _domain=""
3f4513b3 6721 _altdomains="$NO_VALUE"
a63b05a9 6722 _webroot=""
875625b1 6723 _challenge_alias=""
bdbf323f 6724 _keylength=""
6725 _accountkeylength=""
5c539af7 6726 _cert_file=""
6727 _key_file=""
6728 _ca_file=""
6729 _fullchain_file=""
4d2f38b0 6730 _reloadcmd=""
a63b05a9 6731 _password=""
635695ec 6732 _accountconf=""
6733 _useragent=""
b5eb4b90 6734 _accountemail=""
6735 _accountkey=""
b2817897 6736 _certhome=""
27dbe77f 6737 _confighome=""
39c8f79f 6738 _httpport=""
e22bcf7c 6739 _tlsport=""
0e38c60d 6740 _dnssleep=""
dcf4f8f6 6741 _listraw=""
cc179731 6742 _stopRenewOnError=""
e3698edd 6743 #_insecure=""
78009539 6744 _ca_bundle=""
2aa75f03 6745 _ca_path=""
c8e9a31e 6746 _nocron=""
61556a54 6747 _noprofile=""
43822d37 6748 _ecc=""
10afcaca 6749 _csr=""
b0070f03 6750 _pre_hook=""
6751 _post_hook=""
6752 _renew_hook=""
a61fe418 6753 _deploy_hook=""
5ea6e9c9 6754 _logfile=""
d0871bda 6755 _log=""
0463b5d6 6756 _local_address=""
a73c5b33 6757 _log_level=""
6bf281f9 6758 _auto_upgrade=""
6ae0f7f5 6759 _listen_v4=""
6760 _listen_v6=""
a746139c 6761 _openssl_bin=""
e2edf208 6762 _syslog=""
9b124070 6763 _use_wget=""
98394f99 6764 _server=""
b50e701c 6765 _notify_hook=""
6766 _notify_level=""
6767 _notify_mode=""
1041c9f9 6768 _revoke_reason=""
f96d91cb 6769 _eab_kid=""
6770 _eab_hmac_key=""
e3ebd582 6771 _preferred_chain=""
4c2a3841 6772 while [ ${#} -gt 0 ]; do
a63b05a9 6773 case "${1}" in
4c2a3841 6774
6775 --help | -h)
a63b05a9 6776 showhelp
6777 return
6778 ;;
4c2a3841 6779 --version | -v)
a63b05a9 6780 version
6781 return
6782 ;;
4c2a3841 6783 --install)
a63b05a9 6784 _CMD="install"
6785 ;;
4c2a3841 6786 --uninstall)
a63b05a9 6787 _CMD="uninstall"
6788 ;;
4c2a3841 6789 --upgrade)
52677b0a 6790 _CMD="upgrade"
6791 ;;
4c2a3841 6792 --issue)
a63b05a9 6793 _CMD="issue"
6794 ;;
4c2a3841 6795 --deploy)
a61fe418 6796 _CMD="deploy"
6797 ;;
4c2a3841 6798 --signcsr)
10afcaca 6799 _CMD="signcsr"
6800 ;;
4c2a3841 6801 --showcsr)
10afcaca 6802 _CMD="showcsr"
6803 ;;
db7e4bf9 6804 --installcert | -i | --install-cert)
a63b05a9 6805 _CMD="installcert"
6806 ;;
4c2a3841 6807 --renew | -r)
a63b05a9 6808 _CMD="renew"
6809 ;;
db7e4bf9 6810 --renewAll | --renewall | --renew-all)
a63b05a9 6811 _CMD="renewAll"
6812 ;;
4c2a3841 6813 --revoke)
a63b05a9 6814 _CMD="revoke"
6815 ;;
78f0201d 6816 --remove)
6817 _CMD="remove"
6818 ;;
4c2a3841 6819 --list)
6d7eda3e 6820 _CMD="list"
6821 ;;
ee20015d 6822 --installcronjob | --install-cronjob)
a63b05a9 6823 _CMD="installcronjob"
6824 ;;
db7e4bf9 6825 --uninstallcronjob | --uninstall-cronjob)
a63b05a9 6826 _CMD="uninstallcronjob"
6827 ;;
4c2a3841 6828 --cron)
a63b05a9 6829 _CMD="cron"
6830 ;;
4c2a3841 6831 --toPkcs)
a63b05a9 6832 _CMD="toPkcs"
4c2a3841 6833 ;;
4410226d 6834 --toPkcs8)
6835 _CMD="toPkcs8"
342128a4 6836 ;;
0984585d 6837 --createAccountKey | --createaccountkey | -cak | --create-account-key)
a63b05a9 6838 _CMD="createAccountKey"
6839 ;;
0984585d 6840 --createDomainKey | --createdomainkey | -cdk | --create-domain-key)
a63b05a9 6841 _CMD="createDomainKey"
6842 ;;
4c2a3841 6843 --createCSR | --createcsr | -ccr)
a63b05a9 6844 _CMD="createCSR"
6845 ;;
4c2a3841 6846 --deactivate)
0c00e870 6847 _CMD="deactivate"
6848 ;;
ee20015d 6849 --updateaccount | --update-account)
eb59817e 6850 _CMD="updateaccount"
6851 ;;
ee20015d 6852 --registeraccount | --register-account)
eb59817e 6853 _CMD="registeraccount"
6854 ;;
422dd1fa 6855 --deactivate-account)
6856 _CMD="deactivateaccount"
6857 ;;
b50e701c 6858 --set-notify)
6859 _CMD="setnotify"
6860 ;;
737e9e48 6861 --set-default-ca)
6862 _CMD="setdefaultca"
6863 ;;
4c2a3841 6864 --domain | -d)
a63b05a9 6865 _dvalue="$2"
4c2a3841 6866
6867 if [ "$_dvalue" ]; then
6868 if _startswith "$_dvalue" "-"; then
ee1737a5 6869 _err "'$_dvalue' is not a valid domain for parameter '$1'"
6870 return 1
6871 fi
4c2a3841 6872 if _is_idn "$_dvalue" && ! _exists idn; then
9774b01b 6873 _err "It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn' command first."
6874 return 1
6875 fi
4c2a3841 6876
9e9f839d 6877 if _startswith "$_dvalue" "*."; then
6878 _debug "Wildcard domain"
6879 export ACME_VERSION=2
6880 fi
4c2a3841 6881 if [ -z "$_domain" ]; then
ee1737a5 6882 _domain="$_dvalue"
a63b05a9 6883 else
4c2a3841 6884 if [ "$_altdomains" = "$NO_VALUE" ]; then
ee1737a5 6885 _altdomains="$_dvalue"
6886 else
6887 _altdomains="$_altdomains,$_dvalue"
6888 fi
a63b05a9 6889 fi
6890 fi
4c2a3841 6891
a63b05a9 6892 shift
6893 ;;
6894
4c2a3841 6895 --force | -f)
a63b05a9 6896 FORCE="1"
6897 ;;
4c2a3841 6898 --staging | --test)
a63b05a9 6899 STAGE="1"
6900 ;;
48d9a8c1 6901 --server)
737e9e48 6902 _server="$2"
6903 _selectServer "$_server"
48d9a8c1 6904 shift
6905 ;;
4c2a3841 6906 --debug)
6907 if [ -z "$2" ] || _startswith "$2" "-"; then
fc6cf4d9 6908 DEBUG="$DEBUG_LEVEL_DEFAULT"
a63b05a9 6909 else
6910 DEBUG="$2"
6911 shift
4c2a3841 6912 fi
a63b05a9 6913 ;;
e6e85b0c 6914 --output-insecure)
6915 export OUTPUT_INSECURE=1
6916 ;;
4c2a3841 6917 --webroot | -w)
a63b05a9 6918 wvalue="$2"
4c2a3841 6919 if [ -z "$_webroot" ]; then
a63b05a9 6920 _webroot="$wvalue"
6921 else
6922 _webroot="$_webroot,$wvalue"
6923 fi
6924 shift
4c2a3841 6925 ;;
875625b1 6926 --challenge-alias)
6927 cvalue="$2"
6928 _challenge_alias="$_challenge_alias$cvalue,"
6929 shift
6930 ;;
64821ad4 6931 --domain-alias)
6932 cvalue="$DNS_ALIAS_PREFIX$2"
6933 _challenge_alias="$_challenge_alias$cvalue,"
6934 shift
1f7df33e 6935 ;;
4c2a3841 6936 --standalone)
3f4513b3 6937 wvalue="$NO_VALUE"
4c2a3841 6938 if [ -z "$_webroot" ]; then
a63b05a9 6939 _webroot="$wvalue"
6940 else
6941 _webroot="$_webroot,$wvalue"
6942 fi
6943 ;;
08681f4a 6944 --alpn)
6945 wvalue="$W_ALPN"
6946 if [ -z "$_webroot" ]; then
6947 _webroot="$wvalue"
6948 else
6949 _webroot="$_webroot,$wvalue"
6950 fi
6951 ;;
0e44f587 6952 --stateless)
6953 wvalue="$MODE_STATELESS"
6954 if [ -z "$_webroot" ]; then
6955 _webroot="$wvalue"
6956 else
6957 _webroot="$_webroot,$wvalue"
6958 fi
6959 ;;
4c2a3841 6960 --local-address)
0463b5d6 6961 lvalue="$2"
6962 _local_address="$_local_address$lvalue,"
6963 shift
6964 ;;
4c2a3841 6965 --apache)
a63b05a9 6966 wvalue="apache"
4c2a3841 6967 if [ -z "$_webroot" ]; then
a63b05a9 6968 _webroot="$wvalue"
6969 else
6970 _webroot="$_webroot,$wvalue"
6971 fi
6972 ;;
9d725af6 6973 --nginx)
6974 wvalue="$NGINX"
1081d98b 6975 if [ "$2" ] && ! _startswith "$2" "-"; then
6976 wvalue="$NGINX$2"
6977 shift
6978 fi
9d725af6 6979 if [ -z "$_webroot" ]; then
6980 _webroot="$wvalue"
6981 else
6982 _webroot="$_webroot,$wvalue"
6983 fi
6984 ;;
4c2a3841 6985 --dns)
3881f221 6986 wvalue="$W_DNS"
a5c56c54 6987 if [ "$2" ] && ! _startswith "$2" "-"; then
a63b05a9 6988 wvalue="$2"
6989 shift
6990 fi
4c2a3841 6991 if [ -z "$_webroot" ]; then
a63b05a9 6992 _webroot="$wvalue"
6993 else
6994 _webroot="$_webroot,$wvalue"
6995 fi
6996 ;;
4c2a3841 6997 --dnssleep)
0e38c60d 6998 _dnssleep="$2"
6999 Le_DNSSleep="$_dnssleep"
7000 shift
7001 ;;
4c2a3841 7002
7003 --keylength | -k)
a63b05a9 7004 _keylength="$2"
a63b05a9 7005 shift
7006 ;;
4c2a3841 7007 --accountkeylength | -ak)
2ce87fe2 7008 _accountkeylength="$2"
a63b05a9 7009 shift
7010 ;;
7011
5c539af7 7012 --cert-file | --certpath)
7013 _cert_file="$2"
a63b05a9 7014 shift
7015 ;;
5c539af7 7016 --key-file | --keypath)
7017 _key_file="$2"
a63b05a9 7018 shift
7019 ;;
5c539af7 7020 --ca-file | --capath)
7021 _ca_file="$2"
a63b05a9 7022 shift
7023 ;;
5c539af7 7024 --fullchain-file | --fullchainpath)
7025 _fullchain_file="$2"
a63b05a9 7026 shift
7027 ;;
4c2a3841 7028 --reloadcmd | --reloadCmd)
a63b05a9 7029 _reloadcmd="$2"
7030 shift
7031 ;;
4c2a3841 7032 --password)
a63b05a9 7033 _password="$2"
7034 shift
7035 ;;
4c2a3841 7036 --accountconf)
635695ec 7037 _accountconf="$2"
7038 ACCOUNT_CONF_PATH="$_accountconf"
a7b7355d 7039 shift
a63b05a9 7040 ;;
4c2a3841 7041 --home)
a63b05a9 7042 LE_WORKING_DIR="$2"
a7b7355d 7043 shift
a63b05a9 7044 ;;
ee20015d 7045 --certhome | --cert-home)
b2817897 7046 _certhome="$2"
7047 CERT_HOME="$_certhome"
7048 shift
4c2a3841 7049 ;;
27dbe77f 7050 --config-home)
7051 _confighome="$2"
f5b546b3 7052 LE_CONFIG_HOME="$_confighome"
27dbe77f 7053 shift
7054 ;;
4c2a3841 7055 --useragent)
635695ec 7056 _useragent="$2"
7057 USER_AGENT="$_useragent"
7058 shift
7059 ;;
737e9e48 7060 --accountemail | -m)
b5eb4b90 7061 _accountemail="$2"
7062 ACCOUNT_EMAIL="$_accountemail"
7063 shift
7064 ;;
4c2a3841 7065 --accountkey)
b5eb4b90 7066 _accountkey="$2"
7067 ACCOUNT_KEY_PATH="$_accountkey"
7068 shift
7069 ;;
4c2a3841 7070 --days)
06625071 7071 _days="$2"
7072 Le_RenewalDays="$_days"
7073 shift
7074 ;;
4c2a3841 7075 --httpport)
39c8f79f 7076 _httpport="$2"
7077 Le_HTTPPort="$_httpport"
7078 shift
7079 ;;
79a0a66f 7080 --tlsport)
7081 _tlsport="$2"
7082 Le_TLSPort="$_tlsport"
7083 shift
7084 ;;
4c2a3841 7085 --listraw)
dcf4f8f6 7086 _listraw="raw"
4c2a3841 7087 ;;
7088 --stopRenewOnError | --stoprenewonerror | -se)
cc179731 7089 _stopRenewOnError="1"
7090 ;;
4c2a3841 7091 --insecure)
e3698edd 7092 #_insecure="1"
fac1e367 7093 HTTPS_INSECURE="1"
13d7cae9 7094 ;;
4c2a3841 7095 --ca-bundle)
78d1cfb4 7096 _ca_bundle="$(_readlink "$2")"
78009539
PS
7097 CA_BUNDLE="$_ca_bundle"
7098 shift
7099 ;;
2aa75f03 7100 --ca-path)
7101 _ca_path="$2"
7102 CA_PATH="$_ca_path"
7103 shift
7104 ;;
4c2a3841 7105 --nocron)
c8e9a31e 7106 _nocron="1"
7107 ;;
61556a54
HH
7108 --noprofile)
7109 _noprofile="1"
7110 ;;
08b4e1a7 7111 --no-color)
7112 export ACME_NO_COLOR=1
7113 ;;
e32b3aac
SB
7114 --force-color)
7115 export ACME_FORCE_COLOR=1
7116 ;;
4c2a3841 7117 --ecc)
43822d37 7118 _ecc="isEcc"
7119 ;;
4c2a3841 7120 --csr)
10afcaca 7121 _csr="$2"
7122 shift
7123 ;;
4c2a3841 7124 --pre-hook)
b0070f03 7125 _pre_hook="$2"
7126 shift
7127 ;;
4c2a3841 7128 --post-hook)
b0070f03 7129 _post_hook="$2"
7130 shift
7131 ;;
4c2a3841 7132 --renew-hook)
b0070f03 7133 _renew_hook="$2"
7134 shift
7135 ;;
4c2a3841 7136 --deploy-hook)
93bce1b2 7137 if [ -z "$2" ] || _startswith "$2" "-"; then
7138 _usage "Please specify a value for '--deploy-hook'"
7139 return 1
7140 fi
7141 _deploy_hook="$_deploy_hook$2,"
a61fe418 7142 shift
7143 ;;
4c2a3841 7144 --ocsp-must-staple | --ocsp)
96db9362 7145 Le_OCSP_Staple="1"
0c9546cc 7146 ;;
c4b2e582 7147 --always-force-new-domain-key)
7148 if [ -z "$2" ] || _startswith "$2" "-"; then
7149 Le_ForceNewDomainKey=1
7150 else
7151 Le_ForceNewDomainKey="$2"
7152 shift
7153 fi
7154 ;;
a0923622 7155 --yes-I-know-dns-manual-mode-enough-go-ahead-please)
7156 export FORCE_DNS_MANUAL=1
7157 ;;
4c2a3841 7158 --log | --logfile)
d0871bda 7159 _log="1"
5ea6e9c9 7160 _logfile="$2"
4c2a3841 7161 if _startswith "$_logfile" '-'; then
d0871bda 7162 _logfile=""
7163 else
7164 shift
7165 fi
5ea6e9c9 7166 LOG_FILE="$_logfile"
4c2a3841 7167 if [ -z "$LOG_LEVEL" ]; then
a73c5b33 7168 LOG_LEVEL="$DEFAULT_LOG_LEVEL"
7169 fi
7170 ;;
4c2a3841 7171 --log-level)
30bfc2ce 7172 _log_level="$2"
a73c5b33 7173 LOG_LEVEL="$_log_level"
7174 shift
5ea6e9c9 7175 ;;
e2edf208 7176 --syslog)
7177 if ! _startswith "$2" '-'; then
7178 _syslog="$2"
7179 shift
7180 fi
7181 if [ -z "$_syslog" ]; then
fc6cf4d9 7182 _syslog="$SYSLOG_LEVEL_DEFAULT"
e2edf208 7183 fi
7184 ;;
4c2a3841 7185 --auto-upgrade)
6bf281f9 7186 _auto_upgrade="$2"
4c2a3841 7187 if [ -z "$_auto_upgrade" ] || _startswith "$_auto_upgrade" '-'; then
6bf281f9 7188 _auto_upgrade="1"
7189 else
7190 shift
7191 fi
7192 AUTO_UPGRADE="$_auto_upgrade"
7193 ;;
4c2a3841 7194 --listen-v4)
6ae0f7f5 7195 _listen_v4="1"
7196 Le_Listen_V4="$_listen_v4"
7197 ;;
4c2a3841 7198 --listen-v6)
6ae0f7f5 7199 _listen_v6="1"
7200 Le_Listen_V6="$_listen_v6"
7201 ;;
a746139c 7202 --openssl-bin)
7203 _openssl_bin="$2"
851fedf7 7204 ACME_OPENSSL_BIN="$_openssl_bin"
7c2e8754 7205 shift
a746139c 7206 ;;
9b124070 7207 --use-wget)
7208 _use_wget="1"
7209 ACME_USE_WGET="1"
7210 ;;
66686de4 7211 --branch | -b)
7212 export BRANCH="$2"
7213 shift
7214 ;;
b50e701c 7215 --notify-hook)
7216 _nhook="$2"
7217 if _startswith "$_nhook" "-"; then
7218 _err "'$_nhook' is not a hook name for '$1'"
7219 return 1
7220 fi
7221 if [ "$_notify_hook" ]; then
7222 _notify_hook="$_notify_hook,$_nhook"
7223 else
7224 _notify_hook="$_nhook"
7225 fi
7226 shift
7227 ;;
7228 --notify-level)
7229 _nlevel="$2"
7230 if _startswith "$_nlevel" "-"; then
7231 _err "'$_nlevel' is not a integer for '$1'"
7232 return 1
7233 fi
7234 _notify_level="$_nlevel"
7235 shift
7236 ;;
7237 --notify-mode)
7238 _nmode="$2"
7239 if _startswith "$_nmode" "-"; then
7240 _err "'$_nmode' is not a integer for '$1'"
7241 return 1
7242 fi
7243 _notify_mode="$_nmode"
7244 shift
1041c9f9 7245 ;;
a9953330 7246 --revoke-reason)
1041c9f9 7247 _revoke_reason="$2"
7248 if _startswith "$_revoke_reason" "-"; then
7249 _err "'$_revoke_reason' is not a integer for '$1'"
7250 return 1
7251 fi
7252 shift
b50e701c 7253 ;;
f96d91cb 7254 --eab-kid)
7255 _eab_kid="$2"
7256 shift
7257 ;;
7258 --eab-hmac-key)
7259 _eab_hmac_key="$2"
7260 shift
d42ff227 7261 ;;
e3ebd582 7262 --preferred-chain)
7263 _preferred_chain="$2"
7264 shift
7265 ;;
4c2a3841 7266 *)
a63b05a9 7267 _err "Unknown parameter : $1"
7268 return 1
7269 ;;
7270 esac
7271
7272 shift 1
7273 done
7274
4c2a3841 7275 if [ "${_CMD}" != "install" ]; then
5bdfdfef 7276 if [ "$__INTERACTIVE" ] && ! _checkSudo; then
7277 if [ -z "$FORCE" ]; then
7278 #Use "echo" here, instead of _info. it's too early
7279 echo "It seems that you are using sudo, please read this link first:"
7280 echo "$_SUDO_WIKI"
7281 return 1
7282 fi
7283 fi
5ea6e9c9 7284 __initHome
661f0583 7285 if [ "$_log" ]; then
4c2a3841 7286 if [ -z "$_logfile" ]; then
661f0583 7287 _logfile="$DEFAULT_LOG_FILE"
7288 fi
d0871bda 7289 fi
4c2a3841 7290 if [ "$_logfile" ]; then
5ea6e9c9 7291 _saveaccountconf "LOG_FILE" "$_logfile"
661f0583 7292 LOG_FILE="$_logfile"
5ea6e9c9 7293 fi
a73c5b33 7294
4c2a3841 7295 if [ "$_log_level" ]; then
a73c5b33 7296 _saveaccountconf "LOG_LEVEL" "$_log_level"
7297 LOG_LEVEL="$_log_level"
7298 fi
4c2a3841 7299
e2edf208 7300 if [ "$_syslog" ]; then
7301 if _exists logger; then
7302 if [ "$_syslog" = "0" ]; then
7303 _clearaccountconf "SYS_LOG"
7304 else
7305 _saveaccountconf "SYS_LOG" "$_syslog"
7306 fi
7307 SYS_LOG="$_syslog"
7308 else
7309 _err "The 'logger' command is not found, can not enable syslog."
7310 _clearaccountconf "SYS_LOG"
7311 SYS_LOG=""
7312 fi
7313 fi
7314
5ea6e9c9 7315 _processAccountConf
7316 fi
4c2a3841 7317
9d548d81 7318 _debug2 LE_WORKING_DIR "$LE_WORKING_DIR"
4c2a3841 7319
7320 if [ "$DEBUG" ]; then
dcf9cb58 7321 version
98394f99 7322 if [ "$_server" ]; then
7323 _debug "Using server: $_server"
7324 fi
dcf9cb58 7325 fi
9a733a57 7326 _debug "Running cmd: ${_CMD}"
a63b05a9 7327 case "${_CMD}" in
61556a54 7328 install) install "$_nocron" "$_confighome" "$_noprofile" ;;
bc96082f 7329 uninstall) uninstall "$_nocron" ;;
52677b0a 7330 upgrade) upgrade ;;
a63b05a9 7331 issue)
e3ebd582 7332 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain"
a63b05a9 7333 ;;
a61fe418 7334 deploy)
7335 deploy "$_domain" "$_deploy_hook" "$_ecc"
7336 ;;
10afcaca 7337 signcsr)
875625b1 7338 signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias"
10afcaca 7339 ;;
7340 showcsr)
7341 showcsr "$_csr" "$_domain"
7342 ;;
a63b05a9 7343 installcert)
5c539af7 7344 installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
a63b05a9 7345 ;;
4c2a3841 7346 renew)
43822d37 7347 renew "$_domain" "$_ecc"
a63b05a9 7348 ;;
4c2a3841 7349 renewAll)
cc179731 7350 renewAll "$_stopRenewOnError"
a63b05a9 7351 ;;
4c2a3841 7352 revoke)
1041c9f9 7353 revoke "$_domain" "$_ecc" "$_revoke_reason"
a63b05a9 7354 ;;
78f0201d 7355 remove)
7356 remove "$_domain" "$_ecc"
7357 ;;
4c2a3841 7358 deactivate)
3f4513b3 7359 deactivate "$_domain,$_altdomains"
eb59817e 7360 ;;
4c2a3841 7361 registeraccount)
f96d91cb 7362 registeraccount "$_accountkeylength" "$_eab_kid" "$_eab_hmac_key"
eb59817e 7363 ;;
4c2a3841 7364 updateaccount)
eb59817e 7365 updateaccount
7366 ;;
422dd1fa 7367 deactivateaccount)
7368 deactivateaccount
7369 ;;
4c2a3841 7370 list)
95ef046d 7371 list "$_listraw" "$_domain"
6d7eda3e 7372 ;;
27dbe77f 7373 installcronjob) installcronjob "$_confighome" ;;
a63b05a9 7374 uninstallcronjob) uninstallcronjob ;;
7375 cron) cron ;;
4c2a3841 7376 toPkcs)
43822d37 7377 toPkcs "$_domain" "$_password" "$_ecc"
a63b05a9 7378 ;;
4410226d 7379 toPkcs8)
7380 toPkcs8 "$_domain" "$_ecc"
7381 ;;
4c2a3841 7382 createAccountKey)
5fbc47eb 7383 createAccountKey "$_accountkeylength"
a63b05a9 7384 ;;
4c2a3841 7385 createDomainKey)
a63b05a9 7386 createDomainKey "$_domain" "$_keylength"
7387 ;;
4c2a3841 7388 createCSR)
43822d37 7389 createCSR "$_domain" "$_altdomains" "$_ecc"
a63b05a9 7390 ;;
b50e701c 7391 setnotify)
7392 setnotify "$_notify_hook" "$_notify_level" "$_notify_mode"
7393 ;;
737e9e48 7394 setdefaultca)
7395 setdefaultca
7396 ;;
a63b05a9 7397 *)
27dbe77f 7398 if [ "$_CMD" ]; then
7399 _err "Invalid command: $_CMD"
7400 fi
4c2a3841 7401 showhelp
a63b05a9 7402 return 1
4c2a3841 7403 ;;
a63b05a9 7404 esac
d3595686 7405 _ret="$?"
4c2a3841 7406 if [ "$_ret" != "0" ]; then
d3595686 7407 return $_ret
7408 fi
4c2a3841 7409
7410 if [ "${_CMD}" = "install" ]; then
7411 if [ "$_log" ]; then
7412 if [ -z "$LOG_FILE" ]; then
d0871bda 7413 LOG_FILE="$DEFAULT_LOG_FILE"
7414 fi
7415 _saveaccountconf "LOG_FILE" "$LOG_FILE"
5ea6e9c9 7416 fi
4c2a3841 7417
7418 if [ "$_log_level" ]; then
a73c5b33 7419 _saveaccountconf "LOG_LEVEL" "$_log_level"
7420 fi
e2edf208 7421
7422 if [ "$_syslog" ]; then
7423 if _exists logger; then
7424 if [ "$_syslog" = "0" ]; then
7425 _clearaccountconf "SYS_LOG"
7426 else
7427 _saveaccountconf "SYS_LOG" "$_syslog"
7428 fi
7429 else
7430 _err "The 'logger' command is not found, can not enable syslog."
7431 _clearaccountconf "SYS_LOG"
7432 SYS_LOG=""
7433 fi
7434 fi
7435
5ea6e9c9 7436 _processAccountConf
b5eb4b90 7437 fi
635695ec 7438
a63b05a9 7439}
7440
4c2a3841 7441if [ "$INSTALLONLINE" ]; then
d1f97fc8 7442 INSTALLONLINE=""
2fbf3991 7443 _installOnline
4a0f23e2 7444 exit
7445fi
4c3b3608 7446
319e0ae3 7447main() {
7448 [ -z "$1" ] && showhelp && return
4c2a3841 7449 if _startswith "$1" '-'; then _process "$@"; else "$@"; fi
319e0ae3 7450}
e69a7c38 7451
aa7b82de 7452main "$@"