Created
August 6, 2019 20:34
-
-
Save Syquel/41e072a7f91abd0f6e08eafc67b65fa0 to your computer and use it in GitHub Desktop.
A script to setup ECC / ECDSA certificates with Certbot including automatic renewal support.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -euf -o pipefail | |
## Prerequisites | |
# This script assumes lexicon-dns to be installed and configured for your specific DNS provider | |
# for the Let's Encrypt DNS-challenge. | |
# Further it is assumed that the lexicon-dns certbot-hook is installed under ${CONF_BASE_PATH}/renewal-hooks/manual/lexicon.sh . | |
# The lexicon-dns hook for certbot can be downloaded from https://github.com/AnalogJ/lexicon/blob/master/examples/certbot.default.sh . | |
# Make sure to adjust the variables in the section "Configuration" according to your installation. | |
## Configuration | |
ACCOUNT_HASH="CHANGE_ME" # The user-specific Let's Encrypt account hash. | |
TMP_BASE_PATH="/tmp/certbot-ecdsa" # The base path to be used for temporary files created by this script. | |
CONF_BASE_PATH="/etc/letsencrypt" # The configuration base path of the certbot installation. | |
## Helper function declarations | |
function join_by { local IFS="$1"; shift; echo "$*"; } | |
# Script start | |
if [ "$#" -lt 1 ]; then | |
echo "Domains parameter missing!" | |
echo "Example usage: $0 \"mydomain.example\" \"*.mydomain.example\"" | |
exit 1 | |
fi | |
if [ ! -d "${CONF_BASE_PATH}/accounts/acme-v02.api.letsencrypt.org/directory/${ACCOUNT_HASH}" ]; then | |
echo "The ACCOUNT_HASH is not correctly configured." | |
exit 4 | |
fi | |
DOMAINS=(${@}) | |
PRIMARY_DOMAIN="${DOMAINS[0]}" | |
TMP_PATH="${TMP_BASE_PATH}/$PRIMARY_DOMAIN" | |
CONF_ARCHIVE_PATH="${CONF_BASE_PATH}/archive/${PRIMARY_DOMAIN}-ecdsa" | |
CONF_LIVE_PATH="${CONF_BASE_PATH}/live/${PRIMARY_DOMAIN}-ecdsa" | |
CONF_RENEW_PATH="${CONF_BASE_PATH}/renewal/${PRIMARY_DOMAIN}-ecdsa.conf" | |
## Directory structure setup | |
if [ -d "${CONF_LIVE_PATH}" ]; then | |
echo "Live Path already exists!"; | |
exit 2 | |
fi | |
if [ -d "${CONF_ARCHIVE_PATH}" ]; then | |
echo "Archive Path already exists!"; | |
exit 3 | |
fi | |
if [ -d "${TMP_PATH}" ]; then | |
rm -r "${TMP_PATH}" | |
fi | |
mkdir "${CONF_LIVE_PATH}" | |
mkdir "${CONF_ARCHIVE_PATH}" | |
mkdir -p "${TMP_PATH}" | |
## ECC private key and CSR generation | |
SUBJECT_ALT=$(join_by , "${DOMAINS[@]/#/DNS:}") | |
openssl ecparam -genkey -name secp384r1 -out "${CONF_ARCHIVE_PATH}/privkey1.pem" | |
openssl req -new -key "${CONF_ARCHIVE_PATH}/privkey1.pem" -out "${TMP_PATH}/cert.csr" -sha256 \ | |
-subj "/CN=${PRIMARY_DOMAIN}" -reqexts reqext -extensions reqext \ | |
-config <(printf "[ req ]\nprompt=no\nencrypt_key = no\ndistinguished_name = dname\n[ reqext ]\nsubjectAltName=${SUBJECT_ALT}\n[ dname ]\nCN = ${PRIMARY_DOMAIN}\nemailAddress = admin@${PRIMARY_DOMAIN}") | |
## Let's Encrypt certificate request | |
certbot certonly -n --csr "${TMP_PATH}/cert.csr" --manual --preferred-challenges dns \ | |
--manual-auth-hook "${CONF_BASE_PATH}/renewal-hooks/manual/lexicon.sh auth" \ | |
--manual-cleanup-hook "${CONF_BASE_PATH}/renewal-hooks/manual/lexicon.sh cleanup" \ | |
--manual-public-ip-logging-ok \ | |
--cert-path "${CONF_ARCHIVE_PATH}/cert1.pem" --chain-path "${CONF_ARCHIVE_PATH}/chain1.pem" \ | |
--fullchain-path="${CONF_ARCHIVE_PATH}/fullchain1.pem" ${DOMAINS[@]/#/-d } | |
## Local certificate enablement | |
ln -s "${CONF_ARCHIVE_PATH}/privkey1.pem" "${CONF_LIVE_PATH}/privkey.pem" | |
ln -s "${CONF_ARCHIVE_PATH}/cert1.pem" "${CONF_LIVE_PATH}/cert.pem" | |
ln -s "${CONF_ARCHIVE_PATH}/chain1.pem" "${CONF_LIVE_PATH}/chain.pem" | |
ln -s "${CONF_ARCHIVE_PATH}/fullchain1.pem" "${CONF_LIVE_PATH}/fullchain.pem" | |
## Cleanup | |
rm -r "${TMP_PATH}" | |
# Automatic renewal enablement | |
cat <<EOT >> ${CONF_RENEW_PATH} | |
# renew_before_expiry = 30 days | |
cert = ${CONF_LIVE_PATH}/cert.pem | |
privkey = ${CONF_LIVE_PATH}/privkey.pem | |
chain = ${CONF_LIVE_PATH}/chain.pem | |
fullchain = ${CONF_LIVE_PATH}/fullchain.pem | |
version = 0.31.0 | |
archive_dir = ${CONF_ARCHIVE_PATH} | |
# Options and defaults used in the renewal process | |
[renewalparams] | |
account = ${ACCOUNT_HASH} | |
reuse_key = True | |
server = https://acme-v02.api.letsencrypt.org/directory | |
pref_challs = dns-01, | |
authenticator = manual | |
manual_auth_hook = ${CONF_BASE_PATH}/renewal-hooks/manual/lexicon.sh auth | |
manual_cleanup_hook = ${CONF_BASE_PATH}/renewal-hooks/manual/lexicon.sh cleanup | |
manual_public_ip_logging_ok = True | |
EOT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment