Use the PCA IP blocklist and CVE feed
Get a key, pull the list in your firewall's format, update it automatically, and see what it holds today.
What the feeds hold, measured
Measured on 11 November 2026, 02:49 UTC, with one call per format on the live API, using a temporary key created for the test and revoked right after. We print the numbers as they are: today the connection works end to end and the content is only starting. Check the date above before you rely on a number.
2 entries in the IP blocklist
No open source connected yet
Rebuilt once a day
Six steps from key to automatic updates
For network engineers and SOC analysts of member organisations. Every command below is copy-paste; replace nothing but the key, and only in step 1.
1. Get an API key
Registration on the site is closed: keys are for member organisations, and accounts are opened by hand. Ask for an account through the contact form (the get-started page says we answer within one working day). Once your account is open, sign in, go to My account, then API Keys, and create a key, for yourself or for your organisation. The key is shown once: put it in your secret manager or in a file only root can read.
Where to ask: Get started with the PCA API (the account form is at the bottom) or the contact form. Once signed in: My account, API Keys.
sudo install -d -m 700 /etc/pca
sudo sh -c 'umask 077; cat > /etc/pca/api-key'
# paste the key (pca_live_xxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx), press Enter, then Ctrl-D:
# the key stays out of your shell history2. Check the key
GET /whoami answers 200 with the key's scope and its daily quota. A wrong or revoked key gets a 401. The curl option -H @file reads the header from a file, so the key never shows in the process list.
(umask 077; printf 'X-API-Key: %s\n' "$(sudo cat /etc/pca/api-key)" > ~/.pca-header)
curl -sS -H @"$HOME/.pca-header" https://portcyberalliance.org/wp-json/pca/v1/whoami
# 200 → {"org_hash":…,"scope":"user","quota":{…}} · 401 → wrong or revoked key3. Pull the list in the format your edge reads
One endpoint, GET /ip-blocklist, and a format parameter. These five formats answered 200 when we measured; any other value gets a 422.
B=https://portcyberalliance.org/wp-json/pca/v1/ip-blocklist
curl -sS -H @"$HOME/.pca-header" "$B?format=json" # records with source, first_seen, last_seen
curl -sS -H @"$HOME/.pca-header" "$B?format=csv" # ip,cidr,source,reason,last_seen
curl -sS -H @"$HOME/.pca-header" "$B?format=nginx" # deny 203.0.113.7;
curl -sS -H @"$HOME/.pca-header" "$B?format=htaccess" # Deny from 203.0.113.7
curl -sS -H @"$HOME/.pca-header" "$B?format=iptables" # iptables -A INPUT -s 203.0.113.7 -j DROP4. Apply it safely
Treat the answer as data, never as commands. Never pipe the iptables format into a shell: read the addresses, check each one, and load them into an ipset that a single firewall rule matches. For nginx, check every line, run nginx -t, and keep the previous file if the test fails. The pull script below does both, and applies nothing when the answer is not a 200 or holds an unexpected line. Note that an nginx deny written at http or server level is not inherited by a location that has its own allow or deny lines.
# nginx: once, in each server block to protect
include /etc/nginx/snippets/pca-blocklist.conf;
# ipset + iptables: once, after the first pull has created the two sets
iptables -I INPUT -m set --match-set pca-blocklist src -j DROP
ip6tables -I INPUT -m set --match-set pca-blocklist-v6 src -j DROP5. Keep it up to date
The list is rebuilt once a day, so pulling every six hours is plenty and stays far below the daily quota. Install the script, then choose a systemd timer or a cron line.
The script, the two systemd units and the cron line are in the files folder and in full in the files section below.
sudo install -m 750 pca-blocklist-pull.sh /usr/local/sbin/
sudo install -m 644 pca-blocklist.service pca-blocklist.timer /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now pca-blocklist.timer
systemctl list-timers pca-blocklist.timer # next run
journalctl -u pca-blocklist.service -n 20 # what the last run did
# or, without systemd: sudo install -m 644 pca-blocklist.cron /etc/cron.d/pca-blocklist6. Read the CVE feed
GET /cve-feed lists the vulnerabilities that concern port software, as JSON or RSS, optionally for one product. Both formats answered 200 when we measured, with an empty list (see « What the feed covers »).
C=https://portcyberalliance.org/wp-json/pca/v1/cve-feed
curl -sS -H @"$HOME/.pca-header" "$C?format=json" # {"cves":[…]}
curl -sS -H @"$HOME/.pca-header" "$C?format=rss" # RSS 2.0 for a feed reader
curl -sS -H @"$HOME/.pca-header" "$C?format=json&product=<product>"What each format gives
The same entries in five shapes. Measured answer of the nginx format on 11 November 2026, 02:49 UTC:
deny 203.0.113.7;
deny 198.51.100.0/24;format=json
format=csv
format=nginx
format=htaccess
format=iptables
The files, in full
Click a file to read it. They are also in the files folder, with a README and the licence (CC BY 4.0).
Pulls the list, checks every entry, applies it to nginx or to two ipsets, and applies nothing on a non-200 answer or an unexpected line. Install in /usr/local/sbin/.
#!/usr/bin/env bash
# pca-blocklist-pull.sh · Port Cyber Alliance · CC BY 4.0 · https://portcyberalliance.org/api-developers/ip-blocklist-cve-feed-howto/
# Pull the PCA IP blocklist, check every entry, apply it only if the whole answer is valid; otherwise keep the last good list.
# pca-blocklist-pull.sh nginx → writes the nginx snippet, runs nginx -t, reloads nginx
# pca-blocklist-pull.sh ipset → refills the ipsets pca-blocklist (IPv4) and pca-blocklist-v6 (IPv6)
# The key is read from a root-only file and sent with curl -H @file, so it never shows in the process list.
set -euo pipefail
MODE="${1:-nginx}"
URL="${PCA_URL:-https://portcyberalliance.org/wp-json/pca/v1/ip-blocklist}"
KEY_FILE="${PCA_API_KEY_FILE:-/etc/pca/api-key}"
STATE="${PCA_STATE_DIR:-/var/lib/pca-blocklist}"
SNIPPET="${PCA_NGINX_SNIPPET:-/etc/nginx/snippets/pca-blocklist.conf}"
SET="${PCA_IPSET:-pca-blocklist}"
TOKEN='^[0-9A-Fa-f:.]+(/[0-9]{1,3})?$'
umask 077; mkdir -p "$STATE"
body="$(mktemp "$STATE/body.XXXXXX")"; list="$(mktemp "$STATE/list.XXXXXX")"; hdr="$(mktemp "$STATE/hdr.XXXXXX")"
trap 'rm -f "$body" "$list" "$hdr"' EXIT
printf 'X-API-Key: %s\n' "$(tr -d '\r\n' < "$KEY_FILE")" > "$hdr"
case "$MODE" in nginx) fmt=nginx ;; ipset) fmt=csv ;; *) echo "usage: pca-blocklist-pull.sh nginx|ipset" >&2; exit 2 ;; esac
code="$(curl -sS --max-time 60 -H @"$hdr" -o "$body" -w '%{http_code}' "$URL?format=$fmt" || echo 000)"
[ "$code" = 200 ] || { echo "pca-blocklist: HTTP $code, nothing applied, last good list kept" >&2; exit 1; }
if [ "$MODE" = nginx ]; then
sed -n 's/^deny \(.*\);$/\1/p' "$body" > "$list"
if grep -Ev '^(deny [0-9A-Fa-f:.]+(/[0-9]{1,3})?;)?$' "$body" | grep -q .; then
echo "pca-blocklist: unexpected line in the answer, nothing applied" >&2; exit 1
fi
mkdir -p "$(dirname "$SNIPPET")"
[ -f "$SNIPPET" ] && cp -p "$SNIPPET" "$STATE/last-good.conf"
{ echo "# PCA IP blocklist · pulled $(date -u +%FT%TZ) · do not edit, rewritten by pca-blocklist-pull.sh"; cat "$body"; } > "$SNIPPET.new"
chmod 644 "$SNIPPET.new"; mv "$SNIPPET.new" "$SNIPPET"
if nginx -t -q; then
systemctl reload nginx
else
if [ -f "$STATE/last-good.conf" ]; then cp -p "$STATE/last-good.conf" "$SNIPPET"; else : > "$SNIPPET"; fi
echo "pca-blocklist: nginx -t failed, previous list restored" >&2; exit 1
fi
else
# CSV columns: ip,cidr,source,reason,last_seen · the entry is the range when there is one, else the address
tail -n +2 "$body" | tr -d '"' | while IFS=, read -r ip cidr _; do
if [ -n "$cidr" ]; then echo "$cidr"; elif [ -n "$ip" ]; then echo "$ip"; fi
done > "$list"
if grep -Ev "$TOKEN" "$list" | grep -q .; then
echo "pca-blocklist: unexpected entry in the answer, nothing applied" >&2; exit 1
fi
ipset -exist create "$SET" hash:net family inet
ipset -exist create "$SET-v6" hash:net family inet6
ipset -exist create "$SET-new" hash:net family inet; ipset flush "$SET-new"
ipset -exist create "$SET-v6-new" hash:net family inet6; ipset flush "$SET-v6-new"
while read -r t; do
case "$t" in *:*) ipset -exist add "$SET-v6-new" "$t" ;; *) ipset -exist add "$SET-new" "$t" ;; esac
done < "$list"
ipset swap "$SET-new" "$SET"; ipset destroy "$SET-new"
ipset swap "$SET-v6-new" "$SET-v6"; ipset destroy "$SET-v6-new"
fi
echo "pca-blocklist: $(grep -c . "$list" || true) entries applied ($MODE)"The systemd service that runs the script once. Install in /etc/systemd/system/.
# Port Cyber Alliance · CC BY 4.0 · install in /etc/systemd/system/
[Unit]
Description=Pull the Port Cyber Alliance IP blocklist
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
Environment=PCA_API_KEY_FILE=/etc/pca/api-key
# nginx snippet; use « ipset » instead of « nginx » for the ipset + iptables setup
ExecStart=/usr/local/sbin/pca-blocklist-pull.sh nginxEvery six hours, spread by up to 15 minutes, and a missed run is caught up after downtime. Install in /etc/systemd/system/.
# Port Cyber Alliance · CC BY 4.0 · install in /etc/systemd/system/, then: systemctl enable --now pca-blocklist.timer
[Unit]
Description=Pull the Port Cyber Alliance IP blocklist every six hours
[Timer]
OnCalendar=*-*-* 00/6:00:00
RandomizedDelaySec=15min
Persistent=true
[Install]
WantedBy=timers.targetThe same schedule for hosts without systemd. Install as /etc/cron.d/pca-blocklist.
# Port Cyber Alliance · CC BY 4.0 · install as /etc/cron.d/pca-blocklist (cron.d lines carry the user)
# every six hours, at a minute of your choice; the result goes to syslog under the tag pca-blocklist
17 */6 * * * root PCA_API_KEY_FILE=/etc/pca/api-key /usr/local/sbin/pca-blocklist-pull.sh nginx 2>&1 | logger -t pca-blocklistDownload the files
Version 1.0, published 11 November 2026, from one source file (YAML). Generic: no client, no supplier, no invented figure.
What the feed covers
GET /cve-feed answers in JSON or RSS. When we measured, both formats answered 200 with an empty list:
{"cves":[]}Where it comes from
What gets in
What each entry holds
How often
Today
IP blocklist and CVE feed: questions
Click a question to read the answer.
Keys are for member organisations and accounts are opened by hand: registration on the site is closed. Ask for an account through the form on the Get started page or the contact form. Once your account is open, sign in and create the key under My account, API Keys. It is shown once, so store it in a secret manager or a root-only file.
Five, all measured with a 200 on 11 November 2026: json, csv, nginx, htaccess and iptables. Any other value of the format parameter gets a 422 that lists the accepted values. A call without a key, or with a revoked key, gets a 401.
Small. On 11 November 2026, 02:49 UTC it held 2 entries: one address and one range, both in blocks that RFC 5737 reserves for documentation, so blocking them changes nothing on your network. No open blocklist source is connected to the aggregator yet. The measurement is dated on this page: check the date before you rely on it.
The list is rebuilt once a day, so every six hours is plenty. The test key's quota was 1,000 GET calls a day, so a pull every six hours uses four of them.
No. Treat the answer as data, never as commands: read the addresses, check that each one is only an address or a range, and load them into an ipset matched by one firewall rule. The pull script on this page does that, and applies nothing when the answer is not a 200 or holds an unexpected line.
Vulnerabilities from the US National Vulnerability Database whose affected products match a watchlist of port and industrial software and of the alliance's own web stack. On 11 November 2026, 02:49 UTC it answered an empty list: the first collection had read 200 records, all published between 1988 and 1997, and none matched. One collection a day is scheduled.
Yes. They are published under the Creative Commons Attribution 4.0 licence (CC BY 4.0): copy and adapt them, including commercially, as long as you credit Port Cyber Alliance and link to this page. Test them on a non-production host first, and keep an allowlist for your partners.
Sources
Public references for the address blocks, the CVE source and the tools used in the examples. The numbers come from our own measurement, dated above.
Related pages
Ready to pull the list?
Accounts are opened by hand for member organisations. Tell us who you are and what you want to connect.
Need an API key?
