Forum Discussion

Juha__Ranta_106's avatar
Juha__Ranta_106
Icon for Nimbostratus rankNimbostratus
Mar 10, 2005

Requiring an SSL Certificate for Parts of an Application and HTTP::header insert..

Hi!

 

 

I have problems inserting SSL client x509 certificate information into HTTP::header's when client SSL certificate is required only for certain parts of an application (https virtual server).

 

 

The problem is, that in HTTP_REQUEST event we check if client SSL certificate is needed...

 

 

if { $gotcert == 0 and [HTTP::uri] startswith "/needcert"') {

 

..

 

}

 

 

When this condition is true, we have not yet received SSL client cert but we sure would like to get one, thus we have to set 'SSL::cert mode require' and ask for 'SSL::renegotiate' ...

 

 

Now CLIENTSSL_HANDSHAKE event is re-evaluated and client supplies it's client x509 certificate (or does not) and we either:

 

 

- reject the request when no SSL::cert is supplied but $needcert==1

 

 

or

 

 

- accept the certificate and call HTTP::release.

 

 

 

As a result of all this, when '/needcert' URL is requested (for the VERY FIRST TIME by the client) [SSL::cert count] == 0 when HTTP_REQUEST event is evaluated and there is no X509 certificate to be used in those 'HTTP::header insert [X509::.. ]' commands (at the end of the HTTP_REQUEST event -- see the iRule below).

 

 

But, if the client re-requests the page, the SSL session gets reused

 

and client certificate is present when HTTP_REQUEST event is evaluated and everything works the way I want..

 

 

Is there some way to work-around this problem?

 

 

eg. Is it somehow possible to call HTTP::header insert functions in CLIENTSSL_CLIENTCERT or CLIENTSSL_HANDSHAKE events?

 

 

Currently i cannot call command 'HTTP::header insert ' in CLIENTSSL_HANDSHAKE event, but I get a iRule compilation error:

 

 

[command is not valid in current event context (CLIENTSSL_HANDSHAKE)] [HTTP::header insert ...]

 

 

-------------------------

 

 

SEE ALSO:

 

http://devcentral.f5.com/Default.aspx?TabID=29&newsType=ArticleView&articleId=39

 

 

--- here is my iRule ---

 

 

rule https-needcert {

 

 

when CLIENT_ACCEPTED {

 

set needcert 0;

 

set gotcert 0;

 

}

 

 

when CLIENTSSL_HANDSHAKE {

 

log LOCAL0.warn "cert count=[SSL::cert count] result=[SSL::verify_result]";

 

if { [SSL::cert count] == 0 or [SSL::verify_result] != 0 } {

 

log LOCAL0.warn "Bad cert!"

 

if { $needcert == 1 } {

 

reject

 

}

 

} else {

 

log LOCAL0.warn "Good cert! ($needcert)"

 

set gotcert 1

 

set crt [SSL::cert 0];

 

if { $needcert == 1 } {

 

HTTP::header insert "X-ENV-SSL_CLIENT_S_DN" [X509::subject $crt];

 

HTTP::release

 

}

 

}

 

}

 

 

when HTTP_REQUEST {

 

if { $gotcert == 0 and [HTTP::uri] starts_with "/needcert" } {

 

log LOCAL0.warn "Requiring certificate..."

 

HTTP::collect

 

SSL::cert mode require

 

SSL::renegotiate

 

set needcert 1

 

} else {

 

log LOCAL0.warn "No cert needed."

 

}

 

if { [SSL::cert count] > 0 } {

 

set crt [SSL::cert 0];

 

HTTP::header insert "X-ENV-SSL_CLIENT_S_DN" [X509::subject $crt];

 

HTTP::header insert "X-ENV-SSL_CLIENT_I_DN" [X509::issuer $crt];

 

HTTP::header insert "X-ENV-SSL_CLIENT_M_SERIAL" [X509::serial_number $crt];

 

HTTP::header insert "X-ENV-SSL_CLIENT_V_START" [X509::not_valid_before $crt];

 

HTTP::header insert "X-ENV-SSL_CLIENT_V_END" [X509::not_valid_after $crt];

 

HTTP::header insert "X-ENV-SSL_CLIENT_CERTIFICATE" [X509::whole $crt];

 

}

 

}

 

}
  • drteeth_127330's avatar
    drteeth_127330
    Historic F5 Account
    No, it is not possible to examine or insert HTTP headers from the SSL rule events. The brief explanation is that the SSL handshake must complete before the HTTP headers are sent. I think you can solve your problem by using HTTP::redirect back to the same HTTP::uri if the client certificate is required but not present. This will force the client to retry the request. Some care should be taken to ensure that you don't create a redirect loop in the event that the client continues to connect without a certificate. There's no really good way to do this. Your best bet is probably to add a parameter to the querystring of the redirect URI. Hope this helps...
  • I had the HTTP::redirect in mind, but I think it can be little

     

    dangerous as it is not guaranteed that the client would re-use the

     

    SSL session.

     

     

    The HTTP::redirect loop could be avoided by adding some artificial request parameter to indicate that this 'ssl-renegotiate-workaround=1' BUT as the

     

    original HTTP request method for /needcert might be a POST its starting to

     

    get nasty if we need to include those POST http-request paramaters into the same HTTP::redirect 'Location: ' headers as well..