blob: 36abbca5ee0371397438ccf700ebf70ce12eb06f (
plain) (
tree)
|
|
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
#############
# This script creates a key and csr, and uses acme-tiny to have the csr signed
# with lets encrypt.
# by Dennis Eriksen, dennis@eriksen.im, 2015-12-16
#############
################################################################################
# TODO
################################################################################
# Handling of getopts. If you trigger -b after -r, -b doesn't count. Also, you
# don't want to be able to trigger -s and -r, etc.
# There should probably be a file indicating which domains each cert is valid
# for.. Like $domain/SAN, with an entry for each domain.
################################################################################
# Config
################################################################################
readonly CERTDIR="/etc/letsencrypt/certs"
readonly CERTDEST="/etc/ssl/letsencrypt"
readonly OPENSSLCONF="/etc/letsencrypt/openssl.cnf"
readonly ACCOUNTKEY="/etc/letsencrypt/account.key"
readonly -a ARGS=("$@")
readonly LETSCAUSER="letsencrypt"
readonly LETSCAGROUP="letsencrypt"
readonly CERTKEYOWNER="root"
readonly CERTGROUP="ssl-cert"
readonly INTERMEDIATE="/etc/ssl/lets-encrypt-x3-cross-signed.pem"
readonly ACMETINY="/usr/local/sbin/acme_tiny.py"
readonly CHALLENGEDIR="/var/www/letsencrypt-challenges"
readonly NGINXCONFSUFFIX=""
BATCHMODE=FALSE
QUIET=FALSE
################################################################################
# Usage
################################################################################
usage() {
echo "Yes. This is Usage. Hello."
}
################################################################################
# say
################################################################################
echo() {
if [[ ! "$QUIET" == TRUE ]]; then
builtin echo $1
fi
}
################################################################################
# cmdline
################################################################################
cmdline() {
local bflag=FALSE
local dflag=FALSE
local qflag=FALSE
local rflag=FALSE
local sflag=FALSE
while getopts "xqhbr:s:d:" OPTION
do
case ${OPTION} in
b)
bflag=TRUE
BATCHMODE=TRUE
;;
h)
usage
exit 0
;;
x)
readonly DEBUG='-x'
echo "-x was triggered"
echo "\${ARGS{[@]} = ${ARGS[@]}"
set -x
;;
s)
sflag=TRUE
sign ${OPTARG}
;;
d)
dflag=TRUE
deploy ${OPTARG}
;;
q)
QUIET=TRUE
;;
r)
rflag=TRUE
req ${OPTARG}
;;
\?)
usage && exit 1
;;
:)
usage && exit 1
;;
esac
done
shift $((OPTIND-1))
if [[ "$bflag" == TRUE ]] && [[ ! "$rflag" == TRUE ]]; then
echo "-b is only used with -r."
echo ""
usage && exit 1
fi
return 0
}
################################################################################
# deploy
################################################################################
deploy() {
local domain=""
# Domains are provided separated by spaces.
IFS=' ' read -r -a domain <<< $1
if [[ ! ${#domain[@]} == 1 ]]; then
echo "-d only takes one domain."
exit 1
elif [[ ! -d "$CERTDIR/${domain[0]}" ]]; then
echo "\"${domain[0]}\" does not exist in $CERTDIR"
exit 1
elif [[ ! -f "$CERTDIR/${domain[0]}/${domain[0]}.key" ]]; then
echo "${domain[0]}.key seems to be missing."
exit 1
elif [[ ! -f "$CERTDIR/${domain[0]}/${domain[0]}.crt" ]]; then
echo "${domain[0]}.crt seems to be missing."
echo "Are you sure you've signed the CSR?"
exit 1
fi
if [[ ! -d "$CERTDEST" ]]; then
mkdir -p $CERTDEST
chgrp $CERTGROUP $CERTDEST
fi
# Lets's copy!
echo "Copying ${domain[0]}.crt and ${domain[0]}.key to $CERTDEST"
cp --preserve=all $CERTDIR/${domain[0]}/${domain[0]}.{crt,key} $CERTDEST
# If the certificate is meant for nginx, we want to combine the cert and the
# intermediary certificate.
if [[ -f "/etc/nginx/sites-enabled/${domain[0]}$NGINXCONFSUFFIX" ]] || \
[[ -f "$CERTDEST/${domain[0]}.pem" ]]; then
echo "Detected nginx-config for ${domain[0]}, or existing pem-file."
echo "Moving crt to pem, and appending intermediate certificate."
mv $CERTDEST/${domain[0]}.crt $CERTDEST/${domain[0]}.pem
cat $INTERMEDIATE >> $CERTDEST/${domain[0]}.pem
fi
echo ""
echo "Deployment finished. You should probably reload your configuration."
exit 0
}
################################################################################
# req
################################################################################
req() {
local domain=""
local i=0
local batch=""
# Domains are provided separated by spaces.
IFS=' ' read -r -a domain <<< $1
# Is batchmode triggered?
[[ "$BATCHMODE" == TRUE ]] && batch="-batch"
if [[ -z "$CERTDIR" ]] || [[ -z "${domain[0]}" ]]; then
echo "\$CERTDIR and/or \${domain[0]} is empty. Something is wrong."
exit 1
fi
if [[ ! -d $CERTDIR ]]; then
mkdir -p $CERTDIR
chmod 750 $CERTDIR
chown $LETSCAUSER:$CERTGROUP $CERTDIR
fi
if [[ -d "$CERTDIR/${domain[0]}" ]]; then
echo "The directory $CERTDIR/${domain[0]} already exists."
echo "If you continue, it will be deleted."
echo ""
read -p "Press Y to continue, or any other button to abort." -n 1 -r
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
echo "Okay then. Live long and prosper."
exit 1
else
echo ""
rm -r "$CERTDIR/${domain[0]}" && mkdir -p "$CERTDIR/${domain[0]}"
fi
else
mkdir -p "$CERTDIR/${domain[0]}"
fi
# if there is only one domain
if [[ ${#domain[@]} == 1 ]]; then
echo "Only one domain - ${domain[0]}"
openssl req -new -sha256 -nodes -out $CERTDIR/${domain[0]}/${domain[0]}.csr\
-keyout $CERTDIR/${domain[0]}/${domain[0]}.key -config <(cat $OPENSSLCONF\
| sed -r "s/REPLACE/${domain[0]}/") $batch
# if there is more than one domain
else
echo "Several domains: "
SANtext="[SAN]\nsubjectAltName="
for SAN in "${domain[@]}"; do
# Echo both to stdout and to SAN-file in cert-dir.
echo "$SAN" | tee -a "$CERTDIR/${domain[0]}/SAN"
if [[ ! "$SAN" == "${domain[0]}" ]]; then
((i+=1)) && [[ $i > 1 ]] && SANtext+=","
SANtext+="DNS:$SAN"
fi
done
echo "Common Name will be ${domain[0]}"
echo $SANtext
openssl req -new -sha256 -nodes -out $CERTDIR/${domain[0]}/${domain[0]}.csr\
-keyout $CERTDIR/${domain[0]}/${domain[0]}.key -reqexts SAN -config <(cat\
$OPENSSLCONF <(printf "$SANtext") | sed -r "s/REPLACE/${domain[0]}/") \
$batch
fi
chown -R $LETSCAUSER:$CERTGROUP $CERTDIR/${domain[0]}
chmod 750 $CERTDIR/${domain[0]}
chown $CERTKEYOWNER:$CERTGROUP $CERTDIR/${domain[0]}/${domain[0]}.key
chmod 440 $CERTDIR/${domain[0]}/${domain[0]}.{csr,key}
exit 0
}
################################################################################
# sign
################################################################################
sign() {
local domain=""
# Domains are provided separated by spaces.
IFS=' ' read -r -a domain <<< $1
if [[ ! ${#domain[@]} == 1 ]]; then
echo "-s only takes one domain."
exit 1
elif [[ ! -d "$CERTDIR/${domain[0]}" ]]; then
echo "\"${domain[0]}\" does not exist in $CERTDIR"
exit 1
elif [[ ! -f "$CERTDIR/${domain[0]}/${domain[0]}.key" ]] || \
[[ ! -f "$CERTDIR/${domain[0]}/${domain[0]}.csr" ]]; then
echo "The CSR or KEY for ${domain[0]} seems to be missing."
echo "Are you sure you've created the request?"
exit 1
fi
if [[ ! "$QUIET" == TRUE ]]; then
echo "Have you remembered to point the address"
echo ""
echo "${domain[0]}/.well-known/acme-challenge/"
echo ""
echo "to your challenges-directory? - $CHALLENGEDIR"
echo "See https://github.com/diafygi/acme-tiny#step-3-make-your-website-host"\
"-challenge-files for more info."
echo ""
echo "If the directory isn't available at the above address, "\
"the signing will fail."
echo ""
read -p "Press Y to continue, or any other button to abort." -n 1 -r
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
echo "Okay then. Live long and prosper."
exit 1
fi
fi
sudo -u $LETSCAUSER -H python $ACMETINY --account-key $ACCOUNTKEY \
--csr $CERTDIR/${domain[0]}/${domain[0]}.csr --acme-dir $CHALLENGEDIR \
> $CERTDIR/${domain[0]}/${domain[0]}.crt
chown $LETSCAUSER:$CERTGROUP $CERTDIR/${domain[0]}/${domain[0]}.crt
exit 0
}
################################################################################
# main
################################################################################
main() {
cmdline ${ARGS[@]}
}
main
exit 0
|