# SPEC / BACKLOG — Validação Mail TLS no Wizard (IMAP :993 + Cert Multi-SAN) **Versão:** 1.0 **Data:** 2026-06-12 **Autor:** Roger / Cursor DevOps **Estado:** 📋 **BACKLOG** (correcção manual VM112 ✅ concluída) **Depende de:** `010-admin-domain-validation/spec.md`, `SPEC-CORRECAO-ADMIN-DOMINIOS-VIRTUAIS.md` --- ## 1. Resposta directa **Sim** — os passos executados manualmente em Jun/2026 (cert multi-SAN 10 domínios, deploy `nginx.crt`, validação IMAP `:993`, renovação via CT114 HTTP-01) **devem fazer parte da sequência de validação e provisionamento do wizard**. Hoje o wizard valida **presença do hostname no certbot** (`cert_san`), mas **não valida o certificado efectivamente servido** em IMAP/SMTP — exactamente o cenário que causou **"Wrong Site" no Thunderbird**. --- ## 2. Lacuna actual | O que o wizard faz hoje | O que falta | |-------------------------|-------------| | `cert_san` → `certbot certificates` lista SANs | Runtime TLS em `:993` / `:465` | | `webmail_https` → GET `:443` | Cert `:6071` admin (spec 010, não implementada) | | `haproxy_sni` → ficheiro SNI CT114 | Deploy pós-expand (`carbonio-cert-deploy.sh`) | | — | Renovação automatizada (`certbot-renew-mail-vm112-multi.sh`) | | — | Alerta quando SAN falta após onboarding novo domínio | --- ## 3. Sequência alvo — 14 checks infra (P1 bloqueiam `ready`) ### 3.1 Já existem (6) — `infrastructure.py` ``` carbonio_domain → dns_mail → haproxy_sni → traefik_router → cert_san → webmail_https ``` ### 3.2 Admin — spec 010 (4) — **pendente implementação** ``` → admin_block_nginx → admin_redirect_443 → admin_cert_6071 → admin_login_6071 ``` Ver: `specs/010-admin-domain-validation/spec.md` ### 3.3 Mail TLS — **NOVO backlog (4)** | ID | Label | O que valida | Método | P | |----|-------|--------------|--------|---| | `imap_cert_san_993` | IMAP TLS :993 | Cert apresentado inclui `mail.{dominio}` no SAN | `openssl s_client -connect 127.0.0.1:993 -servername {host}` | **P1** | | `smtp_cert_san_465` | SMTP TLS :465 | Idem (cert global nginx/smtpd) | `openssl s_client -connect 127.0.0.1:465 -servername {host}` | **P2** | | `cert_deployed_global` | Cert deploy Carbonio | `nginx.crt` == `mail-vm112-multi/fullchain.pem` (hash ou mtime) | comparação ficheiros | **P1** | | `cert_renew_ready` | Renovação LE | Script + cron/systemd timer activo | `test -x /usr/local/bin/certbot-renew-mail-vm112-multi.sh` | **P2** | **Regra `ready`:** todos P1 OK. P2 = warning visível no Desk, não bloqueia onboarding. ### 3.4 Diagrama completo ```mermaid flowchart LR A[carbonio_domain] --> B[dns_mail] B --> C[haproxy_sni] C --> D[traefik_router] D --> E[cert_san LE lista] E --> F[webmail_https] F --> G[admin_block_nginx] G --> H[admin_redirect_443] H --> I[admin_cert_6071] I --> J[admin_login_6071] J --> K[imap_cert_san_993] K --> L[cert_deployed_global] L --> M[cert_renew_ready] ``` --- ## 4. Provisionamento — steps novos / alterados ### 4.1 Melhorar step `cert_san` existente **Problema:** expand LE com DNS multi-token falha para domínios em contas CF diferentes (bet*). **Solução implementada manualmente (Jun/2026):** | Componente | Path VM112 / CT114 | |------------|-------------------| | Hook HTTP-01 | `/usr/local/bin/cf-http-auth-all.sh` | | Hook cleanup | `/usr/local/bin/cf-http-cleanup-all.sh` | | Hook DNS multi (fallback) | `/usr/local/bin/cf-dns-auth-multi-zone.sh` | | Renew wrapper | `/usr/local/bin/certbot-renew-mail-vm112-multi.sh` | | Deploy | `/usr/local/bin/carbonio-cert-deploy.sh` | | CT114 nginx temp :80 | `docker run acme-http-80` (durante renew) | **Wizard `do_cert()` deve:** 1. Calcular SANs: todos `mail.*` activos + `nfe.{dominio}` se aplicável 2. Tentar expand `mail-vm112-multi` (HTTP-01 via CT114 — parar Traefik ~60s) 3. Executar `carbonio-cert-deploy.sh` 4. Re-correr checks `imap_cert_san_993` + `cert_deployed_global` 5. Emitir webhook `infra.cert.deployed` → VM122 ### 4.2 Novo step `mail_tls_verify` (só validação, pós-provision) Idempotente — não altera infra, apenas confirma runtime TLS. --- ## 5. Implementação wizard — ficheiros | Ficheiro | Acção | Esforço | |----------|-------|---------| | `services/mail_tls_validation.py` | **Criar** — checks :993/:465, cert deploy | M | | `services/admin_domain_validation.py` | **Criar** (spec 010) | M | | `services/infrastructure.py` | Integrar 8 checks (4 admin + 4 mail TLS) | M | | `services/infrastructure.py` `do_cert()` | HTTP-01 + CT114 orchestration | L | | `tests/test_mail_tls_validation.py` | Unit + mock openssl | S | | `tests/test_infrastructure_status.py` | Snapshot 14 steps | S | ### 5.1 Interface proposta — `mail_tls_validation.py` ```python def check_imap_cert_san(mail_host: str, port: int = 993) -> StepResult def check_smtp_cert_san(mail_host: str, port: int = 465) -> StepResult def check_cert_deployed_global() -> StepResult def check_cert_renew_ready() -> StepResult def verify_mail_tls_runtime(domain: str, mail_aliases: list[str] | None) -> list[StepResult] ``` **`check_imap_cert_san` — lógica:** ```python # openssl s_client -connect 127.0.0.1:993 -servername mail.example.com # Parse SAN; ok se mail_host in SAN ou CN == mail_host # ok=False → message: "Wrong Site — SAN não inclui {host} (CN={cn})" ``` --- ## 6. Integração VM122 Desk | Evento webhook | Quando | Payload mínimo | |----------------|--------|----------------| | `infra.mail_tls.failed` | `imap_cert_san_993` falha | domain, mail_host, cn, sans | | `infra.cert.deployed` | pós `carbonio-cert-deploy.sh` | domains[], expiry, serial | | `admin.validation.failed` | spec 010 | (já definido) | **UI Desk — card domínio:** | Coluna | Fonte | |--------|-------| | IMAP TLS | `imap_cert_san_993` | | Admin :6071 | `admin_cert_6071` | | Cert LE SAN | `cert_san` | | Último deploy | `cert_deployed_global` | --- ## 7. Referência — estado VM112 pós-correcção manual (baseline) | Check | 9 domínios Carbonio | Notas | |-------|---------------------|-------| | `cert_san` (lista LE) | ✅ 10 SANs | inclui bet* via HTTP-01 | | `imap_cert_san_993` | ✅ 10/10 | cert global multi-SAN | | `admin_*` (spec 010) | ✅ 9/9 | scripts offline OK | | `cert_renew_ready` | ✅ script manual | falta cron no wizard | **SANs actuais `mail-vm112-multi`:** ``` mail.ligbox.com.br, mail.diarissima.com, mail.dratcoin.com, mail.ibytera.com, mail.myvexx.com, nfe.diarissima.com, mail.betinplace.com, mail.betinsport.com, mail.eplacebets.com, mail.iofficebooks.com ``` --- ## 8. Backlog — fases de implementação ### Fase 1 — Validação runtime (sem alterar provision) — **P1** | ID | Task | Est. | |----|------|------| | WZ-TLS-1 | Criar `mail_tls_validation.py` | 4h | | WZ-TLS-2 | Adicionar `imap_cert_san_993` + `cert_deployed_global` em `get_status()` | 2h | | WZ-TLS-3 | Testes unitários openssl mock | 2h | | WZ-TLS-4 | CLI: `python -m app.services.mail_tls_validation --domain X` | 1h | **Entrega:** API `GET /infrastructure/status/{domain}` devolve checks IMAP. ### Fase 2 — Admin checks (spec 010) — **P1** | ID | Task | Est. | |----|------|------| | WZ-ADM-1 | `admin_domain_validation.py` (4 checks) | 6h | | WZ-ADM-2 | Integrar em `get_status()` + `admin_ready` flag | 2h | | WZ-ADM-3 | Reutilizar `check-admin-login-flow.mjs` via subprocess | 2h | ### Fase 3 — Provision cert + deploy automático — **P1** | ID | Task | Est. | |----|------|------| | WZ-TLS-5 | Refactor `do_cert()` — HTTP-01 CT114 orchestration | 8h | | WZ-TLS-6 | Idempotência: skip se SAN já presente | 2h | | WZ-TLS-7 | Cron systemd `certbot-renew-mail-vm112-multi.timer` | 1h | | WZ-TLS-8 | Webhook `infra.cert.deployed` → VM122 | 2h | ### Fase 4 — Desk + alertas — **P2** | ID | Task | Est. | |----|------|------| | OPS-TLS-1 | Card domínio: colunas IMAP + Admin TLS | 4h | | OPS-TLS-2 | Ticket auto `infra.mail_tls.failed` | 3h | | OPS-TLS-3 | Scorecard AUD: +2 checks mail TLS | 2h | --- ## 9. Critérios de aceitação - [ ] Novo domínio onboarded → wizard detecta SAN em falta **antes** do cliente configurar Thunderbird - [ ] `GET infrastructure/status/{domain}` inclui `imap_cert_san_993` com mensagem acionável ("Wrong Site") - [ ] `POST infrastructure/provision?step=cert_san` expande multi-SAN + deploy + re-valida IMAP - [ ] Renovação LE não exige intervenção manual (timer + script CT114) - [ ] Desk mostra estado TLS IMAP e Admin por domínio - [ ] Falha IMAP TLS gera evento webhook VM122 --- ## 10. Riscos e mitigação | Risco | Mitigação | |-------|-----------| | Renew para Traefik ~60s (porta 80) | Timer 03:00 UTC; script idempotente; alerta se renew falha | | bet* sem token CF DNS | Manter HTTP-01 via CT114 como path primário | | nginx mail sem SNI real | Documentar: cert **global** — SAN multi obrigatório | | Falso positivo `cert_san` (lista vs runtime) | Check `cert_deployed_global` + `imap_cert_san_993` | --- ## 11. Scripts / paths de referência (VM112) ``` /opt/ligbox-deploy/scripts/admin-login-check/ apply-admin-nginx-overrides.py check-admin-login-flow.mjs sync-traefik-admin-certs.sh /usr/local/bin/ carbonio-cert-deploy.sh certbot-renew-mail-vm112-multi.sh cf-http-auth-all.sh cf-http-cleanup-all.sh cf-dns-auth-multi-zone.sh /etc/letsencrypt/live/mail-vm112-multi/ /opt/zextras/conf/nginx.crt ← cert global IMAP/SMTP ``` --- ## 12. Índice relacionado | Documento | Conteúdo | |-----------|----------| | `specs/010-admin-domain-validation/spec.md` | Admin :6071 — 4 checks | | `specs/010-admin-domain-validation/correcao-vm112.md` | Correcção manual admin | | `SPEC-CORRECAO-ADMIN-DOMINIOS-VIRTUAIS.md` | Histórico VM112 | | `BACKLOG.md` (VM122) | Track WZ-TLS-* | --- *Próximo passo quando possível: **Fase 1** (WZ-TLS-1..4) — validação runtime sem risco de alterar produção.*