mirror of
https://github.com/tiennm99/coolify.git
synced 2026-04-17 17:21:04 +00:00
feat: implement TrustHosts middleware to handle FQDN and IP address trust logic
This commit is contained in:
@@ -14,7 +14,7 @@ class Kernel extends HttpKernel
|
|||||||
* @var array<int, class-string|string>
|
* @var array<int, class-string|string>
|
||||||
*/
|
*/
|
||||||
protected $middleware = [
|
protected $middleware = [
|
||||||
// \App\Http\Middleware\TrustHosts::class,
|
\App\Http\Middleware\TrustHosts::class,
|
||||||
\App\Http\Middleware\TrustProxies::class,
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
\Illuminate\Http\Middleware\HandleCors::class,
|
\Illuminate\Http\Middleware\HandleCors::class,
|
||||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||||
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
class TrustHosts extends Middleware
|
class TrustHosts extends Middleware
|
||||||
{
|
{
|
||||||
@@ -13,8 +15,25 @@ class TrustHosts extends Middleware
|
|||||||
*/
|
*/
|
||||||
public function hosts(): array
|
public function hosts(): array
|
||||||
{
|
{
|
||||||
return [
|
$trustedHosts = [];
|
||||||
$this->allSubdomainsOfApplicationUrl(),
|
// Trust the configured FQDN from InstanceSettings
|
||||||
];
|
try {
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if ($settings && $settings->fqdn) {
|
||||||
|
$url = Url::fromString($settings->fqdn);
|
||||||
|
$host = $url->getHost();
|
||||||
|
if ($host) {
|
||||||
|
$trustedHosts[] = $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// If instance settings table doesn't exist yet (during installation),
|
||||||
|
// fall back to APP_URL only
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trust all subdomains of APP_URL as fallback
|
||||||
|
$trustedHosts[] = $this->allSubdomainsOfApplicationUrl();
|
||||||
|
|
||||||
|
return array_filter($trustedHosts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
142
tests/Feature/TrustHostsMiddlewareTest.php
Normal file
142
tests/Feature/TrustHostsMiddlewareTest.php
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Middleware\TrustHosts;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
|
||||||
|
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||||
|
|
||||||
|
it('trusts the configured FQDN from InstanceSettings', function () {
|
||||||
|
// Create instance settings with FQDN
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'https://coolify.example.com']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
expect($hosts)->toContain('coolify.example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects password reset request with malicious host header', function () {
|
||||||
|
// Set up instance settings with legitimate FQDN
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'https://coolify.example.com']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
// The malicious host should NOT be in the trusted hosts
|
||||||
|
expect($hosts)->not->toContain('coolify.example.com.evil.com');
|
||||||
|
expect($hosts)->toContain('coolify.example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles missing FQDN gracefully', function () {
|
||||||
|
// Create instance settings without FQDN
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => null]
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
// Should still return APP_URL pattern without throwing
|
||||||
|
expect($hosts)->not->toBeEmpty();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters out null and empty values from trusted hosts', function () {
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => '']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
// Should not contain empty strings or null
|
||||||
|
foreach ($hosts as $host) {
|
||||||
|
if ($host !== null) {
|
||||||
|
expect($host)->not->toBeEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts host from FQDN with protocol and port', function () {
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'https://coolify.example.com:8443']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
expect($hosts)->toContain('coolify.example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles exception during InstanceSettings fetch', function () {
|
||||||
|
// Drop the instance_settings table to simulate installation
|
||||||
|
\Schema::dropIfExists('instance_settings');
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
|
||||||
|
// Should not throw an exception
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
expect($hosts)->not->toBeEmpty();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trusts IP addresses with port', function () {
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'http://65.21.3.91:8000']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
expect($hosts)->toContain('65.21.3.91');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trusts IP addresses without port', function () {
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'http://192.168.1.100']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
expect($hosts)->toContain('192.168.1.100');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects malicious host when using IP address', function () {
|
||||||
|
// Simulate an instance using IP address
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'http://65.21.3.91:8000']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
// The malicious host attempting to mimic the IP should NOT be trusted
|
||||||
|
expect($hosts)->not->toContain('65.21.3.91.evil.com');
|
||||||
|
expect($hosts)->not->toContain('evil.com');
|
||||||
|
expect($hosts)->toContain('65.21.3.91');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trusts IPv6 addresses', function () {
|
||||||
|
InstanceSettings::updateOrCreate(
|
||||||
|
['id' => 0],
|
||||||
|
['fqdn' => 'http://[2001:db8::1]:8000']
|
||||||
|
);
|
||||||
|
|
||||||
|
$middleware = new TrustHosts($this->app);
|
||||||
|
$hosts = $middleware->hosts();
|
||||||
|
|
||||||
|
// IPv6 addresses are enclosed in brackets, getHost() should handle this
|
||||||
|
expect($hosts)->toContain('[2001:db8::1]');
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user