ligbox-ops-platform/specs/010-admin-domain-validation/spec.md
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

514 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SPEC — Integração Validação Admin Domínios Virtuais (Wizard VM112 + Desk VM122)
**Versão:** 1.0
**Data:** 2026-06-12
**Autor:** Roger / Cursor DevOps
**Estado:** 📋 **PLANEAMENTO** (correcção VM112 ✅ concluída)
**Depende de:** `SPEC-CORRECAO-ADMIN-DOMINIOS-VIRTUAIS.md`
---
## 1. Objectivo
Integrar a **validação e provisionamento admin Carbonio** (porta `:6071`, redirect `/admin`, certs SSL) no:
1. **Wizard VM112** (`/opt/ligbox-wizard/`) — onboarding automático por domínio virtual
2. **VM122 Ligbox Ops** (`ligbox-ops-platform`) — Desk, tickets, timeline, auditoria
**Problema actual:** validação admin existe em scripts offline (`admin-login-check/`) mas **não** no wizard API nem no Desk.
---
## 2. Contexto — dois conceitos de «admin»
| Conceito | URL | Onde vive hoje |
|----------|-----|----------------|
| **Painel delegado wizard** | `https://onboard.ligbox.com.br/admin` | SPA `DomainAdmin.jsx` + `/api/domain-admin/*` |
| **Carbonio Admin Console** | `https://mail.{dominio}/admin``:6071/static/login/` | nginx VM112 + Carbonio auth |
**Esta spec trata exclusivamente do Carbonio Admin Console por domínio virtual.**
---
## 3. Estado actual vs alvo
### 3.1 Wizard — `infrastructure.py` (6 checks hoje)
| ID actual | O que valida | Admin? |
|-----------|--------------|--------|
| `carbonio_domain` | zmprov domínio existe | ❌ |
| `dns_mail` | MX/A públicos | ❌ |
| `haproxy_sni` | SNI CT114 | ❌ |
| `traefik_router` | Router Traefik :443 | ❌ |
| `cert_san` | LE `mail-vm112-multi` (webmail) | ❌ (não :6071) |
| `webmail_https` | GET :443 | ❌ |
### 3.2 Novos checks admin (4 adicionais)
| ID novo | Label | O que valida | Severidade |
|---------|-------|--------------|------------|
| `admin_redirect_443` | Admin redirect :443 | `GET /admin` → 302 Location `:6071/static/login/` | **P1** |
| `admin_cert_6071` | Cert SSL admin :6071 | CN/SAN = `mail.{dominio}` no cert nginx :6071 | **P1** |
| `admin_login_6071` | Admin login :6071 | E4 `/zx/login/supported` JSON 200; E6 config OK | **P1** |
| `admin_block_nginx` | Bloco nginx admin | server block `:6071` existe (admin ou .custom) | **P2** |
### 3.3 Estado alvo — 10 checks infra
```
carbonio_domain → dns_mail → haproxy_sni → traefik_router → cert_san → webmail_https
→ admin_block_nginx → admin_redirect_443 → admin_cert_6071 → admin_login_6071
```
**`ready: true`** quando todos P1 OK (P2 = warning, não bloqueia onboarding).
---
## 4. Wizard VM112 — desenho técnico
### 4.1 Novo módulo Python
**Ficheiro:** `/opt/ligbox-wizard/backend/app/services/admin_domain_validation.py`
```python
# Funções principais (interface proposta)
def check_admin_redirect(mail_host: str) -> StepResult
def check_admin_cert_sni(mail_host: str) -> StepResult
def check_admin_login_flow(mail_host: str) -> StepResult
def check_admin_nginx_block(mail_host: str) -> StepResult
def provision_admin_infra(domain: str, mail_aliases: list[str] | None) -> ProvisionResult
```
**Implementação interna:**
| Check | Método |
|-------|--------|
| `admin_redirect_443` | `httpx` HEAD `https://{mail_host}/admin` → Location contém `:6071/static/login/` |
| `admin_cert_6071` | `openssl s_client -connect 127.0.0.1:6071 -servername {host}` ou leitura nginx ssl_certificate |
| `admin_login_6071` | Subprocess `node check-admin-login-flow.mjs --host {mail_host}` ou reimplementar E4/E6 em Python |
| `admin_block_nginx` | grep server block em `carbonio.admin` + `.custom` |
### 4.2 Alterações em `infrastructure.py`
**`get_status()`** — adicionar 4 steps após `webmail_https`:
```python
steps.append(check_admin_block_nginx(all_hosts))
steps.append(check_admin_redirect_443(all_hosts))
steps.append(check_admin_cert_6071(all_hosts))
steps.append(check_admin_login_6071(all_hosts))
```
**Payload de resposta (exemplo):**
```json
{
"domain": "iofficebooks.com",
"mail_host": "mail.iofficebooks.com",
"steps": [
{"id": "admin_redirect_443", "ok": true, "message": "302 → :6071/static/login/"},
{"id": "admin_cert_6071", "ok": true, "message": "CN=mail.iofficebooks.com"},
{"id": "admin_login_6071", "ok": true, "message": "E4 JSON 200, E6 domain OK"},
{"id": "admin_block_nginx", "ok": true, "message": "bloco :6071 presente"}
],
"admin_ready": true
}
```
**`provision()`** — novo step `admin_infra` após `cert_san`:
```python
def do_admin_infra() -> str:
# 1. sync-traefik-admin-certs.sh (se domínio usa cert Traefik)
# 2. apply-admin-nginx-overrides.py --reload
# 3. re-validar admin_redirect + admin_cert + admin_login
```
**Ordem provision completa:**
```
domain_site_layout → haproxy_sni → traefik_router → cert_san → admin_infra → (re-check all)
```
### 4.3 API routes (sem breaking changes)
| Método | Route | Alteração |
|--------|-------|-----------|
| GET | `/api/onboarding/infrastructure/status/{domain}` | +4 steps admin |
| POST | `/api/onboarding/infrastructure/provision?step=admin_infra` | Novo step |
| POST | `/api/onboarding/infrastructure/provision` | Inclui `admin_infra` no fluxo completo |
### 4.4 Frontend wizard (`App.jsx` / `WizardProcessHub`)
**UI — secção «Infraestrutura admin»** no painel de progresso:
| Step UI | Ícone OK | Ícone fail | Acção |
|---------|----------|------------|-------|
| Redirect `/admin` | ✅ | ⚠️ | «Abrir admin» link |
| Certificado :6071 | ✅ | ❌ | «Sincronizar cert» botão |
| Login admin | ✅ | ❌ | «Diagnosticar» → activity log |
| Bloco nginx | ✅ | ⚠️ | automático |
**Link pós-onboarding (Passo Concluído):**
```
Admin do domínio: https://mail.{dominio}/admin
(credenciais: admin@{dominio})
```
### 4.5 Activity log — mensagens
```
[infra] admin_redirect_443: OK — 302 → mail.iofficebooks.com:6071/static/login/
[infra] admin_cert_6071: FAIL — CN=mail.diarissima.com (esperado mail.iofficebooks.com)
[infra] admin_infra: sync Traefik certs + apply nginx overrides
[infra] admin_login_6071: OK — E4/E6 validados
```
### 4.6 Configuração (`.env` / `config.py`)
```python
# Novos settings
admin_check_scripts_dir: str = "/opt/ligbox-deploy/scripts/admin-login-check"
admin_nginx_apply_script: str = ".../apply-admin-nginx-overrides.py"
admin_traefik_sync_script: str = ".../sync-traefik-admin-certs.sh"
admin_canonical_url: str = "https://mail.ligbox.com.br:6071/static/login/"
```
### 4.7 Hook em `POST /account/create`
Após criar conta e auto-provision infra existente:
```python
# onboarding.py — após provision infra
infra = infrastructure.provision(domain, mail_aliases=aliases)
if not infra["status"].get("admin_ready"):
activity_log.warn(f"Admin infra incompleto: {domain}", source="infra")
# Não bloquear cliente — emitir webhook infra.partial
ops_webhook.emit("infra.synced", domain, session_id, data={
"steps": infra["status"]["steps"],
"admin_ready": infra["status"].get("admin_ready"),
"admin_url": f"https://mail.{domain}/admin",
})
```
---
## 5. VM122 Desk — desenho técnico
### 5.1 VM122 actual
| Componente | Path | Estado |
|------------|------|--------|
| API | `ligbox-ops-platform/api/app/main.py` | ✅ running |
| Desk UI | `frontend/``desk.ligbox.com.br` | ✅ |
| Webhook ingest | `POST /api/v1/webhooks/onboard` | ✅ |
| SQLite | tenants, tickets, webhook_events | ✅ |
### 5.2 Novos eventos webhook (extend contract 001 + 004)
| Event | Emissor | Quando | Cria ticket? |
|-------|---------|--------|--------------|
| `infra.synced` | VM112 wizard | Após provision infra completo | Nota em ticket existente |
| `admin.validation.failed` | VM112 wizard | admin_ready=false pós-provision | **Sim** (prioridade média) |
| `admin.cert.expiring` | VM122 cron/worker | Cert :6071 < 14 dias | **Sim** (prioridade baixa) |
**Payload `infra.synced` (extendido):**
```json
{
"event": "infra.synced",
"domain": "iofficebooks.com",
"session_id": "sess-abc123",
"data": {
"mail_host": "mail.iofficebooks.com",
"admin_ready": true,
"admin_url": "https://mail.iofficebooks.com/admin",
"admin_url_canonical": "https://mail.ligbox.com.br:6071/static/login/",
"steps": [
{"id": "admin_redirect_443", "ok": true},
{"id": "admin_cert_6071", "ok": true},
{"id": "admin_login_6071", "ok": true}
],
"provisioned_at": "2026-06-12T18:52:00Z"
}
}
```
**Payload `admin.validation.failed`:**
```json
{
"event": "admin.validation.failed",
"domain": "betinplace.com",
"session_id": "sess-xyz",
"data": {
"failed_steps": ["admin_cert_6071"],
"messages": ["CN mismatch: expected mail.betinplace.com"],
"suggested_action": "Run sync-traefik-admin-certs.sh + apply-admin-nginx-overrides.py --reload"
}
}
```
### 5.3 VM122 — alterações API
**Ficheiro:** `api/app/routers/webhooks.py` (ou equivalente)
```python
# Novos handlers
EVENT_HANDLERS = {
...
"infra.synced": handle_infra_synced, # update ticket + timeline
"admin.validation.failed": handle_admin_fail, # create ticket priority=medium
}
```
**Ficheiro:** `api/app/routers/desk.py` extend ticket detail
```python
# GET /api/v1/desk/tickets/{id}
# + admin_status block quando domain conhecido:
{
"admin_status": {
"ready": true,
"url": "https://mail.iofficebooks.com/admin",
"last_check": "2026-06-12T18:52:00Z",
"failed_steps": []
}
}
```
### 5.4 VM122 — novo endpoint auditoria admin
```
GET /api/v1/infra/admin-status/{domain}
```
**Resposta:** proxy para VM112 `GET /api/onboarding/infrastructure/status/{domain}` filtrando steps admin, ou VM122 armazena último `infra.synced`.
**Uso Desk:** widget «Admin domínio» no detalhe do ticket.
### 5.5 VM122 Desk UI — telas
#### Tela 1: Ticket detalhe — secção «Admin domínio»
```
┌─────────────────────────────────────────────────┐
│ Admin Carbonio — iofficebooks.com │
├─────────────────────────────────────────────────┤
│ URL: https://mail.iofficebooks.com/admin [↗] │
│ Redirect :443 ✅ 302 → :6071 │
│ Cert :6071 ✅ CN=mail.iofficebooks.com │
│ Login E4/E6 ✅ │
│ Última validação: 2026-06-12 18:52 UTC │
│ [Re-validar agora] [Abrir admin] │
└─────────────────────────────────────────────────┘
```
#### Tela 2: Dashboard Ops — widget «Admin domínios»
| Domínio | Admin OK | Cert :6071 | Último check |
|---------|----------|------------|--------------|
| iofficebooks.com | | | 12/06 18:52 |
| betinplace.com | | | 12/06 18:52 |
| novo-cliente.com | | | |
**Fonte:** agregação `webhook_events` tipo `infra.synced` + `admin.validation.failed`.
#### Tela 3: Runbook Desk — «Admin ERR_CERT»
Passos automáticos sugeridos (link para runbook):
1. Verificar Traefik tem cert para `mail.{dominio}`
2. Correr `sync-traefik-admin-certs.sh`
3. Correr `apply-admin-nginx-overrides.py --reload`
4. Re-validar via wizard ou script E1E8
### 5.6 Processos Desk Support
#### Processo 1: Onboarding novo domínio (automático)
```mermaid
sequenceDiagram
participant C as Cliente
participant W as Wizard VM112
participant C114 as Traefik CT114
participant V112 as Carbonio VM112
participant O as Ops VM122 Desk
C->>W: validate-domain + create account
W->>C114: provision SNI + router + cert :443
W->>V112: cert_san + admin_infra
Note over V112: sync Traefik certs + nginx overrides
W->>W: validate admin_redirect + cert + login
alt admin_ready=true
W->>O: webhook infra.synced (admin_ready=true)
O->>O: timeline + nota ticket
else admin_ready=false
W->>O: webhook admin.validation.failed
O->>O: ticket prioridade média + runbook
end
W->>C: onboarding.completed + link /admin
```
#### Processo 2: Ticket manual «Admin não abre»
| Passo | Actor | Acção |
|-------|-------|-------|
| 1 | NOC | Cliente reporta ERR_CERT ou 504 em `mail.X/admin` |
| 2 | Desk | Abrir ticket; campo domínio preenchido |
| 3 | Desk | `GET /api/v1/infra/admin-status/{domain}` |
| 4 | Técnico | Se cert fail sync Traefik + apply overrides |
| 5 | Técnico | Se redirect fail apply-admin-nginx-overrides.py |
| 6 | Técnico | Se NAT fail verificar pfSense :6071 VM112 |
| 7 | Desk | Re-validar; fechar ticket com evidência curl |
#### Processo 3: Renovação cert (preventivo)
| Trigger | Acção |
|---------|-------|
| Traefik renova LE (CT114) | Cron VM112: `sync-traefik-admin-certs.sh` semanal |
| Cert < 14 dias | VM122 emite `admin.cert.expiring` ticket low priority |
| zmproxyconfgen executado | **Obrigatório** `zmproxyconfgen-ligbox` |
#### Processo 4: Novo domínio virtual (pós-onboarding)
Quando ops adiciona domínio manualmente (`zmprov cd`):
1. Correr wizard `infrastructure/provision?domain=X` (ou script CLI)
2. Inclui step `admin_infra` automaticamente
3. Desk recebe `infra.synced` ou `admin.validation.failed`
---
## 6. Wire VM112 → VM122 (pré-requisitos)
### 6.1 Completar `ops_webhook.py` (VM112)
**Ficheiro:** `/opt/ligbox-wizard/backend/app/services/ops_webhook.py`
**Adicionar em `config.py`:**
```python
ops_webhook_enabled: bool = False
ops_webhook_url: str = "http://10.10.10.122:8080/api/v1/webhooks/onboard"
ops_webhook_secret: str = ""
```
**Montar em `main.py`:** routers `assist` se necessário.
### 6.2 Secret partilhado
VM112 `.env`:
```
OPS_WEBHOOK_SECRET=<same-as-VM122-WEBHOOK_SECRET>
OPS_WEBHOOK_ENABLED=true
```
VM122 `.env`:
```
WEBHOOK_SECRET=<shared>
```
---
## 7. Plano de implementação (fases)
### Fase 1 — Wizard backend (P1) — ~2-3 dias
| Task | Ficheiro | Esforço |
|------|----------|---------|
| Criar `admin_domain_validation.py` | services/ | M |
| Integrar 4 checks em `infrastructure.get_status()` | infrastructure.py | S |
| Step `admin_infra` em `provision()` | infrastructure.py | M |
| Config settings | config.py | S |
| Testes unitários checks | tests/ | M |
### Fase 2 — Wizard frontend (P1) — ~1-2 dias
| Task | Ficheiro | Esforço |
|------|----------|---------|
| UI steps admin no ProcessHub | frontend/ | M |
| Link admin pós-onboarding | App.jsx | S |
| Botão «Sincronizar admin» | frontend/ | S |
### Fase 3 — Webhooks VM112→VM122 (P1) — ~1-2 dias
| Task | Ficheiro | Esforço |
|------|----------|---------|
| Settings ops_webhook | config.py, .env | S |
| Emit `infra.synced` extendido | onboarding.py | S |
| Emit `admin.validation.failed` | onboarding.py | S |
| Handlers VM122 | api/app/ | M |
### Fase 4 — Desk UI admin widget (P2) — ~2-3 dias
| Task | Ficheiro | Esforço |
|------|----------|---------|
| Secção admin no ticket detail | frontend-desk/ | M |
| Widget dashboard domínios | frontend-desk/ | M |
| Endpoint proxy admin-status | api/app/ | S |
| Runbook ERR_CERT na UI | docs/ | S |
### Fase 5 — Automação ops (P2) — ~1 dia
| Task | Esforço |
|------|---------|
| Cron sync Traefik certs semanal | S |
| Alert cert expiring VM122 | M |
| Documentar zmproxyconfgen-ligbox em runbook Desk | S |
**Total estimado:** 7-11 dias dev.
---
## 8. Critérios de aceitação integração
### Wizard
- [ ] `GET infrastructure/status/{domain}` inclui 4 steps admin
- [ ] `POST infrastructure/provision` executa `admin_infra` automaticamente
- [ ] UI mostra estado admin no ProcessHub
- [ ] Link `mail.{dominio}/admin` no passo Concluído
- [ ] Novo domínio onboarding admin_ready=true sem intervenção manual
### VM122 Desk
- [ ] `infra.synced` aparece na timeline do ticket
- [ ] `admin.validation.failed` cria ticket com runbook
- [ ] Widget «Admin domínio» no detalhe do ticket
- [ ] Dashboard lista domínios com status admin
- [ ] Técnico consegue re-validar via API sem SSH
### Ops
- [ ] Cron sync certs Traefik documentado e activo
- [ ] Runbook Desk para ERR_CERT / 504 / redirect
- [ ] zmproxyconfgen-ligbox referenciado em procedimento padrão
---
## 9. Referências
| Documento | Conteúdo |
|-----------|----------|
| `SPEC-CORRECAO-ADMIN-DOMINIOS-VIRTUAIS.md` | Correcção implementada VM112 |
| `SPEC-ADMIN-DOMINIO-BLINDAGEM-NGINX.md` | Blindagem nginx |
| `SPEC-CARBONIO-ADMIN-LOGIN-FLOW-VALIDATION.md` | Fluxo E1E8 |
| `ligbox-ops-platform/specs/001-webhook-vm112-integration/` | Webhook MVP |
| `ligbox-ops-platform/specs/004-onboard-funnel-events/` | Funil onboarding |
| `ligbox-ops-platform/specs/009-ops-audit-overview/` | Audit scorecard |
---
## 10. Sincronização documentação
| Destino | Caminho |
|---------|---------|
| VM112 | `/opt/ligbox-deploy/docs/SPEC-INTEGRACAO-ADMIN-WIZARD-VM122-DESK.md` |
| Obsidian | `/root/obsidian-infra/carbonio/carbonio-server/docs/` |
| VM122 | `/root/obsidian-infra/ligbox-ops-platform/specs/010-admin-domain-validation/` |
| GitHub | `itecnologys/ibytera-mail-portal` + `ligbox-ops-platform` |
---
*Roger — planeamento integração Wizard + Desk — 2026-06-12*