Setup guide
OPNsense blocklist of malicious IPs, as an authorized URL Table alias
No credit card required · Free API key
On this page08
What you get
IP lists go to a URL Table (IPs) alias, which OPNsense fetches with your key. Domain lists go to Unbound’s DNS blocklists, which take no credentials, so they come from a relay.
| List | Entries | Rebuilt | Plans |
|---|---|---|---|
| blocklist-ips-critical.txtDefault alias. IPs listed by 6 or more threat sources, or 3 or more with a critical category. | about 89,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-ips-c2.txtCommand-and-control IPs reported by C2 trackers: a smaller alias. | about 44,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-c2.txtUnbound blocklist, through the relay: command-and-control domains reported by C2 trackers. | about 22,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-ransomware.txtUnbound blocklist, through the relay: domains in the ransomware category, whatever their level. | about 3,600 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-cryptomining.txtUnbound blocklist, through the relay: domains in the cryptomining category, whatever their level. | about 6,100 | every 12 h | Basic, Pro, and Enterprise |
Entries rounded from the build of ; each list is rebuilt every 12 hours. Today’s counts are public and need no key.
What each plan receives
- FreeFree account, or no key: the first 10% of each list, marked
X-Blocklist-Version: lite. - Basic, Pro, and EnterpriseBasic, Pro, and Enterprise: every list in full.
- Pro and EnterpriseTAXII 2.1 collections, for platforms that read STIX indicators: Pro and Enterprise.
GET https:/
Full list or 10% sample
| Field | Basic, Pro, and Enterprise | Free |
|---|---|---|
X-Blocklist-Version: | full | lite |
X-Blocklist-Percentage: | 100 | 10 |
Total entries: | <COUNT> | <COUNT> |
First lines of the file
# IsMalicious.com Blocklist - IPs (Critical)
# Format: Plain
# Generated: <BUILD_TIME>
# Total entries: <COUNT>
# Update frequency: every 12 hours
# Category: All
# Threat level: Critical
# Website: https://ismalicious.com
# © <YEAR> IsMalicious (compilation). Licensed to the downloading account under https://ismalicious.com/terms; redistribution of the compilation prohibited. Third-party entries remain under their providers' licences — see https://ismalicious.com/sources.
#Values in angle brackets are set by each build.
Prerequisites
- OPNsense 25.1 or later, the first release with authorization on URL aliases; menus checked against 25.1 to 26.7.
- For the domain lists only: a Linux host with systemd, curl and an HTTPS web server, which OPNsense reaches on your network.
- An isMalicious API key and secret, from Account › API access.
- Outbound HTTPS (TCP 443) over IPv4 from OPNsense, and from the relay host for the DNS blocklist, to
api.ismalicious.com, which has no IPv6 address.
Set it up
Copy your API key and secret
Open Account › API access and copy the API Key and the API Secret. The full list needs a Basic, Pro, or Enterprise plan; a Free key, or a lapsed plan, loads the 10% sample.Turn on certificate checking for alias downloads
In Firewall › Settings › Advanced (on the development branch, Firewall › Advanced), tick Check certificate of aliases URLs, before you save the alias. The setting is global, off in the factory configuration, and without it OPNsense sends the API secret to any server that answers. Save.Add the URL Table alias
Firewall › Aliases, then +: enable it, name itisMalicious_IPs, choose the type URL Table (IPs), set Refresh Frequency to 0 days and 1 hour (12 hours is enough: the lists are rebuilt every 12 hours), paste the list URL in Content, and set Authorization to Basic with the API key as Username and the API secret as Password. Save, then Apply.Alias fieldsGUI - Type
URLTable (IPs) - Refresh Frequency
0days, 1 hour - Content
https:// api. ismalicious. com/ blocklist/ download/ blocklist-ips-critical. txt - Authorization
Basic- Username
<API_KEY>- Password
<API_SECRET>
Type: URL Table (IPs) Refresh Frequency: 0 days, 1 hour Content: https://api.ismalicious.com/blocklist/download/blocklist-ips-critical.txt Authorization: Basic Username: <API_KEY> Password: <API_SECRET>Block the alias in firewall rules
- 25.1 to 26.1: on Firewall › Rules › WAN, add a Block rule with
isMalicious_IPsas the source; on Firewall › Rules › LAN, a Block rule with it as the destination, above the allow rule; Apply. - 26.7: Firewall › Rules, select WAN, +, Action Block, Version any, Source
isMalicious_IPs, Save; select LAN, +, Action Block, Version any, DestinationisMalicious_IPs, Save, then Move selected rule before this rule on the default allow rule; Apply. - The TCP/IP version defaults to IPv4 on every release: set it to IPv4+IPv6, or any.
- 25.1 to 26.1: on Firewall › Rules › WAN, add a Block rule with
Set up a relay for the domain lists (optional)
- Unbound’s blocklists download without credentials, so a host you control fetches the domain lists with your key and serves them over HTTPS.
- Create
/etc/ismalicious/netrcroot-only with the commands below and write the three lines into it with an editor; install the script as/, mode 755, with its service and timer units, and run the enable block, which createsusr/ local/ sbin/ ismalicious-mirror /var/www/ismaliciousand runs the first fetch at once. - The service runs as root to read the netrc, and may write nothing but that directory.
- The script refuses an error, a body that is not a list, the 10% sample and a file whose entry count differs from its header.
- Publish
/var/www/ismaliciousashttps:/with a certificate OPNsense trusts (a private CA goes under System › Trust › Authorities), refuse dot files in the web server, as in the nginx example, and keep it internal: each file is licensed to the account that downloads it, and its header forbids redistributing the compilation./ RELAY_HOST/ ismalicious/
Create the file, root-onlysh install -d -m 700 /etc/ismalicious [ -e /etc/ismalicious/netrc ] || install -m 600 /dev/null /etc/ismalicious/netrc chmod 600 /etc/ismalicious/netrc # Then write the three lines below into it with an editor, unless it holds them already./ etc/ ismalicious/ netrcnetrc machine api.ismalicious.com login <API_KEY> password <API_SECRET>/ usr/ local/ sbin/ ismalicious-mirrorsh #!/bin/sh # Fetch the lists with your key and publish them for the firewall. # A list that fails a check keeps its previous copy, and the run exits 1. set -eu NETRC=/etc/ismalicious/netrc DEST=/var/www/ismalicious LISTS="blocklist-domains-c2.txt blocklist-domains-ransomware.txt" # fetch_list LIST OUT: download one list to OUT and check it. # On any failure OUT is removed and the function returns 1. fetch_list() { if ! code=$(curl --silent --show-error --fail --netrc-file "$NETRC" \ --proto '=https' --max-time 300 --retry 2 \ --output "$2" --write-out '%{http_code}' "https://api.ismalicious.com/blocklist/download/$1"); then rm -f "$2"; echo "$1: download failed" >&2; return 1 fi if [ "$code" != 200 ]; then rm -f "$2"; echo "$1: HTTP $code" >&2; return 1 fi if ! head -n 1 "$2" | grep -q '^[#!] IsMalicious.com Blocklist'; then rm -f "$2"; echo "$1: not an isMalicious list" >&2; return 1 fi if grep -q 'Lite Version' "$2"; then rm -f "$2"; echo "$1: lite list received, check the API key and the plan" >&2 return 1 fi # Every entry ends with a newline, and one header counts them: refuse a # cut, doubled or empty file. if [ -n "$(tail -c 1 "$2")" ]; then rm -f "$2"; echo "$1: cut short, no final newline" >&2; return 1 fi if [ "$(grep -c '^[#!] Total entries:' "$2")" != 1 ]; then rm -f "$2"; echo "$1: not one Total entries line" >&2; return 1 fi total=$(sed -n 's/^[#!] Total entries: \([0-9,]*\)$/\1/p' "$2" | tr -d ,) got=$(grep -c '^[^#!]' "$2" || true) if [ -z "$total" ] || [ "$total" = 0 ] || [ "$got" != "$total" ]; then rm -f "$2"; echo "$1: $got entries, the header says ${total:-none}" >&2 return 1 fi } mkdir -p "$DEST" failed=0 for f in $LISTS; do tmp=$(mktemp "$DEST/.$f.XXXXXX") if fetch_list "$f" "$tmp"; then chmod 644 "$tmp" mv -f "$tmp" "$DEST/$f" else failed=1 fi done exit "$failed"/ etc/ systemd/ system/ ismalicious-mirror. servicesystemd [Unit] Description=Fetch isMalicious blocklists Wants=network-online.target After=network-online.target [Service] Type=oneshot ExecStart=/usr/local/sbin/ismalicious-mirror # Root, to read the root-only netrc; it may write only the published directory. NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes PrivateTmp=yes ReadWritePaths=/var/www/ismalicious/ etc/ systemd/ system/ ismalicious-mirror. timersystemd [Unit] Description=Fetch isMalicious blocklists every hour [Timer] OnCalendar=hourly RandomizedDelaySec=15min Persistent=true [Install] WantedBy=timers.targetEnable the timer and run the first fetchsh install -d -m 755 /var/www/ismalicious systemctl daemon-reload systemctl enable --now ismalicious-mirror.timer systemctl start ismalicious-mirror.service # the first fetch, now ls -l /var/www/ismalicious/nginx, in the relay’s server blocknginx location /ismalicious/ { allow <FIREWALL_IP>; deny all; # Downloads in progress are dot files: never serve them. location ~ /\. { deny all; } }Add the relay URL to Unbound’s blocklists
- 25.7.8 and later: Services › Unbound DNS › Blocklists, Blocklists tab, +.
- Switch on advanced mode, tick Enable, add
https:/and/ RELAY_HOST/ ismalicious/ blocklist-domains-c2. txt https:/to URLs of Blocklists, set Cache TTL to/ RELAY_HOST/ ismalicious/ blocklist-domains-ransomware. txt 43200(12 hours), tick Return NXDOMAIN or enter a Destination Address, type a Description, Save, then Apply. - 25.1 to 25.7.7: Services › Unbound DNS › Blocklist, advanced mode, Enable, the same URLs, Return NXDOMAIN or a Destination Address, Apply; these releases have no Cache TTL field and download a list again after 20 hours.
Schedule the blocklist update
Unbound’s blocklists refresh only from a cron task: System › Settings › Cron, then +, with the command Update Unbound DNSBLs every hour (minute15, hour*) and a Description, which is required. Save, then Apply. A list is downloaded again once its cached copy is older than the Cache TTL.
Verify it works
- Firewall › Diagnostics › Aliases, with
isMalicious_IPsselected, lists the current entries: about the list’scountin /blocklist/stats. - The OPNsense API returns the same entries, with an OPNsense API key: from 25.7 a request returns at most 9,999 rows, so read
total. - Firewall › Log Files › General shows
fetch alias urlwith the line count after each download, orerror fetching alias urlwith the HTTP code. - Services › Unbound DNS › Log File shows
blocklist downloadwith the lines downloaded for each URL; from 25.7.8 the Blocklist Tester checks a domain against your settings.
# The alias entries, through the OPNsense API: an OPNsense API key with the
# "Diagnostics: PF Table IP addresses" privilege. curl asks for its secret;
# the GUI CA (System › Trust › Authorities) verifies the firewall.
curl -s --cacert opnsense-gui-ca.pem -u '<OPN_KEY>' \
"https://<FIREWALL>/api/firewall/alias_util/list/isMalicious_IPs" | grep -o '"total":[0-9]*'Troubleshooting
error fetching alias url … [http_code:401]
The key or the secret is wrong or incomplete, and the alias keeps its previous content. The response body reads “Blocklist not found or empty”; trust the status code. Enter both values again from Account › API access: regenerating the key there invalidates the old pair.
Only about 10% of the entries load
Authorization is not set, OPNsense is older than 25.1, or the plan is Free or lapsed (past due, unpaid, canceled, incomplete or paused counts as Free). The file’s Total entries line then says Lite Version. For an Unbound blocklist pointed straight at api.ismalicious.com, this is expected: use the relay.
The relay prints “entries, the header says”, “cut short, no final newline” or “not one Total entries line”
The file was empty, cut short or not one of the lists, as a proxy or captive portal answers. The relay keeps the previous copy and the next run tries again.
“Error loading alias [isMalicious_IPs]”
Firewall › Log Files › General shows the table could not take the entries. Raise Firewall Maximum Table Entries above the total of your aliases, or use a smaller list.
Timeouts on a large list
A download that takes longer than 30 seconds on our side ends in a timeout or a 502: use a tier or category list from the table.
The Unbound blocklist loads nothing
Both parsers ignore the # header lines, so they are not the cause: an -adguard or -dnsmasq file is. Point Unbound at the plain domain file.
Certificate errors after ticking the check
api.ismalicious.com uses a Let’s Encrypt certificate: the firewall’s CA store must trust ISRG Root X1 or X2. For the relay, its certificate must chain to a CA OPNsense trusts.
403 from ismalicious.com
The ismalicious.com edge refuses some sources. Use api.ismalicious.com.
Limits
- Certificate checking for alias downloads is off by default: with it off, the Basic credentials go to whoever answers. Tick it before saving the alias.
- OPNsense stores the key and the secret in clear in
config.xml, its backups,/and alias exports: use a key dedicated to this firewall, and encrypt the backups.usr/ local/ etc/ filter_tables. conf - Unbound’s DNS blocklists have no credential field: the full domain lists need the relay.
- An IP alias resolves every line that is not an address through DNS: never point it at a domain list.
- No CIDR or ranges are served: the alias would accept them, but the IP lists hold single addresses.
- The 10% sample is the first tenth of an unsorted file, not the riskiest tenth.
Questions
Can OPNsense send an API key for a URL Table alias?
Yes, from OPNsense 25.1. Set the alias’s Authorization to Basic, with the API key as username and the API secret as password: OPNsense sends them with the first request, so the alias loads the full list.
Why tick Check certificate of aliases URLs?
It is off in the factory configuration, and without it OPNsense does not verify the server it sends the credentials to. Tick it before saving an alias that carries the API secret.
Can the Unbound DNS blocklist use the API key?
No. Unbound’s blocklists download without credentials and get the 10% sample. Serve them the full domain lists from a relay that fetches them with your key.
Which refresh frequency should the alias use?
1 hour, or 12 hours. The lists are rebuilt every 12 hours; OPNsense checks every minute and fetches an alias again once its frequency has elapsed.
Will the list fit in OPNsense’s tables?
The default table size is at least 1 million entries on 25.1, and grows with the RAM from 25.7. OPNsense replaces an alias’s table in place, so the total of your aliases must stay under Firewall Maximum Table Entries.
Related
IP and domain feeds with basic authentication
External Dynamic Lists sized to PAN-OS limits
A URL Table alias or pfBlockerNG feed through a relay
Threat domains as a RouterOS DNS adlist
Threat IPs as nftables sets on any Linux host
Keep firewall blocklists current
Every list, level and category
TAXII 2.1 collections for SIEMs and TIPs
Authentication, endpoints and limits
Check one address before you block it
Get Started
Ready to get started?
No credit card required · Free API key