[GTALUG] Script to show HTTP(S) and TLS details for a website

William Park opengeometry at yahoo.ca
Sun Aug 11 23:48:54 EDT 2019


On Sat, Aug 10, 2019 at 11:46:40AM -0400, Giles Orr via talk wrote:
> If you're interested, you can find the details here:
> 
> https://www.gilesorr.com/blog/tls-https-details.html
> 
> Any suggestions to improve the script would be most welcome.

OK, here it goes...

> # Put most desirable/least-likely last:
> for opensslbinary in /usr/local/opt/openssl at 1.1/bin/openssl /usr/local/opt/opens
> sl/bin/openssl /usr/bin/openssl
> do
>     # Every version of OpenSSL I've checked (several across multiple
>     # OpenSSL versions and Apple's LibreSSL) respond to 'openssl version'
>     # with '<brand> <version-no> ...' - there may or may not be stuff after
>     # those two items (usually a build date, but not with Libre).
>     #
>     if [ -f "${opensslbinary}" ]

'-f' tests if it's a file.  Maybe you should use '-x' which tests if it's
executable.

>     then
>         output="$("${opensslbinary}" version)"
>         brand="$(  echo "${output}" | awk '{ print $1 }')"
>         version="$(echo "${output}" | awk '{ print $2 }')"
>         if [[ "OpenSSL" == "${brand}" ]]
>         then
>             if [[ "${version}" == "1.1"* ]]
>             then
>                 OPENSSL="${opensslbinary}"
>             fi
>         fi

You got 7 subshells here.  You can reduce it to 1 subshell:

	read a b rest <<< $($opensslbinary version)
	case $a:$b in
	    OpenSSL:1.1*) OPENSSL=$opensslbinary ;;
	esac

>     fi
> done

Maybe you should break out of the for-loop as soon as you find something
correct.  As is, OPENSSL will be the last correct one.


> function help() {
>     echo "Usage:"
>     echo "    $(basename "${0}") [-h]|<domain-name>"
>     echo ""
>     echo "Show HTTP(s) and certificate details."
>     echo "Do not include the 'http(s)://' leader on the domain name."
>     echo ""
>     echo "-h            show this help and exit"

Instead of echo'ing each and every line, you can do

    cat << EOF
Usage:
    $(basename "${0}") [-h]|<domain-name>

Show HTTP(s) and certificate details.
Do not include the 'http(s)://' leader on the domain name.

-h            show this help and exit
EOF


> function expiry_date() {
>     echo "${1}" | ${OPENSSL} x509 -noout -dates | grep notAfter | awk 'BEGIN { F
> S="=" } { print $2 }'
> }

You can get rid of grep, since you're already using awk.

    echo "$1" | ${OPENSSL} x509 -noout -dates | awk -F= '/notAfter/ {print $2}'

> 
> function days_to_expiry() {
>     expiry_date="$(echo "${1}" | ${OPENSSL} x509 -noout -dates | grep notAfter |
>  awk 'BEGIN { FS="=" } { print $2 }')"

You already defined expiry_date(), so why not use it here.

    expiry_date=$(expiry_date "$1")

>     if [[ "$(date --version 2>/dev/null)" == *"GNU"* ]]
>     then
>         # Linux (or at least GNU)
>         expiry_epoch_seconds=$(date --date="${expiry_date}" "+%s")
>     else
>         # Assuming the Mac version:
>         expiry_epoch_seconds=$(date -jf '%b %e %H:%M:%S %Y %Z' "${expiry_date}"
> "+%s")
>     fi

Testing for "GNU" string can be done more simply using grep.

    if date --version 2>/dev/null | grep -q GNU; then
	...
    else
	...
    fi

> function tlsversions() {
>     successful=""
>     failed=""
>     for tlsversion in ssl2 ssl3 tls1 tls1_1 tls1_2 tls1_3
>     do
>         success=$(echo | ${OPENSSL} s_client -connect "${1}":443 -${tlsversion}
> > /dev/null 2> /dev/null ; echo $?)
>         if [ ${success} -eq 0 ]
>         then
>             successful="${tlsversion} ${successful}"
>         else
>             failed="${tlsversion} ${failed}"
>         fi

Testing if a command succeeded or failed can be done in-line.

	if echo | ${OPENSSL} s_client -connect "${1}":443 -${tlsversion} &>/dev/null; then
	    successful=...
	else
	    failed=...
	fi

Also, do you really need to send empty line to openssl?  Maybe you can redirect
stdin from /dev/null as well, like

    openssl ... </dev/null ...

That will reduce 3 subshells to 0.


General observation:

1. You don't need quotes in variable assignments like 
	var="$(...)"
	var="$var1"

2. The following tests are the same.  They both tests if var is empty.
	[ "$var"x = x ]
	[ -z "$var" ]
    
3. Testing if command succeeded or failed can be done in-line.
	if command1 | command2 | command2; then
	    ...
	else
	    ...
	fi

4. Case statement is usually better than if statement.  eg.
	if [[ "$var" == glob ]]; then
	    ...
	fi
   vs
	case $var in
	    glob) ... ;;
	esac
    Note the missing quotes in case, too.  You don't need it.
-- 
William Park <opengeometry at yahoo.ca>


More information about the talk mailing list