2017-10-19 11:05:54 +02:00
|
|
|
#!/bin/bash
|
2017-08-25 18:13:32 +02:00
|
|
|
#
|
2017-10-13 00:47:02 +02:00
|
|
|
# make-csr is a shell script designed to automatically generate a
|
2017-08-25 18:13:32 +02:00
|
|
|
# certificate signing request (CSR) from an Apache or a Nginx vhost
|
|
|
|
#
|
|
|
|
# Author: Victor Laborie <vlaborie@evolix.fr>
|
|
|
|
# Licence: AGPLv3
|
|
|
|
#
|
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
set -u
|
|
|
|
|
|
|
|
usage() {
|
|
|
|
cat <<EOT
|
|
|
|
Usage: ${PROGNAME} VHOST DOMAIN...
|
|
|
|
VHOST must correspond to an Apache or Nginx enabled VHost
|
|
|
|
If VHOST ends with ".conf" it is stripped,
|
|
|
|
then files are seached at those paths:
|
|
|
|
- /etc/apache2/sites-enables/VHOST.conf
|
|
|
|
- /etc/nginx/sites-enabled/VHOST.conf
|
|
|
|
- /etc/nginx/sites-enabled/VHOST
|
|
|
|
DOMAIN... is a list of domains for the CSR (passed as arguments or input)
|
|
|
|
|
|
|
|
If env variable VERBOSE=1, debug messages are sent to stderr
|
|
|
|
EOT
|
2017-10-13 00:47:02 +02:00
|
|
|
}
|
2017-10-17 14:46:26 +02:00
|
|
|
debug() {
|
|
|
|
if [ "${VERBOSE}" = 1 ]; then
|
|
|
|
>&2 echo "${PROGNAME}: $1"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
error() {
|
|
|
|
>&2 echo "${PROGNAME}: $1"
|
|
|
|
exit 1
|
2017-10-13 11:16:21 +02:00
|
|
|
}
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
default_key_size() {
|
|
|
|
grep default_bits "${SSL_CONFIG_FILE}" | cut -d'=' -f2 | xargs
|
|
|
|
}
|
|
|
|
|
|
|
|
sed_selfsigned_cert_path_for_apache() {
|
|
|
|
local apache_ssl_vhost_path="$1"
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
mkdir -p $(dirname "${apache_ssl_vhost_path}")
|
|
|
|
if [ ! -f "${apache_ssl_vhost_path}" ]; then
|
|
|
|
cat > "${apache_ssl_vhost_path}" <<EOF
|
|
|
|
SSLEngine On
|
|
|
|
SSLCertificateFile ${SELF_SIGNED_FILE}
|
|
|
|
SSLCertificateKeyFile ${SSL_KEY_FILE}
|
|
|
|
EOF
|
|
|
|
debug "SSL config added in ${apache_ssl_vhost_path}"
|
|
|
|
else
|
|
|
|
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}"
|
2017-09-21 00:39:06 +02:00
|
|
|
fi
|
2017-10-17 14:46:26 +02:00
|
|
|
}
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
sed_selfsigned_cert_path_for_nginx() {
|
|
|
|
local nginx_ssl_vhost_path="$1"
|
|
|
|
|
|
|
|
mkdir -p $(dirname "${nginx_ssl_vhost_path}")
|
|
|
|
if [ ! -f "${nginx_ssl_vhost_path}" ]; then
|
|
|
|
cat > "${nginx_ssl_vhost_path}" <<EOF
|
|
|
|
ssl_certificate ${SELF_SIGNED_FILE};
|
|
|
|
ssl_certificate_key ${SSL_KEY_FILE};
|
|
|
|
EOF
|
|
|
|
debug "SSL config added in ${nginx_ssl_vhost_path}"
|
2017-09-21 00:39:06 +02:00
|
|
|
else
|
2017-10-17 14:46:26 +02:00
|
|
|
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}"
|
2017-09-21 00:39:06 +02:00
|
|
|
fi
|
2017-10-17 14:46:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
openssl_selfsigned() {
|
|
|
|
local csr="$1"
|
|
|
|
local key="$2"
|
|
|
|
local crt="$3"
|
2018-07-10 17:46:41 +02:00
|
|
|
local cfg="$4"
|
2017-10-17 14:46:26 +02:00
|
|
|
local crt_dir=$(dirname ${crt})
|
|
|
|
|
|
|
|
[ -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"
|
2018-07-10 17:46:41 +02:00
|
|
|
if grep -q SAN "${cfg}"; then
|
|
|
|
"${OPENSSL_BIN}" x509 -req -sha256 -days 365 -in "${csr}" -extensions SAN -extfile "${cfg}" -signkey "${key}" -out "${crt}" 2> /dev/null
|
|
|
|
else
|
|
|
|
"${OPENSSL_BIN}" x509 -req -sha256 -days 365 -in "${csr}" -signkey "${key}" -out "${crt}" 2> /dev/null
|
|
|
|
fi
|
2017-10-18 00:44:04 +02:00
|
|
|
|
|
|
|
[ -r "${crt}" ] || error "Something went wrong, ${crt} has not been generated"
|
2017-10-17 14:46:26 +02:00
|
|
|
}
|
|
|
|
openssl_key(){
|
|
|
|
local key="$1"
|
|
|
|
local key_dir=$(dirname "${key}")
|
|
|
|
local size="$2"
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
[ -w "${key_dir}" ] || error "Directory ${key_dir} is not writable"
|
|
|
|
|
|
|
|
"${OPENSSL_BIN}" genrsa -out "${key}" "${size}" 2> /dev/null
|
|
|
|
|
2017-10-18 00:44:04 +02:00
|
|
|
[ -r "${key}" ] || error "Something went wrong, ${key} has not been generated"
|
2017-10-17 14:46:26 +02:00
|
|
|
}
|
2017-10-18 00:44:04 +02:00
|
|
|
openssl_csr() {
|
2017-10-17 14:46:26 +02:00
|
|
|
local csr="$1"
|
|
|
|
local csr_dir=$(dirname "${csr}")
|
|
|
|
local key="$2"
|
|
|
|
local cfg="$3"
|
|
|
|
|
|
|
|
[ -w "${csr_dir}" ] || error "Directory ${csr_dir} is not writable"
|
|
|
|
|
2017-10-18 00:44:04 +02:00
|
|
|
if $(grep -q "DNS:" "${cfg}"); then
|
|
|
|
# CSR with SAN
|
|
|
|
"${OPENSSL_BIN}" req -new -sha256 -key "${key}" -reqexts SAN -config "${cfg}" -out "${csr}"
|
|
|
|
else
|
|
|
|
# Single domain CSR
|
|
|
|
"${OPENSSL_BIN}" req -new -sha256 -key "${key}" -config "${cfg}" -out "${csr}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
[ -r "${csr}" ] || error "Something went wrong, ${csr} has not been generated"
|
2017-08-25 18:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
make_key() {
|
2017-10-17 14:46:26 +02:00
|
|
|
local key="$1"
|
|
|
|
local size="$2"
|
|
|
|
|
|
|
|
openssl_key "${key}" "${size}"
|
|
|
|
debug "Private key stored at ${key}"
|
|
|
|
|
|
|
|
chown root: "${key}"
|
|
|
|
chmod 600 "${key}"
|
2017-08-25 18:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
make_csr() {
|
2017-10-17 14:46:26 +02:00
|
|
|
local domains=$@
|
|
|
|
local nb=$#
|
|
|
|
local config_file="/tmp/make-csr-${VHOST}.conf"
|
2017-10-19 11:05:54 +02:00
|
|
|
local san=""
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-13 12:46:40 +02:00
|
|
|
mkdir -p -m 0755 "${CSR_DIR}" || error "Unable to mkdir ${CSR_DIR}"
|
2017-08-25 18:13:32 +02:00
|
|
|
|
2017-10-13 00:47:02 +02:00
|
|
|
if [ "${nb}" -eq 1 ]; then
|
2017-10-17 14:46:26 +02:00
|
|
|
cat "${SSL_CONFIG_FILE}" - > "${config_file}" <<EOF
|
2017-08-25 18:13:32 +02:00
|
|
|
CN=$domains
|
|
|
|
EOF
|
2017-10-13 00:47:02 +02:00
|
|
|
elif [ "${nb}" -gt 1 ]; then
|
2017-10-18 00:44:04 +02:00
|
|
|
for domain in ${domains}; do
|
2017-10-13 00:47:02 +02:00
|
|
|
san="${san},DNS:${domain}"
|
2017-09-21 00:39:06 +02:00
|
|
|
done
|
2017-10-18 00:44:04 +02:00
|
|
|
san=$(echo "${san}" | sed 's/^,//')
|
|
|
|
cat "${SSL_CONFIG_FILE}" - > "${config_file}" <<EOF
|
2018-07-12 11:12:29 +02:00
|
|
|
CN=${domains%% *}
|
2017-08-25 18:13:32 +02:00
|
|
|
[SAN]
|
2017-10-13 00:47:02 +02:00
|
|
|
subjectAltName=${san}
|
2017-08-25 18:13:32 +02:00
|
|
|
EOF
|
2017-09-21 00:39:06 +02:00
|
|
|
fi
|
2017-10-18 00:44:04 +02:00
|
|
|
openssl_csr "${CSR_FILE}" "${SSL_KEY_FILE}" "${config_file}"
|
2017-10-17 14:46:26 +02:00
|
|
|
debug "CSR stored at ${CSR_FILE}"
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
if [ -r "${CSR_FILE}" ]; then
|
2017-10-13 00:47:02 +02:00
|
|
|
chmod 644 "${CSR_FILE}"
|
|
|
|
mkdir -p -m 0755 "${SELF_SIGNED_DIR}"
|
2017-02-03 15:52:48 +01:00
|
|
|
|
2018-07-10 17:46:41 +02:00
|
|
|
openssl_selfsigned "${CSR_FILE}" "${SSL_KEY_FILE}" "${SELF_SIGNED_FILE}" "${config_file}"
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
[ -r "${SELF_SIGNED_FILE}" ] && chmod 644 "${SELF_SIGNED_FILE}"
|
|
|
|
debug "Self-signed certificate stored at ${SELF_SIGNED_FILE}"
|
2017-09-21 00:39:06 +02:00
|
|
|
fi
|
2017-08-25 18:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
main() {
|
2017-10-17 14:46:26 +02:00
|
|
|
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
|
2017-10-19 11:05:54 +02:00
|
|
|
VHOST="$1"
|
2017-10-17 14:46:26 +02:00
|
|
|
# remove the first argument
|
|
|
|
shift
|
|
|
|
# read domains from remaining arguments
|
2017-10-19 11:05:54 +02:00
|
|
|
DOMAINS=$@
|
2017-10-17 14:46:26 +02:00
|
|
|
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
|
2017-10-19 11:05:54 +02:00
|
|
|
VHOST="$1"
|
2017-10-17 14:46:26 +02:00
|
|
|
# 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)
|
2017-09-21 00:39:06 +02:00
|
|
|
fi
|
2017-10-19 11:05:54 +02:00
|
|
|
readonly VHOST
|
|
|
|
readonly DOMAINS
|
2017-08-25 18:13:32 +02:00
|
|
|
|
2017-10-19 11:08:35 +02:00
|
|
|
mkdir -p "${CSR_DIR}"
|
|
|
|
chown root: "${CSR_DIR}"
|
2017-10-13 12:46:40 +02:00
|
|
|
[ -w "${CSR_DIR}" ] || error "Directory ${CSR_DIR} is not writable"
|
2017-10-19 11:08:35 +02:00
|
|
|
|
|
|
|
mkdir -p "${SELF_SIGNED_DIR}"
|
|
|
|
chown root: "${SELF_SIGNED_DIR}"
|
2017-10-13 12:46:40 +02:00
|
|
|
[ -w "${SELF_SIGNED_DIR}" ] || error "Directory ${SELF_SIGNED_DIR} is not writable"
|
2017-10-19 11:08:35 +02:00
|
|
|
|
|
|
|
mkdir -p "${SSL_KEY_DIR}"
|
|
|
|
chown root: "${SSL_KEY_DIR}"
|
2017-10-13 12:46:40 +02:00
|
|
|
[ -w "${SSL_KEY_DIR}" ] || error "Directory ${SSL_KEY_DIR} is not writable"
|
2017-10-19 11:08:35 +02:00
|
|
|
|
2017-10-13 12:46:40 +02:00
|
|
|
[ -r "${SSL_CONFIG_FILE}" ] || error "File ${SSL_CONFIG_FILE} is not readable"
|
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
# check for important programs
|
|
|
|
readonly OPENSSL_BIN=$(command -v openssl) || error "openssl command not installed"
|
|
|
|
|
2017-10-19 11:05:54 +02:00
|
|
|
readonly SELF_SIGNED_FILE="${SELF_SIGNED_DIR}/${VHOST}.pem"
|
|
|
|
readonly SSL_KEY_FILE="${SSL_KEY_DIR}/${VHOST}.key"
|
|
|
|
readonly CSR_FILE="${CSR_DIR}/${VHOST}.csr"
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
make_key "${SSL_KEY_FILE}" "${SSL_KEY_SIZE}"
|
|
|
|
make_csr ${DOMAINS}
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
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"
|
2018-07-10 17:08:39 +02:00
|
|
|
exit 0
|
2017-10-17 14:46:26 +02:00
|
|
|
}
|
2017-08-25 18:13:32 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
readonly PROGNAME=$(basename "$0")
|
2017-10-18 00:42:15 +02:00
|
|
|
readonly PROGDIR=$(realpath -m $(dirname "$0"))
|
2017-10-17 14:46:26 +02:00
|
|
|
readonly ARGS=$@
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
readonly VERBOSE=${VERBOSE:-"0"}
|
2017-08-25 18:13:32 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
# Read configuration file, if it exists
|
|
|
|
[ -r /etc/default/evoacme ] && . /etc/default/evoacme
|
2017-10-13 00:47:02 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
# Default value for main variables
|
2017-10-18 00:44:04 +02:00
|
|
|
readonly CSR_DIR=${CSR_DIR:-'/etc/ssl/requests'}
|
|
|
|
readonly SSL_CONFIG_FILE=${SSL_CONFIG_FILE:-"/etc/letsencrypt/openssl.cnf"}
|
|
|
|
readonly SELF_SIGNED_DIR=${SELF_SIGNED_DIR:-'/etc/ssl/self-signed'}
|
|
|
|
readonly SSL_KEY_DIR=${SSL_KEY_DIR:-'/etc/ssl/private'}
|
|
|
|
readonly SSL_KEY_SIZE=${SSL_KEY_SIZE:-$(default_key_size)}
|
2017-08-25 18:13:32 +02:00
|
|
|
|
2017-10-17 14:46:26 +02:00
|
|
|
main ${ARGS}
|