HTTPS - Monitor SSL Handshake

Problem this snippet solves:

External HTTPS monitor script that tests for successful SSL Handshake, then terminates. In BigIP versions v9.3.0 to v10.0.1, when no Receive String is specified in the HTTPS monitor (default setting), the monitor would mark a resource as "Available" upon successful completion of the SSL Handshake. This was an unintended behavior (ID 207411 / CR 120157), which was corrected in v10.1.0, and the HTTPS monitor now requires valid HTTP data in the response even if no Receive String is specified in the monitor; see SOL10904 for further details on this change.

However, the original, unintended behavior may still be desirable in certain situations; for instance, HTTPS based VPN gateways which have no facility to provide HTTPS data themselves, and for which the monitor was only intended to verify SSL handshake. One way of working around this specific example is by re-engineering the monitoring to be more sophisticated and test the entire traffic path - i.e.: adding user auth info (User Name and Password) and Send String, so that the device in question may proxy the request successfully and send back a valid HTTP response.

However, in circumstances where this is undesired, or the device in question is expected to not respond with HTTP data and only the handshake needs to be tested, the following external health monitor script will provide behavior similar to the pre-v10.1.0 behavior.

NOTE: It is worth mentioning that the pre-v10.1.0 behavior basically caused the HTTPS Monitor to behave like a general SSL monitor when no Receive String was specified.

IMPORTANT: Use external monitors only when a built-in monitor cannot suffice. This example is merely intended to demonstrate a means of allowing monitoring of successful SSL Handshake where the HTTP response is expected to be null. Otherwise, for an HTTPS resource which can be expected to have some valid HTTP response, but for which the response doesn't matter, you should simply use the built-in HTTPS monitor template with a "null" Receive string.

How to use this snippet:

  1. Create a new file containing the code below in /usr/bin/monitors on the LTM filesystem. Permissions on the file must be 700 or better, giving root rwx access to the file.
  2. Create a monitor profile -- via WebGUI: Local Traffic module -> Monitors menu -> Create... button

    • Type: "External"
    • External Program: [filename from step 1 with full path: /usr/bin/monitors/[filename]]
    • 3. Set Interval and Timeout as desired (typical best practice: Timeout = 3 x Interval + 1), along with any other settings needed

Caveats

This monitor does NOT support much of the features available in the stock HTTPS monitor. For instance: user auth (User Name & Password), Send String, and, of course, Receive String (though at that point the built-in monitor would be a better fit). It would be trivial to add in this kind of support, but was outside the originating goal for which this script was specifically crafted.

Code :

#!/bin/sh
#
# https_v10.0.1_monitor-v1.0
#

# I, nor F5, provide any guarantee that this script will function as intended.
#       it should be thoroughly understood by the implementor (you), and tested
#       in a dev/qa environment prior to use in a production environment.
#       Script is provided AS-IS.

#
# expected arguments (standard):
#    $1 = IP (nnn.nnn.nnn.nnn notation or hostname)
#    $2 = port (decimal, host byte order)
#

# This script has had limited testing to indicate it's function is similar (if not precisely) to the v10.0.1 monitor:
#
#   1) requires port connection
#   2) requires SSL negotiation
#   3) does NOT require any data after SSL negotiation -- i.e.:  no send string, and no receive string
#
# NOTE:  Has not been written to support IPv6.  At minimum would need to verify openssl's IPv6 support and connection syntax.
#   Check for comments:  IPv6 support

me=`basename $0`

pidfile="/var/run/$me-$1:$2.pid"

if [ -f "$pidfile" ]; then
   kill -9 `cat $pidfile` > /dev/null 2>&1
fi

echo "$$" > $pidfile

# IPv6 support 1/2:  Here is where IPv6 support is stripped out
#   an if check for ::ffff: would do it:
#      * if matched, strip as shown
#      * else do nothing & pass through as a true IPv6 address
node_ip=`echo $1 | sed 's/::ffff://'`
pm_port="$2"

# IPv6 support 2/2:  Need to determine valid openssl syntax for connecting to an IPv6 address
#   (may work as-is, but the ':' port demarcation is a concern)
echo 'QUIT'|openssl s_client -verify 1 -connect $node_ip:$pm_port >/dev/null 2>&1

# Mark node up if expected response was received
if [ $? -eq 0 ]
then
    # Remove the PID file
    rm -f $PIDFILE

    # Echo anything to STDOUT to indicate success
    echo "UP"
else
    # Remove the PID file and exit
    rm -f $PIDFILE
    exit
fi
Published Mar 12, 2015
Version 1.0

Was this article helpful?

2 Comments

  • RLU5's avatar
    RLU5
    Icon for Altostratus rankAltostratus

    Hi Dr. A

     

    I am pretty new to external monitoring. Sorry to ask dummy question. Trying to use this code to do a health check via our websense proxy with the following configurtion and failed. Wondering what would be the correct arguments and variables. Thanks.

     

    Richard

     

  • I think you don't need arguments. Host and Port are always sent as the first two arguments. However the script didn't work for me like that. When I pasted the openssl command into bash, I always got return code 1, whether the connection succeeded or not. My quick and dirty fix was to change the line with the openssl command like this:

    echo 'QUIT'|openssl s_client -verify 1 -connect $node_ip:$pm_port  | grep "Verify return code: 0" >/dev/null 2>&1

    Now I get a 0 if "Verify return code: 0" is found, and a 1 if it is not found.