mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 13:30:55 +01:00
adc2002d28
Summary: Fixes T13392. If you have 17 load balancers in sequence, Phabricator will receive requests with at least 17 "X-Forwarded-For" components in the header. We want to select the 17th-from-last element, since prior elements are not trustworthy. This currently isn't very easy/obvious, and you have to add a kind of sketchy piece of custom code to `preamble.php` to do any "X-Forwarded-For" parsing. Make handling this correctly easier. Test Plan: - Ran unit tests. - Configured my local `preamble.php` to call `preamble_trust_x_forwarded_for_header(4)`, then made `/debug/` dump the header and the final value of `REMOTE_ADDR`. ``` $ curl http://local.phacility.com/debug/ <pre> HTTP_X_FORWARDED_FOR = FINAL REMOTE_ADDR = 127.0.0.1 </pre> ``` ``` $ curl -H 'X-Forwarded-For: 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4, 5.5.5.5, 6.6.6.6' http://local.phacility.com/debug/ <pre> HTTP_X_FORWARDED_FOR = 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4, 5.5.5.5, 6.6.6.6 FINAL REMOTE_ADDR = 3.3.3.3 </pre> ``` ``` $ curl -H 'X-Forwarded-For: 5.5.5.5, 6.6.6.6' http://local.phacility.com/debug/ <pre> HTTP_X_FORWARDED_FOR = 5.5.5.5, 6.6.6.6 FINAL REMOTE_ADDR = 5.5.5.5 </pre> ``` Maniphest Tasks: T13392 Differential Revision: https://secure.phabricator.com/D20785
103 lines
3.3 KiB
PHP
103 lines
3.3 KiB
PHP
<?php
|
|
|
|
phabricator_startup();
|
|
|
|
$fatal_exception = null;
|
|
try {
|
|
PhabricatorStartup::beginStartupPhase('libraries');
|
|
PhabricatorStartup::loadCoreLibraries();
|
|
|
|
PhabricatorStartup::beginStartupPhase('purge');
|
|
PhabricatorCaches::destroyRequestCache();
|
|
|
|
PhabricatorStartup::beginStartupPhase('sink');
|
|
$sink = new AphrontPHPHTTPSink();
|
|
|
|
// PHP introduced a "Throwable" interface in PHP 7 and began making more
|
|
// runtime errors throw as "Throwable" errors. This is generally good, but
|
|
// makes top-level exception handling that is compatible with both PHP 5
|
|
// and PHP 7 a bit tricky.
|
|
|
|
// In PHP 5, "Throwable" does not exist, so "catch (Throwable $ex)" catches
|
|
// nothing.
|
|
|
|
// In PHP 7, various runtime conditions raise an Error which is a Throwable
|
|
// but NOT an Exception, so "catch (Exception $ex)" will not catch them.
|
|
|
|
// To cover both cases, we "catch (Exception $ex)" to catch everything in
|
|
// PHP 5, and most things in PHP 7. Then, we "catch (Throwable $ex)" to catch
|
|
// everything else in PHP 7. For the most part, we only need to do this at
|
|
// the top level.
|
|
|
|
$main_exception = null;
|
|
try {
|
|
PhabricatorStartup::beginStartupPhase('run');
|
|
AphrontApplicationConfiguration::runHTTPRequest($sink);
|
|
} catch (Exception $ex) {
|
|
$main_exception = $ex;
|
|
} catch (Throwable $ex) {
|
|
$main_exception = $ex;
|
|
}
|
|
|
|
if ($main_exception) {
|
|
$response_exception = null;
|
|
try {
|
|
$response = new AphrontUnhandledExceptionResponse();
|
|
$response->setException($main_exception);
|
|
$response->setShowStackTraces($sink->getShowStackTraces());
|
|
|
|
PhabricatorStartup::endOutputCapture();
|
|
$sink->writeResponse($response);
|
|
} catch (Exception $ex) {
|
|
$response_exception = $ex;
|
|
} catch (Throwable $ex) {
|
|
$response_exception = $ex;
|
|
}
|
|
|
|
// If we hit a rendering exception, ignore it and throw the original
|
|
// exception. It is generally more interesting and more likely to be
|
|
// the root cause.
|
|
|
|
if ($response_exception) {
|
|
throw $main_exception;
|
|
}
|
|
}
|
|
} catch (Exception $ex) {
|
|
$fatal_exception = $ex;
|
|
} catch (Throwable $ex) {
|
|
$fatal_exception = $ex;
|
|
}
|
|
|
|
if ($fatal_exception) {
|
|
PhabricatorStartup::didEncounterFatalException(
|
|
'Core Exception',
|
|
$fatal_exception,
|
|
false);
|
|
}
|
|
|
|
function phabricator_startup() {
|
|
// Load the PhabricatorStartup class itself.
|
|
$t_startup = microtime(true);
|
|
$root = dirname(dirname(__FILE__));
|
|
require_once $root.'/support/startup/PhabricatorStartup.php';
|
|
|
|
// Load client limit classes so the preamble can configure limits.
|
|
require_once $root.'/support/startup/PhabricatorClientLimit.php';
|
|
require_once $root.'/support/startup/PhabricatorClientRateLimit.php';
|
|
require_once $root.'/support/startup/PhabricatorClientConnectionLimit.php';
|
|
require_once $root.'/support/startup/preamble-utils.php';
|
|
|
|
// If the preamble script exists, load it.
|
|
$t_preamble = microtime(true);
|
|
$preamble_path = $root.'/support/preamble.php';
|
|
if (file_exists($preamble_path)) {
|
|
require_once $preamble_path;
|
|
}
|
|
|
|
$t_hook = microtime(true);
|
|
PhabricatorStartup::didStartup($t_startup);
|
|
|
|
PhabricatorStartup::recordStartupPhase('startup.init', $t_startup);
|
|
PhabricatorStartup::recordStartupPhase('preamble', $t_preamble);
|
|
PhabricatorStartup::recordStartupPhase('hook', $t_hook);
|
|
}
|