mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 21:18:11 -04:00
feat(proxy): make the Traefik certificate resolver configurable per server
Coolify always added tls.certresolver=letsencrypt to generated HTTPS routers, so Traefik logged errors when the proxy used its own certificates (for example Cloudflare Origin CA) without that resolver. Add a server setting (default letsencrypt) in the proxy page and API. An empty value omits the resolver from resource labels, the Coolify dashboard routes, and the catch-all router. Closes #12138
This commit is contained in:
@@ -7,6 +7,7 @@ use App\Enums\ProxyTypes;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\RestartProxyJob;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerSetting;
|
||||
use App\Rules\SafeExternalUrl;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -41,6 +42,7 @@ class ServerProxyController extends Controller
|
||||
* redirect_enabled: bool,
|
||||
* redirect_url: string|null,
|
||||
* generate_exact_labels: bool,
|
||||
* traefik_certificate_resolver: string|null,
|
||||
* configuration?: string|null
|
||||
* }
|
||||
*/
|
||||
@@ -52,6 +54,7 @@ class ServerProxyController extends Controller
|
||||
'redirect_enabled' => (bool) data_get($server->proxy, 'redirect_enabled', true),
|
||||
'redirect_url' => data_get($server->proxy, 'redirect_url'),
|
||||
'generate_exact_labels' => (bool) ($server->settings->generate_exact_labels ?? false),
|
||||
'traefik_certificate_resolver' => $server->traefikCertificateResolver(),
|
||||
];
|
||||
|
||||
// Proxy compose can contain secrets; only expose with read:sensitive (and admin) like other APIs.
|
||||
@@ -86,6 +89,7 @@ class ServerProxyController extends Controller
|
||||
new OA\Property(property: 'redirect_enabled', type: 'boolean', example: true),
|
||||
new OA\Property(property: 'redirect_url', type: 'string', nullable: true, example: 'https://example.com'),
|
||||
new OA\Property(property: 'generate_exact_labels', type: 'boolean', example: false),
|
||||
new OA\Property(property: 'traefik_certificate_resolver', type: 'string', nullable: true, example: 'letsencrypt', description: 'Traefik certificate resolver for generated HTTPS routers. Null when the proxy uses its own certificates.'),
|
||||
new OA\Property(property: 'configuration', type: 'string', nullable: true, description: 'Docker Compose proxy configuration when stored in the database. Only present with read:sensitive.'),
|
||||
]
|
||||
)
|
||||
@@ -114,7 +118,7 @@ class ServerProxyController extends Controller
|
||||
|
||||
#[OA\Patch(
|
||||
summary: 'Update server proxy',
|
||||
description: 'Update proxy redirect settings, exact labels generation, and optionally the proxy type for a team-owned server.',
|
||||
description: 'Update proxy redirect settings, exact labels generation, the Traefik certificate resolver, and optionally the proxy type for a team-owned server.',
|
||||
path: '/servers/{uuid}/proxy',
|
||||
operationId: 'update-server-proxy',
|
||||
security: [['bearerAuth' => []]],
|
||||
@@ -130,6 +134,7 @@ class ServerProxyController extends Controller
|
||||
new OA\Property(property: 'redirect_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'redirect_url', type: 'string', nullable: true, description: 'Public http(s) redirect URL, or null to clear.'),
|
||||
new OA\Property(property: 'generate_exact_labels', type: 'boolean'),
|
||||
new OA\Property(property: 'traefik_certificate_resolver', type: 'string', nullable: true, description: 'Traefik certificate resolver for generated HTTPS routers (letters, digits, _ and -). Null or empty to use your own certificates. Redeploy resources to apply.'),
|
||||
new OA\Property(property: 'proxy_type', type: 'string', enum: ['traefik', 'caddy', 'nginx', 'none'], description: 'Proxy type (case-insensitive).'),
|
||||
]
|
||||
)
|
||||
@@ -146,6 +151,7 @@ class ServerProxyController extends Controller
|
||||
new OA\Property(property: 'redirect_enabled', type: 'boolean'),
|
||||
new OA\Property(property: 'redirect_url', type: 'string', nullable: true),
|
||||
new OA\Property(property: 'generate_exact_labels', type: 'boolean'),
|
||||
new OA\Property(property: 'traefik_certificate_resolver', type: 'string', nullable: true),
|
||||
new OA\Property(property: 'configuration', type: 'string', nullable: true),
|
||||
]
|
||||
)
|
||||
@@ -168,11 +174,12 @@ class ServerProxyController extends Controller
|
||||
return $return;
|
||||
}
|
||||
|
||||
$allowedFields = ['redirect_enabled', 'redirect_url', 'generate_exact_labels', 'proxy_type'];
|
||||
$allowedFields = ['redirect_enabled', 'redirect_url', 'generate_exact_labels', 'traefik_certificate_resolver', 'proxy_type'];
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'redirect_enabled' => 'boolean',
|
||||
'redirect_url' => ['nullable', 'string', new SafeExternalUrl],
|
||||
'generate_exact_labels' => 'boolean',
|
||||
'traefik_certificate_resolver' => ['nullable', 'string', 'max:255', 'regex:'.ServerSetting::TRAEFIK_CERTIFICATE_RESOLVER_PATTERN],
|
||||
'proxy_type' => 'string|nullable',
|
||||
]);
|
||||
|
||||
@@ -228,16 +235,29 @@ class ServerProxyController extends Controller
|
||||
$server->settings->save();
|
||||
}
|
||||
|
||||
$certificateResolverChanged = false;
|
||||
if ($request->exists('traefik_certificate_resolver')) {
|
||||
$server->settings->traefik_certificate_resolver = $request->input('traefik_certificate_resolver') ?: null;
|
||||
$certificateResolverChanged = $server->settings->isDirty('traefik_certificate_resolver');
|
||||
$server->settings->save();
|
||||
}
|
||||
|
||||
if ($request->has('proxy_type') && filled($request->proxy_type)) {
|
||||
$server->changeProxy($request->proxy_type, async: true);
|
||||
$server->refresh();
|
||||
}
|
||||
|
||||
// Apply redirect file on the server only when reachable (DB settings always saved above).
|
||||
if ($redirectChanged && $server->isFunctional()) {
|
||||
// The catch-all router in that file uses the certificate resolver too.
|
||||
if (($redirectChanged || $certificateResolverChanged) && $server->isFunctional()) {
|
||||
$server->setupDefaultRedirect();
|
||||
}
|
||||
|
||||
// Coolify's own dashboard routes use the resolver too.
|
||||
if ($certificateResolverChanged && $server->isFunctional()) {
|
||||
$server->setupDynamicProxyConfiguration();
|
||||
}
|
||||
|
||||
auditLog('api.server.proxy.updated', [
|
||||
'team_id' => $teamId,
|
||||
'server_uuid' => $server->uuid,
|
||||
|
||||
Reference in New Issue
Block a user