addons-cm/docs/openspec/changes/security-audit-hardening/proposal.md
2026-08-07 16:46:33 +02:00

6.5 KiB
Raw Blame History

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 H1H5 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 H1H5.
  • 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 H1H5 en estado resuelto/verificado.