From beff333a1a53fed8c60110c0eab221dd3331c719 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Tue, 17 Oct 2017 14:46:26 +0200 Subject: [PATCH] Evoacme: big refactoring * debug messages are sent to stdout * domains discovery from vhosts is extracted to "vhost-domains" * fixes suggested by shellcheck * variables are "local" or "readonly" wherever possible --- evoacme/files/evoacme.sh | 161 ++++++++-------- evoacme/files/make-csr.sh | 331 +++++++++++++++++---------------- evoacme/files/vhost-domains.sh | 151 +++++++++++++++ 3 files changed, 411 insertions(+), 232 deletions(-) create mode 100755 evoacme/files/vhost-domains.sh diff --git a/evoacme/files/evoacme.sh b/evoacme/files/evoacme.sh index 5f109bae..4d9f9f47 100755 --- a/evoacme/files/evoacme.sh +++ b/evoacme/files/evoacme.sh @@ -11,37 +11,40 @@ set -e set -u usage() { - echo "Usage: $0 NAME" - echo "" - echo "NAME must be correspond to :" - echo "- a CSR in ${CSR_DIR}/NAME.csr" - echo "- a KEY in ${SSL_KEY_DIR}/NAME.key" - echo "" - echo "If env variable TEST=1, certbot is run in staging mode" - echo "If env variable DRY_RUN=1, certbot is run in dry-run mode" - echo "If env variable CRON=1, no message is output" - echo "" + cat <&2 echo "${PROGNAME}: $1" } - error() { - echo "error: $1" >&2 - [ "$1" = "invalid argument(s)" ] && usage + >&2 echo "${PROGNAME}: $1" + [ "$1" = "invalid argument(s)" ] && >&2 usage exit 1 } sed_cert_path_for_apache() { - vhost=$1 - vhost_full_path="/etc/apache2/ssl/${vhost}.conf" - cert_path=$2 + local vhost=$1 + local vhost_full_path="/etc/apache2/ssl/${vhost}.conf" + local cert_path=$2 [ ! -r "${vhost_full_path}" ] || return 0 - search="^SSLCertificateFile.*$" - replace="SSLCertificateFile ${cert_path}" + local search="^SSLCertificateFile.*$" + local replace="SSLCertificateFile ${cert_path}" if ! $(grep -qE "${search}" "${vhost_full_path}"); then [ -w "${vhost_full_path}" ] || error "File ${vhost_full_path} is not writable" @@ -51,16 +54,15 @@ sed_cert_path_for_apache() { $(command -v apache2ctl) -t fi } - sed_cert_path_for_nginx() { - vhost=$1 - vhost_full_path="/etc/nginx/ssl/${vhost}.conf" - cert_path=$2 + local vhost=$1 + local vhost_full_path="/etc/nginx/ssl/${vhost}.conf" + local cert_path=$2 [ ! -r "${vhost_full_path}" ] || return 0 - search="^ssl_certificate[^_].*$" - replace="ssl_certificate ${cert_path};" + local search="^ssl_certificate[^_].*$" + local replace="ssl_certificate ${cert_path};" if ! $(grep -qE "${search}" "${vhost_full_path}"); then [ -w "${vhost_full_path}" ] || error "File ${vhost_full_path} is not writable" @@ -70,36 +72,27 @@ sed_cert_path_for_nginx() { $(command -v nginx) -t fi } - x509_verify() { - file="$1" + local file="$1" [ -r "$file" ] || error "File ${file} not found" - ${OPENSSL_BIN} x509 -noout -modulus -in "$file" >/dev/null -} -csr_verify() { - file="$1" - [ -r "$file" ] || error "File ${file} not found" - ${OPENSSL_BIN} req -noout -modulus -in "$file" >/dev/null + "${OPENSSL_BIN}" x509 -noout -modulus -in "$file" >/dev/null } x509_enddate() { - file="$1" + local file="$1" [ -r "$file" ] || error "File ${file} not found" - ${OPENSSL_BIN} x509 -noout -enddate -in "$file" + "${OPENSSL_BIN}" x509 -noout -enddate -in "$file" +} +csr_verify() { + local file="$1" + [ -r "$file" ] || error "File ${file} not found" + "${OPENSSL_BIN}" req -noout -modulus -in "$file" >/dev/null } main() { - # Read configuration file, if it exists - [ -r /etc/default/evoacme ] && . /etc/default/evoacme - - # Default value for main variables - SSL_KEY_DIR=${SSL_KEY_DIR:-"/etc/ssl/private"} - ACME_DIR=${ACME_DIR:-"/var/lib/letsencrypt"} - CSR_DIR=${CSR_DIR:-"/etc/ssl/requests"} - CRT_DIR=${CRT_DIR:-"/etc/letsencrypt"} - LOG_DIR=${LOG_DIR:-"/var/log/evoacme"} - SSL_MINDAY=${SSL_MINDAY:-"30"} - SELF_SIGNED_DIR=${SELF_SIGNED_DIR:-"/etc/ssl/self-signed"} - SSL_EMAIL=${SSL_EMAIL:-""} + [ "$1" = "-h" ] || [ "$1" = "--help" ] && usage && exit 0 + # check arguments + echo "1: '$1'" + [ "$#" -eq 1 ] || error "invalid argument(s)" [ -w "${SSL_KEY_DIR}" ] || error "Directory ${SSL_KEY_DIR} is not writable" [ -w "${ACME_DIR}" ] || error "Directory ${ACME_DIR} is not writable" @@ -108,19 +101,11 @@ main() { [ -w "${LOG_DIR}" ] || error "Directory ${LOG_DIR} is not writable" [ -w "${SELF_SIGNED_DIR}" ] || error "Directory ${SELF_SIGNED_DIR} is not writable" - CRON=${CRON:-"0"} - TEST=${TEST:-"0"} - DRY_RUN=${DRY_RUN:-"0"} - - [ "$1" = "-h" ] || [ "$1" = "--help" ] && usage && exit 0 - # check arguments - [ "$#" -eq 1 ] || error "invalid argument(s)" - - VHOST=$(basename "$1" .conf) + readonly VHOST=$(basename "$1" .conf) # check for important programs - OPENSSL_BIN=$(command -v openssl) || error "openssl command not installed" - CERTBOT_BIN=$(command -v certbot) || error "certbot command not installed" + readonly OPENSSL_BIN=$(command -v openssl) || error "openssl command not installed" + readonly CERTBOT_BIN=$(command -v certbot) || error "certbot command not installed" # double check for directories [ -d "${ACME_DIR}" ] || error "${ACME_DIR} is not a directory" @@ -130,7 +115,7 @@ main() { #### CSR VALIDATION # verify .csr file - CSR_FILE="${CSR_DIR}/${VHOST}.csr" + readonly CSR_FILE="${CSR_DIR}/${VHOST}.csr" debug "Using CSR file: ${CSR_FILE}" [ -f "${CSR_FILE}" ] || error "${CSR_FILE} absent" [ -r "${CSR_FILE}" ] || error "${CSR_FILE} is not readable" @@ -138,7 +123,7 @@ main() { csr_verify "${CSR_FILE}" || error "${CSR_FILE} is invalid" # Hook for evoadmin-web in cluster mode : check master status - evoadmin_state_file="/home/${VHOST}/state" + local evoadmin_state_file="/home/${VHOST}/state" [ -r "${evoadmin_state_file}" ] \ && grep -q "STATE=slave" "${evoadmin_state_file}" \ && debug "We are slave of this evoadmin cluster. Quit!" \ @@ -146,10 +131,10 @@ main() { #### INIT OR RENEW? - LIVE_DIR="${CRT_DIR}/${VHOST}/live" - LIVE_CERT="${LIVE_DIR}/cert.crt" - LIVE_FULLCHAIN="${LIVE_DIR}/fullchain.pem" - LIVE_CHAIN="${LIVE_DIR}/chain.pem" + readonly LIVE_DIR="${CRT_DIR}/${VHOST}/live" + readonly LIVE_CERT="${LIVE_DIR}/cert.crt" + readonly LIVE_FULLCHAIN="${LIVE_DIR}/fullchain.pem" + readonly LIVE_CHAIN="${LIVE_DIR}/chain.pem" # If live symlink already exists, it's not our first time... if [ -h "${LIVE_DIR}" ]; then @@ -169,10 +154,10 @@ main() { #### CERTIFICATE CREATION WITH CERTBOT - ITERATION=$(date "+%Y%m%d%H%M%S") - [ -n "${ITERATION}" ] || error "invalid iteration (${ITERATION})" + local iteration=$(date "+%Y%m%d%H%M%S") + [ -n "${iteration}" ] || error "invalid iteration (${iteration})" - NEW_DIR="${CRT_DIR}/${VHOST}/${ITERATION}" + readonly NEW_DIR="${CRT_DIR}/${VHOST}/${iteration}" [ -d "${NEW_DIR}" ] && error "${NEW_DIR} directory already exists, remove it manually." mkdir -p "${NEW_DIR}" @@ -180,16 +165,16 @@ main() { chown -R acme: "${CRT_DIR}" debug "New cert will be created in ${NEW_DIR}" - NEW_CERT="${NEW_DIR}/cert.crt" - NEW_FULLCHAIN="${NEW_DIR}/fullchain.pem" - NEW_CHAIN="${NEW_DIR}/chain.pem" + readonly NEW_CERT="${NEW_DIR}/cert.crt" + readonly NEW_FULLCHAIN="${NEW_DIR}/fullchain.pem" + readonly NEW_CHAIN="${NEW_DIR}/chain.pem" - CERTBOT_MODE="" + local CERTBOT_MODE="" [ "${TEST}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --test-cert" - [ "${CRON}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --quiet" + [ "${QUIET}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --quiet" [ "${DRY_RUN}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --dry-run" - CERTBOT_REGISTRATION="--agree-tos" + local CERTBOT_REGISTRATION="--agree-tos" if [ -n "${SSL_EMAIL}" ]; then debug "Registering at certbot with ${SSL_EMAIL} as email" CERTBOT_REGISTRATION="${CERTBOT_REGISTRATION} -m ${SSL_EMAIL}" @@ -204,7 +189,7 @@ main() { # create a certificate with certbot sudo -u acme \ - ${CERTBOT_BIN} \ + "${CERTBOT_BIN}" \ certonly \ ${CERTBOT_MODE} \ ${CERTBOT_REGISTRATION} \ @@ -220,7 +205,7 @@ main() { | grep -v "certbot.crypto_util" if [ "${DRY_RUN}" = "1" ]; then - echo "In dry-run mode, we stop here. Bye" + debug "In dry-run mode, we stop here. Bye" exit 0 fi @@ -229,6 +214,8 @@ main() { x509_verify "${NEW_FULLCHAIN}" || error "${NEW_FULLCHAIN} is invalid" x509_verify "${NEW_CHAIN}" || error "${NEW_CHAIN} is invalid" + log "New certificate available at ${NEW_CERT}" + #### CERTIFICATE ACTIVATION # link dance @@ -264,4 +251,26 @@ main() { fi } -main "$@" +readonly PROGNAME=$(basename "$0") +readonly PROGDIR=$(readlink -m $(dirname "$0")) +readonly ARGS=$@ + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} +readonly TEST=${TEST:-"0"} +readonly DRY_RUN=${DRY_RUN:-"0"} + +# Read configuration file, if it exists +[ -r /etc/default/evoacme ] && . /etc/default/evoacme + +# Default value for main variables +readonly SSL_KEY_DIR=${SSL_KEY_DIR:-"/etc/ssl/private"} +readonly ACME_DIR=${ACME_DIR:-"/var/lib/letsencrypt"} +readonly CSR_DIR=${CSR_DIR:-"/etc/ssl/requests"} +readonly CRT_DIR=${CRT_DIR:-"/etc/letsencrypt"} +readonly LOG_DIR=${LOG_DIR:-"/var/log/evoacme"} +readonly SSL_MINDAY=${SSL_MINDAY:-"30"} +readonly SELF_SIGNED_DIR=${SELF_SIGNED_DIR:-"/etc/ssl/self-signed"} +readonly SSL_EMAIL=${SSL_EMAIL:-""} + +main ${ARGS} diff --git a/evoacme/files/make-csr.sh b/evoacme/files/make-csr.sh index 33aa77b0..6bd27125 100755 --- a/evoacme/files/make-csr.sh +++ b/evoacme/files/make-csr.sh @@ -7,110 +7,38 @@ # Licence: AGPLv3 # -real_ip_for_domain() { - dig +short "$1" | grep -oE "([0-9]+\.){3}[0-9]+" +set -u + +usage() { + cat <&2 echo "${PROGNAME}: $1" + fi +} +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 } -get_domains() { - echo "$vhostfile" | grep -q nginx - if [ "$?" -eq 0 ]; then - domains=$( - grep -oE "^( )*[^#]+" "$vhostfile" \ - | grep -oE "[^\$]server_name.*;$" \ - | sed 's/server_name//' \ - | tr -d ';' \ - | sed 's/\s\{1,\}//' \ - | sed 's/\s\{1,\}/\n/g' \ - | sort \ - | uniq - ) - fi - - echo "$vhostfile" | grep -q apache2 - if [ "$?" -eq 0 ]; then - domains=$( - grep -oE "^( )*[^#]+" "$vhostfile" \ - | grep -oE "(ServerName|ServerAlias).*" \ - | sed 's/ServerName//' \ - | sed 's/ServerAlias//' \ - | sed 's/\s\{1,\}//' \ - | sort \ - | uniq - ) - fi - valid_domains="" - nb=0 - - echo "Valid(s) domain(s) in ${VHOST} :" - for domain in $domains; do - real_ip=$(real_ip_for_domain "${domain}") - for ip in $(echo "${SRV_IP}" | xargs -n1); do - if [ "${ip}" = "${real_ip}" ]; then - valid_domains="${valid_domains} ${domain}" - nb=$(( nb + 1 )) - echo "* ${domain} -> ${real_ip}" - fi - done - done - - if [ "${nb}" -eq 0 ]; then - nb=$(echo "${domains}" | wc -l) - echo "* No valid domain found" - echo "All following(s) domain(s) will be used for CSR creation :" - for domain in $domains; do - echo "* ${domain}" - done - else - domains="${valid_domains}" - fi - - domains=$(echo "$domains" | xargs -n1) -} - -make_key() { - openssl genrsa -out "${SSL_KEY_FILE}" "${SSL_KEY_SIZE}" 2>/dev/null - chown root: "${SSL_KEY_FILE}" - chmod 600 "${SSL_KEY_FILE}" -} - -make_csr() { - domains="$1" - nb=$(echo "${domains}" | wc -l) - config_file="/tmp/make-csr-${VHOST}.conf" - - mkdir -p -m 0755 "${CSR_DIR}" || error "Unable to mkdir ${CSR_DIR}" - - if [ "${nb}" -eq 1 ]; then - cat ${SSL_CONFIG_FILE} - > "${config_file}" < "${config_file}" < "${CSR_FILE}" - fi - - if [ -f "${CSR_FILE}" ]; then - chmod 644 "${CSR_FILE}" - mkdir -p -m 0755 "${SELF_SIGNED_DIR}" - openssl x509 -req -sha256 -days 365 -in "${CSR_FILE}" -signkey "${SSL_KEY_FILE}" -out "${SELF_SIGNED_FILE}" - [ -f "${SELF_SIGNED_FILE}" ] && chmod 644 "${SELF_SIGNED_FILE}" - fi +default_key_size() { + grep default_bits "${SSL_CONFIG_FILE}" | cut -d'=' -f2 | xargs } sed_selfsigned_cert_path_for_apache() { - apache_ssl_vhost_path="$1" + local apache_ssl_vhost_path="$1" mkdir -p $(dirname "${apache_ssl_vhost_path}") if [ ! -f "${apache_ssl_vhost_path}" ]; then @@ -119,13 +47,18 @@ SSLEngine On SSLCertificateFile ${SELF_SIGNED_FILE} SSLCertificateKeyFile ${SSL_KEY_FILE} EOF + debug "SSL config added in ${apache_ssl_vhost_path}" else - sed -i "s~^SSLCertificateFile.*$~SSLCertificateFile ${SELF_SIGNED_FILE}~" "${apache_ssl_vhost_path}" + local search="^SSLCertificateFile.*$" + local replace="SSLCertificateFile ${SELF_SIGNED_FILE}" + + sed -i "s~${search}~${replace}~" "${apache_ssl_vhost_path}" + debug "SSL config updated in ${apache_ssl_vhost_path}" fi } sed_selfsigned_cert_path_for_nginx() { - nginx_ssl_vhost_path="$1" + local nginx_ssl_vhost_path="$1" mkdir -p $(dirname "${nginx_ssl_vhost_path}") if [ ! -f "${nginx_ssl_vhost_path}" ]; then @@ -133,85 +66,171 @@ sed_selfsigned_cert_path_for_nginx() { ssl_certificate ${SELF_SIGNED_FILE}; ssl_certificate_key ${SSL_KEY_FILE}; EOF + debug "SSL config added in ${nginx_ssl_vhost_path}" else - sed -i "s~^ssl_certificate[^_].*$~ssl_certificate ${SELF_SIGNED_FILE};~" "${nginx_ssl_vhost_path}" + local search="^ssl_certificate[^_].*$" + local replace="ssl_certificate ${SELF_SIGNED_FILE};" + + sed -i "s~${search}~${replace}~" "${nginx_ssl_vhost_path}" + debug "SSL config updated in ${nginx_ssl_vhost_path}" fi } -first_vhost_file_found() { - vhost=$1 +openssl_selfsigned() { + local csr="$1" + local key="$2" + local crt="$3" + local crt_dir=$(dirname ${crt}) - ls "/etc/nginx/sites-enabled/${vhost}" \ - "/etc/nginx/sites-enabled/${vhost}.conf" \ - "/etc/apache2/sites-enabled/${vhost}.conf" 2>/dev/null \ - | head -n 1 + [ -r "${csr}" ] || error "File ${csr} is not readable" + [ -r "${key}" ] || error "File ${key} is not readable" + [ -w "${crt_dir}" ] || error "Directory ${crt_dir} is not writable" + + "${OPENSSL_BIN}" x509 -req -sha256 -days 365 -in "${csr}" -signkey "${key}" -out "${crt}" 2> /dev/null +} +openssl_key(){ + local key="$1" + local key_dir=$(dirname "${key}") + local size="$2" + + [ -w "${key_dir}" ] || error "Directory ${key_dir} is not writable" + + "${OPENSSL_BIN}" genrsa -out "${key}" "${size}" 2> /dev/null +} +openssl_csr_san() { + local csr="$1" + local csr_dir=$(dirname "${csr}") + local key="$2" + local cfg="$3" + + [ -w "${csr_dir}" ] || error "Directory ${csr_dir} is not writable" + + "${OPENSSL_BIN}" req -new -sha256 -key "${key}" -reqexts SAN -config "${cfg}" -out "${csr}" +} +openssl_csr_single() { + local csr="$1" + local csr_dir=$(dirname "${csr}") + local key="$2" + local cfg="$3" + + [ -w "${csr_dir}" ] || error "Directory ${csr_dir} is not writable" + + "${OPENSSL_BIN}" req -new -sha256 -key "${key}" -config "${cfg}" -out "${csr}" } -default_key_size() { - grep default_bits ${SSL_CONFIG_FILE} \ - | cut -d'=' -f2 \ - | xargs +make_key() { + local key="$1" + local size="$2" + + openssl_key "${key}" "${size}" + debug "Private key stored at ${key}" + + chown root: "${key}" + chmod 600 "${key}" +} + +make_csr() { + local domains=$@ + local nb=$# + local config_file="/tmp/make-csr-${VHOST}.conf" + local san= + + mkdir -p -m 0755 "${CSR_DIR}" || error "Unable to mkdir ${CSR_DIR}" + + if [ "${nb}" -eq 1 ]; then + cat "${SSL_CONFIG_FILE}" - > "${config_file}" < "${config_file}" <&2 - exit 1 + if [ -t 0 ]; then + # We have STDIN, so we should have at least 2 arguments + if [ "$#" -lt 2 ]; then + >&2 echo "invalid arguments" + >&2 usage + exit 1 + fi + # read VHOST from first argument + readonly VHOST="$1" + # remove the first argument + shift + # read domains from remaining arguments + readonly DOMAINS=$@ + else + # We don't have STDIN, so we should have only 1 argument + if [ "$#" != 1 ]; then + >&2 echo "invalid arguments" + >&2 usage + exit 1 + fi + # read VHOST from first argument + readonly VHOST="$1" + # read domains from input + DOMAINS= + while read -r line ; do + DOMAINS="${DOMAINS} ${line}" + done + # trim the string to remove leading/trailing spaces + DOMAINS=$(echo "${DOMAINS}" | xargs) fi - # Read configuration file, if it exists - [ -r /etc/default/evoacme ] && . /etc/default/evoacme - - # Default value for main variables - CSR_DIR=${CSR_DIR:-'/etc/ssl/requests'} - CRT_DIR=${CRT_DIR:-'/etc/letsencrypt'} - SSL_CONFIG_FILE=${SSL_CONFIG_FILE:-"${CRT_DIR}/openssl.cnf"} - SELF_SIGNED_DIR=${SELF_SIGNED_DIR:-'/etc/ssl/self-signed'} - SSL_KEY_DIR=${SSL_KEY_DIR:-'/etc/ssl/private'} - SSL_KEY_SIZE=${SSL_KEY_SIZE:-$(default_key_size)} - SRV_IP=${SRV_IP:-""} - [ -w "${CSR_DIR}" ] || error "Directory ${CSR_DIR} is not writable" - [ -w "${CRT_DIR}" ] || error "Directory ${CRT_DIR} is not writable" [ -w "${SELF_SIGNED_DIR}" ] || error "Directory ${SELF_SIGNED_DIR} is not writable" [ -w "${SSL_KEY_DIR}" ] || error "Directory ${SSL_KEY_DIR} is not writable" [ -r "${SSL_CONFIG_FILE}" ] || error "File ${SSL_CONFIG_FILE} is not readable" - VHOST=$(basename "$1" .conf) + # check for important programs + readonly OPENSSL_BIN=$(command -v openssl) || error "openssl command not installed" + SELF_SIGNED_FILE="${SELF_SIGNED_DIR}/${VHOST}.pem" SSL_KEY_FILE="${SSL_KEY_DIR}/${VHOST}.key" - LIVE_DIR="${CRT_DIR}/${VHOST}/live" CSR_FILE="${CSR_DIR}/${VHOST}.csr" - LOCAL_IP=$(local_ip) - if [ -n "${SRV_IP}" ]; then - SRV_IP="${SRV_IP} ${LOCAL_IP}" - else - SRV_IP="${LOCAL_IP}" - fi - - vhostfile=$(first_vhost_file_found "${VHOST}") - - if [ ! -h "${vhostfile}" ]; then - echo "${VHOST} is not a valid virtualhost !" >&2 - exit 1 - fi - - if [ -f "${SSL_KEY_FILE}" ]; then - echo "${VHOST} key already exist, overwrite it? [yN]" - read REPLY - - [ "${REPLY}" = "Y" ] || [ "${REPLY}" = "y" ] || exit 0 - rm -f "/etc/apache2/ssl/${VHOST}.conf /etc/nginx/ssl/${VHOST}.conf" - [ -h "${LIVE_DIR}" ] && rm "${LIVE_DIR}" - fi - - get_domains - make_key - make_csr "${domains}" + make_key "${SSL_KEY_FILE}" "${SSL_KEY_SIZE}" + make_csr ${DOMAINS} command -v apache2ctl >/dev/null && sed_selfsigned_cert_path_for_apache "/etc/apache2/ssl/${VHOST}.conf" command -v nginx >/dev/null && sed_selfsigned_cert_path_for_nginx "/etc/nginx/ssl/${VHOST}.conf" } -main "$@" +readonly PROGNAME=$(basename "$0") +readonly PROGDIR=$(readlink -m $(dirname "$0")) +readonly ARGS=$@ + +readonly VERBOSE=${VERBOSE:-"0"} + +# Read configuration file, if it exists +[ -r /etc/default/evoacme ] && . /etc/default/evoacme + +# Default value for main variables +CSR_DIR=${CSR_DIR:-'/etc/ssl/requests'} +SSL_CONFIG_FILE=${SSL_CONFIG_FILE:-"${CRT_DIR}/openssl.cnf"} +SELF_SIGNED_DIR=${SELF_SIGNED_DIR:-'/etc/ssl/self-signed'} +SSL_KEY_DIR=${SSL_KEY_DIR:-'/etc/ssl/private'} +SSL_KEY_SIZE=${SSL_KEY_SIZE:-$(default_key_size)} + +main ${ARGS} diff --git a/evoacme/files/vhost-domains.sh b/evoacme/files/vhost-domains.sh new file mode 100755 index 00000000..d8cdd2f4 --- /dev/null +++ b/evoacme/files/vhost-domains.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# +# make-csr is a shell script designed to automatically generate a +# certificate signing request (CSR) from an Apache or a Nginx vhost +# +# Author: Victor Laborie +# Licence: AGPLv3 +# + +set -u + +usage() { + cat <&2 echo "${PROGNAME}: $1" +} +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} + +real_ip_for_domain() { + dig +short "$1" | grep -oE "([0-9]+\.){3}[0-9]+" +} +local_ip() { + ip a | grep brd | cut -d'/' -f1 | grep -oE "([0-9]+\.){3}[0-9]+" +} + +nginx_domains() { + local vhost_file="$1" + + grep -oE "^( )*[^#]+" "${vhost_file}" \ + | grep -oE "[^\$]server_name.*;$" \ + | sed 's/server_name//' \ + | tr -d ';' \ + | sed 's/\s\{1,\}//' \ + | sed 's/\s\{1,\}/\n/g' \ + | sort \ + | uniq +} + +apache_domains() { + local vhost_file="$1" + + grep -oE "^( )*[^#]+" "${vhost_file}" \ + | grep -oE "(ServerName|ServerAlias).*" \ + | sed 's/ServerName//' \ + | sed 's/ServerAlias//' \ + | sed 's/\s\{1,\}//' \ + | sort \ + | uniq +} + +get_domains() { + local vhost_file="$1" + local ips="$2" + local domains="" + local valid_domains="" + local nb=0 + + if $(echo "${vhost_file}" | grep -q nginx); then + debug "Nginx vhost file used" + domains=$(nginx_domains "${vhost_file}") + fi + if $(echo "${vhost_file}" | grep -q apache2); then + debug "Apache vhost file used" + domains=$(apache_domains "${vhost_file}") + fi + + debug "Valid(s) domain(s) in ${vhost_file} :" + for domain in ${domains}; do + real_ip=$(real_ip_for_domain "${domain}") + for ip in $(echo "${ips}" | xargs -n1); do + if [ "${ip}" = "${real_ip}" ]; then + valid_domains="${valid_domains} ${domain}" + nb=$(( nb + 1 )) + debug "* ${domain} -> ${real_ip}" + fi + done + done + + if [ "${nb}" -eq 0 ]; then + nb=$(echo "${domains}" | wc -l) + debug "* No valid domain found" + debug "All following(s) domain(s) will be used for CSR creation :" + for domain in ${domains}; do + debug "* ${domain}" + done + else + domains="${valid_domains}" + fi + + echo "${domains}" | xargs -n 1 +} + +first_vhost_file_found() { + local vhost_name="$1" + + ls "/etc/nginx/sites-enabled/${vhost_name}" \ + "/etc/nginx/sites-enabled/${vhost_name}.conf" \ + "/etc/apache2/sites-enabled/${vhost_name}.conf" \ + 2>/dev/null \ + | head -n 1 +} + +main() { + if [ "$#" != 1 ]; then + >&2 usage + exit 1 + fi + if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + usage + exit 0 + fi + + local vhost_name=$(basename "$1" .conf) + local vhost_file=$(first_vhost_file_found "${vhost_name}") + + if [ ! -h "${vhost_file}" ]; then + >&2 echo "No virtualhost has been found for '${vhost_name}'." + exit 1 + fi + + local ips=$(local_ip) + if [ -n "${SRV_IP}" ]; then + ips="${ips} ${SRV_IP}" + fi + + get_domains "${vhost_file}" "${ips}" +} + +readonly PROGNAME=$(basename "$0") +readonly PROGDIR=$(readlink -m $(dirname "$0")) +readonly ARGS=$@ + +readonly VERBOSE=${VERBOSE:-"0"} +readonly SRV_IP=${SRV_IP:-""} + +main $ARGS