Gestionar certificados SSL en entornos multi-dominio (SaaS, plataformas de clientes, paneles) implica resolver un problema clave:

  • Algunos dominios están bajo tu control (Cloudflare con API)
  • Otros no (clientes externos)
  • Algunos usan proxy (Cloudflare naranja)
  • Otros no

En este artículo se muestra cómo construir una arquitectura robusta con Caddy que:

  • Usa DNS-01 cuando es posible
  • Usa HTTP-01 como fallback
  • Evita fallos en renovación automática

Problema base

Caddy usa ACME (Let's Encrypt) y necesita validar dominios.

Pero:

 
Proxy Cloudflare activo → HTTP-01 falla
Sin acceso al DNS → DNS-01 falla
 

Por tanto, necesitas una estrategia híbrida.


Objetivo de la arquitectura

Diseñar políticas TLS que:

  1. Usen DNS-01 para dominios conocidos (Cloudflare con token)
  2. Usen HTTP-01 para dominios externos
  3. Permitan fallback automático

Estructura de políticas TLS en Caddy (API)

Caddy permite definir múltiples políticas en:

 
/apps/tls/automation/policies
 

Cada política puede tener:

  • subjects
  • issuers
  • configuración de challenge

Política específica (Cloudflare con DNS-01)

Ejemplo para dominios que controlas:

 
{
"subjects": ["cineart.es", "*.cineart.es"],
"issuers": [
{
"module": "acme",
"challenges": {
"dns": {
"provider": {
"name": "cloudflare",
"api_token": "{env.CF_API_TOKEN_ACCOUNT_1}"
}
}
}
}
]
}
 

Esto garantiza:

  • Validación DNS-01
  • Compatible con proxy naranja
  • Soporte wildcard

Segunda cuenta de Cloudflare

 
{
"subjects": ["otrodominio.com"],
"issuers": [
{
"module": "acme",
"challenges": {
"dns": {
"provider": {
"name": "cloudflare",
"api_token": "{env.CF_API_TOKEN_ACCOUNT_2}"
}
}
}
}
]
}
 

Política catch-all (la clave)

Aquí está la parte importante:

 
{
"issuers": [
{
"module": "acme"
},
{
"module": "acme",
"challenges": {
"dns": {
"provider": {
"name": "cloudflare",
"api_token": "{env.CF_FALLBACK_TOKEN}"
}
}
}
}
]
}
 

Qué significa esto realmente

Orden de ejecución:

 
1. Intentar HTTP-01 (por defecto)
2. Si falla → intentar DNS-01 con Cloudflare
 

Esto permite:

  • Dominios externos → HTTP-01 funciona
  • Dominios Cloudflare conocidos → usan políticas específicas
  • Fallback razonable

Automatización en PHP (ejemplo real)

Basado en tu sistema:

 
public static function ensureTlsCatchAllPolicy(string $caddyApi): void
{
$policy = [
'issuers' => [
[
'module' => 'acme'
],
[
'module' => 'acme',
'challenges' => [
'dns' => [
'provider' => [
'name' => 'cloudflare',
'api_token' => getenv('CF_FALLBACK_TOKEN')
]
]
]
]
]
];

self::upsertPolicy($caddyApi, $policy);
}
 

Forzar regeneración de políticas

 
cd /opt/musedock-panel && php -r "
require_once 'app/Services/SystemService.php';
SystemService::ensureTlsCatchAllPolicy('http://localhost:2019');
"
 

Verificar configuración activa

 
curl -s localhost:2019/config/apps/tls/automation/policies | jq
 

Salida esperada:

 
CATCH-ALL → HTTP-01 → DNS-01
 

Forzar renovación / emisión

 
curl -X POST localhost:2019/load \
-H "Content-Type: application/json" \
-d "$(curl -s localhost:2019/config/)"
 

Debug en tiempo real

Logs de Caddy:

 
journalctl -u caddy -f
 

Buscar:

 
acme: obtaining certificate
challenge failed
http-01
dns-01
 

Caso real: dominio externo sin Cloudflare

 
aphroditefilmawards.com → IP directa
 

Resultado:

  • HTTP-01 funciona
  • Certificado emitido correctamente

Caso real: dominio en Cloudflare (tu cuenta)

 
cineart.es → proxy naranja
 

Resultado:

  • HTTP-01 falla
  • DNS-01 funciona (token disponible)

Caso problemático (cliente externo)

 
cliente.com → Cloudflare (cuenta externa) + proxy naranja
 

Resultado:

 
HTTP-01 → falla
DNS-01 → sin acceso
→ ERROR
 

Mitigaciones

Opción 1

El cliente te da API token:

 
→ Añades nueva política TLS
→ DNS-01 funciona
 

Opción 2

Cliente desactiva proxy:

 
→ HTTP-01 funciona
 

Opción 3 (manual)

Certificado de origen de Cloudflare:

 
→ Instalación manual
→ No ACME automático
 

Comparación con Nginx y Apache

Esto no es exclusivo de Caddy:

  • Nginx + Certbot → mismo problema
  • Apache HTTP Server → igual

Ejemplo Certbot HTTP-01:

 
certbot certonly --webroot -w /var/www/html -d dominio.com
 

Fallará detrás de proxy Cloudflare.


Buenas prácticas finales

  • Siempre tener HTTP-01 como fallback
  • Usar DNS-01 solo cuando tengas control del DNS
  • Evitar dependencias de terceros sin acceso API
  • Monitorizar certificados
  • Diseñar pensando en multi-tenant

Conclusión

Una arquitectura correcta de TLS en entornos multi-dominio no depende del servidor, sino de:

  • Control del DNS
  • Presencia de proxies
  • Estrategia de validación

Caddy permite resolver esto elegantemente mediante políticas dinámicas y múltiples issuers, pero los límites siguen siendo los del protocolo ACME.