
ioc2rpz is a place where threat intelligence meets DNS.
ioc2rpz™: DNS 보안 솔루션 - ioc2rpz™는 위협 지표를 실행 가능한 응답 정책 영역(RPZ) 피드로 변환하는 강력한 DNS 서버입니다. 업데이트 프로세스를 자동화하여 악성 도메인 및 IP 주소를 포함한 최신 위협으로부터 네트워크를 보호합니다. IOC 피드를 RPZ로 변환함으로써 ioc2rpz™는 위협 인텔리전스와 DNS 보안 간의 중요한 연결 고리 역할을 하며, ISC Bind 또는 PowerDNS와 같은 RPZ 지원 DNS 서버와 호환됩니다.
DNS는 인터넷의 제어 평면입니다. 일반적으로 DNS는 좋은 목적으로 사용되지만:

ISC Bind는 사실상 네임서버의 표준입니다. ISC BIND 9.8에 응답 정책 영역이 도입되면서 DNS 계층에서 멀웨어를 모니터링하고 차단하는 것이 간단한 작업이 되었습니다. RPZ는 PowerDNS 리커서 4.0.0 이상에서 지원되며, Knot DNS도 부분적으로 RPZ를 지원합니다.
기존 네트워크 보호 솔루션과 비교할 때, DNS 서버는 성능 저하 없이 수백만 개의 지표를 처리할 수 있지만, 주 DNS 서버에서 응답 정책 영역을 유지 관리하는 자동화되고 효율적인 방법은 없었습니다.
일반적으로 침해 지표는 일반 텍스트로 배포되지만 형식이 다르며, IOC 제공업체 중 일부만이 RPZ를 통해 제공합니다.
ioc2rpz™는 다양한 소스의 지표(예: 악성 FQDN, IP)를 RPZ 피드로 자동 변환하고 자동으로 유지/업데이트하는 맞춤형 DNS 서버입니다. 이 피드는 ISC Bind, PowerDNS 등 RPZ를 지원하는 모든 오픈 소스 및/또는 상용 DNS 서버에 배포할 수 있습니다. 라우터, 데스크탑, 서버, 심지어 Raspberry Pi에서도 RPZ 필터링이 가능한 자체 DNS 서버를 실행할 수 있습니다. 시스템 메모리만이 유일한 제한 사항입니다.
ioc2rpz™를 사용하여 자체 피드와 작업을 정의하고 원치 않는 통신을 방지할 수 있습니다.
ioc2rpz™는 IOC 피드를 응답 정책 영역(RPZ)으로 변환합니다. 여러 피드를 혼합하여 단일 RPZ 또는 여러 RPZ를 생성할 수 있습니다. 신뢰할 수 있는 도메인과 IP는 화이트리스트에 추가할 수 있습니다. ioc2rpz™는 지표의 만료를 지원하고 그에 따라 영역을 재구축합니다.
현재 릴리스는 다음을 지원합니다: 로컬 파일, http/https/ftp를 통한 파일/요청, 그리고 다른 리소스 유형에 접근하기 위한 셸 스크립트. 지표를 추출하기 위해 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]
Key modules:
| 모듈 | 담당 |
|--------|---------------|
| `ioc2rpz.erl` | TCP/TLS DNS 작업자 — 수락, 구문 분석, 검증, 응답 |
| `ioc2rpz_udp.erl` | UDP DNS 수신기 — SOA 질의 |
| `ioc2rpz_conn.erl` | IOC 소스 가져오기 (HTTP/HTTPS/파일/셸) |
| `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)가 있습니다. 샘플 BIND 구성 파일(named.conf)이 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>
## ioc2rpz™ 웹 인터페이스
[ioc2rpz.gui](https://github.com/Homas/ioc2rpz.gui)는 별도 프로젝트로 개발된 관리 웹 인터페이스입니다. 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는 zone transfers (AXFR/IXFR), SOA queries 및 management commands를 처리합니다. 5개의 pre-spawned accept workers로 구성된 pool이 들어오는 연결을 처리합니다.```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 배포를 지원합니다. TLS 리스너는 구성에 `cert` 레코드가 있을 때 포트 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 wire-format 메시지를 전달하며 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}`별로 추적됩니다. 따라서 여러 영역(예: `rpz1`, `rpz2`, `rpz3`)을 폴링/전송하고 관리하는 보조 서버가 하나의 IP에서 영역+유형별로 독립적으로 집계됩니다.
- **그 외 모든 것**(알 수 없거나 프로비저닝되지 않은 영역, 지원되지 않는 질의 유형, 잘못된 클래스, 또는 인식되지 않은 관리 이름)은 `{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 컨테이너를 사용하는 것입니다. Docker 배포 방법은 ioc2rpz™ 위키에서 확인할 수 있습니다.
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``
## 환경 변수
다음 환경 변수는 ioc2rpz™에서 사용되며, 주로 Docker 배포와 관련이 있습니다. 이들은 `config/sys.config.src` 및 `config/vm.args`에 정의되어 있습니다.
| 변수 | 기본값 | 설명 |
|----------|---------|-------------|
| `IPv4` | — | IPv4 바인드 주소 |
| `IPv6` | — | IPv6 바인드 주소 |
| `CONF` | — | 설정 파일 경로 (예: `./cfg/ioc2rpz.conf`) |
| `DB` | `/opt/ioc2rpz/db` | ETS 지속성을 위한 데이터베이스 디렉토리 |
| `CD` | `/opt/ioc2rpz` | 작업 디렉토리 |
| `NODE_NAME` | `ioc2rpz` | 얼랭 노드 짧은 이름 |
| `IO2Cookie` | `ioc2rpz` | 얼랭 분산 쿠키 |
환경 변수를 `-e`와 함께 Docker에 전달하세요:```bash
docker run -d --name ioc2rpz -e CONF=./cfg/custom.conf -e NODE_NAME=mynode ...
도커 컴포즈를 사용하여 ioc2rpz™ 및 ioc2rpz.gui를 배포할 수 있습니다. 표준 docker-compose.yml 파일은 ioc2rpz.dc 저장소에서 찾을 수 있습니다.
다음은 ioc2rpz와 웹 UI 및 Let's Encrypt certbot 사이드카를 보여주는 docker-compose.yml 예제입니다.```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가 인증서 파일이 변경되었음을 감지하면, 약 2분의 Erlang SSL 캐시를 기다리지 않고 즉시 TLS 리스너를 새 인증서로 다시 시작합니다. 재생성 훅에 `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 \
...
Reference in ioc2rpz.conf:
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 백업을 ./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"],[],[]}}.
[문서/구성.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/configuration.md)에서 필드별 공식 참조(소스/RPZ 튜플 레이아웃, SOA 타이머, 키 그룹, 인증서 등)를 확인하세요. DoT(포트 853), DoH 및 HTTPS REST API를 활성화하려면 `{cert,{...}}` 항목을 추가하세요 — [인증서 설정](#certificate-setup)을(를) 참조하세요.
### 개발 셸 (개발/테스트 전용)
다음 명령어는 로컬 개발 및 테스트용이며, 프로덕션 용도가 아닙니다:```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를 통한 관리를 지원합니다. DNS/TCP 대신 DoT 또는 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 Class CHAOS, RR Type TXTioc2rpz-update-tkeys, RR Class CHAOS, RR Type TXTioc2rpz-update-all-rpz, RR Class CHAOS, RR Type TXTzone_name, RR Class CHAOS, RR Type 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 인증이 요청 인증에 사용됩니다. 요청 인증에는 관리 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
}
]
}
The status 필드 (ready, updating, forceAXFR, notready)는 보고된 카운트/시리얼이 현재 상태인지 나타냅니다. updating 또는 forceAXFR 상태일 경우 마지막으로 완료된 업데이트를 반영합니다. 카운트와 시리얼은 구성 재로드 시 0으로 초기화되지 않고 유지됩니다.
/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
Success: `{"status":"ok","msg":"RPZ dga.ioc2rpz will be updated"}`
Error (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
Success: {"status":"ok","msg":"All sources will loaded to the hot cache"}
/api/v1/feed/:rpz_name — RPZ 피드에서 지표 가져오기
쿼리 파라미터: ?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"]
}
Error (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"
성공:```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
대화형 쉘에서 실행 중이거나 실행 중인 노드에 연결된 경우:```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를 참조하여 전체 모니터링 및 로그 참조를 확인하세요.
자세한 문제 해결은 docs/deployment.md를 참조하세요.
설정은 Erlang 파일입니다. 모든 설정 옵션은 Erlang 용어이므로 설정은 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}}.
The 4-field form remains valid and defaults `TrackSources` to `off`.
### **cert** 레코드
**cert** 레코드는 DNS over TLS, REST API 및 DoH 통신을 위한 인증서와 개인 키를 정의하는 데 사용됩니다. 인증서 생성 및 관리에 대한 내용은 [Certificate Setup](#certificate-setup)을 참조하십시오.
다음으로 구성됩니다:
- 인증서가 포함된 파일의 경로;
- PEM 인코딩된 개인 키가 포함된 파일의 경로;
- PEM 인코딩된 CA 인증서가 포함된 파일의 경로.
Sample **cert** 레코드:```
{cert,{"cfg/cert.pem", "cfg/key.pem", "cfg/cacerts.pem"}}.
include record는 ioc2rpz™ 구성을 여러 파일로 분할할 수 있게 합니다.
샘플 include record:``` {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` 레코드의 선택적 네 번째 필드를 통한 인라인: `{key, {Name, Alg, Secret, [Groups]}}`
2. `key_group` 레코드를 통한 명시적 할당 (아래 참조)```erlang
{key_group, {GroupName, [KeyName1, KeyName2, ...]}}.
| Field | Type | Description |
|---|---|---|
| GroupName | string | 그룹 이름 (예: "customers") |
| Keys | list of strings | 이 그룹에 속하는 TSIG 키 이름 목록 |
key_group 레코드 예시:```erlang {key_group, {"customers", ["dnsproxykey_1", "dnsproxykey_2"]}}. {key_group, {"public", ["dnsproxykey_3"]}}.
그룹은 키 목록의 `{groups, ["group1", "group2"]}` 튜플을 사용하여 `rpz` 및 `srv` 레코드에서 참조됩니다:```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 레코드는 다음으로 구성됩니다:
"")를 지정하면 기본 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** 레코드
소스는 악성 지표의 피드(feed)입니다. FQDN, IPv4 및 IPv6 주소를 지원합니다. 소스는 텍스트 파일 또는 텍스트 데이터의 피드입니다. 지표는 개행/캐리지 리턴 문자(/n, /r 또는 /r/n 모두)로 구분되어야 합니다. ioc2rpz™는 무제한의 지표 수를 지원합니다.
**source** 레코드는 다음으로 구성됩니다:
- 소스 이름;
- 전체 소스 전송(AXFR)을 위한 소스 경로. URL(http/https/ftp), 로컬 파일 및 스크립트를 지원합니다. 접두사 **file:** 은 로컬 파일에 사용됩니다. 접두사 **shell:** 은 호스트/컨테이너에서 로컬 스크립트/명령을 실행하여 지표와 선택적 만료일을 STDOUT으로 반환하도록 하는 데 사용됩니다;
- 증분 소스 전송(IXFR)을 위한 소스 경로. AXFR, IXFR 경로는 URL을 단축하고 영역 업데이트 타임스탬프를 제공하기 위해 키워드를 지원합니다:
- **[:AXFR:]** - 전체 AXFR 경로. IXFR 경로에서만 사용할 수 있습니다;
- **[:FTimestamp:]** - 소스가 마지막으로 업데이트된 시점의 타임스탬프 (예: 1507946281)
- **[:ToTimestamp:]** - 현재 타임스탬프;
- 지표와 만료 시간을 추출하는 데 사용되는 REGEX. 첫 번째 일치는 지표, 두 번째 일치는 만료 시간입니다. 만료 시간은 선택적 매개변수입니다. 정규 표현식은 큰따옴표 안에 포함되어야 합니다. 빈 REGEX(`""`)를 지정하면 기본 REGEX(`"^([A-Za-z0-9][A-Za-z0-9\-\._]+)[^A-Za-z0-9\-\._]*.*$"`)가 사용됩니다. REGEX가 필요하지 않은 경우(소스가 이미 필요한 형식으로 데이터를 제공하는 경우) `none`이 사용됩니다.
선택적 매개변수(모두 사용하거나 전혀 사용하지 않아야 함):
- UserID (내부적으로 사용됨).
- 최대 IoC 수.
- 전체 소스 업데이트, 핫 캐시 시간(초 단위).
- 증분 소스 업데이트, 핫 캐시 시간(초 단위).
HTTPS 소스 다운로드는 원격 서버의 TLS 인증서를 확인합니다. 유효하지 않거나 자체 서명된 인증서가 있는 소스는 다운로드에 실패합니다. 자체 서명된 인증서의 경우 `curl --insecure`와 함께 `shell:` 소스를 사용하십시오.
**로컬 파일 제한:** `file:` 경로에 `..`(상위 디렉터리 이동)가 포함된 것은 보안상 거부됩니다. `..` 없는 경로(작업/데이터 디렉터리 내)를 사용하십시오.
**셸 명령 제한:** 각 파이프라인 세그먼트의 실행 파일은 절대 경로(예: `/usr/bin/curl`) 또는 일반 이름의 안전한 텍스트 유틸리티(`sort`, `uniq`, `grep`, `sed`, `awk`, `gawk` 등)여야 합니다. 파괴적인 명령과 셸(`rm`, `bash`, `sh` 등)은 차단되며, 명령 치환(`$(...)`, 백틱) 및 출력 리디렉션(`>`, `>>`)은 거부됩니다. 거부된 명령은 실행되지 않고 CEF 이벤트 151(실행된 명령은 150)을 통해 기록됩니다. 전체 규칙 집합은 [docs/configuration.md](https://github.com/homas/ioc2rpz/blob/HEAD/docs/configuration.md#shell-command-restrictions)를 참조하십시오.
소스가 이전 지표 수의 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 피드를 혼합하거나 데이터베이스에서 데이터를 가져올 수 있습니다.
셸 명령 제한: 모든 실행 파일은 절대 경로를 사용해야 합니다(예: /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 — 피드별 소스 귀속: auto | true | false. 설정된 경우 서버의 전역 기본값을 재정의합니다. 생략된 경우(15개 필드 형식) 피드는 서버 기본값( , 구성되지 않은 경우 )을 상속합니다. 을 참조하세요.여러 로컬 액션은 목록으로 결합할 수 있습니다: [{"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`)은 변경 없이 로드되며 off로 동작합니다.
**해결 우선순위** — 피드의 유효 상태는 다음과 같이 결정됩니다:
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"]
}
단일 소스 피드: "sources": ["sample_fqdn"]. 추적 비활성화/알 수 없음: "sources": null.
출시 (기본적으로 꺼짐)
바이너리를 업그레이드해도 track_sources 값이 설정될 때까지 아무것도 변경되지 않습니다 — 추적 없음, 영역 재구축 없음, 변경되지 않은 API. 피드는 GUI로 관리됩니다. GUI/커뮤니티 사이트는 추후 업데이트에서 이러한 선택적 설정 값을 기록하고 sources를 표시합니다. 캐시된 피드에 대해 추적을 활성화하면 소스 마스크를 채우기 위해 일회성 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 캐시를 위한 DB 저장소 정의. 현재 버전은 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 지표는 1분 동안 캐시됩니다(0으로 설정되더라도) 현재 시리얼이 항상 이전 분으로 반올림되기 때문입니다.
- ``RATE_LIMIT_WINDOW`` (숫자 값, 밀리초 단위, 기본값 10000) - IP당 속도 제한 윈도우 지속 시간;
- ``MAX_REQUESTS_PER_WINDOW`` (숫자 값, 기본값 1) - 속도 제한 윈도우당 IP당 최대 DNS 요청 수;
- ``ShellMaxRespSize`` (숫자 값, 기본값 2 GiB) - 셸 명령 소스의 최대 응답 크기;
- ``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은 증분 영역 전송에 사용 가능한 최소 증분 영역 시리얼 역할을 합니다.
- IXFR 캐시는 전체 영역 업데이트(AXFR) 후 플러시됩니다. 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/).
### 샘플 바인드 구성```
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 워커 확인: supervisor:count_children(ioc2rpz_tls_sup_v6). 포트 853이 방화벽에서 차단되지 않았는지 확인하세요. |
| 영역 전송 실패 (TSIG 불일치) | 클라이언트와 서버 간 키 이름과 시크릿이 일치하는지 확인하세요. 로그에서 CEF 104/105 이벤트를 찾으세요. |
| 소스 다운로드 실패 | 네트워크 연결을 확인하세요. 로그에서 Error downloading feed를 찾으세요. 서버는 3초 지연으로 3번 재시도합니다. HTTPS 소스의 경우 유효한 TLS 인증서를 확인하세요. |
| 높은 메모리 사용량 | Erlang 셸에서 ETS 테이블 크기를 확인하세요. rate_limits와 rpz_hotcache_table은 주기적으로 정리됩니다. ?HotCacheTime을 줄이는 것을 고려하세요. |
#srvTrackSourcesoff| Action | Config Value | Description |
|---|
| 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)
- [awesome-threat-intelligence list on GitHub](https://github.com/hslatman/awesome-threat-intelligence)
다른 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 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>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 | 낮음 | 셸 명령 실행됨 | 셸 소스 명령 실행됨 (정보) |
| 151 | 높음 | 셸 명령 거부됨 | 셸 소스 명령 거부됨 (보안) |
| 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)을 사용할 수 있습니다.
# 후원자
**craSH**와 **rrbone**에게 감사드립니다. 그분들은 [GitHub Sponsor](https://github.com/sponsors/Homas)에서 제 프로젝트를 지원해 주십니다.
# 연락처
이메일: feedback(at)ioc2rpz[.]net 또는 [Telegram](https://t.me/ioc2rpz)으로 연락 주십시오.
# 라이선스
Copyright 2017 - 2025 Vadim Pavlov ioc2rpz[at]gmail[.]com
Apache License, Version 2.0 (이하 "라이선스")에 따라 라이선스가 부여됩니다. 라이선스를 준수하지 않는 한 이 파일을 사용할 수 없습니다.
라이선스 사본은 다음에서 얻을 수 있습니다.
http://www.apache.org/licenses/LICENSE-2.0
적용 가능한 법률에 의해 요구되거나 서면으로 동의하지 않는 한, 라이선스에 따라 배포되는 소프트웨어는 "있는 그대로" 배포되며, 명시적이든 묵시적이든 어떠한 종류의 보증이나 조건도 없습니다. 라이선스에 따른 권리 및 제한 사항을 관장하는 특정 언어는 라이선스를 참조하십시오.