movidas openspec a /docs
This commit is contained in:
parent
0ef0c9951c
commit
aae6b6096e
9 changed files with 486 additions and 0 deletions
106
docs/openspec/changes/security-audit-hardening/proposal.md
Normal file
106
docs/openspec/changes/security-audit-hardening/proposal.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Proposal — Auditoría de seguridad y hardening (Odoo 18.0)
|
||||
|
||||
## Why
|
||||
|
||||
Detectamos ataques automatizados cada vez más sofisticados que la heurística de fail2ban no frena,
|
||||
y somos responsables de la seguridad de nuestra infra y la de nuestros clientes. Necesitamos (a) un
|
||||
proceso de auditoría **reproducible** tipo "checklist de prevuelo" para verificar la resiliencia
|
||||
ante fuerza bruta, acceso indebido a datos vía API y vectores inadvertidos; y (b) corregir cinco
|
||||
hallazgos ya confirmados leyendo el propio código de `website_sale_aplicoop`.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Dejar la infra Odoo con una postura de seguridad auditable y repetible, y cerrar los hallazgos
|
||||
H1–H5 con tests que impidan su regresión.
|
||||
|
||||
## Alcance
|
||||
|
||||
- **Solo nuestra infra** (autorización directa, sin terceros). Producción está tras nginx/traefik
|
||||
con TLS.
|
||||
- Auditoría **activa autorizada** (fuerza bruta controlada contra cuenta de test y fuzzing de
|
||||
endpoints) desde la máquina Kali de la red y desde `localhost` vía SSH, preferentemente contra un
|
||||
clon de staging o en ventana de mantenimiento con snapshot previo.
|
||||
- Remediación de los hallazgos confirmados en `website_sale_aplicoop` (+ `groups_id` en la server
|
||||
action de mandatos SEPA de `account_banking_mandate_batch`).
|
||||
|
||||
## No-objetivos
|
||||
|
||||
- No se audita ni escanea infra de terceros ni hosting ajeno.
|
||||
- No se corrige aquí ningún hallazgo **nuevo** que surja durante la auditoría activa: se registra en
|
||||
el findings register y se aborda en un change posterior (salvo severidad crítica, que se escalará).
|
||||
- No se modifica `ocb/` ni los addons OCA originales.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Proceso de auditoría**: se añaden entregables de documentación (`docs/SECURITY_AUDIT_CHECKLIST.md`,
|
||||
`docs/SECURITY_AUDIT_RUNBOOK.md`, `docs/SECURITY_FINDINGS.md`) y scripts no destructivos en
|
||||
`scripts/security/` (probe de ACL vía API, probe de IDOR, harness de fuerza bruta, PoC de CSRF,
|
||||
recon web) parametrizados con cuentas de test.
|
||||
- **Fix H1 (Alta)** — ACL con `group_id` vacío que concede write+create global sobre `group.order` y
|
||||
`group.order.slot`: se reemplaza por ACLs de mínimo privilegio (lectura interna/portal explícitas;
|
||||
escritura solo manager). Los writes de los controladores usan `sudo()` y no dependen de estas ACLs.
|
||||
- **Fix H2 (Alta)** — record rule de `product.supplierinfo` con `domain=[(1,'=',1)]` que expone todos
|
||||
los precios de proveedor al portal. El portal usa `supplierinfo` para pintar el **origen** del
|
||||
producto y el **proveedor principal** en la web, así que no se elimina sin más: o se preparan esos
|
||||
valores server-side (vía `sudo()` en el controlador, según la regla "sin lógica en QWeb") o se acota
|
||||
el dominio a los productos accesibles por el usuario, sin exponer campos de coste/precio.
|
||||
- **Fix H3 (Media)** — endpoints de cambio de estado con `csrf=False`: se convierten a `type="json"`
|
||||
(bloquea el POST de formulario cross-site), tomando como plantilla `confirm_order_from_portal`.
|
||||
- **Fix H4 (Media)** — endpoints de lectura/ajax que solo comprueban `exists()`+`state`: se añade
|
||||
comprobación de pertenencia al grupo reutilizando `_validate_user_group_access` /
|
||||
`_get_consumer_group_for_user`.
|
||||
- **Fix H5 (Baja)** — `t-raw` inyectado en `<script>`: se sustituye por un patrón de salida seguro.
|
||||
- **Hardening de config**: `admin_passwd`, `list_db=False`, `proxy_mode=True`, password de BD fuerte,
|
||||
rate-limit/cabeceras en nginx, jails de fail2ban (web + API). Documentado como requisitos; los
|
||||
cambios de infra no versionable se ejecutan en el servidor, los versionables (`groups_id` en la
|
||||
server action) en el repo.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `eskaera-access-control`: cómo `website_sale_aplicoop` restringe acceso a datos y acciones —
|
||||
ACL de mínimo privilegio, alcance de `product.supplierinfo`, autorización por pertenencia al grupo
|
||||
de consumo en todos los endpoints, protección CSRF en endpoints de estado, y escapado de salida en
|
||||
plantillas. Cubre H1–H5.
|
||||
- `security-audit-process`: proceso repetible de auditoría — reglas de compromiso/autorización,
|
||||
checklist de prevuelo, runbook de auditoría activa, scripts de prueba y registro de hallazgos.
|
||||
- `infra-hardening`: requisitos de endurecimiento de producción — `odoo.conf`, reverse proxy
|
||||
(nginx/traefik) con TLS/rate-limit/cabeceras, fail2ban (web + API), y gestión de secretos/BD.
|
||||
|
||||
### Modified Capabilities
|
||||
- (ninguna — no existen specs previas en `openspec/specs/`)
|
||||
|
||||
## Impact
|
||||
|
||||
- **Addons afectados**: `website_sale_aplicoop` (security CSV + record rules, controladores,
|
||||
plantilla, tests, bump de `__manifest__.py`); `account_banking_mandate_batch` (`groups_id` en la
|
||||
server action).
|
||||
- **Addons NO afectados**: `ocb/`, los addons OCA originales, y el resto de custom (solo se auditan;
|
||||
cualquier hallazgo se registra, no se modifica en este change).
|
||||
- **Nuevas rutas en el repo**: `docs/SECURITY_*`, `scripts/security/`.
|
||||
- **Impacto UI/website/QWeb**: al convertir endpoints a `type="json"` hay que ajustar el transporte
|
||||
en el JS de `website_sale_aplicoop` (solo transporte, sin lógica). La plantilla
|
||||
`load_from_history_templates.xml` cambia su forma de inyectar JSON. Sin cambios visibles de UX.
|
||||
- **Infra (no versionable)**: `odoo.conf`, config de nginx/traefik y fail2ban en el servidor de prod.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- La auditoría activa (fuerza bruta/fuzzing) puede degradar o afectar a producción: mitigado con
|
||||
clon de staging o ventana de mantenimiento, snapshot previo y cuentas de test dedicadas (nunca
|
||||
contra cuentas reales, por riesgo de lockout/DoS de usuarios legítimos).
|
||||
- Convertir endpoints a `type="json"` cambia el contrato de request/response: riesgo de regresión en
|
||||
el carrito/checkout del portal; mitigado con tests y verificación end-to-end.
|
||||
- Recortar el acceso a `product.supplierinfo` podría romper la visualización de precios si algún
|
||||
punto del portal dependía de la ACL abierta: verificar que el pricing se computa vía `sudo()`.
|
||||
|
||||
## Criterios de aceptación
|
||||
|
||||
- Los probes `odoo_acl_probe.py` e `idor_probe.py` deniegan tras los fixes lo que permitían antes
|
||||
(write/create de `group.order`, lectura de `supplierinfo` ajeno, datos de grupos ajenos).
|
||||
- El PoC de CSRF funciona antes del fix y falla después.
|
||||
- El harness de fuerza bruta acaba con la IP de Kali baneada por fail2ban en `/web/login` y en la
|
||||
ruta API.
|
||||
- Tests de `website_sale_aplicoop` en verde:
|
||||
`docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u website_sale_aplicoop`.
|
||||
- El checklist de prevuelo cubre los tres frentes + infra, con criterio pass/fail por ítem, y el
|
||||
findings register queda con H1–H5 en estado resuelto/verificado.
|
||||
Loading…
Add table
Add a link
Reference in a new issue