Forum Discussion

Mr_Moody's avatar
Mr_Moody
Icon for Nimbostratus rankNimbostratus
Oct 30, 2020

Help with IRule Client Auth Certs

I have an IRule below that I'm working on. My intent is to enforce client certificate authentication on two URI's only and if any client certificate errors should result in a redirect to a custom error page. The VS has a SSL profile set to 'ignore' client certificates. This rule successfully processes the first two 'If' statements. The problem is that the custom error page is not displayed. Any help would be appreciated.

when HTTP_REQUEST {
	if { not ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
			return
	} else {

if { ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
		SSL::session invalidate
    SSL::authenticate always
    SSL::authenticate depth 9
    SSL::cert mode require
    set cmd "SSL::profile /Common/require_clientssl"
    eval $cmd
    SSL::renegotiate
 	} else {
if {[SSL::verify_result] == 0 }{
			return			
  } else {
HTTP::respond 302 Location "http://error.com/error.html" Cache-Control No-Cache Pragma No-Cache
}
}
}
}

I have another rule that meets two of the three requirements. With the VS SSL profile set to 'request' client certs. This rule does provide a custom error page when Cert errors occur on the two named URI's. But it also results in a certificate prompt for users who access other URI's due to the default SSL profile setting.

when HTTP_REQUEST {
if { not ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
			return
	} else {
if {[SSL::verify_result] == 0 }{
			return			
  } else {
HTTP::respond 302 Location "http://error.com/error.html" Cache-Control No-Cache Pragma No-Cache
}
}
}

  • Your HTTP::respond 302 is in the else clause of your second if statement.

    It is never reached.

    when HTTP_REQUEST {
    	if { not ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
                            # This path has no client-authentication
    			return
    	} else {
          # perform client-authentication if the uri matches
          if { ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
    		SSL::session invalidate
            SSL::authenticate always
            SSL::authenticate depth 9
            SSL::cert mode require
            set cmd "SSL::profile /Common/require_clientssl"
            eval $cmd
            SSL::renegotiate
     	  } else {
            # we cannot execute this path because of the first *if* statement
            if {[SSL::verify_result] == 0 }{
    			return			
            } else {
              HTTP::respond 302 Location "http://error.com/error.html" Cache-Control No-Cache Pragma No-Cache
            }
          }
        }
    }

    Try something like

    when HTTP_REQUEST {
    	if { not ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
          # we don't need to renegotiate with client-authentication
    	  return
    	} else {
          # we do need to renegotiate with client-authentication
      	  SSL::session invalidate
          SSL::authenticate always
          SSL::authenticate depth 9
          SSL::cert mode require
          set cmd "SSL::profile /Common/require_clientssl"
          eval $cmd
          SSL::renegotiate
          # check if renegotiation with client-auth succeeded
          if {[SSL::verify_result] == 0 }{
    		return			
          } else {
            HTTP::respond 302 Location "http://error.com/error.html" Cache-Control No-Cache Pragma No-Cache
          }
    	}
    }

    but I haven't tested this to check ...

  • Your HTTP::respond 302 is in the else clause of your second if statement.

    It is never reached.

    when HTTP_REQUEST {
    	if { not ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
                            # This path has no client-authentication
    			return
    	} else {
          # perform client-authentication if the uri matches
          if { ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
    		SSL::session invalidate
            SSL::authenticate always
            SSL::authenticate depth 9
            SSL::cert mode require
            set cmd "SSL::profile /Common/require_clientssl"
            eval $cmd
            SSL::renegotiate
     	  } else {
            # we cannot execute this path because of the first *if* statement
            if {[SSL::verify_result] == 0 }{
    			return			
            } else {
              HTTP::respond 302 Location "http://error.com/error.html" Cache-Control No-Cache Pragma No-Cache
            }
          }
        }
    }

    Try something like

    when HTTP_REQUEST {
    	if { not ([HTTP::uri] starts_with "/uri1") || ([HTTP::uri] starts_with "/uri2")} {
          # we don't need to renegotiate with client-authentication
    	  return
    	} else {
          # we do need to renegotiate with client-authentication
      	  SSL::session invalidate
          SSL::authenticate always
          SSL::authenticate depth 9
          SSL::cert mode require
          set cmd "SSL::profile /Common/require_clientssl"
          eval $cmd
          SSL::renegotiate
          # check if renegotiation with client-auth succeeded
          if {[SSL::verify_result] == 0 }{
    		return			
          } else {
            HTTP::respond 302 Location "http://error.com/error.html" Cache-Control No-Cache Pragma No-Cache
          }
    	}
    }

    but I haven't tested this to check ...

    • Mr_Moody's avatar
      Mr_Moody
      Icon for Nimbostratus rankNimbostratus

      Thank you that was it! I also had to change the cert mode to 'request'.