ioc2rpz™:DNS 安全解决方案 —— ioc2rpz™ 是一款强大的 DNS 服务器,可将威胁指标转换为可操作的响应策略区(RPZ)订阅源。它自动化更新流程,确保您的网络免受最新威胁(包括恶意域名和 IP 地址)的侵害。通过将 IOC 订阅源转换为 RPZ,ioc2rpz™ 成为威胁情报与 DNS 安全之间的关键纽带,兼容 ISC Bind 或 PowerDNS 等支持 RPZ 的 DNS 服务器。
DNS 是互联网的控制平面。通常 DNS 用于正当目的,但:

ISC Bind 实际上是域名服务器的事实标准。随着响应策略区在 ISC BIND 9.8 中的引入,在 DNS 层监控和遏制恶意软件已成为一项简单的任务。PowerDNS recursor 4.0.0 及更高版本支持 RPZ。Knot DNS 也部分支持 RPZ。
与传统网络防护解决方案相比,DNS 服务器可以处理数百万个指标而不会影响性能,但当时没有自动化且高效的方法来维护主 DNS 服务器上的响应策略区。
通常,失陷指标(IOC)以纯文本形式分发,但格式各不相同,只有少数 IOC 提供商通过 RPZ 提供这些指标。
ioc2rpz™ 是一款定制 DNS 服务器,可自动将来自各种来源的指标(例如恶意 FQDN、IP)转换为 RPZ 订阅源,并自动维护/更新它们。这些订阅源可分发到任何支持 RPZ 的开源和/或商业 DNS 服务器,例如 ISC Bind、PowerDNS。您可以在路由器、台式机、服务器甚至 Raspberry Pi 上运行带 RPZ 过滤功能的自有 DNS 服务器。唯一的限制是系统内存。
使用 ioc2rpz™,您可以定义自己的订阅源、操作,并防止不需要的通信。
ioc2rpz™ 将 IOC 订阅源转换为响应策略区(RPZ)。您可以混合订阅源以生成单个 RPZ 或多个 RPZ。受信任的域名和 IP 可被加入白名单。ioc2rpz™ 支持指标过期并相应地重建区域。
当前版本支持:本地文件、通过 http/https/ftp 获取的文件/请求,以及用于访问其他资源类型的 shell 脚本。只要您能编写 REGEX 来提取指标,并且指标以换行符和/或回车符(/n、/r、/r/n)分隔,您就可以使用任何文件格式。
ioc2rpz 基于 Erlang/OTP 构建,带有监督树,可确保容错和自动恢复。有关完整详情,请参阅 docs/architecture.md。``` 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]
关键模块:
| 模块 | 职责 |
|--------|---------------|
| `ioc2rpz.erl` | TCP/TLS DNS 工作进程 — 接受、解析、验证、响应 |
| `ioc2rpz_udp.erl` | UDP DNS 监听器 — SOA 查询 |
| `ioc2rpz_conn.erl` | IOC 源获取(HTTP/HTTPS/文件/Shell) |
| `ioc2rpz_db.erl` | 基于 ETS 的区域和包缓存 |
| `ioc2rpz_sup.erl` | 主监督者、配置加载、区域调度 |
| `ioc2rpz_rest.erl` | REST 管理 API(Cowboy) |
| `ioc2rpz_doh.erl` | DNS-over-HTTPS 处理器 |
## 如何使用 ioc2rpz™
你可以将 ioc2rpz™ 与任何支持响应策略区域(Response Policy Zones)的 DNS 服务器配合使用,例如较新版本的 ISC BIND、PowerDNS,以及任何基于这些产品的商业 DNS 服务器(例如 Infoblox、Blue Cat、Efficient IP)。cfg 文件夹中提供了一份示例 BIND 配置文件(named.conf)。
<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>
## ioc2rpz™ Web 界面
[ioc2rpz.gui](https://github.com/Homas/ioc2rpz.gui) 是一个独立项目开发的 Web 管理界面。运行 ioc2rpz™ 并不需要它。
## 协议支持
ioc2rpz™ 监听多种传输协议。所有传输共享相同的查询处理流程:限速、TSIG 验证、区域查询和响应生成。完整的协议文档请参阅 [docs/protocols.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/protocols.md)。
### 端口摘要
| 端口 | 协议 | 服务 | 条件 |
|------|----------|---------|-----------|
| 53 | UDP | DNS 查询(仅 SOA) | 始终 |
| 53 | TCP | DNS 查询、AXFR/IXFR 区域传输、管理 | 始终 |
| 853 | TCP+TLS | DoT — 与 TCP 相同,但已加密 | 需要 `cert` 配置 |
| 443/8443 | TCP+TLS | DoH(`/dns-query`)和 REST API | 需要 `cert` 配置 |
### UDP(端口 53)
UDP 用于轻量级 DNS 查询,主要是 SOA 查找。响应超过 512 字节(或 EDNS0 通告的缓冲区大小)时,会按照 RFC 1035 §4.2.1 设置 TC(截断)位,提示客户端通过 TCP 重试。UDP 不支持管理命令。```bash
dig @127.0.0.1 zone.ioc2rpz SOA -y hmac-sha256:keyname:base64key
TCP 负责处理区域传输(AXFR/IXFR)、SOA 查询和管理命令。一个由 5 个预派生的 accept 工作进程组成的池负责处理传入连接。```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 over TLS / DoT (端口 853)
ioc2rpz™ 支持通过 DoT 进行 RPZ 分发。当配置中存在 `cert` 记录时,TLS 监听器会自动在 853 端口启动。DoT 支持与 TCP 相同的操作(AXFR、IXFR、SOA、管理)。
- 支持的 TLS 版本:1.2 和 1.3(`?TLSVersion = 'tlsv1.2-1.3'`)
- 支持连接复用(依据 RFC 7858 §3.4,每个 TLS 会话可执行多个查询,空闲超时 30 秒)
- 不支持 TLS PIN
- DNS NOTIFY 消息以未加密方式发送(纯 UDP)
- 当磁盘上的文件被替换时,证书会自动刷新(由于 Erlang SSL 缓存,最多约 2 分钟延迟)
有关证书设置,请参阅 [证书设置](#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 通过 /dns-query 端点提供基于 HTTPS 的 DNS 解析。支持的方法:
?dns= 查询参数中携带 base64url 编码的 DNS 消息Content-Type: application/dns-message(最大 4096 字节;更大的负载将收到 HTTP 413)响应使用 Content-Type: application/dns-message。DoH 携带完整的 DNS 线格式消息,并通过与 Do53/DoT 相同的路径进行处理,因此它继承了相同的 TSIG 认证;区域传输(AXFR/IXFR)仍然仅限 TCP,不通过 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
### 速率限制
DNS 查询使用智能(混合)键进行速率限制,这样合法的多区域客户端不会受到惩罚,同时可阻止通过改变查询名称进行的滥用:
- **已配置区域 + 支持的 QTYPE**(`SOA`/`AXFR`/`IXFR`,类别 `IN`)和**可识别的管理命令**(类别 `CHAOS`/`TXT`)按 `{client_IP, query_name, query_type}` 进行跟踪——因此,从同一 IP 轮询/传输多个区域(例如 `rpz1`、`rpz2`、`rpz3`)并执行管理操作的辅助服务器,会按区域+类型独立计数。
- **其他所有情况**(未知/未配置的区域、不支持的查询类型、错误的类别或无法识别的管理名称)按 `{client_IP}` 聚合,因此攻击者无法通过改变查询名称来绕过限制。
当超过限制时,服务器返回 DNS `REFUSED` 响应。
| 参数 | 默认值 | 宏 |
|-----------|---------|-------|
| 窗口 | 10 秒 | `?RATE_LIMIT_WINDOW`(10000 ms) |
| 每窗口最大请求数(细粒度:已知区域+类型 / 管理) | 1 | `?MAX_REQUESTS_PER_WINDOW` |
| 每窗口最大请求数(聚合:未知区域 / 不支持的查询类型) | 1 | `?MAX_UNKNOWN_REQUESTS_PER_WINDOW` |
速率限制适用于所有 DNS 查询传输(UDP、TCP、TLS、DoH)。窗口和阈值可通过 `include/ioc2rpz.hrl` 中的宏进行配置。
### DNS NOTIFY
区域更新(AXFR 或 IXFR)后,ioc2rpz™ 通过 UDP 向 RPZ `NotifyList` 中配置的 IP 地址发送 DNS NOTIFY 消息([RFC 1996](https://tools.ietf.org/html/rfc1996))。这会提示辅助 DNS 服务器检查区域 SOA 序列号,如果序列号已更改,则启动传输。```erlang
%% NotifyList in RPZ config — last field before whitelists
{rpz, {"zone.ioc2rpz", ..., ["source1"], ["10.0.0.1", "10.0.0.2"], []}}.
部署该服务的最简单方式是使用 Docker Hub 上的 docker 容器。你可以在 ioc2rpz™ wiki 中找到 Docker 部署操作指南。
ioc2rpz™ 在 Docker Hub 上可用。直接搜索 ioc2rpz™ 即可。 先决条件:
where /home/ioc2rpz/cfg, /home/ioc2rpz/db directories on a host system.
You can pass a custom configuration file name via``-e`` parameter. E.g. ``-e CONF=./cfg/ioc2rpz2.conf``
## Environment Variables
The following environment variables are used by ioc2rpz™, primarily relevant for Docker deployments. They are defined in `config/sys.config.src` and `config/vm.args`.
| Variable | Default | Description |
|----------|---------|-------------|
| `IPv4` | — | IPv4 bind address |
| `IPv6` | — | IPv6 bind address |
| `CONF` | — | Path to configuration file (e.g. `./cfg/ioc2rpz.conf`) |
| `DB` | `/opt/ioc2rpz/db` | Database directory for ETS persistence |
| `CD` | `/opt/ioc2rpz` | Working directory |
| `NODE_NAME` | `ioc2rpz` | Erlang node short name |
| `IO2Cookie` | `ioc2rpz` | Erlang distributed cookie |
Pass environment variables to Docker with `-e`:
</translation>其中 /home/ioc2rpz/cfg、/home/ioc2rpz/db 目录位于宿主机系统上。
您可以通过``-e``参数传递自定义配置文件名称。例如:``-e CONF=./cfg/ioc2rpz2.conf``
## 环境变量
以下环境变量由 ioc2rpz™ 使用,主要与 Docker 部署相关。它们定义在 `config/sys.config.src` 和 `config/vm.args` 中。
| Variable | Default | Description |
|----------|---------|-------------|
| `IPv4` | — | IPv4 绑定地址 |
| `IPv6` | — | IPv6 绑定地址 |
| `CONF` | — | 配置文件路径(例如 `./cfg/ioc2rpz.conf`) |
| `DB` | `/opt/ioc2rpz/db` | 用于 ETS 持久化的数据库目录 |
| `CD` | `/opt/ioc2rpz` | 工作目录 |
| `NODE_NAME` | `ioc2rpz` | Erlang 节点短名称 |
| `IO2Cookie` | `ioc2rpz` | Erlang 分布式 cookie |
使用 `-e` 将环境变量传递给 Docker:```bash
docker run -d --name ioc2rpz -e CONF=./cfg/custom.conf -e NODE_NAME=mynode ...
您可以使用 docker compose 部署 ioc2rpz™ 和 ioc2rpz.gui。规范的 docker-compose.yml 文件可以在 ioc2rpz.dc 仓库中找到。
下面是一个示例 docker-compose.yml,展示了带有 web UI 和 Let's Encrypt certbot sidecar 容器的 ioc2rpz:```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:
更新你的 `ioc2rpz.conf` 以引用挂载的证书路径:```erlang
{cert, {"ssl/live/ns1.rpz-proxy.com/fullchain.pem", "ssl/live/ns1.rpz-proxy.com/privkey.pem", ""}}.
你可以在 AWS 上运行 ioc2rpz™ 和 ioc2rpz.gui。对于相对较小的部署(数十万条指标),即使是免费套餐也足够。 下面的视频展示了如何使用 ECS 在 AWS 上设置 ioc2rpz™ 和 ioc2rpz.gui。
DoT(端口 853)、REST API(端口 8443)和 DoH 都需要 TLS 证书。所有 TLS 服务使用相同的证书。完整详情请参阅 docs/deployment.md。
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 (生产环境)```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"
证书也会在配置重载(`ioc2rpz-reload-cfg`)期间被显式重新加载:当 ioc2rpz 检测到证书文件发生变化时,它会立即使用新证书重启 TLS 监听器,而不是等待约 2 分钟的 Erlang SSL 缓存。在续期钩子中添加一个 `ioc2rpz-reload-cfg` 步骤,即可在无需停机的情况下应用新证书。
### Docker 卷挂载
在 Docker 中运行时,请从宿主机挂载证书目录:```bash
docker run -d --name ioc2rpz \
--mount type=bind,source=/etc/letsencrypt/live/ns1.rpz-proxy.com,target=/opt/ioc2rpz/ssl,readonly \
...
在 ioc2rpz.conf 中的引用:```erlang
{cert, {"ssl/fullchain.pem", "ssl/privkey.pem", ""}}.
Erlang 会在约 2 分钟内自动拾取替换后的证书文件。在配置重载(`ioc2rpz-reload-cfg`)期间,证书也会被显式重新加载。切勿让证书过期——请在到期前续期,以确保服务不中断。
## 从源码构建
> **注意:** 从源码构建适用于**开发和测试**。对于生产环境,**Docker 是推荐的部署方式**——参见 [Docker Compose](#docker-compose) 与 [ioc2rpz.dc](https://github.com/Homas/ioc2rpz.dc) 仓库。
### 前置要求
- **Erlang/OTP 24 或更新版本**(用 `erl -version` 检查)以及与之匹配的 [rebar3](https://www.rebar3.org)。
- C 工具链(用于构建依赖项)和 `git`。
### 构建与运行```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
默认情况下,ioc2rpz™ 从 ./cfg/ioc2rpz.conf 读取其配置,监听所有网络接口,并将其数据库备份写入 ./db。编译时默认值(端口、路径、计时器)位于 include/ioc2rpz.hrl。
一个包含一个 TSIG 密钥、一个文件源和一个 RPZ 区域的最小化 cfg/ioc2rpz.conf:```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"],[],[]}}.
有关权威的逐字段参考(source/RPZ 元组布局、SOA 计时器、密钥组、证书等),请参阅 [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/configuration.md)。要启用 DoT(端口 853)、DoH 和 HTTPS REST API,请添加 `{cert,{...}}` 条目 — 参见 [证书设置](#certificate-setup)。
### 开发 Shell(仅限开发/测试)
以下命令仅供本地开发和测试使用 — 不可用于生产环境:```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
在 rebar3 shell 中,你可以直接操作运行中的系统,例如:```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).
## ioc2rpz™ 管理
### 通过 DNS
ioc2rpz™ 支持通过 DNS/TCP 或 DoT 进行管理。建议使用 DoT 或基于 DNS/TCP 的 REST API。当前版本的 ioc2rpz™ 不支持独立的管理 IP/接口。无论如何,强烈建议创建一个专用的 TSIG 密钥(或多个密钥),仅用于管理。您可以关闭通过 DNS 的管理。
支持的操作:
- ioc2rpz™ 当前状态。请求 ``ioc2rpz-status``,类别 ``CHAOS``,记录 ``TXT``。例如:```
dig +tcp -y dnsmkey_1:ayVnL+h2QKMszRVohrngagcEuIpN3RkecXKdwSa5WsHD5N4Y5R3NUMGM W8sIGv36gPkAtWtgarqKzN9tmHqEnA== @127.0.0.1 ioc2rpz-status TXT -c CHAOS
ioc2rpz-reload-cfg,RR 类别 CHAOS,RR 类型 TXTioc2rpz-update-tkeys,RR 类别 CHAOS,RR 类型 TXTioc2rpz-update-all-rpz,RR 类别 CHAOS,RR 类型 TXTzone_name,RR 类别 CHAOS,RR 类型 TXT。例如,全面刷新 dga.ioc2rpz 可以通过以下方式调用:```
dig +tcp -y dnsmkey_1:ayVnL+h2QKMszRVohrngagcEuIpN3RkecXKdwSa5WsHD5N4Y5R3NUMGM W8sIGv36gPkAtWtgarqKzN9tmHqEnA== @127.0.0.1 dga.ioc2rpz TXT -c CHAOS- 停止 ioc2rpz™。RR Name ``ioc2rpz-terminate``,RR Class ``CHAOS``,RR Type ``TXT``
- 请求一个示例区域。RR Name ``sample-zone.ioc2rpz``,RR Class ``IN``,RR Type ``AXFR``
### 通过 REST
REST API(端口 8443/tcp)是首选的管理接口。出于安全原因,所有管理流量都必须加密,如果没有 SSL 证书,REST API 将不会启动。所有端点都接受 GET 和 POST 方法。
使用 HTTP Basic 认证来验证请求。管理 TSIG 密钥用于请求认证。TSIG 密钥名称用作 HTTP 用户名,TSIG 密钥用作密码。对 REST API 的访问由 `srv` 记录中定义的 ACL 限制。
API 版本段支持 `v1` 和 `v1.0`(例如,`/api/v1/stats/serv` 或 `/api/v1.0/stats/serv`)。
REST API 根据 `Accept` 头支持 JSON(默认)和纯文本输出:
- `application/json` — JSON 输出(默认)
- `text/plain` — 纯文本输出```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
文本响应使用以下格式:``` status: ok msg: TSIG keys were updated
#### 路径参数
| 参数 | 描述 |
|-----------|-------------|
| `:rpz_name` | RPZ 区域名称(例如 `dga.ioc2rpz`) |
| `:source_name` | IOC 源名称(例如 `sample_fqdn`) |
| `:ioc` | 要查询的指标(FQDN 或 IP,例如 `baddomain.com`) |
#### 统计端点
`/api/v1/stats/serv` — 服务器统计信息(节点名称、规则总数、内存使用情况)```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 — 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
}
]
}
status 字段(ready、updating、forceAXFR、notready)指示所报告的计数/序号是否为当前值;当为 updating 或 forceAXFR 时,它们反映的是上一次完成的更新。计数和序号在配置重载后会被保留,而不是重置为零。
/api/v1/stats/source — 来源统计```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 — 重新加载配置文件```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/mgmt/reload_cfg
成功:`{"status":"ok","msg":"Configuration reloaded"}`
错误(HTTP 520):`{"status":"error","msg":"Configuration reload error"}`
`/api/v1/mgmt/update_tkeys` — 从配置重新加载 TSIG 密钥```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/mgmt/update_tkeys
成功:{"status":"ok","msg":"TSIG keys were updated"}
错误(HTTP 520):{"status":"error","msg":"TSIG keys update error"}
/api/v1/mgmt/terminate — 优雅关闭服务器```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/mgmt/terminate
成功:`{"status":"ok","msg":"Terminating"}`
#### 区域更新端点
`/api/v1/update/all_rpz` — 强制完全刷新所有 RPZ 区域```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/update/all_rpz
成功:{"status":"ok","msg":"All RPZ zones will be updated"}
/api/v1/update/:rpz_name — 强制完整刷新指定区域```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/update/dga.ioc2rpz
成功: `{"status":"ok","msg":"RPZ dga.ioc2rpz will be updated"}`
错误(HTTP 520): `{"status":"error","msg":"RPZ dga.ioc2rpz not found"}`
#### 缓存管理端点
`/api/v1/cache/sources/clear/all` — 从热缓存中移除所有源```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/cache/sources/clear/all
成功:{"status":"ok","msg":"All sources were removed from the hotcache"}
/api/v1/cache/sources/clear/:source_name — 从热缓存中移除特定来源```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/cache/sources/clear/sample_fqdn
Success: `{"status":"ok","msg":"sample_fqdn source was removed from the hot cache"}`
`/api/v1/cache/sources/load/all` — 将所有源重新加载到热缓存中```bash
curl -u "keyname:base64key==" -k https://127.0.0.1:8443/api/v1/cache/sources/load/all
成功: {"status":"ok","msg":"All sources will loaded to the hot cache"}
/api/v1/feed/:rpz_name — 从 RPZ feed 获取指标
查询参数:?type=fqdn|ip|both(默认:both)```bash
curl -u "keyname:base64key==" -k "https://127.0.0.1:8443/api/v1/feed/malware.ioc2rpz?type=fqdn"
成功:```json
{
"status": "ok",
"rpz": "malware.ioc2rpz",
"iocs": ["baddomain.com", "evil.example.org"]
}
错误(HTTP 520):{"status":"error","msg":"RPZ malware.ioc2rpz not found"}
/api/v1/ioc/:ioc — 检查某个 IOC 是否被任何 RPZ 源屏蔽
查询参数:?tkey=keyname — 将搜索范围限制为该密钥可访问的区域(可选)```bash
curl -u "keyname:base64key==" -k "https://127.0.0.1:8443/api/v1/ioc/baddomain.com?tkey=dnsproxykey_1"
Success:```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"]
}
]
}
]
}
错误: {"status":"error", "ioc": "nonexistent.com"}
附加的 sources 字段在启用来源跟踪时列出各数据源中贡献的来源名称;当跟踪被禁用或归属未知时,该字段为 null。有关配置和语义,请参阅 IOC 来源归属。
任何无法识别的路径都会返回 HTTP 200,包含以下内容:```json {"status":"error","msg":"Unsupported request"}
## 监控与健康检查
### 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
如果在交互式 shell 中运行,或附加到运行中的节点:```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).
### 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
See docs/deployment.md for full monitoring and log reference.
有关详细的故障排除,请参阅 docs/deployment.md。
配置是一个 Erlang 文件。每个配置选项都是一个 Erlang 项(term),因此配置必须符合 Erlang 语法。服务器在加载/重新加载时会校验配置条目:TSIG 密钥编码、证书文件是否存在、正则表达式编译以及 URL 语法。无效条目会被记录并跳过。完整的配置参考请参阅 docs/configuration.md。
ioc2rpz™ 支持以下配置参数:
srv 记录用于定义服务器默认值。它由以下内容组成:
TrackSources — 服务器级别的源归属全局默认值:off | auto | on(默认 off)。应用于任何未设置自身 track_sources 的数据源。请参阅 IOC 源归属。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}}.
4 字段形式仍然有效,并且默认将 `TrackSources` 设为 `off`。
### **cert** 记录
**cert** 记录用于定义 DNS over TLS、REST API 和 DoH 通信的证书和私钥。有关证书的生成和管理,请参阅 [证书设置](#certificate-setup)。
它由以下部分组成:
- 证书文件的路径;
- 包含 PEM 编码私钥的文件的路径;
- 包含 PEM 编码 CA 证书的文件的路径。
示例 **cert** 记录:```
{cert,{"cfg/cert.pem", "cfg/key.pem", "cfg/cacerts.pem"}}.
include 记录允许将 ioc2rpz™ 配置拆分为多个文件。
示例 include 记录:``` {include,"cfg/tkeys.include.cfg"}.
### **key** 记录
TSIG 密钥用于认证和授权。建议为 ioc2rpz™ 管理和区域传输使用不同的 TSIG 密钥。
**key** 记录由以下部分组成:
- TSIG 密钥名称;
- 算法。支持 ``md5``、``sha256`` 和 ``sha512``;
- 密钥;
- (可选)它所属的密钥组列表。
**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"]}}.
dnssec-keygen 工具可用于生成 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
请参阅 `dnssec-keygen` 文档以了解详情。密钥可以分配给组,以便在 RPZ 区域访问控制中实现基于组的授权。
### **key_group** 记录
密钥组为区域传输和管理提供基于组的授权。不必在 `rpz` 或 `srv` 记录中列出各个密钥,您可以将密钥分配给命名组并引用该组。
将密钥分配到组有两种方式:
1. 通过 `key` 记录的可选第 4 个字段以内联方式指定:`{key, {Name, Alg, Secret, [Groups]}}`
2. 通过 `key_group` 记录显式指定(如下所示)```erlang
{key_group, {GroupName, [KeyName1, KeyName2, ...]}}.
| 字段 | 类型 | 描述 |
|---|---|---|
| GroupName | string | 组名(例如 "customers") |
| Keys | list of strings | 属于该组的 TSIG 密钥名称列表 |
示例 key_group 记录:```erlang {key_group, {"customers", ["dnsproxykey_1", "dnsproxykey_2"]}}. {key_group, {"public", ["dnsproxykey_3"]}}.
在 `rpz` 和 `srv` 记录中,组通过键列表中的 `{groups, ["group1", "group2"]}` 元组进行引用:```erlang
{rpz, {"zone.ioc2rpz", 7202, 3600, 2592000, 7200, "true", "true", "nxdomain",
["dnsproxykey_1", {groups, ["customers", "public"]}],
"fqdn", 86400, 3600, ["source1"], [], []}}.
白名单用于防止可能的错误,并阻止对受信任域名和 IP 地址的拦截。被加入白名单的 IOC 会从响应策略区域中移除。ioc2rpz™ 仅进行精确匹配,因此如果某个被加入白名单的 IP 地址位于被阻止的子网内,它不会拆分或丢弃该网络,反之亦然。白名单是一个文本文件或文本数据源。指标应以换行符(/n、/r 或同时使用 /n/r)分隔。白名单必须包含有效的 FQDN 和/或 IP 地址。ioc2rpz™ 支持无限数量的指标。 whitelists 记录由以下部分组成:
file:;""),将使用默认 REGEX("^([A-Za-z0-9][A-Za-z0-9\-\._]+)[^A-Za-z0-9\-\._]*.*$")。如果不需要 REGEX(源数据已提供所需格式),则使用 none。示例 whitelist 记录:``` {whitelist,{"whitelist_1","file:cfg/whitelist1.txt",none}}.
### **source** 记录
source 是恶意指示器的订阅源。支持 FQDN、IPv4 和 IPv6 地址。source 是一个文本文件或文本数据订阅源。指示器应以换行/回车字符(/n、/r 或 /r/n 两者)分隔。ioc2rpz™ 支持无限数量的指示器。
**source** 记录由以下部分组成:
- source 名称;
- 用于全量源传输(AXFR)的 source 路径。支持 URL(http/https/ftp)、本地文件和脚本。前缀 **file:** 用于本地文件。前缀 **shell:** 用于在主机/容器上执行本地脚本/命令,该脚本/命令应将指示器及可选的过期日期返回至 STDOUT;
- 用于增量源传输(IXFR)的 source 路径。AXFR、IXFR 路径支持关键字以缩短 URL 并提供区域更新时间戳:
- **[:AXFR:]** - 完整的 AXFR 路径。只能在 IXFR 路径中使用;
- **[:FTimestamp:]** - source 上次更新的时间戳 (例如 1507946281)
- **[:ToTimestamp:]** - 当前时间戳;
- 用于提取指示器及其过期时间的 REGEX。第一个匹配项为指示器,第二个匹配项为过期时间。过期时间是可选参数。正则表达式必须包含在双引号中。如果指定空 REGEX(`""`),将使用默认 REGEX(`"^([A-Za-z0-9][A-Za-z0-9\-\._]+)[^A-Za-z0-9\-\._]*.*$"`)。如果不需要 REGEX,则使用 `none`(该 source 已按要求格式提供数据)。
可选参数(必须全部使用或全部不使用):
- UserID(内部使用)。
- 最大 IoC 数量。
- 全量 source 更新,热缓存时间(秒)。
- 增量 source 更新,热缓存时间(秒)。
HTTPS source 下载会验证远程服务器的 TLS 证书。证书无效或自签名的 source 将无法下载。对于自签名证书,请使用带 `curl --insecure` 的 `shell:` source。
**本地文件限制:** 出于安全原因,包含 `..`(父目录遍历)的 `file:` 路径将被拒绝;请使用不含 `..` 的路径(位于工作/数据目录内)。
**Shell 命令限制:** 每个管道段的可执行文件必须是绝对路径(例如 `/usr/bin/curl`)或裸名称的安全文本工具(`sort`、`uniq`、`grep`、`sed`、`awk`、`gawk` 等);破坏性命令和 shell(`rm`、`bash`、`sh` 等)会被阻止,命令替换(`$(...)`、反引号)和输出重定向(`>`、`>>`)会被拒绝。被拒绝的命令不会执行,并通过 CEF 事件 151 记录(已执行命令通过 150 记录)。完整规则集请参阅 [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/configuration.md#shell-command-restrictions)。
如果某个 source 返回的指示器数量低于其先前数量的 50%,则更新会被拒绝并保留之前的数据。这可以防止质量下降的订阅源降低 RPZ 覆盖率。可通过 `?SOURCE_MIN_IOC_RATIO` 宏进行配置。
示例 **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}}.
shell: 源用于扩展 ioc2rpz™ 的连接选项(其原生选项有限)。ioc2rpz™ 容器中包含 dig、grep、awk 和 python。例如,你可以混合不同的 RPZ 源,或从数据库获取数据。
Shell 命令限制: 所有可执行文件必须使用绝对路径(例如 /usr/bin/curl 而非 curl)。破坏性命令(rm、bash、sh 等)会被阻止。命令替换($(...)、反引号)和输出重定向(>、>>)会被拒绝。详见 docs/configuration.md。
本地文件安全: 包含 ..(父目录遍历)的文件路径出于安全考虑会被拒绝。
RPZ 术语定义了一个响应策略区域。
rpz 记录由以下部分组成:
true 或 false。true 表示应缓存 RPZ,false 表示不缓存,实时区域源会被下载,并通过 AXFR 请求生成 RPZ。“实时”区域不支持增量区域传输。如果 RPZ 源未被缓存,它仍会临时存储在热缓存中。如果客户端请求超时,我们下次还能响应。AXFR 时间将用于确定缓存生命周期;true 或 false。定义是否应生成通配符规则;mixed、fqdn、ip。用于优化。TrackSources — 按源(feed)进行来源归属:auto | true | false。存在时,它覆盖服务器全局默认值。省略时(15 字段形式),该源继承服务器默认值(#srv ,除非配置,否则为 )。参见 。多个本地动作可以组合在一个列表中:[{"local_a","127.0.0.1"},{"local_aaaa","fe80::1"},{"local_txt","Blocked"}]
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}}.
### IOC 来源归因
RPZ 订阅源通过合并来自多个来源的指标构建。来源归因让 IOC 查找 API 能够报告**订阅源内部的哪个(些)来源**提供了某条指标——这对于对报告误报进行分类处理很有用。跟踪**默认关闭**,并按订阅源通过可选的服务器级全局默认值进行控制。
**配置**
- 按订阅源:`{rpz,{...}}` 元组的可选尾部(第 16 个)`TrackSources` 元素——`auto | true | false`。
- 服务器全局默认值:`{srv,{...}}` 元组的可选尾部(第 5 个)`TrackSources` 元素——`off | auto | on`(默认 `off`)。
两者都是可选的且向后兼容:现有配置文件(15 字段的 `rpz`、4 字段的 `srv`)会原样加载并按关闭状态运行。
**解析优先级** —— 订阅源的有效状态按以下方式解析:
1. 订阅源的显式 `track_sources` 值(如果已设置);
2. 否则使用服务器全局默认值;
3. 否则使用内置默认值 `off`。
`auto` 表示:仅跟踪**多来源**订阅源。单来源订阅源永远不会被遮蔽——其唯一来源名称会直接返回(无需跟踪开销)。
**API——新增的 `sources` 字段**
`/api/v1/ioc/:ioc` 响应在每个订阅源对象上新增了一个 `sources` 字段。它是**增量式且向后兼容的**:所有现有字段均保持不变,因此忽略该字段的旧客户端仍可正常工作。
- 已跟踪的多来源订阅源:包含贡献来源名称的 JSON 数组。
- 单来源订阅源:包含该唯一来源名称的单元素数组。
- 跟踪已禁用,或归属未知(例如升级前缓存的记录行在其一次性 AXFR 重建之前):`null`(JSON)/ `(disabled)` 或 `(unavailable)`(TXT)。```json
{
"feed": "mixed.ioc2rpz",
"wildcard": "true",
"type": "fqdn",
"rpz_serial": 1709000000,
"ioc_expiration": 0,
"sources": ["abuse-ch", "internal-list"]
}
单一来源 feed:"sources": ["sample_fqdn"]。跟踪已禁用/未知:"sources": null。
Rollout(默认关闭)
升级二进制文件不会改变任何东西 — 没有跟踪、不会重建 zone、API 不变 — 直到设置了 track_sources 值。Feeds 由 GUI 管理;GUI/社区网站将在后续更新中写入这些可选配置值并展示 sources。为缓存 feed 启用跟踪会触发一次性的 AXFR 重建,以填充来源掩码。
局限性
cache = "true"),因为 API 查询读取的是 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>
## 预定义配置值 - include/ioc2rpz.hrl
include/ioc2rpz.hrl 包含预编译参数。
标准参数:
- ``MGMToDNS`` (true/false) - 启用通过 DNS/TCP 进行的管理;
- ``DBStorage`` (ets) - 定义 AXFR 和 IXFR 缓存的数据库存储。当前版本仅支持 ETS;
- ``SaveETS`` (true/false) - 定义是否应将 ETS AXFR/IXFR 表保存到磁盘;
- ``Port`` (数值,1 - 65535) - 定义服务运行的 DNS 端口;
- ``PortTLS`` (数值,1 - 65535) - 定义服务运行的 DoT 端口;
- ``PortREST`` (数值,1 - 65535) - 定义服务运行的 HTTPs 端口;
- ``TTL`` (数值,以秒为单位) - DNS 记录/RPZ 规则的默认 TTL。
- ``DefConf`` (字符串) - 默认配置文件;
- ``DefDB`` (字符串) - 默认数据库路径;
- ``logTS`` - 如果定义,日志消息中会添加时间戳;
- ``debug`` - 如果定义,将打印调试日志消息;
- ``TLSVersion`` ('tlsv1.2-1.3') - DoT 和 REST API 支持的 TLS 版本;
优化参数:
- ``DNSPktMax`` (数值,100 - 65535) - 最大数据包大小。推荐值:
- 16384 - 最小区域传输大小;
- 65535 - 最小 DNS 数据包数量;
- ``Compression`` (数值,0 - 9) - 压缩级别(0 - 无压缩,9 - 最高压缩)。AXFR 缓存和磁盘上的表存储压缩数据;
- ``ZoneRefTime`` (数值,以毫秒为单位) - 定义区域刷新检查间隔;
- ``TCPTimeout`` (数值,以毫秒为单位) - 定义 TCP 会话超时;
- ``HotCacheTime`` (数值,以秒为单位) - IOC、规则、数据包的热缓存时间。实时区域存储在热缓存中;
- ``HotCacheTimeIXFR`` (数值,以秒为单位) - 热缓存中 IXFR IOC 的热缓存时间。默认情况下,IXFR 指示器缓存一分钟(即使设置为 0),因为当前序列号总是四舍五入到上一分钟;
- ``RATE_LIMIT_WINDOW`` (数值,以毫秒为单位,默认 10000) - 每个 IP 的速率限制窗口持续时间;
- ``MAX_REQUESTS_PER_WINDOW`` (数值,默认 1) - 每个速率限制窗口内每个 IP 的最大 DNS 请求数;
- ``ShellMaxRespSize`` (数值,默认 2 GiB) - shell 命令源的最大响应大小;
- ``SourcePullTimeout`` (数值,以毫秒为单位,默认 300000) - 源下载超时(5 分钟);
## AXFR(完整)和 IXFR(增量)缓存如何更新
- AXFR 缓存始终包含没有 SOA/NS/TSIG 记录的预构建区域。预构建意味着所有记录都按数据包拆分,标签被缩短/压缩。
- 如果服务器收到 AXFR 请求,它会从 AXFR 缓存中检索数据包,并在需要时添加 SOA/NS 记录和 TSIG。
- AXFR 区域更新应被视为清理过程,应定期进行。只是为了确保源与缓存之间没有失步。
- 对于大区域,AXFR 更新应安排得不频繁,以尽量减少对服务器性能以及向所有客户端传输数据量的影响。
- 如果可能,所有更改都应通过增量区域更新完成。在这种情况下,AXFR 缓存只会在区域被更新时才重建。
- [TODO] 由于优化,对于新的 IOC,只会重建最后一个数据包,对于过期的 IOC,会重建相关且一致的数据包。
- IXFR 缓存仅包含 IOC 和过期日期。[TODO] 以及数据包 ID(以便能够快速重建区域)。
- RPZ 记录包含当前区域 Serial 和 Serial_IXFR。Serial_IXFR 充当可用于增量区域传输的最小增量区域序列号。
- 完整区域更新(AXFR)后,IXFR 缓存会被刷新。Serial_IXFR = Serial。无论何种情况下客户端都会收到完整区域更新,这就是为什么 AXFR 区域传输不宜频繁的原因。
- 当 IXFR 缓存更新时,必须重建 AXFR 缓存。
- 如果区域不支持 IXFR 更新 -> 它不会被保存到 IXFR 表中。
- 实时区域不会缓存在 AXFR、IXFR 缓存中,但源(IOC)可以缓存在热缓存中。
## 热缓存
所有 IOC、规则、数据包包括实时 RPZ 都存储在热缓存中。预编译参数 ``HotCacheTime``、``HotCacheTimeIXFR`` 定义存储时间。
## 如何试用 ioc2rpz™(或由 ioc2rpz™ 托管的示例和免费 RPZ 订阅源)
### 免责声明
作者对这些 RPZ 订阅源内容中的任何错误或遗漏不承担任何责任。这些订阅源仅按"原样"提供,不保证完整性、准确性、有用性或及时性,仅用于演示 ioc2rpz™ 技术。RPZ 订阅源服务的分发可能会在未事先通知的情况下中断或停止。作者不对因使用本服务而造成的任何直接或间接损害承担责任。
### RPZ 订阅源
如果您在 [ioc2rpz™ 社区](https://ioc2rpz.net) 注册,可以使用以下订阅源测试 ioc2rpz™ 技术:
- [notracking](https://github.com/notracking/hosts-blocklists);
- [Phishtank](https://www.phishtank.com/)。
### 示例 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";};
};
| 问题 | 解决方案 |
|---|
| 端口已被占用 | 使用 lsof -i :53 / lsof -i :853 检查。监听套接字上已设置 {reuseaddr, true},以便快速重启。 |
| DoT 不接受连接 | 确认已配置 {cert, ...}。检查 TLS worker:supervisor:count_children(ioc2rpz_tls_sup_v6). 检查 853 端口是否未被防火墙拦截。 |
| 区域传输失败(TSIG 不匹配) | 确认客户端与服务器之间的密钥名称和 secret 一致。在日志中查找 CEF 104/105 事件。 |
| 源下载失败 | 检查网络连接。在日志中查找 Error downloading feed。服务器会以 3 秒的延迟重试 3 次。对于 HTTPS 源,请确保 TLS 证书有效。 |
| 内存使用过高 | 在 Erlang shell 中检查 ETS 表大小。rate_limits 和 rpz_hotcache_table 会被定期清理。可考虑减小 ?HotCacheTime。 |
TrackSourcesoff| 动作 | 配置值 | 描述 |
|---|
| NXDOMAIN | "nxdomain" | 返回 NXDOMAIN(域名不存在) |
| NODATA | "nodata" | 返回空应答(域名存在,无记录) |
| Passthru | "passthru" | 允许查询(豁免规则) |
| Drop | "drop" | 静默丢弃查询 |
| TCP-Only | "tcp-only" | 强制客户端通过 TCP 重试 |
| Block NS | "blockns" | 阻止权威名称服务器 |
| Redirect (domain) | {"redirect_domain","example.com"} | 重定向到指定域名(local_cname 的别名) |
| Redirect (IP) | {"redirect_ip","127.0.0.1"} | 重定向到指定 IP(local_a/local_aaaa 的别名) |
| Local A | {"local_a","127.0.0.1"} | 返回自定义 IPv4 地址 |
| Local AAAA | {"local_aaaa","fe80::1"} | 返回自定义 IPv6 地址 |
| Local CNAME | {"local_cname","www.example.com"} | 返回 CNAME 重定向 |
| Local TXT | {"local_txt","Text Record"} | 返回 TXT 记录 |
rpzMaster("94.130.30.123", "phishtank.ioc2rpz", {defpol=Policy.NXDOMAIN, tsigname="ioc2rpz-YOUR-UNIQUE-KEY-NAME", tsigalgo="hmac-sha256", tsigsecret="ioc2rpz-YOUR-UNIQUE-KEY"})
### 示例 Infoblox 配置(导入文件)```
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
## 一些免费的威胁情报源
- [Netlab](http://data.netlab.360.com)
- [GitHub 上的 awesome-threat-intelligence 列表](https://github.com/hslatman/awesome-threat-intelligence)
您可以在 wiki 页面上找到其他 IOC 源:https://github.com/Homas/ioc2rpz/wiki/IOC-Sources。
## 更多文档
有关详细文档,请参阅 `docs/` 目录:
- [docs/architecture.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/architecture.md) — OTP 监督树、模块职责、ETS 表、数据流
- [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/configuration.md) — 包含所有元组类型和选项的完整配置参考
- [docs/deployment.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/deployment.md) — 构建说明、Docker 部署、证书、监控、故障排除
- [docs/protocols.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/protocols.md) — 协议支持(UDP/TCP/DoT/DoH)、REST API、TSIG、速率限制、DNS NOTIFY
## 参考资料
- [RFC-6895 域名系统(DNS)IANA 注意事项](https://tools.ietf.org/html/rfc6895)
- [RFC-1035 域名 - 实现与规范](https://tools.ietf.org/html/rfc1035)
- [RFC-1995 DNS 中的增量区域传输](https://tools.ietf.org/html/rfc1995)
- [DNS 响应策略区域(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 用于 DNS 的秘密密钥事务认证(TSIG)](https://tools.ietf.org/html/rfc2845)
- [RFC-2104 HMAC:用于消息认证的密钥散列](https://tools.ietf.org/html/rfc2104)
- [RFC-4635 HMAC SHA TSIG 算法标识符](https://tools.ietf.org/html/rfc4635)
- [RFC-5966 基于 TCP 的 DNS 传输 - 实现要求](https://tools.ietf.org/html/rfc5966)
- [RFC-1996 区域变更快速通知机制(DNS NOTIFY)](https://tools.ietf.org/html/rfc1996)
- [DNS 扩展机制(EDNS(0))](https://tools.ietf.org/html/rfc6891) + [EDNS 选项代码](https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11)
- [RFC-7873 域名系统(DNS)Cookie](https://tools.ietf.org/html/rfc7873)
- [RFC-7858 基于传输层安全(TLS)的 DNS 规范](https://tools.ietf.org/html/rfc7858)
- [Cowboy Web 服务器](https://ninenines.eu)
- [Rebar3](https://www.rebar3.org)
<details>
<summary><strong>CEF 事件代码参考</strong>(点击展开)</summary>
| 代码 | 严重级别 | 事件 | 描述 |
|------|----------|-------|-------------|
| 101 | 低 | DNS 数据包错误 | 收到格式错误的 DNS 数据包 |
| 102 | 低 | DNS 请求错误 | 无法解析的 DNS 请求 |
| 103 | 中 | 拒绝 | DNS 请求被拒绝 |
| 104 | 中 | 未找到 TSIG 密钥 | 请求使用了未知的 TSIG 密钥 |
| 105 | 中 | TSIG MAC 错误 | TSIG 签名验证失败 |
| 106 | 中 | TSIG 时间错误 | TSIG 时间戳超出范围 |
| 107 | 中 | 其他 TSIG 错误 | 未分类的 TSIG 错误 |
| 108 | 中 | TSIG 位置错误 | TSIG 记录位于意外位置 |
| 109 | 低 | 收到 DNS 响应 | 收到意外 DNS 响应 |
| 120 | 中 | 未找到 RPZ | 请求的 RPZ 区域不存在 |
| 121 | 低 | RPZ 未就绪 | RPZ 区域仍在加载/更新中 |
| 130 | 低 | RPZ 传输错误 | 区域传输期间出错(仅限 RPZ 传输) |
| 131 | 低 | RPZ 传输已关闭 | 传输期间远程端关闭了连接(仅限 RPZ 传输) |
| 140 | 高 | REST 基本认证失败 | REST API 基本认证失败 |
| 141 | 高 | REST 认证失败 | REST API 授权失败 |
| 145 | 高 | REST 管理被拒绝 | REST 管理请求被 ACL 拒绝 |
| 146 | 高 | 管理请求失败 | 管理请求处理失败 |
| 147 | 高 | 不支持的请求 | 未知的 REST API 端点 |
| 148 | 高 | 未找到区域 | REST API 引用了不存在的区域 |
| 150 | 低 | 已执行 Shell 命令 | 已执行 Shell 源命令(信息) |
| 151 | 高 | Shell 命令被拒绝 | Shell 源命令被拒绝(安全) |
| 201 | 低 | RPZ 传输成功 | 区域传输完成 |
| 202 | 低 | DNS 查询 | 已处理标准 DNS 查询 |
| 221 | 低 | DNS 通知 | 已向辅助服务器发送通知 |
| 222 | 中 | DNS 通知错误 | 发送通知失败 |
| 230 | 高 | 管理请求 | 已执行管理操作 |
| 301 | 高 | 管理请求被拒绝 | DNS 管理命令被拒绝 |
| 429 | 高 | 请求过多 | 超出速率限制 |
| 501 | 高 | 疑似 DDoS | 检测到 CVE-2004-0789 模式 |
</details>
# 您想支持这个项目吗?
您可以通过 [GitHub Sponsor](https://github.com/sponsors/Homas)(定期付款)支持 ioc2rpz™ 项目和 ioc2rpz™ 社区(https://ioc2rpz.net)。如需一次性捐赠,您可以使用 [PayPal](https://paypal.me/ioc2rpz) 或 Zelle(我们的邮箱:zelle [at] ioc2rpz [.] net)。
# 支持者
特别鸣谢在 [GitHub Sponsor](https://github.com/sponsors/Homas) 上支持我项目的 **craSH** 和 **rrbone**。
# 联系我们
您可以通过电子邮件 feedback(at)ioc2rpz[.]net 或通过 [Telegram](https://t.me/ioc2rpz) 联系我们。
# 许可证
版权所有 2017 - 2025 Vadim Pavlov ioc2rpz[at]gmail[.]com
根据 Apache License 2.0 版(以下简称“许可证”)获得许可;除非遵守许可证,否则您不得使用此文件。
您可以在以下位置获取许可证副本:
http://www.apache.org/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则按许可证分发的软件按“原样”分发,不附带任何明示或暗示的保证或条件。请参阅许可证,了解具体的权限和限制。