Export¶
Exports turn indicators into blocklists. They merge the selected sources, deduplicate values (case-insensitive; the same IP with different netmasks counts as different), apply whitelists and render the requested format.
Endpoints¶
| Endpoint | Sources | ACL | Cached |
|---|---|---|---|
GET /api/v1/export |
all non-private sources | export |
yes (CONFIG_REDIS_TTL) |
GET /api/v1/export/:id |
one source, private allowed | export/<id> |
no |
GET /api/v1/export/private/:ids |
listed sources (5,6), private allowed |
export/private/<id> for every ID |
no |
GET /api/v1/export/group/:ref |
members of a group + its blacklist | export/group/<id or name> |
no |
CLI: indicators:export (public sources) or indicators:export --group <ref>.
Responses are plain text (text/plain, text/dns for RPZ), errors too (403 Forbidden. Insufficient permissions., 404 Source not found.). Only live sources are exported – a deleted source's indicators never appear.
Conditional requests and limits¶
Every export sends a weak ETag computed over its content (the generation timestamp and SOA serial are ignored). Sending it back as If-None-Match returns 304 Not Modified without a body when nothing changed – tis-pull.sh uses this to skip downloading, comparing and reloading.
Exports are limited to CONFIG_RATE_LIMIT_EXPORTS (default 120) per token and minute; beyond that the API answers 429 with Retry-After.
Parameters¶
| Parameter | Values | Default |
|---|---|---|
format |
plain, hosts, rpz, zone, csv, nftables, ipset, ipfw, dnsmasq, unbound |
plain |
type |
ipv4, ipv6, fqdn, url, json |
all |
tag |
tag name – matches a tag of the indicator or of its source | – |
whitelist |
comma-separated IPs, CIDRs, domain globs | – |
rpz_ip |
response, client, nsip – RPZ only, see IP triggers |
– |
country |
comma-separated country codes – only IP indicators in these GeoIP countries (other types pass) | – |
exclude_country |
comma-separated country codes – no IP indicators in these countries | – |
Authentication: Authorization: Bearer <token>, access-token: <token> header, or ?access_token=<token> (handy for appliances; it is redacted from logs).
Formats¶
plain¶
One value per line. IPs with a non-default netmask get their prefix (10.0.0.0/8); single addresses don't (192.0.2.1). URLs and JSON values are included as-is. Use type to get a single kind, e.g. type=ipv4 for a firewall table.
hosts¶
Domains only, as /etc/hosts lines pointing to 0.0.0.0:
rpz¶
A complete Response Policy Zone for BIND/Unbound (domains only; $TTL 300, SOA serial from the generation time):
$TTL 300
@ IN SOA localhost. root.localhost. ( ... )
IN NS localhost.
safe.example.com IN CNAME rpz-passthru. ; whitelist safe.example.com
*.safe.example.com IN CNAME rpz-passthru. ; whitelist safe.example.com
27.73.2.168.192.rpz-ip IN CNAME rpz-passthru. ; whitelist 192.168.2.73/27
evil.example.com IN CNAME .
*.evil.example.com IN CNAME .
www.bad.example IN CNAME .
- A blocked domain also blocks its subdomains (
*.name), except names starting withwww.(blocking*.bad.examplefromwww.bad.examplewould over-block) and explicit wildcards. - Whitelist entries become explicit
rpz-passthrurecords (IPs as reversedrpz-ipnames) – from the request whitelist and from the whitelists of sources present in the export – so the zone documents the allowlist.
IP triggers¶
Since v1.26.0. By default the RPZ contains domains only. With rpz_ip= IPv4 and IPv6 indicators are added as RPZ IP triggers (<prefix>.<reversed address>.<trigger>, IPv6 in 16-bit groups with zz for ::):
rpz_ip |
Trigger | Matches |
|---|---|---|
response |
rpz-ip |
answers (A/AAAA records) pointing to the IP |
client |
rpz-client-ip |
queries coming from the IP (BIND only) |
nsip |
rpz-nsip |
names served by a nameserver at the IP |
Whitelisted IPs get their rpz-passthru under the same trigger. Unbound supports rpz-ip and rpz-nsip but not rpz-client-ip.
zone¶
BIND zone statements pointing each domain to an empty zone (domains only; leading *. stripped):
Include it from named.conf and provide master/empty.db.
csv¶
type,value,source,tags,last_observed,country – one row per indicator, with the indicator's and its source's tags and the GeoIP country of IPs (text/csv; country since v1.39.0). For spreadsheets and SIEM imports.
nftables¶
A script for nft -f <file>: declares the sets blocklist_v4 / blocklist_v6 (interval sets with auto-merge) in table inet tis, flushes and refills them – safe to load repeatedly. Use them in your own rules, e.g. ip saddr @blocklist_v4 drop. IPs only.
ipset¶
A file for ipset restore < <file>: creates (if missing), flushes and fills the hash:net sets tis-blocklist-v4 / tis-blocklist-v6. IPs only.
ipfw¶
Since v1.33.0. An ipfw command file for ipfw -q /path/to/file (FreeBSD) – the path must be absolute, otherwise ipfw takes it for a command, not a file: creates the addr table tis-blocklist if missing, fills a temporary table tis-blocklist_tmp, swaps the two and drops the temporary one – the live table is never empty or half-filled, and as ipfw stops at the first error, a failed load leaves it unchanged. Each address is listed once. Use it in your rules, e.g. ipfw add deny ip from table(tis-blocklist) to any. IPs only.
For your own table name, change detection and rollback, prefer tis-pull.sh --ipfw-table <name>.
dnsmasq¶
address=/evil.example/ per domain – NXDOMAIN for the domain and its subdomains. Exact whitelist entries become server=/name/# exceptions. Include with conf-file=<file>.
unbound¶
A server: block with local-zone: "evil.example." always_nxdomain per domain (covers subdomains); exact whitelist entries become more specific always_transparent zones. Include with include: <file>.
All formats except ipfw were validated with unbound-checkconf, dnsmasq --test, nft -c and repeated nft -f / ipset restore.
Whitelisting¶
Three whitelists apply, and an indicator is removed if any of them matches:
| Whitelist | Applies to | Set via |
|---|---|---|
| request | the whole export | ?whitelist= / --whitelist / tis-pull.sh --whitelist |
| source | indicators of that source only | source options.whitelist |
| group | the whole group export | group:update --whitelist … |
Matching rules:
- IP / CIDR – an indicator is removed if its network lies within or equals a whitelisted network: whitelisting
10.0.0.0/16removes10.0.5.5and10.0.0.0/16, but not the wider10.0.0.0/8. A single address (192.0.2.1) removes exactly that address. - Domains – case-insensitive globs:
example.comremoves onlyexample.com;*.example.comremoves every subdomain but notexample.comitself. Whitelist both if you need both. (In RPZ, the passthru record for a plainexample.comentry additionally covers its subdomains.)
Invalid entries (e.g. 1.2.3.4/40) are ignored.
Examples¶
# RPZ for a resolver, keep the local network
curl -H "Authorization: Bearer $TOKEN" \
"https://tis.cap-net.cz/api/v1/export?format=rpz&tag=malware&whitelist=192.168.0.0/16"
# IPv4 list of two private sources for a firewall table
curl "https://tis.cap-net.cz/api/v1/export/private/5,6?format=plain&type=ipv4&access_token=$TOKEN"
# a customer's group as BIND zone statements
curl "https://tis.cap-net.cz/api/v1/export/group/customer-x?format=zone&type=fqdn&access_token=$TOKEN"
For scheduled deployment with atomic replace and rollback use tis-pull.sh.