mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
Merge remote-tracking branch 'origin/release/v4.5' into automation/sync-main-to-release-v4.5
# Conflicts: # config/constants.php # tests/Feature/Api/ApplicationSettingsApiTest.php
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Actions\Shared\MigrateResourceToDestination;
|
||||
use App\Enums\BuildPackTypes;
|
||||
use App\Enums\HttpBasicAuthHashAlgorithm;
|
||||
use App\Enums\RedirectTypes;
|
||||
use App\Enums\StaticImageTypes;
|
||||
use App\Models\Application;
|
||||
@@ -276,6 +277,10 @@ function sharedDataApplications(?Application $application = null): array
|
||||
'docker_compose_custom_build_command' => ValidationPatterns::shellSafeCommandRules(),
|
||||
'is_container_label_escape_enabled' => 'boolean',
|
||||
'is_preserve_repository_enabled' => 'boolean',
|
||||
'http_basic_auth_hash_algorithm' => Rule::enum(HttpBasicAuthHashAlgorithm::class),
|
||||
'http_basic_auth_bcrypt_cost' => 'integer|min:4|max:14',
|
||||
'http_basic_auth_argon2id_memory_cost' => 'integer|min:8192|max:262144',
|
||||
'http_basic_auth_argon2id_time_cost' => 'integer|min:1|max:12',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -766,7 +766,20 @@ function isNoindexDomain(string $domain, ?Collection $noindex_domains): bool
|
||||
->contains(ValidationPatterns::normalizeApplicationDomainUrl($domain));
|
||||
}
|
||||
|
||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null, bool $is_traffic_analytics_enabled = false, array $domainPortOverrides = [], bool $supports_log_append = false, bool $supports_basic_auth_directive = false)
|
||||
/**
|
||||
* Caddy site address of a domain. Maintenance blocks in the base Caddyfile must use the same
|
||||
* address as the labels, so caddy-docker-proxy merges them into one site block.
|
||||
*/
|
||||
function caddySiteAddress(string $scheme, string $host, bool $isForceHttpsEnabled): string
|
||||
{
|
||||
if ($scheme === 'https' && ! $isForceHttpsEnabled) {
|
||||
return "http://{$host}, https://{$host}";
|
||||
}
|
||||
|
||||
return "{$scheme}://{$host}";
|
||||
}
|
||||
|
||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null, bool $is_traffic_analytics_enabled = false, array $domainPortOverrides = [], bool $supports_log_append = false, bool $supports_basic_auth_directive = false, ?int $http_basic_auth_bcrypt_cost = null, array $http_basic_auth_argon2id_options = [])
|
||||
{
|
||||
$labels = collect([]);
|
||||
if ($serviceLabels) {
|
||||
@@ -777,7 +790,9 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
||||
|
||||
$is_http_basic_auth_enabled = $is_http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
$hashedPassword = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => 10]);
|
||||
$hashedPassword = $http_basic_auth_argon2id_options
|
||||
? password_hash($http_basic_auth_password, PASSWORD_ARGON2ID, $http_basic_auth_argon2id_options)
|
||||
: password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => $http_basic_auth_bcrypt_cost ?? 10]);
|
||||
}
|
||||
|
||||
$trafficAppKey = caddyTrafficAppKey($uuid, $service_name);
|
||||
@@ -788,10 +803,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
||||
$path = $url->getPath();
|
||||
$host_without_www = str($host)->replace('www.', '');
|
||||
$schema = $url->getScheme();
|
||||
$siteAddress = "{$schema}://{$host}";
|
||||
if ($schema === 'https' && ! $is_force_https_enabled) {
|
||||
$siteAddress = "http://{$host}, https://{$host}";
|
||||
}
|
||||
$siteAddress = caddySiteAddress($schema, $host, $is_force_https_enabled);
|
||||
$portlessDomain = ServiceApplication::withoutPort($domain);
|
||||
$port = $url->getPort() ?? ($domainPortOverrides[$portlessDomain] ?? null);
|
||||
$handle = 'handle_path';
|
||||
@@ -834,6 +846,9 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
// Caddy 2.8 renamed basicauth to basic_auth; see Server::caddySupportsBasicAuthDirective().
|
||||
$basicAuthDirective = $supports_basic_auth_directive ? 'basic_auth' : 'basicauth';
|
||||
if ($http_basic_auth_argon2id_options) {
|
||||
$labels->push("caddy_{$loop}.{$basicAuthDirective}=argon2id");
|
||||
}
|
||||
$labels->push("caddy_{$loop}.{$basicAuthDirective}.{$http_basic_auth_username}=\"{$hashedPassword}\"");
|
||||
}
|
||||
if ($is_traffic_analytics_enabled) {
|
||||
@@ -907,7 +922,7 @@ function dockerComposeServicePorts(?string $compose, ?string $serviceName): arra
|
||||
->unique()->values()->all();
|
||||
}
|
||||
|
||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both', bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null, bool $escape_redirect_replacement_for_compose = true, array $domainPortOverrides = [])
|
||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both', bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null, bool $escape_redirect_replacement_for_compose = true, array $domainPortOverrides = [], ?int $http_basic_auth_bcrypt_cost = null)
|
||||
{
|
||||
$labels = collect([]);
|
||||
$labels->push('traefik.enable=true');
|
||||
@@ -919,7 +934,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$is_http_basic_auth_enabled = $is_http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
|
||||
$http_basic_auth_label = "http-basic-auth-{$uuid}";
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
$hashedPassword = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => 10]);
|
||||
$hashedPassword = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => $http_basic_auth_bcrypt_cost ?? 10]);
|
||||
}
|
||||
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
@@ -1186,6 +1201,10 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
$appUuid = $appUuid.'-pr-'.$pull_request_id;
|
||||
}
|
||||
$labels = collect([]);
|
||||
$httpBasicAuthArgon2idOptions = $application->usesArgon2idBasicAuth() ? [
|
||||
'memory_cost' => $application->http_basic_auth_argon2id_memory_cost,
|
||||
'time_cost' => $application->http_basic_auth_argon2id_time_cost,
|
||||
] : [];
|
||||
if ($pull_request_id === 0) {
|
||||
if ($application->fqdn) {
|
||||
$domains = str(data_get($application, 'fqdn'))->explode(',');
|
||||
@@ -1205,6 +1224,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
noindex_domains: $noindexDomains,
|
||||
domainPortOverrides: $application->domain_port_overrides ?? [],
|
||||
));
|
||||
@@ -1222,6 +1242,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
http_basic_auth_argon2id_options: $httpBasicAuthArgon2idOptions,
|
||||
noindex_domains: $noindexDomains,
|
||||
is_traffic_analytics_enabled: $application->destination->server->isTrafficAnalyticsEnabled(),
|
||||
supports_log_append: $application->destination->server->caddySupportsLogAppend(),
|
||||
@@ -1242,6 +1264,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
noindex_domains: $noindexDomains,
|
||||
escape_redirect_replacement_for_compose: false,
|
||||
domainPortOverrides: $application->domain_port_overrides ?? [],
|
||||
@@ -1258,6 +1281,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
http_basic_auth_argon2id_options: $httpBasicAuthArgon2idOptions,
|
||||
noindex_domains: $noindexDomains,
|
||||
is_traffic_analytics_enabled: $application->destination->server->isTrafficAnalyticsEnabled(),
|
||||
supports_log_append: $application->destination->server->caddySupportsLogAppend(),
|
||||
@@ -1288,6 +1313,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
noindex_domains: $noindexDomains,
|
||||
escape_redirect_replacement_for_compose: false,
|
||||
domainPortOverrides: $preview->domain_port_overrides ?? [],
|
||||
@@ -1305,6 +1331,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
http_basic_auth_argon2id_options: $httpBasicAuthArgon2idOptions,
|
||||
noindex_domains: $noindexDomains,
|
||||
is_traffic_analytics_enabled: $application->destination->server->isTrafficAnalyticsEnabled(),
|
||||
supports_log_append: $application->destination->server->caddySupportsLogAppend(),
|
||||
@@ -1324,6 +1352,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
noindex_domains: $noindexDomains,
|
||||
escape_redirect_replacement_for_compose: false,
|
||||
domainPortOverrides: $preview->domain_port_overrides ?? [],
|
||||
@@ -1339,6 +1368,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
http_basic_auth_bcrypt_cost: $application->http_basic_auth_bcrypt_cost,
|
||||
http_basic_auth_argon2id_options: $httpBasicAuthArgon2idOptions,
|
||||
noindex_domains: $noindexDomains,
|
||||
is_traffic_analytics_enabled: $application->destination->server->isTrafficAnalyticsEnabled(),
|
||||
supports_log_append: $application->destination->server->caddySupportsLogAppend(),
|
||||
@@ -2078,3 +2109,37 @@ function injectDockerComposeBuildArgs(string $command, string $buildArgsString):
|
||||
|
||||
return $modifiedCommand ?? $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a command that uses the Railpack BuildKit builder, then stop the builder when no other
|
||||
* process uses it. An idle buildkitd keeps the memory of earlier builds until it restarts.
|
||||
*
|
||||
* The lock file is in the buildx metadata directory, which all helper containers of a server share.
|
||||
* The script has no `&&`, `||`, pipe, or `$(`, because the sudo parser would put sudo into it.
|
||||
*/
|
||||
function railpackBuilderLockedScript(string $command): string
|
||||
{
|
||||
return 'exec 9>/root/.docker/buildx/coolify-railpack.lock; if flock -s 9; then :; else exit $?; fi; '
|
||||
."{$command}; status=\$?; "
|
||||
.'if flock -n -x 9; then DOCKER_CONFIG=/root/.docker docker buildx stop coolify-railpack >/dev/null 2>&1; fi; '
|
||||
.'exit $status';
|
||||
}
|
||||
|
||||
/**
|
||||
* The buildx metadata of a server, as the `docker run -v` source that the deployment helper uses.
|
||||
*/
|
||||
function railpackBuildxMetadataVolume(Server $server): string
|
||||
{
|
||||
return isDev() && $server->isLocalhost() ? 'coolify-buildx' : '$HOME/.docker/buildx';
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a command for the Railpack builder from a temporary helper container, then stop the builder
|
||||
* when no build uses it.
|
||||
*/
|
||||
function railpackBuilderHelperCommand(string $buildxMetadataVolume, string $helperImage, string $command): string
|
||||
{
|
||||
$script = railpackBuilderLockedScript($command);
|
||||
|
||||
return "docker run --rm -v {$buildxMetadataVolume}:/root/.docker/buildx -v /var/run/docker.sock:/var/run/docker.sock {$helperImage} bash -c '{$script}' 2>/dev/null || true";
|
||||
}
|
||||
|
||||
@@ -950,3 +950,16 @@ function getTraefikVersionFromDockerCompose(Server $server): ?string
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation rule for an HTML page that Coolify sends to the proxy in one shell command
|
||||
* (see Server::PROXY_ERROR_PAGE_MAX_BYTES).
|
||||
*/
|
||||
function proxyPageSizeRule(string $name): Closure
|
||||
{
|
||||
return function (string $attribute, mixed $value, Closure $fail) use ($name): void {
|
||||
if (strlen((string) $value) > Server::PROXY_ERROR_PAGE_MAX_BYTES) {
|
||||
$fail("The {$name} must not be larger than ".(Server::PROXY_ERROR_PAGE_MAX_BYTES / 1024).' KB.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user