Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

source.remote_address well known attribute #111

Merged
merged 11 commits into from
Oct 18, 2024
Merged

source.remote_address well known attribute #111

merged 11 commits into from
Oct 18, 2024

Conversation

eguzki
Copy link
Contributor

@eguzki eguzki commented Oct 15, 2024

What

This PR introduces a new Well Known Attribute called source.remote_address.

Why

Envoy's source.address (well known) attribute includes the port as IP:PORT. It is desirable to run rate limiting or auth policies relying only on the client address. Hence, this source.remote_address can be used to implement those policies.

Attribute Value

This attribute evaluates to the trusted client address (IP address without port) as it is being defined by Envoy Doc.

In summary, the trusted client address is calculated as follows: By default, Envoy gateway instances are configured to append the source IP address of the immediate downstream node’s connection of the incoming HTTP connections to the X-Forwarded-For header. By default Envoy sets the number of trusted hops to 0,
indicating that Envoy should use the address the connection is opened from,
rather than a value inside the X-Forwarded-For list.
Increasing this count will have Envoy use the nth value from the list, counting from the right.

Where can I use it?

This attribute can be used on selector fields.

As conditions:

- conditions:
  - allOf:
    - selector: source.remote_address
      operator: eq
      value: 50.0.0.1

As SelectorItem to generate descriptor entries.

data:
- selector:
    selector: source.remote_address

Fixes #97

Verification Steps

For the verification steps, this PR adds a new configuration to the existing local development environment. It's the new set of rules for *.d.rlp.com.

{                                                
  "name": "rlp-ns-D/rlp-name-D",                 
  "hostnames": [                                 
    "*.d.rlp.com"                                
  ],                                             
  "rules": [                                     
     {                                            
          "conditions": [
               {
                    "allOf": [
                           {
                                "selector": "source.remote_address",
                                 "operator": "neq",
                                 "value": "50.0.0.1"
                             }
                     ]
                }
        ], 
       "actions": [                               
           {                                        
               "extension": "limitador",              
               "scope": "rlp-ns-D/rlp-name-D",        
               "data": [                              
                    {                                 
                         "selector": {                      
                                 "selector": "source.remote_address"
                          }                                  
                     }                                    
                 ]                                      
            }                                        
         ]                                          
      }                                            
   ]                                              
},                                               

And a new limit configuration

- namespace: rlp-ns-D/rlp-name-D  
  max_value: 2                    
  seconds: 10                     
  conditions: []                  
  variables:                      
  - source.remote_address       

That configuration enables source based rate limiting on *.d.rlp.com subdomains, with only one "privileged" exception: IP "50.0.0.1" will not be rate limited.

  • Run the environment
make build && make local-setup

Port-forward envoy:

kubectl port-forward --namespace default deployment/envoy 8000:8000

Alice (IP: 40.0.0.1) has 2 requests per 10 seconds:

while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null  -H "X-Forwarded-For: 40.0.0.1" -H "Host: test.d.rlp.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done

Curl tool logs should show only two requests allowed for every 10 requests.

Bob (IP: 30.0.0.1) has 2 requests per 10 seconds:

while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null  -H "X-Forwarded-For: 30.0.0.1" -H "Host: test.d.rlp.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done

Curl tool logs should show only two requests allowed for every 10 requests.

Charley (the Boss) with privileged IP 50.0.0.1 does not get rate limited.

while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null  -H "X-Forwarded-For: 50.0.0.1" -H "Host: test.d.rlp.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done

Curl tool logs should show all requests being accepted.

Note: running all the three clients also shows that rate limiting counter is not shared and they have their own counter. IOW, each one has 2 requests every 10 seconds.

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@eguzki eguzki linked an issue Oct 15, 2024 that may be closed by this pull request
@eguzki eguzki self-assigned this Oct 15, 2024
@eguzki eguzki added enhancement New feature or request size/medium labels Oct 15, 2024
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@eguzki eguzki changed the title remote_address well known attribute source.remote_address well known attribute Oct 16, 2024
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Copy link
Member

@adam-cattermole adam-cattermole left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, the changes look good, just going to go through verification

Comment on lines 479 to 486
for action in &rule.actions {
for datum in &action.data {
let result = datum.item.compile();
if result.is_err() {
return Err(result.err().unwrap());
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually one thing that's standing out to me is that we no longer keep the path as well as compiled version for PatternExpressions and don't pre-compile data items. I'm not 100%, does this conflict somewhat with the intention here cc @alexsnaps

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to look into those changes properly, but that would also mean that we'd err out at evaluation time instead of at config parse time... that seems... not desirable 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed because I did not see any point in early compilation. The "compilation" is just about building the path out of the selector string, which can never fail. Hence, I removed.

Can you confirm my analysis?

Anyway, I am happy to add that back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing it once, instead of on all invocations? Now we need to parse on every Policy::pattern_expression_applies, right? And it will possibly fail when we move these to CEL expression as @adam-cattermole pointed out. But arguably that's my problem.

In any case, I'm a big proponent of doing things as early as possible, represent them in the proper way from there on in the system and do the least amount of work. This code was early parsing the "path" and keeping it in proper form to avoid paying the computation time on the data plane's path. These are my guiding principle in general, but more so here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming.

Bringing that back

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@eguzki eguzki marked this pull request as ready for review October 17, 2024 16:16
@eguzki
Copy link
Contributor Author

eguzki commented Oct 17, 2024

Comments addressed. Ready for review @adam-cattermole @alexsnaps

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Copy link
Member

@adam-cattermole adam-cattermole left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, verification worked great

@eguzki eguzki merged commit 7b62c68 into main Oct 18, 2024
9 checks passed
@eguzki eguzki deleted the 97-remote_address branch October 18, 2024 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request size/medium
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

remote_address
3 participants