| @ -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 | |||