]> git.street.me.uk Git - andy/dehydrated.git/commitdiff
Merge remote-tracking branch 'origin/master'
authorAndy Street <andy@street.me.uk>
Sun, 12 Mar 2017 14:09:18 +0000 (14:09 +0000)
committerAndy Street <andy@street.me.uk>
Sun, 12 Mar 2017 14:09:18 +0000 (14:09 +0000)
1  2 
dehydrated

diff --combined dehydrated
index e491d89fccd1ede0f83868796c8ee7fd032b12ee,8b31ee1f97cbda486b82455b19c264f5769c6a97..8442dbcee15b4c955d1b76d42d1c6da3700d6129
@@@ -34,8 -34,8 +34,8 @@@ check_dependencies() 
    openssl version > /dev/null 2>&1 || _exiterr "This script requires an openssl binary."
    _sed "" < /dev/null > /dev/null 2>&1 || _exiterr "This script requires sed with support for extended (modern) regular expressions."
    command -v grep > /dev/null 2>&1 || _exiterr "This script requires grep."
-   _mktemp -u > /dev/null 2>&1 || _exiterr "This script requires mktemp."
-   diff -u /dev/null /dev/null || _exiterr "This script requires diff."
+   command -v mktemp > /dev/null 2>&1 || _exiterr "This script requires mktemp."
+   command -v diff > /dev/null 2>&1 || _exiterr "This script requires diff."
  
    # curl returns with an error code in some ancient versions so we have to catch that
    set +e
@@@ -73,7 -73,6 +73,7 @@@ reset_configvars() 
    OPENSSL_CNF="${__OPENSSL_CNF}"
    RENEW_DAYS="${__RENEW_DAYS}"
    IP_VERSION="${__IP_VERSION}"
 +  ALT_NAMES=
  }
  
  # verify configuration values
@@@ -82,7 -81,7 +82,7 @@@ verify_config() 
    if [[ "${CHALLENGETYPE}" = "dns-01" ]] && [[ -z "${HOOK}" ]]; then
      _exiterr "Challenge type dns-01 needs a hook script for deployment... can not continue."
    fi
-   if [[ "${CHALLENGETYPE}" = "http-01" && ! -d "${WELLKNOWN}" ]]; then
+   if [[ "${CHALLENGETYPE}" = "http-01" && ! -d "${WELLKNOWN}" && ! "${COMMAND:-}" = "register" ]]; then
      _exiterr "WELLKNOWN directory doesn't exist, please create ${WELLKNOWN} and set appropriate permissions."
    fi
    [[ "${KEY_ALGO}" =~ ^(rsa|prime256v1|secp384r1)$ ]] || _exiterr "Unknown public key algorithm ${KEY_ALGO}... can not continue."
@@@ -106,7 -105,8 +106,8 @@@ load_config() 
  
    # Default values
    CA="https://acme-v01.api.letsencrypt.org/directory"
-   LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
+   CA_TERMS="https://acme-v01.api.letsencrypt.org/terms"
+   LICENSE=
    CERTDIR=
    ACCOUNTDIR=
    CHALLENGETYPE="http-01"
    [[ -z "${DOMAINS_TXT}" ]] && DOMAINS_TXT="${BASEDIR}/domains.txt"
    [[ -z "${WELLKNOWN}" ]] && WELLKNOWN="/var/www/dehydrated"
    [[ -z "${LOCKFILE}" ]] && LOCKFILE="${BASEDIR}/lock"
+   [[ -n "${PARAM_LOCKFILE_SUFFIX:-}" ]] && LOCKFILE="${LOCKFILE}-${PARAM_LOCKFILE_SUFFIX}"
    [[ -n "${PARAM_NO_LOCK:-}" ]] && LOCKFILE=""
  
    [[ -n "${PARAM_HOOK:-}" ]] && HOOK="${PARAM_HOOK}"
@@@ -221,7 -222,7 +223,7 @@@ init_system() 
    _exiterr "Problem retrieving ACME/CA-URLs, check if your configured CA points to the directory entrypoint."
  
    # Export some environment variables to be used in hook script
-   export WELLKNOWN BASEDIR CERTDIR CONFIG
+   export WELLKNOWN BASEDIR CERTDIR CONFIG COMMAND
  
    # Checking for private key ...
    register_new_key="no"
    else
      # Check if private account key exists, if it doesn't exist yet generate a new one (rsa key)
      if [[ ! -e "${ACCOUNT_KEY}" ]]; then
+       REAL_LICENSE="$(http_request head "${CA_TERMS}" | (grep Location: || true) | awk -F ': ' '{print $2}' | tr -d '\n\r')"
+       if [[ -z "${REAL_LICENSE}" ]]; then
+         printf '\n'
+         printf 'Error retrieving terms of service from certificate authority.\n'
+         printf 'Please set LICENSE in config manually.\n'
+         exit 1
+       fi
+       if [[ ! "${LICENSE}" = "${REAL_LICENSE}" ]]; then
+         if [[ "${PARAM_ACCEPT_TERMS:-}" = "yes" ]]; then
+           LICENSE="${REAL_LICENSE}"
+         else
+           printf '\n'
+           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}"
+           printf 'To accept these terms of service run `%s --register --accept-terms`.\n' "${0}"
+           exit 1
+         fi
+       fi
        echo "+ Generating account key..."
        _openssl genrsa -out "${ACCOUNT_KEY}" "${KEYSIZE}"
        register_new_key="yes"
    # If we generated a new private key in the step above we have to register it with the acme-server
    if [[ "${register_new_key}" = "yes" ]]; then
      echo "+ Registering account key with ACME server..."
-     [[ ! -z "${CA_NEW_REG}" ]] || _exiterr "Certificate authority doesn't allow registrations."
-     # If an email for the contact has been provided then adding it to the registration request
      FAILED=false
-     if [[ -n "${CONTACT_EMAIL}" ]]; then
-       (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "contact":["mailto:'"${CONTACT_EMAIL}"'"], "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
-     else
-       (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
+     if [[ -z "${CA_NEW_REG}" ]]; then
+       echo "Certificate authority doesn't allow registrations."
+       FAILED=true
+     fi
+     # If an email for the contact has been provided then adding it to the registration request
+     if [[ "${FAILED}" = "false" ]]; then
+       if [[ -n "${CONTACT_EMAIL}" ]]; then
+         (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "contact":["mailto:'"${CONTACT_EMAIL}"'"], "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
+       else
+         (signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "agreement": "'"$LICENSE"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
+       fi
      fi
      if [[ "${FAILED}" = "true" ]]; then
        echo
        echo
        rm "${ACCOUNT_KEY}" "${ACCOUNT_KEY_JSON}"
        exit 1
      fi
+   elif [[ "${COMMAND:-}" = "register" ]]; then
+     echo "+ Account already registered!"
+     exit 0
    fi
  }
  
  # Different sed version for different os types...
@@@ -307,6 -336,13 +337,13 @@@ get_json_string_value() 
    sed -n "${filter}"
  }
  
+ rm_json_arrays() {
+   local filter
+   filter='s/\[[^][]*\]/null/g'
+   # remove three levels of nested arrays
+   sed -e "${filter}" -e "${filter}" -e "${filter}"
+ }
  # OpenSSL writes to stderr/stdout even when there are no errors. So just
  # display the output if the exit code was != 0 to simplify debugging.
  _openssl() {
@@@ -353,22 -389,31 +390,31 @@@ http_request() 
    fi
  
    if [[ ! "${statuscode:0:1}" = "2" ]]; then
-     echo "  + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
-     echo >&2
-     echo "Details:" >&2
-     cat "${tempcont}" >&2
-     echo >&2
-     echo >&2
-     rm -f "${tempcont}"
+     if [[ ! "${2}" = "${CA_TERMS}" ]] || [[ ! "${statuscode:0:1}" = "3" ]]; then
+       echo "  + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
+       echo >&2
+       echo "Details:" >&2
+       cat "${tempcont}" >&2
+       echo >&2
+       echo >&2
  
-     # Wait for hook script to clean the challenge if used
-     if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token:+set}" ]]; then
-       "${HOOK}" "clean_challenge" '' "${challenge_token}" "${keyauth}"
-     fi
+       # An exclusive hook for the {1}-request error might be useful (e.g., for sending an e-mail to admins)
+       if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
+         errtxt=`cat ${tempcont}`
+         "${HOOK}" "request_failure" "${statuscode}" "${errtxt}" "${1}"
+       fi
  
-     # remove temporary domains.txt file if used
-     [[ -n "${PARAM_DOMAIN:-}" && -n "${DOMAINS_TXT:-}" ]] && rm "${DOMAINS_TXT}"
-     exit 1
+       rm -f "${tempcont}"
+       # Wait for hook script to clean the challenge if used
+       if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token:+set}" ]]; then
+         "${HOOK}" "clean_challenge" '' "${challenge_token}" "${keyauth}"
+       fi
+       # remove temporary domains.txt file if used
+       [[ -n "${PARAM_DOMAIN:-}" && -n "${DOMAINS_TXT:-}" ]] && rm "${DOMAINS_TXT}"
+       exit 1
+     fi
    fi
  
    cat "${tempcont}"
@@@ -411,7 -456,7 +457,7 @@@ extract_altnames() 
    reqtext="$( <<<"${csr}" openssl req -noout -text )"
    if <<<"${reqtext}" grep -q '^[[:space:]]*X509v3 Subject Alternative Name:[[:space:]]*$'; then
      # SANs used, extract these
-     altnames="$( <<<"${reqtext}" grep -A1 '^[[:space:]]*X509v3 Subject Alternative Name:[[:space:]]*$' | tail -n1 )"
+     altnames="$( <<<"${reqtext}" awk '/X509v3 Subject Alternative Name:/{print;getline;print;}' | tail -n1 )"
      # split to one per line:
      # shellcheck disable=SC1003
      altnames="$( <<<"${altnames}" _sed -e 's/^[[:space:]]*//; s/, /\'$'\n''/g' )"
@@@ -452,9 -497,9 +498,9 @@@ sign_csr() 
  
    local idx=0
    if [[ -n "${ZSH_VERSION:-}" ]]; then
-     local -A challenge_uris challenge_tokens keyauths deploy_args
+     local -A challenge_altnames challenge_uris challenge_tokens keyauths deploy_args
    else
-     local -a challenge_uris challenge_tokens keyauths deploy_args
+     local -a challenge_altnames challenge_uris challenge_tokens keyauths deploy_args
    fi
  
    # Request challenges
      echo " + Requesting challenge for ${altname}..."
      response="$(signed_request "${CA_NEW_AUTHZ}" '{"resource": "new-authz", "identifier": {"type": "dns", "value": "'"${altname}"'"}}' | clean_json)"
  
+     challenge_status="$(printf '%s' "${response}" | rm_json_arrays | get_json_string_value status)"
+     if [ "${challenge_status}" = "valid" ]; then
+        echo " + Already validated!"
+        continue
+     fi
      challenges="$(printf '%s\n' "${response}" | sed -n 's/.*\("challenges":[^\[]*\[[^]]*]\).*/\1/p')"
      repl=$'\n''{' # fix syntax highlighting in Vim
      challenge="$(printf "%s" "${challenges//\{/${repl}}" | grep \""${CHALLENGETYPE}"\")"
          ;;
      esac
  
+     challenge_altnames[${idx}]="${altname}"
      challenge_uris[${idx}]="${challenge_uri}"
      keyauths[${idx}]="${keyauth}"
      challenge_tokens[${idx}]="${challenge_token}"
      deploy_args[${idx}]="${altname} ${challenge_token} ${keyauth_hook}"
      idx=$((idx+1))
    done
+   challenge_count="${idx}"
  
    # Wait for hook script to deploy the challenges if used
-   # shellcheck disable=SC2068
-   [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[@]}
+   if [[ ${challenge_count} -ne 0 ]]; then
+     # shellcheck disable=SC2068
+     [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[@]}
+   fi
  
    # Respond to challenges
+   reqstatus="valid"
    idx=0
-   for altname in ${altnames}; do
-     challenge_token="${challenge_tokens[${idx}]}"
-     keyauth="${keyauths[${idx}]}"
+   if [ ${challenge_count} -ne 0 ]; then
+     for altname in "${challenge_altnames[@]:0}"; do
+       challenge_token="${challenge_tokens[${idx}]}"
+       keyauth="${keyauths[${idx}]}"
  
-     # Wait for hook script to deploy the challenge if used
-     # shellcheck disable=SC2086
-     [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[${idx}]}
-     # Ask the acme-server to verify our challenge and wait until it is no longer pending
-     echo " + Responding to challenge for ${altname}..."
-     result="$(signed_request "${challenge_uris[${idx}]}" '{"resource": "challenge", "keyAuthorization": "'"${keyauth}"'"}' | clean_json)"
+       # Wait for hook script to deploy the challenge if used
+       # shellcheck disable=SC2086
+       [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[${idx}]}
  
-     reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
+       # Ask the acme-server to verify our challenge and wait until it is no longer pending
+       echo " + Responding to challenge for ${altname}..."
+       result="$(signed_request "${challenge_uris[${idx}]}" '{"resource": "challenge", "keyAuthorization": "'"${keyauth}"'"}' | clean_json)"
  
-     while [[ "${reqstatus}" = "pending" ]]; do
-       sleep 1
-       result="$(http_request get "${challenge_uris[${idx}]}")"
        reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
-     done
  
-     [[ "${CHALLENGETYPE}" = "http-01" ]] && rm -f "${WELLKNOWN}/${challenge_token}"
+       while [[ "${reqstatus}" = "pending" ]]; do
+         sleep 1
+         result="$(http_request get "${challenge_uris[${idx}]}")"
+         reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
+       done
  
-     # Wait for hook script to clean the challenge if used
-     if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token}" ]]; then
-       # shellcheck disable=SC2086
-       "${HOOK}" "clean_challenge" ${deploy_args[${idx}]}
-     fi
-     idx=$((idx+1))
+       [[ "${CHALLENGETYPE}" = "http-01" ]] && rm -f "${WELLKNOWN}/${challenge_token}"
  
-     if [[ "${reqstatus}" = "valid" ]]; then
-       echo " + Challenge is valid!"
-     else
-       break
-     fi
-   done
+       # Wait for hook script to clean the challenge if used
+       if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token}" ]]; then
+         # shellcheck disable=SC2086
+         "${HOOK}" "clean_challenge" ${deploy_args[${idx}]}
+       fi
+       idx=$((idx+1))
+       if [[ "${reqstatus}" = "valid" ]]; then
+         echo " + Challenge is valid!"
+       else
+         [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "invalid_challenge" "${altname}" "${result}"
+       fi
+     done
+   fi
  
    # Wait for hook script to clean the challenges if used
    # shellcheck disable=SC2068
-   [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "clean_challenge" ${deploy_args[@]}
+   if [[ ${challenge_count} -ne 0 ]]; then
+     [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "clean_challenge" ${deploy_args[@]}
+   fi
  
    if [[ "${reqstatus}" != "valid" ]]; then
      # Clean up any remaining challenge_tokens if we stopped early
-     if [[ "${CHALLENGETYPE}" = "http-01" ]]; then
+     if [[ "${CHALLENGETYPE}" = "http-01" ]] && [[ ${challenge_count} -ne 0 ]]; then
        while [ ${idx} -lt ${#challenge_tokens[@]} ]; do
          rm -f "${WELLKNOWN}/${challenge_tokens[${idx}]}"
          idx=$((idx+1))
    echo " + Done!"
  }
  
+ # grep issuer cert uri from certificate
+ get_issuer_cert_uri() {
+   certificate="${1}"
+   openssl x509 -in "${certificate}" -noout -text | (grep 'CA Issuers - URI:' | cut -d':' -f2-) || true
+ }
+ # walk certificate chain, retrieving all intermediate certificates
+ walk_chain() {
+   local certificate
+   certificate="${1}"
+   local issuer_cert_uri
+   issuer_cert_uri="${2:-}"
+   if [[ -z "${issuer_cert_uri}" ]]; then issuer_cert_uri="$(get_issuer_cert_uri "${certificate}")"; fi
+   if [[ -n "${issuer_cert_uri}" ]]; then
+     # create temporary files
+     local tmpcert
+     local tmpcert_raw
+     tmpcert_raw="$(_mktemp)"
+     tmpcert="$(_mktemp)"
+     # download certificate
+     http_request get "${issuer_cert_uri}" > "${tmpcert_raw}"
+     # PEM
+     if grep -q "BEGIN CERTIFICATE" "${tmpcert_raw}"; then mv "${tmpcert_raw}" "${tmpcert}"
+     # DER
+     elif openssl x509 -in "${tmpcert_raw}" -inform DER -out "${tmpcert}" -outform PEM 2> /dev/null > /dev/null; then :
+     # PKCS7
+     elif openssl pkcs7 -in "${tmpcert_raw}" -inform DER -out "${tmpcert}" -outform PEM -print_certs 2> /dev/null > /dev/null; then :
+     # Unknown certificate type
+     else _exiterr "Unknown certificate type in chain"
+     fi
+     local next_issuer_cert_uri
+     next_issuer_cert_uri="$(get_issuer_cert_uri "${tmpcert}")"
+     if [[ -n "${next_issuer_cert_uri}" ]]; then
+       printf "\n%s\n" "${issuer_cert_uri}"
+       cat "${tmpcert}"
+       walk_chain "${tmpcert}" "${next_issuer_cert_uri}"
+     fi
+     rm -f "${tmpcert}" "${tmpcert_raw}"
+   fi
+ }
  # Create certificate for domain(s)
  sign_domain() {
    domain="${1}"
    # Create fullchain.pem
    echo " + Creating fullchain.pem..."
    cat "${crt_path}" > "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
-   tmpchain="$(_mktemp)"
-   http_request get "$(openssl x509 -in "${CERTDIR}/${domain}/cert-${timestamp}.pem" -noout -text | grep 'CA Issuers - URI:' | cut -d':' -f2-)" > "${tmpchain}"
-   if grep -q "BEGIN CERTIFICATE" "${tmpchain}"; then
-     mv "${tmpchain}" "${CERTDIR}/${domain}/chain-${timestamp}.pem"
-   else
-     openssl x509 -in "${tmpchain}" -inform DER -out "${CERTDIR}/${domain}/chain-${timestamp}.pem" -outform PEM
-     rm "${tmpchain}"
-   fi
+   walk_chain "${crt_path}" > "${CERTDIR}/${domain}/chain-${timestamp}.pem"
    cat "${CERTDIR}/${domain}/chain-${timestamp}.pem" >> "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
  
    # Update symlinks
    ln -sf "cert-${timestamp}.pem" "${CERTDIR}/${domain}/cert.pem"
  
    # Wait for hook script to clean the challenge and to deploy cert if used
-   export KEY_ALGO
    [[ -n "${HOOK}" ]] && "${HOOK}" "deploy_cert" "${domain}" "${CERTDIR}/${domain}/privkey.pem" "${CERTDIR}/${domain}/cert.pem" "${CERTDIR}/${domain}/fullchain.pem" "${CERTDIR}/${domain}/chain.pem" "${timestamp}"
  
    unset challenge_token
    echo " + Done!"
  }
  
+ # Usage: --register
+ # Description: Register account key
+ command_register() {
+   init_system
+   echo "+ Done!"
+   exit 0
+ }
  # Usage: --cron (-c)
  # Description: Sign/renew non-existant/changed/expiring certificates.
  command_sign_domains() {
  
    if [[ -n "${PARAM_DOMAIN:-}" ]]; then
      DOMAINS_TXT="$(_mktemp)"
 +    tmp_domains="yes"
      printf -- "${PARAM_DOMAIN}" > "${DOMAINS_TXT}"
    elif [[ -e "${DOMAINS_TXT}" ]]; then
      if [[ ! -r "${DOMAINS_TXT}" ]]; then
        _exiterr "domains.txt found but not readable"
      fi
 +  elif [[ -n "${DOMAINS_D}" ]]; then
 +      DOMAINS_TXT="$(_mktemp)"
 +      tmp_domains="yes"
 +      find "${DOMAINS_D}" -maxdepth 1 -type f | grep -o '[^/]*$' > "${DOMAINS_TXT}"
    else
      _exiterr "domains.txt not found and --domain not given"
    fi
    # Generate certificates for all domains found in domains.txt. Check if existing certificate are about to expire
    ORIGIFS="${IFS}"
    IFS=$'\n'
-   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
+   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
      reset_configvars
      IFS="${ORIGIFS}"
      domain="$(printf '%s\n' "${line}" | cut -d' ' -f1)"
          config_var="$(echo "${cfgline:1}" | cut -d'=' -f1)"
          config_value="$(echo "${cfgline:1}" | cut -d'=' -f2-)"
          case "${config_var}" in
 +          ALT_NAMES)
 +            config_value="$(echo "${config_value}" | tr '[:upper:]' '[:lower:]' | _sed -e "s/^'[[:space:]]*//g" -e "s/[[:space:]]*'$//g" -e 's/[[:space:]]+/ /g')"
 +            ;&
            KEY_ALGO|OCSP_MUST_STAPLE|PRIVATE_KEY_RENEW|PRIVATE_KEY_ROLLOVER|KEYSIZE|CHALLENGETYPE|HOOK|WELLKNOWN|HOOK_CHAIN|OPENSSL_CNF|RENEW_DAYS)
              echo "   + ${config_var} = ${config_value}"
              declare -- "${config_var}=${config_value}"
        done
        IFS="${ORIGIFS}"
      fi
 +
 +    if [[ -n "${ALT_NAMES}" ]]; then
 +      if [[ -n "${morenames}" ]]; then
 +        morenames="${morenames} ${ALT_NAMES}"
 +      else
 +        morenames="${ALT_NAMES}"
 +      fi
 +      line="${domain} ${morenames}";
 +    fi
 +
      verify_config
+     export WELLKNOWN CHALLENGETYPE KEY_ALGO PRIVATE_KEY_ROLLOVER
  
      if [[ -e "${cert}" ]]; then
        printf " + Checking domain name(s) of existing cert..."
    done
  
    # remove temporary domains.txt file if used
 -  [[ -n "${PARAM_DOMAIN:-}" ]] && rm -f "${DOMAINS_TXT}"
 +  [[ "${tmp_domains:-}" = "yes" ]] && rm -f "${DOMAINS_TXT}"
  
+   [[ -n "${HOOK}" ]] && "${HOOK}" "exit_hook"
    exit 0
  }
  
@@@ -1012,6 -1101,16 +1120,16 @@@ main() 
          set_command sign_domains
          ;;
  
+       --register)
+         set_command register
+         ;;
+       # PARAM_Usage: --accept-terms
+       # PARAM_Description: Accept CAs terms of service
+       --accept-terms)
+         PARAM_ACCEPT_TERMS="yes"
+         ;;
        --signcsr|-s)
          shift 1
          set_command sign_csr
          PARAM_NO_LOCK="yes"
          ;;
  
+       # PARAM_Usage: --lock-suffix example.com
+       # PARAM_Description: Suffix lockfile name with a string (useful for with -d)
+       --lock-suffix)
+               shift 1
+         check_parameters "${1:-}"
+       PARAM_LOCKFILE_SUFFIX="${1}"
+         ;;
        # PARAM_Usage: --ocsp
        # PARAM_Description: Sets option in CSR indicating OCSP stapling to be mandatory
        --ocsp)
    case "${COMMAND}" in
      env) command_env;;
      sign_domains) command_sign_domains;;
+     register) command_register;;
      sign_csr) command_sign_csr "${PARAM_CSR}";;
      revoke) command_revoke "${PARAM_REVOKECERT}";;
      cleanup) command_cleanup;;