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:

LogContenido
conn.logCada conexión TCP, UDP e ICMP: origen, destino, duración y bytes
dns.logConsultas y respuestas DNS
http.logPeticiones HTTP sin cifrar: host, URI, user agent y código
ssl.logHandshakes TLS: versión, SNI y resultado de validación
ssh.logSesiones SSH y si la autenticación tuvo éxito
notice.logAvisos generados por los scripts de detección
weird.logAnomalí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

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 status muestra crashed: revisa la salida con sudo /opt/zeek/bin/zeekctl diag. La causa más habitual es un nombre de interfaz incorrecto en node.cfg.
  • Muchas entradas bad_TCP_checksum en weird.log y conexiones incompletas: la tarjeta de red calcula los checksums por hardware y Zeek ve los paquetes salientes antes de que se rellenen. Añade redef ignore_checksums = T; a local.zeek y vuelve a desplegar.
  • capture_loss.log muestra pérdidas altas: Zeek no da abasto con el tráfico. Revisa el uso de CPU con top; 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/zeek y que sudo /opt/zeek/bin/zeekctl cron se 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.