Catch SSL Errors and return a friendly page...

Problem this snippet solves:

A requirement arose whereby we needed to return a more friendly page to users who were attempting to connect to our services, but didn't have the relevant SSL requirements fulfilled. E.g. they weren't presenting a Client SSL certificate, or their certificate was expired.

The trick was to change the 'peer_cert_mode' from 'require' to 'request'. This allows the browser to open a Secure connection with the F5 and the F5 perform the required validation on the certificate being presented. Thanks to Hoolio and Nitass for their pointers...

The rule uses iFiles to get the error page being returned to the client. You need v11 for iFiles to work, but you should be able to replace the functionality with External files or inline HTML if you want to use this rule on v10 or earlier... As part of the rule, I've added a keyword called $failure_result. This stores the X509 Verify result string value. If you want to return this value to the client as part of the HTML, then simply add the variable somewhere within your HTML. The inline subst below will then replace it with the relevant failure string. A full list can be found on the 'SSL::Verify' page.

Comments/improvements always welcome.

Code :

when RULE_INIT {
# Debug Logging
set static::CatchSSLErrorsDebug 0
# Get the Error Page HTML
set static::error_page [ifile get error_page_html]
}

when HTTP_REQUEST priority 10 {

if {$static::CatchSSLErrorsDebug == 1} { log local0. "CatchSSLErrors HTTP Request:" }

# Hide the SSL:: command's from the iRule parser
# so the iRule can be used on a VS without a SSL profile. 
set cipher_cmd "SSL::cipher version"
set count_cmd "SSL::cert 0"
set verify_cmd "SSL::verify_result"
set failure_cmd "X509::verify_cert_error_string"

# Check if the client used an SSL cipher and it's not "none"
if {not ([catch {eval $cipher_cmd} result]) && $result ne "none"}{
# Client did use a cipher, verify certificate
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "SSL Connection initiated... Verifying client certificate." }

# Is there a client cert present?
catch {eval $count_cmd} count_result
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "SSL::cert returned the following output... '$count_result'" }

if {$count_result ne ""} {
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "Client cert presented. Verifying..." }

# Verify the certificate using SSL::verify_result
catch {eval $verify_cmd} verify_result
if { $verify_result == 0 } {
# Client Cert present and verified successfully. 
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "SSL::Verify_result was OK... '$verify_result' Continuing..." }
return
} else {
# Client cert verification error. 
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "SSL::Verify_result was NOT OK... '$verify_result'" }

# Get failure reason
catch {eval $failure_cmd $verify_result } failure_result
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "X509::verify_cert_error_string value is... '$failure_result'" }

# Respond to client and close connection.
HTTP::respond 403 content [subst $static::error_page] "Content-Type" "text/html" "Connection" "Close"
event disable
return
}
} else {
# No client cert provided. Return appropriate error. 
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "No Client cert presented. Returning error page..." }

# Set the failure string that would be set by X509::verify_cert_error_string above. 
set failure_result "No client certificate present"
# Return failure page to client and close connection. 
HTTP::respond 403 content [subst $static::error_page] "Content-Type" "text/html" "Connection" "Close"
event disable
return
}

} else {
# Client did not use a cipher, there must be HTTP connection. 
if {$static::CatchSSLErrorsDebug == 1} { log local0.info "Non-SSL Connection initiated... Continuing..." }
return
}

}
Published Mar 16, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment