summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2021-11-08 23:01:33 +0000
committerLuke Bratch <luke@bratch.co.uk>2021-11-08 23:01:33 +0000
commit24c56616bfae82a08ccad62e256a84952bd47ead (patch)
treeb52a1370d15860d2fd9dbef0ed9671706a380add
parente3fb9717e627de7653e389be7ee7c67bdd435d25 (diff)
Add support for checking IPv4 and IPv6 hosts separately, plus support for raw IPv6 addresses
-rwxr-xr-xcertexpiry.sh112
1 files changed, 94 insertions, 18 deletions
diff --git a/certexpiry.sh b/certexpiry.sh
index 3901f95..468e4e0 100755
--- a/certexpiry.sh
+++ b/certexpiry.sh
@@ -4,21 +4,37 @@ set -euo pipefail
# ==== Variables ====
# Output warning if fewer than this number of seconds until expiry
WARNSECONDS=1209600 # Two weeks
-# File containing a newline separated list of host:port combinations to be checked
+# File containing a newline separated list of host:port combinations to be checked, e.g.:
+# example.org
+# example.org:1234
+# 1.2.3.4
+# 1.2.3.4:1234
+# [2001:12:34::]
+# [2001:12:34::]:1234
HOSTSANDPORTS="hostsandports.txt"
# Number of seconds before OpenSSL should timeout when connecting to hosts
TIMEOUT=10
# ==== Variables ====
+IPV4REGEX="((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}"
+IPV6REGEX="(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"
+
# Loop through all host:port combinations
while read -r HOSTANDPORT ; do
# echo "DEBUG: HOSTANDPORT: $HOSTANDPORT."
- # Host before colon
- HOST=$(echo "$HOSTANDPORT" | cut -d ":" -f "1")
- # Port after colon, or nothing if no port given (assumes 443 later on)
- PORT=$(echo "$HOSTANDPORT" | cut -s -d ":" -f "2")
+ # Get the host and port
+ # Is it an IPv6 address?
+ if echo "$HOSTANDPORT" | grep -P "^\[$IPV6REGEX" ; then
+ HOST=$(echo "$HOSTANDPORT" | grep -Eo "^\[.*?\]" | sed "s/\[//g ; s/\]//g")
+ PORT=$(echo "$HOSTANDPORT" | sed "s/.*\]//" | cut -s -d ":" -f "2")
+ # Let's assume it's an IPv4 address or a hostname
+ else
+ HOST=$(echo "$HOSTANDPORT" | cut -d ":" -f "1")
+ # Port after colon, or nothing if no port given (assumes 443 later on)
+ PORT=$(echo "$HOSTANDPORT" | cut -s -d ":" -f "2")
+ fi
# echo "DEBUG: HOST: $HOST."
@@ -39,36 +55,96 @@ while read -r HOSTANDPORT ; do
# echo "DEBUG: PORT: $PORT."
# echo "DEBUG: STARTTLS: $STARTTLS."
- # Try various TLS versions against this host:port to try and get a response
+ # Sanity check (IPv4)
+ NOIPV4=0
+ # Does it have an A record?
+ if [ $(dig A "$HOST" +short | wc -l) -eq 0 ] ; then
+ NOIPV4=1
+ fi
+ # Is it in fact an IPv4 address?
+ if echo "$HOST" | grep -Pq "^$IPV4REGEX$" ; then
+ NOIPV4=0
+ fi
+
+ # Try various TLS versions against this host:port to try and get a response (IPv4)
+ EXPIRYSTRING4=0
RETCODE=1
for PROTOCOL in -tls1_3 -tls1_2 -tls1_1 -tls1 ; do
+ if [ "$NOIPV4" -eq 1 ] ; then
+ break
+ fi
if [ "$RETCODE" -eq 0 ] ; then
break
fi
set +e
# Get the "Not After" field for the certificate expiry
- EXPIRYSTRING=$(echo "Q" | timeout "$TIMEOUT" openssl s_client $STARTTLS -connect "$HOST:$PORT" -servername "$HOST" "$PROTOCOL" 2> /dev/null | openssl x509 -noout -text 2> /dev/null | grep "Not After" | sed -r 's/\s*Not After : //')
+ EXPIRYSTRING4=$(echo "Q" | timeout "$TIMEOUT" openssl s_client -4 $STARTTLS -connect "$HOST:$PORT" -servername "$HOST" "$PROTOCOL" 2> /dev/null | openssl x509 -noout -text 2> /dev/null | grep "Not After" | sed -r 's/\s*Not After : //')
RETCODE="$?"
set -e
done
-# echo "DEBUG: EXPIRYSTRING: $EXPIRYSTRING."
+ # Sanity check (IPv6)
+ NOIPV6=0
+ # Does it have a AAAA record?
+ if [ $(dig AAAA "$HOST" +short | wc -l) -eq 0 ] ; then
+ NOIPV6=1
+ fi
+ # Is it in fact an IPv6 address?
+ if echo "$HOST" | grep -Pq "^$IPV6REGEX$" ; then
+ NOIPV6=0
+ fi
+
+ # Try various TLS versions against this host:port to try and get a response (IPv6)
+ EXPIRYSTRING6=0
+ RETCODE=1
+ for PROTOCOL in -tls1_3 -tls1_2 -tls1_1 -tls1 ; do
+ if [ "$NOIPV6" -eq 1 ] ; then
+ break
+ fi
+ if [ "$RETCODE" -eq 0 ] ; then
+ break
+ fi
+ set +e
+ # Get the "Not After" field for the certificate expiry
+ EXPIRYSTRING6=$(echo "Q" | timeout "$TIMEOUT" openssl s_client -6 $STARTTLS -connect "[$HOST]:$PORT" -servername "$HOST" "$PROTOCOL" 2> /dev/null | openssl x509 -noout -text 2> /dev/null | grep "Not After" | sed -r 's/\s*Not After : //')
+ RETCODE="$?"
+ set -e
+ done
+
+# echo "DEBUG: EXPIRYSTRING4: $EXPIRYSTRING4."
+# echo "DEBUG: EXPIRYSTRING6: $EXPIRYSTRING6."
- # Convert expiry into unixtime
- EXPIRY=$(date -d "$EXPIRYSTRING" +%s)
-# echo "DEBUG: EXPIRY: $EXPIRY."
NOW=$(date +%s)
# echo "DEBUG: NOW: $NOW."
- # Number of seconds left
- DIFFERENCE=$(("$EXPIRY" - "$NOW"))
-# echo "DEBUG: DIFFERENCE: $DIFFERENCE."
+ # Convert expiry into unixtime (IPv4)
+ EXPIRY4=$(date -d "$EXPIRYSTRING4" +%s)
+# echo "DEBUG: EXPIRY4: $EXPIRY4."
+
+ # Convert expiry into unixtime (IPv6)
+ EXPIRY6=$(date -d "$EXPIRYSTRING6" +%s)
+# echo "DEBUG: EXPIRY6: $EXPIRY6."
+
+ # Number of seconds left (IPv4)
+ DIFFERENCE4=$(("$EXPIRY4" - "$NOW"))
+# echo "DEBUG: DIFFERENCE4: $DIFFERENCE4."
+
+ # Number of seconds left (IPv6)
+ DIFFERENCE6=$(("$EXPIRY6" - "$NOW"))
+# echo "DEBUG: DIFFERENCE6: $DIFFERENCE6."
+
+ # Warn if less than WARNSECONDS less (IPv4)
+ if [ "$DIFFERENCE4" -lt "$WARNSECONDS" ] && [ "$NOIPV4" -eq 0 ] ; then
+ echo "Warning! The certificate at $HOST:$PORT (IPv4) expires in $DIFFERENCE4 seconds (~$((DIFFERENCE4 / 60 / 60 / 24)) days)."
+# else
+# echo "DEBUG: The certificate at $HOST:$PORT (IPv4) expires in $DIFFERENCE4 seconds (~$((DIFFERENCE4 / 60 / 60 / 24)) days)."
+ fi
- # Warn if less than WARNSECONDS less
- if [ "$DIFFERENCE" -lt "$WARNSECONDS" ] ; then
- echo "Warning! The certificate at $HOST:$PORT expires in $DIFFERENCE seconds (~$((DIFFERENCE / 60 / 60 / 24)) days)."
+ # Warn if less than WARNSECONDS less (IPv6)
+ if [ "$DIFFERENCE6" -lt "$WARNSECONDS" ] && [ "$NOIPV6" -eq 0 ] ; then
+ echo "Warning! The certificate at $HOST:$PORT (IPv6) expires in $DIFFERENCE6 seconds (~$((DIFFERENCE6 / 60 / 60 / 24)) days)."
# else
-# echo "DEBUG: The certificate at $HOST:$PORT expires in $DIFFERENCE seconds (~$((DIFFERENCE / 60 / 60 / 24)) days)."
+# echo "DEBUG: The certificate at $HOST:$PORT (IPv6) expires in $DIFFERENCE6 seconds (~$((DIFFERENCE6 / 60 / 60 / 24)) days)."
fi
done < "$HOSTSANDPORTS"