All Playbooks
beginner20 minNetwork Engineer / Security Administrator

Blocking Malicious IPs with Firewall Automation

What you will achieve

A cron-driven pipeline that automatically blocks malicious IPs at the network edge, updated every 6 hours from live threat intelligence.

isMalicious APIPythoniptablespfSenseAWS
Jean-Vincent QUILICHINIJean-Vincent QUILICHINIMar 5, 2026

What You Will Achieve

By the end of this playbook you will have:

  • A Python script that pulls malicious IPs from isMalicious and formats them for your firewall
  • iptables, pfSense, Palo Alto, and cloud implementation examples
  • A cron schedule that keeps blocklists fresh every 6 hours
  • Change detection so only new IPs are added (no duplicate rules)

Prerequisites

| Requirement | Details | |---|---| | isMalicious API key | Free tier gives 1,000 checks/day; Pro for bulk export | | Python 3.9+ | Runs the sync script | | Root/admin access | Required to modify firewall rules |


Step 1: Fetch Malicious IPs from isMalicious

The STIX/TAXII feed (Pro) is the cleanest way to pull IOCs in bulk. For free tier, use the bulk check endpoint.

Using the TAXII feed (Pro)

python
import base64
import requests

TAXII_URL = "https://api.ismalicious.com/taxii/api-root/collections/malicious-ips/objects"
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
HDR = {"X-API-KEY": base64.b64encode(f"{API_KEY}:{API_SECRET}".encode()).decode()}

def fetch_malicious_ips():
    resp = requests.get(
        TAXII_URL,
        params={"limit": 1000},
        headers=HDR
    )
    resp.raise_for_status()
    bundle = resp.json()
    
    ips = []
    for obj in bundle.get("objects", []):
        if obj.get("type") == "indicator":
            pattern = obj.get("pattern", "")
            # Extract IPv4 from STIX pattern: [ipv4-addr:value = '1.2.3.4']
            if "ipv4-addr:value" in pattern:
                ip = pattern.split("'")[1]
                ips.append(ip)
    
    return ips

Step 2: iptables Integration

blocklist_update.sh

bash
#!/bin/bash
set -euo pipefail

CHAIN="ISMALICIOUS_BLOCK"
BLOCKLIST="/tmp/ismalicious_ips.txt"

# Fetch fresh blocklist
python3 /opt/ismalicious/fetch_ips.py > "$BLOCKLIST"

# Create chain if it doesn't exist
iptables -N "$CHAIN" 2>/dev/null || true
iptables -F "$CHAIN"

# Re-add all rules
while IFS= read -r ip; do
  [[ -z "$ip" ]] && continue
  iptables -A "$CHAIN" -s "$ip" -j DROP
  iptables -A "$CHAIN" -d "$ip" -j DROP
done < "$BLOCKLIST"

# Ensure chain is referenced from INPUT/OUTPUT/FORWARD
iptables -C INPUT   -j "$CHAIN" 2>/dev/null || iptables -I INPUT   1 -j "$CHAIN"
iptables -C OUTPUT  -j "$CHAIN" 2>/dev/null || iptables -I OUTPUT  1 -j "$CHAIN"
iptables -C FORWARD -j "$CHAIN" 2>/dev/null || iptables -I FORWARD 1 -j "$CHAIN"

echo "$(date): Updated $(wc -l < "$BLOCKLIST") blocked IPs"

Add to cron

bash
# Every 6 hours
0 */6 * * * /opt/ismalicious/blocklist_update.sh >> /var/log/ismalicious_blocklist.log 2>&#x26;1

Step 3: pfSense with pfBlockerNG

pfBlockerNG cannot send the X-API-KEY header. Use a cron job on the firewall (or a jump host) to download with curl and Base64 credentials, then point pfBlockerNG at the local file (see blocklist guide):

bash
curl -fsS -H "X-API-KEY: $(echo -n 'KEY:SECRET' | base64)" \
  "https://api.ismalicious.com/blocklist/download/blocklist-ips-all.txt" \
  -o /var/db/pfblockerng/ismalicious-ips.txt

Step 4: AWS Security Group Automation

python
import boto3
from fetch_ips import fetch_malicious_ips

ec2 = boto3.client("ec2", region_name="us-east-1")
SG_ID = "sg-XXXXXXXXXXXXXXXXX"

def update_security_group():
    ips = fetch_malicious_ips()
    
    # Remove all existing deny rules
    sg = ec2.describe_security_groups(GroupIds=[SG_ID])["SecurityGroups"][0]
    if sg.get("IpPermissionsEgress"):
        ec2.revoke_security_group_egress(
            GroupId=SG_ID,
            IpPermissions=sg["IpPermissionsEgress"]
        )
    
    # Add new deny rules in batches of 50 (AWS limit)
    ip_ranges = [{"CidrIp": f"{ip}/32", "Description": "isMalicious block"} for ip in ips[:50]]
    if ip_ranges:
        ec2.authorize_security_group_egress(
            GroupId=SG_ID,
            IpPermissions=[{
                "IpProtocol": "-1",
                "IpRanges": ip_ranges
            }]
        )
    
    print(f"Updated {len(ip_ranges)} blocked IPs in {SG_ID}")

if __name__ == "__main__":
    update_security_group()

Step 5: Palo Alto Networks

PAN-OS EDL URLs cannot send X-API-KEY. Host the list yourself: cron downloads blocklist-ips-all.txt (or critical) via curl + Base64 credentials to an internal HTTPS server or object storage, then point the EDL at that URL.


Validation

After deploying, verify a known malicious IP is blocked:

bash
# Test connectivity to a known malicious IP (should timeout/be blocked)
curl --connect-timeout 3 http://185.220.101.1/ || echo "Blocked successfully"

# Check iptables hit count
iptables -L ISMALICIOUS_BLOCK -v -n | head -20

Monitoring & Alerting

Log every blocked connection for SOC review:

bash
# Log instead of silently drop (for SIEM ingestion)
iptables -A ISMALICIOUS_BLOCK -s "$ip" -j LOG --log-prefix "ISMALICIOUS_BLOCKED: "
iptables -A ISMALICIOUS_BLOCK -s "$ip" -j DROP

Route these logs to your SIEM and create an alert rule for high-frequency hits (potential active attack).

Did this playbook work for you?

Protect Your Infrastructure

Enrich your detections with real-time threat intelligence from 500M+ records.