Zeek (antes conocido como Bro) es un monitor de seguridad de red de código abierto. En lugar de buscar firmas como un IDS clásico, Zeek analiza los protocolos y escribe registros estructurados de cada conexión, consulta DNS, petición HTTP o handshake TLS que ve pasar por una interfaz. En este tutorial instalarás Zeek en Ubuntu 24.04 desde el repositorio oficial del proyecto, lo pondrás en marcha en modo standalone con zeekctl, aprenderás a consultar sus logs y crearás un script de detección propio y una lista de indicadores con el framework Intel.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Al menos 2 vCPU y 4 GB de RAM. Zeek procesa cada paquete en espacio de usuario, así que en enlaces con mucho tráfico necesitará más CPU.
- Espacio en disco para los logs: calcula varios GB al día en un servidor con tráfico moderado.
Zeek solo ve el tráfico que llega a la interfaz que monitoriza. En un VPS eso es el tráfico del propio servidor; para vigilar una red completa necesitas un puerto espejo (SPAN) o un TAP conectado a esa interfaz.
Paso 1: Añadir el repositorio oficial de Zeek
El proyecto Zeek publica paquetes para Ubuntu en el servicio openSUSE Build Service. Instala primero las herramientas para descargar la clave:
sudo apt update
sudo apt install -y curl gpg
Descarga la clave de firma del repositorio y guárdala en /etc/apt/keyrings:
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_24.04/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/zeek.gpg
Añade el repositorio indicando esa clave con signed-by:
echo "deb [signed-by=/etc/apt/keyrings/zeek.gpg] https://download.opensuse.org/repositories/security:/zeek/xUbuntu_24.04/ /" | sudo tee /etc/apt/sources.list.d/zeek.list
Actualiza el índice y comprueba que apt ve el paquete:
sudo apt update
apt-cache policy zeek
zeek:
Installed: (none)
Candidate: 8.0.1-0
Version table:
8.0.1-0 500
500 https://download.opensuse.org/repositories/security:/zeek/xUbuntu_24.04 Packages
El número de versión que veas será el más reciente publicado. Si prefieres una rama con soporte largo, el repositorio también ofrece el paquete zeek-lts.
Paso 2: Instalar Zeek
Instala el paquete zeek, que incluye el motor, zeekctl y utilidades como zeek-cut:
sudo apt install -y zeek
Es posible que apt instale un servidor de correo (Postfix) como dependencia para que zeekctl pueda enviar informes. Si te pregunta por su configuración y no vas a enviar correo desde este servidor, elige Local only.
Zeek se instala en /opt/zeek, que no está en el PATH. Añádelo para todos los usuarios:
echo 'export PATH=/opt/zeek/bin:$PATH' | sudo tee /etc/profile.d/zeek.sh
source /etc/profile.d/zeek.sh
Comprueba la instalación:
zeek --version
zeek version 8.0.1
Paso 3: Configurar la interfaz y las redes locales
Averigua el nombre de la interfaz de red pública del servidor:
ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 203.0.113.10/24 2001:db8::10/64
En este ejemplo es eth0; en tu servidor puede llamarse ens3, ens18 o similar. Abre el fichero de nodos de zeekctl:
sudo nano /opt/zeek/etc/node.cfg
Deja la configuración standalone con tu interfaz:
[zeek]
type=standalone
host=localhost
interface=eth0
Después indica qué direcciones consideras locales. Zeek usa esta lista para saber qué lado de cada conexión es el tuyo:
sudo nano /opt/zeek/etc/networks.cfg
Sustituye el contenido de ejemplo por las redes de tu servidor, una por línea, con una descripción opcional:
203.0.113.10/32 Servidor principal
2001:db8::/64 Red IPv6 del servidor
Por último, ajusta la retención de logs en zeekctl.cfg:
sudo nano /opt/zeek/etc/zeekctl.cfg
Busca estas opciones y déjalas así:
MailTo = root@localhost
LogRotationInterval = 3600
LogExpireInterval = 30day
Con esto Zeek rota los logs cada hora y zeekctl cron borra los archivados con más de 30 días.
Paso 4: Desplegar y arrancar Zeek
zeekctl deploy comprueba los scripts, instala la configuración y arranca Zeek en un solo paso:
sudo /opt/zeek/bin/zeekctl deploy
Comprueba que el proceso está en marcha:
sudo /opt/zeek/bin/zeekctl status
Name Type Host Status Pid Started
zeek standalone localhost running 24817 25 Sep 10:12:03
zeekctl no crea un servicio de systemd. Para que rote y archive los logs, borre los antiguos y vuelva a arrancar Zeek si se cae o tras un reinicio, ejecuta zeekctl cron cada cinco minutos:
echo '*/5 * * * * root /opt/zeek/bin/zeekctl cron' | sudo tee /etc/cron.d/zeek
Paso 5: Leer los logs de Zeek
Los logs de la hora en curso están en /opt/zeek/logs/current/ y los archivados, comprimidos por día, en /opt/zeek/logs/AAAA-MM-DD/. Genera algo de tráfico y lista los ficheros:
curl -s https://example.com > /dev/null
ls /opt/zeek/logs/current/
capture_loss.log conn.log dns.log http.log notice.log ssl.log stats.log stderr.log stdout.log weird.log
Los más útiles son:
| Log | Contenido |
|---|---|
conn.log | Cada conexión TCP, UDP e ICMP: origen, destino, duración y bytes |
dns.log | Consultas y respuestas DNS |
http.log | Peticiones HTTP sin cifrar: host, URI, user agent y código |
ssl.log | Handshakes TLS: versión, SNI y resultado de validación |
ssh.log | Sesiones SSH y si la autenticación tuvo éxito |
notice.log | Avisos generados por los scripts de detección |
weird.log | Anomalías de protocolo |
Los logs son TSV con una cabecera #fields. zeek-cut extrae columnas por nombre; con -d convierte las marcas de tiempo a formato legible. Estas son las últimas conexiones:
zeek-cut -d ts id.orig_h id.resp_h id.resp_p proto service < /opt/zeek/logs/current/conn.log | tail -5
2026-09-25T10:20:14+0000 203.0.113.10 93.184.215.14 443 tcp ssl
2026-09-25T10:20:14+0000 203.0.113.10 1.1.1.1 53 udp dns
2026-09-25T10:20:31+0000 198.51.100.7 203.0.113.10 22 tcp ssh
Los dominios más consultados:
zeek-cut query < /opt/zeek/logs/current/dns.log | sort | uniq -c | sort -rn | head
Los nombres de servidor (SNI) de las conexiones TLS:
zeek-cut -d ts id.orig_h server_name version < /opt/zeek/logs/current/ssl.log | tail
Para leer un log ya archivado, descomprímelo al vuelo:
zcat /opt/zeek/logs/2026-09-24/conn.*.log.gz | zeek-cut id.orig_h | sort | uniq -c | sort -rn | head
Consejosi vas a enviar los logs a un SIEM, puedes escribirlos en JSON añadiendo
@load policy/tuning/json-logs.zeekal final de/opt/zeek/share/zeek/site/local.zeek. Ten en cuenta que entonceszeek-cutdeja de servir y tendrás que usarjq.
Paso 6: Escribir un script de detección propio
Los scripts de Zeek reaccionan a eventos (una conexión establecida, una petición HTTP, una respuesta DNS) y pueden generar avisos en notice.log. Como ejemplo, este script avisa cuando se establece una conexión TCP hacia puertos que suelen usar shells remotas o botnets. Crea el fichero en el directorio site:
sudo nano /opt/zeek/share/zeek/site/local-detect.zeek
module LocalDetect;
export {
redef enum Notice::Type += { Suspicious_Port };
## Puertos de destino que se consideran sospechosos.
const suspicious_ports: set[port] = { 4444/tcp, 6667/tcp, 31337/tcp } &redef;
}
event connection_established(c: connection)
{
if ( c$id$resp_p !in suspicious_ports )
return;
NOTICE([$note=Suspicious_Port,
$conn=c,
$msg=fmt("Conexión de %s a %s en el puerto %s", c$id$orig_h, c$id$resp_h, c$id$resp_p),
$identifier=cat(c$id$orig_h, c$id$resp_h, c$id$resp_p)]);
}
El campo $identifier hace que el framework de avisos no repita el mismo aviso para el mismo par de hosts y puerto durante un tiempo. Carga el script desde local.zeek, que es el punto de entrada de la configuración del sitio:
echo '@load local-detect' | sudo tee -a /opt/zeek/share/zeek/site/local.zeek
Valida los scripts antes de desplegarlos:
sudo /opt/zeek/bin/zeekctl check
zeek scripts are ok.
Aplica los cambios:
sudo /opt/zeek/bin/zeekctl deploy
Para probarlo, abre una conexión hacia un servicio que acepte conexiones en cualquier puerto, como portquiz.net:
curl -s --max-time 5 http://portquiz.net:4444/ > /dev/null
zeek-cut note msg < /opt/zeek/logs/current/notice.log
LocalDetect::Suspicious_Port Conexión de 203.0.113.10 a 35.180.139.74 en el puerto 4444/tcp
Zeek ya carga por defecto otros scripts de detección desde local.zeek, como policy/protocols/ssh/detect-bruteforcing, que genera avisos SSH::Password_Guessing cuando una IP falla repetidamente al autenticarse por SSH.
Paso 7: Cargar indicadores con el framework Intel
El framework Intel compara lo que Zeek ve (direcciones IP, dominios, URLs, hashes de ficheros) con una lista de indicadores y registra cada coincidencia en intel.log. El fichero de indicadores debe estar separado por tabuladores, así que créalo con printf para no depender del editor. Este ejemplo usa example.org como indicador de prueba:
printf '#fields\tindicator\tindicator_type\tmeta.source\tmeta.desc\nexample.org\tIntel::DOMAIN\tprueba\tDominio de prueba\n198.51.100.66\tIntel::ADDR\tprueba\tIP de prueba\n' | sudo tee /opt/zeek/share/zeek/site/intel.dat
Añade al final de local.zeek la carga del framework y la ruta del fichero:
sudo nano /opt/zeek/share/zeek/site/local.zeek
@load frameworks/intel/seen
redef Intel::read_files += { "/opt/zeek/share/zeek/site/intel.dat" };
El script frameworks/intel/seen es el que alimenta al framework con lo que aparece en DNS, HTTP, TLS y conexiones. Valida y despliega:
sudo /opt/zeek/bin/zeekctl check
sudo /opt/zeek/bin/zeekctl deploy
Provoca una coincidencia resolviendo el dominio de prueba y revisa intel.log:
curl -s https://example.org > /dev/null
zeek-cut seen.indicator seen.where sources < /opt/zeek/logs/current/intel.log
example.org DNS::IN_REQUEST prueba
example.org SSL::IN_SERVER_NAME prueba
Zeek vuelve a leer intel.dat automáticamente cuando cambia, así que puedes actualizar la lista (por ejemplo, con un feed de amenazas descargado por cron) sin reiniciar. Cuando termines la prueba, elimina las líneas de ejemplo.
Solución de problemas
zeekctl statusmuestracrashed: revisa la salida consudo /opt/zeek/bin/zeekctl diag. La causa más habitual es un nombre de interfaz incorrecto ennode.cfg.- Muchas entradas
bad_TCP_checksumenweird.logy conexiones incompletas: la tarjeta de red calcula los checksums por hardware y Zeek ve los paquetes salientes antes de que se rellenen. Añaderedef ignore_checksums = T;alocal.zeeky vuelve a desplegar. capture_loss.logmuestra pérdidas altas: Zeek no da abasto con el tráfico. Revisa el uso de CPU contop; si el proceso está al 100 %, necesitas más vCPU o un despliegue en clúster con varios workers.- Los logs dejan de rotarse: comprueba que existe
/etc/cron.d/zeeky quesudo /opt/zeek/bin/zeekctl cronse ejecuta sin errores.
Conclusión
Ahora tienes Zeek analizando el tráfico de tu servidor, con logs estructurados por protocolo, rotación automática, un script de detección propio y una lista de indicadores Intel. A partir de aquí puedes enviar los logs en JSON a un SIEM como Wazuh o Elasticsearch, suscribirte a feeds de amenazas para alimentar intel.dat o instalar paquetes de la comunidad con zkg, el gestor de paquetes de Zeek.
