Commit 785ffa55 authored by Alex Macleod's avatar Alex Macleod Committed by Lukas Schauer

Make certificate output location configurable (#210)

parent d5b28586
...@@ -46,6 +46,7 @@ Parameters: ...@@ -46,6 +46,7 @@ Parameters:
--privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation) --privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation)
--config (-f) path/to/config Use specified config file --config (-f) path/to/config Use specified config file
--hook (-k) path/to/hook.sh Use specified script for hooks --hook (-k) path/to/hook.sh Use specified script for hooks
--out (-o) certs/directory Output certificates into the specified directory
--challenge (-t) http-01|dns-01 Which challenge should be used? Currently http-01 and dns-01 are supported --challenge (-t) http-01|dns-01 Which challenge should be used? Currently http-01 and dns-01 are supported
--algo (-a) rsa|prime256v1|secp384r1 Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1 --algo (-a) rsa|prime256v1|secp384r1 Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
``` ```
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
# File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt) # File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt)
#DOMAINS_TXT="${BASEDIR}/domains.txt" #DOMAINS_TXT="${BASEDIR}/domains.txt"
# Output directory for generated certificates
#CERTDIR="${BASEDIR}/certs"
# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: $BASEDIR/.acme-challenges) # Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: $BASEDIR/.acme-challenges)
#WELLKNOWN="${BASEDIR}/.acme-challenges" #WELLKNOWN="${BASEDIR}/.acme-challenges"
......
...@@ -62,6 +62,7 @@ load_config() { ...@@ -62,6 +62,7 @@ load_config() {
# Default values # Default values
CA="https://acme-v01.api.letsencrypt.org/directory" CA="https://acme-v01.api.letsencrypt.org/directory"
LICENSE="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" LICENSE="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
CERTDIR=
CHALLENGETYPE="http-01" CHALLENGETYPE="http-01"
CONFIG_D= CONFIG_D=
DOMAINS_TXT= DOMAINS_TXT=
...@@ -118,11 +119,13 @@ load_config() { ...@@ -118,11 +119,13 @@ load_config() {
[[ -z "${ACCOUNT_KEY}" ]] && ACCOUNT_KEY="${BASEDIR}/private_key.pem" [[ -z "${ACCOUNT_KEY}" ]] && ACCOUNT_KEY="${BASEDIR}/private_key.pem"
[[ -z "${ACCOUNT_KEY_JSON}" ]] && ACCOUNT_KEY_JSON="${BASEDIR}/private_key.json" [[ -z "${ACCOUNT_KEY_JSON}" ]] && ACCOUNT_KEY_JSON="${BASEDIR}/private_key.json"
[[ -z "${CERTDIR}" ]] && CERTDIR="${BASEDIR}/certs"
[[ -z "${DOMAINS_TXT}" ]] && DOMAINS_TXT="${BASEDIR}/domains.txt" [[ -z "${DOMAINS_TXT}" ]] && DOMAINS_TXT="${BASEDIR}/domains.txt"
[[ -z "${WELLKNOWN}" ]] && WELLKNOWN="${BASEDIR}/.acme-challenges" [[ -z "${WELLKNOWN}" ]] && WELLKNOWN="${BASEDIR}/.acme-challenges"
[[ -z "${LOCKFILE}" ]] && LOCKFILE="${BASEDIR}/lock" [[ -z "${LOCKFILE}" ]] && LOCKFILE="${BASEDIR}/lock"
[[ -n "${PARAM_HOOK:-}" ]] && HOOK="${PARAM_HOOK}" [[ -n "${PARAM_HOOK:-}" ]] && HOOK="${PARAM_HOOK}"
[[ -n "${PARAM_CERTDIR:-}" ]] && CERTDIR="${PARAM_CERTDIR}"
[[ -n "${PARAM_CHALLENGETYPE:-}" ]] && CHALLENGETYPE="${PARAM_CHALLENGETYPE}" [[ -n "${PARAM_CHALLENGETYPE:-}" ]] && CHALLENGETYPE="${PARAM_CHALLENGETYPE}"
[[ -n "${PARAM_KEY_ALGO:-}" ]] && KEY_ALGO="${PARAM_KEY_ALGO}" [[ -n "${PARAM_KEY_ALGO:-}" ]] && KEY_ALGO="${PARAM_KEY_ALGO}"
...@@ -154,7 +157,7 @@ init_system() { ...@@ -154,7 +157,7 @@ init_system() {
_exiterr "Problem retrieving ACME/CA-URLs, check if your configured CA points to the directory entrypoint." _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 some environment variables to be used in hook script
export WELLKNOWN BASEDIR CONFIG export WELLKNOWN BASEDIR CERTDIR CONFIG
# Checking for private key ... # Checking for private key ...
register_new_key="no" register_new_key="no"
...@@ -505,19 +508,19 @@ sign_domain() { ...@@ -505,19 +508,19 @@ sign_domain() {
fi fi
# If there is no existing certificate directory => make it # If there is no existing certificate directory => make it
if [[ ! -e "${BASEDIR}/certs/${domain}" ]]; then if [[ ! -e "${CERTDIR}/${domain}" ]]; then
echo " + Creating new directory ${BASEDIR}/certs/${domain} ..." echo " + Creating new directory ${CERTDIR}/${domain} ..."
mkdir -p "${BASEDIR}/certs/${domain}" mkdir -p "${CERTDIR}/${domain}" || _exiterr "Unable to create directory ${CERTDIR}/${domain}"
fi fi
privkey="privkey.pem" privkey="privkey.pem"
# generate a new private key if we need or want one # generate a new private key if we need or want one
if [[ ! -r "${BASEDIR}/certs/${domain}/privkey.pem" ]] || [[ "${PRIVATE_KEY_RENEW}" = "yes" ]]; then if [[ ! -r "${CERTDIR}/${domain}/privkey.pem" ]] || [[ "${PRIVATE_KEY_RENEW}" = "yes" ]]; then
echo " + Generating private key..." echo " + Generating private key..."
privkey="privkey-${timestamp}.pem" privkey="privkey-${timestamp}.pem"
case "${KEY_ALGO}" in case "${KEY_ALGO}" in
rsa) _openssl genrsa -out "${BASEDIR}/certs/${domain}/privkey-${timestamp}.pem" "${KEYSIZE}";; rsa) _openssl genrsa -out "${CERTDIR}/${domain}/privkey-${timestamp}.pem" "${KEYSIZE}";;
prime256v1|secp384r1) _openssl ecparam -genkey -name "${KEY_ALGO}" -out "${BASEDIR}/certs/${domain}/privkey-${timestamp}.pem";; prime256v1|secp384r1) _openssl ecparam -genkey -name "${KEY_ALGO}" -out "${CERTDIR}/${domain}/privkey-${timestamp}.pem";;
esac esac
fi fi
...@@ -532,33 +535,33 @@ sign_domain() { ...@@ -532,33 +535,33 @@ sign_domain() {
tmp_openssl_cnf="$(_mktemp)" tmp_openssl_cnf="$(_mktemp)"
cat "${OPENSSL_CNF}" > "${tmp_openssl_cnf}" cat "${OPENSSL_CNF}" > "${tmp_openssl_cnf}"
printf "[SAN]\nsubjectAltName=%s" "${SAN}" >> "${tmp_openssl_cnf}" printf "[SAN]\nsubjectAltName=%s" "${SAN}" >> "${tmp_openssl_cnf}"
openssl req -new -sha256 -key "${BASEDIR}/certs/${domain}/${privkey}" -out "${BASEDIR}/certs/${domain}/cert-${timestamp}.csr" -subj "/CN=${domain}/" -reqexts SAN -config "${tmp_openssl_cnf}" openssl req -new -sha256 -key "${CERTDIR}/${domain}/${privkey}" -out "${CERTDIR}/${domain}/cert-${timestamp}.csr" -subj "/CN=${domain}/" -reqexts SAN -config "${tmp_openssl_cnf}"
rm -f "${tmp_openssl_cnf}" rm -f "${tmp_openssl_cnf}"
crt_path="${BASEDIR}/certs/${domain}/cert-${timestamp}.pem" crt_path="${CERTDIR}/${domain}/cert-${timestamp}.pem"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
sign_csr "$(< "${BASEDIR}/certs/${domain}/cert-${timestamp}.csr" )" ${altnames} 3>"${crt_path}" sign_csr "$(< "${CERTDIR}/${domain}/cert-${timestamp}.csr" )" ${altnames} 3>"${crt_path}"
# Create fullchain.pem # Create fullchain.pem
echo " + Creating fullchain.pem..." echo " + Creating fullchain.pem..."
cat "${crt_path}" > "${BASEDIR}/certs/${domain}/fullchain-${timestamp}.pem" cat "${crt_path}" > "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
http_request get "$(openssl x509 -in "${BASEDIR}/certs/${domain}/cert-${timestamp}.pem" -noout -text | grep 'CA Issuers - URI:' | cut -d':' -f2-)" > "${BASEDIR}/certs/${domain}/chain-${timestamp}.pem" 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"
if ! grep -q "BEGIN CERTIFICATE" "${BASEDIR}/certs/${domain}/chain-${timestamp}.pem"; then if ! grep -q "BEGIN CERTIFICATE" "${CERTDIR}/${domain}/chain-${timestamp}.pem"; then
openssl x509 -in "${BASEDIR}/certs/${domain}/chain-${timestamp}.pem" -inform DER -out "${BASEDIR}/certs/${domain}/chain-${timestamp}.pem" -outform PEM openssl x509 -in "${CERTDIR}/${domain}/chain-${timestamp}.pem" -inform DER -out "${CERTDIR}/${domain}/chain-${timestamp}.pem" -outform PEM
fi fi
cat "${BASEDIR}/certs/${domain}/chain-${timestamp}.pem" >> "${BASEDIR}/certs/${domain}/fullchain-${timestamp}.pem" cat "${CERTDIR}/${domain}/chain-${timestamp}.pem" >> "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
# Update symlinks # Update symlinks
[[ "${privkey}" = "privkey.pem" ]] || ln -sf "privkey-${timestamp}.pem" "${BASEDIR}/certs/${domain}/privkey.pem" [[ "${privkey}" = "privkey.pem" ]] || ln -sf "privkey-${timestamp}.pem" "${CERTDIR}/${domain}/privkey.pem"
ln -sf "chain-${timestamp}.pem" "${BASEDIR}/certs/${domain}/chain.pem" ln -sf "chain-${timestamp}.pem" "${CERTDIR}/${domain}/chain.pem"
ln -sf "fullchain-${timestamp}.pem" "${BASEDIR}/certs/${domain}/fullchain.pem" ln -sf "fullchain-${timestamp}.pem" "${CERTDIR}/${domain}/fullchain.pem"
ln -sf "cert-${timestamp}.csr" "${BASEDIR}/certs/${domain}/cert.csr" ln -sf "cert-${timestamp}.csr" "${CERTDIR}/${domain}/cert.csr"
ln -sf "cert-${timestamp}.pem" "${BASEDIR}/certs/${domain}/cert.pem" ln -sf "cert-${timestamp}.pem" "${CERTDIR}/${domain}/cert.pem"
# Wait for hook script to clean the challenge and to deploy cert if used # Wait for hook script to clean the challenge and to deploy cert if used
export KEY_ALGO export KEY_ALGO
[[ -n "${HOOK}" ]] && "${HOOK}" "deploy_cert" "${domain}" "${BASEDIR}/certs/${domain}/privkey.pem" "${BASEDIR}/certs/${domain}/cert.pem" "${BASEDIR}/certs/${domain}/fullchain.pem" "${BASEDIR}/certs/${domain}/chain.pem" "${timestamp}" [[ -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 unset challenge_token
echo " + Done!" echo " + Done!"
...@@ -587,7 +590,7 @@ command_sign_domains() { ...@@ -587,7 +590,7 @@ command_sign_domains() {
IFS="${ORIGIFS}" IFS="${ORIGIFS}"
domain="$(printf '%s\n' "${line}" | cut -d' ' -f1)" domain="$(printf '%s\n' "${line}" | cut -d' ' -f1)"
morenames="$(printf '%s\n' "${line}" | cut -s -d' ' -f2-)" morenames="$(printf '%s\n' "${line}" | cut -s -d' ' -f2-)"
cert="${BASEDIR}/certs/${domain}/cert.pem" cert="${CERTDIR}/${domain}/cert.pem"
force_renew="${PARAM_FORCE:-no}" force_renew="${PARAM_FORCE:-no}"
...@@ -627,7 +630,7 @@ command_sign_domains() { ...@@ -627,7 +630,7 @@ command_sign_domains() {
else else
# Certificate-Names unchanged and cert is still valid # Certificate-Names unchanged and cert is still valid
echo "Skipping renew!" echo "Skipping renew!"
[[ -n "${HOOK}" ]] && "${HOOK}" "unchanged_cert" "${domain}" "${BASEDIR}/certs/${domain}/privkey.pem" "${BASEDIR}/certs/${domain}/cert.pem" "${BASEDIR}/certs/${domain}/fullchain.pem" "${BASEDIR}/certs/${domain}/chain.pem" [[ -n "${HOOK}" ]] && "${HOOK}" "unchanged_cert" "${domain}" "${CERTDIR}/${domain}/privkey.pem" "${CERTDIR}/${domain}/cert.pem" "${CERTDIR}/${domain}/fullchain.pem" "${CERTDIR}/${domain}/chain.pem"
continue continue
fi fi
else else
...@@ -706,7 +709,7 @@ command_cleanup() { ...@@ -706,7 +709,7 @@ command_cleanup() {
fi fi
# Loop over all certificate directories # Loop over all certificate directories
for certdir in "${BASEDIR}/certs/"*; do for certdir in "${CERTDIR}/"*; do
# Skip if entry is not a folder # Skip if entry is not a folder
[[ -d "${certdir}" ]] || continue [[ -d "${certdir}" ]] || continue
...@@ -775,7 +778,7 @@ command_help() { ...@@ -775,7 +778,7 @@ command_help() {
command_env() { command_env() {
echo "# letsencrypt.sh configuration" echo "# letsencrypt.sh configuration"
load_config load_config
typeset -p CA LICENSE CHALLENGETYPE DOMAINS_TXT HOOK HOOK_CHAIN RENEW_DAYS ACCOUNT_KEY ACCOUNT_KEY_JSON KEYSIZE WELLKNOWN PRIVATE_KEY_RENEW OPENSSL_CNF CONTACT_EMAIL LOCKFILE typeset -p CA LICENSE CERTDIR CHALLENGETYPE DOMAINS_TXT HOOK HOOK_CHAIN RENEW_DAYS ACCOUNT_KEY ACCOUNT_KEY_JSON KEYSIZE WELLKNOWN PRIVATE_KEY_RENEW OPENSSL_CNF CONTACT_EMAIL LOCKFILE
} }
# Main method (parses script arguments and calls command_* methods) # Main method (parses script arguments and calls command_* methods)
...@@ -875,6 +878,14 @@ main() { ...@@ -875,6 +878,14 @@ main() {
PARAM_HOOK="${1}" PARAM_HOOK="${1}"
;; ;;
# PARAM_Usage: --out (-o) certs/directory
# PARAM_Description: Output certificates into the specified directory
--out|-o)
shift 1
check_parameters "${1:-}"
PARAM_CERTDIR="${1}"
;;
# PARAM_Usage: --challenge (-t) http-01|dns-01 # PARAM_Usage: --challenge (-t) http-01|dns-01
# PARAM_Description: Which challenge should be used? Currently http-01 and dns-01 are supported # PARAM_Description: Which challenge should be used? Currently http-01 and dns-01 are supported
--challenge|-t) --challenge|-t)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment