
Currently we connect to the LE staging environment with acme.sh during CI to get the DNS-01 tokens (but we never follow-through and actually generate the certificate, as we have nowhere to publish the tokens). We've known for a while that LE staging isn't really meant to be used by CI like this, and recent instability has made the issue pronounced. This modifies the driver script to generate fake tokens which work to ensure all the DNS processing, etc. is happening correctly. I have put this behind a flag so the letsencrypt job still does this however. I think it is worth this job actually calling acme.sh to validate this path; this shouldn't be required too often. Change-Id: I7c0b471a0661aa311aaa861fd2a0d47b07e45a72
128 lines
5.0 KiB
Bash
128 lines
5.0 KiB
Bash
#!/bin/bash
|
|
|
|
ACME_SH=${ACME_SH:-/opt/acme.sh/acme.sh}
|
|
CERT_HOME=${CERT_HOME:-/etc/letsencrypt-certs}
|
|
CHALLENGE_ALIAS_DOMAIN=${CHALLENGE_ALIAS_DOMAIN:-acme.opendev.org.}
|
|
# Set to !0 to use letsencrypt staging rather than production requests
|
|
LETSENCRYPT_STAGING=${LETSENCRYPT_STAGING:-0}
|
|
LOG_FILE=${LOG_FILE:-/var/log/acme.sh/acme.sh.log}
|
|
|
|
SERVER=""
|
|
if [[ ${LETSENCRYPT_STAGING} != 0 ]]; then
|
|
# TODO acme.sh doesn't let us specify staging and also set the server.
|
|
# If --staging is passed then the built in default is used. Can/should
|
|
# we change this to --server letsencrypt_test?
|
|
SERVER="--staging"
|
|
#SERVER="--server letsencrypt_test"
|
|
else
|
|
SERVER="--server letsencrypt"
|
|
fi
|
|
|
|
# Ensure we don't write out files as world-readable
|
|
umask 027
|
|
|
|
echo -e "\n--- start --- ${1} --- $(date -u '+%Y-%m-%dT%k:%M:%S%z') ---" >> ${LOG_FILE}
|
|
|
|
if [[ ${1} == "issue" ]]; then
|
|
# Take output like:
|
|
# [Thu Feb 14 13:44:37 AEDT 2019] Domain: '_acme-challenge.test.opendev.org'
|
|
# [Thu Feb 14 13:44:37 AEDT 2019] TXT value: 'QjkChGcuqD7rl0jN8FNWkWNAISX1Zry_vE-9RxWF2pE'
|
|
#
|
|
# and turn it into:
|
|
#
|
|
# _acme-challenge.test.opendev.org:QjkChGcuqD7rl0jN8FNWkWNAISX1Zry_vE-9RxWF2pE
|
|
#
|
|
# Ansible then parses this back to a dict.
|
|
shift;
|
|
for arg in "$@"; do
|
|
$ACME_SH ${SERVER} \
|
|
--cert-home ${CERT_HOME} \
|
|
--no-color \
|
|
--yes-I-know-dns-manual-mode-enough-go-ahead-please \
|
|
--issue \
|
|
--dns \
|
|
--challenge-alias ${CHALLENGE_ALIAS_DOMAIN} \
|
|
$arg 2>&1 | tee -a ${LOG_FILE} | \
|
|
egrep 'Domain:|TXT value:' | cut -d"'" -f2 | paste -d':' - -
|
|
# shell magic ^ is
|
|
# - extract everything between ' '
|
|
# - stick every two lines together, separated by a :
|
|
done
|
|
elif [[ ${1} == "issue-selfsign" ]]; then
|
|
shift;
|
|
for arg in "$@"; do
|
|
# looks like
|
|
# "-d foo01.com -d foo.com " "-d bar01.com -d bar.com"
|
|
arr=(${arg})
|
|
len=${#arr[@]}
|
|
for (( i=0; i<$len; i++ )); do
|
|
if [[ $((i%2)) -eq 0 ]]; then
|
|
continue # this should be a "-d"
|
|
else
|
|
# The ACME protocol hashes "stuff" and the TXT record
|
|
# is ultimately a sha256 encoded into base64url
|
|
# (RFC8555); emulate that here.
|
|
base64url=$(echo -n ${arr[$i]}-${RANDOM} | \
|
|
openssl dgst -binary -sha256 | \
|
|
openssl base64 | sed 's/+/-/g; s,/,_,g; s/=//g')
|
|
echo "${arr[$i]}:${base64url}"
|
|
fi
|
|
done
|
|
done
|
|
elif [[ ${1} == "renew" ]]; then
|
|
shift;
|
|
for arg in "$@"; do
|
|
# NOTE(ianw) 2020-02-28 : we only set force here because of a
|
|
# bug/misfeature in acme.sh dns manual-mode where it does not
|
|
# notice that the renewal is required when we update domain
|
|
# names in a cert
|
|
# (https://github.com/acmesh-official/acme.sh/issues/2763).
|
|
# This is safe (i.e. will not explode our quota limits by
|
|
# constantly renewing) because Ansible only calls this path
|
|
# when TXT records have been installed for this certificate;
|
|
# i.e. we will never run this renewal unless it is actually
|
|
# required.
|
|
$ACME_SH ${SERVER} \
|
|
--cert-home ${CERT_HOME} \
|
|
--no-color \
|
|
--yes-I-know-dns-manual-mode-enough-go-ahead-please \
|
|
--force \
|
|
--renew \
|
|
$arg 2>&1 | tee -a ${LOG_FILE}
|
|
done
|
|
elif [[ ${1} == "selfsign" ]]; then
|
|
# For testing, simulate the key generation
|
|
shift;
|
|
for arg in "$@"; do
|
|
# TODO(ianw): Set SAN names from the other "-d" arguments?;
|
|
# it's a pita to parse.
|
|
{
|
|
read -r -a domain_array <<< "$arg"
|
|
domain=${domain_array[1]}
|
|
mkdir -p ${CERT_HOME}/${domain}
|
|
cd ${CERT_HOME}/${domain}
|
|
echo "Creating certs in ${CERT_HOME}/${domain}"
|
|
# Create key for domain
|
|
openssl genrsa -out ${domain}.key 2048
|
|
# openssl makes this 0600; match the permissions acme.sh
|
|
# makes it with for general sanity
|
|
chmod 0640 ${domain}.key
|
|
# Generate a fake CA key
|
|
openssl genrsa -out ca.key 2048
|
|
# Create fake CA root certificate
|
|
openssl req -x509 -new -nodes -key ca.key -sha256 -days 1024 -subj "/C=US/ST=CA/O=opendev" -out ca.cer
|
|
# Create localhost certificate signing request
|
|
openssl req -sha256 -new -key ${domain}.key -out ${domain}.csr -subj '/CN=localhost'
|
|
# Create localhost certificate signed by fake CA
|
|
openssl x509 -req -CA ca.cer -CAkey ca.key -CAcreateserial \
|
|
-sha256 -days 365 -in ${domain}.csr -out ${domain}.cer
|
|
cp ${domain}.cer fullchain.cer
|
|
} 2>&1 | tee -a ${LOG_FILE}
|
|
done
|
|
else
|
|
echo "Unknown driver arg: $1"
|
|
exit 1
|
|
fi
|
|
|
|
echo "--- end --- $(date -u '+%Y-%m-%dT%k:%M:%S%z') ---" >> ${LOG_FILE}
|