Skip to content

Instantly share code, notes, and snippets.

@ThinGuy
Last active October 5, 2020 05:25
Show Gist options
  • Save ThinGuy/44b5cc260ebf4aa539d2b53393c32e3a to your computer and use it in GitHub Desktop.
Save ThinGuy/44b5cc260ebf4aa539d2b53393c32e3a to your computer and use it in GitHub Desktop.
Self Signed Cert Script - multi-host, multi-ip, multi-domain, and wilcard certs
#!/usr/bin/env bash
# vim: set et ts=2 sw=2 filetype=bash :
#
# Copyright (C) 2020 Craig Bender
#
# Author(s): Craig Bender <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# If you did not receive a copy of the GNU General Public License
# along with this program, see <http://www.gnu.org/licenses/>.
#
## Updated 4 Oct 2020:
# - Change file name in help/examples to reflect actual filename
# - Start of work to include Online Certificate Status Protocol (OCSP) capabilities
# - Start of work to include Certificate Revocation Lists (CRLs) capabilities
## Updated 17 Sep 2020:
# - Create a wildcard cert that does not have an * in the CN, rather
# put all wildcard entries in the SAN list
# - Add new option to manually set the host cert's CN ( --subject-name )
# for more control over wildcard certs
# - Remove mandatory sudo rights requirement
# only -a, --auto-add requires running with superuser access
## Updated 29 Jan 2020:
# - Fix missing getopt args for custom issuers/subjects
## Updated 29 Dec 2019:
# - Added extendedKeyUsage=serverAuth,clientAuth directives to v3_req and usr_cert sections
# - Added keyUsage = nonRepudiation, digitalSignature, keyEncipherment to usr_cert section
# - Added -a,--auto-add option to copy files to common locations (e.g. /usr/local/share/ca-certificates/, /etc/ssl/certs/, and /etc/ssl/private/)
# - Added -P,--prefix option to give created files a given prefix rather than the default of CN
# - Create README with useful information about the certs created
# - Create sample cloud-init yaml files for use with MAAS, Juju, and LXD
## Updated 27 Dec 2019:
# - Fixed missing default ISSUER_* and SUBJ_* parameters which caused
# the following SSL error: "SSL: couldn't get X509-issuer name!"
## Updated 3 Dec 2019:
# - Added -l,--localhost option so localhost, 127.0.0.1, and 127.0.1.1 are willfully added
# - Added -p,--private option for private top-level-domain namespaces (.corp .home .internal .intranet .lan .private .test)
# - Fixed logic for conflicting options
# - Fixed wildcard domain handling
# - Fixed single-label domain name handling
# - Added more information while script is processing.
## Updated 10 Oct 2019:
# - Root CA/Key now kept so it can be used for future csr/cert/key signing
# - Clean up of wildcard domains
# - Added localhost addresses (127.0.0.1 127.0.1.1) to DNS based SANs (was only in IP SANs before)
# - Options for for custom issuers/subjects
# - Cleaned up usage to (mostly) fit in 80 char terminal
export PROG=${0##*/} PROG_PATH="$(cd $(dirname ${0}) && pwd)/$(echo -n ${0##*/})"
DESC="\n\e[1m${0##*/}\e[0m - Create CA certs, host certs, and RSA keys for\n\e[23GMulti-Hostname and Multi-Domain self-signed SSL\n\e[23Gcertificates, including wildcard certs\n"
[[ $1 = '--desc' ]] && { printf "${DESC}";return; }
Usage() {
printf -- "${DESC}\n"
printf -- "Usage: ${PROG_PATH} -s <FQDN> [ OPTIONS ]\n\e[0m"
printf -- "\nOptions:\n\n\e[0m"
printf -- " -s --site \e[21GPrimary FQDN that this ssl cert should be valid for\n\n"
printf -- " -n, --names \e[21GSpace or comma separated list of all FQDNs that this\n\e[21Gssl cert should be valid for (enclose space eparated\n\e[21Gslist in quotes)\n\n"
printf -- " -i, --ips \e[21GSpace or comma separated list of IP addresses to\n\e[21Ginclude as a Subject Alternative Name (SAN) (enclose space\n\e[21Gseparated list in quotes)\n\n"
printf -- " -d, --domains \e[21GSpace or comma separated list of domain names to\n\e[21Ginclude as a wildcard entry in Subject Alternative\n\e[21GName (SAN) (enclose space separate list in quotes)\n\n"
printf -- " -l, --local \e[21GInclude DNS and IP entries for localhost, 127.0.0,1, and\n\e[21G127.0.1.1\n\n"
printf -- " -w, --wildcard \e[21GCreate wildcard entries for the domains of the\n\e[21GFQDNs provided with the -n,--names option\n\n"
printf -- " -p, --private \e[21GInclude Private DNS Namespaces for top-level domains\n\e[21G(.corp .home .internal .intranet .lan .private .test)\n\e[21GSee: https://tools.ietf.org/html/rfc6762#appendix-G\n\n"
printf -- " -P, --prefix \e[21GPrefix to give to certs,keys, etc.\n\e[21GDefault: site name given with -s,--site option\n\n"
printf -- " \e[0;38;2;255;200;0m-a\e[0m, \e[0;38;2;255;200;0m--auto-add\e[0m\e[21GAutomatically copy CA Cert to local CA store, host cert\n\e[21Gto /etc/ssl/certs/, and host key to /etc/ssl/private/\n\e[21G\e[1m*** \e[0;38;2;255;200;0mThis option requires root, run ${PROG} via sudo. \e[0;1m***\e[0m\n\n"
printf -- " --nohosts \e[21GOnly include wildcard entries in Subject Alternative Name\n\e[21G(SAN), i.e. each SAN entry would be *.example.org vs host.example.org\n\n"
printf -- " --san-only \e[21GDo not wildcard the host cert CN name\n\e[21Gi.e. CN for the host cert will be exactly as entered with the -s or --subject-name options instead *.example.org\n\n"
printf -- " --issuer-country \e[21GIssuer (CA) Country Name (2 Letter Code) \n\e[21GDefault: GB\n\n"
printf -- " --issuer-state \e[21GIssuer (CA) State or Province Name (Full Name) \n\e[21GDefault: England\n\n"
printf -- " --issuer-locality \e[21GIssuer (CA) Locality Name (i.e. City)\n\e[21GDefault: London\n\n"
printf -- " --issuer-org \e[21GIssuer (CA) Organization Name (i.e. Company) \n\e[21GDefault: Canonical Ltd.\n\n"
printf -- " --issuer-unit \e[21GIssuer (CA) Organizational Unit (i.e. Section)\n\e[21GDefault: Data Center Field Engineering\n\n"
printf -- " --issuer-name \e[21GIssuer (CA) Common Name (Your name or FQDN)\n\e[21GDefault: Canonical AIR stack\n\n"
printf -- " --subject-country \e[21GSubject (Host) Country Name (2 Letter Code) \n\e[21GDefault: GB\n\n"
printf -- " --subject-state \e[21GSubject (Host) State or Province Name (Full Name) \n\e[21GDefault: England\n\n"
printf -- " --subject-locality \e[21GSubject (Host) Locality Name (i.e. City)\n\e[21GDefault: London\n\n"
printf -- " --subject-org \e[21GSubject (Host) Organization Name (i.e. Company) \n\e[21GDefault: Canonical Ltd.\n\n"
printf -- " --subject-unit \e[21GSubject (Host) Organizational Unit (i.e. Section)\n\e[21GDefault: Data Center Field Engineering\n\n"
printf -- " --subject-name \e[21GSubject (Host) Common Name (Your name or FQDN)\n\e[21GDefault: FQDN provided with -s,--site arg\n\e[21Gor \*.DN of -s,--site arg\n\n\e[21G\e[1mNote\e[0m: If -s,--site is null, and no alternate\n\e[21Gnames are provided (-n, -i, -d args), the\n\e[21G--subject-name argument must start with an '*'\n"
printf -- "\nEx:\n\n"
printf -- "${PROG_PATH} \\ \n-w \\ \n-s image-server.orangebox.me \\ \n-n maas-images.orangebox.me,juju-images.orangebox.me,lxd-images.orangebox.me \\ \n-d maas.io,images.linuxcontainers.org,linuxcontainers.org \\ \n-i 172.27.20.1,172.27.20.2,172.27.20.3,172.27.20.4,172.27.20.5,172.27.20.6\n\n"
printf -- "\nNotes:\n\n"
printf -- " - Be careful of subdomains with wildcard enabled certs:\n\n\e[6GFQDN:\e[14Gserver.example.com\n\e[6GHOST:\e[14Gserver\n\e[6GDOMAIN:\e[14Gexample.com\n\n\e[6GFQDN:\e[14Gus.server.example.com\n\e[6GHOST:\e[14Gus\n\e[6GDOMAIN:\e[14Gserver.example.com\n\n\e[3m\e[6GTo ensure wildcards cover both, add example.com AND server.example.com\n\e[6Gto the -d,--domain option\e[0m\n\n"
printf -- " - How to use self-signed certs, testing tips, and configuration examples\n\e[4Gprovided upon completion\n\n"
printf -- " - Log file will be located in \${HOME}/ssl/<domain name of site>/<site>.log \n\n\e[0m"
printf -- " - The --nohosts option assumes -w,--wildcard \n\n\e[0m"
printf -- " - You can use -d,--domain with -w.--wildcard, however only unique entries will\n\e[4Gbe listed in the SAN field\n\n\e[0m"
}
tstatus() {
RETCODE=$(echo $?)
[[ $RETCODE -eq 0 ]] && printf '\e['$(($(tput cols)-10))'G [ \e[32mOK\e[0m ]\n'
[[ $RETCODE -eq 1 ]] && printf '\e['$(($(tput cols)-10))'G [\e[31mFAIL\e[0m]\n'
return $RETCODE
}
export PRIVATE=false
export WILDCARD=false
export SAN_ONLY_WC=false
export NOHOSTS=false
export LOCALHOST=false
export AUTOADD=false
unset ALT_NAMES ALT_DNS ALT_IPS ALT_DOMS WC_DOMS PREFIX README
unset ISSUER_C ISSUER_ST ISSUER_L ISSUER_O ISSUER_OU ISSUER_CN SUBJ_C SUBJ_ST SUBJ_L SUBJ_O SUBJ_OU
ARGS=`getopt -o s:n:i:d:P:awlph --long issuer-country:,issuer-state:,issuer-locality:,issuer-org:,issuer-unit:,issuer-name:,subject-country:,subject-state:,subject-locality:,subject-org:,subject-unit:,subject-name:,site:,names:,ips:,domains:,prefix:,auto-add,wildcard,localhost,private,nohosts,san-only,help -- "$@"`
eval set -- "$ARGS"
while true ; do
case "$1" in
-s|--site) export SITE="${2,,}";shift 2;;
-n|--names) declare -ag ALT_DNS=($(printf "%s\n" "${2//,/ }"));shift 2;;
-i|--ips) declare -ag ALT_IPS=($(printf "%s\n" "${2//,/ }"));shift 2;;
-d|--domains) declare -ag ALT_DOMS=($(printf "%s\n" "${2//,/ }"));shift 2;;
-l|--localhost) export LOCALHOST=true;shift 1;;
-p|--private) export PRIVATE=true;shift 1;;
-w|--wildcard) export WILDCARD=true;shift 1;;
-P|--prefix) export PREFIX="${2,,}";shift 2;;
-a|--auto-add) export AUTOADD=true;shift 1;;
--nohosts) export NOHOSTS=true;export WILDCARD=true;shift 1;;
--san-only) export SAN_ONLY_WC=true;shift 1;;
--issuer-country) export ISSUER_C="${2}";shift 2;;
--issuer-state) export ISSUER_ST="${2}";shift 2;;
--issuer-locality) export ISSUER_L="${2}";shift 2;;
--issuer-org) export ISSUER_O="${2}";shift 2;;
--issuer-unit) export ISSUER_OU="${2}";shift 2;;
--issuer-name) export ISSUER_CN="${2}";shift 2;;
--subject-country) export SUBJ_C="${2}";shift 2;;
--subject-state) export SUBJ_ST="${2}";shift 2;;
--subject-locality) export SUBJ_L="${2}";shift 2;;
--subject-org) export SUBJ_O="${2}";shift 2;;
--subject-unit) export SUBJ_OU="${2}";shift 2;;
--subject-name) export SUBJ_CN="${2}";shift 2;;
-h|--help) Usage;exit 0;;
*/?) Usage;exit 0;;
--) shift;break;;
esac
done
# Root Check
#[[ $EUID -ne 0 ]] && { printf 'This script requires super-user access. Please run via sudo\n\n';exit 1; }
# Validate that a site was given
[[ -z ${SITE} && -z ${SUBJ_CN} ]] && { printf 'Please provide primary site name with the -s,--site or --subject-name args\n\n';exit 1; }
# Set Default Subject/Issuer Fields if not provided
[[ -n ${ISSUER_C} ]] || export ISSUER_C='GB'
[[ -n ${ISSUER_ST} ]] || export ISSUER_ST='England'
[[ -n ${ISSUER_L} ]] || export ISSUER_L='London'
[[ -n ${ISSUER_O} ]] || export ISSUER_O='Canonical Ltd.'
[[ -n ${ISSUER_OU} ]] || export ISSUER_OU='Data Center Field Engineering'
[[ -n ${ISSUER_CN} ]] || export ISSUER_CN='Canonical AIR stack'
[[ -n ${SUBJ_C} ]] || export SUBJ_C='GB'
[[ -n ${SUBJ_ST} ]] || export SUBJ_ST='England'
[[ -n ${SUBJ_L} ]] || export SUBJ_L='London'
[[ -n ${SUBJ_O} ]] || export SUBJ_O='Canonical Ltd.'
[[ -n ${SUBJ_OU} ]] || export SUBJ_OU='Data Center Field Engineering'
[[ -z ${SITE} ]] && export SITE_DERIVED=true
[[ -n ${SUBJ_CN} ]] && export SUBJ_CN_DERIVED=false
[[ -n ${SITE} && -z ${SUBJ_CN} ]] && export SUBJ_CN_DERIVED=true
[[ ${WILDCARD} = true && ${NOHOSTS} = false ]] && { export WC_DERIVED=false WCSTR='Wildcard option selected'; }
[[ ${NOHOSTS} = true ]] && { export WC_DERIVED=true NHST='No hosts options selected' WCSTR='Wildcard option auto-selected (--nohosts)'; }
[[ ${WILDCARD} = true && ${WC_DERIVED} = true ]] && export WC_SEL_STR='Enabled via --nohosts option'
[[ ${WILDCARD} = true && ${WC_DERIVED} = false ]] && export WC_SEL_STR='Enabled via -w,--wildcard option'
SAN_VALUE=$((${#ALT_DOMS[@]}+${#ALT_IPS[@]}+${#ALT_DNS[@]}))
[[ ${LOCALHOST} = true ]] && SAN_VALUE=$((${SAN_VALUE}+3))
[[ ${PRIVATE} = true ]] && SAN_VALUE=$((${SAN_VALUE}+6))
declare -a SUBJCN_ISSUES SITE_ISSUES
[[ -n ${SUBJ_CN} && ${SUBJ_CN} =~ " " && ${SUBJ_CN_DERIVED} = false ]] && SUBJCN_ISSUES+=('Spaces in provided host subject name (--subject-name)')
[[ -n ${SUBJ_CN} && ! ${SUBJ_CN} =~ . && ${SUBJ_CN_DERIVED} = false ]] && SUBJCN_ISSUES+=('No periods to determine top-level domain in provided host subject name (--subject-name)')
[[ -n ${SITE} && ${SITE} =~ " " && ${SITE_DERIVED} = false ]] && SITE_ISSUES+=('Spaces in provided site name (-s,--site)')
[[ -n ${SITE} && ! ${SITE} =~ "\." && ${SITE_DERIVED} = false ]] && SITE_ISSUES+=('No periods in provided site name to determine top-level domain (-s,--site)')
[[ -z ${SITE} && ${SUBJ_CN} =~ " " && ${SITE_DERIVED} = true ]] && SITE_ISSUES+=('Spaces in derived site name')
[[ -z ${SITE} && ! ${SUBJ_CN} =~ "\." && ${SITE_DERIVED} = true ]] && SITE_ISSUES+=('No periods in derived site name to determine top-level domain')
[[ -z ${SITE} && -n ${SUBJ_CN} ]] && export SITE="${SUBJ_CN}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = false && ${SITE_DERIVED} = true && ${SAN_VALUE} -ge 1 ]] && export WC_SITE="*.${SUBJ_CN#*.}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = false && ${SITE_DERIVED} = false && ${SAN_VALUE} -ge 1 ]] && export WC_SITE="*.${SITE#*.}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = false && ${SITE_DERIVED} = true && ${SAN_VALUE} -eq 0 ]] && export WC_SITE="*.${SUBJ_CN}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = false && ${SITE_DERIVED} = false && ${SAN_VALUE} -eq 0 ]] && export WC_SITE="*.${SITE}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = true && ${SITE_DERIVED} = true ]] && export WC_SITE="${SUBJ_CN}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = true && ${SITE_DERIVED} = false ]] && export WC_SITE="${SITE}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = true && ${SITE_DERIVED} = true ]] && export WC_SITE="${SUBJ_CN}"
[[ ${WILDCARD} = true && ${SAN_ONLY_WC} = true && ${SITE_DERIVED} = false ]] && export WC_SITE="${SITE}"
[[ ${SAN_VALUE} -le 1 ]] && {
[[ ${SUBJ_CN} =~ " " || ${SITE} =~ " " ]] && {
printf "\n\e[2G\e[1m*** \e[0;38;2;255;200;0mWARNING \e[0;1m***\e[0m \e[0;38;2;255;200;0mWARNING \e[0;1m***\e[0m \e[0;38;2;255;200;0mWARNING \e[0;1m***\e[0m\n\n\e[2GThe selected option combinations of:\n\n\e[2G - Site (-s,--site): ${SITE:-null}\n\n\e[2G - Host Subject Name (--subject-name): ${SUBJ_CN}\n\n\e[2G - Wildcard (-w,--wildcard): \e[3m${WC_SEL_STR}\e[0m\n\n\e[2G - Subject Alternate Names (SAN):\n\n\e[4G - Hosts (-n,--names): ${#ALT_DNS[@]} \e[3mcount\e[0m\n\e[4G - Domain Names (-d,--domains): ${#ALT_DOMS[@]} \e[3mcount\e[0m\n\e[4G - IP Addresses (-i,--ips) ${#ALT_IPS[@]} \e[3mcount\e[0m\n\n\e[2Gwill result in a SUBJECT CN on the ${TXT:-$([[ ${WILDCARD} = true ]] && echo -n wildcard)} host certificates of \x27*.${SUBJ_CN#*.}\x27\n\e[2Gwhich may be unusable.\n\n"
}; }
# Set the working directory
if [[ -n ${PREFIX} ]];then
export CDIR=${HOME}/ssl/${PREFIX}
else
export CDIR=${HOME}/ssl/${SITE#*.}
fi
[[ ${WILDCARD} = false ]] && printf "\n\e[1mCreating a self-signed SSL certificate\e[0m\n\n" || printf "\n\e[1mCreating a self-signed Wildcard SSL certificate\e[0m\n\n"
printf "\e[2G- Working directory: ${CDIR}\e[0m\n\n"
[[ ${#ALT_DOMS[@]} -ge 1 ]] && printf "\e[2G- Domain list provided\e[0m\n\n"
[[ ${LOCALHOST} = true ]] && printf "\e[2G- localhost option selected\n\n\e[4G- - Adding localhost, 127.0.0.1, and 127.0.1.1 to SAN list\e[0m\n\n"
if [[ ${LOCALHOST} = true ]];then
ALT_DNS+=( localhost )
ALT_IPS+=( 127.0.0.1 127.0.1.1 )
fi
[[ ${PRIVATE} = true ]] && printf "\e[2G- private option selected\n\n\e[4G- - Adding .corp .home .internal .intranet .lan .private .test to domain list\e[0m\n\n"
[[ ${PRIVATE} = true ]] && ALT_DOMS+=(corp home internal intranet lan private test)
[[ ${NOHOSTS} = true && ${LOCALHOST} = true ]] && { printf "\e[2G- Note: --nohosts and --localhost are conflicting options.\n\n\e[4G- - Ignoring --nohosts.\e[0m\n\n"; export NOHOSTS=false; }
[[ ${WILDCARD} = true ]] && printf "\e[2G- ${WCSTR}\e[0m\n\n"
[[ ${NOHOSTS} = true ]] && printf "\e[2G- ${NHSTR}\e[0m\n\n"
[[ ${NOHOSTS} = true && ${#ALT_DOMS[@]} -ge 1 && ${#ALT_IP[@]} -lt 1 && ${LOCALHOST} = false ]] && printf "\e[2G- Note: Selected options will result in a wildcard only certificate\e[0m\n\n"
if [[ ${NOHOSTS} = true ]];then
#Grab domains from list of FQDNs provided
printf "\e[4G- - Creating wildcard SAN entries from site and name list...\e[0m\n"
declare -ag WC_DOMS=($(printf "%s\n" ${SITE} ${ALT_DNS[@],,}|sed -r 's/^[^\.]*\./*./g;/^$|:$/d'|sort -uV))
[[ ${#ALT_DOMS[@]} -ge 1 ]] && { printf "\e[4G- - Adding wildcard entries from domain list...\e[0m\n";while IFS= read -r D;do WC_DOMS+=( "$D" );done < <(printf "*.%s\n" ${ALT_DOMS[@],,}|sort -uV); }
declare -ag ALT_DOMS=()
elif [[ ${NOHOSTS} = false && ${#ALT_DOMS[@]} -ge 1 ]];then
printf "\e[4G- - Creating SAN wildcard entries from domain list...\e[0m\n";
declare -ag WC_DOMS=($(printf "*.%s\n" ${ALT_DOMS[@]}|sort -uV))
declare -ag ALT_DOMS=()
fi
[[ ${AUTOADD} = true ]] && printf -- "\e[2G- Auto-add option selected\e[0m\n\n\e[4G- - Will copy certs to proper locations on this host only\e[0m\n\n"
[[ ${WILDCARD} = true && ${NOHOSTS} = false ]] && WC_DOMS+=( "*.${SITE#*.}" )
#if --nohosts was selected, clear the FQDN list as it should have been processed
[[ ${NOHOSTS} = true ]] && declare -ag ALT_DNS=()
[[ ${SAN_ONLY_WC} = false ]] && declare -ag ALT_NAMES=($(printf "%s\n" ${SITE,,} ${ALT_DNS[@],,}|sort -uV))
[[ ${SAN_ONLY_WC} = false ]] && declare -ag ALT_DNS=($(printf "DNS:%s\n" ${SITE,,} ${ALT_DNS[@],,} ${ALT_IPS[@]} ${ALT_DOMS[@]} ${WC_DOMS[@],,}|sort -uV))
[[ ${SAN_ONLY_WC} = true ]] && declare -ag ALT_NAMES=($(printf "%s\n" ${ALT_DNS[@],,}|sort -uV))
[[ ${SAN_ONLY_WC} = true ]] && declare -ag ALT_DNS=($(printf "DNS:%s\n" ${ALT_DNS[@],,} ${ALT_IPS[@]} ${ALT_DOMS[@]} ${WC_DOMS[@],,}|sort -uV))
[[ ${#ALT_IPS[@]} -ge 1 ]] && declare -ag ALT_IPS=($(printf "IP:%s\n" ${ALT_IPS[@]}|sort -uV))
SAN_LIST=$(printf "%s\n" ${ALT_DNS[@]} ${ALT_IPS[@]}|sort -uV|paste -sd",")
# Make directory if needed
[[ -d ${CDIR} ]] || mkdir -p ${CDIR}
# Create README File
[[ -n ${PREFIX} ]] && export README=${CDIR}/README.${PREFIX}.txt || export README=${CDIR}/README.${SITE#*.}.txt
[[ -f ${README} ]] && { rm -f ${README};touch ${README}; } || touch ${README}
if [[ -n ${PREFIX} ]];then
export CNF="${CDIR}/${PREFIX}.openssl.cnf"
export LOG="${CDIR}/${PREFIX}.log"
export CA_SEC_KEY="${CDIR}/${PREFIX}.ca.secure.key"
export CA_KEY="${CDIR}/${PREFIX}.ca.key"
export CA_CRT="${CDIR}/${PREFIX}.ca.crt"
if [[ ${WILDCARD} = false ]];then
export PEM="${CDIR}/${PREFIX}.host.pem"
export SEC_KEY="${CDIR}/${PREFIX}.host.secure.key"
export KEY="${CDIR}/${PREFIX}.host.key"
export CSR=${CDIR}/${PREFIX}.csr
else
export PEM="${CDIR}/${PREFIX}.wc.host.pem"
export SEC_KEY="${CDIR}/${PREFIX}.wc.host.secure.key"
export KEY="${CDIR}/${PREFIX}.wc.host.key"
export CSR="${CDIR}/${PREFIX}.wc.csr"
fi
else
export CNF="${CDIR}/${SITE#*.}.openssl.cnf"
export LOG="${CDIR}/${SITE#*.}.log"
export CA_SEC_KEY="${CDIR}/${SITE#*.}.ca.secure.key"
export CA_KEY="${CDIR}/${SITE#*.}.ca.key"
export CA_CRT="${CDIR}/${SITE#*.}.ca.crt"
if [[ ${WILDCARD} = false ]];then
export PEM="${CDIR}/${SITE}.host.pem"
export SEC_KEY="${CDIR}/${SITE}.host.secure.key"
export KEY="${CDIR}/${SITE}.host.key"
export CSR=${CDIR}/${SITE}.csr
else
export PEM="${CDIR}/${SITE#*.}.wc.host.pem"
export SEC_KEY="${CDIR}/${SITE#*.}.wc.host.secure.key"
export KEY="${CDIR}/${SITE#*.}.wc.host.key"
export CSR="${CDIR}/${SITE#*.}.wc.csr"
fi
fi
# Write information out to README
printf "\nScript: ${0}\n\n"|tee 1>/dev/null ${README}
printf "Executed on: $(date)\n\n"|tee 1>/dev/null -a ${README}
printf "Options Selected:\n-----------------\n"|tee 1>/dev/null -a ${README}
(echo ${ARGS};echo)|sed -r 's/ -| --/\n&/g'|sed 's/^.*$/ &/g'|sed '1s/^.*$/ &/;s/--$//g'|tee 1>/dev/null -a ${README}
printf "\nFile locations:\n---------------\n CA Certificate: ${CA_CRT}\n\n CA RSA Key: ${CA_KEY}\n\n Host SSL Certificate (PEM format): ${PEM}\n\n HOST RSA Key: ${KEY}\n\n OpenSSL Conf: ${CNF}\n\n Certificate Signing Request: ${CSR}\n\n LOG: ${LOG}\n\n"|tee 1>/dev/null -a ${README}
# Display Issuer/Subject/and SAN details
if [[ ${WILDCARD} = false ]];then
printf "\n\e[3G\e[1mThis SSL cert will have the following Issuer and Subject Lines:\e[0m\n\n"|tee -a ${README}
printf "\e[5G Issuer: C = ${ISSUER_C}, ST = ${ISSUER_ST}, L = ${ISSUER_L}, O = ${ISSUER_O}, OU = ${ISSUER_OU}, CN = ${ISSUER_CN}\n"|tee -a ${README}
printf "\e[5G Subject: C = ${SUBJ_C}, ST = ${SUBJ_ST}, L = ${SUBJ_L}, O = ${SUBJ_O}, OU = ${SUBJ_OU}, CN = ${SITE}\n"|tee -a ${README}
printf "\n\e[3G\e[1mThis SSL cert will be valid for the following names/addresses:\e[0m\n\n"|tee -a ${README}
printf "\e[5G -- %s\n" ${ALT_DNS[@]} ${ALT_IPS[@]}|sort -uV|tee -a ${README}
else
printf "\n\e[3G\e[1mThis Wildcard SSL cert will have the following Issuer and Subject Lines:\e[0m\n\n"|tee -a ${README}
printf "\e[5G Issuer: C = ${ISSUER_C}, ST = ${ISSUER_ST}, L = ${ISSUER_L}, O = ${ISSUER_O}, OU = ${ISSUER_OU}, CN = ${ISSUER_CN}\n"|tee -a ${README}
printf "\e[5G Subject: C = ${SUBJ_C}, ST = ${SUBJ_ST}, L = ${SUBJ_L}, O = ${SUBJ_O}, OU = ${SUBJ_OU}, CN = ${SITE}\n"|tee -a ${README}
printf "\n\e[3G\e[1mThis Wildcard SSL cert will be valid for the following names/addresses:\e[0m\n\n"|tee -a ${README}
printf "\e[5G -- %s\n" ${ALT_DNS[@]} ${ALT_IPS[@]}|sort -uV|tee -a ${README}
fi
# Create openssl configuration file
[[ ${WILDCARD} = false ]] && printf "\n\e[2G - Creating openssl.cnf for ${SITE#*.}" || printf "\n\e[2G - Creating openssl.cnf for ${WC_SITE}"
cp /etc/ssl/openssl.cnf ${CNF}
sed -i \
-e 's|.*\(req_extensions =\)|\1|' \
-e "s|\[ v3_req \]|\0\nsubjectAltName = ${SAN_LIST}\nextendedKeyUsage=serverAuth,clientAuth|" \
-e "s|\[ usr_cert \]|\0\nsubjectAltName = ${SAN_LIST}\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\nextendedKeyUsage=serverAuth,clientAuth|" \
-e "/^RAND/d" \
-e "/^nsComment/d" \
${CNF} >> ${LOG} 2>&1;tstatus
# Create RSA Key for CA - Use existing Key if one exists
if [[ ! -f ${CA_KEY} ]];then
printf "\e[2G - Creating Root CA Key"
openssl genrsa -des3 -passout pass:1234 -out ${CA_SEC_KEY} >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
sleep 1
# Remove passphrase from CA RSA key
printf "\e[2G - Removing passphrase from Root CA Key"
openssl rsa -in ${CA_SEC_KEY} -passin pass:1234 -out ${CA_KEY} >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || exit 1
sleep 1
[[ -f ${CA_SEC_KEY} ]] && rm -f ${CA_SEC_KEY}
else
printf "\e[2G - Using existing Root CA Key: ${CA_KEY}\n"|tee -a ${README}
fi
# Validate non-passphrased CA RSA Key
printf "\e[2G - Validating Root CA Key"
CAKEYCHECK=$(openssl rsa -noout -in ${CA_KEY} -check)
[[ ${CAKEYCHECK} = "RSA key ok" ]] && { true;tstatus; } || { false;tstatus;printf '\e[1;31m\e[4G - Error validating Root CA Key! Exiting.\e[0m\n';exit 1; }
# Create CA Certificate - Use existing CRT if one exists
if [[ ! -f ${CA_CRT} ]];then
printf "\e[2G - Creating Root CA certificate"
openssl req -x509 -new -nodes -subj "/C=${ISSUER_C}/ST=${ISSUER_ST}/L=${ISSUER_L}/O=${ISSUER_O}/OU=${ISSUER_OU}/CN=${ISSUER_CN}" -extensions v3_ca -config ${CNF} -key ${CA_KEY} -sha256 -days 11499 -out ${CA_CRT} >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
else
printf "\e[2G - Using existing Root CA certificate: ${CA_CRT}\n"|tee -a ${README}
fi
# Create RSA Key for Host
[[ ${WILDCARD} = false ]] && printf "\e[2G - Creating RSA Host Key for ${SITE}" || printf "\e[2G - Creating RSA Host Key for *.${SITE#*.}"
openssl genrsa -des3 -passout pass:1234 -out ${SEC_KEY} >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
sleep 1
# Remove passphrase from Host RSA key
printf "\e[2G - Removing passphrase from Host RSA key"
openssl rsa -in ${SEC_KEY} -passin pass:1234 -out ${KEY} >> ${LOG} 2>&1;tstatus
sleep 1
# Validate non-passphrased Host RSA Key
printf "\e[2G - Validating non-passphrased Host RSA Key"
KEYCHECK=$(openssl rsa -noout -in ${KEY} -check)
[[ ${KEYCHECK} = "RSA key ok" ]] && { true;tstatus; } || { false;tstatus;printf '\e[1;31m\e[4G - Error validating RSA Key! Exiting.\e[0m\n';exit 1; }
[[ -f ${SEC_KEY} ]] && { rm -f ${SEC_KEY} >> ${LOG} 2>&1; }
# Generate Certificate Signing Request
if [[ ${WILDCARD} = false ]];then
printf "\e[2G - Generating a multihost CSR"
export CSR=${CDIR}/${SITE}.csr
openssl req -new -key ${KEY} -subj "/C=${SUBJ_C}/ST=${SUBJ_ST}/L=${SUBJ_L}/O=${SUBJ_O}/OU=${SUBJ_OU}/CN=${SITE}" -extensions usr_cert -config ${CNF} -out ${CSR} >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
else
printf "\e[2G - Generating a multihost wildcard CSR"
openssl req -new -key ${KEY} -subj "/C=${SUBJ_C}/ST=${SUBJ_ST}/L=${SUBJ_L}/O=${SUBJ_O}/OU=${SUBJ_OU}/CN=${WC_SITE}" -extensions usr_cert -config ${CNF} -out ${CSR} >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
fi
# Validate Certificate Signing Request
printf "\e[2G - Validating that ${CSR##*/} was signed by\n\e[5G${KEY##*/}"
CSRMD5=$(openssl req -noout -modulus -in ${CSR}|openssl md5|awk '{print $2}')
KEYMD5=$(openssl rsa -noout -modulus -in ${KEY}|openssl md5|awk '{print $2}')
[[ ${CSRMD5} = ${KEYMD5} ]] && { true;tstatus; } || { false;tstatus;printf '\n\e[1;31m\e[4G - Error validating csr! Exiting.\e[0m\n';exit 1; }
# Create Host SSL Cert
if [[ ${WILDCARD} = false ]];then
printf "\e[2G - Generating Multihost SSL Certificate for ${SITE}"
(cd ${CDIR} && openssl x509 -req -in ${CSR} -CA ${CA_CRT} -CAkey ${CA_KEY} -CAcreateserial -extensions usr_cert -extfile ${CNF} -out ${PEM} -days 11499 -sha256) >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
else
printf "\e[2G - Generating Multihost Wildcard SSL Certificate for ${WC_SITE}"
(cd ${CDIR} && openssl x509 -req -in ${CSR} -CA ${CA_CRT} -CAkey ${CA_KEY} -CAcreateserial -extensions usr_cert -extfile ${CNF} -out ${PEM} -days 11499 -sha256) >> ${LOG} 2>&1;tstatus
[[ $? -eq 0 ]] || { printf "\e[4G\e[1;31m - A critical error has occurred! \n\e[7GPlease check ${LOG}.\n\e[0m\n";exit 1; }
fi
# Create copy of openssl.conf
[[ -f ${CNF} ]] && { mv ${CNF} ${CNF}.last; }
# Validate Host SSL Certificate
printf "\e[2G - Validating that ${PEM##*/} was signed by\n\e[5G${KEY##*/}"
CRTMD5=$(openssl x509 -noout -modulus -in ${PEM}|openssl md5|awk '{print $2}')
KEYMD5=$(openssl rsa -noout -modulus -in ${KEY}|openssl md5|awk '{print $2}')
[[ ${CRTMD5} = ${KEYMD5} ]] && { true;tstatus; } || { false;tstatus;printf '\e[1;31m\e[4G - Error validating cert! Exiting.\e[0m\n';exit 1; }
# Copy certs and keys to proper locations if -a,--auto-add option was given
if [[ ${AUTOADD} = true ]];then
printf "\n\e[2G- Executing auto-add option:\n"
printf "\e[4G- Copying ${CA_CRT##*/} to\n\e[8G/usr/local/share/ca-certificates/${CA_CRT##*/}..."
cp -a ${CA_CRT} /usr/local/share/ca-certificates/${CA_CRT##*/};tstatus
printf "\e[4G- Running 'update-ca-certificates --fresh'..."
update-ca-certificates --fresh >> ${LOG} 2>&1;tstatus
printf "\e[4G- Copying ${PEM##*/} to \n\e[8G/etc/ssl/certs/${PEM##*/}..."
cp -a ${PEM} /etc/ssl/certs/${PEM##*/};tstatus
printf "\e[4G- Copying ${KEY##*/} to \n\e[8G/etc/ssl/private/${KEY##*/}..."
cp -a ${KEY} /etc/ssl/private/${KEY##*/};tstatus
printf "\e[4G- Changing permissions to '0640' for\n\e[8G/etc/ssl/private/${KEY##*/}..."
chmod 0640 /etc/ssl/private/${KEY##*/};tstatus
fi
# Print completion message and tips
sp() { printf "%0.0s " $(seq 1 $1); }
printf "\n\e[2G\e[1;32mComplete! \e[0m\e[38;2;255;255;255;3mCerts, Keys, and Logs are located in ${CDIR}\e[0m\n"
(printf "\n\e[5G\e[38;2;255;255;255mTo Use Self-Signed Certs:\e[0m\n"
printf "\n\e[6G\e[35;4mOn Client Machines:\e[0m\n\n\e[6G(\e[3mNote: You will need to copy ${CA_CRT##*/} any client that needs to validate ${PEM##*/})\n\n\e[7G1) \e[38;2;255;255;255;3msudo cp ${CA_CRT} /usr/local/share/ca-certificates/${CA_CRT##*/}\e[0m\n"
printf "\e[7G2) \e[38;2;255;255;255;3msudo update-ca-certificates --fresh --verbose\n\e[0m"
printf "\n\e[6G\e[35;4mOn SSL Enabled Servers:\e[0m\n\n\e[6G(\e[3mNote: You may need to copy ${PEM##*/} and ${KEY##*/} to other hosts)\e[0m\n\n\e[7G1) \e[38;2;255;255;255;3msudo cp ${PEM} /etc/ssl/certs/${PEM##*/}\e[0m\n"
printf "\e[7G2) \e[38;2;255;255;255;3msudo cp ${KEY} /etc/ssl/private/${KEY##*/}\e[0m\n"
printf "\e[7G3) \e[38;2;255;255;255mAdd host certs/keys to applications \e[0m\n"
printf "\e[0m\n\e[5G\e[38;2;255;255;255mTips:\e[0m\n"
if [[ ${WILDCARD} = true ]];then
printf "\n\e[4G - To read the Wildcard SSL Cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${PEM} -text -noout\n\e[0m"
printf "\n\e[4G - To see if a hostname would be covered by this Wildcard SSL Cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${PEM} -noout -checkhost <fqdn>\n\e[0m"
printf "\n\e[4G - To see if an IP address would be covered by this Wildcard SSL Cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${PEM} -noout -checkip <IP Address>\n\e[0m"
fi
if [[ ${WILDCARD} = false ]];then
printf "\n\e[4G - To read the Host SSL cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${PEM} -text -noout\n\e[0m"
printf "\n\e[4G - To see if a hostname would be covered by this Host SSL cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${PEM} -noout -checkhost <fqdn>\n\e[0m"
printf "\n\e[4G - To see if an IP address would be covered by this Host SSL cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${PEM} -noout -checkip <IP Address>\n\e[0m"
fi
printf "\n\e[4G - To read the Root CA cert, type:\n\e[7G\e[38;2;255;255;255;3mopenssl x509 -in ${CA_CRT} -text -noout\n\e[0m")|tee -a ${README}
printf "\n\n\e[2G\e[38;2;255;255;255mAll of the above information and more is contained in the readme file\n\e[2Glocated @ ${README}\n\e[0m\n\n"
printf "\e[2G\e[38;2;255;255;255mSample Cloud-Init implementations for MAAS, juju and LXD that use these SSL \n\e[2Gcertificates can be found in: ${CDIR}/\n\e[0m\n\n"
# Dump Host and CA cert into README
printf "\n\nCA Certificate Details:\n-----------------------\n"|tee 1>/dev/null -a ${README}
openssl x509 -in ${CA_CRT} -text -noout|tee 1>/dev/null -a ${README}
printf "\n\nHost Certificate Details:\n-------------------------\n"|tee 1>/dev/null -a ${README}
openssl x509 -in ${PEM} -text -noout|tee 1>/dev/null -a ${README}
# Remove ansi sequences from README
[[ -f ${README} ]] && sudo sed 2>/dev/null -i 's/\x1b\[[0-9;]*[a-zA-Z]//g' ${README}
# Create sample cloud-init yaml files for MAAS, Juju, and LXD
cat <<EOF|sudo tee 1>/dev/null ${CDIR}/standard-cloud-init-with-certs.example.yaml
##### This is a sample standard cloud-init with relevant certificate/file sections
##### This would be relevant for MAAS or other standard Cloud-Init implementations but
##### not Juju or LXD as cloud-init is embedded in other files
##### With MAAS cloud-init is passed as a deployment time option of --user-data="\$(base64 -w0 /path/to/cloud-init.yaml)"
##### This is NOT a full cloud-init file, but the included sections are valid
##### All certs in the example are valid from the last run of ${0##*/}
#cloud-config
final_message: 'Standard Cloud-Init Has Completed'
timezone: 'America/Los_Angeles'
locale: 'en_US.UTF-8'
write_files:
- encoding: b64
content: $(base64 -w0 ${PEM})
path: /etc/ssl/certs/${PEM##*/}
permissions: '0644'
- encoding: b64
content: $(base64 -w0 ${KEY})
path: /etc/ssl/private/${KEY##*/}
permissions: '0640'
ca-certs:
trusted: |
$(cat ${CA_CRT}|sed 's/^.*$/ &/g')
EOF
cat <<EOF|sudo tee 1>/dev/null ${CDIR}/juju-cloud-init-with-certs.example.yaml
##### This is a sample juju model-default/model-config file with relevant certificate/file sections
##### With Juju, cloud-init is embedded in model-default or model-config yaml files
##### This is NOT a full juju model configuration file, but the included sections are valid
##### All certs in the example are valid from the last run of ${0##*/}
container-inherit-properties: 'ca-certs, apt-primary, apt-security, apt-sources'
enable-os-refresh-update: true
enable-os-upgrade: true
cloudinit-userdata: |
#cloud-config
final_message: 'Juju Cloud-Init Has Completed'
timezone: 'America/Los_Angeles'
locale: 'en_US.UTF-8'
write_files:
- encoding: b64
content: $(base64 -w0 ${PEM})
path: /etc/ssl/certs/${PEM##*/}
permissions: '0644'
- encoding: b64
content: $(base64 -w0 ${KEY})
path: /etc/ssl/private/${KEY##*/}
permissions: '0640'
ca-certs:
trusted: |
$(cat ${CA_CRT}|sed 's/^.*$/ &/g')
EOF
cat <<EOF|sudo tee 1>/dev/null ${CDIR}/lxd-cloud-init-with-certs.example.yaml
##### This is a sample LXD profile with relevant certificate/file sections
##### With LXD, cloud-init is embedded in a container profile or machine configuration
##### This is NOT a full LXD profile, but the included sections are valid
##### All certs in the example are valid from the last run of ${0##*/}
config:
boot.autostart: "true"
security.privileged: 'true'
security.nesting: "true"
user.user-data: |
#cloud-config
final_message: 'LXD Cloud-Init Has Completed'
timezone: 'America/Los_Angeles'
locale: 'en_US.UTF-8'
write_files:
- encoding: b64
content: $(base64 -w0 ${PEM})
path: /etc/ssl/certs/${PEM##*/}
permissions: '0644'
- encoding: b64
content: $(base64 -w0 ${KEY})
path: /etc/ssl/private/${KEY##*/}
permissions: '0640'
ca-certs:
trusted: |
$(cat ${CA_CRT}|sed 's/^.*$/ &/g')
EOF
# If we got here, cleanly exit
exit 0
@ThinGuy
Copy link
Author

ThinGuy commented Dec 30, 2019

$ ./multihost-wc-ssl.sh -h

multihost-wc-ssl.sh - Create CA certs, host certs, and RSA keys for
                      Multi-Hostname and Multi-Domain self-signed SSL
                      certificates, including wildcard certs

Usage: sudo ./multihost-wc-ssl.sh -s <FQDN> [ OPTIONS ]

Options:

 -s --site          Primary FQDN that this ssl cert should be valid for

 -n, --names        Space or comma separated list of all FQDNs that this
                    ssl cert should be valid for (enclose space eparated
                    slist in quotes)

 -i, --ips          Space or comma separated list of IP addresses to
                    include as a Subject Alternative Name (SAN) (enclose space
                    separated list in quotes)

 -d, --domains      Space or comma separated list of domain names to
                    include as a wildcard entry in Subject Alternative
                    Name (SAN) (enclose space separate list in quotes)

 -l, --local        Include DNS and IP entries for localhost, 127.0.0,1, and
                    127.0.1.1

 -w, --wildcard     Create wildcard entries for the domains of the
                    FQDNs provided with the -n,--names option

 -p, --private      Include Private DNS Namespaces for top-level domains
                    (.corp .home .internal .intranet .lan .private .test)
                    See: https://tools.ietf.org/html/rfc6762#appendix-G

 -P, --prefix       Prefix to give to certs,keys, etc.
                    Default: site name given with -s,--site option

 -a, --auto-add     Automatically copy CA Cert to local CA store, host cert
                    to /etc/ssl/certs/, and host key to /etc/ssl/private/

 --nohosts          Only include wildcard entries in Subject Alternative Name
                    (SAN), not host entries

 --issuer-country   Issuer (CA) Country Name (2 Letter Code) 
                    Default: GB

 --issuer-state     Issuer (CA) State or Province Name (Full Name) 
                    Default: England

 --issuer-locality  Issuer (CA) Locality Name (i.e. City)
                    Default: London

 --issuer-org       Issuer (CA) Organization Name (i.e. Company) 
                    Default: Canonical Ltd.

 --issuer-unit      Issuer (CA) Organizational Unit (i.e. Section)
                    Default: Data Center Field Engineering

 --issuer-name      Issuer (CA) Common Name (Your name or FQDN)
                    Default: Canonical AIR stack

 --subject-country  Subject (Host) Country Name (2 Letter Code) 
                    Default: GB

 --subject-state    Subject (Host) State or Province Name (Full Name) 
                    Default: England

 --subject-locality Subject (Host) Locality Name (i.e. City)
                    Default: London

 --subject-org      Subject (Host) Organization Name (i.e. Company) 
                    Default: Canonical Ltd.

 --subject-unit     Subject (Host) Organizational Unit (i.e. Section)
                    Default: Data Center Field Engineering

                    Note: Subject CN is determined by -s,--site or 
                    -P,--prefix args and wildcard options


Ex:

sudo ./multihost-wc-ssl.sh \ 
-w \ 
-s image-server.orangebox.me \ 
-n maas-images.orangebox.me,juju-images.orangebox.me,lxd-images.orangebox.me \ 
-d maas.io,images.linuxcontainers.org,linuxcontainers.org \ 
-i 172.27.20.1,172.27.20.2,172.27.20.3,172.27.20.4,172.27.20.5,172.27.20.6


Notes:

 - Be careful of subdomains with wildcard enabled certs:

     FQDN:   server.example.com
     HOST:   server
     DOMAIN: example.com

     FQDN:   us.server.example.com
     HOST:   us
     DOMAIN: server.example.com

     To ensure wildcards cover both, add example.com AND server.example.com
     to the -d,--domain option

 - How to use self-signed certs, testing tips, and configuration examples
   provided upon completion

 - Log file will be located in  ${HOME}/ssl/<domain name of site>/<site>.log 

 - The --nohosts option assumes -w,--wildcard 

 - You can use -d,--domain with -w.--wildcard, however only unique entries will
   be listed in the SAN field

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment