IOC Proxy
Package name: intrudect-iocfeed
intrudect-iocfeed is a standalone IOC (Indicator of Compromise) feed cache and
HTTP feed service. It is the single downloader and proxy that fetches threat
intelligence from upstream sources — MISP, Suricata/Snort .rules files,
abuse.ch/Emerging Threats IP and domain blocklists, and the Tor relay list — and
re-serves them as clean, deduplicated feeds over HTTP.
It writes last-good copies of every feed to disk, so upstream outages never take your feeds down, and it uses only the Go standard library with no database.
Important
The IOC Proxy service must be installed and running for the Intrudect
Web UI to receive IOC and Tor feed data. The Web UI mirrors its
feeds from this service (see its IOCFEED configuration block). If
intrudect-iocfeed is not up and reachable, there is no IOC or Tor feed —
the network agents will not receive updated blocklists or Tor relay lists.
Inputs: MISP API, HTTP feeds (Suricata/Snort .rules, plain/hostfile/CSV
ip & dns lists), Tor relay list (Onionoo)
Outputs: by-source feeds (misp/…, rules/…, tor/…) and merged by-category
feeds (ip.txt, dns.txt, rules.rules)
Storage: /opt/intrudect-iocfeed/data
Files
/opt/intrudect-iocfeed/bin/intrudect-iocfeed
/opt/intrudect-iocfeed/etc/config.json
/opt/intrudect-iocfeed/etc/basic.htpasswd
/opt/intrudect-iocfeed/etc/bearer.tokens
/opt/intrudect-iocfeed/data/misp/
/opt/intrudect-iocfeed/data/rules/
/var/log/intrudect-iocfeed.log
The package installs the systemd unit, rsyslog config, and logrotate config, but does not auto-enable or auto-start the service.
Configuration
Configuration file is /opt/intrudect-iocfeed/etc/config.json. A working example
(with the MISP server address and API key removed — fill in your own) looks like:
{
"LISTEN": "127.0.0.1:8084",
"DATA_DIR": "/opt/intrudect-iocfeed/data",
"STALE_AFTER": "24h",
"AUTH": {
"BASIC_FILE": "/opt/intrudect-iocfeed/etc/basic.htpasswd",
"BEARER_FILE": "/opt/intrudect-iocfeed/etc/bearer.tokens"
},
"MISP": {
"ENABLED": true,
"URL": "https://misp.example.com",
"APIKEY": "",
"APIKEY_ENV": "",
"INTERVAL": "1h",
"TIMEOUT": "60s",
"MAX_BYTES": 67108864,
"TAGS": ["Cobalt Strike", "C2", "gophish", "evilginx"],
"TYPES": ["domain", "ip-dst"],
"PUBLISH_TIMESTAMP": "7d",
"ATTRIBUTE_TIMESTAMP": "62d"
},
"RULES": {
"ENABLED": true,
"INTERVAL": "6h",
"TIMEOUT": "60s",
"MAX_BYTES": 67108864,
"FEEDS": [
{
"NAME": "Emerging Threats",
"URL": "https://rules.emergingthreats.net/open/suricata-7.0/emerging-all.rules",
"CATEGORY": "rules",
"ENABLED": true
},
{
"NAME": "Emerging Threats Block IPs",
"URL": "https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt",
"CATEGORY": "ip",
"ENABLED": true
},
{
"NAME": "Threat Fox DNS",
"URL": "https://threatfox.abuse.ch/downloads/hostfile/",
"CATEGORY": "dns",
"FORMAT": "hostfile",
"ENABLED": true
},
{
"NAME": "Threat Fox IP",
"URL": "https://threatfox.abuse.ch/export/csv/ip-port/full/",
"CATEGORY": "ip",
"FORMAT": "csv",
"ENABLED": true
}
]
},
"TOR": {
"ENABLED": true,
"URL": "https://onionoo.torproject.org/details",
"INTERVAL": "12h",
"TIMEOUT": "60s",
"MAX_BYTES": 100000000
}
}
LISTEN- IP address and port to bind. The service has no TLS of its own; bind it to an address the Web UI host can reach (a loopback, a management interface, or a Docker bridge such as172.17.0.1:8084when the Web UI runs in a container), and put a reverse proxy in front of it if you expose it externally.DATA_DIR- directory where last-good feed files are written.STALE_AFTER- a feed older than this is reported as stale in the status output.AUTH- paths to the basic-auth and bearer-token credential files (see Auth).DEBUG- set the top-level"DEBUG": trueto log detailed outbound request and response diagnostics. MISP API keys are never logged.
MISP
URL/APIKEY- your MISP instance and its API key. Set theAPIKEYhere, or leave it empty and setAPIKEY_ENVto the name of an environment variable that holds the key.TAGS- only attributes carrying these tags are pulled.TYPES- MISP attribute types to import (domain,ip-dst).PUBLISH_TIMESTAMP/ATTRIBUTE_TIMESTAMP- only fetch events/attributes seen within these windows.- Set
"ENABLED": falseto disable MISP entirely.
Feed categories
Each entry in RULES.FEEDS is an HTTP feed described by these fields beyond
NAME / URL / ENABLED:
| Field | Values | Default | Meaning |
|---|---|---|---|
CATEGORY |
rules, ip, dns |
rules |
Indicator type produced; selects the merged feed (rules.rules, ip.txt, dns.txt) and the on-disk extension (.rules for rules, .txt for ip/dns). |
FORMAT |
plain, hostfile, csv |
plain |
How the source is parsed (see below). |
MIN_CONFIDENCE |
0–100 |
0 |
For csv feeds only: drop rows whose confidence_level is below this value. 0 keeps everything. |
Formats:
plain— one indicator per line;#comments and blank lines are ignored. Matches Suricata.rulesfiles and simple IP/domain blocklists.hostfile— hosts-file lines such as0.0.0.0 ads.example.com; the leading address and any trailing# commentare stripped. RequiresCATEGORY: "dns".csv— abuse.ch / ThreatFox CSV dumps. Theioc_valuecolumn is extracted; forCATEGORY: "ip"a trailing:portis removed. Combine withMIN_CONFIDENCEto keep only high-confidence rows.
For ip/dns feeds, extracted values are validated (invalid entries dropped),
deduplicated and sorted before being written. Rule feeds are stored verbatim, and
their URL must end in .rules (or .rules.zip).
Zip archives: if a feed URL path ends in .zip, the download is decompressed
before parsing — the FORMAT then applies to the extracted content. The
decompressed size is capped by MAX_BYTES as a zip-bomb guard.
HTTP API
Browser/help endpoint:
The root page shows service version, overall status, active problems, available feeds, endpoint examples, and recent feed events. It does not expose secrets, the MISP API key, or the auth files.
Open (no-auth) endpoints:
# By category (merged across sources)
GET /open/ip.txt # MISP ip-dst + all ip HTTP feeds
GET /open/dns.txt # MISP domain + all dns HTTP feeds
GET /open/rules.rules # all rule feeds (same as rules/all.rules)
# By source
GET /open/misp/ip-dst.txt
GET /open/misp/domain.txt
GET /open/rules/all.rules
GET /open/rules/<feed-name>.rules
GET /open/rules/<feed-name>.txt
GET /open/tor/relays.json
GET /open/metadata
Basic-auth endpoints use the same paths under /basic/; bearer-auth endpoints
under /bearer/.
Service endpoints:
GET /health
GET /ready
GET /api/v1/status # primary machine-readable status
GET /api/v1/events # recent feed events
GET /api/v1/metadata # raw MISP/rules metadata
Category feeds
/open/ip.txt and /open/dns.txt are merged, deduplicated, sorted lists built at
request time from every source of that category (the MISP ip-dst/domain files
plus all HTTP feeds of the matching CATEGORY). /open/rules.rules serves the
merged rule set. A category endpoint returns 404 until at least one of its
sources exists on disk, and responses carry an ETag so If-None-Match requests
return 304 when nothing changed.
Tor relay feed
/open/tor/relays.json serves the last-good Tor relay list fetched from the
Onionoo details document.
Note
The Tor relay list is deliberately not part of the ip category and is
never merged into /ip.txt. MISP, rules, and HTTP IP feeds are known-bad
indicators, safe to block by default. Tor relay membership is a policy
signal — a Tor IP is not inherently malicious — so it is kept as its own feed
for consumers that explicitly want to act on it.
Auth
Both credential files use the same formats as the other Intrudect components.
Basic auth reads /opt/intrudect-iocfeed/etc/basic.htpasswd (formats {SHA},
$apr1$, and plaintext fallback), one username:hash per line:
Bearer auth reads /opt/intrudect-iocfeed/etc/bearer.tokens, one token per line.
FortiGate external resources support HTTP Basic auth with set username /
set password. Bearer auth is provided for Intrudect and other clients that can
send Authorization: Bearer headers.
FortiGate example
config system external-resource
edit "Intrudect_MISP_IP"
set type address
set resource "https://feeds.example.com/open/misp/ip-dst.txt"
set refresh-rate 60
set server-identity-check full
next
end
Start & enable service
Verify it is serving feeds: