Forum Discussion

Peter_Z's avatar
Peter_Z
Icon for Cirrus rankCirrus
Jun 17, 2019

Selective URL client cert authentication with OU check

I'm trying to write an iRule which requires client cert for selected URLs - similar to this one: https://devcentral.f5.com/s/articles/selective-client-cert-authentication.

I need to add one extra check though: when client provides the certificate, only clients with specific OU in the certificate (or the ones coming from certain source IP, which isn't relevant to the problem though described below) are allowed. It doesn't work, I see errors in the log when trying to call the certificate value. Although it should be possible to use certificate commands [X509::subject [SSL::cert 0]] or call the variable holding the same info in the HTTP_REQUEST event, everytime I try to call it from within Event HTTP_REQUEST I see TCL errors, even when I remove the OU check condition and just try to log the cert info. I am able to use the cert info in HTTP_REQUEST_SEND event though.

I am migrating this functionality from existing Apache Reverse Proxy config, where they achieved that quite easily, so there must be a way to replicate it to F5.

when CLIENTSSL_CLIENTCERT {
set cert_subject [X509::subject [SSL::cert 0]]
set cert_issuer [X509::issuer [SSL::cert 0]]

# release any stored data just in case
HTTP::release
# if there is still no cert after the SSL renegotiation kill the connection by sending a reset back to the client
if { [SSL::cert count] < 1 } {
reject
}
}
 
when HTTP_REQUEST {
set uri [HTTP::uri]
 
switch -glob $uri {
"/test1/*" {
if { [SSL::cert count] <= 0 } {
HTTP::collect
SSL::authenticate always
SSL::authenticate depth 10
SSL::cert mode require
SSL::renegotiate

 
if { ($cert_subject contains "OU=Department1") || (($cert_subject contains "OU=Department2") && ($cert_subject contains "OU=PROD")) || ( [IP::addr [getfield [IP::client_addr] "%" 1] equals 192.168.20.1 ] ) } {
pool pool1
} else {
reject
}
}
 
}
}
}


1 Reply

  • Hi

    I've just re-ordered your logic slightly, hopefully it helps a little

    when CLIENTSSL_CLIENTCERT {
    	set cert_subject [X509::subject [SSL::cert 0]]
    	set cert_issuer [X509::issuer [SSL::cert 0]]
    		if { ($cert_subject contains "OU=Department1") || (($cert_subject contains "OU=Department2") && ($cert_subject contains "OU=PROD")) || ( [IP::addr [getfield [IP::client_addr] "%" 1] equals 192.168.20.1 ] ) } {
    		pool localhost_http
    		} else {
    		reject
    	}
    # if there is still no cert after the SSL renegotiation kill the connection by sending a reset back to the client
    	if { [SSL::cert count] < 1 } {
    	reject
    	}
    }
     
    when HTTP_REQUEST {
    	switch -glob [HTTP::uri] {
    		"/test1/*" {
    			if { [SSL::cert count] <= 0 } {
    			SSL::authenticate always
    			SSL::authenticate depth 10
    			SSL::cert mode require
    			SSL::renegotiate
    			}
    		}
    	}
    }