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 Oct 7, 2019

$ sudo ./multihost-wc-ssl.sh \
-P airstack \
-a \
-p \
-l \
-w \
-s orangebox20.orangebox.me \
-n maas.io,jaas.ai,fce.orangebox.me,juju-controller.orangebox.me,canonical-archive.orangebox.me,cloud-archive.orangebox.me,cloud-images.orangebox.me,elastic-archive.orangebox.me,fce.orangebox.me,grafana-archive.orangebox.me,juju-images.orangebox.me,key-server.orangebox.me,maas-images.orangebox.me,nvidia-archive.orangebox.me,ppa-archive.orangebox.me,private-ppa.orangebox.me,security-archive.orangebox.me,snap-proxy.orangebox.me,ubuntu-archive.orangebox.me,us.cloud-images.ubuntu.com,rocks.canonical.com,streams-archive.orangebox.me \
-d apps.ubuntu.com,archive.canonical.com,archive.ubuntu.com,canonical.com,cloud-images.ubuntu.com,developer.ubuntu.com,download.nvidia.com,elastic.co,github.com,grafana.com,images.linuxcontainers.org,jaas.ai,jujucharms.com,launchpad.net,linuxcontainers.org,maas,maas.io,orangebox.me,ubuntu.com

Creating a self-signed Wildcard SSL certificate

 - Working directory: /home/ubuntu/ssl/airstack

 - Domain list provided

 - localhost option selected

   - - Adding localhost, 127.0.0.1, and 127.0.1.1 to SAN list

 - private option selected

   - - Adding .corp .home .internal .intranet .lan .private .test to domain list

 - Wildcard option selected

   - - Creating SAN wildcard entries from domain list...
 - Auto-add option selected

   - - Will copy certs to proper locations on this host only


  This Wildcard SSL cert will have the following Issuer and Subject Lines:

     Issuer:  C = GB, ST = England, L =  London, O = Canonical Ltd., OU = Data Center Field Engineering, CN = Canonical AIR stack
     Subject: C = GB, ST = England, L =  London, O = Canonical Ltd., OU = Data Center Field Engineering, CN = *.orangebox.me

  This Wildcard SSL cert will be valid for the following names/addresses:

     -- DNS:127.0.0.1
     -- DNS:127.0.1.1
     -- DNS:canonical-archive.orangebox.me
     -- DNS:cloud-archive.orangebox.me
     -- DNS:cloud-images.orangebox.me
     -- DNS:elastic-archive.orangebox.me
     -- DNS:fce.orangebox.me
     -- DNS:grafana-archive.orangebox.me
     -- DNS:jaas.ai
     -- DNS:juju-controller.orangebox.me
     -- DNS:juju-images.orangebox.me
     -- DNS:key-server.orangebox.me
     -- DNS:localhost
     -- DNS:maas.io
     -- DNS:maas-images.orangebox.me
     -- DNS:nvidia-archive.orangebox.me
     -- DNS:orangebox20.orangebox.me
     -- DNS:ppa-archive.orangebox.me
     -- DNS:private-ppa.orangebox.me
     -- DNS:rocks.canonical.com
     -- DNS:security-archive.orangebox.me
     -- DNS:snap-proxy.orangebox.me
     -- DNS:streams-archive.orangebox.me
     -- DNS:ubuntu-archive.orangebox.me
     -- DNS:us.cloud-images.ubuntu.com
     -- DNS:*.apps.ubuntu.com
     -- DNS:*.archive.canonical.com
     -- DNS:*.archive.ubuntu.com
     -- DNS:*.canonical.com
     -- DNS:*.corp
     -- DNS:*.developer.ubuntu.com
     -- DNS:*.download.nvidia.com
     -- DNS:*.elastic.co
     -- DNS:*.github.com
     -- DNS:*.grafana.com
     -- DNS:*.home
     -- DNS:*.images.linuxcontainers.org
     -- DNS:*.internal
     -- DNS:*.intranet
     -- DNS:*.jaas.ai
     -- DNS:*.jujucharms.com
     -- DNS:*.lan
     -- DNS:*.launchpad.net
     -- DNS:*.linuxcontainers.org
     -- DNS:*.maas
     -- DNS:*.maas.io
     -- DNS:*.orangebox.me
     -- DNS:*.private
     -- DNS:*.test
     -- DNS:*.ubuntu.com
     -- DNS:*.cloud-images.ubuntu.com
     -- IP:127.0.0.1
     -- IP:127.0.1.1

  - Creating openssl.cnf for *.orangebox.me                           [ OK ]
  - Creating Root CA Key                                              [ OK ]
  - Removing passphrase from Root CA Key                              [ OK ]
  - Validating Root CA Key                                            [ OK ]
  - Creating Root CA certificate                                      [ OK ]
  - Creating RSA Host Key for *.orangebox.me                          [ OK ]
  - Removing passphrase from Host RSA key                             [ OK ]
  - Validating non-passphrased Host RSA Key                           [ OK ]
  - Generating a multihost wildcard CSR                               [ OK ]
  - Validating that airstack.wildcard.csr was signed by
    airstack.host.wildcard.key                                        [ OK ]
  - Generating Multihost Wildcard SSL Certificate for *.orangebox.me  [ OK ]
  - Validating that airstack.host.wildcard.pem was signed by
    airstack.host.wildcard.key                                        [ OK ]

 - Executing auto-add option:
   - Copying airstack.ca.crt to
       /usr/local/share/ca-certificates/airstack.ca.crt...            [ OK ]
   - Running 'update-ca-certificates --fresh'...                      [ OK ]
   - Copying airstack.host.wildcard.pem to 
       /etc/ssl/certs/airstack.host.wildcard.pem...                   [ OK ]
   - Copying airstack.host.wildcard.key to 
       /etc/ssl/private/airstack.host.wildcard.key...                 [ OK ]
   - Changing permissions to '0640' for
       /etc/ssl/private/airstack.host.wildcard.key...                 [ OK ]

 Complete! Certs, Keys, and Logs are located in /home/ubuntu/ssl/airstack

    To Use Self-Signed Certs:

     On Client Machines:

     (Note: You will need to copy airstack.ca.crt any client that needs to validate airstack.host.wildcard.pem)

      1) sudo cp /home/ubuntu/ssl/airstack/airstack.ca.crt /usr/local/share/ca-certificates/airstack.ca.crt
      2) sudo update-ca-certificates --fresh --verbose

     On SSL Enabled Servers:

     (Note: You may need to copy airstack.host.wildcard.pem and airstack.host.wildcard.key to other hosts)

      1) sudo cp /home/ubuntu/ssl/airstack/airstack.host.wildcard.pem /etc/ssl/certs/airstack.host.wildcard.pem
      2) sudo cp /home/ubuntu/ssl/airstack/airstack.host.wildcard.key /etc/ssl/private/airstack.host.wildcard.key
      3) Add host certs/keys to applications 

    Tips:

    - To read the Wildcard SSL Cert, type:
      openssl x509 -in /home/ubuntu/ssl/airstack/airstack.host.wildcard.pem -text -noout

    - To see if a hostname would be covered by this Wildcard SSL Cert, type:
      openssl x509 -in /home/ubuntu/ssl/airstack/airstack.host.wildcard.pem -noout -checkhost <fqdn>

    - To see if an IP address would be covered by this Wildcard SSL Cert, type:
      openssl x509 -in /home/ubuntu/ssl/airstack/airstack.host.wildcard.pem -noout -checkip <IP Address>

    - To read the Root CA cert, type:
      openssl x509 -in /home/ubuntu/ssl/airstack/airstack.ca.crt -text -noout


 All of the above information and more is contained in the readme file
 located @ /home/ubuntu/ssl/airstack/README.airstack.txt


 Sample Cloud-Init implementations for MAAS, juju and LXD that use these SSL 
 certificates can be found in: /home/ubuntu/ssl/airstack/



@ThinGuy
Copy link
Author

ThinGuy commented Oct 7, 2019

$ openssl x509 -in /home/ubuntu/ssl/airstack/airstack.host.wildcard.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            6a:52:d4:a0:87:f6:49:28:03:b5:d2:1e:e9:fc:95:54:0e:15:a8:7b
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = GB, ST = England, L = London, O = Canonical Ltd., OU = Data Center Field Engineering, CN = Canonical AIR stack
        Validity
            Not Before: Dec 30 03:12:47 2019 GMT
            Not After : Jun 24 03:12:47 2051 GMT
        Subject: C = GB, ST = England, L = London, O = Canonical Ltd., OU = Data Center Field Engineering, CN = *.orangebox.me
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:b1:e2:cc:aa:36:c0:12:55:84:59:0a:bd:31:6d:
                    6c:2e:11:92:95:8a:a0:03:ca:ef:fc:5b:9b:09:2b:
                    25:e3:51:f1:2c:14:e5:bf:f5:17:04:43:2f:a5:1e:
                    32:6b:a3:b9:c3:55:12:3a:c7:59:e0:f8:60:48:f1:
                    45:cb:7f:f1:e2:18:7d:e9:8f:2a:b0:70:65:3a:ca:
                    eb:72:61:70:d7:ec:8d:a9:00:ad:19:36:0f:be:07:
                    7d:c1:8c:d2:74:13:44:e2:9c:0f:12:66:7f:92:f0:
                    92:48:00:da:e8:17:f9:48:0f:07:c2:bc:7f:b3:73:
                    1a:16:6f:82:b0:a8:96:8b:11:2e:db:0b:a1:5b:0c:
                    dc:94:86:65:5b:83:6d:f4:76:24:37:e3:a4:dd:64:
                    c9:c9:d4:90:48:24:04:4f:83:ad:c2:3f:d6:8a:d9:
                    bc:a4:28:f2:31:24:7b:7c:9d:d7:8b:3a:5a:da:ff:
                    44:2e:c3:f9:bc:41:18:17:ad:5e:07:88:2e:a8:08:
                    70:ba:74:6d:84:f6:65:bd:9b:36:c0:81:a7:36:59:
                    a3:8d:19:1a:9c:e5:a0:cc:57:d3:ad:15:57:30:45:
                    a3:8f:ae:e3:da:89:9a:34:1a:c8:e6:21:90:fb:3b:
                    00:42:82:b1:8c:a9:ae:80:29:18:ac:7f:fc:6c:31:
                    4c:9d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:127.0.0.1, DNS:127.0.1.1, DNS:canonical-archive.orangebox.me, DNS:cloud-archive.orangebox.me, DNS:cloud-images.orangebox.me, DNS:elastic-archive.orangebox.me, DNS:fce.orangebox.me, DNS:grafana-archive.orangebox.me, DNS:jaas.ai, DNS:juju-controller.orangebox.me, DNS:juju-images.orangebox.me, DNS:key-server.orangebox.me, DNS:localhost, DNS:maas.io, DNS:maas-images.orangebox.me, DNS:nvidia-archive.orangebox.me, DNS:orangebox20.orangebox.me, DNS:ppa-archive.orangebox.me, DNS:private-ppa.orangebox.me, DNS:rocks.canonical.com, DNS:security-archive.orangebox.me, DNS:snap-proxy.orangebox.me, DNS:streams-archive.orangebox.me, DNS:ubuntu-archive.orangebox.me, DNS:us.cloud-images.ubuntu.com, DNS:*.apps.ubuntu.com, DNS:*.archive.canonical.com, DNS:*.archive.ubuntu.com, DNS:*.canonical.com, DNS:*.corp, DNS:*.developer.ubuntu.com, DNS:*.download.nvidia.com, DNS:*.elastic.co, DNS:*.github.com, DNS:*.grafana.com, DNS:*.home, DNS:*.images.linuxcontainers.org, DNS:*.internal, DNS:*.intranet, DNS:*.jaas.ai, DNS:*.jujucharms.com, DNS:*.lan, DNS:*.launchpad.net, DNS:*.linuxcontainers.org, DNS:*.maas, DNS:*.maas.io, DNS:*.orangebox.me, DNS:*.private, DNS:*.test, DNS:*.ubuntu.com, DNS:*.cloud-images.ubuntu.com, IP Address:127.0.0.1, IP Address:127.0.1.1
            X509v3 Key Usage: 
                Digital Signature, Non Repudiation, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Subject Key Identifier: 
                1E:22:54:B3:12:D1:58:66:A2:9F:FE:A7:AA:BA:41:64:A5:69:22:63
            X509v3 Authority Key Identifier: 
                keyid:F7:FE:C4:89:77:06:3F:D8:80:9F:04:B5:DA:18:42:D2:BF:EE:07:8F

    Signature Algorithm: sha256WithRSAEncryption
         39:cf:6e:49:64:62:06:a8:25:50:c3:60:ab:ec:4b:a7:15:40:
         a0:c0:47:d5:32:ad:ee:27:71:60:76:34:b6:08:bd:18:5f:b3:
         0d:75:e7:ba:0f:69:ac:09:64:23:bd:f8:8c:39:73:84:51:e2:
         32:00:13:08:5b:a7:cc:38:25:28:6f:e1:b3:90:16:1f:df:68:
         9f:92:ad:05:28:71:57:d2:f8:66:49:b5:0a:5f:85:70:88:33:
         70:58:f1:29:c5:50:e0:7b:6c:d7:af:71:f9:5f:0b:e5:59:d7:
         e7:0e:d8:e1:f4:9a:f0:75:18:0e:bd:7b:eb:6d:e9:e3:08:64:
         ae:4b:14:0f:1c:e9:09:31:de:54:6f:62:74:5f:23:b9:90:c4:
         2e:f9:76:cc:cf:78:6b:f9:2b:f2:28:7c:fa:f7:60:d5:6b:66:
         49:62:d3:7a:f2:f6:5e:8a:5b:e7:4d:d3:cf:d2:84:2e:cf:2d:
         41:ce:a1:5f:ca:b1:f4:44:49:c3:74:0a:20:3e:f0:5f:c9:73:
         99:be:0c:ee:04:e9:4a:0f:ce:c4:b6:7b:a5:7d:51:cf:20:ca:
         5a:e7:b0:0b:7f:dc:02:27:d2:57:9c:cc:15:9c:70:cc:76:9b:
         f8:af:7c:3c:3d:7a:ae:5e:81:d8:46:6c:c8:a3:05:b5:ca:1a:
         76:b1:10:f6

@ThinGuy
Copy link
Author

ThinGuy commented Oct 7, 2019

$ openssl x509 -in /home/ubuntu/ssl/airstack/airstack.ca.crt -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            61:f1:7e:81:b0:7f:72:88:5d:58:44:68:f3:68:b4:47:8b:9d:2a:f2
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = GB, ST = England, L = London, O = Canonical Ltd., OU = Data Center Field Engineering, CN = Canonical AIR stack
        Validity
            Not Before: Dec 30 03:12:45 2019 GMT
            Not After : Jun 24 03:12:45 2051 GMT
        Subject: C = GB, ST = England, L = London, O = Canonical Ltd., OU = Data Center Field Engineering, CN = Canonical AIR stack
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:a6:88:9d:a2:2f:c5:4f:29:73:27:2f:6d:26:ab:
                    fd:75:82:c9:87:5f:45:07:e9:b7:7b:41:b8:bd:d2:
                    1f:93:e1:f0:4d:b7:37:5f:40:6a:fc:3f:91:58:be:
                    25:54:34:c5:4b:0a:46:66:00:f2:92:e0:5a:84:89:
                    40:ee:dc:40:b7:2d:ce:f4:ee:03:d3:1b:26:25:e6:
                    08:83:d4:48:4d:35:7b:7a:54:a6:ca:79:da:f8:cd:
                    49:35:95:51:91:18:19:9e:67:a1:6f:32:52:c6:ba:
                    d1:cd:eb:b7:bc:1e:93:1c:a4:60:a6:01:c2:39:2c:
                    65:ba:43:28:88:97:c1:af:f3:f7:d1:79:f7:82:24:
                    29:aa:c5:24:79:a2:90:bc:3d:b6:31:fc:c1:92:7d:
                    2b:fa:17:e7:fb:e7:1b:af:09:de:e3:39:ac:af:b8:
                    7c:c6:b2:3e:b9:99:cd:62:4f:b2:56:71:6a:1a:81:
                    2c:59:ab:06:df:67:8d:de:4e:6a:93:6b:6a:e1:aa:
                    79:24:88:67:49:96:2b:19:2d:03:f7:43:9f:62:4e:
                    d1:bc:42:1a:32:09:bf:52:94:f2:b2:35:c7:fe:7b:
                    98:ac:59:81:d6:66:0c:21:85:63:39:3c:0f:07:35:
                    b8:79:89:09:e1:48:82:74:09:c3:f1:12:87:1c:39:
                    27:db
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                F7:FE:C4:89:77:06:3F:D8:80:9F:04:B5:DA:18:42:D2:BF:EE:07:8F
            X509v3 Authority Key Identifier: 
                keyid:F7:FE:C4:89:77:06:3F:D8:80:9F:04:B5:DA:18:42:D2:BF:EE:07:8F

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         41:a2:60:f1:0a:34:47:9e:48:78:ea:97:72:f0:60:16:5c:f7:
         67:f9:29:94:24:aa:34:96:ce:fe:33:4e:bd:b4:34:34:33:ee:
         95:c6:39:f3:f8:ab:bb:fe:6a:33:1e:77:f3:38:5b:38:97:34:
         41:fc:85:55:bb:78:a0:e7:22:92:1c:f4:3e:90:0e:0a:75:a0:
         88:44:80:b8:cc:49:3e:7e:68:29:0f:bf:c8:36:e2:41:7e:b3:
         dc:d5:f2:39:5e:43:39:04:58:c7:0e:e6:65:88:5b:d1:03:49:
         f1:88:cc:79:81:56:e6:ea:90:75:15:1b:68:8b:50:71:95:f7:
         c2:0e:2b:20:58:68:8e:34:02:db:4c:4a:ec:29:cf:ed:cf:f8:
         dd:cd:a1:b8:b6:e0:3c:9a:e7:26:9e:1f:fa:ef:b0:9b:68:fd:
         f1:1a:16:0c:47:4d:ee:39:ce:e6:df:84:68:e2:10:b0:b7:14:
         a7:27:f2:22:4c:76:af:06:fb:03:5e:bd:93:ef:f4:69:36:05:
         cf:f3:2a:9e:c5:64:40:2e:c1:2b:fe:b6:fd:7b:da:fc:47:57:
         cf:ef:f0:24:23:a9:cf:71:73:4e:2e:ab:fd:2b:0a:ba:8e:7d:
         e9:08:2d:a3:6a:3e:74:7e:23:3d:66:3e:53:fb:98:63:c7:14:
         f8:68:03:79

@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