Score:0

How to get the true client IP when a reverse proxy sets the value in a custom header rather than in X-Forwarded-For (HTTP_X_FORWARDED_FOR)

hk flag

I was working with a Drupal site that sits behind a load balancer and a varnish cache - 2 proxies. The true client IP is not present in the X-Forwarded-For header ($_SERVER['HTTP_X_FORWARDED_FOR') but is present in $_SERVER['HTTP_X_REAL_IP']

The IP that Drupal detects is interestingly from some hostname lookup and not present in any of the $_SERVER variables.

I don't have control over the infrastructure team to fix the HTTP_X_FORWARDED_FOR at the network level. I will need to fix it in settings.php. Now that Symfony is handling the IP detection, was wondering what $_SERVER variable should be altered in settings.php to get Drupal to detect the actual client IP.

id flag
I deleted my answer as not really answering the question. If Apache, you can try copying one header value to another with a technique like this: https://stackoverflow.com/questions/30703952/how-to-convert-a-http-header-into-another A technique like that will provide HTTP_X_FORWARDED_FOR from the HTTP_X_REAL_IP value before the request reaches Drupal.
hk flag
I believe you can set $_SERVER variables directly in settings.php and printing $_SERVER seems to be printing the revised variables. I tried setting HTTP_X_REAL_IP as the first item in the HTTP_X_FORWARDED_FOR variable (comma separated string) but did not seem to help. Might need to read through how symfony parses these header values to really figure this out. Was wondering if somebody already has this figured out.
apaderno avatar
us flag
If PHP does not get `$_SERVER['HTTP_X_REAL_IP']`, there is not much you can do. You can set it with another value from `$_SERVER`, assuming it contains an equivalent value.
apaderno avatar
us flag
This does not seem a question which requires Drupal expertise to be answered. Drupal does not have control over the values contained in `$_SERVER`.
hk flag
Thanks for looking into this @apaderno. Maybe the question will need edits. The scenario is that Drupal is not getting the true client IP because of the way the headers come to the application. The question is to figure out what we should alter in $_SERVER to make the symfony request processing get the true client IP.
id flag
After setting up the DrupalKernel Drupal's index.php initializes `$request = Request::createFromGlobals()`, and createFromGlobals uses `$_SERVER` directly as `$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER)`.
apaderno avatar
us flag
If Drupal does not remove `$_SERVER['HTTP_X_REAL_IP']` or change that value, there is nothing that can be done from Drupal side. That is why this question does not require Drupal expertise. If the answer does not change for Drupal, it is not on-topic for us.
hk flag
The point is to not have my custom code get the IP from the custom header but about having Drupal detect the IP correctly in all places an IP is used. Eg: floods limits, dblog etc. So the question is what do we manipulate in settings.php to get the desired behavior.
id flag
The actual header to edit is X_FORWARDED_FOR. Is that your question?
hk flag
Thanks again @cilefen. Am working on this issue now. Will fix this and post the solution to the problem with findings.
hk flag
Thanks for your patience. Have added my answer below.
Score:1
hk flag

To make Drupal detect the IP of the client correctly you will have to update $_SERVER['HTTP_X_FORWARDED_FOR'] by prefixing the true client IP to it or setting it to the true client IP itself.

$_SERVER['HTTP_X_FORWARDED_FOR'] = $_SERVER['HTTP_X_REAL_IP'] . ', ' . $_SERVER['HTTP_X_FORWARDED_FOR'];

or

$_SERVER['HTTP_X_FORWARDED_FOR'] = $_SERVER['HTTP_X_REAL_IP'];

The challenge is that by the time settings.php is parsed, the Request class has already been instantiated and the server variables have already been processed and loaded into the request object. So doing this in settings.php will not help solve the problem.

In index.php

$kernel = new DrupalKernel('prod', $autoloader);

$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();

settings.php is processed in $kernel->handle.

By then $request has already been loaded with values from $_SERVER

So to make Drupal detect the correct IP you will have to patch index.php to add the below line before $request is initialized.

There is likely a PHP OOPs / Symfony approach by overriding some class / extending some class to fix this more appropriately but this will solve the problem.

id flag
The header is `X-Forwarded-For`. `HTTP_ X_FORWARDED_FOR` is a Symfony Framework constant. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
id flag
Oh hold on: this isn’t headers. It’s what that header becomes in PHP superglobals. Ignore that comment. But I think the Question needs some cleanup to clarify HTTP header vs header-as-transformed into a $_SERVER value.
hk flag
Thanks for the feedback. Does that look better now?
id flag
Yes, thank you.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.