Lectura: ~9 minutos
Cuando una alerta de AdGuard llega a Wazuh sabes qué dominio se ha bloqueado, pero no si realmente es phishing, malware o simplemente publicidad.
¿Qué conseguirás al terminar este artículo?
- Enriquecer automáticamente alertas de AdGuard con urlscan.io.
- Mostrar únicamente amenazas relevantes en Telegram.
- Evitar enviar URLs privadas a servicios externos.
- Integrar todo en Wazuh-lite sin depender del Dashboard oficial.
En este artículo integramos urlscan.io con Wazuh-lite para enriquecer automáticamente las alertas con URL reputation, sin abandonar Grafana ni el flujo habitual de Telegram. AdGuard bloquea la conexión; urlscan aporta el contexto necesario para decidir si la amenaza requiere atención.
La inspiración viene del artículo de Moiz Ud Din Rafay en Medium (Urlscan.io Integration with Wazuh SIEM for Automated URL Reputation Enrichment, julio 2026).
En nuestro caso lo adaptamos a:
- Wazuh 4.14.5
- Docker (manager en raam)
- Grafana +
alerts.json(sin Indexer Dashboard clásico) custom-telegram.py
Documentación útil: urlscan.io, API de urlscan y Wazuh Integrator. Encaja con el control parental y el dashboard Grafana sin Elasticsearch.
Antes y después: el valor en una mirada
Antes (solo AdGuard → Wazuh):
AdGuard bloquea evil-domain.com
↓
Wazuh alerta level ≥7
↓
Telegram: “dominio bloqueado” + equipo
Sabes quién y qué dominio. No sabes qué es.
Después (+ enriquecimiento urlscan):
AdGuard bloquea evil-domain.com
↓
Wazuh alerta level ≥7 ──→ Telegram parental (como siempre)
↓
custom-urlscan → urlscan.io (search / result)
↓
alerts.json → Grafana
↓
Si malicious/suspicious + API key → Telegram URLSCAN REPUTATION
· Título: Microsoft Login
· Verdict: Malicious
· IP: 104.x.x.x
· Enlace al scan
El operador decide en segundos si merece atención o es ruido.
¿Por qué urlscan y no VirusTotal?
Una pregunta que suele aparecer es: ¿por qué urlscan y no VirusTotal? Elegimos urlscan porque:
- Permite buscar escaneos existentes (
/search) sin publicar tu URL. - No hace falta hacer submit de dominios privados del hogar (privacidad).
- Devuelve metadatos útiles: título de página, IP, enlace al resultado, y con API key el veredicto.
- Tiene API gratuita suficiente para un SOC lite.
- Encaja bien como threat intelligence ligera / enriquecimiento de IOCs sobre alertas ya filtradas.
VirusTotal sería otra opción válida (más motor de firmas, más ecosistema). En nuestro caso el cuello de botella no era “¿está en VT?”, sino “¿qué pinta tiene esta URL que AdGuard acaba de cortar?” sin salir del SIEM y sin filtrar URLs internas a un portal de submit.
Para un homelab, urlscan ofrece un equilibrio muy bueno entre privacidad, simplicidad y contexto.
Problemas reales durante la implementación
Esta sección diferencia el post de una documentación oficial. Lo que falló de verdad:
- Directorio
logs/del host no escribible → crear ficheros de log/state condocker exec. - API
/result/→ 403 sin login → sin key no hayverdict_malicious; sí metadatos + enlace + regla 126109. - Falsos positivos de regex (timestamps ISO tipo
57.000Zcomo “dominio”) → TLD alfabético + prioridad adata.domain. - Integraciones custom no viven en el volumen → tras recreate hay que volver a ejecutar
install-urlscan-integration.sh(docker cpa/var/ossec/integrations). - Nombre sin prefijo
custom-→integratordrechaza el script; colateral:k8s-audit-telegram.py→custom-k8s-audit-telegram.py. - Telegram urlscan sin “quién/equipo” → el enricher no copiaba
client_*; corregido en enricher + formatter.
Arquitectura: enriquecimiento asíncrono
AdGuard / parental / web / AUR
│
▼
Wazuh-lite
(level ≥ 7)
┌────┴────┐
▼ ▼
Telegram custom-urlscan
“de siempre” │
(equipo) ▼
urlscan.io
search (+ /result)
│
▼
enrichment.log
│
▼
reglas 12610x
│
┌──────┴──────┐
▼ ▼
alerts.json Telegram urlscan
+ Grafana (solo malicious /
suspicious + key)
Decisiones de diseño
- Solo usamos
/searchy/result— nunca submit de URLs privadas. - Integración únicamente para grupos concretos + level ≥ 7 (no todo el DNS).
- Caché 24 h para no martillar la API de reputación.
- IDs propios 126100–126109 (evitar choque con AdGuard 111xxx / AUR 125xxx; no los 111100 del artículo Medium).
- Python embebido de Wazuh:
/var/ossec/framework/python/bin/python3.
Qué mensajes de Telegram recibirás
| Evento | Rule ID | Level | ¿Telegram? |
|---|---|---|---|
| AdGuard / control parental | 1110xx / 1100xx | ≥7 | Sí — Quién, dominio, categoría |
| urlscan genérico / cache / not_found | 126100, 104, 106–108 | 3 | No |
| urlscan sin veredicto (sin API key) | 126109 | 3 | No |
| urlscan API failure | 126105 | 5 | No |
| urlscan malicious / suspicious / critical | 126101 / 102 / 103 | 14 / 12 / 14 | Sí* |
*Solo con veredicto real de /result/. Sin API key no se generan 126101/102/103 desde la API.
En resumen:
- Telegram cuando AdGuard/parental detecta algo (level ≥ 7), con el equipo.
- Telegram cuando urlscan confirma malware/suspicious (y hay API key).
- Ningún aviso si simplemente falta la API key o el enriquecimiento no sacó veredicto.
El enricher propaga client_name, client_ip y domain de la alerta origen. Ejemplo de formato verificado (regla 126101):
🦠 *URLSCAN REPUTATION*
👤 *Quién:* Marcelo
📋 *Alerta:* urlscan.io malicious URL reputation detected
💻 *Equipo/IP:* `10.5.5.124`
🌐 *Dominio:* `evil.example`
🔗 *URL:* https://evil.example/phish
🧾 *urlscan:* https://urlscan.io/result/abc/
🦠 *Veredicto:* MALICIOSO
📊 *Risk:* critical
API key: metadatos sí, veredicto solo con key
Importante. Sin API key el enriquecimiento funciona (título, IP, enlace al scan), pero no obtienes el veredicto de reputación (
malicious/suspicious). La regla126109es level 3: no spamea Telegram. Si “no me llega el veredicto”, primero comprueba que existeetc/urlscan_api_key— no asumas que la integración está rota.
Sin etc/urlscan_api_key |
Con API key |
|---|---|
| Search público OK | Search + /result/ |
| Título, IP, enlace al scan | + veredicto / score |
Evento urlscan_verdict_unavailable (level 3) |
Posible Telegram 126101/102/103 |
| Sin Telegram de reputación urlscan | Telegram si malicious/suspicious |
La key va en etc/urlscan_api_key (gitignored). Cuenta gratis: urlscan.io → Settings → API. En el momento de este post aún no estaba en producción: el circuito Telegram de reputación queda pendiente de esa pieza.
Adaptaciones frente al artículo Medium
| Artículo de referencia | Nuestro wazuh-lite |
|---|---|
| Integración amplia por level | Grupos concretos + level ≥ 7 |
| IDs tipo 111100 | 126100–126109 |
| Stack Indexer / Dashboard | Grafana + proxy alerts.json |
| Submit en algunos flujos | Nunca submit; solo search/result |
| Telegram genérico | Dos capas: parental vs urlscan con veredicto |
Verificación de la integración (ejecutado 2026-07-25)
ssh raam 'cd /home/dguillermo/kubernetes/wazuh-lite && bash scripts/install-urlscan-integration.sh'
ssh raam 'cd /home/dguillermo/kubernetes/wazuh-lite && bash scripts/test-urlscan-integration.sh'
Salida real: RESULTADO: OK — urlscan integration verificada.
Comprobado: enricher sobre example.com; sin key → urlscan_verdict_unavailable; wazuh-logtest → 126101 level 14; evento en alerts.json; Enabling integration for: 'custom-urlscan'; formato Telegram con Quién + Equipo/IP.
No verificado aún en producción: Telegram real disparado por veredicto urlscan con API key.
Cómo integrar urlscan.io en tu Wazuh-lite
Requisitos
- Wazuh 4.14+ (probado en 4.14.5)
- Docker (manager en contenedor)
- AdGuard (u otra fuente) ya integrado en Wazuh, con alertas level ≥ 7
- Python embebido de Wazuh (
/var/ossec/framework/python/bin/python3) - API key de urlscan (opcional para metadatos; recomendada para veredictos y Telegram de reputación)
Pasos
- Cuenta en urlscan.io → API key (docs API).
- Escribir la key en
wazuh-lite/etc/urlscan_api_key(chmod 640). bash scripts/install-urlscan-integration.shbash scripts/test-urlscan-integration.sh- Esperar una alerta AdGuard level ≥ 7 o invocar el enricher con JSON de prueba.
Piezas: scripts/custom-urlscan.py, etc/rules/urlscan_rules.xml, integración en ossec.conf (Wazuh Integrator) y formatter Telegram para el grupo urlscan.
Tras docker compose … up --force-recreate: /var/ossec/integrations no está en volumen → reinstalar la integración.
Conclusión
La integración no pretende sustituir a AdGuard ni convertir urlscan en un antivirus. El objetivo es práctico: cuando una alerta importante aparece en Wazuh, disponer de inmediato del contexto suficiente para decidir si merece atención o puede ignorarse.
En un homelab eso supone menos tiempo investigando y menos falsas alarmas, sin modificar el flujo habitual del SIEM ni spamear el móvil cuando no hay veredicto.
En un SOC doméstico el tiempo también importa. Si una alerta ya llega enriquecida con contexto y reputación, la decisión deja de ser «¿qué hago ahora?» para convertirse en «¿requiere actuación o puedo descartarla?».
Próximos pasos: poner la API key en producción, un panel Grafana filtrando rule.groups con urlscan, y una prueba E2E con un dominio malicioso conocido.
Más contexto en el blog: DNS tunneling vía AdGuard, SOC proactivo del homelab y Wazuh 5 en producción.