| @ -1,466 +0,0 @@ | |||||
| #!/bin/bash | |||||
| # --------------------------------------------------------------------------- | |||||
| # create-getssl-config - Create a config file interactively to obtain an SSL certificate using getssl | |||||
| # 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, either version 3 of the License, or | |||||
| # (at your option) any later version. | |||||
| # 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 at <http://www.gnu.org/licenses/> for | |||||
| # more details. | |||||
| # Usage: create-getssl-config [-h|--help] [-d|--debug] | |||||
| # Revision history: | |||||
| # 2016-02-04 Created (v0.1) | |||||
| # 2016-02-05 Updated to include more variables. Still not full operational. (v0.2) | |||||
| # 2016-05-04 Corrected typo on DNS_DEL_COMMAND (v0.3) | |||||
| # 2016-05-23 Added option to provide a blank response, for things like SANS. (0.4) | |||||
| # 2016-08-01 updated agreement for letsencrypt (0.5) | |||||
| # --------------------------------------------------------------------------- | |||||
| PROGNAME=${0##*/} | |||||
| VERSION="0.5" | |||||
| # defaults | |||||
| CA="https://acme-staging.api.letsencrypt.org" | |||||
| AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf" | |||||
| ACCOUNT_KEY_LENGTH=4096 | |||||
| WORKING_DIR=~/.getssl | |||||
| DOMAIN_KEY_LENGTH=4096 | |||||
| SSLCONF="$(openssl version -d | cut -d\" -f2)/openssl.cnf" | |||||
| VALIDATE_VIA_DNS="false" | |||||
| RELOAD_CMD="" | |||||
| RENEW_ALLOW="30" | |||||
| PRIVATE_KEY_ALG="rsa" | |||||
| SERVER_TYPE="https" | |||||
| CHECK_REMOTE="true" | |||||
| DNS_EXTRA_WAIT=0 | |||||
| clean_up() { # Perform pre-exit housekeeping | |||||
| return | |||||
| } | |||||
| error_exit() { | |||||
| echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2 | |||||
| clean_up | |||||
| exit 1 | |||||
| } | |||||
| graceful_exit() { | |||||
| clean_up | |||||
| exit | |||||
| } | |||||
| signal_exit() { # Handle trapped signals | |||||
| case $1 in | |||||
| INT) | |||||
| error_exit "Program interrupted by user" ;; | |||||
| TERM) | |||||
| echo -e "\n$PROGNAME: Program terminated" >&2 | |||||
| graceful_exit ;; | |||||
| *) | |||||
| error_exit "$PROGNAME: Terminating on unknown signal" ;; | |||||
| esac | |||||
| } | |||||
| usage() { | |||||
| echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug]" | |||||
| } | |||||
| log() { | |||||
| echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $*" >> "${PROGNAME}.log" | |||||
| } | |||||
| debug() { | |||||
| if [[ "${_USE_DEBUG:-"0"}" -eq 1 ]]; then | |||||
| echo "$@" | |||||
| fi | |||||
| } | |||||
| info() { | |||||
| echo "$@" | |||||
| } | |||||
| get_user_input() { | |||||
| prompt=$1 | |||||
| DefVal=$2 | |||||
| HelpInfo=$3 | |||||
| response="" | |||||
| echo "" | |||||
| validresponse="false" | |||||
| while [[ "$validresponse" == "false" ]]; do | |||||
| read -p "${prompt} (${DefVal}) : " response | |||||
| if [[ -z $response ]]; then | |||||
| debug "response blank - used default - $DefVal" | |||||
| res=$DefVal | |||||
| validresponse="true" | |||||
| elif [[ "$response" == "h" ]]; then | |||||
| echo "" | |||||
| echo "$HelpInfo" | |||||
| echo "" | |||||
| elif [[ "$response" == "-" ]]; then | |||||
| res="" | |||||
| validresponse="true" | |||||
| else | |||||
| res=$response | |||||
| validresponse="true" | |||||
| fi | |||||
| done | |||||
| } | |||||
| write_getssl_template() { # write out the main template file | |||||
| cat > "$1" <<- _EOF_getssl_ | |||||
| # Uncomment and modify any variables you need | |||||
| # The staging server is best for testing (hence set as default) | |||||
| #CA="https://acme-staging.api.letsencrypt.org" | |||||
| # This server issues full certificates, however has rate limits | |||||
| #CA="https://acme-v01.api.letsencrypt.org" | |||||
| CA="$CA" | |||||
| AGREEMENT="$AGREEMENT" | |||||
| # Set an email address associated with your account - generally set at account level rather than domain. | |||||
| #ACCOUNT_EMAIL="me@example.com" | |||||
| ACCOUNT_KEY_LENGTH=4096 | |||||
| ACCOUNT_KEY="$WORKING_DIR/account.key" | |||||
| PRIVATE_KEY_ALG="rsa" | |||||
| # The command needed to reload apache / nginx or whatever you use | |||||
| #RELOAD_CMD="" | |||||
| # The time period within which you want to allow renewal of a certificate | |||||
| # this prevents hitting some of the rate limits. | |||||
| RENEW_ALLOW="30" | |||||
| # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, | |||||
| # smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which | |||||
| # will be checked for certificate expiry and also will be checked after | |||||
| # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true | |||||
| SERVER_TYPE="https" | |||||
| CHECK_REMOTE="true" | |||||
| # openssl config file. The default should work in most cases. | |||||
| SSLCONF="$SSLCONF" | |||||
| # Use the following 3 variables if you want to validate via DNS | |||||
| #VALIDATE_VIA_DNS="true" | |||||
| #DNS_ADD_COMMAND= | |||||
| #DNS_DEL_COMMAND= | |||||
| # If your DNS-server needs extra time to make sure your DNS changes are readable by the ACME-server (time in seconds) | |||||
| #DNS_EXTRA_WAIT=60 | |||||
| _EOF_getssl_ | |||||
| } | |||||
| write_domain_template() { # write out a template file for a domain. | |||||
| cat > "$1" <<- _EOF_domain_ | |||||
| # Uncomment and modify any variables you need | |||||
| # The staging server is best for testing | |||||
| #CA="https://acme-staging.api.letsencrypt.org" | |||||
| # This server issues full certificates, however has rate limits | |||||
| #CA="https://acme-v01.api.letsencrypt.org" | |||||
| CA="$CA" | |||||
| AGREEMENT="$AGREEMENT" | |||||
| ACCOUNT_EMAIL="$ACCOUNT_EMAIL" | |||||
| ACCOUNT_KEY_LENGTH=$ACCOUNT_KEY_LENGTH | |||||
| ACCOUNT_KEY="$ACCOUNT_KEY" | |||||
| PRIVATE_KEY_ALG="$PRIVATE_KEY_ALG" | |||||
| # Additional domains - this could be multiple domains / subdomains in a comma separated list | |||||
| SANS=${SANS} | |||||
| # Acme Challenge Location. The first entry for the domain, the following ones for each additional domain. | |||||
| # If these start with ssh: then the next variable is assumed to be the hostname and the rest the location. | |||||
| # An ssh key will be needed to provide you with access to the remote server. | |||||
| # If these start with ftp: or sftp: then the next variables are userid:password:servername:ACL_location | |||||
| #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' | |||||
| # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' | |||||
| # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge') | |||||
| ACL=(${ACL[*]}) | |||||
| # Location for all your certs, these can either be on the server (so full path name) or using ssh as for the ACL | |||||
| DOMAIN_CERT_LOCATION="$DOMAIN_CERT_LOCATION" | |||||
| DOMAIN_KEY_LOCATION="$DOMAIN_KEY_LOCATION" | |||||
| CA_CERT_LOCATION="$CA_CERT_LOCATION" | |||||
| DOMAIN_CHAIN_LOCATION="" | |||||
| DOMAIN_PEM_LOCATION="" | |||||
| # The command needed to reload apache / nginx or whatever you use | |||||
| RELOAD_CMD="$RELOAD_CMD" | |||||
| # The time period within which you want to allow renewal of a certificate | |||||
| # this prevents hitting some of the rate limits. | |||||
| RENEW_ALLOW="$RENEW_ALLOW" | |||||
| # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, | |||||
| # smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which | |||||
| # will be checked for certificate expiry and also will be checked after | |||||
| # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true | |||||
| SERVER_TYPE="$SERVER_TYPE" | |||||
| CHECK_REMOTE="$CHECK_REMOTE" | |||||
| # Use the following 3 variables if you want to validate via DNS | |||||
| VALIDATE_VIA_DNS="$VALIDATE_VIA_DNS" | |||||
| DNS_ADD_COMMAND="$DNS_ADD_COMMAND" | |||||
| DNS_DEL_COMMAND="$DNS_DEL_COMMAND" | |||||
| # If your DNS-server needs extra time to make sure your DNS changes are readable by the ACME-server (time in seconds) | |||||
| DNS_EXTRA_WAIT=$DNS_EXTRA_WAIT | |||||
| _EOF_domain_ | |||||
| } | |||||
| help_message() { | |||||
| cat <<- _EOF_ | |||||
| $PROGNAME ver. $VERSION | |||||
| Create a config file interactively to obtain an SSL certificate using getssl | |||||
| $(usage) | |||||
| Options: | |||||
| -h, --help Display this help message and exit. | |||||
| -d, --debug outputs debug information | |||||
| _EOF_ | |||||
| return | |||||
| } | |||||
| # Trap signals | |||||
| trap "signal_exit TERM" TERM HUP | |||||
| trap "signal_exit INT" INT | |||||
| # Parse command-line | |||||
| while [[ -n $1 ]]; do | |||||
| case $1 in | |||||
| -h | --help) | |||||
| help_message; graceful_exit ;; | |||||
| -d | --debug) | |||||
| _USE_DEBUG=1 ;; | |||||
| -w) | |||||
| shift; WORKING_DIR="$1" ;; | |||||
| -* | --*) | |||||
| usage | |||||
| error_exit "Unknown option $1" ;; | |||||
| *) | |||||
| DOMAIN="$1" ;; | |||||
| esac | |||||
| shift | |||||
| done | |||||
| # Main logic | |||||
| info "" | |||||
| info "This is an interactive script to create a config file for getssl, please answer the following questions" | |||||
| info "Just press return for using the default" | |||||
| info "enter the letter 'h' for help / more information" | |||||
| info "enter the minus character '-' to provide a completely empty response." | |||||
| info "" | |||||
| if [ -f "$WORKING_DIR/getssl.cfg" ]; then | |||||
| debug "reading main config from existing $WORKING_DIR/getssl.cfg" | |||||
| . "$WORKING_DIR/getssl.cfg" | |||||
| fi | |||||
| get_user_input "What is the working directory for getssl" "$WORKING_DIR" \ | |||||
| "The working directory is where getssl saves all the config and certifcates" | |||||
| WORKING_DIR=$res | |||||
| # if the "working directory" doesn't exist, then create it. | |||||
| if [ ! -d "$WORKING_DIR" ]; then | |||||
| debug "Making working directory - $WORKING_DIR" | |||||
| mkdir -p "$WORKING_DIR" | |||||
| fi | |||||
| # if main config file exists, read it, else write default. | |||||
| if [ -f "$WORKING_DIR/getssl.cfg" ]; then | |||||
| debug "reading main config from existing $WORKING_DIR/getssl.cfg" | |||||
| . "$WORKING_DIR/getssl.cfg" | |||||
| else | |||||
| write_getssl_template "$WORKING_DIR/getssl.cfg" | |||||
| fi | |||||
| get_user_input "Domain name" "${DOMAIN}" \ | |||||
| "This should be the primary domain name you want on your SSL certificate" | |||||
| DOMAIN=$res | |||||
| # need to check if domain is valid | |||||
| DOMAIN_DIR="$WORKING_DIR/$DOMAIN" | |||||
| if [ ! -d "$DOMAIN_DIR" ]; then | |||||
| info "Making domain directory - $DOMAIN_DIR" | |||||
| mkdir -p "$DOMAIN_DIR" | |||||
| fi | |||||
| #if domain config file exists, read it. | |||||
| if [ -f "$DOMAIN_DIR/getssl.cfg" ]; then | |||||
| debug "reading config from $DOMAIN_DIR/getssl.cfg" | |||||
| . "$DOMAIN_DIR/getssl.cfg" | |||||
| fi | |||||
| #prompt to use staging server .... best for testing | |||||
| #CA="https://acme-staging.api.letsencrypt.org" | |||||
| # This server issues full certificates, however has rate limits | |||||
| #CA="https://acme-v01.api.letsencrypt.org" | |||||
| #prompt for agreement | |||||
| get_user_input "Agreement" "${AGREEMENT}" \ | |||||
| "This is the agreement with LetsEncrypt, and shouldn't generally be changed" | |||||
| AGREEMENT=$res | |||||
| # Set an email address associated with your account - generally set at account level rather than domain. | |||||
| get_user_input "Account email address" "${ACCOUNT_EMAIL}" \ | |||||
| "The email address that will be used by LetsEncrypt to notify you when your certificate is due for renewal" | |||||
| ACCOUNT_EMAIL=$res | |||||
| get_user_input "Account key location" "${ACCOUNT_KEY}" \ | |||||
| "The location of the account key. " | |||||
| ACCOUNT_KEY=$res | |||||
| get_user_input "Account key length" "${ACCOUNT_KEY_LENGTH}" \ | |||||
| "Account key length - the default is typically the best option" | |||||
| ACCOUNT_KEY_LENGTH=$res | |||||
| get_user_input "Domain private key algorithm" "${PRIVATE_KEY_ALG}" \ | |||||
| "Domain private key algorithm - the default is typically the best option" | |||||
| PRIVATE_KEY_ALG=$res | |||||
| get_user_input "Check server for certificate validity" "$CHECK_REMOTE" \ | |||||
| "If true, getssl will check the live server for certificate validity rather than using the local certs | |||||
| getssl also checks after installation, that the new valid certificate is in place" | |||||
| CHECK_REMOTE=$res | |||||
| if [[ "$CHECK_REMOTE" == "true" ]]; then | |||||
| get_user_input "server type" "$SERVER_TYPE" \ | |||||
| "This can be 'https', 'ftp', 'ftpi', 'imap', 'imaps', 'pop3', 'pop3s', 'smtp', 'smtps_deprecated', 'smtps', 'smtp_submission', 'xmpp', 'xmpps', 'ldaps' or a port number that getssl will use for certifcate checks" | |||||
| SERVER_TYPE=$res | |||||
| else | |||||
| SERVER_TYPE="" | |||||
| fi | |||||
| if [[ ${SERVER_TYPE} == "https" ]] || [[ ${SERVER_TYPE} == "webserver" ]]; then | |||||
| REMOTE_PORT=443 | |||||
| elif [[ ${SERVER_TYPE} == "ftp" ]]; then | |||||
| REMOTE_PORT=21 | |||||
| REMOTE_EXTRA="-starttls ftp" | |||||
| elif [[ ${SERVER_TYPE} == "ftpi" ]]; then | |||||
| REMOTE_PORT=990 | |||||
| elif [[ ${SERVER_TYPE} == "imap" ]]; then | |||||
| REMOTE_PORT=143 | |||||
| REMOTE_EXTRA="-starttls imap" | |||||
| elif [[ ${SERVER_TYPE} == "imaps" ]]; then | |||||
| REMOTE_PORT=993 | |||||
| elif [[ ${SERVER_TYPE} == "pop3" ]]; then | |||||
| REMOTE_PORT=110 | |||||
| REMOTE_EXTRA="-starttls pop3" | |||||
| elif [[ ${SERVER_TYPE} == "pop3s" ]]; then | |||||
| REMOTE_PORT=995 | |||||
| elif [[ ${SERVER_TYPE} == "smtp" ]]; then | |||||
| REMOTE_PORT=25 | |||||
| REMOTE_EXTRA="-starttls smtp" | |||||
| elif [[ ${SERVER_TYPE} == "smtps_deprecated" ]]; then | |||||
| REMOTE_PORT=465 | |||||
| elif [[ ${SERVER_TYPE} == "smtps" ]] || [[ ${SERVER_TYPE} == "smtp_submission" ]]; then | |||||
| REMOTE_PORT=587 | |||||
| REMOTE_EXTRA="-starttls smtp" | |||||
| elif [[ ${SERVER_TYPE} == "xmpp" ]]; then | |||||
| REMOTE_PORT=5222 | |||||
| REMOTE_EXTRA="-starttls xmpp" | |||||
| elif [[ ${SERVER_TYPE} == "xmpps" ]]; then | |||||
| REMOTE_PORT=5269 | |||||
| elif [[ ${SERVER_TYPE} == "ldaps" ]]; then | |||||
| REMOTE_PORT=636 | |||||
| elif [[ ${SERVER_TYPE} =~ ^[0-9]+$ ]]; then | |||||
| REMOTE_PORT=${SERVER_TYPE} | |||||
| else | |||||
| error_exit "unknown server type" | |||||
| fi | |||||
| SANS="www.${DOMAIN}" | |||||
| if [[ ! -z ${REMOTE_PORT} ]]; then | |||||
| # Additional domains - this could be multiple domains / subdomains in a comma separated list | |||||
| EX_CERT=$(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null | openssl x509 2>/dev/null) | |||||
| if [ ! -z "${EX_CERT}" ]; then | |||||
| SANS=$(echo "$EX_CERT" | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ | |||||
| | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-) | |||||
| SANS=${SANS//$'\n'/','} | |||||
| fi | |||||
| fi | |||||
| get_user_input "Additional domain names" "${SANS}" \ | |||||
| "this could be multiple domains / subdomains in a comma separated list" \ | |||||
| "use the minus sign - if you don't want any SANS" | |||||
| SANS=$res | |||||
| get_user_input "Validate via DNS" "${VALIDATE_VIA_DNS}" \ | |||||
| "If true, getssl will use DNS to validate the domain, if false then http / https will be used" | |||||
| VALIDATE_VIA_DNS=$res | |||||
| if [[ $VALIDATE_VIA_DNS == "true" ]]; then | |||||
| get_user_input "DNS add command" "${DNS_ADD_COMMAND}" \ | |||||
| "location/name of script which will add the token message to DNS" | |||||
| DNS_ADD_COMMAND=$res | |||||
| get_user_input "DNS del command" "${DNS_DEL_COMMAND}" \ | |||||
| "location/name of script which will delete the token message from DNS" | |||||
| DNS_DEL_COMMAND=$res | |||||
| get_user_input "DNS extra wait time" "${DNS_EXTRA_WAIT}" \ | |||||
| "delay time, to wait for DNS to propagate once changed." | |||||
| DNS_EXTRA_WAIT=$res | |||||
| else | |||||
| # find IP of this server | |||||
| LocalIP=$(dig +short @208.67.222.222 myip.opendns.com) | |||||
| #loop over all domains | |||||
| alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") | |||||
| dn=0 | |||||
| for d in $alldomains; do | |||||
| # find IP of domain | |||||
| DomainIP=$(dig +short ${d}) | |||||
| # if domain is local, try and find location of files | |||||
| if [[ "${DomainIP}" == "${LocalIP}" ]]; then | |||||
| if [[ ! -d "/var/www/${d}/web" ]]; then | |||||
| ACL[$dn]="/var/www/${DOMAIN}/web/.well-known/acme-challenge" | |||||
| elif [[ ! -d "/var/www/${d}" ]]; then | |||||
| ACL[$dn]="/var/www/${d}/.well-known/acme-challenge" | |||||
| else | |||||
| ACL[$dn]="/var/www/.well-known/acme-challenge" | |||||
| fi | |||||
| else #domain is remote | |||||
| ACL[$dn]="ssh:${d}:/var/www/.well-known/acme-challenge" | |||||
| fi | |||||
| get_user_input "ACL for $d" "${ACL[$dn]}" \ | |||||
| "The Acme challenge location for domaind ${d}. This should be your web root plus .well-known/acme-challenge" | |||||
| ACL[$dn]=$res | |||||
| ((dn++)) | |||||
| done | |||||
| fi | |||||
| # Location for all your certs, these can either be on the server (so full path name) or using ssh as for the ACL | |||||
| #DOMAIN_CERT_LOCATION="ssh:server5:/etc/ssl/domain.crt" | |||||
| #DOMAIN_KEY_LOCATION="ssh:server5:/etc/ssl/domain.key" | |||||
| #CA_CERT_LOCATION="/etc/ssl/chain.crt" | |||||
| #DOMAIN_CHAIN_LOCATION="" this is the domain cert and CA cert | |||||
| #DOMAIN_PEM_LOCATION="" this is the domain_key. domain cert and CA cert | |||||
| # The command needed to reload apache / nginx or whatever you use | |||||
| #RELOAD_CMD="" | |||||
| # The time period within which you want to allow renewal of a certificate | |||||
| # this prevents hitting some of the rate limits. | |||||
| # RENEW_ALLOW="30" | |||||
| # create domain directory if it doesn't exist | |||||
| if [ ! -d "$DOMAIN_DIR" ]; then | |||||
| info "Making domain directory - $DOMAIN_DIR" | |||||
| mkdir -p "$DOMAIN_DIR" | |||||
| fi | |||||
| #Write out domain config | |||||
| write_domain_template "$DOMAIN_DIR/getssl.cfg" | |||||
| # Is it worth, with this create script, setting it to run once on the "happy hacker" CA to test, before | |||||
| # it would then change the CA ( if all OK ) and running on the live LE server ? | |||||
| graceful_exit | |||||