d8-contrib-modules / cloudflare

Cloudflare Module for Drupal
16 stars 14 forks source link

Add functionality to restore the visitors IP address #23

Closed aweingarten closed 8 years ago

aweingarten commented 9 years ago

Business Requirements

[ ] As a developer using Drupal I expect REMOTE_ADDR to be the the end user's IP address NOT the cloudflare CDN's IP.

Technical Requirements

[ ] Rename the menu task from API Credentials to Config [ ] Add admin functionality to enable this here: admin/config/services/cloudflare [ ] Store the setting in CMI [ ] Add code to update REMOTE_ADDR to HTTP_CF_CONNECTING_IP [ ] Write automated tests

Background Reading

https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-CloudFlare-handle-HTTP-Request-headers-

Also recommend looking at the D7 cloudflare modules for some ideas. DO NOT SLAVISHLY copy.

* 
* Replace $_SERVER['REMOTE_ADDR'] with $_SERVER['HTTP_CF_CONNECTING_IP'] header
* Properly flag $_SERVER['HTTPS'] and set the base-url correctly if cloudflare is using flexible SSL 
*/
function cloudflare_boot() {
  global $base_url;
  global $conf;

  // Assign a proper IP address for end-client connecting via cloudflare using CF-Connecting-IP header
  if (variable_get('cloudflare_cf_connecting_ip', FALSE)) {
    $trusted = FALSE;
    if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
      // If an array of known reverse proxy IPs is provided, then trust
      // the CF header if request really comes from one of them or from a cloudflare IP.
      $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array());
      $connecting_ip = $_SERVER['REMOTE_ADDR'];

      if (in_array($connecting_ip, $reverse_proxy_addresses) || cloudflare_ip_address_in_range($connecting_ip)) {
        $_SERVER['ORIG_REMOTE_ADDR'] = $connecting_ip;
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
        $trusted = TRUE;
        $conf['reverse_proxy'] = FALSE; // Disable any furthur reverse proxy handling.
      }
    }
  }
  // If we are not configured to use CF-Connecting-IP then use X-Forwarded-For instead
  // This is the recommended method and is used by default.
  else {
    $conf['reverse_proxy'] = TRUE;
    // Remove CloudFlare IP addresses from X-Forwarded-For header. 
    // This ensures that the end-user IP address is never identified as a CloudFlare IP. 
    $reverse_proxy_header = variable_get('reverse_proxy_header', 'HTTP_X_FORWARDED_FOR');
    if (!empty($_SERVER[$reverse_proxy_header])) {
      $forwarded = explode(',', $_SERVER[$reverse_proxy_header]);
      $forwarded = array_map('trim', $forwarded);
      $good_ips = array();
      foreach ($forwarded as $ip) {
        if (!cloudflare_ip_address_in_range($ip)) {
          $good_ips[] = $ip;
        }
      }
      $_SERVER[$reverse_proxy_header] = implode(',', $good_ips);
    }
  }

  // Properly flag the request as a HTTPS request if CloudFlare's flexible SSL is being used
  if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' && empty($_SERVER['HTTPS'])) {
    if ($trusted || in_array($connecting_ip, $reverse_proxy_addresses) || ip_address_in_range($connecting_ip, cloudflare_ips())) {
      $_SERVER['HTTPS'] = 'on';
    }
  }

  // Check for AlwaysOnline spider and set appropriate cache-control headers
  if (!empty($_SERVER['HTTP_USER_AGENT'])  && $_SERVER['HTTP_USER_AGENT'] == CLOUDFLARE_ALWAYSONLINE_USER_AGENT) {
    drupal_add_http_header('Cache-Control', 'public, max-age=86400');
    drupal_add_http_header('Vary', 'User-Agent');
  }
}
aweingarten commented 9 years ago

@victorcpereira tagging you here for visibility.

aweingarten commented 8 years ago

@victorcpereira Heads up I have released a tag to drupal.org with this functionality. I will be moving this to D.O. I encourage you to check it out!