Certificados SSL (Traefik → Django) ===================================== NoxPanel sincroniza automáticamente los certificados Let's Encrypt gestionados por Traefik hacia la base de datos Django, permitiendo mostrar información de certificados en el dashboard de hosting. Visión General -------------- Traefik gestiona todos los certificados SSL via Let's Encrypt (ACME). Los certificados se almacenan en ``acme.json``. NoxPanel lee este fichero periódicamente y crea/actualiza registros ``HostingSSLCertificate`` en Django, vinculando cada certificado al sitio correspondiente. Flujo de Datos -------------- .. code-block:: text ┌──────────────┐ ACME ┌──────────────┐ mount ┌──────────────┐ │ Let's Encrypt│ ───────────> │ Traefik │ ──────────> │ acme.json │ └──────────────┘ └──────────────┘ └──────┬───────┘ │ ┌──────────────────────────────────┘ │ /traefik-certs/acme.json ▼ ┌──────────────┐ │ sync_ssl_ │ Celery (cada 6h) │ certificates │ o manual │ _from_ │ │ traefik() │ └──────┬───────┘ │ ▼ ┌──────────────┐ │ Hosting │ │ SSL │ │ Certificate │ │ (Django DB) │ └──────────────┘ Implementación -------------- Función Principal ~~~~~~~~~~~~~~~~~ **Ubicación**: ``hosting/services.py`` ``sync_ssl_certificates_from_traefik() → dict`` 1. Lee ``/traefik-certs/acme.json`` (montado desde el host) 2. Extrae todos los certificados del resolver ``letsencrypt`` 3. Decodifica los certificados PEM desde base64 4. Para cada certificado: a. Extrae el dominio principal y SANs b. Parsea la fecha de expiración con ``cryptography.x509`` c. Busca un ``HostingSite`` activo que coincida con el dominio d. Crea o actualiza un ``HostingSSLCertificate`` via ``update_or_create`` 5. Devuelve ``{'created': N, 'updated': N, 'errors': N}`` ``_parse_cert_expiry(cert_pem: str) → datetime`` Parsea un certificado PEM y extrae la fecha de expiración usando la librería ``cryptography`` (disponible como dependencia de ``paramiko``). Tarea Celery ~~~~~~~~~~~~~ **Ubicación**: ``hosting/tasks.py`` .. code-block:: python @shared_task(name='hosting.sync_ssl_certificates') def sync_ssl_certificates(): from hosting.services import sync_ssl_certificates_from_traefik return sync_ssl_certificates_from_traefik() Registrada en ``CELERY_BEAT_SCHEDULE`` (cada 6 horas): .. code-block:: python 'sync-ssl-certificates': { 'task': 'hosting.sync_ssl_certificates', 'schedule': 21600.0, # 6 horas }, Modelo ~~~~~~ ``HostingSSLCertificate`` en ``hosting/models.py``: .. list-table:: :header-rows: 1 :widths: 20 20 60 * - Campo - Tipo - Descripción * - ``site`` - ForeignKey - Sitio asociado (``HostingSite``) * - ``domain`` - CharField - Dominio del certificado * - ``type`` - CharField - Tipo: ``letsencrypt``, ``custom``, ``self_signed`` * - ``issuer`` - CharField - Entidad emisora (ej: ``Let's Encrypt``) * - ``expires_at`` - DateTimeField - Fecha de expiración * - ``is_active`` - BooleanField - Estado activo * - ``auto_renew`` - BooleanField - Renovación automática habilitada Dashboard --------- El dashboard de hosting muestra el conteo de certificados SSL usando los registros de ``HostingSSLCertificate``: .. code-block:: python # hosting/views.py "ssl_count": HostingSSLCertificate.objects.filter( site__tenant=tenant, is_active=True ).count() or HostingSite.objects.filter( tenant=tenant, ssl_enabled=True, is_active=True ).count() El modal de certificados muestra: dominio, tipo, emisor, fecha de expiración y estado de renovación automática. Ejecución Manual ----------------- Para ejecutar la sincronización manualmente: .. code-block:: bash # Via Django shell docker exec noxpanel-web-1 python manage.py shell -c \ "from hosting.services import sync_ssl_certificates_from_traefik; \ print(sync_ssl_certificates_from_traefik())" # Via Celery docker exec noxpanel-web-1 python manage.py shell -c \ "from hosting.tasks import sync_ssl_certificates; \ sync_ssl_certificates.delay()" Certificados SNI para Mail --------------------------- Los certificados para subdominios de correo (``mail.*``, ``imap.*``, ``smtp.*``, ``pop.*``) se sincronizan por separado via el script ``sync_sni_certs.sh``: - **Script**: ``app/scripts/sync_sni_certs.sh`` - **Frecuencia**: Cron cada 15 minutos en el host - **Destino**: Dovecot (``local_name`` blocks) + Postfix (``sni_maps``) - **Fuente**: Mismo ``acme.json`` de Traefik .. important:: Postfix requiere ``stop && start`` (no solo reload) tras actualizar SNI maps, ya que los procesos ``smtpd`` cachean la configuración SNI en memoria.