Forum Discussion

RiverFish's avatar
RiverFish
Icon for Altostratus rankAltostratus
Jun 04, 2014

Turn off client auth if uri equals

Customers connect to one IP. They connect with an app, not a browser. They use port 5443 to register (obtain a cert we issue) for the service, and port 443 for the actual service.

register: https://5.5.5.5:5443/register (1-way ssl) (get the cert)

service: https://5.5.5.5/service (2-way ssl) (use the cert)

We have a registration VS and a service VS. Both VS's have their own client ssl profile (no server profile). Below are the differences in the ssl profiles:

I am tasked with getting rid of port 5443 and making it so that customers can both register and hit the service on port 443/https. The majority of the traffic comes in on 443 with the occasional new customer registering first on 5443. The VS's point to different pools. We are running BIG-IP 11.3.0 Build 2806.0 Final.

With that being said, I'd like to make the 2-way ssl profile the default and turn off client auth if uri equals "/register". Here is what I have so far...

when HTTP_REQUEST {
     Turn off client auth for registration requests
    if { [HTTP::uri] contains "/register" } {
      SSL::cert mode ignore
      SSL::renegotiate enable
      SSL::renegotiate
      pool registration-pool  
      return
    } elseif { ([SSL::cert count] > 0) && ([HTTP::uri] contains "/service") } {
     scrub header
      HTTP::header remove chain
      HTTP::header remove client
      HTTP::header remove testCert
      HTTP::header remove ClientCert-Subject
      HTTP::header remove SSLClientCertSubject
      HTTP::header remove SSLClientCertThumbprint
       insert cert subject
      HTTP::header insert SSLClientCertSubject [X509::subject [SSL::cert 0]]
    } else {
      drop
    }
}

Concerns:

  • When to use "return" and "SSL::renegotiate"
  • Security (ssl renegotiate vulnerabilities)
  • Do I need to set variables?

Very grateful for any help on this. Thanks.

19 Replies

  • Try this (minor update):

    when CLIENTSSL_CLIENTCERT {
        if { [SSL::cert count] < 1 } {
             if the client did not present a certificate - fail
            reject
        } else {
             do something with the cert here
        }
        HTTP::release
    }
    when CLIENT_ACCEPTED {
        set default_pool [LB::server pool]
    }
    when HTTP_REQUEST {
         non-registration URI space requested and F5AUTH cookie does not exist - prompt for client certificate
        if { not ( [HTTP::uri] equals "/favicon.ico" ) and not ( [HTTP::uri] starts_with "/register" ) and not ( [HTTP::cookie exists F5AUTH] ) } { 
             invalidate SSL and renegotiate
            HTTP::collect
            SSL::session invalidate
            SSL::authenticate always
            SSL::authenticate depth 9
            SSL::cert mode require
            SSL::renegotiate
        } elseif { [HTTP::cookie exists F5AUTH] } {
             F5AUTH cookie exists - send HTTP header data
            if { [table lookup -subtable CERTDATA [HTTP::cookie value F5AUTH]] ne "" } {
                HTTP::header replace X-CLIENT-CERT [table lookup -subtable CERTDATA [HTTP::cookie value F5AUTH]] 
            }
        }
    
        if { [HTTP::uri] starts_with "/register" } {
            pool local-pool-2
        } else {
            pool $default_pool
        }
    }
    when HTTP_REQUEST_SEND {    
        clientside {
            if { ( [SSL::cert count] > 0 ) and not ( [HTTP::cookie exists F5AUTH] ) } {
                 generate a GUID
                set uniqueid "_[string range [AES::key 256] 34 end]"
    
                 add cert to local session table
                table add -subtable CERTDATA $uniqueid [X509::subject [SSL::cert 0]]
            }
        }
    }
    when HTTP_RESPONSE {
         if the uniqueid variable is set - send the F5AUTH cookie to client
        if { [info exists uniqueid] } {
            HTTP::header insert "Set-Cookie" "F5AUTH=$uniqueid; path=/; secure; HTTPOnly"
            unset uniqueid
        }
    }
    
    • iamsajjad's avatar
      iamsajjad
      Icon for Cirrus rankCirrus

      Dumb questions:

      1. Whats' the purpose of HTTP::release? What's the harm not adding this?

      2. Why should we do SSL::session invalidate when we are doing SSL::renogotiate

      3. What happens if we skip add F5AUTH Cookie portions? clientside if SSL::cert count > 1 we insert filed values

      Thank you! 

  • This sort of thing becomes a bit easier with APM, but here's an iRule that comes close to the same functionality:

    when CLIENTSSL_CLIENTCERT {
        if { [SSL::cert count] < 1 } {
             if the client did not present a certificate - fail
            reject
        } else {
             do something with the cert here
        }
        HTTP::release
    }
    when HTTP_REQUEST {
         non-registration URI space requested and F5AUTH cookie does not exist - prompt for client certificate
        if { not ( [HTTP::uri] equals "/favicon.ico" ) and not ( [HTTP::uri] starts_with "/register" ) and not ( [HTTP::cookie exists F5AUTH] ) } { 
             invalidate SSL and renegotiate
            HTTP::collect
            SSL::session invalidate
            SSL::authenticate always
            SSL::authenticate depth 9
            SSL::cert mode require
            SSL::renegotiate
        } elseif { [HTTP::cookie exists F5AUTH] } {
             F5AUTH cookie exists - send HTTP header data
            if { [table lookup -subtable CERTDATA [HTTP::cookie value F5AUTH]] ne "" } {
                HTTP::header replace X-CLIENT-CERT [table lookup -subtable CERTDATA [HTTP::cookie value F5AUTH]] 
            }
        }
    }
    when HTTP_REQUEST_SEND {    
        clientside {
            if { ( [SSL::cert count] > 0 ) and not ( [HTTP::cookie exists F5AUTH] ) } {
                 generate a GUID
                set uniqueid "_[string range [AES::key 256] 34 end]"
    
                 add cert to local session table
                table add -subtable CERTDATA $uniqueid [X509::subject [SSL::cert 0]]
            }
        }
    }
    when HTTP_RESPONSE {
         if the uniqueid variable is set - send the F5AUTH cookie to client
        if { [info exists uniqueid] } {
            HTTP::header insert "Set-Cookie" "F5AUTH=$uniqueid; path=/; secure; HTTPOnly"
            unset uniqueid
        }
    }
    

    Configure your client SSL profile to ignore client certificates. The above iRule will trigger an SSL renegotiation for any URL that isn't in the /register namespace, and if the F5AUTH cookie does not exist. Once the client has presented a certificate, the F5AUTH cookie is written to the client so that the routine is never triggered again. The iRule also creates a session table entry, based on the unique ID of the F5AUTH cookie, so that you can store certificate data for later requests.

  • The problem here is you want to make layer 7 decisions on something that has happened in the past. The SSL connection has already been negotiated. My recommendation is when you make your decision, redirect them to virtual servers which have the SSL setup the way you want it.

    when HTTP_REQUEST {
      if {not ([HTTP::uri] eq "/register")} {
        HTTP::redirect "https://[HTTP::host]:4443/"
      }
    }
    

    Then on port 4443 setup a virtual server with 2way SSL to the same backend pool.

  • Does anyone have any suggestions? Is setting the 2-way ssl profile to "ignore" going to work? Currently it does not appear to work.

     

  • Just brainstorming here.... you will have to use a less secure profile until they come in then change the profile and force a SSL::renegotiate if they are not using /register. Something like...

    when HTTP_REQUEST {
      if {!([HTTP::uri] eq "/register")} {
        SSL::profile ssl_2way
        SSL::renegotiate
      }
    }
    

    But this will make normal connections take longer to establish as a result because its a two step process for them instead of one.

    • RiverFish's avatar
      RiverFish
      Icon for Altostratus rankAltostratus
      Thanks for your response, Kevin. I believe the "SSL::profile" command cannot be used in the "when HTTP_REQUEST" event, so I have shifted focus to the "SSL::mode ignore" command. We tested my iRule above and the ignore command is not working as hoped. The 2-way ssl profile that is assigned to the Virtual Server is requesting a cert from the client. Followed by a "Warning, No Certificate", then "Fatal, Handshake Failure".