Jump to content
Welcome to our new Citrix community!

IP reputation responder policy for client source IP evaluation and "X-Forwarded-For" http header evaluation issues. IP responder category question


Josh Slaney

Recommended Posts

Hello,  I'm building individual responder policies each category of IP reputation.  I'm referencing this documentation here:

https://docs.citrix.com/en-us/citrix-adc/current-release/reputation/ip-reputation.html

Example 1:

The following command creates a policy that identifies malicious IP addresses and block the request if a match is triggered:

add appfw policy pol1 CLIENT.IP.SRC.IPREP_IS_MALICIOUS APPFW_BLOCK

Example 2:

The following command creates a policy that uses the reputation service to check the client IP address in the X-Forwarded-For header and reset the connection if a match is triggered.

> add appfw policy pol1 "HTTP.REQ.HEADER(\"X-Forwarded-For\").TYPECAST_IP_ADDRESS_AT.IPREP_IS_MALICIOUS" APPFW_RESET**

 

I've created a responder policy that looks at the Source Client IP OR the http request header "X-Forwarded-For" for the evaluation:

CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES)||HTTP.REQ.HEADER("X-Forwarded-For").TYPECAST_IP_ADDRESS_AT.IPREP_THREAT_CATEGORY(SPAM_SOURCES)

 

My responder policy is coming up with "Undefined hits". If I change the policy to CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES) only it works fine.  I believe the undefined hits occur when there is no actual "X-Forwarded-For" header in a request.  I tested this by sending a curl request with the header and without the header.  Without the header I see the undefined hit counter increasing.  What is the proper way to setup a responder policy to look at the client Source IP or the X-Forwarded-For header?  I realize the documentation above shows examples of AppFW policy vs responder.

 

I believe I've been able to get this to work by setting up a conditional policy that looks at the (client IP source) OR  (If the HTTP Header exists and the IP is in the spam category)

CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES)||(HTTP.REQ.HEADER("X-Forwarded-For").EXISTS&&HTTP.REQ.HEADER("X-Forwarded-For").TYPECAST_IP_ADDRESS_AT.IPREP_THREAT_CATEGORY(SPAM_SOURCES))

 

When I set this policy, I no longer see the undefined hits counter progressing.  Is this a good way to set these policies up?

 

 

 

Link to comment
Share on other sites

First:  Explanation for your original issue:

You can't do a comparison if the header doesn't exist.  

When an expresion evaluates TRUE, take the action specified.

When an expression evaluates FALSE, policy doesn't apply.

When an expression is "UNDEFINED" meaning the fundamental evaluation being made "does not compute", then the "undefined result" action is taken. Usually this is a variation of do nothing: no-operation (noop) for responder, no-rewrite for rewrite, and bypass for appfw or block for appfw (depending on setting).  Its a safety for when the expression doesn't make since.

 

So, in your first example, trying to typecast a header to compare if it contains a value is UNDEFINED when the header doesn't exist at all (or had a bad data type).

Classic example is taking a host header and typecasting to ip to see if in subnet...but the user passed in a fqdn, so the typecast fails.  Similar thing here, you are trying to say typecast this header and compare to this other thing and the system says "undefined" since it doesn't return null or zero or "" something that could make a true/false evaluation.

 

Next:  Your correction, does work for following reasons (well, mostly; you need some parentheses to guarantee logic)

Basically, your modified expression checks for the header existing before the typecast can run. (There are other ways to do it; but this can work as well; but I would adjust the parentheses as I'll not below.)

 

Now, in basic compound expressions in boolean logic, consider the following:

Example 1:  A && B

In order for this to result in true the A expression AND the B expression have to be "true".

When compilers (and the policy engine) evaluate this, they can take a shortcut. If the "A" expression is ever false, they never have to attempt the "B" evaluation as "false" && <anything> will never be true.  

So, your header.exists clause if false, means it never attempts the typecast if the header is NOT present. Avoiding your "undefined" result scenario.

 

Example 2:  A || B

Similar things happen here, if A returns TRUE, we never have to attempt to resolve B at all as if A is true, B can be true or false it doesn't change the evaluation.  The B expression is only tested, if A is false.

 

Excessive nerding out on boolean logic follows:

For guaranteed evaluation, you should use parentheses to guarantee order of operations (or to prevent unexpected evaluations if you modify stuff later).

For clarify ("safety"):  I would write the expression as:  A || (B && C), because that's really what you are enforcing.

While order of operations will result in the "AND" taking precedence over the "A" clause, if you add stuff to this you could end up unexpected results.  

It also, only works if your "B" phrase is the "exists" test.  

Without the parentheses it does work as expected, but if you did something like A || B && C || D  things get interesting.  The parentheses disambiguate in the same way that if I wrote:

A - B+C  (the addition takes precedence over the minus), but writing A- (B+C) guarantees what we want.

 

 

So, this as written works for your intended purpose, but later modifications or your changes could result in a different behavior.  

CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES)||(HTTP.REQ.HEADER("X-Forwarded-For").EXISTS&&HTTP.REQ.HEADER("X-Forwarded-For").TYPECAST_IP_ADDRESS_AT.IPREP_THREAT_CATEGORY(SPAM_SOURCES))

 

Modifying it to this (with parentheses), would help guarantee behavior if you need to make additional modifications:

(CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES)) || ((HTTP.REQ.HEADER("X-Forwarded-For").EXISTS&&HTTP.REQ.HEADER("X-Forwarded-For").TYPECAST_IP_ADDRESS_AT.IPREP_THREAT_CATEGORY(SPAM_SOURCES)))

 

Finally, is there another way to do this?

There is. But this method does work and shouldn't be that costly to proceess.

You could use content switching to sort traffic with "x-forwarded-for" header to lb_vsrv_demo1 with the header based expression on it; and the other if "header" doesn't exist cs policy to direct traffic to lb_vsrv_demo2 for the alternate filter policy, if needed.

 

If you have a lot of complicated scenarios and filters where this would be more flexible than the complex policy scenario, then it might be useful. (such as if you want more filter categories)

If this is really the end of your threat category types, then your current method is usable.

There might be a slight performance benefit of one over the other; but I don't have a way to confirm (though I would guess cs might be more efficient; but I wouldn't be surprised if the opposite is true.)  

 

Bottom line, you solved this the "correct" way because it does work and it works for the right reasons. There are other ways to approach it too. But your choice.

 

Hope this explanation helps, though.

 

 

 

 

 

 

  • Like 3
Link to comment
Share on other sites

17 hours ago, Rhonda Rowland1709152125 said:

First:  Explanation for your original issue:

You can't do a comparison if the header doesn't exist.  

When an expresion evaluates TRUE, take the action specified.

When an expression evaluates FALSE, policy doesn't apply.

When an expression is "UNDEFINED" meaning the fundamental evaluation being made "does not compute", then the "undefined result" action is taken. Usually this is a variation of do nothing: no-operation (noop) for responder, no-rewrite for rewrite, and bypass for appfw or block for appfw (depending on setting).  Its a safety for when the expression doesn't make since.

 

So, in your first example, trying to typecast a header to compare if it contains a value is UNDEFINED when the header doesn't exist at all (or had a bad data type).

Classic example is taking a host header and typecasting to ip to see if in subnet...but the user passed in a fqdn, so the typecast fails.  Similar thing here, you are trying to say typecast this header and compare to this other thing and the system says "undefined" since it doesn't return null or zero or "" something that could make a true/false evaluation.

 

Next:  Your correction, does work for following reasons (well, mostly; you need some parentheses to guarantee logic)

Basically, your modified expression checks for the header existing before the typecast can run. (There are other ways to do it; but this can work as well; but I would adjust the parentheses as I'll not below.)

 

Now, in basic compound expressions in boolean logic, consider the following:

Example 1:  A && B

In order for this to result in true the A expression AND the B expression have to be "true".

When compilers (and the policy engine) evaluate this, they can take a shortcut. If the "A" expression is ever false, they never have to attempt the "B" evaluation as "false" && <anything> will never be true.  

So, your header.exists clause if false, means it never attempts the typecast if the header is NOT present. Avoiding your "undefined" result scenario.

 

Example 2:  A || B

Similar things happen here, if A returns TRUE, we never have to attempt to resolve B at all as if A is true, B can be true or false it doesn't change the evaluation.  The B expression is only tested, if A is false.

 

Excessive nerding out on boolean logic follows:

For guaranteed evaluation, you should use parentheses to guarantee order of operations (or to prevent unexpected evaluations if you modify stuff later).

For clarify ("safety"):  I would write the expression as:  A || (B && C), because that's really what you are enforcing.

While order of operations will result in the "AND" taking precedence over the "A" clause, if you add stuff to this you could end up unexpected results.  

It also, only works if your "B" phrase is the "exists" test.  

Without the parentheses it does work as expected, but if you did something like A || B && C || D  things get interesting.  The parentheses disambiguate in the same way that if I wrote:

A - B+C  (the addition takes precedence over the minus), but writing A- (B+C) guarantees what we want.

 

 

So, this as written works for your intended purpose, but later modifications or your changes could result in a different behavior.  

CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES)||(HTTP.REQ.HEADER("X-Forwarded-For").EXISTS&&HTTP.REQ.HEADER("X-Forwarded-For").TYPECAST_IP_ADDRESS_AT.IPREP_THREAT_CATEGORY(SPAM_SOURCES))

 

Modifying it to this (with parentheses), would help guarantee behavior if you need to make additional modifications:

(CLIENT.IP.SRC.IPREP_THREAT_CATEGORY(SPAM_SOURCES)) || ((HTTP.REQ.HEADER("X-Forwarded-For").EXISTS&&HTTP.REQ.HEADER("X-Forwarded-For").TYPECAST_IP_ADDRESS_AT.IPREP_THREAT_CATEGORY(SPAM_SOURCES)))

 

Finally, is there another way to do this?

There is. But this method does work and shouldn't be that costly to proceess.

You could use content switching to sort traffic with "x-forwarded-for" header to lb_vsrv_demo1 with the header based expression on it; and the other if "header" doesn't exist cs policy to direct traffic to lb_vsrv_demo2 for the alternate filter policy, if needed.

 

If you have a lot of complicated scenarios and filters where this would be more flexible than the complex policy scenario, then it might be useful. (such as if you want more filter categories)

If this is really the end of your threat category types, then your current method is usable.

There might be a slight performance benefit of one over the other; but I don't have a way to confirm (though I would guess cs might be more efficient; but I wouldn't be surprised if the opposite is true.)  

 

Bottom line, you solved this the "correct" way because it does work and it works for the right reasons. There are other ways to approach it too. But your choice.

 

Hope this explanation helps, though.

 

 

 

 

 

 

Rhonda,

Thanks for the excellent detailed response! I'll include the brackets that you suggest.  I don't see myself modifying these policies moving forward, but you never know how that goes.  

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...