Drupal - Configure Dynamic Ips Addresses in the reverse proxy

When we have an elaborate infrastructure for our Drupal 8/9 site or when it is under a corporate infrastructure, it is most likely that we have a Load Balancer, CDN or Proxy through which all request will pass until it reaches our application.

In the following graphic you can see a scenario where the user's request must pass through a CDN, a Load Balancer and finally to the application's container network.

Diagrama de Red

In this scenario, imagine a module that must take the user's IP to distinguish it from other users, for example the way the Polls module works to identify that the user has voted, i.e., we need the user's original IP to reach the application so that it can work properly.

The different layers usually pass the public IP to the HTTP_X_FORWARDED_FOR header, however, this can include IPs from the other layers as you can see in the following image

X-FORWARDED

Drupal offers the possibility to discard those IPs that we know that are part of our infrastructure and in this way make sure to deliver the correct IP to our application, we only have to add the IPs in an array in the variable $settings in the settings.php file as you can see in the following code

$settings['reverse_proxy'] = TRUE;
$settings['reverse_proxy_addresses'] = [10.0.3.173, 10.10.0.20];

More information at: https://www.drupal.org/node/425990

However, now imagine that one of your infrastructure components does not have a fixed IP or is not only one but is part of an internal network that serves requests and often will not be the same therefore the IP varies, this means that you would need to define a range of IPs, however the previous configuration we saw does not accept IP Ranges but fixed IP's so it becomes a problem.

To solve this problem I share with you the following function that I have created to be included or called in the settings.php file, the function allows you to identify the IPs that are part of the Internal Network IP range, if they are, it adds them to an array that is returned so we can use it in our configuration.

function is_privateip(){

  $private_ips = [];
  // lista de argumentos, puede recibir cuantos argumentos con Ips se le pasen
  $arg_list = func_get_args();

  foreach($arg_list as $value){
    if(is_string($value)){
      $value_array = explode(',',$value);
      // Se recorre la cadena de IPs convertidas en un arreglo
      foreach( $value_array as $ip){

        $ip_stripped = str_replace(' ', '', $ip);

        // Verifica si la IP es parte del rango interno
        if(filter_var(trim($ip_stripped),
          FILTER_VALIDATE_IP,
          FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE |  FILTER_FLAG_NO_RES_RANGE
        ) == false){
          // Agrega la IP al arreglo
          $private_ips[] = $ip_stripped;
        }
      }
    }
  }
  return $private_ips;
}

The previous function allows to generate the array of internal IPs and I can pass as parameter all the IPs that I receive from headers as long as they are a string or that string has several IPs separated by comma, note the following example.

$private_ips = is_privateip($_SERVER['HTTP_X_REAL_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['REMOTE_ADDR']);

And finally I use it

$settings['reverse_proxy'] = TRUE;
$settings['reverse_proxy_addresses'] = $private_ips;

I hope you find this little code useful, I am also sure that it can be improved much more according to your needs and experience.

 

Aldibier_Morales