mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-13 16:14:42 +00:00
fix(api): secure proxy config, clone volumes, and rollback tags (#11003)
This commit is contained in:
1 parent
16972f6483
commit
e560bdf9e0
43 files changed
+16229
-29
No files matched your search
@@ -7,6 +7,7 @@ it('defines OpenAPI documentation for every destinations endpoint', function ()
|
||||
expect($model)->toContain("schema: 'Destination'")
|
||||
->and($controller)->toContain("operationId: 'list-destinations'")
|
||||
->and($controller)->toContain("operationId: 'get-destination-by-uuid'")
|
||||
->and($controller)->toContain("operationId: 'update-destination-by-uuid'")
|
||||
->and($controller)->toContain("operationId: 'delete-destination-by-uuid'")
|
||||
->and($controller)->toContain("operationId: 'list-server-destinations'")
|
||||
->and($controller)->toContain("operationId: 'create-server-destination'");
|
||||
|
||||
@@ -104,3 +104,56 @@ it('accepts explicitly allowlisted intranet S3 endpoints', function (string $end
|
||||
'Self-hosted MinIO on 172.16.x CIDR' => ['http://172.16.0.10:9000', ['172.16.0.0/12']],
|
||||
'Self-hosted MinIO on 192.168.x exact IP' => ['http://192.168.1.50:9000', ['192.168.1.50']],
|
||||
]);
|
||||
|
||||
it('accepts allowlisted docker-style MinIO hostnames that resolve to private IPs', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'webhook_allowed_internal_hosts' => ['coolify-minio'],
|
||||
]));
|
||||
|
||||
$validator = Validator::make(
|
||||
['endpoint' => 'http://coolify-minio:9000'],
|
||||
['endpoint' => ['required', 'max:255', new SafeWebhookUrl(fn (string $host): array => ['172.16.0.5'])]],
|
||||
);
|
||||
|
||||
expect($validator->passes())->toBeTrue('Expected coolify-minio with private IP to pass when allowlisted');
|
||||
});
|
||||
|
||||
it('rejects docker-style MinIO hostnames resolving to private IPs without allowlist', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'webhook_allowed_internal_hosts' => [],
|
||||
]));
|
||||
|
||||
$validator = Validator::make(
|
||||
['endpoint' => 'http://coolify-minio:9000'],
|
||||
['endpoint' => ['required', 'max:255', new SafeWebhookUrl(fn (string $host): array => ['172.16.0.5'])]],
|
||||
);
|
||||
|
||||
expect($validator->fails())->toBeTrue('Expected coolify-minio private IP rejection without allowlist');
|
||||
});
|
||||
|
||||
it('accepts allowlisted docker MinIO hostname after custom DNS miss and system DNS hit', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'custom_dns_servers' => '1.1.1.1',
|
||||
'webhook_allowed_internal_hosts' => ['coolify-minio'],
|
||||
]));
|
||||
|
||||
$rule = new class extends SafeWebhookUrl
|
||||
{
|
||||
protected function resolveHostWithCustomDnsServers(string $host, array $dnsServers): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function resolveHostWithSystemDns(string $host): array
|
||||
{
|
||||
return ['172.16.0.5'];
|
||||
}
|
||||
};
|
||||
|
||||
$validator = Validator::make(
|
||||
['endpoint' => 'http://coolify-minio:9000'],
|
||||
['endpoint' => ['required', 'max:255', $rule]],
|
||||
);
|
||||
|
||||
expect($validator->passes())->toBeTrue('Expected S3 endpoint to pass after DNS fallback + allowlist');
|
||||
});
|
||||
@@ -278,3 +278,103 @@ it('redacts webhook URLs for logs', function () {
|
||||
expect(SafeWebhookUrl::redactedUrlForLog('https://hooks.slack.com/services/T000/B000/secret-token?foo=bar'))
|
||||
->toBe('https://hooks.slack.com');
|
||||
});
|
||||
|
||||
it('falls back to system DNS when custom DNS returns no answers', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'custom_dns_servers' => '1.1.1.1',
|
||||
'webhook_allowed_internal_hosts' => ['coolify-minio'],
|
||||
]));
|
||||
|
||||
$rule = new class extends SafeWebhookUrl
|
||||
{
|
||||
/** @var array<int, array{0: string, 1: array<int, string>}> */
|
||||
public array $customCalls = [];
|
||||
|
||||
/** @var array<int, string> */
|
||||
public array $systemCalls = [];
|
||||
|
||||
protected function resolveHostWithCustomDnsServers(string $host, array $dnsServers): array
|
||||
{
|
||||
$this->customCalls[] = [$host, $dnsServers];
|
||||
|
||||
// Simulate public DNS (1.1.1.1) having no record for a docker hostname.
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function resolveHostWithSystemDns(string $host): array
|
||||
{
|
||||
$this->systemCalls[] = $host;
|
||||
|
||||
return ['172.16.0.5'];
|
||||
}
|
||||
};
|
||||
|
||||
$validator = Validator::make(
|
||||
['url' => 'http://coolify-minio:9000'],
|
||||
['url' => $rule],
|
||||
);
|
||||
|
||||
expect($validator->passes())->toBeTrue('Expected system-DNS fallback for allowlisted docker hostname')
|
||||
->and($rule->customCalls)->toHaveCount(1)
|
||||
->and($rule->customCalls[0][0])->toBe('coolify-minio')
|
||||
->and($rule->customCalls[0][1])->toBe(['1.1.1.1'])
|
||||
->and($rule->systemCalls)->toBe(['coolify-minio']);
|
||||
});
|
||||
|
||||
it('does not fall back to system DNS when custom DNS returns answers', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'custom_dns_servers' => '1.1.1.1',
|
||||
]));
|
||||
|
||||
$rule = new class extends SafeWebhookUrl
|
||||
{
|
||||
public int $systemCalls = 0;
|
||||
|
||||
protected function resolveHostWithCustomDnsServers(string $host, array $dnsServers): array
|
||||
{
|
||||
return ['93.184.216.34'];
|
||||
}
|
||||
|
||||
protected function resolveHostWithSystemDns(string $host): array
|
||||
{
|
||||
$this->systemCalls++;
|
||||
|
||||
return ['10.0.0.1'];
|
||||
}
|
||||
};
|
||||
|
||||
$validator = Validator::make(
|
||||
['url' => 'https://example.com/webhook'],
|
||||
['url' => $rule],
|
||||
);
|
||||
|
||||
expect($validator->passes())->toBeTrue()
|
||||
->and($rule->systemCalls)->toBe(0);
|
||||
});
|
||||
|
||||
it('still rejects private targets after system DNS fallback when host is not allowlisted', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'custom_dns_servers' => '1.1.1.1',
|
||||
'webhook_allowed_internal_hosts' => [],
|
||||
]));
|
||||
|
||||
$rule = new class extends SafeWebhookUrl
|
||||
{
|
||||
protected function resolveHostWithCustomDnsServers(string $host, array $dnsServers): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function resolveHostWithSystemDns(string $host): array
|
||||
{
|
||||
return ['172.16.0.5'];
|
||||
}
|
||||
};
|
||||
|
||||
$validator = Validator::make(
|
||||
['url' => 'http://coolify-minio:9000'],
|
||||
['url' => $rule],
|
||||
);
|
||||
|
||||
expect($validator->fails())->toBeTrue('Expected private IP rejection without allowlist after fallback');
|
||||
});
|
||||
Reference in new issue
Block a user