Privacy Tools Guide

How to Set Up Suricata in IPS Mode

Suricata in IDS mode logs alerts but lets traffic through. IPS mode uses the Linux kernel’s NFQUEUE to intercept packets before delivery, letting Suricata drop matching traffic in real time. This guide covers the NFQUEUE setup, inline rule tuning, and fail-open handling so a Suricata crash never takes down your network.

IDS vs IPS Mode

Mode Mechanism Traffic Impact
IDS (pcap/af-packet) Passive copy None — read-only
IPS (NFQUEUE) Kernel intercepts; Suricata decides verdict Blocks matching traffic

IPS mode adds latency (0.5–2 ms typical) and a new failure mode: if Suricata crashes, traffic stops unless you configure fail-open.


1. Install Suricata

sudo add-apt-repository ppa:oisf/suricata-stable
sudo apt update && sudo apt install -y suricata suricata-update

suricata --version

2. Configure NFQUEUE

NFQUEUE is a netfilter target that holds packets in a kernel queue until an userspace process accepts or drops them. Add rules to send traffic through Suricata:

# IPS on a forwarding host (router/firewall)
sudo iptables -I FORWARD -j NFQUEUE --queue-num 0 --queue-bypass

# IPS on the host itself (protect this server)
sudo iptables -I INPUT  -j NFQUEUE --queue-num 0 --queue-bypass
sudo iptables -I OUTPUT -j NFQUEUE --queue-num 0 --queue-bypass

# IPv6
sudo ip6tables -I INPUT  -j NFQUEUE --queue-num 0 --queue-bypass
sudo ip6tables -I OUTPUT -j NFQUEUE --queue-num 0 --queue-bypass

# --queue-bypass is critical for fail-open:
# if Suricata is not running, the kernel passes traffic without inspection
# REMOVE --queue-bypass if you want fail-CLOSED (drop all traffic when Suricata is down)

Make iptables rules persistent:

sudo apt install -y iptables-persistent
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6

3. Configure Suricata for IPS Mode

# /etc/suricata/suricata.yaml — relevant sections only

# IPS via NFQUEUE
nfq:
  mode: accept          # accept packets by default; rules drop specific ones
  batchcount: 20        # packets to batch per syscall (performance tuning)
  fail-open: true       # if Suricata queue is full, let packets through

# Disable IDS interfaces (we're using NFQUEUE, not a NIC)
af-packet:
  - interface: default
    # comment out or set enabled: false

# Set default action for rules to 'drop' instead of 'alert' for IPS
# (optional — controlled per-rule with 'drop' keyword)
# action-order:
#   - drop
#   - pass
#   - alert

# Thread count for NFQUEUE workers
threading:
  set-cpu-affinity: yes
  cpu-affinity:
    - management-cpu-set:
        cpu: [0]
    - receive-cpu-set:
        cpu: [0]
    - worker-cpu-set:
        cpu: [1, 2, 3]

4. Download and Update Rules

# suricata-update manages rulesets
sudo suricata-update

# Add Emerging Threats Open (free, high quality)
sudo suricata-update add-source et/open \
  --url https://rules.emergingthreats.net/open/suricata-6.0/emerging.rules.tar.gz

# Add ProofPoint ET Pro (paid, latest signatures)
# sudo suricata-update add-source etpro/etpro --secret-code YOUR_CODE

# Update all sources
sudo suricata-update --no-reload

# Reload rules without restarting Suricata
sudo kill -USR2 $(cat /var/run/suricata.pid)

5. Start Suricata in IPS Mode

# IPS mode: -q 0 = NFQUEUE 0
sudo suricata -c /etc/suricata/suricata.yaml -q 0 --pidfile /var/run/suricata.pid -D

sudo tail -f /var/log/suricata/suricata.log
# Look for: "All threads are running"

Systemd service for IPS mode:

# /etc/systemd/system/suricata-ips.service
[Unit]
Description=Suricata IPS (NFQUEUE mode)
After=network.target

[Service]
Type=simple
ExecStartPre=/sbin/iptables -I INPUT -j NFQUEUE --queue-num 0 --queue-bypass
ExecStartPre=/sbin/iptables -I OUTPUT -j NFQUEUE --queue-num 0 --queue-bypass
ExecStart=/usr/bin/suricata -c /etc/suricata/suricata.yaml -q 0
ExecStopPost=/sbin/iptables -D INPUT -j NFQUEUE --queue-num 0 --queue-bypass
ExecStopPost=/sbin/iptables -D OUTPUT -j NFQUEUE --queue-num 0 --queue-bypass
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now suricata-ips

6. Verify Drops Are Working

# Check stats — blocked packet count
sudo suricatasc -c "dump-counters" | python3 -m json.tool | grep -i drop

# Or via stats log
sudo tail /var/log/suricata/stats.log | grep drop

# View fast alerts (drops show as [Drop])
sudo tail -f /var/log/suricata/fast.log

Test with a known-bad signature:

# EICAR test string — triggers AV/IPS rules
curl -s http://www.eicar.org/download/eicar.com 2>&1 || echo "Blocked"
# If IPS is working: connection refused or empty response

7. Write IPS Drop Rules

Use drop instead of alert to actively block traffic:

sudo tee /etc/suricata/rules/local.rules > /dev/null <<'EOF'
# Drop Telnet connections
drop tcp any any -> $HOME_NET 23 \
  (msg:"IPS: DROP Telnet"; flow:to_server; sid:9100001; rev:1;)

# Drop known C2 domain (replace with actual IOC)
drop dns any any -> any any \
  (msg:"IPS: DROP known malware C2"; dns.query; \
   content:"badactor.example.com"; nocase; \
   sid:9100002; rev:1;)

# Drop large ICMP packets (ping-of-death signature)
drop icmp any any -> $HOME_NET any \
  (msg:"IPS: DROP large ICMP"; dsize:>1000; \
   sid:9100003; rev:1;)

# Rate-limit SSH: drop if >10 SYNs in 30s from same source
drop tcp any any -> $HOME_NET 22 \
  (msg:"IPS: SSH brute force drop"; flags:S; \
   threshold:type threshold, track by_src, count 10, seconds 30; \
   sid:9100004; rev:1;)
EOF

Add to suricata.yaml under rule-files:

rule-files:
  - suricata.rules
  - /etc/suricata/rules/local.rules

8. Tune False Positives: Convert drop to alert

When a drop rule fires on legitimate traffic, suppress it or convert to alert while you investigate:

# Suppress a specific rule for an internal host
sudo tee /etc/suricata/threshold.conf > /dev/null <<'EOF'
# Suppress rule 2001219 for the monitoring server
suppress gen_id 1, sig_id 2001219, track by_src, ip 10.0.0.50

# Rate-limit an alert to once per hour per source
threshold gen_id 1, sig_id 2012648, type limit, track by_src, count 1, seconds 3600
EOF

In suricata.yaml:

threshold-file: /etc/suricata/threshold.conf

9. Monitor Alerts in EVE JSON

Suricata’s EVE JSON log is the richest output format:

# View live drops
sudo tail -f /var/log/suricata/eve.json \
  | python3 -c "
import sys, json
for line in sys.stdin:
    e = json.loads(line)
    if e.get('event_type') == 'alert' and e.get('alert', {}).get('action') == 'blocked':
        a = e['alert']
        print(f\"{e['timestamp']} DROP [{a['signature_id']}] {a['signature']} \
{e.get('src_ip')}:{e.get('src_port')} -> {e.get('dest_ip')}:{e.get('dest_port')}\")
"

Multi-Queue Performance Scaling

For high-throughput (>1 Gbps) networks:

# Use multiple NFQUEUE queues (0–3) with load balancing
sudo iptables -I FORWARD -j NFQUEUE --queue-balance 0:3 --queue-bypass

# Start Suricata with 4 workers
sudo suricata -c /etc/suricata/suricata.yaml \
  -q 0 -q 1 -q 2 -q 3 -D


Built by theluckystrike — More at zovo.one