diff --git a/CHANGELOG.txt b/CHANGELOG.txt index de22887f57d2676a80aaec5663f720fc4221c472..1baa84e7f40c4e943d26999758ac1c279fd8b2db 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -11,6 +11,7 @@ Drupal 6.0, xxxx-xx-xx (development version) - Added optional e-mail notifications when user are approved, blocked, or deleted. - Added versioning support to node terms. - Made it easier to theme the forum overview page. +- Made Drupal work correctly when running behind a reverse proxy like Squid or Pound. - Drupal works with error reporting set to E_ALL. - Added scripts/drupal.sh to execute Drupal code from the command line. Useful to use Drupal as a framework to build command-line tools. - Made signature support optional and made it possible to theme signatures. diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 505c3872ed1e925e730457ada77737bf8824a536..d3c35b5f2960e32c0043d84e3bb3e9c19f701a06 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -703,7 +703,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO 'user' => $user, 'request_uri' => $base_root . request_uri(), 'referer' => referer_uri(), - 'ip' => $_SERVER['REMOTE_ADDR'], + 'ip' => ip_address(), 'timestamp' => time(), ); @@ -819,7 +819,7 @@ function drupal_is_denied($type, $mask) { function drupal_anonymous_user($session = '') { $user = new stdClass(); $user->uid = 0; - $user->hostname = $_SERVER['REMOTE_ADDR']; + $user->hostname = ip_address(); $user->roles = array(); $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; $user->session = $session; @@ -881,9 +881,9 @@ function _drupal_bootstrap($phase) { case DRUPAL_BOOTSTRAP_ACCESS: // Deny access to hosts which were banned - t() is not yet available. - if (drupal_is_denied('host', $_SERVER['REMOTE_ADDR'])) { + if (drupal_is_denied('host', ip_address())) { header('HTTP/1.1 403 Forbidden'); - print 'Sorry, '. $_SERVER['REMOTE_ADDR'] .' has been banned.'; + print 'Sorry, '. ip_address() .' has been banned.'; exit(); } break; @@ -1056,3 +1056,32 @@ function language_list($field = 'language', $reset = FALSE) { function language_default() { return variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0)); } + + +/** + * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header + * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address + * of the proxy server, and not the client's. + * + * @return + * IP address of client machine, adjusted for reverse proxy. + */ +function ip_address() { + static $remote_ip; + + if ($remote_ip) { + // We have been here before, so just return the one we processed before + return $remote_ip; + } + else { + $remote_ip = $_SERVER['REMOTE_ADDR']; + if (variable_get('reverse_proxy', FALSE) && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { + $ip_array = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + // If there are several arguments, the leftmost one is the farthest client + $remote_ip = $ip_array[0]; + } + } + // Store the satnized version in the static variable + $remote_ip = check_plain($remote_ip); + return $remote_ip; +} diff --git a/includes/common.inc b/includes/common.inc index fb7f21a413aae88b996eb02bcd9913bf202fa89d..7af684578d799f241b53f3dc3c5b0edc6d80c443 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -801,7 +801,7 @@ function valid_url($url, $absolute = FALSE) { * The name of the event. */ function flood_register_event($name) { - db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d)", $name, $_SERVER['REMOTE_ADDR'], time()); + db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d)", $name, ip_address(), time()); } /** @@ -817,7 +817,7 @@ function flood_register_event($name) { * True if the user did not exceed the hourly threshold. False otherwise. */ function flood_is_allowed($name, $threshold) { - $number = db_num_rows(db_query("SELECT event FROM {flood} WHERE event = '%s' AND hostname = '%s' AND timestamp > %d", $name, $_SERVER['REMOTE_ADDR'], time() - 3600)); + $number = db_num_rows(db_query("SELECT event FROM {flood} WHERE event = '%s' AND hostname = '%s' AND timestamp > %d", $name, ip_address(), time() - 3600)); return ($number < $threshold ? TRUE : FALSE); } @@ -2805,3 +2805,4 @@ function watchdog_severity_levels() { WATCHDOG_DEBUG => t('debug'), ); } + diff --git a/includes/session.inc b/includes/session.inc index 103209671a8f8be163051ecf9ae3dae0bee92fbe..e9464c20a1df8fd98d2f456630d47c7e09ab5cd2 100644 --- a/includes/session.inc +++ b/includes/session.inc @@ -69,11 +69,11 @@ function sess_write($key, $value) { // and gives more useful statistics. We can't eliminate anonymous session // table rows without breaking throttle module and "Who's Online" block. if ($user->uid || $value || count($_COOKIE)) { - db_query("INSERT INTO {sessions} (sid, uid, cache, hostname, session, timestamp) VALUES ('%s', %d, %d, '%s', '%s', %d)", $key, $user->uid, isset($user->cache) ? $user->cache : '', $_SERVER["REMOTE_ADDR"], $value, time()); + db_query("INSERT INTO {sessions} (sid, uid, cache, hostname, session, timestamp) VALUES ('%s', %d, %d, '%s', '%s', %d)", $key, $user->uid, isset($user->cache) ? $user->cache : '', ip_address(), $value, time()); } } else { - db_query("UPDATE {sessions} SET uid = %d, cache = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '%s'", $user->uid, isset($user->cache) ? $user->cache : '', $_SERVER["REMOTE_ADDR"], $value, time(), $key); + db_query("UPDATE {sessions} SET uid = %d, cache = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '%s'", $user->uid, isset($user->cache) ? $user->cache : '', ip_address(), $value, time(), $key); // TODO: this can be an expensive query. Perhaps only execute it every x minutes. Requires investigation into cache expiration. if ($user->uid) { diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 494da60bcfa3d2c819de96685668a2fbf13fa8cb..6839c259e5c49d0a1ec9bb870f85b930ba06b885 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -820,7 +820,7 @@ function comment_save($edit) { } $edit += array('mail' => '', 'homepage' => ''); - db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, format, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit['cid'], $edit['nid'], $edit['pid'], $edit['uid'], $edit['subject'], $edit['comment'], $edit['format'], $_SERVER['REMOTE_ADDR'], $edit['timestamp'], $status, $score, $users, $thread, $edit['name'], $edit['mail'], $edit['homepage']); + db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, format, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit['cid'], $edit['nid'], $edit['pid'], $edit['uid'], $edit['subject'], $edit['comment'], $edit['format'], ip_address(), $edit['timestamp'], $status, $score, $users, $thread, $edit['name'], $edit['mail'], $edit['homepage']); _comment_update_node_statistics($edit['nid']); diff --git a/modules/poll/poll.module b/modules/poll/poll.module index 8fef97946f79b558ad2dcc401675bfe8b199e5f5..8fb710cae1934356a94f679b2311f318449390fc 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -308,7 +308,7 @@ function poll_load($node) { $result = db_fetch_object(db_query('SELECT chorder FROM {poll_votes} WHERE nid = %d AND uid = %d', $node->nid, $user->uid)); } else { - $result = db_fetch_object(db_query("SELECT chorder FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, $_SERVER['REMOTE_ADDR'])); + $result = db_fetch_object(db_query("SELECT chorder FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, ip_address())); } if (isset($result->chorder)) { $poll->vote = $result->chorder; @@ -534,7 +534,7 @@ function poll_vote(&$node) { db_query('INSERT INTO {poll_votes} (nid, chorder, uid) VALUES (%d, %d, %d)', $node->nid, $choice, $user->uid); } else { - db_query("INSERT INTO {poll_votes} (nid, chorder, hostname) VALUES (%d, %d, '%s')", $node->nid, $choice, $_SERVER['REMOTE_ADDR']); + db_query("INSERT INTO {poll_votes} (nid, chorder, hostname) VALUES (%d, %d, '%s')", $node->nid, $choice, ip_address()); } // Add one to the votes. @@ -573,7 +573,7 @@ function poll_cancel(&$node) { db_query('DELETE FROM {poll_votes} WHERE nid = %d and uid = %d', $node->nid, $user->uid); } else { - db_query("DELETE FROM {poll_votes} WHERE nid = %d and hostname = '%s'", $node->nid, $_SERVER['REMOTE_ADDR']); + db_query("DELETE FROM {poll_votes} WHERE nid = %d and hostname = '%s'", $node->nid, ip_address()); } // Subtract from the votes. diff --git a/modules/statistics/statistics.module b/modules/statistics/statistics.module index dbbf238b277bb64ff5a6769e247714893c8e5cd4..69f65c3267ea2a0f40e861264b8f8e5aa3618245 100644 --- a/modules/statistics/statistics.module +++ b/modules/statistics/statistics.module @@ -68,7 +68,7 @@ function statistics_exit() { } if ((variable_get('statistics_enable_access_log', 0)) && (module_invoke('throttle', 'status') == 0)) { // Log this page access. - db_query("INSERT INTO {accesslog} (title, path, url, hostname, uid, sid, timer, timestamp) values('%s', '%s', '%s', '%s', %d, '%s', %d, %d)", strip_tags(drupal_get_title()), $_GET['q'], referer_uri(), $_SERVER['REMOTE_ADDR'], $user->uid, session_id(), timer_read('page'), time()); + db_query("INSERT INTO {accesslog} (title, path, url, hostname, uid, sid, timer, timestamp) values('%s', '%s', '%s', '%s', %d, '%s', %d, %d)", strip_tags(drupal_get_title()), $_GET['q'], referer_uri(), ip_address(), $user->uid, session_id(), timer_read('page'), time()); } } diff --git a/modules/system/system.module b/modules/system/system.module index 4cc1029d7ec1cbaaf46ed60270342b0e146ab3f7..3820d27ef351bd155f3071c31592ad01ef6be361 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -706,6 +706,20 @@ function system_performance_settings() { '#description' => t("Some Drupal modules include their own CSS files. When these modules are enabled, each module's CSS file adds an additional HTTP request to the page, which can increase the load time of each page. These HTTP requests can also slightly increase server load. It is recommended to only turn this option on when your site is in production, as it can interfere with theme development. This option is disabled if you have not set up your files directory, or if your download method is set to private."), ); + $form['reverse_proxy'] = array( + '#type' => 'fieldset', + '#title' => t('Reverse proxy'), + '#description' => t('Proper extraction of client IP addresses when Drupal is behind a reverse proxy.'), + ); + + $form['reverse_proxy']['reverse_proxy'] = array( + '#type' => 'radios', + '#title' => t('Reverse proxy'), + '#default_value' => variable_get('reverse_proxy', FALSE), + '#options' => array(t('Disabled'), t('Enabled')), + '#description' => t('Enable this setting to determine the correct IP address of the remote client by examining information stored in the X-Forwarded-For headers. X-Forwarded-For headers are a standard mechanism for identifying client systems connecting through a reverse proxy server, such as Squid or Pound. Reverse proxy servers are often used to enhance the performance of heavily visited sites and may also provide other site caching, security or encryption benefits. If this Drupal installation operates behind a reverse proxy, this setting should be enabled so that correct IP address information is captured in Drupal\'s session management, logging, statistics and access management systems; if you are unsure about this setting, do not have a reverse proxy, or Drupal operates in a shared hosting environment, this setting should be set to disabled.'), + ); + $form['#submit'][] = 'drupal_clear_css_cache'; return system_settings_form($form);