From cc241c2add4ebddad7a9ee505f44512ca2c5b9b4 Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Fri, 8 Oct 2021 12:26:24 -0700 Subject: [PATCH] Allow using a reverse proxy for local HTTP requests $wgLocalHTTPProxy can be used to configure a reverse proxy for requests to domains in $wgLocalVirtualHosts. The previous implementation of using it as a proper HTTP proxy is no longer supported and has been reverted out of REL1_37 (856da72363d1ba8bf). It sets the hostname of the request as a "Host" header, scheme as "X-Forwarded-Proto" and then sets the proxy's scheme, host and port as those of the request. Bug: T288848 Change-Id: Ibc3616f5ad925d464d937ab15461a88619c8b7a7 --- includes/DefaultSettings.php | 12 ++++-- includes/http/MWHttpRequest.php | 37 ++++++++++++++++++- .../includes/http/MWHttpRequestTest.php | 12 ++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 2b06be9ace3..29aa28f512f 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -9674,6 +9674,8 @@ $wgHTTPProxy = ''; * Local virtual hosts. * * This lists domains that are configured as virtual hosts on the same machine. + * It is expected that each domain can be identified by its hostname alone, + * without any ports. * * This affects the following: * - MWHttpRequest: If a request is to be made to a domain listed here, or any @@ -9686,12 +9688,16 @@ $wgHTTPProxy = ''; $wgLocalVirtualHosts = []; /** - * Proxy to use to requests to domains in $wgLocalVirtualHosts + * Reverse proxy to use for requests to domains in $wgLocalVirtualHosts * - * If set to false, no proxy will be used for local requests + * When used, any port in the request URL will be dropped. The behavior of + * redirects and cookies is dependent upon the reverse proxy actually in use, + * as MediaWiki doesn't implement any special handling for them. + * + * If set to false, no reverse proxy will be used for local requests. * * @var string|bool - * @since 1.37 + * @since 1.38 */ $wgLocalHTTPProxy = false; diff --git a/includes/http/MWHttpRequest.php b/includes/http/MWHttpRequest.php index b75905e0d3a..ef1025accf8 100644 --- a/includes/http/MWHttpRequest.php +++ b/includes/http/MWHttpRequest.php @@ -239,12 +239,47 @@ abstract class MWHttpRequest implements LoggerAwareInterface { // Otherwise, fallback to $wgLocalHTTPProxy for local URLs // or $wgHTTPProxy for everything else if ( self::isLocalURL( $this->url ) ) { - $this->proxy = (string)$wgLocalHTTPProxy; + if ( $wgLocalHTTPProxy !== false ) { + $this->setReverseProxy( $wgLocalHTTPProxy ); + } } else { $this->proxy = (string)$wgHTTPProxy; } } + /** + * Enable use of a reverse proxy in which the hostname is + * passed as a "Host" header, and the request is sent to the + * proxy's host:port instead. + * + * Note that any custom port in the request URL will be lost + * and cookies and redirects may not work properly. + * + * @param string $proxy URL of proxy + */ + protected function setReverseProxy( string $proxy ) { + $parsedProxy = wfParseUrl( $proxy ); + if ( $parsedProxy === false ) { + throw new Exception( "Invalid reverseProxy configured: $proxy" ); + } + // Set the current host in the Host header + $this->setHeader( 'Host', $this->parsedUrl['host'] ); + // Set current protocol in X-Forwarded-Proto + // TODO: consider supporting the standardized "Forwarded" header too + $this->setHeader( 'X-Forwarded-Proto', $this->parsedUrl['scheme'] ); + // Replace scheme, host and port in the request + $this->parsedUrl['scheme'] = $parsedProxy['scheme']; + $this->parsedUrl['host'] = $parsedProxy['host']; + if ( isset( $parsedProxy['port'] ) ) { + $this->parsedUrl['port'] = $parsedProxy['port']; + } else { + unset( $this->parsedUrl['port'] ); + } + $this->url = wfAssembleUrl( $this->parsedUrl ); + // Mark that we're already using a proxy + $this->noProxy = true; + } + /** * Check if the URL can be served by localhost * diff --git a/tests/phpunit/includes/http/MWHttpRequestTest.php b/tests/phpunit/includes/http/MWHttpRequestTest.php index 714a4dc5d62..26e9596fe5c 100644 --- a/tests/phpunit/includes/http/MWHttpRequestTest.php +++ b/tests/phpunit/includes/http/MWHttpRequestTest.php @@ -1,5 +1,7 @@ assertSame( $expect, MWHttpRequest::isValidURI( $uri ), $message ); } + public function testSetReverseProxy() { + $req = TestingAccessWrapper::newFromObject( + MWHttpRequest::factory( 'https://example.org/path?query=string' ) + ); + $req->setReverseProxy( 'http://localhost:1234' ); + $this->assertSame( $req->url, 'http://localhost:1234/path?query=string' ); + $this->assertSame( $req->reqHeaders['Host'], 'example.org' ); + $this->assertSame( $req->reqHeaders['X-Forwarded-Proto'], 'https' ); + } + }