diff --git a/framework/core/src/Admin/AdminServiceProvider.php b/framework/core/src/Admin/AdminServiceProvider.php index 65be464cc..6a41751c7 100644 --- a/framework/core/src/Admin/AdminServiceProvider.php +++ b/framework/core/src/Admin/AdminServiceProvider.php @@ -25,6 +25,7 @@ use Flarum\Http\RouteHandlerFactory; use Flarum\Http\UrlGenerator; use Flarum\Locale\LocaleManager; use Flarum\Settings\Event\Saved; +use Illuminate\Support\Arr; use Laminas\Stratigility\MiddlewarePipe; class AdminServiceProvider extends AbstractServiceProvider @@ -48,6 +49,7 @@ class AdminServiceProvider extends AbstractServiceProvider $this->app->singleton('flarum.admin.middleware', function () { return [ 'flarum.admin.error_handler', + 'flarum.admin.proxy_middleware', HttpMiddleware\ParseJsonBody::class, HttpMiddleware\StartSession::class, HttpMiddleware\RememberFromCookie::class, @@ -66,6 +68,15 @@ class AdminServiceProvider extends AbstractServiceProvider ); }); + $this->app->bind('flarum.admin.proxy_middleware', function () { + $config = $this->app->make('flarum.config'); + + return new HttpMiddleware\ProxyAddress( + Arr::get($config, 'reverse_proxy.enabled', false), + Arr::get($config, 'reverse_proxy.allowed', ['127.0.0.1']) + ); + }); + $this->app->singleton('flarum.admin.handler', function () { $pipe = new MiddlewarePipe; diff --git a/framework/core/src/Api/ApiServiceProvider.php b/framework/core/src/Api/ApiServiceProvider.php index 4737d2e8c..4f6f5cd06 100644 --- a/framework/core/src/Api/ApiServiceProvider.php +++ b/framework/core/src/Api/ApiServiceProvider.php @@ -22,6 +22,7 @@ use Flarum\Http\Middleware as HttpMiddleware; use Flarum\Http\RouteCollection; use Flarum\Http\RouteHandlerFactory; use Flarum\Http\UrlGenerator; +use Illuminate\Support\Arr; use Laminas\Stratigility\MiddlewarePipe; class ApiServiceProvider extends AbstractServiceProvider @@ -45,6 +46,7 @@ class ApiServiceProvider extends AbstractServiceProvider $this->app->singleton('flarum.api.middleware', function () { return [ 'flarum.api.error_handler', + 'flarum.api.proxy_middleware', HttpMiddleware\ParseJsonBody::class, Middleware\FakeHttpMethods::class, HttpMiddleware\StartSession::class, @@ -64,6 +66,15 @@ class ApiServiceProvider extends AbstractServiceProvider ); }); + $this->app->bind('flarum.api.proxy_middleware', function () { + $config = $this->app->make('flarum.config'); + + return new HttpMiddleware\ProxyAddress( + Arr::get($config, 'reverse_proxy.enabled', false), + Arr::get($config, 'reverse_proxy.allowed', ['127.0.0.1']) + ); + }); + $this->app->singleton('flarum.api.handler', function () { $pipe = new MiddlewarePipe; diff --git a/framework/core/src/Api/Controller/CreateDiscussionController.php b/framework/core/src/Api/Controller/CreateDiscussionController.php index 0a325ee29..ff2ff6b5b 100644 --- a/framework/core/src/Api/Controller/CreateDiscussionController.php +++ b/framework/core/src/Api/Controller/CreateDiscussionController.php @@ -62,7 +62,7 @@ class CreateDiscussionController extends AbstractCreateController protected function data(ServerRequestInterface $request, Document $document) { $actor = $request->getAttribute('actor'); - $ipAddress = Arr::get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1'); + $ipAddress = $request->getAttribute('ipAddress'); if (! $request->getAttribute('bypassFloodgate')) { $this->floodgate->assertNotFlooding($actor); diff --git a/framework/core/src/Api/Controller/CreatePostController.php b/framework/core/src/Api/Controller/CreatePostController.php index fd46de865..4be1641ee 100644 --- a/framework/core/src/Api/Controller/CreatePostController.php +++ b/framework/core/src/Api/Controller/CreatePostController.php @@ -63,7 +63,7 @@ class CreatePostController extends AbstractCreateController $actor = $request->getAttribute('actor'); $data = Arr::get($request->getParsedBody(), 'data', []); $discussionId = Arr::get($data, 'relationships.discussion.data.id'); - $ipAddress = Arr::get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1'); + $ipAddress = $request->getAttribute('ipAddress'); if (! $request->getAttribute('bypassFloodgate')) { $this->floodgate->assertNotFlooding($actor); diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index d333b7505..24cc64774 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -29,6 +29,7 @@ use Flarum\Locale\LocaleManager; use Flarum\Settings\Event\Saved; use Flarum\Settings\Event\Saving; use Flarum\Settings\SettingsRepositoryInterface; +use Illuminate\Support\Arr; use Laminas\Stratigility\MiddlewarePipe; use Symfony\Component\Translation\TranslatorInterface; @@ -57,6 +58,7 @@ class ForumServiceProvider extends AbstractServiceProvider $this->app->singleton('flarum.forum.middleware', function () { return [ 'flarum.forum.error_handler', + 'flarum.forum.proxy_middleware', HttpMiddleware\ParseJsonBody::class, HttpMiddleware\CollectGarbage::class, HttpMiddleware\StartSession::class, @@ -76,6 +78,15 @@ class ForumServiceProvider extends AbstractServiceProvider ); }); + $this->app->bind('flarum.forum.proxy_middleware', function () { + $config = $this->app->make('flarum.config'); + + return new HttpMiddleware\ProxyAddress( + Arr::get($config, 'reverse_proxy.enabled', false), + Arr::get($config, 'reverse_proxy.allowed', ['127.0.0.1']) + ); + }); + $this->app->singleton('flarum.forum.handler', function () { $pipe = new MiddlewarePipe; diff --git a/framework/core/src/Http/Exception/ProxyNotAllowedException.php b/framework/core/src/Http/Exception/ProxyNotAllowedException.php new file mode 100644 index 000000000..9ce44bb7a --- /dev/null +++ b/framework/core/src/Http/Exception/ProxyNotAllowedException.php @@ -0,0 +1,21 @@ +enabled = $enabled; + $this->allowedAddresses = $allowedAddresses; + } + + private function wildcardMatch(string $ipAddress): bool + { + foreach ($this->allowedAddresses as $allowedAddress) { + if (fnmatch($allowedAddress, $ipAddress)) { + return true; + } + } + + return false; + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $serverParams = $request->getServerParams(); + $ipAddress = Arr::get($serverParams, 'REMOTE_ADDR', '127.0.0.1'); + + if ($this->enabled) { + if ($this->wildcardMatch($ipAddress)) { + // standard header for proxies, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For + $ipAddress = Arr::get($serverParams, 'X_FORWARDED_FOR', $ipAddress); + $ipAddress = Arr::get($serverParams, 'HTTP_CLIENT_IP', $ipAddress); + $ipAddress = Arr::get($serverParams, 'X_PROXYUSER_IP', $ipAddress); + } else { + throw new ProxyNotAllowedException(); + } + } + + $request = $request->withAttribute('ipAddress', $ipAddress); + + return $handler->handle($request); + } +}