mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-13 02:14:36 +00:00
fix(backups): pin all resolved addresses in one CURLOPT_RESOLVE entry (#11061)
This commit is contained in:
2 files changed
+36
-5
No files matched your search
@@ -111,11 +111,20 @@ class SafeWebhookUrl implements ValidationRule
|
||||
return $options;
|
||||
}
|
||||
|
||||
// libcurl keeps only the last CURLOPT_RESOLVE entry for a given
|
||||
// host:port pair, so every resolved address must be pinned in a single
|
||||
// comma-separated entry. Emitting one entry per address silently drops
|
||||
// all but the last, which is an IPv6 address whenever the host has AAAA
|
||||
// records -- and that fails instantly on hosts without IPv6 egress.
|
||||
$addresses = implode(',', array_map(
|
||||
fn (string $ip): string => filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '['.$ip.']' : $ip,
|
||||
$target['ips'],
|
||||
));
|
||||
|
||||
$options['curl'] = [
|
||||
CURLOPT_RESOLVE => array_map(
|
||||
fn (string $ip): string => sprintf('%s:%d:%s', $target['host'], $target['port'], filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '['.$ip.']' : $ip),
|
||||
$target['ips'],
|
||||
),
|
||||
CURLOPT_RESOLVE => [
|
||||
sprintf('%s:%d:%s', $target['host'], $target['port'], $addresses),
|
||||
],
|
||||
];
|
||||
|
||||
return $options;
|
||||
|
||||
@@ -219,10 +219,32 @@ it('builds HTTP client options that pin resolved DNS for the request', function
|
||||
expect($options['allow_redirects'])->toBeFalse();
|
||||
|
||||
if (defined('CURLOPT_RESOLVE')) {
|
||||
expect($options['curl'][CURLOPT_RESOLVE])->toContain('localhost:8080:127.0.0.1');
|
||||
expect($options['curl'][CURLOPT_RESOLVE])->toContain('localhost:8080:127.0.0.1,[::1]');
|
||||
}
|
||||
});
|
||||
|
||||
it('pins every resolved address in a single CURLOPT_RESOLVE entry', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], [
|
||||
'webhook_allowed_internal_hosts' => ['localhost'],
|
||||
'webhook_allow_localhost' => true,
|
||||
]));
|
||||
|
||||
if (! defined('CURLOPT_RESOLVE')) {
|
||||
expect(true)->toBeTrue();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$entries = SafeWebhookUrl::httpClientOptions('http://localhost:8080/webhook')['curl'][CURLOPT_RESOLVE];
|
||||
|
||||
// localhost resolves to both 127.0.0.1 and ::1. libcurl overrides an
|
||||
// existing host:port cache entry with each new one, so multiple entries
|
||||
// would leave only [::1] pinned and break every request on hosts without
|
||||
// IPv6 egress.
|
||||
expect($entries)->toHaveCount(1)
|
||||
->and($entries[0])->toBe('localhost:8080:127.0.0.1,[::1]');
|
||||
});
|
||||
|
||||
it('fails closed while building HTTP options when the send-time resolution is unsafe', function () {
|
||||
expect(fn () => SafeWebhookUrl::httpClientOptions('http://localhost:8080/webhook'))
|
||||
->toThrow(RuntimeException::class, 'unsafe IP address');
|
||||
|
||||
Reference in new issue
Block a user