Problem/Motivation
We are setting up a client website in Azure Kubernetes(K8S) Service behind Application Gateway (AppGW) fronted by Cloudflare (CF):
CF => AppGW => K8S => Drupal
The problem is that, when 'Restore Client Ip Address' option is enabled, the module tries to validate that request is really coming from Cloudflare by comparing $client_ip = $current_request->getClientIp(); which is coming from Kubernetes ingress controller (and is private un-routable IP, like 10.170.76.133) to the list of known Cloudflare IPs. Of course this validation fails.
In our case, we setting it all up in a secure recommended way - only CF IPs are allowed to talk to AppGW, so we can safely avoid this check, and implement the same code as in ClientIpRestore::onRequest():
$event->getRequest()->server->set('HTTPS', $event->getRequest()->isSecure() ? 'on' : 'off');
$event->getRequest()->server->set('REMOTE_ADDR', $cf_connecting_ip);
$event->getRequest()->overrideGlobals();
without remote address IP validation.
Proposed resolution
- Use CF-Visitor header provided by CloudFlare to detect request scheme used by website visitor
- Add option to skip remote address IP validation
- Move restore visitor IP address functionality into middleware
First item solves the problem of an incorrect "secure" flag value on a session cookie when website is configured in the flexible encryption mode. Last two supports both the cookie problem, and flexible encryption set and set up with intermediaries.
Remaining tasks
- Patch - Done
- Review
- Commit
User interface changes
New checkbox on the configuration form.
API changes
None.
Data model changes
None
Release notes snippet
Ability to skip Cloudflare IP validation when restoring real client IP address.
Original description
We are setting up a client website in Azure Kubernetes(K8S) Service behind Application Gateway (AppGW) , but I'm not sure if that is related to the issue we are having, fronted by Cloudflare (CF):
CF => AppGW => K8S => Drupal
The problem is that, when 'Restore Client Ip Address' option is enabled, the module tries to validate that request is really coming from Cloudflare by comparing $client_ip = $current_request->getClientIp(); which is coming from Kubernetes ingress controller (and is private un-routable IP, like 10.170.76.133) to the list of known Cloudflare IPs. Of course this validation fails.
The question is how do we as a community address this ?
In our case, we setting it all up in a secure recommended way - only CF IPs are allowed to talk to AppGW so we can safely avoid this check, and implement the same code as in ClientIpRestore::onRequest():
$event->getRequest()->server->set('HTTPS', $event->getRequest()->isSecure() ? 'on' : 'off');
$event->getRequest()->server->set('REMOTE_ADDR', $cf_connecting_ip);
$event->getRequest()->overrideGlobals();
skipping all previous checks. This solution however wouldn't fit all other use-cases .
I would prefer to see some kind of configuration option in this module that would allow me to achieve what I'm after, rather than throwing custom code in my projects. If that approach sounds acceptable I'm happy to code it and provide a patch. Something like "Skip request origin check" with the bold description similar to "Scary-sounding warning" of Trusted Reverse Proxy that this should only be enabled in exceptional circumstances and full responsibility about consequences is on the user ticking that checkbox.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | cloudflare-3280262-7-9-interdiff.txt | 13 KB | rosk0 |
| #9 | cloudflare-3280262-9.patch | 22.44 KB | rosk0 |
Comments
Comment #2
rosk0After some additional time thinking about it and conversation with colleague I decided to re-qualify this to feature request.
Patch attached.
Comment #3
rosk0Disregard the previous patch please.
Comment #4
rosk0Not starting tests as they are broken.
I fixed all tests in #2915341: Proposal to switch Cloudflare library, but that includes the changes related to the subject. Might need to pull our test fixes into a separate issue...
Comment #5
rosk0Important bit that I missed previously is that TLS termination is happening in K8s ingress controller and Drupal receives plain HTTP traffic so using original request to determine the scheme is pointless. So in this patch I used supplied by Cloudflare CF-Visitor header. It contains scheme of the original request.
Adding #3137858: Not original IPs on "access denied", "page not found", Ban module to related, as in our case, it was required to make the whole thing work as expected. See #3137858-6: Not original IPs on "access denied", "page not found", Ban module for details.
Comment #6
rosk0Comment #7
rosk0Changing the title and description to better reflect what is actually happening here and why.
Also I will start with just the test to proof missing support for various use cases:
For starters this patch includes priority change from #3137858: Not original IPs on "access denied", "page not found", Ban module just to show that authenticated users are still getting insecure cookie in flexible encryption mode with the priority change.
Could not generate interdiff as module moved one since patch in #5, the changes are:
request->server->get()calls received default values (somehow that was breaking test)Comment #9
rosk0Functional test failed in expected way, unit tests failed because of missing configuration value - fixed.
And now with the middleware.
Comment #11
rosk0Tests are green, manual testing on a real project went good as well.