HAProxy community

ACL condition with AND


#1

I would like to be able to do something like:

ACL myacl   hdr(Host) www.example.com && path /sample/path

Is there a way to do this? The only way to do this that I’ve been able to find is have multiple ACLs, and then combine them in a condition for an action. But that becomes very unwieldy if there are multiple cases for the ACL and/or if there are multiple actions that use the ACL (possibly combined with other ACLs or inline conditions).


#2

No, single ACL statements are always ORed.

The only thing closer to this are anonymous ACLs directly in the rule:

http-request deny if {hdr(Host) www.example.com} {path /sample/path }

#3

That can get difficult to maintain, when you have several actions for the same condition (for example setting multiple headers). If the condition changes, you then have to change it in several places instead of just one.


#4

Right, with inline/anonymous ACL’s you’d need to update every single statement on changes.

With named ACLs, imho mixing different match statements into a single ACL is actually more confusing then the alternative. I’d argue that a configuration like this is more readable, especially if used multiple times:

acl ACL_MATCH_EXAMPLE_COM hdr(Host) www.example.com
acl ACL_MATCH_SAMPLE_PATH path /sample/path

http-request deny if ACL_MATCH_EXAMPLE_COM ACL_MATCH_SAMPLE_PATH

As opposed to an ACL that would match multiple different types and AND’ing them (this doesn’t work):

acl ACL_MATCH_EXAMPLE_COM_AND_SAMPLE_PATH hdr(Host) www.example.com && path /sample/path
http-request deny if ACL_MATCH_EXAMPLE_COM_AND_SAMPLE_PATH 

A “meta” ACL that can combine multiple ACL’s (just like a condition today) would probably be a construct that it’s best suited for this. But I’m not sure if this is worth the effort.


#5

the example I gave was fairly simple, consider a more complex scenario where you have something like:

acl ACL_sample hdr(Host) special-domain
acl ACL_sample hdr(Host) domain1 && path /path1
acl ACL_sample hdr(Host) domain2 && path /path2
acl ACL_sample hdr(Host) domain3 && path /path3
acl ACL_sample hdr(Host) domain4 && path /path4
# ... etc."

http-request set-header Header1 value1 if ACL_sample
http-request set-header Header2 value2 if ACL_sample
http-request set-header Header2 value2 if ACL_sample
http-request set-header Header2 value2 if ACL_sample

(I have a real use case that isn’t too different than this)

This results in a very long condition that is repeated several times.

A “meta” ACL that can combine multiple ACL’s (just like a condition today) would probably be a construct that it’s best suited for this.

Sure that would work, or there could be a new matcher that would look like:

acl ACL_combined     condition ACL_some_domain ACL_some_path

or similar.


#6

I should also note that this particular example could also be improved if there was a single fetch to get the domain and path together without the query string. But afaik, that isn’t possible either. And there are other cases where this would be useful, matching on both the domain and path is just the most common that I’ve run into.


#7

Hi,

The ACLs are mixed with an AND logic when listed as requirements for actions.

So:
define ACL1 as criteria Alpha
define ACL1 again as criteria Beta
define ACL2 as criteria gamma

Require ACL1 and ACL2 for Action3

Action 3 will be done when Gamma AND (Alpha or Beta) is True

Good luck,


#8

@j_bourdeau I’m aware of that. The problem is that I end up with actions like:

http-request set-header My-Header1 value1 if ACL_domain1 ACL_path1 or ACL_domain2 ACL_path2 or ACL_domain3 or ACL_domain3 or ACL_domain3 ACL_path4 or ...

possibly with the condition repeated multiple times to perform multiple actions (say, setting multiple headers if a condition applies), and then if I need to add a new url to that condition, I have to make the change in several places.


#9

Hi again,

looks like you are trying to do the web site’s job at the proxy level. What is supposed to be done by the Web application should be done by that application and not compensated for by a proxy.

Also, the example you gave is again pretty basic. Either give the full extent of the problem so we can work it with you, or instead, we can only provide you with the same basic solutions.

From here, I guess your logic is wrong / over complicated. You should re-design your logic and I expect a simpler way will emerge from that.

Or as another solution, you could cascade multiple processing stages to compensate for your messy logic :
HAproxy receives the request on a first socket and routes the request based on a first criteria like domain. Requests for Domain1 are sent to local port 1, Domain2 is sent to port 2, Domain 3 is sent to port 3, each of them with the required header for the corresponding domain (Header Dx).

On the FrontEnd listening on Port 1, you don’t need to test for Domain1 anymore because you know it is set. It has been sorted already. So all you need now is to work based on path. Path A sent to port A, path B sent to port B, … again adding the proper Header Px.

On that third FrontEnd, do whatever you need and keep going as long as you need.

At the end of the maze, all your backends may go to the same place or to different places, as you need. But each query will have been modified according to the criteria from its corresponding HAProxy path routing.

Have fun reviewing your logic,


#10

@j_bourdeau you’re coming across as aggressive, please tone it down. Thanks!

It is, by using the base:

acl ACL_sample hdr(Host) special-domain
acl ACL_sample base domain1/path1
acl ACL_sample base domain2/path2
acl ACL_sample base domain3/path3
acl ACL_sample base domain4/path4

Does that cover your actual use-case?


#11

It is, by using the base

That’s helpful, thanks.

Here is a specific example, that base doesn’t solve (but only benefits from AND in acls a little):

We have a MATCH_allow-iframed ACL that has a whitelist of paths that are allowed to be iframed, and then the following rules:

http-response del-header X-Frame-Options
http-response set-header X-Frame-Options SAMEORIGIN if !MATCH_allow-iframed

But then we wanted to add rules that set X-Frame-Options for a certain path only if it doesn’t come from a certain referrer. In this case it is a single action that uses the ACL, so we could write something like:

http-response set-header X-Frame-Options SAMEORIGIN unless MATCH_allow-iframed or MATCH_path1 MATCH_referer1 or MATCH_path2 MATCH_referer2 or ...

But that leads to a very long condition. We eventually found a better solution, but it made me wonder if there was a better way to do something like that, and if not, why not.