Browse Source

Update to Dehydrated 0.4.0

master
Chris Smith 6 years ago
parent
commit
453c3d5699
Signed by: Chris Smith <chris@chameth.com> GPG Key ID: 3A2D4BBDC4A3C9A9
4 changed files with 241 additions and 86 deletions
  1. 3
    3
      Dockerfile
  2. 2
    2
      LICENCE
  3. 234
    79
      dehydrated
  4. 2
    2
      run.sh

+ 3
- 3
Dockerfile View File

@@ -1,13 +1,13 @@
1 1
 FROM ubuntu:xenial 
2
-MAINTAINER Chris Smith <chris87@gmail.com> 
2
+MAINTAINER Chris Smith <dle@chameth.com> 
3 3
 
4 4
 RUN apt-get update \
5 5
  && apt-get install -y \
6 6
       curl \
7 7
       inotify-tools
8 8
 
9
-COPY letsencrypt.sh run.sh config /
10
-RUN chmod +x /run.sh /letsencrypt.sh
9
+COPY dehydrated run.sh config /
10
+RUN chmod +x /run.sh /dehydrated
11 11
 
12 12
 VOLUME ["/letsencrypt"]
13 13
 

+ 2
- 2
LICENCE View File

@@ -1,7 +1,7 @@
1 1
 The MIT License (MIT)
2 2
 
3
-letsencrypt.sh copyright (c) 2015 Lukas Schauer
4
-all other content copyright (c) 2016 Chris Smith
3
+boulder copyright (c) 2015-2017 Lukas Schauer
4
+all other content copyright (c) 2016-2017 Chris Smith
5 5
 
6 6
 
7 7
 Permission is hereby granted, free of charge, to any person obtaining a copy

letsencrypt.sh → dehydrated View File

@@ -1,7 +1,7 @@
1 1
 #!/usr/bin/env bash
2 2
 
3
-# letsencrypt.sh by lukas2511
4
-# Source: https://github.com/lukas2511/letsencrypt.sh
3
+# dehydrated by lukas2511
4
+# Source: https://github.com/lukas2511/dehydrated
5 5
 #
6 6
 # This script is licensed under The MIT License (see LICENSE for more information).
7 7
 
@@ -25,7 +25,7 @@ BASEDIR="${SCRIPTDIR}"
25 25
 # Create (identifiable) temporary files
26 26
 _mktemp() {
27 27
   # shellcheck disable=SC2068
28
-  mktemp ${@:-} "${TMPDIR:-/tmp}/letsencrypt.sh-XXXXXX"
28
+  mktemp ${@:-} "${TMPDIR:-/tmp}/dehydrated-XXXXXX"
29 29
 }
30 30
 
31 31
 # Check for script dependencies
@@ -34,8 +34,8 @@ check_dependencies() {
34 34
   openssl version > /dev/null 2>&1 || _exiterr "This script requires an openssl binary."
35 35
   _sed "" < /dev/null > /dev/null 2>&1 || _exiterr "This script requires sed with support for extended (modern) regular expressions."
36 36
   command -v grep > /dev/null 2>&1 || _exiterr "This script requires grep."
37
-  _mktemp -u > /dev/null 2>&1 || _exiterr "This script requires mktemp."
38
-  diff -u /dev/null /dev/null || _exiterr "This script requires diff."
37
+  command -v mktemp > /dev/null 2>&1 || _exiterr "This script requires mktemp."
38
+  command -v diff > /dev/null 2>&1 || _exiterr "This script requires diff."
39 39
 
40 40
   # curl returns with an error code in some ancient versions so we have to catch that
41 41
   set +e
@@ -81,7 +81,7 @@ verify_config() {
81 81
   if [[ "${CHALLENGETYPE}" = "dns-01" ]] && [[ -z "${HOOK}" ]]; then
82 82
     _exiterr "Challenge type dns-01 needs a hook script for deployment... can not continue."
83 83
   fi
84
-  if [[ "${CHALLENGETYPE}" = "http-01" && ! -d "${WELLKNOWN}" ]]; then
84
+  if [[ "${CHALLENGETYPE}" = "http-01" && ! -d "${WELLKNOWN}" && ! "${COMMAND:-}" = "register" ]]; then
85 85
     _exiterr "WELLKNOWN directory doesn't exist, please create ${WELLKNOWN} and set appropriate permissions."
86 86
   fi
87 87
   [[ "${KEY_ALGO}" =~ ^(rsa|prime256v1|secp384r1)$ ]] || _exiterr "Unknown public key algorithm ${KEY_ALGO}... can not continue."
@@ -94,7 +94,7 @@ verify_config() {
94 94
 load_config() {
95 95
   # Check for config in various locations
96 96
   if [[ -z "${CONFIG:-}" ]]; then
97
-    for check_config in "/etc/letsencrypt.sh" "/usr/local/etc/letsencrypt.sh" "${PWD}" "${SCRIPTDIR}"; do
97
+    for check_config in "/etc/dehydrated" "/usr/local/etc/dehydrated" "${PWD}" "${SCRIPTDIR}"; do
98 98
       if [[ -f "${check_config}/config" ]]; then
99 99
         BASEDIR="${check_config}"
100 100
         CONFIG="${check_config}/config"
@@ -105,7 +105,8 @@ load_config() {
105 105
 
106 106
   # Default values
107 107
   CA="https://acme-v01.api.letsencrypt.org/directory"
108
-  LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
108
+  CA_TERMS="https://acme-v01.api.letsencrypt.org/terms"
109
+  LICENSE=
109 110
   CERTDIR=
110 111
   ACCOUNTDIR=
111 112
   CHALLENGETYPE="http-01"
@@ -118,6 +119,7 @@ load_config() {
118 119
   KEYSIZE="4096"
119 120
   WELLKNOWN=
120 121
   PRIVATE_KEY_RENEW="yes"
122
+  PRIVATE_KEY_ROLLOVER="no"
121 123
   KEY_ALGO=rsa
122 124
   OPENSSL_CNF="$(openssl version -d | cut -d\" -f2)/openssl.cnf"
123 125
   CONTACT_EMAIL=
@@ -181,8 +183,9 @@ load_config() {
181 183
 
182 184
   [[ -z "${CERTDIR}" ]] && CERTDIR="${BASEDIR}/certs"
183 185
   [[ -z "${DOMAINS_TXT}" ]] && DOMAINS_TXT="${BASEDIR}/domains.txt"
184
-  [[ -z "${WELLKNOWN}" ]] && WELLKNOWN="/var/www/letsencrypt"
186
+  [[ -z "${WELLKNOWN}" ]] && WELLKNOWN="/var/www/dehydrated"
185 187
   [[ -z "${LOCKFILE}" ]] && LOCKFILE="${BASEDIR}/lock"
188
+  [[ -n "${PARAM_LOCKFILE_SUFFIX:-}" ]] && LOCKFILE="${LOCKFILE}-${PARAM_LOCKFILE_SUFFIX}"
186 189
   [[ -n "${PARAM_NO_LOCK:-}" ]] && LOCKFILE=""
187 190
 
188 191
   [[ -n "${PARAM_HOOK:-}" ]] && HOOK="${PARAM_HOOK}"
@@ -219,7 +222,7 @@ init_system() {
219 222
   _exiterr "Problem retrieving ACME/CA-URLs, check if your configured CA points to the directory entrypoint."
220 223
 
221 224
   # Export some environment variables to be used in hook script
222
-  export WELLKNOWN BASEDIR CERTDIR CONFIG
225
+  export WELLKNOWN BASEDIR CERTDIR CONFIG COMMAND
223 226
 
224 227
   # Checking for private key ...
225 228
   register_new_key="no"
@@ -231,6 +234,24 @@ init_system() {
231 234
   else
232 235
     # Check if private account key exists, if it doesn't exist yet generate a new one (rsa key)
233 236
     if [[ ! -e "${ACCOUNT_KEY}" ]]; then
237
+      REAL_LICENSE="$(http_request head "${CA_TERMS}" | (grep Location: || true) | awk -F ': ' '{print $2}' | tr -d '\n\r')"
238
+      if [[ -z "${REAL_LICENSE}" ]]; then
239
+        printf '\n'
240
+        printf 'Error retrieving terms of service from certificate authority.\n'
241
+        printf 'Please set LICENSE in config manually.\n'
242
+        exit 1
243
+      fi
244
+      if [[ ! "${LICENSE}" = "${REAL_LICENSE}" ]]; then
245
+        if [[ "${PARAM_ACCEPT_TERMS:-}" = "yes" ]]; then
246
+          LICENSE="${REAL_LICENSE}"
247
+        else
248
+          printf '\n'
249
+          printf 'To use dehydrated with this certificate authority you have to agree to their terms of service which you can find here: %s\n\n' "${REAL_LICENSE}"
250
+          printf 'To accept these terms of service run `%s --register --accept-terms`.\n' "${0}"
251
+          exit 1
252
+        fi
253
+      fi
254
+
234 255
       echo "+ Generating account key..."
235 256
       _openssl genrsa -out "${ACCOUNT_KEY}" "${KEYSIZE}"
236 257
       register_new_key="yes"
@@ -246,15 +267,23 @@ init_system() {
246 267
 
247 268
   # If we generated a new private key in the step above we have to register it with the acme-server
248 269
   if [[ "${register_new_key}" = "yes" ]]; then
249
-    echo "+ Registering account key with letsencrypt..."
250
-    [[ ! -z "${CA_NEW_REG}" ]] || _exiterr "Certificate authority doesn't allow registrations."
251
-    # If an email for the contact has been provided then adding it to the registration request
270
+    echo "+ Registering account key with ACME server..."
252 271
     FAILED=false
253
-    if [[ -n "${CONTACT_EMAIL}" ]]; then
254
-      (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "contact":["mailto:'"${CONTACT_EMAIL}"'"], "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
255
-    else
256
-      (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
272
+
273
+    if [[ -z "${CA_NEW_REG}" ]]; then
274
+      echo "Certificate authority doesn't allow registrations."
275
+      FAILED=true
257 276
     fi
277
+
278
+    # If an email for the contact has been provided then adding it to the registration request
279
+    if [[ "${FAILED}" = "false" ]]; then
280
+      if [[ -n "${CONTACT_EMAIL}" ]]; then
281
+        (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "contact":["mailto:'"${CONTACT_EMAIL}"'"], "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
282
+      else
283
+        (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
284
+      fi
285
+    fi
286
+
258 287
     if [[ "${FAILED}" = "true" ]]; then
259 288
       echo
260 289
       echo
@@ -262,8 +291,10 @@ init_system() {
262 291
       rm "${ACCOUNT_KEY}" "${ACCOUNT_KEY_JSON}"
263 292
       exit 1
264 293
     fi
294
+  elif [[ "${COMMAND:-}" = "register" ]]; then
295
+    echo "+ Account already registered!"
296
+    exit 0
265 297
   fi
266
-
267 298
 }
268 299
 
269 300
 # Different sed version for different os types...
@@ -305,6 +336,13 @@ get_json_string_value() {
305 336
   sed -n "${filter}"
306 337
 }
307 338
 
339
+rm_json_arrays() {
340
+  local filter
341
+  filter='s/\[[^][]*\]/null/g'
342
+  # remove three levels of nested arrays
343
+  sed -e "${filter}" -e "${filter}" -e "${filter}"
344
+}
345
+
308 346
 # OpenSSL writes to stderr/stdout even when there are no errors. So just
309 347
 # display the output if the exit code was != 0 to simplify debugging.
310 348
 _openssl() {
@@ -351,22 +389,31 @@ http_request() {
351 389
   fi
352 390
 
353 391
   if [[ ! "${statuscode:0:1}" = "2" ]]; then
354
-    echo "  + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
355
-    echo >&2
356
-    echo "Details:" >&2
357
-    cat "${tempcont}" >&2
358
-    echo >&2
359
-    echo >&2
360
-    rm -f "${tempcont}"
392
+    if [[ ! "${2}" = "${CA_TERMS}" ]] || [[ ! "${statuscode:0:1}" = "3" ]]; then
393
+      echo "  + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
394
+      echo >&2
395
+      echo "Details:" >&2
396
+      cat "${tempcont}" >&2
397
+      echo >&2
398
+      echo >&2
361 399
 
362
-    # Wait for hook script to clean the challenge if used
363
-    if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token:+set}" ]]; then
364
-      "${HOOK}" "clean_challenge" '' "${challenge_token}" "${keyauth}"
365
-    fi
400
+      # An exclusive hook for the {1}-request error might be useful (e.g., for sending an e-mail to admins)
401
+      if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
402
+        errtxt=`cat ${tempcont}`
403
+        "${HOOK}" "request_failure" "${statuscode}" "${errtxt}" "${1}"
404
+      fi
405
+
406
+      rm -f "${tempcont}"
366 407
 
367
-    # remove temporary domains.txt file if used
368
-    [[ -n "${PARAM_DOMAIN:-}" && -n "${DOMAINS_TXT:-}" ]] && rm "${DOMAINS_TXT}"
369
-    exit 1
408
+      # Wait for hook script to clean the challenge if used
409
+      if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token:+set}" ]]; then
410
+        "${HOOK}" "clean_challenge" '' "${challenge_token}" "${keyauth}"
411
+      fi
412
+
413
+      # remove temporary domains.txt file if used
414
+      [[ -n "${PARAM_DOMAIN:-}" && -n "${DOMAINS_TXT:-}" ]] && rm "${DOMAINS_TXT}"
415
+      exit 1
416
+    fi
370 417
   fi
371 418
 
372 419
   cat "${tempcont}"
@@ -409,7 +456,7 @@ extract_altnames() {
409 456
   reqtext="$( <<<"${csr}" openssl req -noout -text )"
410 457
   if <<<"${reqtext}" grep -q '^[[:space:]]*X509v3 Subject Alternative Name:[[:space:]]*$'; then
411 458
     # SANs used, extract these
412
-    altnames="$( <<<"${reqtext}" grep -A1 '^[[:space:]]*X509v3 Subject Alternative Name:[[:space:]]*$' | tail -n1 )"
459
+    altnames="$( <<<"${reqtext}" awk '/X509v3 Subject Alternative Name:/{print;getline;print;}' | tail -n1 )"
413 460
     # split to one per line:
414 461
     # shellcheck disable=SC1003
415 462
     altnames="$( <<<"${altnames}" _sed -e 's/^[[:space:]]*//; s/, /\'$'\n''/g' )"
@@ -450,9 +497,9 @@ sign_csr() {
450 497
 
451 498
   local idx=0
452 499
   if [[ -n "${ZSH_VERSION:-}" ]]; then
453
-    local -A challenge_uris challenge_tokens keyauths deploy_args
500
+    local -A challenge_altnames challenge_uris challenge_tokens keyauths deploy_args
454 501
   else
455
-    local -a challenge_uris challenge_tokens keyauths deploy_args
502
+    local -a challenge_altnames challenge_uris challenge_tokens keyauths deploy_args
456 503
   fi
457 504
 
458 505
   # Request challenges
@@ -461,6 +508,12 @@ sign_csr() {
461 508
     echo " + Requesting challenge for ${altname}..."
462 509
     response="$(signed_request "${CA_NEW_AUTHZ}" '{"resource": "new-authz", "identifier": {"type": "dns", "value": "'"${altname}"'"}}' | clean_json)"
463 510
 
511
+    challenge_status="$(printf '%s' "${response}" | rm_json_arrays | get_json_string_value status)"
512
+    if [ "${challenge_status}" = "valid" ]; then
513
+       echo " + Already validated!"
514
+       continue
515
+    fi
516
+
464 517
     challenges="$(printf '%s\n' "${response}" | sed -n 's/.*\("challenges":[^\[]*\[[^]]*]\).*/\1/p')"
465 518
     repl=$'\n''{' # fix syntax highlighting in Vim
466 519
     challenge="$(printf "%s" "${challenges//\{/${repl}}" | grep \""${CHALLENGETYPE}"\")"
@@ -487,6 +540,7 @@ sign_csr() {
487 540
         ;;
488 541
     esac
489 542
 
543
+    challenge_altnames[${idx}]="${altname}"
490 544
     challenge_uris[${idx}]="${challenge_uri}"
491 545
     keyauths[${idx}]="${keyauth}"
492 546
     challenge_tokens[${idx}]="${challenge_token}"
@@ -494,56 +548,64 @@ sign_csr() {
494 548
     deploy_args[${idx}]="${altname} ${challenge_token} ${keyauth_hook}"
495 549
     idx=$((idx+1))
496 550
   done
551
+  challenge_count="${idx}"
497 552
 
498 553
   # Wait for hook script to deploy the challenges if used
499
-  # shellcheck disable=SC2068
500
-  [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[@]}
554
+  if [[ ${challenge_count} -ne 0 ]]; then
555
+    # shellcheck disable=SC2068
556
+    [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[@]}
557
+  fi
501 558
 
502 559
   # Respond to challenges
560
+  reqstatus="valid"
503 561
   idx=0
504
-  for altname in ${altnames}; do
505
-    challenge_token="${challenge_tokens[${idx}]}"
506
-    keyauth="${keyauths[${idx}]}"
507
-
508
-    # Wait for hook script to deploy the challenge if used
509
-    # shellcheck disable=SC2086
510
-    [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[${idx}]}
562
+  if [ ${challenge_count} -ne 0 ]; then
563
+    for altname in "${challenge_altnames[@]:0}"; do
564
+      challenge_token="${challenge_tokens[${idx}]}"
565
+      keyauth="${keyauths[${idx}]}"
511 566
 
512
-    # Ask the acme-server to verify our challenge and wait until it is no longer pending
513
-    echo " + Responding to challenge for ${altname}..."
514
-    result="$(signed_request "${challenge_uris[${idx}]}" '{"resource": "challenge", "keyAuthorization": "'"${keyauth}"'"}' | clean_json)"
567
+      # Wait for hook script to deploy the challenge if used
568
+      # shellcheck disable=SC2086
569
+      [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[${idx}]}
515 570
 
516
-    reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
571
+      # Ask the acme-server to verify our challenge and wait until it is no longer pending
572
+      echo " + Responding to challenge for ${altname}..."
573
+      result="$(signed_request "${challenge_uris[${idx}]}" '{"resource": "challenge", "keyAuthorization": "'"${keyauth}"'"}' | clean_json)"
517 574
 
518
-    while [[ "${reqstatus}" = "pending" ]]; do
519
-      sleep 1
520
-      result="$(http_request get "${challenge_uris[${idx}]}")"
521 575
       reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
522
-    done
523 576
 
524
-    [[ "${CHALLENGETYPE}" = "http-01" ]] && rm -f "${WELLKNOWN}/${challenge_token}"
577
+      while [[ "${reqstatus}" = "pending" ]]; do
578
+        sleep 1
579
+        result="$(http_request get "${challenge_uris[${idx}]}")"
580
+        reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
581
+      done
525 582
 
526
-    # Wait for hook script to clean the challenge if used
527
-    if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token}" ]]; then
528
-      # shellcheck disable=SC2086
529
-      "${HOOK}" "clean_challenge" ${deploy_args[${idx}]}
530
-    fi
531
-    idx=$((idx+1))
583
+      [[ "${CHALLENGETYPE}" = "http-01" ]] && rm -f "${WELLKNOWN}/${challenge_token}"
532 584
 
533
-    if [[ "${reqstatus}" = "valid" ]]; then
534
-      echo " + Challenge is valid!"
535
-    else
536
-      break
537
-    fi
538
-  done
585
+      # Wait for hook script to clean the challenge if used
586
+      if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token}" ]]; then
587
+        # shellcheck disable=SC2086
588
+        "${HOOK}" "clean_challenge" ${deploy_args[${idx}]}
589
+      fi
590
+      idx=$((idx+1))
591
+
592
+      if [[ "${reqstatus}" = "valid" ]]; then
593
+        echo " + Challenge is valid!"
594
+      else
595
+        [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "invalid_challenge" "${altname}" "${result}"
596
+      fi
597
+    done
598
+  fi
539 599
 
540 600
   # Wait for hook script to clean the challenges if used
541 601
   # shellcheck disable=SC2068
542
-  [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "clean_challenge" ${deploy_args[@]}
602
+  if [[ ${challenge_count} -ne 0 ]]; then
603
+    [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "clean_challenge" ${deploy_args[@]}
604
+  fi
543 605
 
544 606
   if [[ "${reqstatus}" != "valid" ]]; then
545 607
     # Clean up any remaining challenge_tokens if we stopped early
546
-    if [[ "${CHALLENGETYPE}" = "http-01" ]]; then
608
+    if [[ "${CHALLENGETYPE}" = "http-01" ]] && [[ ${challenge_count} -ne 0 ]]; then
547 609
       while [ ${idx} -lt ${#challenge_tokens[@]} ]; do
548 610
         rm -f "${WELLKNOWN}/${challenge_tokens[${idx}]}"
549 611
         idx=$((idx+1))
@@ -569,6 +631,51 @@ sign_csr() {
569 631
   echo " + Done!"
570 632
 }
571 633
 
634
+# grep issuer cert uri from certificate
635
+get_issuer_cert_uri() {
636
+  certificate="${1}"
637
+  openssl x509 -in "${certificate}" -noout -text | (grep 'CA Issuers - URI:' | cut -d':' -f2-) || true
638
+}
639
+
640
+# walk certificate chain, retrieving all intermediate certificates
641
+walk_chain() {
642
+  local certificate
643
+  certificate="${1}"
644
+
645
+  local issuer_cert_uri
646
+  issuer_cert_uri="${2:-}"
647
+  if [[ -z "${issuer_cert_uri}" ]]; then issuer_cert_uri="$(get_issuer_cert_uri "${certificate}")"; fi
648
+  if [[ -n "${issuer_cert_uri}" ]]; then
649
+    # create temporary files
650
+    local tmpcert
651
+    local tmpcert_raw
652
+    tmpcert_raw="$(_mktemp)"
653
+    tmpcert="$(_mktemp)"
654
+
655
+    # download certificate
656
+    http_request get "${issuer_cert_uri}" > "${tmpcert_raw}"
657
+
658
+    # PEM
659
+    if grep -q "BEGIN CERTIFICATE" "${tmpcert_raw}"; then mv "${tmpcert_raw}" "${tmpcert}"
660
+    # DER
661
+    elif openssl x509 -in "${tmpcert_raw}" -inform DER -out "${tmpcert}" -outform PEM 2> /dev/null > /dev/null; then :
662
+    # PKCS7
663
+    elif openssl pkcs7 -in "${tmpcert_raw}" -inform DER -out "${tmpcert}" -outform PEM -print_certs 2> /dev/null > /dev/null; then :
664
+    # Unknown certificate type
665
+    else _exiterr "Unknown certificate type in chain"
666
+    fi
667
+
668
+    local next_issuer_cert_uri
669
+    next_issuer_cert_uri="$(get_issuer_cert_uri "${tmpcert}")"
670
+    if [[ -n "${next_issuer_cert_uri}" ]]; then
671
+      printf "\n%s\n" "${issuer_cert_uri}"
672
+      cat "${tmpcert}"
673
+      walk_chain "${tmpcert}" "${next_issuer_cert_uri}"
674
+    fi
675
+    rm -f "${tmpcert}" "${tmpcert_raw}"
676
+  fi
677
+}
678
+
572 679
 # Create certificate for domain(s)
573 680
 sign_domain() {
574 681
   domain="${1}"
@@ -596,6 +703,26 @@ sign_domain() {
596 703
       prime256v1|secp384r1) _openssl ecparam -genkey -name "${KEY_ALGO}" -out "${CERTDIR}/${domain}/privkey-${timestamp}.pem";;
597 704
     esac
598 705
   fi
706
+  # move rolloverkey into position (if any)
707
+  if [[ -r "${CERTDIR}/${domain}/privkey.pem" && -r "${CERTDIR}/${domain}/privkey.roll.pem" && "${PRIVATE_KEY_RENEW}" = "yes" && "${PRIVATE_KEY_ROLLOVER}" = "yes" ]]; then
708
+    echo " + Moving Rolloverkey into position....  "
709
+    mv "${CERTDIR}/${domain}/privkey.roll.pem" "${CERTDIR}/${domain}/privkey-tmp.pem"
710
+    mv "${CERTDIR}/${domain}/privkey-${timestamp}.pem" "${CERTDIR}/${domain}/privkey.roll.pem"
711
+    mv "${CERTDIR}/${domain}/privkey-tmp.pem" "${CERTDIR}/${domain}/privkey-${timestamp}.pem"
712
+  fi
713
+  # generate a new private rollover key if we need or want one
714
+  if [[ ! -r "${CERTDIR}/${domain}/privkey.roll.pem" && "${PRIVATE_KEY_ROLLOVER}" = "yes" && "${PRIVATE_KEY_RENEW}" = "yes" ]]; then
715
+    echo " + Generating private rollover key..."
716
+    case "${KEY_ALGO}" in
717
+      rsa) _openssl genrsa -out "${CERTDIR}/${domain}/privkey.roll.pem" "${KEYSIZE}";;
718
+      prime256v1|secp384r1) _openssl ecparam -genkey -name "${KEY_ALGO}" -out "${CERTDIR}/${domain}/privkey.roll.pem";;
719
+    esac
720
+  fi
721
+  # delete rolloverkeys if disabled
722
+  if [[ -r "${CERTDIR}/${domain}/privkey.roll.pem" && ! "${PRIVATE_KEY_ROLLOVER}" = "yes" ]]; then
723
+    echo " + Removing Rolloverkey (feature disabled)..."
724
+    rm -f "${CERTDIR}/${domain}/privkey.roll.pem"
725
+  fi
599 726
 
600 727
   # Generate signing request config and the actual signing request
601 728
   echo " + Generating signing request..."
@@ -621,10 +748,7 @@ sign_domain() {
621 748
   # Create fullchain.pem
622 749
   echo " + Creating fullchain.pem..."
623 750
   cat "${crt_path}" > "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
624
-  http_request get "$(openssl x509 -in "${CERTDIR}/${domain}/cert-${timestamp}.pem" -noout -text | grep 'CA Issuers - URI:' | cut -d':' -f2-)" > "${CERTDIR}/${domain}/chain-${timestamp}.pem"
625
-  if ! grep -q "BEGIN CERTIFICATE" "${CERTDIR}/${domain}/chain-${timestamp}.pem"; then
626
-    openssl x509 -in "${CERTDIR}/${domain}/chain-${timestamp}.pem" -inform DER -out "${CERTDIR}/${domain}/chain-${timestamp}.pem" -outform PEM
627
-  fi
751
+  walk_chain "${crt_path}" > "${CERTDIR}/${domain}/chain-${timestamp}.pem"
628 752
   cat "${CERTDIR}/${domain}/chain-${timestamp}.pem" >> "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
629 753
 
630 754
   # Update symlinks
@@ -636,13 +760,20 @@ sign_domain() {
636 760
   ln -sf "cert-${timestamp}.pem" "${CERTDIR}/${domain}/cert.pem"
637 761
 
638 762
   # Wait for hook script to clean the challenge and to deploy cert if used
639
-  export KEY_ALGO
640 763
   [[ -n "${HOOK}" ]] && "${HOOK}" "deploy_cert" "${domain}" "${CERTDIR}/${domain}/privkey.pem" "${CERTDIR}/${domain}/cert.pem" "${CERTDIR}/${domain}/fullchain.pem" "${CERTDIR}/${domain}/chain.pem" "${timestamp}"
641 764
 
642 765
   unset challenge_token
643 766
   echo " + Done!"
644 767
 }
645 768
 
769
+# Usage: --register
770
+# Description: Register account key
771
+command_register() {
772
+  init_system
773
+  echo "+ Done!"
774
+  exit 0
775
+}
776
+
646 777
 # Usage: --cron (-c)
647 778
 # Description: Sign/renew non-existant/changed/expiring certificates.
648 779
 command_sign_domains() {
@@ -662,7 +793,7 @@ command_sign_domains() {
662 793
   # Generate certificates for all domains found in domains.txt. Check if existing certificate are about to expire
663 794
   ORIGIFS="${IFS}"
664 795
   IFS=$'\n'
665
-  for line in $(<"${DOMAINS_TXT}" tr -d '\r' | tr '[:upper:]' '[:lower:]' | _sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' -e 's/[[:space:]]+/ /g' | (grep -vE '^(#|$)' || true)); do
796
+  for line in $(<"${DOMAINS_TXT}" tr -d '\r' | awk '{print tolower($0)}' | _sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' -e 's/[[:space:]]+/ /g' | (grep -vE '^(#|$)' || true)); do
666 797
     reset_configvars
667 798
     IFS="${ORIGIFS}"
668 799
     domain="$(printf '%s\n' "${line}" | cut -d' ' -f1)"
@@ -705,7 +836,7 @@ command_sign_domains() {
705 836
         config_var="$(echo "${cfgline:1}" | cut -d'=' -f1)"
706 837
         config_value="$(echo "${cfgline:1}" | cut -d'=' -f2-)"
707 838
         case "${config_var}" in
708
-          KEY_ALGO|OCSP_MUST_STAPLE|PRIVATE_KEY_RENEW|KEYSIZE|CHALLENGETYPE|HOOK|WELLKNOWN|HOOK_CHAIN|OPENSSL_CNF|RENEW_DAYS)
839
+          KEY_ALGO|OCSP_MUST_STAPLE|PRIVATE_KEY_RENEW|PRIVATE_KEY_ROLLOVER|KEYSIZE|CHALLENGETYPE|HOOK|WELLKNOWN|HOOK_CHAIN|OPENSSL_CNF|RENEW_DAYS)
709 840
             echo "   + ${config_var} = ${config_value}"
710 841
             declare -- "${config_var}=${config_value}"
711 842
             ;;
@@ -716,6 +847,7 @@ command_sign_domains() {
716 847
       IFS="${ORIGIFS}"
717 848
     fi
718 849
     verify_config
850
+    export WELLKNOWN CHALLENGETYPE KEY_ALGO PRIVATE_KEY_ROLLOVER
719 851
 
720 852
     if [[ -e "${cert}" ]]; then
721 853
       printf " + Checking domain name(s) of existing cert..."
@@ -767,6 +899,7 @@ command_sign_domains() {
767 899
   # remove temporary domains.txt file if used
768 900
   [[ -n "${PARAM_DOMAIN:-}" ]] && rm -f "${DOMAINS_TXT}"
769 901
 
902
+  [[ -n "${HOOK}" ]] && "${HOOK}" "exit_hook"
770 903
   exit 0
771 904
 }
772 905
 
@@ -797,10 +930,13 @@ command_sign_csr() {
797 930
   if [ -n "${PARAM_FULL_CHAIN:-}" ]; then
798 931
     # get and convert ca cert
799 932
     chainfile="$(_mktemp)"
800
-    http_request get "$(openssl x509 -in "${certfile}" -noout -text | grep 'CA Issuers - URI:' | cut -d':' -f2-)" > "${chainfile}"
801
-
802
-    if ! grep -q "BEGIN CERTIFICATE" "${chainfile}"; then
803
-      openssl x509 -inform DER -in "${chainfile}" -outform PEM -out "${chainfile}"
933
+    tmpchain="$(_mktemp)"
934
+    http_request get "$(openssl x509 -in "${certfile}" -noout -text | grep 'CA Issuers - URI:' | cut -d':' -f2-)" > "${tmpchain}"
935
+    if grep -q "BEGIN CERTIFICATE" "${tmpchain}"; then
936
+      mv "${tmpchain}" "${chainfile}"
937
+    else
938
+      openssl x509 -in "${tmpchain}" -inform DER -out "${chainfile}" -outform PEM
939
+      rm "${tmpchain}"
804 940
     fi
805 941
 
806 942
     echo "# CHAIN #" >&3
@@ -924,7 +1060,7 @@ command_help() {
924 1060
 # Usage: --env (-e)
925 1061
 # Description: Output configuration variables for use in other scripts
926 1062
 command_env() {
927
-  echo "# letsencrypt.sh configuration"
1063
+  echo "# dehydrated configuration"
928 1064
   load_config
929 1065
   typeset -p CA LICENSE CERTDIR CHALLENGETYPE DOMAINS_D DOMAINS_TXT HOOK HOOK_CHAIN RENEW_DAYS ACCOUNT_KEY ACCOUNT_KEY_JSON KEYSIZE WELLKNOWN PRIVATE_KEY_RENEW OPENSSL_CNF CONTACT_EMAIL LOCKFILE
930 1066
 }
@@ -965,6 +1101,16 @@ main() {
965 1101
         set_command sign_domains
966 1102
         ;;
967 1103
 
1104
+      --register)
1105
+        set_command register
1106
+        ;;
1107
+
1108
+      # PARAM_Usage: --accept-terms
1109
+      # PARAM_Description: Accept CAs terms of service
1110
+      --accept-terms)
1111
+        PARAM_ACCEPT_TERMS="yes"
1112
+        ;;
1113
+
968 1114
       --signcsr|-s)
969 1115
         shift 1
970 1116
         set_command sign_csr
@@ -1031,6 +1177,14 @@ main() {
1031 1177
         PARAM_NO_LOCK="yes"
1032 1178
         ;;
1033 1179
 
1180
+      # PARAM_Usage: --lock-suffix example.com
1181
+      # PARAM_Description: Suffix lockfile name with a string (useful for with -d)
1182
+      --lock-suffix)
1183
+      	shift 1
1184
+        check_parameters "${1:-}"
1185
+	PARAM_LOCKFILE_SUFFIX="${1}"
1186
+        ;;
1187
+
1034 1188
       # PARAM_Usage: --ocsp
1035 1189
       # PARAM_Description: Sets option in CSR indicating OCSP stapling to be mandatory
1036 1190
       --ocsp)
@@ -1099,6 +1253,7 @@ main() {
1099 1253
   case "${COMMAND}" in
1100 1254
     env) command_env;;
1101 1255
     sign_domains) command_sign_domains;;
1256
+    register) command_register;;
1102 1257
     sign_csr) command_sign_csr "${PARAM_CSR}";;
1103 1258
     revoke) command_revoke "${PARAM_REVOKECERT}";;
1104 1259
     cleanup) command_cleanup;;

+ 2
- 2
run.sh View File

@@ -9,8 +9,8 @@ interrupt() {
9 9
 trap interrupt SIGINT
10 10
 
11 11
 while true; do
12
-  /letsencrypt.sh --cron --hook /dns/hook --challenge dns-01
13
-  /letsencrypt.sh --cleanup
12
+  /dehydrated --cron --hook /dns/hook --challenge dns-01
13
+  /dehydrated --cleanup
14 14
   inotifywait --timeout 86400 /letsencrypt/domains.txt
15 15
   sleep 60
16 16
 done

Loading…
Cancel
Save