
Un SIEM ligero con Cloudflare, n8n y Slack puede consultar Audit Logs v2 de forma periódica, reconocer modificaciones de seguridad y enviar una notificación cuando aparezca un evento nuevo de alta relevancia. La clave no está en enviar todos los registros: está en filtrar, clasificar, deduplicar y confirmar la entrega. En esta guía montaremos esa arquitectura utilizando un token de solo lectura, nodos visuales y una Data Table de n8n.
En esta guía
¿Qué es un SIEM ligero y qué eventos de Cloudflare puede detectar?
En esta implementación, «SIEM ligero» describe un flujo de observabilidad y alerta: consulta un origen de auditoría, normaliza acciones, aplica reglas simples y conserva una marca de los eventos revisados. No utiliza un motor de correlación avanzado ni promete detectar todas las amenazas posibles.
Cloudflare Audit Logs v2 registra actividades iniciadas por usuarios y por sistemas. Según el recurso y el producto, podemos encontrar operaciones sobre tokens API, miembros, DNS, zonas, configuraciones y túneles. Un mismo cambio humano puede generar registros automáticos relacionados: por eso una acción create, update o delete no demuestra por sí sola un ataque.
| Ejemplo de actividad | Severidad inicial sugerida | Motivo |
|---|---|---|
| Crear, modificar o eliminar un token API | CRITICAL | Cambia la superficie de acceso |
| Cambiar miembros o permisos de una cuenta | CRITICAL | Puede alterar los privilegios |
| Eliminar un registro DNS | CRITICAL | Puede interrumpir o desviar servicios |
| Crear o modificar registros DNS | HIGH | Requiere contexto y validación |
| Crear o actualizar Cloudflare Tunnel | HIGH | Puede modificar rutas de acceso |
| Cambiar ajustes o reglas de seguridad | HIGH/CRITICAL | Depende del recurso y el efecto real |
| Renovación automática de certificados | INFORMATIVE | No suele requerir aviso individual |
Consultas view habituales |
IGNORE | Generan ruido administrativo |
Los niveles de la tabla son una política propuesta para el workflow, no una severidad emitida oficialmente por Cloudflare. Ajusta las condiciones con tus procesos de cambios autorizados y revisa especialmente las acciones de sistemas automáticos.
Requisitos y arquitectura del flujo
Necesitas una cuenta de Cloudflare con acceso a Audit Logs v2, una instalación operativa de n8n con Data Tables y un workspace de Slack con una aplicación que pueda publicar mediante Incoming Webhooks. No es necesario instalar un agente en cada servidor para consultar estos registros de cuenta.
↓
↓
↓
Esta arquitectura usa Loop Over Items con lotes de uno para mantener el contexto y retornar correctamente al siguiente evento. La tabla evita volver a notificar un event_id ya guardado. Como veremos, eso es deduplicación práctica, no un bloqueo transaccional entre ejecuciones simultáneas.
Diseño recomendado para datos
Crea una Data Table llamada security_audit_processed con las columnas event_id (String), severity (String), status (String) y processed_at (Date o tipo compatible con tu instalación). n8n añade sus propios campos internos id, createdAt y updatedAt; no confundas id de Data Table con event_id del log.
Estados útiles: baseline para registros históricos sembrados sin alerta, sent_to_slack para notificaciones confirmadas y, si amplías el diseño, reviewed para cambios revisados manualmente. Para alta criticidad conviene guardar el ID solo después de que Slack confirme la entrega.
Paso 1. Consultar Cloudflare Audit Logs v2 desde n8n

En Cloudflare genera un API Token dedicado con permiso Account Settings → Read y ámbito limitado a la cuenta necesaria. Usa un nombre identificable, plazo de caducidad y restricciones de acceso cuando sean compatibles con tu operación. Nunca incluyas el token en el artículo, capturas o repositorios.
En n8n crea una credencial Header Auth:
- Name:
Authorization - Value:
Bearer TU_TOKEN_CLOUDFLARE
Si tu versión permite restringir dominios de la credencial, utiliza api.cloudflare.com.
Configura un nodo HTTP Request con método GET, autenticación Generic Credential Type → Header Auth y esta URL:
https://api.cloudflare.com/client/v4/accounts/ACCOUNT_ID/logs/audit
El valor ACCOUNT_ID es el identificador de cuenta, no el identificador de una zona. En Send Query Parameters → Using Fields Below, añade:
| Name | Value | Propósito |
|---|---|---|
since |
{{ $now.toUTC().minus({ hours: 24 }).toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") }} |
Inicio de ventana |
before |
{{ $now.toUTC().toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") }} |
Fin de ventana |
action_type.not |
view |
Excluir vistas habituales |
limit |
100 |
Límite de la primera página de ejemplo |
Por qué usamos 24 horas: esa ventana móvil permite solapar lecturas si alguna ejecución falla. La deduplicación se encarga de evitar que los mismos IDs produzcan avisos repetidos. Para alto volumen conviene una ventana más corta con un margen controlado, además de paginación completa.
Un ejemplo de la petición HTTP, con valores inventados, sería:
curl --request GET \
'https://api.cloudflare.com/client/v4/accounts/ACCOUNT_ID/logs/audit?since=2026-11-01T00%3A00%3A00Z&before=2026-11-02T00%3A00%3A00Z&action_type.not=view&limit=100' \
--header 'Authorization: Bearer TU_TOKEN_CLOUDFLARE'
El parámetro action_type.not se utiliza aquí para excluir la acción view; si el API o la serialización de tu cliente cambian, contrasta el filtro con la documentación vigente y comprueba los action.type realmente recibidos.

Interpretar la respuesta
La respuesta v2 incluye un arreglo result con entradas de auditoría y puede proporcionar result_info.cursor para continuar leyendo. Entre los campos que nos interesan están id, action.type, action.description, action.time, actor.type, resource.product y, si existe, zone.name.
{
"success": true,
"result": [
{
"id": "EVENT_ID_DE_EJEMPLO",
"action": {
"type": "update",
"description": "Update Token",
"time": "2026-11-02T12:30:00Z",
"result": "success"
},
"actor": { "type": "user" },
"resource": { "product": "tokens" }
}
],
"result_info": { "cursor": null }
}
El objeto anterior es ilustrativo, no un registro real de una empresa. No publiques las respuestas completas: pueden contener correos de administradores, IP de acceso, información de recursos y datos internos en raw.
Paso 2. Clasificar cambios con Code in JavaScript
Conecta un nodo Code inmediatamente después de HTTP Request, en modo Run Once for All Items. El siguiente ejemplo normaliza campos y aplica una política inicial de detección que se puede auditar y modificar:
const response = $input.first().json;
if (response.success !== true || !Array.isArray(response.result)) {
throw new Error('Respuesta inesperada de Cloudflare Audit Logs v2');
}
const alerts = [];
for (const e of response.result) {
const action = String(e.action?.type ?? '').toLowerCase();
const product = String(e.resource?.product ?? '').toLowerCase();
const description = String(e.action?.description ?? '');
const result = String(e.action?.result ?? 'unknown');
if (!['create', 'update', 'delete'].includes(action)) continue;
if (!e.id || !e.action?.time) continue;
const accountPrivilegeChange = product === 'account' &&
/TOKEN|MEMBER|ROLE|PERMISSION|ACCESS|POLICY/i.test(description);
let severity = null;
if (['tokens', 'members', 'iam'].includes(product) || accountPrivilegeChange) {
severity = 'CRITICAL';
} else if (['dns', 'dns_records'].includes(product)) {
severity = action === 'delete' ? 'CRITICAL' : 'HIGH';
} else if (['cfd_tunnel', 'tunnels', 'settings', 'zone',
'rulesets', 'firewall'].includes(product)) {
severity = 'HIGH';
}
if (!severity) continue;
alerts.push({ json: {
event_id: e.id,
severity,
action,
product,
description,
result,
timestamp: e.action.time,
actor_type: e.actor?.type ?? 'unknown',
zone: e.zone?.name ?? ''
}});
}
return alerts;
En producción es preferible que las reglas evolucionen por producto, tipo de recurso y descripción específica, con una lista de acciones permitidas o mantenimientos conocidos. HIGH y CRITICAL indican prioridad de revisión, no compromisos confirmados.
Si el nodo no emite elementos, significa que ninguna entrada coincidió con la política; no significa necesariamente que Cloudflare esté libre de cambios ni que se hayan consultado todas las páginas.
Paso 3. Evitar duplicados con Data Table y Loop Over Items
Crea la ruta: Code clasificador → Loop Over Items (Batch Size = 1) → Data Table (Row / Get) → IF. En Get, busca event_id igual a {{ $json.event_id }}, establece un límite de 1 fila y activa Always Output Data para que un resultado vacío produzca un elemento de continuidad.
Configura el IF ¿Existe ID? con {{ $json.id }} y la operación Exists. Un resultado True significa que la Data Table ya tiene el evento; envíalo a un nodo No Operation que regrese al Loop. Un resultado False continúa por la ruta de eventos nuevos.
La salida de Get no conserva necesariamente el objeto original cuando no encuentra coincidencias. Por eso, en el Loop recuperamos los atributos del elemento de la iteración actual desde un nodo Edit Fields (Set). Por ejemplo:
{{ $('Loop Over Items').first(1,
$('Loop Over Items').context.currentRunIndex).json.event_id }}
El 1 indica la rama loop del nodo (revisa el orden de salidas en tu versión). Comprueba siempre que cada iteración recupere un event_id diferente cuando corresponda; una referencia fija a .first() sin índice de ejecución puede repetir el primer evento.
Después agrega otro IF: {{ $json.severity }} is equal to CRITICAL. En nuestro ejemplo, los HIGH nuevos se registran con status = baseline y vuelven al Loop; los CRITICAL nuevos pasan a Slack. Si tu operación debe notificar también HIGH, crea una ruta de revisión o resumen: registrarlos como baseline significa descartarlos como futuras alertas.
Cierre del Loop: las rutas de evento existente, HIGH registrado y CRITICAL enviado deben regresar a una misma etapa final, por ejemplo No Operation → entrada de Loop Over Items. La salida done permite comprobar cuándo han terminado todos los elementos. No utilices un Merge que espere simultáneamente dos ramas mutuamente excluyentes.
Paso 4. Enviar una alerta a Slack y comprobar la confirmación
Desde Slack API → Your Apps, crea una aplicación y habilita Incoming Webhooks para un canal específico de seguridad. Conserva la URL como secreto: quien conozca un webhook activo podría publicar mensajes en el canal autorizado.
En el nodo HTTP Request de Slack configura POST, Send Body = ON, Body Content Type = JSON, Specify Body = Using Fields Below. Agrega Name = text, y en Value → Expression coloca:
{{ "🚨 *Alerta de auditoría | CLOUDFLARE*\n" +
"*Severidad:* " + $json.severity + "\n" +
"*Acción:* " + $json.action + "\n" +
"*Evento:* " + $json.description + "\n" +
"*Producto:* " + $json.product + "\n" +
"*Zona:* " + ($json.zone || "Cuenta Cloudflare") + "\n" +
"*Fecha UTC:* " + $json.timestamp + "\n" +
"*Event ID:* `" + $json.event_id + "`" }}
Elegimos Using Fields Below para que n8n construya un JSON correcto, sin el error habitual de convertir un objeto JavaScript en el texto [object Object].
Configura Response Format = Text, Put Output in Field = data (valor fijo) e Include Response Headers and Status = ON. Mantén Never Error = OFF y On Error = Stop Workflow.
Después de Slack, agrega un IF con dos condiciones AND: {{ $json.statusCode }} igual a 200 y {{ $json.body ?? $json.data }} igual a ok. La rama True inserta la fila event_id, severity, status = sent_to_slack, processed_at; la rama False termina con Stop And Error. Si el nodo HTTP modifica la forma del item, recupera el ID desde tu nodo Preparar alerta, no desde $json de la respuesta de Slack.
La respuesta 200 / ok prueba que Slack aceptó la solicitud. No equivale a una garantía absoluta de entrega, visualización o lectura humana. Ante un error de red o inserción, no descartes silenciosamente el evento.
Paso 5. Programar, probar y operar sin ruido
Agrega Schedule Trigger cada 5 minutos y conéctalo a la misma lectura de Cloudflare. Antes de publicar, realiza una ejecución manual y verifica el número de registros del clasificador, las salidas True/False de deduplicación, el done del Loop y las filas de Data Table.
Prueba segura de extremo a extremo
- Registra los eventos históricos como
baselinepara que la primera activación no dispare una avalancha de mensajes. - Genera un evento ficticio de prueba, con identificador exclusivo como
siem-test-001y una descripción inequívocaPRUEBA_CONTROLADA, y envíalo únicamente por la ruta Slack → validación → registro; no cambies DNS ni permisos reales para probarlo. - Comprueba la respuesta
ok, la ruta True del IF y el registrosent_to_slackcorrespondiente. - Retira los nodos temporales, restaura las referencias originales y publica el workflow.
- Verifica la siguiente ejecución programada en Executions y crea una ruta de error independiente si necesitas notificación cuando fallen la API, la credencial o la Data Table.
Cuatro límites de diseño que debes conocer
1. Paginación. Cloudflare devuelve un result_info.cursor cuando hay otra página. Si solo procesas la primera, puedes perder cambios relevantes. Para uso continuo debes iterar con cursor, conservar el mismo since/before de esa consulta y terminar cuando ya no exista cursor. El limit puede configurarse, pero no sustituye la paginación. No declares cobertura completa hasta verificarlo.
2. Duplicados bajo concurrencia. Buscar un ID y luego insertarlo evita repeticiones habituales, pero entre ambos pasos existe una condición de carrera. Dos ejecuciones superpuestas pueden enviar el mismo evento. Para una garantía mayor se necesita una reserva atómica y controlada por una restricción única o un sistema equivalente. Agregar una segunda consulta Get no soluciona esto por sí solo.
3. Reintentos y fallos parciales. Slack puede aceptar un mensaje y luego fallar la inserción en la tabla. Al reintentar podría enviarse de nuevo. Diseña reconciliación y monitoreo de errores, en lugar de afirmar que el sistema asegura exactamente una entrega.
4. Alcance de las fuentes. Audit Logs v2 cubre actividad administrativa, pero no reemplaza los eventos detallados del WAF, solicitudes HTTP, inicios de sesión de aplicaciones ni telemetría de endpoints. Si necesitas esas señales, incorpora fuentes específicas mediante los mecanismos oficiales y valida su disponibilidad según tu plan.
Seguridad y mantenimiento
No expongas API tokens, webhooks, identificadores internos o respuestas completas en capturas o tutoriales. Restringe credenciales, rota secretos expuestos, limita el acceso a las Data Tables, revisa el crecimiento de registros y establece una política de retención. n8n documenta límites de capacidad para Data Tables, por lo que no deben utilizarse como almacenamiento masivo de logs a largo plazo.
Para reducir ruido, evita avisar cada consulta view, distingue eventos de system y user, revisa cambios legítimos de mantenimiento y define quién investiga, aprueba y cierra las alertas. Un canal lleno de mensajes sin responsables pierde valor operativo.
Preguntas frecuentes sobre Cloudflare, n8n y Slack

¿Necesito instalar un agente de SIEM en todos mis servidores?
No para consultar Audit Logs v2 de la cuenta de Cloudflare. n8n consume esa API por HTTPS. Si además quieres comandos de terminal, accesos SSH o integridad de archivos, necesitarás fuentes y controles específicos para esos sistemas.
¿El SIEM detecta ataques al WAF de Cloudflare?
No con este flujo por sí solo. Detecta cambios administrativos registrados en Audit Logs v2. Para analizar solicitudes bloqueadas, reglas disparadas o tráfico malicioso necesitas la fuente de eventos WAF correspondiente, disponible según producto, plan y método de acceso.
¿Qué significa action_type.not = view?
Es un filtro de Audit Logs v2 para excluir acciones de consulta. Reduce el volumen, pero no sustituye las reglas de criticidad: la API puede seguir devolviendo cambios legítimos o automáticos.
¿Por qué guardar un event_id en Data Table?
Porque la consulta periódica revisa ventanas de tiempo solapadas. El ID permite reconocer que una entrada ya fue procesada y evitar notificarla otra vez durante las ejecuciones normales.
¿Puede n8n garantizar que nunca se duplique una alerta?
No con una simple comprobación Get → IF → Insert. Es suficiente para muchos escenarios de bajo volumen, pero no es un bloqueo atómico. La concurrencia, el fallo posterior al envío y los reintentos requieren un diseño de entrega más sólido.
¿Cada cuánto conviene consultar la API?
Cinco minutos es un punto de partida operativo para cambios administrativos de bajo volumen, no una regla universal. Ajusta la frecuencia según criticidad, tasa de eventos, límites de API y tiempo de respuesta esperado.
Conclusión: empieza pequeño, pero mide lo importante
Un SIEM ligero con Cloudflare Audit Logs v2, n8n y Slack puede ofrecer visibilidad útil sobre cambios en tokens, permisos, DNS y configuraciones sin convertir cada movimiento administrativo en una notificación. Comienza con reglas explícitas, un registro de IDs y pruebas de confirmación; después añade paginación, supervisión de fallos y controles de concurrencia conforme aumenten el volumen y las exigencias de disponibilidad.
El objetivo no es acumular alertas: es detectar los cambios que merecen investigación y mantener el contexto necesario para actuar.
Fuentes oficiales y documentación
- Cloudflare: Audit Logs v2 — alcance, estructura y retención de auditoría.
- Cloudflare API: Get account audit logs (v2) — parámetros
since,before, filtros y cursor. - n8n: Data Table row operations — Get, Insert, Upsert y filtros.
- n8n: Data Tables — capacidad y consideraciones de almacenamiento.
- Slack: Incoming Webhooks — formato de mensajes, seguridad y errores.


