diff --git a/config/config.default.php b/config/config.default.php index a503a060..3299b07f 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -126,4 +126,7 @@ return [ '3XL' => '3XL', '4XL' => '4XL' ], + + // IP addresses of reverse proxies that are trusted, can be an array or a comma separated list + 'trusted_proxies' => env('TRUSTED_PROXIES', ['127.0.0.0/8', '::ffff:127.0.0.0/8', '::1/128']), ]; diff --git a/contrib/Dockerfile b/contrib/Dockerfile index 8d5fb47b..9b218cf8 100644 --- a/contrib/Dockerfile +++ b/contrib/Dockerfile @@ -33,3 +33,9 @@ FROM php:7-fpm-alpine COPY --from=data /app/ /var/www RUN apk add --no-cache icu-dev gettext-dev && \ docker-php-ext-install intl gettext pdo_mysql + +ENV TRUSTED_PROXIES 10.0.0.0/8,::ffff:10.0.0.0/8,\ + 127.0.0.0/8,::ffff:127.0.0.0/8,\ + 172.16.0.0/12,::ffff:172.16.0.0/12,\ + 192.168.0.0/16,::ffff:192.168.0.0/16,\ + ::1/128,fc00::/7,fec0::/10 diff --git a/contrib/nginx/nginx.conf b/contrib/nginx/nginx.conf index bbaf5824..96e4688f 100644 --- a/contrib/nginx/nginx.conf +++ b/contrib/nginx/nginx.conf @@ -1,35 +1,42 @@ error_log stderr; + events { worker_connections 1024; } + http { client_body_temp_path /tmp/client_body_temp; - fastcgi_temp_path /tmp/fastcgi_temp; - proxy_temp_path /tmp/proxy_temp; - scgi_temp_path /tmp/scgi_temp; - uwsgi_temp_path /tmp/uwsgi_temp; - + fastcgi_temp_path /tmp/fastcgi_temp; + proxy_temp_path /tmp/proxy_temp; + scgi_temp_path /tmp/scgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + + map $http_x_forwarded_proto $forwarded_proto { + default $http_x_forwarded_proto; + https https; + } + server { - include mime.types; - access_log off; - listen [::]:80 ipv6only=off; - proxy_redirect off; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - index index.php; - root /var/www/html; - + include mime.types; + access_log off; + listen [::]:80 ipv6only=off; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $forwarded_proto; + index index.php; + root /var/www/html; + location / { try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { - fastcgi_pass engelsystem:9000; + fastcgi_pass engelsystem:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTPS on; - include fastcgi_params; + include fastcgi_params; } } } diff --git a/src/Http/RequestServiceProvider.php b/src/Http/RequestServiceProvider.php index 077e9ecc..bbf2579c 100644 --- a/src/Http/RequestServiceProvider.php +++ b/src/Http/RequestServiceProvider.php @@ -8,7 +8,34 @@ class RequestServiceProvider extends ServiceProvider { public function register() { + $config = $this->app->get('config'); + $trustedProxies = $config->get('trusted_proxies', []); + + if (!is_array($trustedProxies)) { + $trustedProxies = empty($trustedProxies) ? [] : explode(',', preg_replace('~\s+~', '', $trustedProxies)); + } + + /** @var Request $request */ $request = $this->app->call([Request::class, 'createFromGlobals']); + $this->setTrustedProxies($request, $trustedProxies); $this->app->instance('request', $request); } + + /** + * Set the trusted Proxies + * + * Required for unit tests (static methods can't be mocked) + * + * @param Request $request + * @param array $proxies + * @param int $trustedHeadersSet + * @codeCoverageIgnore + */ + protected function setTrustedProxies( + $request, + $proxies, + $trustedHeadersSet = Request::HEADER_FORWARDED | Request::HEADER_X_FORWARDED_ALL + ) { + $request->setTrustedProxies($proxies, $trustedHeadersSet); + } } diff --git a/tests/Unit/Http/RequestServiceProviderTest.php b/tests/Unit/Http/RequestServiceProviderTest.php index a137b0ac..eddf7ee5 100644 --- a/tests/Unit/Http/RequestServiceProviderTest.php +++ b/tests/Unit/Http/RequestServiceProviderTest.php @@ -2,6 +2,8 @@ namespace Engelsystem\Test\Unit\Http; +use Engelsystem\Config\Config; +use Engelsystem\Container\ServiceProvider; use Engelsystem\Http\Request; use Engelsystem\Http\RequestServiceProvider; use Engelsystem\Test\Unit\ServiceProviderTest; @@ -10,20 +12,50 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; class RequestServiceProviderTest extends ServiceProviderTest { /** - * @covers \Engelsystem\Http\RequestServiceProvider::register() + * @return array */ - public function testRegister() + public function provideRegister() { - /** @var MockObject|Request $request */ - $request = $this->getMockBuilder(Request::class) - ->getMock(); + return [ + ['', []], + [[], []], + ['192.168.10.99', ['192.168.10.99']], + [' 234.234.234.234 ', ['234.234.234.234']], + ['123.234.123.234,10.0.0.0/8', ['123.234.123.234', '10.0.0.0/8']], + ['123.123.234.234 , ' . PHP_EOL . ' 11.22.33.44/22 ', ['123.123.234.234', '11.22.33.44/22']], + [['10.100.20.0/24'], ['10.100.20.0/24']], + ]; + } - $app = $this->getApp(['call', 'instance']); + /** + * @dataProvider provideRegister + * @covers \Engelsystem\Http\RequestServiceProvider::register() + * + * @param string|array $configuredProxies + * @param array $trustedProxies + */ + public function testRegister($configuredProxies, $trustedProxies) + { + /** @var Config|MockObject $config */ + $config = $this->getMockBuilder(Config::class)->getMock(); + /** @var Request|MockObject $request */ + $request = $this->getMockBuilder(Request::class)->getMock(); + + $app = $this->getApp(['call', 'get', 'instance']); $this->setExpects($app, 'call', [[Request::class, 'createFromGlobals']], $request); + $this->setExpects($app, 'get', ['config'], $config); $this->setExpects($app, 'instance', ['request', $request]); + $this->setExpects($config, 'get', ['trusted_proxies'], $configuredProxies); - $serviceProvider = new RequestServiceProvider($app); + /** @var ServiceProvider|MockObject $serviceProvider */ + $serviceProvider = $this->getMockBuilder(RequestServiceProvider::class) + ->setConstructorArgs([$app]) + ->setMethods(['setTrustedProxies']) + ->getMock(); + $serviceProvider->expects($this->once()) + ->method('setTrustedProxies') + ->with($request, $trustedProxies); $serviceProvider->register(); } }