class ClientIpRestore in CloudFlare 8
Restores the true client Ip address.
Hierarchy
- class \Drupal\cloudflare\EventSubscriber\ClientIpRestore implements \Symfony\Component\EventDispatcher\EventSubscriberInterface uses StringTranslationTrait
Expanded class hierarchy of ClientIpRestore
See also
https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Clou...
1 file declares its use of ClientIpRestore
- ClientIpRestoreTest.php in tests/
src/ Unit/ ClientIpRestoreTest.php
1 string reference to 'ClientIpRestore'
1 service uses ClientIpRestore
File
- src/
EventSubscriber/ ClientIpRestore.php, line 23
Namespace
Drupal\cloudflare\EventSubscriberView source
class ClientIpRestore implements EventSubscriberInterface {
use StringTranslationTrait;
const CLOUDFLARE_RANGE_KEY = 'cloudflare_range_key';
const CLOUDFLARE_CLIENT_IP_RESTORE_ENABLED = 'client_ip_restore_enabled';
const CLOUDFLARE_BYPASS_HOST = 'bypass_host';
const IPV4_ENDPOINTS_URL = 'https://www.cloudflare.com/ips-v4';
const IPV6_ENDPOINTS_URL = 'https://www.cloudflare.com/ips-v6';
/**
* Cache backend service.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The settings configuration.
*
* @var \Drupal\Core\Config\Config
*/
protected $config;
/**
* The HTTP client to fetch the feed data with.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* TRUE/FALSE if client ip restoration enabled.
*
* @var bool
*/
protected $isClientIpRestoreEnabled;
/**
* Constructs a ClientIpRestore.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* Cache backend.
* @param \GuzzleHttp\ClientInterface $http_client
* A Guzzle client object.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
*/
public function __construct(ConfigFactoryInterface $config_factory, CacheBackendInterface $cache, ClientInterface $http_client, LoggerInterface $logger) {
$this->httpClient = $http_client;
$this->cache = $cache;
$this->config = $config_factory
->get('cloudflare.settings');
$this->logger = $logger;
$this->isClientIpRestoreEnabled = $this->config
->get(SELF::CLOUDFLARE_CLIENT_IP_RESTORE_ENABLED);
$this->bypassHost = $this->config
->get(SELF::CLOUDFLARE_BYPASS_HOST);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = [
'onRequest',
20,
];
return $events;
}
/**
* Restores the origination client IP delivered to Drupal from CloudFlare.
*/
public function onRequest(GetResponseEvent $event) {
if (!$this->isClientIpRestoreEnabled) {
return;
}
$current_request = $event
->getRequest();
$cf_connecting_ip = $current_request->server
->get('HTTP_CF_CONNECTING_IP');
$has_http_cf_connecting_ip = !empty($cf_connecting_ip);
$has_bypass_host = !empty($this->bypassHost);
$client_ip = $current_request
->getClientIp();
$incoming_uri = $current_request
->getHost();
$request_expected_to_bypass_cloudflare = $has_bypass_host && $this->bypassHost == $incoming_uri;
if ($request_expected_to_bypass_cloudflare) {
return;
}
if (!$has_http_cf_connecting_ip) {
$message = $this
->t("Request came through without being routed through CloudFlare.");
$this->logger
->warning($message);
return;
}
$has_ip_already_changed = $client_ip == $cf_connecting_ip;
// Some environments may make the alteration for us. In which case no
// action is required.
if ($has_ip_already_changed) {
$url_to_settings = Url::fromRoute('cloudflare.admin_settings_form');
$link_to_settings = $url_to_settings
->getInternalPath();
$message = $this
->t('Request has already been updated. This functionality should be deactivated. Please go <a href="@link_to_settings">here</a> to disable "Restore Client Ip Address".', [
'@link_to_settings' => $link_to_settings,
]);
$this->logger
->warning($message);
return;
}
$cloudflare_ipranges = $this
->getCloudFlareIpRanges();
$request_originating_from_cloudflare = IpUtils::checkIp($client_ip, $cloudflare_ipranges);
if ($has_http_cf_connecting_ip && !$request_originating_from_cloudflare) {
$message = $this
->t("Client IP of @client_ip does not match a known CloudFlare IP but there is HTTP_CF_CONNECTING_IP of @cf_connecting_ip.", [
'@cf_connecting_ip' => $cf_connecting_ip,
'@client_ip' => $client_ip,
]);
$this->logger
->warning($message);
return;
}
// As the changed remote address will make it impossible to determine
// a trusted proxy, we need to make sure we set the right protocal as well.
// @see \Symfony\Component\HttpFoundation\Request::isSecure()
$event
->getRequest()->server
->set('HTTPS', $event
->getRequest()
->isSecure() ? 'on' : 'off');
$event
->getRequest()->server
->set('REMOTE_ADDR', $cf_connecting_ip);
$event
->getRequest()
->overrideGlobals();
}
/**
* Get a list of cloudflare IP Ranges.
*
* @return array
* Listing of the CloudFlareIP edge server IP ranges
*/
public function getCloudFlareIpRanges() {
if ($cache = $this->cache
->get(self::CLOUDFLARE_RANGE_KEY)) {
return $cache->data;
}
try {
$ipv4_raw_listings = trim((string) $this->httpClient
->get(SELF::IPV4_ENDPOINTS_URL)
->getBody());
$ipv6_raw_listings = trim((string) $this->httpClient
->get(SELF::IPV6_ENDPOINTS_URL)
->getBody());
$iv4_endpoints = explode("\n", $ipv4_raw_listings);
$iv6_endpoints = explode("\n", $ipv6_raw_listings);
$cloudflare_ips = array_merge($iv4_endpoints, $iv6_endpoints);
$cloudflare_ips = array_map('trim', $cloudflare_ips);
if (empty($cloudflare_ips)) {
$this->logger
->error("Unable to get a listing of CloudFlare IPs.");
return [];
}
$this->cache
->set(SELF::CLOUDFLARE_RANGE_KEY, $cloudflare_ips, Cache::PERMANENT);
return $cloudflare_ips;
} catch (RequestException $exception) {
$this->logger
->error("Unable to get a listing of CloudFlare IPs. " . $exception
->getMessage());
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ClientIpRestore:: |
protected | property | Cache backend service. | |
ClientIpRestore:: |
protected | property | The settings configuration. | |
ClientIpRestore:: |
protected | property | The HTTP client to fetch the feed data with. | |
ClientIpRestore:: |
protected | property | TRUE/FALSE if client ip restoration enabled. | |
ClientIpRestore:: |
protected | property | A logger instance. | |
ClientIpRestore:: |
constant | |||
ClientIpRestore:: |
constant | |||
ClientIpRestore:: |
constant | |||
ClientIpRestore:: |
public | function | Get a list of cloudflare IP Ranges. | |
ClientIpRestore:: |
public static | function | Returns an array of event names this subscriber wants to listen to. | |
ClientIpRestore:: |
constant | |||
ClientIpRestore:: |
constant | |||
ClientIpRestore:: |
public | function | Restores the origination client IP delivered to Drupal from CloudFlare. | |
ClientIpRestore:: |
public | function | Constructs a ClientIpRestore. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |