diff --git a/wire/config.php b/wire/config.php index 831cd8f3..fbe1cfe9 100644 --- a/wire/config.php +++ b/wire/config.php @@ -301,6 +301,30 @@ $config->sessionChallenge = true; */ $config->sessionFingerprint = 1; +/** + * Force current session IP address (overriding auto-detect) + * + * This overrides the return value of `$session->getIP()` method. + * Use this property only for setting the IP address. To get the IP address + * always use the `$session->getIP()` method instead. + * + * This is useful if you are in an environment where the remote IP address + * comes from some property other than the REMOTE_ADDR in $_SERVER. For instance, + * if you are using a load balancer, what’s usually detected as the IP address is + * actually the IP address between the load balancer and the server, rather than + * the client IP address. So in that case, you’d want to set this property as + * follows: + * ~~~~~ + * $config->sessionForceIP = $_SERVER['HTTP_X_FORWARDED_FOR']; + * ~~~~~ + * If you don’t have a specific need to override the IP address of the user + * then you should leave this blank. + * + * @var string + * + */ +$config->sessionForceIP = ''; + /** * Use secure cookies when on HTTPS? * diff --git a/wire/core/Config.php b/wire/core/Config.php index 5eee506d..98269f40 100644 --- a/wire/core/Config.php +++ b/wire/core/Config.php @@ -64,6 +64,7 @@ * @property bool $sessionChallenge Should login sessions have a challenge key? (for extra security, recommended) #pw-group-session * @property bool $sessionFingerprint Should login sessions be tied to IP and user agent? 0 or false: Fingerprint off. 1 or true: Fingerprint on with default/recommended setting (currently 10). 2: Fingerprint only the remote IP. 4: Fingerprint only the forwarded/client IP (can be spoofed). 8: Fingerprint only the useragent. 10: Fingerprint the remote IP and useragent (default). 12: Fingerprint the forwarded/client IP and useragent. 14: Fingerprint the remote IP, forwarded/client IP and useragent (all). #pw-group-session * @property int $sessionHistory Number of session entries to keep (default=0, which means off). #pw-group-session + * @property string $sessionForceIP Force the client IP address returned by $session->getIP() to be this rather than auto-detect (useful with load balancer). Use for setting value only. #pw-group-session * @property array $loginDisabledRoles Array of role name(s) or ID(s) of roles where login is disallowed. #pw-group-session * * @property string $prependTemplateFile PHP file in /site/templates/ that will be loaded before each page's template file (default=none) #pw-group-template-files diff --git a/wire/core/ProcessWire.php b/wire/core/ProcessWire.php index 1e316f02..5fe18c67 100644 --- a/wire/core/ProcessWire.php +++ b/wire/core/ProcessWire.php @@ -268,9 +268,18 @@ class ProcessWire extends Wire { // $config->debugIf: optional setting to determine if debug mode should be on or off if($config->debugIf && is_string($config->debugIf)) { $debugIf = trim($config->debugIf); - if(strpos($debugIf, '/') === 0) $debugIf = (bool) @preg_match($debugIf, $_SERVER['REMOTE_ADDR']); // regex IPs - else if(is_callable($debugIf)) $debugIf = $debugIf(); // callable function to determine debug mode for us - else $debugIf = $debugIf === $_SERVER['REMOTE_ADDR']; // exact IP match + $ip = $config->sessionForceIP; + if(empty($ip)) $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + if(strpos($debugIf, '/') === 0 && !empty($ip)) { + $debugIf = (bool) @preg_match($debugIf, $ip); // regex IPs + } else if(is_callable($debugIf)) { + $debugIf = $debugIf(); // callable function to determine debug mode for us + } else if(!empty($ip)) { + $debugIf = $debugIf === $ip; // exact IP match + } else { + $debugIf = false; + } + unset($ip); $config->debug = $debugIf; } diff --git a/wire/core/Session.php b/wire/core/Session.php index 4fe27fb1..d5c30d19 100644 --- a/wire/core/Session.php +++ b/wire/core/Session.php @@ -139,7 +139,7 @@ class Session extends Wire implements \IteratorAggregate { public function __construct(ProcessWire $wire) { $wire->wire($this); - $this->config = $this->wire('config'); + $this->config = $wire->wire('config'); $this->sessionKey = $this->className(); $instanceID = $wire->getProcessWireInstanceID(); @@ -691,7 +691,7 @@ class Session extends Wire implements \IteratorAggregate { } /** - * Get the IP address of the current user + * Get the IP address of the current user (IPv4) * * ~~~~~ * $ip = $session->getIP(); @@ -705,8 +705,14 @@ class Session extends Wire implements \IteratorAggregate { * */ public function getIP($int = false, $useClient = false) { + + $ip = $this->config->sessionForceIP; + + if(!empty($ip)) { + // use IP address specified in $config->sessionForceIP and disregard other options + $useClient = false; - if(empty($_SERVER['REMOTE_ADDR'])) { + } else if(empty($_SERVER['REMOTE_ADDR'])) { // when accessing via CLI Interface, $_SERVER['REMOTE_ADDR'] isn't set and trying to get it, throws a php-notice $ip = '127.0.0.1'; diff --git a/wire/core/WireShutdown.php b/wire/core/WireShutdown.php index abb7bb08..21fe8dab 100644 --- a/wire/core/WireShutdown.php +++ b/wire/core/WireShutdown.php @@ -341,9 +341,9 @@ class WireShutdown extends Wire { if($useHTML && $config->ajax) $useHTML = false; // include IP address is user name if configured to do so - if($config->logIP && isset($_SERVER['REMOTE_ADDR'])) { - $ip = $this->wire('session') ? $this->wire('session')->getIP() : $_SERVER['REMOTE_ADDR']; - $name = "$name ($ip)"; + if($config->logIP && $this->wire('session')) { + $ip = $this->wire('session')->getIP(); + if(strlen($ip)) $name = "$name ($ip)"; } // send error email if applicable diff --git a/wire/modules/Session/SessionLoginThrottle/SessionLoginThrottle.module b/wire/modules/Session/SessionLoginThrottle/SessionLoginThrottle.module index d6dbed07..ca24478d 100644 --- a/wire/modules/Session/SessionLoginThrottle/SessionLoginThrottle.module +++ b/wire/modules/Session/SessionLoginThrottle/SessionLoginThrottle.module @@ -92,7 +92,7 @@ class SessionLoginThrottle extends WireData implements Module, ConfigurableModul $allowed = false; } else if($this->checkIP) { - $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; + $ip = $this->wire('session')->getIP(); if(strlen($ip) && !$this->allowLogin($ip)) $allowed = false; }