diff --git a/getssl b/getssl index fecbc7c..6343655 100755 --- a/getssl +++ b/getssl @@ -264,6 +264,8 @@ # 2021-07-12 Do not redirect outputs on remote commands when the debug option is used (atisne) # 2021-07-20 Use +noidnout to enable certificates for IDN domains (#679)(2.37) # 2021-07-22 Only pass +noidnout param to dig/drill(#682)(2.38) +# 2021-07-25 Fix copy_file_to_location failures with ssh when suffix applied to file lacking an extension (tlhackque)(#686) +# 2021-07-27 Support ftps://, FTPS_OPTIONS, remove default --insecure parameter to ftpes. Report caller(s) of error_exit in debug and test modes (tlhackque)(#687)(2.39) # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -272,7 +274,7 @@ esac PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.38" +VERSION="2.39" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -290,6 +292,7 @@ DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" FTP_OPTIONS="" +FTPS_OPTIONS="" FULL_CHAIN_INCLUDE_ROOT="false" GETSSL_IGNORE_CP_PRESERVE="false" HTTP_TOKEN_CHECK_WAIT=0 @@ -850,7 +853,12 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. IFS=\; read -r -a copy_locations <<<"$3" for to in "${copy_locations[@]}"; do if [[ -n "$suffix" ]]; then - to="${to%.*}.${suffix}.${to##*.}" + bname="$(basename "$to")" + if [[ "${bname##*.}" == "$bname" ]]; then + to="${to}.${suffix}" + else + to="${to%.*}.${suffix}.${to##*.}" + fi fi info "copying $cert to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then @@ -934,7 +942,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. debug "davs user=$davsuser - pass=$davspass - host=$davshost port=$davsport dir=$davsdirn file=$davsfile" debug "from dir=$fromdir file=$fromfile" curl -u "${davsuser}:${davspass}" -T "${fromdir}/${fromfile}" "https://${davshost}:${davsport}${davsdirn}${davsfile}" - elif [[ "${to:0:6}" == "ftpes:" ]] ; then + elif [[ "${to:0:6}" == "ftpes:" ]] || [[ "${to:0:5}" == "ftps:" ]] ; then debug "using ftp to copy the file from $from" ftpuser=$(echo "$to"| awk -F: '{print $2}') ftppass=$(echo "$to"| awk -F: '{print $3}') @@ -946,7 +954,13 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. fromfile=$(basename "$from") debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" debug "from dir=$fromdir file=$fromfile" - curl --insecure --ftp-ssl -u "${ftpuser}:${ftppass}" -T "${fromdir}/${fromfile}" "ftp://${ftphost}${ftpdirn}/" + if [[ "${to:0:5}" == "ftps:" ]] ; then + # shellcheck disable=SC2086 + curl $FTPS_OPTIONS --ftp-ssl --ftp-ssl-reqd -u "${ftpuser}:${ftppass}" -T "${fromdir}/${fromfile}" "ftp://${ftphost}${ftpdirn}:990/" + else + # shellcheck disable=SC2086 + curl $FTPS_OPTIONS --ftp-ssl --ftp-ssl-reqd -u "${ftpuser}:${ftppass}" -T "${fromdir}/${fromfile}" "ftp://${ftphost}${ftpdirn}/" + fi else if ! mkdir -p "$(dirname "$to")" ; then error_exit "cannot create ACL directory $(basename "$to")" @@ -1146,6 +1160,9 @@ test_output() { # write out debug output for testing error_exit() { # give error message on error exit echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2 + if [[ ${_RUNNING_TEST} -eq 1 ]] || [[ ${_USE_DEBUG} -eq 1 ]] ; then + traceback + fi clean_up exit 1 } @@ -1357,7 +1374,10 @@ for d in "${alldomains[@]}"; do else sleep "$HTTP_TOKEN_CHECK_WAIT" # check that we can reach the challenge ourselves, if not, then error - if [[ ! "$(curl --user-agent "$CURL_USERAGENT" -k --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then + # ACME only allows port 80 (http), but redirects may use https. --insecure is used in case + # those certificates are being renewed. Let's Encrypt does the same. In this case, we verify + # that the correct data is returned, so this is safe. + if [[ ! "$(curl --user-agent "$CURL_USERAGENT" --insecure --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then error_exit "for some reason could not reach $wellknown_url - please check it manually" fi fi @@ -2393,6 +2413,16 @@ signal_exit() { # Handle trapped signals esac } +traceback() { # Print function traceback + local i d=1 lbl=" called" + echo "Traceback" >&2 + for ((i=$((${#FUNCNAME[@]}-1)); i>0; i--)); do + if [[ ${i} -eq 1 ]] ; then lbl=" called traceback" ; fi + printf "%*s%s() line %d%s\n" "$d" '' "${FUNCNAME[$i]}" "${BASH_LINENO[$((i-1))]}" "$lbl" >&2 + ((d++)) + done +} + urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_' openssl base64 -e | tr -d '\n\r' | os_esed -e 's:=*$::g' -e 'y:+/:-_:' } @@ -2448,9 +2478,11 @@ write_domain_template() { # write out a template file for a domain. # An ssh key will be needed to provide you with access to the remote server. # Optionally, you can specify a different userid for ssh/scp to use on the remote server before the @ sign. # If left blank, the username on the local server will be used to authenticate against the remote server. - # If these start with ftp:/ftpes: then the next variables are ftpuserid:ftppassword:servername:ACL_location + # If these start with ftp:/ftpes:/ftps: then the next variables are ftpuserid:ftppassword:servername:ACL_location # These should be of the form "/path/to/your/website/folder/.well-known/acme-challenge" # where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain. + # ftp: uses regular ftp; ftpes: ftp over explicit TLS (port 21); ftps: ftp over implicit TLS (port 990). + # ftps/ftpes support FTPS_OPTIONS, e.g. to add "--insecure" to the curl command for hosts with self-signed certificates. # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, # password, host, port (explicitly needed even if using default port 443) and path on the server. # Multiple locations can be defined for a file by separating the locations with a semi-colon. @@ -2459,6 +2491,7 @@ write_domain_template() { # write out a template file for a domain. # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge' + # 'ftps:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' # 'ftpes:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge') # Specify SSH options, e.g. non standard port in SSH_OPTS