For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Request Client Certificate And Pass To Application

Problem this snippet solves:

We are using BigIP to dynamically request a client certificate. This example differs from the others available in that it actually passes the x509 certificate to the server for processing using a custom http header.

The sequence of event listeners required to accomplish this feat is: HTTP_REQUEST, which invokes CLIENTSSL_HANDSHAKE, which is followed by HTTP_REQUEST_SEND

The reason is that CLIENTSSL_HANDSHAKE occurs after HTTP_REQUEST event is processed entirely, but HTTP_REQUEST_SEND occurs after it. The certificate appears in PEM encoding and is slightly mangled; you need to emit newlines to get back into proper PEM format:

-----BEGIN CERTIFICATE------
Mabcdefghj...
-----END CERTIFICATE-----

This certificate can be converted to DER encoding by jettisoning the BEGIN and END markers and doing base64 decode on the string.

Code :

# Initialize the variables on new client tcp session.
when CLIENT_ACCEPTED {
    set collecting 0
    set renegtried 0
}

# Runs for each new http request
when HTTP_REQUEST {
    # /_hst name and ?_hst=1 parameter triggers client cert renegotiation
    if { $renegtried == 0
         and [SSL::cert count] == 0
         and    ([HTTP::uri] matches_regex {^[^?]*/_hst(\?|/|$)} 
              or [HTTP::uri] matches_regex {[?&]_hst=1(&|$)}) } {

# Collecting means buffering the request. The collection goes on
# until SSL::renegotiate occurs, which happens after the HTTP
# request has been received. The maximum data buffered by collect
# is 1-4 MB.
HTTP::collect
        set collecting 1
        SSL::cert mode request
        SSL::renegotiate
    }
}

# After a handshake, we log that we have tried it. This is to prevent
# constant attempts to renegotiate the SSL session. I'm not sure of this
# feature; this may in fact be a mistake, but we can change it at any time.
# It is transparent if we do: the connections only work slower. It would,
# however, make BigIP detect inserted smartcards immediately. Right answer
# depends on the way the feature is used by applications.
when CLIENTSSL_HANDSHAKE {
    if { $collecting == 1 } {
        set renegtried 1
# Release allows the request processing to occur normally from this
# point forwards. The next event to fire is HTTP_REQUEST_SEND.
HTTP::release
    }
}

# Inject headers based on earlier renegotiations, if any.
when HTTP_REQUEST_SEND {
    clientside {
# Security: reject any user-submitted headers by our magic names.
HTTP::header remove "X-ENV-SSL_CLIENT_CERTIFICATE"
HTTP::header remove "X-ENV-SSL_CLIENT_CERTIFICATE_FAILED"

# if certificate is available, send it. Otherwise, send a header
# indicating a failure, if we have already attempted a renegotiate.
if { [SSL::cert count] > 0 } {
    HTTP::header insert "X-ENV-SSL_CLIENT_CERTIFICATE" [X509::whole [SSL::cert 0]]
} elseif { $renegtried == 1 } {
    # This header has some debug value: if the FAILED header is not
    # present, BigIP is probably not configured to do client certs
    # at all.
    HTTP::header insert "X-ENV-SSL_CLIENT_CERTIFICATE_FAILED" "true"
}
    }
}
Published Mar 18, 2015
Version 1.0

3 Comments

  • I am no expert here, but I have an issue with the comment above:

     

    "The reason is that CLIENTSSL_HANDSHAKE occurs after HTTP_REQUEST event is processed entirely, but HTTP_REQUEST_SEND occurs after it."

     

    This does not seem accurate based on my personal observations that I am able to set variables in CLIENTSSL_HANDSHAKE and access them in HTTP_REQUEST. This "request after handshake" event order is also supported by the excellent event flowchart here: https://devcentral.f5.com/s/feed/0D51T00006i7XB2SAM

     

    Thanks.

     

  • I think the OP meant that in the context of -this rule- the HTTP_REQUEST event handler fires before the CLIENTSSL_HANDSHAKE due to the SSL::renegotiate command.

    • John_Alam's avatar
      John_Alam
      Icon for Employee rankEmployee

      This is correct.  The SSL::renegotiate sparks a new TLS handshake but this time the server "requests" a client cert according to this irule.  They could have "requested and required it" but they chose to just request it.
      This causes the browser to prompt the user to choose a client certificate from it store to submit.  After that a new HTTP request is also submitted.
      HTH