
ioc2rpz es un lugar donde la inteligencia de amenazas se encuentra con el DNS.
ioc2rpz™: La solución de seguridad DNS - ioc2rpz™ es un potente servidor DNS que transforma los indicadores de amenazas en feeds accionables de Response Policy Zone (RPZ). Automatiza el proceso de actualización, garantizando que tu red esté protegida contra las últimas amenazas, incluidos dominios maliciosos y direcciones IP. Al convertir los feeds de IOC en RPZ, ioc2rpz™ actúa como un vínculo crucial entre la inteligencia de amenazas y la seguridad DNS, compatible con servidores DNS que soportan RPZ como ISC Bind o PowerDNS.
DNS es el plano de control de Internet. Normalmente el DNS se usa para el bien, pero:

ISC Bind es, de facto, un estándar de servidor de nombres. Con la introducción de las Response Policy Zones en ISC BIND 9.8, monitorizar y contener el malware en la capa DNS se convirtió en una tarea sencilla. RPZ es compatible con PowerDNS Recursor 4.0.0 y versiones posteriores. Knot DNS también soporta parcialmente RPZ.
En comparación con las soluciones tradicionales de protección de red, un servidor DNS puede gestionar millones de indicadores sin impacto en el rendimiento, pero no existía una forma automatizada y eficiente de mantener las zonas de política de respuesta en los servidores DNS primarios.
Normalmente, los indicadores de compromiso se distribuyen en texto plano, pero en diferentes formatos, y solo unos pocos proveedores de IOC los ponen a disposición mediante RPZ.
ioc2rpz™ es un servidor DNS personalizado que convierte automáticamente indicadores (por ejemplo, FQDN maliciosos, direcciones IP) de diversas fuentes en feeds RPZ y los mantiene/actualiza automáticamente. Los feeds pueden distribuirse a cualquier servidor DNS de código abierto y/o comercial que soporte RPZ, por ejemplo, ISC Bind, PowerDNS. Puedes ejecutar tu propio servidor DNS con filtrado RPZ en un router, un ordenador de escritorio, un servidor e incluso una Raspberry Pi. La memoria del sistema es la única limitación.
Con ioc2rpz™ puedes definir tus propios feeds, acciones y evitar comunicaciones no deseadas.
ioc2rpz™ transforma los feeds de IOC en zonas de política de respuesta (RPZ). Puedes mezclar feeds para generar un único RPZ o varios RPZ. Los dominios e IP de confianza pueden incluirse en la lista blanca. ioc2rpz™ admite la expiración de indicadores y, en consecuencia, reconstruye las zonas.
La versión actual admite: archivos locales, archivos/solicitudes a través de http/https/ftp y scripts de shell para acceder a otros tipos de recursos. Puedes usar cualquier formato de archivo si puedes escribir una REGEX para extraer indicadores y los indicadores están separados por caracteres de nueva línea y/o retorno de carro (/n, /r, /r/n).
ioc2rpz está construido sobre Erlang/OTP con un árbol de supervisión que garantiza tolerancia a fallos y recuperación automática. Consulta docs/architecture.md para obtener todos los detalles.``` ioc2rpz_app (application) └── ioc2rpz_sup (supervisor) ├── ioc2rpz_db_sup — ETS table heir process ├── ioc2rpz_tcp_sup — TCP listener pool (5 workers) ├── ioc2rpz_udp_sup — UDP listener ├── ioc2rpz_tls_sup — TLS/DoT listener pool (5 workers) [if cert configured] └── ioc2rpz_rest_sup — Cowboy HTTPS (REST API + DoH) [if cert configured]
Key modules:
| Module | Responsibility |
|--------|---------------|
| `ioc2rpz.erl` | Trabajador DNS TCP/TLS — aceptar, analizar, validar, responder |
| `ioc2rpz_udp.erl` | Oyente DNS UDP — consultas SOA |
| `ioc2rpz_conn.erl` | Obtención de fuentes IOC (HTTP/HTTPS/archivo/shell) |
| `ioc2rpz_db.erl` | Caché de zonas y paquetes basada en ETS |
| `ioc2rpz_sup.erl` | Supervisor principal, carga de configuración, programación de zonas |
| `ioc2rpz_rest.erl` | API de gestión REST (Cowboy) |
| `ioc2rpz_doh.erl` | Manejador DNS-over-HTTPS |
## Cómo usar ioc2rpz™
Puede usar ioc2rpz™ con cualquier servidor DNS que admita Zonas de Política de Respuesta (Response Policy Zones), p. ej. versiones recientes de ISC BIND, PowerDNS y cualquier servidor DNS comercial basado en estos productos (p. ej. Infoblox, Blue Cat, Efficient IP). Se proporciona un archivo de configuración de bind de ejemplo (named.conf) en la carpeta cfg.
<p align="center"><a href="http://www.youtube.com/watch?feature=player_embedded&v=bvhyMFa_mBM" target="_blank"><img src="https://raw.githubusercontent.com/Homas/ioc2rpz/master/ioc2rpz_demo.png"></a></p>
## Interfaz web de ioc2rpz™
[ioc2rpz.gui](https://github.com/Homas/ioc2rpz.gui) es una interfaz web de gestión desarrollada como un proyecto independiente. No es necesaria para ejecutar ioc2rpz™.
## Soporte de protocolos
ioc2rpz™ escucha en múltiples protocolos de transporte. Todos los transportes comparten la misma canalización de procesamiento de consultas: limitación de tasa, validación TSIG, búsqueda en zonas y generación de respuestas. Consulte [docs/protocols.md](https://github.com/homas/ioc2rpz/blob/master/docs/protocols.md) para obtener la documentación completa de protocolos.
### Resumen de puertos
| Port | Protocol | Service | Condition |
|------|----------|---------|-----------|
| 53 | UDP | Consultas DNS (solo SOA) | Siempre |
| 53 | TCP | Consultas DNS, transferencias de zona AXFR/IXFR, gestión | Siempre |
| 853 | TCP+TLS | DoT — igual que TCP pero cifrado | Requiere configuración `cert` |
| 443/8443 | TCP+TLS | DoH (`/dns-query`) y API REST | Requiere configuración `cert` |
### UDP (Puerto 53)
UDP se utiliza para consultas DNS ligeras, principalmente búsquedas SOA. Las respuestas que superan los 512 bytes (o el tamaño de búfer anunciado por EDNS0) establecen el bit TC (truncamiento) según RFC 1035 §4.2.1, lo que hace que los clientes reintenten por TCP. Los comandos de gestión no son compatibles a través de UDP.```bash
dig @127.0.0.1 zone.ioc2rpz SOA -y hmac-sha256:keyname:base64key
TCP maneja las transferencias de zona (AXFR/IXFR), las consultas SOA y los comandos de gestión. Un pool de 5 workers accept pre-generados gestiona las conexiones entrantes.```bash
dig @127.0.0.1 zone.ioc2rpz AXFR +tcp -y hmac-sha256:keyname:base64key
dig @127.0.0.1 zone.ioc2rpz IXFR=12345 +tcp -y hmac-sha256:keyname:base64key
### DNS sobre TLS / DoT (Puerto 853)
ioc2rpz™ soporta la distribución de RPZ sobre DoT. El listener TLS se inicia automáticamente en el puerto 853 cuando hay un registro `cert` presente en la configuración. DoT soporta las mismas operaciones que TCP (AXFR, IXFR, SOA, gestión).
- Versiones TLS soportadas: 1.2 y 1.3 (`?TLSVersion = 'tlsv1.2-1.3'`)
- Reutilización de conexiones soportada según RFC 7858 §3.4 (múltiples consultas por sesión TLS, tiempo de inactividad de 30 segundos)
- TLS PIN no está soportado
- Los mensajes DNS NOTIFY se envían sin cifrar (UDP simple)
- Los certificados se actualizan automáticamente cuando los archivos se reemplazan en disco (hasta ~2 minutos de retraso debido al caché SSL de Erlang)
Para la configuración de certificados, consulte [Configuración de certificados](#certificate-setup).```bash
# SOA query over DoT
kdig @127.0.0.1 -p 853 zone.ioc2rpz SOA +tls -y hmac-sha256:keyname:base64key
# AXFR over DoT
dig @127.0.0.1 -p 853 zone.ioc2rpz AXFR +tls +tcp -y hmac-sha256:keyname:base64key
DoH proporciona resolución DNS sobre HTTPS a través del endpoint /dns-query. Métodos admitidos:
?dns=Content-Type: application/dns-message (máximo 4096 bytes; los payloads de mayor tamaño reciben HTTP 413)Las respuestas usan Content-Type: application/dns-message. DoH transporta el mensaje DNS completo en formato wire y se procesa a través de la misma ruta que Do53/DoT, por lo que hereda la misma autenticación TSIG; las transferencias de zona (AXFR/IXFR) siguen siendo solo TCP y no se sirven a través de DoH.```bash
curl -H "Accept: application/dns-message"
"https://127.0.0.1:443/dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB" -k
curl -X POST -H "Content-Type: application/dns-message"
--data-binary @dns_query.bin "https://127.0.0.1:443/dns-query" -k
### Limitación de tasa
Las consultas DNS se limitan en tasa utilizando una clave (híbrida) inteligente para que los clientes legítimos de múltiples zonas no sean penalizados mientras se bloquea el abuso por variación del nombre de consulta:
- **Zona aprovisionada + QTYPE compatible** (`SOA`/`AXFR`/`IXFR`, clase `IN`) y **comandos de gestión reconocidos** (clase `CHAOS`/`TXT`) se rastrean por `{client_IP, query_name, query_type}` — por lo que un secundario que consulta/transfiere varias zonas (p. ej. `rpz1`, `rpz2`, `rpz3`) además de la gestión desde una IP se cuenta de forma independiente por zona+tipo.
- **Cualquier otra cosa** (zona desconocida/no aprovisionada, tipo de consulta no compatible, clase incorrecta o nombre de gestión no reconocido) se agrega por `{client_IP}`, de modo que un atacante no puede evitar el límite variando el nombre de consulta.
Cuando se supera el límite, el servidor devuelve una respuesta DNS `REFUSED`.
| Parámetro | Valor predeterminado | Macro |
|-----------|---------|-------|
| Ventana | 10 segundos | `?RATE_LIMIT_WINDOW` (10000 ms) |
| Máx. solicitudes por ventana (granular: zona+tipo conocida / gestión) | 1 | `?MAX_REQUESTS_PER_WINDOW` |
| Máx. solicitudes por ventana (agregado: zona desconocida / tipo no compatible) | 1 | `?MAX_UNKNOWN_REQUESTS_PER_WINDOW` |
La limitación de tasa se aplica a todos los transportes de consultas DNS (UDP, TCP, TLS, DoH). La ventana y el umbral son configurables mediante macros en `include/ioc2rpz.hrl`.
### NOTIFY de DNS
Después de una actualización de zona (AXFR o IXFR), ioc2rpz™ envía mensajes de NOTIFY de DNS ([RFC 1996](https://tools.ietf.org/html/rfc1996)) por UDP a las direcciones IP configuradas en la `NotifyList` de la RPZ. Esto solicita a los servidores DNS secundarios que comprueben el serial SOA de la zona e inicien una transferencia si el serial ha cambiado.```erlang
%% NotifyList in RPZ config — last field before whitelists
{rpz, {"zone.ioc2rpz", ..., ["source1"], ["10.0.0.1", "10.0.0.2"], []}}.
La forma más sencilla de implementar el servicio es usando contenedores Docker en Docker Hub. Guía de implementación en Docker la puedes encontrar en la wiki de ioc2rpz™.
ioc2rpz™ está disponible en Docker Hub. Simplemente busca ioc2rpz™. Requisitos previos:
where /home/ioc2rpz/cfg, /home/ioc2rpz/db directorios en un sistema host.
Puede pasar un nombre de archivo de configuración personalizado mediante el parámetro ``-e``. Por ejemplo: ``-e CONF=./cfg/ioc2rpz2.conf``
## Variables de entorno
Las siguientes variables de entorno son utilizadas por ioc2rpz™, principalmente relevantes para despliegues de Docker. Se definen en `config/sys.config.src` y `config/vm.args`.
| Variable | Default | Descripción |
|----------|---------|-------------|
| `IPv4` | — | Dirección de enlace IPv4 |
| `IPv6` | — | Dirección de enlace IPv6 |
| `CONF` | — | Ruta al archivo de configuración (p. ej. `./cfg/ioc2rpz.conf`) |
| `DB` | `/opt/ioc2rpz/db` | Directorio de base de datos para la persistencia ETS |
| `CD` | `/opt/ioc2rpz` | Directorio de trabajo |
| `NODE_NAME` | `ioc2rpz` | Nombre corto del nodo Erlang |
| `IO2Cookie` | `ioc2rpz` | Cookie distribuida de Erlang |
Pase las variables de entorno a Docker con `-e`:```bash
docker run -d --name ioc2rpz -e CONF=./cfg/custom.conf -e NODE_NAME=mynode ...
Puedes desplegar ioc2rpz™ e ioc2rpz.gui usando docker compose. El archivo docker-compose.yml canónico se puede encontrar en el repositorio ioc2rpz.dc.
A continuación se muestra un ejemplo de docker-compose.yml que presenta ioc2rpz con la interfaz web y un sidecar de Let's Encrypt certbot:```yaml
version: "3.8"
services:
ioc2rpz:
image: pvmdel/ioc2rpz
container_name: ioc2rpz
restart: always
logging:
driver: syslog
ports:
- "53:53/tcp"
- "53:53/udp"
- "853:853/tcp"
- "8443:8443/tcp"
volumes:
- ./cfg:/opt/ioc2rpz/cfg
- ./db:/opt/ioc2rpz/db
- letsencrypt-certs:/opt/ioc2rpz/ssl:ro
ioc2rpz-gui: image: pvmdel/ioc2rpz.gui container_name: ioc2rpz-gui restart: always ports: - "443:443" depends_on: - ioc2rpz
certbot: image: certbot/certbot container_name: certbot volumes: - letsencrypt-certs:/etc/letsencrypt - letsencrypt-www:/var/www/certbot entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --quiet; sleep 12h; done'"
volumes: letsencrypt-certs: letsencrypt-www:
Actualiza tu `ioc2rpz.conf` para que haga referencia a la ruta del certificado montado:```erlang
{cert, {"ssl/live/ns1.rpz-proxy.com/fullchain.pem", "ssl/live/ns1.rpz-proxy.com/privkey.pem", ""}}.
Puede ejecutar ioc2rpz™ e ioc2rpz.gui en AWS. Para despliegues relativamente pequeños (varios cientos de miles de indicadores), incluso el nivel gratuito es suficiente. El siguiente video muestra cómo configurar ioc2rpz™ e ioc2rpz.gui en AWS usando ECS.
Se requieren certificados TLS para DoT (puerto 853), la API REST (puerto 8443) y DoH. El mismo certificado se utiliza para todos los servicios TLS. Consulte docs/deployment.md para obtener todos los detalles.
openssl req -x509 -newkey rsa:2048 -keyout cfg/ioc2rpz_dot.key
-out cfg/ioc2rpz_dot.crt -days 365 -nodes -subj "/CN=ioc2rpz"
### Let's Encrypt (Producción)```bash
# Obtain certificate
sudo certbot certonly --standalone -d ns1.rpz-proxy.com
# Copy to ioc2rpz cfg directory
cp /etc/letsencrypt/live/ns1.rpz-proxy.com/fullchain.pem cfg/ioc2rpz_dot.crt
cp /etc/letsencrypt/live/ns1.rpz-proxy.com/privkey.pem cfg/ioc2rpz_dot.key
0 3 * * * root certbot renew --quiet --deploy-hook "cp /etc/letsencrypt/live/ns1.rpz-proxy.com/fullchain.pem /opt/ioc2rpz/cfg/ioc2rpz_dot.crt && cp /etc/letsencrypt/live/ns1.rpz-proxy.com/privkey.pem /opt/ioc2rpz/cfg/ioc2rpz_dot.key"
Los certificados también se recargan explícitamente durante una recarga de configuración (`ioc2rpz-reload-cfg`): cuando ioc2rpz detecta que los archivos de certificado cambiaron, reinicia los listeners TLS con el nuevo certificado inmediatamente, en lugar de esperar a la caché SSL de Erlang de ~2 minutos. Añade un paso `ioc2rpz-reload-cfg` a tu hook de renovación para aplicar los nuevos certificados sin tiempo de inactividad.
### Montaje de volúmenes en Docker
Cuando se ejecuta en Docker, monta el directorio de certificados desde el host:```bash
docker run -d --name ioc2rpz \
--mount type=bind,source=/etc/letsencrypt/live/ns1.rpz-proxy.com,target=/opt/ioc2rpz/ssl,readonly \
...
Referencia en ioc2rpz.conf:```erlang
{cert, {"ssl/fullchain.pem", "ssl/privkey.pem", ""}}.
Erlang detecta automáticamente los archivos de certificado reemplazados en aproximadamente 2 minutos. Los certificados también se recargan explícitamente durante la recarga de configuración (`ioc2rpz-reload-cfg`). No deje que los certificados caduquen — renueve antes de la expiración para un servicio ininterrumpido.
## Compilación desde el código fuente
> **Nota:** Compilar desde el código fuente está pensado para **desarrollo y pruebas**. Para producción, **Docker es el método de despliegue recomendado** — consulte [Docker Compose](#docker-compose) y el repositorio [ioc2rpz.dc](https://github.com/Homas/ioc2rpz.dc).
### Requisitos previos
- **Erlang/OTP 24 o superior** (`erl -version` para comprobarlo) y un [rebar3](https://www.rebar3.org) compatible.
- Una cadena de herramientas de C (para compilar dependencias) y `git`.
### Compilar y ejecutar```bash
# 1. Clone the repository
git clone https://github.com/Homas/ioc2rpz.git
cd ioc2rpz
# 2. Build a release
rebar3 release
# 3. Edit the configuration (see the minimal example below)
$EDITOR cfg/ioc2rpz.conf
# 4. Start the server (foreground console, or 'start' for background)
_build/default/rel/ioc2rpz/bin/ioc2rpz console
# or: _build/default/rel/ioc2rpz/bin/ioc2rpz start
Por defecto, ioc2rpz™ lee su configuración de ./cfg/ioc2rpz.conf, escucha en todas las interfaces de red y escribe su copia de seguridad de la base de datos en ./db. Los valores predeterminados de compilación (puertos, rutas, temporizadores) se encuentran en include/ioc2rpz.hrl.
Una cfg/ioc2rpz.conf mínima con una clave TSIG, una fuente de archivo y una zona RPZ:```erlang
%% Server NS record, admin mailbox, management key(s), and management ACL
{srv,{"ns1.example.com","hostmaster.example.com",["mgmtkey"],["127.0.0.1","::1"]}}.
%% A TSIG key (name, algorithm, base64 secret) used for management / zone transfers {key,{"mgmtkey","sha256","5Yvt70eJnf95+LJeI8H3TgKGeVparmMB7udA0pv/JRE="}}.
%% An IOC source: a local file parsed as a full (AXFR) feed of domains {source,{"sample","file:cfg/small_ioc.txt","[:AXFR:]","^([0-9A-Za-z.-]+\.[0-9A-Za-z.-]+)$","",0,0,0,"mixed",true}}.
%% An RPZ zone built from the source, served with the nxdomain action {rpz,{"rpz.example.com",86400,3600,2592000,7200,"true","true","nxdomain",["mgmtkey"],"mixed",604800,86400,["sample"],[],[]}}.
Consulta [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/master/docs/configuration.md) para la referencia autoritativa campo por campo (disposición de tuplas source/RPZ, temporizadores SOA, grupos de claves, certificados, etc.). Para habilitar DoT (puerto 853), DoH y la API REST HTTPS, añade una entrada `{cert,{...}}` — consulta [Configuración de certificados](#certificate-setup).
### Shell de desarrollo (solo desarrollo/pruebas)
Los siguientes comandos son para desarrollo y pruebas locales — no para uso en producción:```bash
# Compile only (no release)
rebar3 compile
# Run the EUnit test suite
rebar3 eunit
# Run tests for a single module
rebar3 eunit --module=ioc2rpz_fun
# Start an interactive shell with the application and all deps loaded
rebar3 shell
En rebar3 shell puedes probar el sistema en ejecución directamente, por ejemplo:```erlang
%% Inspect the listener pools and ETS tables
supervisor:which_children(ioc2rpz_sup).
ets:info(cfg_table, size).
ets:info(rpz_hotcache_table, size).
%% Trigger a configuration reload / forced zone update ioc2rpz_sup:reload_config3(reload). ioc2rpz_sup:update_all_zones(true).
## Gestión de ioc2rpz™
### vía DNS
ioc2rpz™ admite gestión mediante DNS/TCP o DoT. Se recomienda utilizar DoT o la API REST sobre DNS/TCP. La versión actual de ioc2rpz™ no admite una IP/interfaz de gestión separada. En cualquier caso, se recomienda encarecidamente crear una clave TSIG designada (o claves) para uso exclusivo de gestión. Puedes desactivar la gestión a través de DNS.
Acciones admitidas:
- Estado actual de ioc2rpz™. Solicita ``ioc2rpz-status``, clase ``CHAOS``, registro ``TXT``. p. ej.:```
dig +tcp -y dnsmkey_1:ayVnL+h2QKMszRVohrngagcEuIpN3RkecXKdwSa5WsHD5N4Y5R3NUMGM W8sIGv36gPkAtWtgarqKzN9tmHqEnA== @127.0.0.1 ioc2rpz-status TXT -c CHAOS
ioc2rpz-reload-cfg, Clase RR CHAOS, Tipo RR TXTioc2rpz-update-tkeys, Clase RR CHAOS, Tipo RR TXTioc2rpz-update-all-rpz, Clase RR CHAOS, Tipo RR TXTzone_name, Clase RR CHAOS, Tipo RR TXT. P. ej. el refresco completo de dga.ioc2rpz se puede invocar mediante:```
dig +tcp -y dnsmkey_1:ayVnL+h2QKMszRVohrngagcEuIpN3RkecXKdwSa5WsHD5N4Y5R3NUMGM W8sIGv36gPkAtWtgarqKzN9tmHqEnA== @127.0.0.1 dga.ioc2rpz TXT -c CHAOS- Detener ioc2rpz™. RR Name ``ioc2rpz-terminate``, RR Class ``CHAOS``, RR Type ``TXT``
- Solicitar una zona de muestra. RR Name ``sample-zone.ioc2rpz``, RR Class ``IN``, RR Type ``AXFR``
### mediante REST
La API REST (puerto 8443/tcp) es la interfaz de gestión preferida. Por razones de seguridad, todo el tráfico de gestión debe estar cifrado y la API REST no se inicia si no hay un certificado SSL. Todos los endpoints aceptan tanto métodos GET como POST.
Se utiliza autenticación HTTP básica para autenticar las solicitudes. Las claves TSIG de gestión se utilizan para la autenticación de solicitudes. El nombre de una clave TSIG se utiliza como nombre de usuario HTTP y la clave TSIG como contraseña. El acceso a la API REST está restringido con la ACL definida en el registro `srv`.
El segmento de versión de la API admite tanto `v1` como `v1.0` (p. ej., `/api/v1/stats/serv` o `/api/v1.0/stats/serv`).
La API REST admite salida JSON (predeterminada) y texto plano según el encabezado `Accept`:
- `application/json` — salida JSON (predeterminada)
- `text/plain` — salida en texto plano```bash
# Example: plain text output
curl -u "keyname:base64key==" -k -H "Accept: text/plain" https://127.0.0.1:8443/api/v1/mgmt/update_tkeys
Las respuestas de texto utilizan el formato:``` status: ok msg: TSIG keys were updated
#### Parámetros de ruta
| Parámetro | Descripción |
|-----------|-------------|
| `:rpz_name` | Nombre de la zona RPZ (p. ej. `dga.ioc2rpz`) |
| `:source_name` | Nombre de la fuente de IOC (p. ej. `sample_fqdn`) |
| `:ioc` | Indicador a buscar (FQDN o IP, p. ej. `baddomain.com`) |
#### Endpoints de estadísticas
`/api/v1/stats/serv` — Estadísticas del servidor (nombre del nodo, reglas totales, uso de memoria)```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/stats/serv
{
"srv": {
"node_name": "ioc2rpz@hostname",
"srv_total_rules": 15000,
"hot_cache_mem": "12.5 Mb",
"axfr_table_mem": "45.2 Mb",
"ixfr_table_mem": "8.1 Mb"
},
"rpz": [...],
"sources": [...]
}
/api/v1/stats/rpz — Estadísticas de zona RPZ```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/stats/rpz
```json
{
"rpz": [
{
"name": "malware.ioc2rpz",
"status": "ready",
"rule_count": 5000,
"ioc_count": 4500,
"serial": 1709000000,
"serial_ixfr": 1708990000,
"update_time": 1709000000,
"ixfr_update_time": 1708995000,
"ixfr_nz_update_time": 1708995000
}
]
}
El campo status (ready, updating, forceAXFR, notready) indica si los recuentos/serial informados están actualizados; cuando es updating o forceAXFR, reflejan la última actualización completada. Los recuentos y el serial se conservan tras una recarga de configuración en lugar de restablecerse a cero.
/api/v1/stats/source — Estadísticas de origen```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/stats/source
```json
{
"sources": [
{"name": "sample_fqdn", "ioc_count": 150}
]
}
/api/v1/mgmt/reload_cfg — Recargar el archivo de configuración```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/mgmt/reload_cfg
Éxito: `{"status":"ok","msg":"Configuration reloaded"}`
Error (HTTP 520): `{"status":"error","msg":"Configuration reload error"}`
`/api/v1/mgmt/update_tkeys` — Recargar las claves TSIG desde la configuración```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/mgmt/update_tkeys
Éxito: {"status":"ok","msg":"TSIG keys were updated"}
Error (HTTP 520): {"status":"error","msg":"TSIG keys update error"}
/api/v1/mgmt/terminate — Apagado ordenado del servidor```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/mgmt/terminate
Éxito: `{"status":"ok","msg":"Terminating"}`
#### Endpoints de actualización de zonas
`/api/v1/update/all_rpz` — Fuerza una actualización completa de todas las zonas RPZ```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/update/all_rpz
Éxito: {"status":"ok","msg":"All RPZ zones will be updated"}
/api/v1/update/:rpz_name — Fuerza la actualización completa de una zona específica```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/update/dga.ioc2rpz
Éxito: `{"status":"ok","msg":"RPZ dga.ioc2rpz will be updated"}`
Error (HTTP 520): `{"status":"error","msg":"RPZ dga.ioc2rpz not found"}`
#### Endpoints de gestión de caché
`/api/v1/cache/sources/clear/all` — Elimina todas las fuentes de la caché activa```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/cache/sources/clear/all
Éxito: {"status":"ok","msg":"All sources were removed from the hotcache"}
/api/v1/cache/sources/clear/:source_name — Eliminar una fuente específica de la caché en caliente```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/cache/sources/clear/sample_fqdn
Éxito: `{"status":"ok","msg":"sample_fqdn source was removed from the hot cache"}`
`/api/v1/cache/sources/load/all` — Recargar todas las fuentes en la caché en caliente```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/cache/sources/load/all
Éxito: {"status":"ok","msg":"All sources will loaded to the hot cache"}
/api/v1/feed/:rpz_name — Obtener indicadores de un feed RPZ
Parámetro de consulta: ?type=fqdn|ip|both (por defecto: both)```bash
curl -u "keyname:base64key==" -k "https://127.0.0.1:8443/api/v1/feed/malware.ioc2rpz?type=fqdn"
Éxito:```json
{
"status": "ok",
"rpz": "malware.ioc2rpz",
"iocs": ["baddomain.com", "evil.example.org"]
}
Error (HTTP 520): {"status":"error","msg":"RPZ malware.ioc2rpz not found"}
/api/v1/ioc/:ioc — Comprueba si un indicador está bloqueado por alguna fuente RPZ
Parámetro de consulta: ?tkey=keyname — limita la búsqueda a zonas accesibles mediante esa clave (opcional)```bash
curl -u "keyname:base64key==" -k "https://127.0.0.1:8443/api/v1/ioc/baddomain.com?tkey=dnsproxykey_1"
Éxito:```json
{
"ioc": "baddomain.com",
"tkey": "dnsproxykey_1",
"data": [
{
"ioc": "baddomain.com",
"feeds": [
{
"feed": "malware.ioc2rpz",
"wildcard": "true",
"type": "fqdn",
"rpz_serial": 1709000000,
"ioc_expiration": 0,
"sources": ["abuse-ch", "internal-list"]
}
]
}
]
}
Error: {"status":"error", "ioc": "nonexistent.com"}
El campo adicional sources enumera los nombres de las fuentes contribuyentes por feed cuando el seguimiento de fuentes está habilitado; es null cuando el seguimiento está deshabilitado o la atribución es desconocida. Consulte Atribución de Fuentes IOC para conocer la configuración y la semántica.
Cualquier ruta no reconocida devuelve HTTP 200 con:```json {"status":"error","msg":"Unsupported request"}
## Monitoreo y Comprobaciones de Salud
### Estadísticas de REST API```bash
# Server statistics
curl -u "keyname:key==" -k https://127.0.0.1:8443/api/v1/stats/serv
# RPZ zone statistics (indicator counts, serials, update times)
curl -u "keyname:key==" -k https://127.0.0.1:8443/api/v1/stats/rpz
# Source statistics
curl -u "keyname:key==" -k https://127.0.0.1:8443/api/v1/stats/source
Si se ejecuta en un shell interactivo o si está conectado a un nodo en ejecución:```erlang %% Check supervisor children supervisor:which_children(ioc2rpz_sup). supervisor:count_children(ioc2rpz_tls_sup_v6). supervisor:count_children(ioc2rpz_tcp_sup_v6).
%% Check ETS table sizes (memory usage) ets:info(cfg_table, size). ets:info(rpz_hotcache_table, size). ets:info(rate_limits, size).
### Comprobaciones de salud de DNS```bash
# SOA query over UDP
dig @localhost -p 53 your-zone.rpz SOA +short
# Zone transfer over TCP
dig @localhost -p 53 your-zone.rpz AXFR +tcp -y hmac-sha256:keyname:base64key
# DoT query
dig @localhost -p 853 +tls your-zone.rpz SOA
# Sample zone (built-in test zone)
dig @localhost sample-zone.ioc2rpz AXFR +tcp
Consulte docs/deployment.md para obtener la referencia completa de supervisión y registros.
| Problema | Solución |
|---|---|
| Puerto ya en uso | Compruébelo con lsof -i :53 / lsof -i :853. {reuseaddr, true} está establecido en los sockets de escucha para reinicios rápidos. |
| DoT no acepta conexiones | Verifique que {cert, ...} esté configurado. Compruebe los workers TLS: supervisor:count_children(ioc2rpz_tls_sup_v6). Compruebe que el puerto 853 no esté bloqueado por el firewall. |
| La transferencia de zona falla (discrepancia de TSIG) | Verifique que el nombre de la clave y el secreto coincidan entre cliente y servidor. Busque eventos CEF 104/105 en los registros. |
| Errores de descarga de fuentes | Compruebe la conectividad de red. Busque Error downloading feed en los registros. El servidor reintenta 3 veces con un retraso de 3 s. Para fuentes HTTPS, asegúrese de que el certificado TLS sea válido. |
| Alto uso de memoria | Compruebe los tamaños de las tablas ETS en el shell de Erlang. rate_limits y rpz_hotcache_table se limpian periódicamente. Considere reducir ?HotCacheTime. |
Consulte docs/deployment.md para obtener una solución de problemas detallada.
La configuración es un archivo de Erlang. Cada opción de configuración es un término de Erlang, por lo que la configuración debe cumplir con la sintaxis de Erlang. El servidor valida las entradas de configuración al cargar/recargar: codificación de claves TSIG, existencia de archivos de certificado, compilación de expresiones regulares y sintaxis de URL. Las entradas no válidas se registran y se omiten. Consulte docs/configuration.md para obtener la referencia completa de configuración.
ioc2rpz™ admite los siguientes parámetros de configuración:
El registro srv se utiliza para definir los valores predeterminados del servidor. Consta de:
TrackSources — el valor predeterminado global a nivel de servidor para la atribución de fuentes: off | auto | on (por defecto off). Se aplica a cualquier feed cuyo propio track_sources no esté establecido. Consulte Atribución de fuentes IOC.Ejemplo de registro srv:``` {srv,{"ns1.example.com","support.email.example.com",["dnsmkey_1","dnsmkey_2","dnsmkey_3"],["acl_ip1","acl_ip2"]}}.
%% With the optional global source-tracking default (5-field form): {srv,{"ns1.example.com","support.email.example.com",["dnsmkey_1"],["acl_ip1"],auto}}.
La forma de 4 campos sigue siendo válida y, por defecto, establece `TrackSources` en `off`.
### Registro **cert**
El registro **cert** se utiliza para definir un certificado y una clave privada para las comunicaciones DNS sobre TLS, API REST y DoH. Para la generación y gestión de certificados, consulte [Configuración de certificados](#certificate-setup).
Consta de:
- la ruta a un archivo que contiene un certificado;
- la ruta a un archivo que contiene una clave privada codificada en PEM;
- la ruta a un archivo con certificados de CA codificados en PEM.
Ejemplo de registro **cert**:```
{cert,{"cfg/cert.pem", "cfg/key.pem", "cfg/cacerts.pem"}}.
El registro include permite dividir la configuración de ioc2rpz™ en múltiples archivos.
Ejemplo de registro include:``` {include,"cfg/tkeys.include.cfg"}.
### Registro **key**
Las claves TSIG se utilizan para la autenticación y autorización. Se recomienda utilizar diferentes claves TSIG para la gestión de ioc2rpz™ y las transferencias de zonas.
El registro **key** consta de:
- nombre de la clave TSIG;
- algoritmo. Se admiten ``md5``, ``sha256`` y ``sha512``;
- la clave;
- (opcional) lista de grupos de claves a los que pertenece.
Ejemplo de registros **key**:```
{key,{"key_name_1","md5","ayVnL+h2QKMszRVohrngagcEuIpN3RkecXKdwSa5WsHD5N4Y5R3NUMGM W8sIGv36gPkAtWtgarqKzN9tmHqEnA=="}}.
{key,{"key_name_2","sha256","5Yvt70eJnf95+LJeI8H3TgKGeVparmMB7udA0pv/JRE="}}.
{key,{"key_name_3","sha512","03uuaGl9kqfenjRgIeCv6e29lVvMwviB1+cDX1I0jcVOcTU4jWFwRkfo3ULRMD+NGDfwzYvXkJ94FNEaAW4vzw==",["customers","public"]}}.
La utilidad dnssec-keygen se puede utilizar para generar claves TSIG:```bash
dnssec-keygen -a HMAC-MD5 -b 512 -n USER tsig-key
dnssec-keygen -a HMAC-SHA256 -b 256 -n USER tsig-key
dnssec-keygen -a HMAC-SHA512 -b 512 -n USER tsig-key
Consulte la documentación de "dnssec-keygen" para obtener más detalles. Las claves pueden asignarse a grupos para la autorización basada en grupos en el control de acceso a zonas RPZ.
### **key_group** registro
Los grupos de claves proporcionan autorización basada en grupos para las transferencias y la administración de zonas. En lugar de enumerar claves individuales en los registros `rpz` o `srv`, puede asignar claves a grupos con nombre y hacer referencia al grupo.
Hay dos formas de asignar claves a grupos:
1. En línea, mediante el campo opcional 4.º del registro `key`: `{key, {Name, Alg, Secret, [Groups]}}`
2. Explícitamente, mediante un registro `key_group` (se muestra a continuación)```erlang
{key_group, {GroupName, [KeyName1, KeyName2, ...]}}.
| Campo | Tipo | Descripción |
|---|---|---|
| GroupName | string | Nombre del grupo (p. ej. "customers") |
| Keys | list of strings | Lista de nombres de claves TSIG que pertenecen a este grupo |
Ejemplo de registros key_group:```erlang {key_group, {"customers", ["dnsproxykey_1", "dnsproxykey_2"]}}. {key_group, {"public", ["dnsproxykey_3"]}}.
Los grupos se referencian en los registros `rpz` y `srv` mediante la tupla `{groups, ["group1", "group2"]}` en la lista de claves:```erlang
{rpz, {"zone.ioc2rpz", 7202, 3600, 2592000, 7200, "true", "true", "nxdomain",
["dnsproxykey_1", {groups, ["customers", "public"]}],
"fqdn", 86400, 3600, ["source1"], [], []}}.
Las listas blancas se utilizan para prevenir posibles errores y el bloqueo de dominios y direcciones IP de confianza. Los IOC incluidos en la lista blanca se eliminan de las zonas de política de respuesta. ioc2rpz™ solo comprueba la coincidencia exacta, por lo que no dividirá ni descartará una red si una dirección IP en la lista blanca está incluida en una subred bloqueada y viceversa. Una lista blanca es un archivo de texto o una fuente de datos de texto. Los indicadores deben separarse mediante caracteres de nueva línea (/n,/r o ambos /n/r). Las listas blancas deben contener FQDN válidos y/o direcciones IP. ioc2rpz™ admite una cantidad ilimitada de indicadores.
El registro whitelists consta de:
""), se utilizará un REGEX predeterminado ("^([A-Za-z0-9][A-Za-z0-9\-\._]+)[^A-Za-z0-9\-\._]*.*$"). Se utiliza none si no se requiere REGEX (la fuente ya proporciona datos en el formato requerido).Ejemplo de registro whitelist:``` {whitelist,{"whitelist_1","file:cfg/whitelist1.txt",none}}.
### **source** record
Una fuente es un feed de indicadores maliciosos. Se admiten FQDNs, direcciones IPv4 e IPv6. Una fuente es un archivo de texto o un feed de datos de texto. Los indicadores deben separarse mediante caracteres de nueva línea/retorno de carro (/n,/r o ambos /r/n). ioc2rpz™ admite un número ilimitado de indicadores.
El registro **source** consta de:
- nombre de la fuente;
- ruta de la fuente para la transferencia completa de la fuente (AXFR). Se admiten URLs(http/https/ftp), archivos locales y scripts. El prefijo **file:** se utiliza para archivos locales. El prefijo **shell:** se utiliza para ejecutar un script/comando local en un host/contenedor que debe devolver indicadores y una fecha de expiración opcional a STDOUT;
- ruta de la fuente para la transferencia incremental de la fuente (IXFR). Las rutas AXFR,IXFR admiten palabras clave para acortar URLs y proporcionar marcas de tiempo de actualización de zona:
- **[:AXFR:]** - ruta AXFR completa. Solo se puede usar en rutas IXFR;
- **[:FTimestamp:]** - marca de tiempo de la última actualización de la fuente (p. ej. 1507946281)
- **[:ToTimestamp:]** - marca de tiempo actual;
- REGEX que se utiliza para extraer indicadores y su tiempo de expiración. La primera coincidencia es un indicador, la segunda es un tiempo de expiración. El tiempo de expiración es un parámetro opcional. Una expresión regular debe incluirse entre comillas dobles. Si se especifica una REGEX vacía (`""`), se utilizará una REGEX predeterminada (`"^([A-Za-z0-9][A-Za-z0-9\-\._]+)[^A-Za-z0-9\-\._]*.*$"`). Se utiliza `none` si no se requiere REGEX (la fuente ya proporciona datos en el formato requerido).
Parámetros opcionales (deben usarse todos o ninguno):
- UserID (utilizado internamente).
- Número máximo de IoCs.
- Actualización completa de la fuente, tiempo de caché caliente (en segundos).
- Actualización incremental de la fuente, tiempo de caché caliente (en segundos).
Las descargas de fuentes HTTPS verifican el certificado TLS del servidor remoto. Las fuentes con certificados no válidos o autofirmados fallarán al descargarse. Para certificados autofirmados, use una fuente `shell:` con `curl --insecure`.
**Restricciones de archivos locales:** las rutas `file:` que contengan `..` (recorrido del directorio padre) se rechazan por seguridad; use una ruta sin `..` (dentro del directorio de trabajo/datos).
**Restricciones de comandos shell:** el ejecutable de cada segmento de la tubería debe ser una ruta absoluta (p. ej. `/usr/bin/curl`) o una utilidad de texto segura de nombre simple (`sort`, `uniq`, `grep`, `sed`, `awk`, `gawk`, etc.); los comandos destructivos y shells (`rm`, `bash`, `sh`, etc.) están bloqueados, y la sustitución de comandos (`$(...)`, comillas invertidas) y la redirección de salida (`>`, `>>`) se rechazan. Los comandos rechazados no se ejecutan y se registran mediante el evento CEF 151 (los comandos ejecutados se registran mediante 150). Consulte [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/master/docs/configuration.md#shell-command-restrictions) para conocer el conjunto de reglas completo.
Si una fuente devuelve menos del 50% de su recuento anterior de indicadores, la actualización se rechaza y se conservan los datos anteriores. Esto evita que los feeds degradados reduzcan la cobertura de RPZ. Configurable mediante la macro `?SOURCE_MIN_IOC_RATIO`.
Ejemplo de registro **source**:```
%% Local file source — indicators without expiration
{source,{"sample_fqdn","file:cfg/sample_ioc_fqdn.txt","[:AXFR:]",none}}.
%% Local file source — indicators with expiration timestamps (tab-separated)
{source,{"sample_expire","file:cfg/sample_ioc_expire.txt","[:AXFR:]","^([A-Za-z0-9][A-Za-z0-9\-\._]+)\t([0-9TZ:\-]+)$"}}.
%% Shell source — fetch RPZ via AXFR and extract CNAMEs
{source,{"base.rpz1","shell:/usr/bin/dig -y KEYNAME:TSIGKEY @127.0.0.1 base.rpz.ioc2rpz.local axfr | /bin/grep -e CNAME | /bin/grep -v '*.' | /usr/bin/awk -F '.base.rpz' '{print $1}'","",none}}.
Fuente shell: se utiliza para ampliar las opciones de conectividad de ioc2rpz™, que de forma nativa son algo limitadas. El contenedor de ioc2rpz™ incluye dig, grep, awk y python. Por ejemplo, puedes mezclar diferentes feeds de RPZ u obtener datos de una base de datos.
Restricciones de comandos Shell: Todos los ejecutables deben usar rutas absolutas (p. ej., /usr/bin/curl, no curl). Los comandos destructivos (rm, bash, sh, etc.) están bloqueados. La sustitución de comandos ($(...), comillas invertidas) y la redirección de salida (>, >>) se rechazan. Ver docs/configuration.md para obtener más detalles.
Seguridad de archivos locales: Las rutas de archivo que contengan .. (recorrido al directorio padre) se rechazan por seguridad.
El término RPZ define una zona de política de respuesta.
El registro rpz se compone de:
true o false. true define que la RPZ debe almacenarse en caché, false - no almacenada en caché, las fuentes de zona en vivo se descargan y se genera una RPZ mediante una solicitud AXFR. Las zonas "en vivo" no admiten transferencias de zona incrementales. Si el feed RPZ no está almacenado en caché, de todos modos se almacena temporalmente en una caché en caliente. En caso de que una solicitud de un cliente agote el tiempo de espera, podremos responder la próxima vez. El tiempo AXFR se utilizará para determinar la vida útil de la caché;true o false. Define si se deben generar reglas comodín;mixed, fqdn, ip. Se utiliza para la optimización.TrackSources — atribución de fuente por feed: auto | true | false. Cuando está presente, anula el valor predeterminado global del servidor. Cuando se omite (formato de 15 campos), el feed hereda el valor predeterminado del servidor (#srv TrackSources, off a menos que esté configurado). Ver IOC Source Attribution.| Acción | Valor de configuración | Descripción |
|---|---|---|
| NXDOMAIN | "nxdomain" | Devuelve NXDOMAIN (el dominio no existe) |
| NODATA | "nodata" | Devuelve respuesta vacía (el dominio existe, sin registros) |
| Passthru | "passthru" | Permite la consulta (regla de exención) |
| Drop | "drop" | Descartar la consulta silenciosamente |
| TCP-Only | "tcp-only" | Fuerza al cliente a reintentar por TCP |
| Block NS | "blockns" | Bloquear el servidor de nombres autoritativo |
| Redirect (domain) | {"redirect_domain","example.com"} | Redirigir a un dominio específico (alias de local_cname) |
| Redirect (IP) | {"redirect_ip","127.0.0.1"} | Redirigir a una IP específica (alias de local_a/local_aaaa) |
| Local A | {"local_a","127.0.0.1"} | Devolver una dirección IPv4 personalizada |
| Local AAAA | {"local_aaaa","fe80::1"} | Devolver una dirección IPv6 personalizada |
| Local CNAME | {"local_cname","www.example.com"} | Devolver una redirección CNAME |
| Local TXT | {"local_txt","Text Record"} | Devolver un registro TXT |
Se pueden combinar varias acciones locales en una lista: [{"local_a","127.0.0.1"},{"local_aaaa","fe80::1"},{"local_txt","Blocked"}]
Ejemplo de registro rpz:``` {rpz,{"zone_name",soa_refresh, soa_update_retry,soa_expire,soa_nxdomain_ttl,"cache","wildcards","action",["key1","key2"],"Zone_type",AXFT_Time, IXFR_Time,["source1","source2"],["notify_ip1","notify_ip2"],["whitelist_1","whitelist_2"]}}.
{rpz,{"zone_name",soa_refresh, soa_update_retry,soa_expire,soa_nxdomain_ttl,"cache","wildcards","action",["key1","key2",{groups,["group1","group2"]}],"Zone_type",AXFT_Time, IXFR_Time,["source1","source2"],["notify_ip1","notify_ip2"],["whitelist_1","whitelist_2"]}}.
{rpz,{"mixed.ioc2rpz",7202,3600,2592000,7200,"true","true","passthru",["dnsproxykey_1","dnsproxykey_2"],"mixed",86400,3600,["sample_fqdn","sample_expire","sample_ip"],[],["whitelist_1","whitelist_2"]}}.
{rpz,{"mixed.ioc2rpz",7202,3600,2592000,7200,"true","true","passthru",["dnsproxykey_1","dnsproxykey_2",{groups,["public","ip2"]}],"mixed",86400,3600,["sample_fqdn","sample_expire","sample_ip"],[],["whitelist_1","whitelist_2"]}}.
%% With explicit per-feed source tracking (16-field form; trailing auto):
{rpz,{"mixed.ioc2rpz",7202,3600,2592000,7200,"true","true","passthru",["dnsproxykey_1"],"mixed",86400,3600,["sample_fqdn","sample_expire","sample_ip"],[],["whitelist_1"],auto}}.
### Atribución de fuentes IOC
Un feed RPZ se construye fusionando indicadores de múltiples fuentes. La atribución de fuentes permite que la API de búsqueda de IOC informe **qué fuente(s) dentro de un feed** contribuyeron con un indicador — útil para el triage de un falso positivo reportado. El seguimiento está **desactivado por defecto** y se controla por feed con un valor global predeterminado opcional a nivel de servidor.
**Configuración**
- Por feed: el elemento `TrackSources` opcional final (16.º) de la tupla `{rpz,{...}}` — `auto | true | false`.
- Valor global predeterminado del servidor: el elemento `TrackSources` opcional final (5.º) de la tupla `{srv,{...}}` — `off | auto | on` (por defecto `off`).
Ambos son opcionales y compatibles con versiones anteriores: los archivos de configuración existentes (`rpz` de 15 campos, `srv` de 4 campos) se cargan sin cambios y se comportan como `off`.
**Precedencia de resolución** — el estado efectivo para un feed se resuelve como:
1. el valor explícito `track_sources` del feed, si está definido;
2. de lo contrario, el valor global predeterminado del servidor;
3. de lo contrario, el valor predeterminado integrado `off`.
`auto` significa: rastrear solo feeds **multifuente**. Los feeds de una sola fuente nunca se enmascaran — su único nombre de fuente se devuelve directamente (sin coste de seguimiento).
**API — el campo aditivo `sources`**
La respuesta de `/api/v1/ioc/:ioc` gana un nuevo campo `sources` en cada objeto de feed. Es **aditivo y compatible con versiones anteriores**: todos los campos existentes no cambian, por lo que los clientes antiguos que lo ignoren seguirán funcionando.
- Feed multifuente rastreado: un array JSON con los nombres de las fuentes que contribuyen.
- Feed de una sola fuente: un array de un solo elemento con ese único nombre de fuente.
- Seguimiento desactivado o atribución desconocida (p. ej., una fila en caché anterior a la actualización, antes de su reconstrucción AXFR única): `null` (JSON) / `(disabled)` o `(unavailable)` (TXT).```json
{
"feed": "mixed.ioc2rpz",
"wildcard": "true",
"type": "fqdn",
"rpz_serial": 1709000000,
"ioc_expiration": 0,
"sources": ["abuse-ch", "internal-list"]
}
Feed de una sola fuente: "sources": ["sample_fqdn"]. Seguimiento deshabilitado/desconocido: "sources": null.
Despliegue (desactivado por defecto)
Actualizar el binario no cambia nada — ni seguimiento, ni reconstrucciones de zona, ni API modificada — hasta que se establezca un valor de track_sources. Los feeds se gestionan desde la GUI; la GUI o el sitio de la comunidad escribirán estos valores de configuración opcionales y expondrán sources en una actualización posterior. Habilitar el seguimiento de un feed en caché desencadena una reconstrucción AXFR única para rellenar las máscaras de fuente.
Limitaciones
cache = "true"), ya que la consulta a la API lee la tabla de caché IXFR.{key,{"dnsproxykey_1","md5","apXqLsDs90H213eV6LS9ryYp5tY8YTpkttOkRCve7dp1Zeob3SGAbaVU9BShpsW25MmR8mTiX5OY0Qetv977Yw=="}}. {key,{"dnsproxykey_2","sha512","03uuaGl9kqfenjRgIeCv6e29lVvMwviB1+cDX1I0jcVOcTU4jWFwRkfo3ULRMD+NGDfwzYvXkJ94FNEaAW4vzw=="}}. {key,{"dnsmkey_3","sha512","03uuaGl9kqfenjRgIeCv6e29lVvMwviB1+cDX1I0jcVOcTU4jWFwRkfo3ULRMD+NGDfwzYvXkJ94FNEaAW4vzw=="}}.
{whitelist,{"whitelist_1","file:cfg/sample_whitelist.txt",none}}. {whitelist,{"whitelist_2","file:cfg/whitelist2.txt",""}}.
{source,{"sample_fqdn","file:cfg/sample_ioc_fqdn.txt","[:AXFR:]",none}}. {source,{"sample_expire","file:cfg/sample_ioc_expire.txt","[:AXFR:]","^([A-Za-z0-9][A-Za-z0-9\-\._]+)\t([0-9TZ:\-]+)$"}}. {source,{"sample_ip","file:cfg/sample_ioc_ip.txt","[:AXFR:]",none}}.
{rpz,{"localdata.ioc2rpz",7202,3600,2592000,7200,"false","true",[{"local_aaaa","fe80::1"},{"local_a","127.0.0.1"},{"local_a","127.0.0.2"},{"local_a","127.0.0.3"},{"local_a","127.0.0.4"},{"local_cname","www.example.com"},{"local_txt","Text Record www.example.com"},{"local_txt","Text Record 2"}],["dnsproxykey_1", "dnsproxykey_2"],"mixed",30,30,["sample_fqdn"],[],["whitelist_1","whitelist_2"]}}. {rpz,{"dga.ioc2rpz",7202,3600,2592000,7200,"true","true","nodata",["dnsproxykey_1","dnsproxykey_2"],"fqdn",172800,3600,["sample_expire"],[],[]}}. {rpz,{"mixed.ioc2rpz",7202,3600,2592000,7200,"true","true","passthru",["dnsproxykey_1", "dnsproxykey_2"],"mixed",86400,3600,["sample_fqdn","sample_expire"],[],["whitelist_1","whitelist_2"]}}. {rpz,{"ip-block.ioc2rpz",7202,3600,2592000,7200,"false","true","nxdomain",["dnsproxykey_1","dnsproxykey_2"],"ip",172800,0,["sample_ip"],[],[]}}.
</details>
## Valores de configuración predefinidos - include/ioc2rpz.hrl
include/ioc2rpz.hrl contiene parámetros precompilados.
Parámetros estándar:
- ``MGMToDNS`` (true/false) - habilita la gestión sobre DNS/TCP;
- ``DBStorage`` (ets) - define el almacenamiento en DB para las cachés AXFR e IXFR. La versión actual solo admite ETS;
- ``SaveETS`` (true/false) - define si las tablas ETS AXFR/IXFR deben guardarse en disco;
- ``Port`` (valor numérico, 1 - 65535) - define el puerto DNS en el que se ejecuta el servicio;
- ``PortTLS`` (valor numérico, 1 - 65535) - define el puerto DoT en el que se ejecuta el servicio;
- ``PortREST`` (valor numérico, 1 - 65535) - define el puerto HTTPs en el que se ejecuta el servicio;
- ``TTL`` (valor numérico, en segundos) - TTL predeterminado para registros DNS/reglas RPZ.
- ``DefConf`` (cadena) - archivo de configuración predeterminado;
- ``DefDB`` (cadena) - ruta de base de datos predeterminada;
- ``logTS`` - si está definido, se agrega una marca de tiempo en los mensajes de registro;
- ``debug`` - si está definido, se imprimen mensajes de registro de depuración;
- ``TLSVersion`` ('tlsv1.2-1.3') - versiones TLS admitidas para DoT y la API REST;
Parámetros de optimización:
- ``DNSPktMax`` (valor numérico, 100 - 65535) - tamaño máximo de paquete. Valores recomendados:
- 16384 - tamaño mínimo de transferencia de zona;
- 65535 - cantidad mínima de paquetes DNS;
- ``Compression`` (valor numérico, 0 - 9) - nivel de compresión (0 - sin compresión, 9 - compresión máxima). La caché AXFR y las tablas en disco almacenan datos comprimidos;
- ``ZoneRefTime`` (valor numérico, en milisegundos) - define el intervalo de comprobación de actualización de zona;
- ``TCPTimeout`` (valor numérico, en milisegundos) - define el tiempo de espera de sesión TCP;
- ``HotCacheTime`` (valor numérico, en segundos) - tiempo de caché activa para IOCs, Rules, Packets. Las zonas en vivo se almacenan en una caché activa;
- ``HotCacheTimeIXFR`` (valor numérico, en segundos) - tiempo de caché activa para IOCs IXFR en una caché activa. De forma predeterminada, los indicadores IXFR se almacenan en caché durante un minuto (incluso si se establece en 0) porque el serial actual siempre se redondea al minuto anterior;
- ``RATE_LIMIT_WINDOW`` (valor numérico, en milisegundos, predeterminado 10000) - duración de la ventana de límite de tasa por IP;
- ``MAX_REQUESTS_PER_WINDOW`` (valor numérico, predeterminado 1) - máximo de solicitudes DNS por IP por ventana de límite de tasa;
- ``ShellMaxRespSize`` (valor numérico, predeterminado 2 GiB) - tamaño máximo de respuesta para fuentes de comandos shell;
- ``SourcePullTimeout`` (valor numérico, en milisegundos, predeterminado 300000) - tiempo de espera para descargas de fuentes (5 minutos);
## Cómo se actualizan las cachés AXFR (completa) e IXFR (incremental)
- La caché AXFR siempre contiene zonas preconstruidas sin registros SOA/NS/TSIG. Preconstruidas significa que todos los registros están divididos en paquetes y las etiquetas se acortaron/comprimieron.
- Si un servidor recibe una solicitud AXFR, recupera los paquetes de la caché AXFR, agrega registros SOA/NS y TSIG si es necesario.
- La actualización de zonas AXFR debe considerarse como un procedimiento de limpieza que debe realizarse periódicamente. Solo para asegurarse de que no haya desincronización entre las fuentes y la caché.
- Para zonas grandes, las actualizaciones AXFR deben programarse con poca frecuencia para minimizar el impacto en el rendimiento del servidor y la cantidad de datos transferidos a todos los clientes.
- Todos los cambios, si es posible, deben realizarse mediante actualizaciones incrementales de zona. En ese caso, la caché AXFR se reconstruirá solo si una zona se actualizó.
- [TODO] Debido a una optimización, solo se reconstruirá el último paquete para nuevos IOCs y los paquetes relevantes y concordantes para IOCs expirados.
- La caché IXFR contiene solo IOCs y fechas de expiración. [TODO] e ID de paquetes (para permitir reconstruir la zona rápidamente).
- El registro RPZ contiene el Serial actual de la zona y Serial_IXFR. Serial_IXFR sirve como el serial mínimo de zona incremental disponible para una transferencia de zona incremental.
- La caché IXFR se vacía después de una actualización completa de zona (AXFR). Serial_IXFR = Serial. Los clientes recibirán la actualización completa de la zona en cualquier caso, por eso es importante que la transferencia de zona AXFR sea poco frecuente.
- Cuando se actualiza la caché IXFR, la caché AXFR debe reconstruirse.
- Si una zona no admite actualizaciones IXFR -> no se guarda en la tabla IXFR.
- Las zonas en vivo no se almacenan en caché en las cachés AXFR, IXFR, pero las fuentes (IOCs) pueden almacenarse en caché en la caché activa.
## Caché activa
Todos los IOCs, Rules, Packets, incluidas las RPZ en vivo, se almacenan en la caché activa. Los parámetros precompilados ``HotCacheTime``, ``HotCacheTimeIXFR`` definen el tiempo de almacenamiento.
## Cómo probar ioc2rpz™ (o fuentes RPZ de muestra y gratuitas alojadas por ioc2rpz™)
### Aviso legal
El autor no asume responsabilidad ni obligación por errores u omisiones en el contenido de estas fuentes RPZ. Las fuentes se proporcionan "tal cual" sin garantías de integridad, precisión, utilidad o puntualidad para demostrar únicamente la tecnología ioc2rpz™. La distribución del servicio de fuentes RPZ puede interrumpirse o detenerse sin previo aviso. El autor no es responsable de daños directos o indirectos causados por el uso de este servicio.
### Fuentes RPZ
Puede probar la tecnología ioc2rpz™ si se registra en la [comunidad ioc2rpz™](https://ioc2rpz.net) con las siguientes fuentes:
- [notracking](https://github.com/notracking/hosts-blocklists);
- [Phishtank](https://www.phishtank.com/).
### Configuración de ejemplo de bind```
options {
#This is just options for RPZs. Add other options as required
recursion yes;
response-policy {
####FQDN only zones
####Mixed zones
zone "phishtank.ioc2rpz" policy nxdomain;
####IP only zones
} qname-wait-recurse no break-dnssec yes;
};
key "ioc2rpz-YOUR-UNIQUE-KEY-NAME"{
algorithm hmac-sha256; secret "ioc2rpz-YOUR-UNIQUE-KEY";
};
zone "phishtank.ioc2rpz" {
type slave;
file "/var/cache/bind/phishtank.ioc2rpz";
masters {94.130.30.123 key "ioc2rpz-YOUR-UNIQUE-KEY-NAME";};
};
rpzMaster("94.130.30.123", "phishtank.ioc2rpz", {defpol=Policy.NXDOMAIN, tsigname="ioc2rpz-YOUR-UNIQUE-KEY-NAME", tsigalgo="hmac-sha256", tsigsecret="ioc2rpz-YOUR-UNIQUE-KEY"})
### Ejemplo de configuración de Infoblox (archivo de importación)```
header-responsepolicyzone,fqdn*,zone_format*,rpz_policy,substitute_name,view,zone_type,external_primaries,grid_secondaries,priority
responsepolicyzone,phishtank.ioc2rpz,FORWARD,Nxdomain,,default,responsepolicy,srv_1/94.130.30.123/FALSE/FALSE/TRUE/ioc2rpz-YOUR-UNIQUE-KEY-NAME/ioc2rpz-YOUR-UNIQUE-KEY/HMAC-SHA256,infoblox.localdomain/False/False/False,0
dig @94.130.30.123 -y hmac-sha256:ioc2rpz-YOUR-UNIQUE-KEY-NAME:ioc2rpz-YOUR-UNIQUE-KEY phishtank.ioc2rpz SOA
kdig @94.130.30.123 -y hmac-sha256:ioc2rpz-YOUR-UNIQUE-KEY-NAME:ioc2rpz-YOUR-UNIQUE-KEY phishtank.ioc2rpz SOA +tls
## Algunas fuentes gratuitas de inteligencia de amenazas
- [Netlab](http://data.netlab.360.com)
- [Lista awesome-threat-intelligence en GitHub](https://github.com/hslatman/awesome-threat-intelligence)
Puedes encontrar otras fuentes de IOC en la página wiki: https://github.com/Homas/ioc2rpz/wiki/IOC-Sources.
## Documentación adicional
Para documentación detallada, consulta el directorio `docs/`:
- [docs/architecture.md](https://github.com/homas/ioc2rpz/blob/master/docs/architecture.md) — Árbol de supervisión OTP, responsabilidades de los módulos, tablas ETS, flujo de datos
- [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/master/docs/configuration.md) — Referencia completa de configuración con todos los tipos de tuplas y opciones
- [docs/deployment.md](https://github.com/homas/ioc2rpz/blob/master/docs/deployment.md) — Instrucciones de compilación, despliegue con Docker, certificados, monitorización, resolución de problemas
- [docs/protocols.md](https://github.com/homas/ioc2rpz/blob/master/docs/protocols.md) — Soporte de protocolos (UDP/TCP/DoT/DoH), API REST, TSIG, limitación de velocidad, DNS NOTIFY
## Referencias
- [RFC-6895 Domain Name System (DNS) IANA Considerations](https://tools.ietf.org/html/rfc6895)
- [RFC-1035 Domain Names - Implementation and Specification](https://tools.ietf.org/html/rfc1035)
- [RFC-1995 Incremental Zone Transfer in DNS](https://tools.ietf.org/html/rfc1995)
- [DNS Response Policy Zones (RPZ)](https://tools.ietf.org/html/draft-ietf-dnsop-dns-rpz-00) + [vixie](https://tools.ietf.org/html/draft-vixie-dns-rpz-02)
- [RFC-2845 Secret Key Transaction Authentication for DNS (TSIG)](https://tools.ietf.org/html/rfc2845)
- [RFC-2104 HMAC: Keyed-Hashing for Message Authentication](https://tools.ietf.org/html/rfc2104)
- [RFC-4635 HMAC SHA TSIG Algorithm Identifiers](https://tools.ietf.org/html/rfc4635)
- [RFC-5966 DNS Transport over TCP - Implementation Requirements](https://tools.ietf.org/html/rfc5966)
- [RFC-1996 A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY)](https://tools.ietf.org/html/rfc1996)
- [Extension Mechanisms for DNS (EDNS(0))](https://tools.ietf.org/html/rfc6891) + [EDNS Option Codes](https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11)
- [RFC-7873 Domain Name System (DNS) Cookies](https://tools.ietf.org/html/rfc7873)
- [RFC-7858 Specification for DNS over Transport Layer Security (TLS)](https://tools.ietf.org/html/rfc7858)
- [Cowboy Web Server](https://ninenines.eu)
- [Rebar3](https://www.rebar3.org)
<details>
<summary><strong>Referencia de códigos de eventos CEF</strong> (haz clic para expandir)</summary>
| Código | Gravedad | Evento | Descripción |
|------|----------|-------|-------------|
| 101 | Baja | Paquete DNS malformado | Se recibió un paquete DNS malformado |
| 102 | Baja | Solicitud DNS incorrecta | Solicitud DNS no analizable |
| 103 | Media | Rechazada | Solicitud DNS rechazada |
| 104 | Media | Clave TSIG no encontrada | La solicitud usó una clave TSIG desconocida |
| 105 | Media | MAC TSIG incorrecta | Falló la verificación de la firma TSIG |
| 106 | Media | Hora TSIG incorrecta | Marca de tiempo TSIG fuera de rango |
| 107 | Media | Otro error TSIG | Error TSIG sin clasificar |
| 108 | Media | Posición TSIG incorrecta | Registro TSIG en posición inesperada |
| 109 | Baja | Respuesta DNS recibida | Se recibió una respuesta DNS inesperada |
| 120 | Media | RPZ no encontrado | La zona RPZ solicitada no existe |
| 121 | Baja | RPZ no listo | La zona RPZ todavía se está cargando/actualizando |
| 130 | Baja | Error de transferencia RPZ | Error durante la transferencia de zona (solo transferencia RPZ) |
| 131 | Baja | Transferencia RPZ cerrada | El remoto cerró la conexión durante la transferencia (solo transferencia RPZ) |
| 140 | Alta | Falló la autenticación básica REST | Falló la autenticación básica de la API REST |
| 141 | Alta | Falló la autenticación REST | Falló la autorización de la API REST |
| 145 | Alta | Gestión REST denegada | Solicitud de gestión REST denegada por ACL |
| 146 | Alta | Falló la solicitud de gestión | Falló el procesamiento de la solicitud de gestión |
| 147 | Alta | Solicitud no soportada | Endpoint desconocido de la API REST |
| 148 | Alta | Zona no encontrada | La API REST hizo referencia a una zona inexistente |
| 150 | Baja | Comando shell ejecutado | Comando de origen shell ejecutado (información) |
| 151 | Alta | Comando shell rechazado | Comando de origen shell rechazado (seguridad) |
| 201 | Baja | Transferencia RPZ exitosa | Transferencia de zona completada |
| 202 | Baja | Consulta DNS | Consulta DNS estándar procesada |
| 221 | Baja | Notificación DNS | Notificación enviada al servidor secundario |
| 222 | Media | Error de notificación DNS | Falló el envío de la notificación |
| 230 | Alta | Solicitud de gestión | Operación de gestión ejecutada |
| 301 | Alta | Solicitud de gestión denegada | Comando de gestión DNS denegado |
| 429 | Alta | Demasiadas solicitudes | Límite de velocidad superado |
| 501 | Alta | Posible DDoS | Patrón CVE-2004-0789 detectado |
</details>
# ¿Quieres apoyar el proyecto?
Puedes apoyar el proyecto ioc2rpz™ y la comunidad ioc2rpz™ (https://ioc2rpz.net) a través de [GitHub Sponsor](https://github.com/sponsors/Homas) (pagos recurrentes). Para hacer una donación única puedes usar [PayPal](https://paypal.me/ioc2rpz) o Zelle (nuestro correo electrónico: zelle [at] ioc2rpz [.] net).
# Patrocinadores
Un agradecimiento a **craSH** y **rrbone**, que apoyan mis proyectos en [GitHub Sponsor](https://github.com/sponsors/Homas).
# Contáctanos
Puedes contactarnos por correo electrónico: feedback(at)ioc2rpz[.]net o en [Telegram](https://t.me/ioc2rpz).
# Licencia
Copyright 2017 - 2025 Vadim Pavlov ioc2rpz[at]gmail[.]com
Licenciado bajo la Licencia Apache, Versión 2.0 (la "Licencia"); no puede usar este archivo excepto en cumplimiento de la Licencia.
Puede obtener una copia de la Licencia en
http://www.apache.org/licenses/LICENSE-2.0
A menos que lo exija la ley aplicable o se acuerde por escrito, el software distribuido bajo la Licencia se distribuye "TAL CUAL", SIN GARANTÍAS NI CONDICIONES DE NINGÚN TIPO, ya sean expresas o implícitas. Vea la Licencia para conocer el lenguaje específico que rige los permisos y las limitaciones bajo la Licencia.