Snort 2.9.x ist tot. Nicht “wird eingestellt” — tot. Der pfSense-Package-Manager zeigt noch munter snort-2.9.20_8 an, aber die Regeln werden nicht mehr gepflegt und VRT Subscriber-Regeln für 2.9 sind längst eingefroren.
Suricata ist der logische Nachfolger. Aber die Migration ist nicht so trivial wie “Suricata installieren, Snort deinstallieren, fertig” — zumindest nicht wenn du die Konfiguration nicht manuell durch 47 GUI-Screens klicken willst.
Hier ist wie ich es auf einer pfSense 2.8.1 (FreeBSD) vollautomatisch via PHP-API erledigt habe.
Ausgangslage
- pfSense 2.8.1-RELEASE (FreeBSD), cloud VPS
- Snort 2.9.20_8 im IDS-Mode (passiv, kein Inline)
- ET Open + Snort VRT Subscriber Regeln (Oinkcode vorhanden)
- Bestehende Suppress-Einträge die migriert werden müssen
- WAN Interface: virtio NIC (
vtnet0)
Ziel: Suricata im Legacy IPS Mode auf WAN, Block Offenders aktiv, Suppress-Liste migriert, 0 Klicks in der GUI.
Warum Legacy IPS und kein Inline Mode?
Kurze Antwort: VPS + virtio NIC = kein Netmap.
Inline IPS auf pfSense nutzt DPDK/Netmap für kernel-bypass — das braucht hardware-native NIC-Treiber. Virtio-NICs auf Hetzner, DigitalOcean & Co. unterstützen das nicht. Wer es trotzdem versucht, riskiert kompletten Netzwerkausfall.
Legacy IPS (pcap-basiert) ist die richtige Wahl für VPS-Umgebungen. Suricata liest Pakete via libpcap, schreibt verdächtige Quell-/Ziel-IPs in die pf-Tabelle snort2c, und pfSense blockiert über normale Firewall-Regeln. Kein Netmap, kein Kernel-Bypass, kein Drama.
Schritt 1: Suricata installieren + Regelwerk runterladen
GUI: System → Package Manager → Available Packages → suricata → Install
Dann Global Settings konfigurieren:
| Einstellung | Wert |
|---|---|
| Install ETOpen Emerging Threats rules | ✅ |
| Install Snort VRT rules | ✅ |
| Snort Oinkmaster Code | <DEIN-OINKCODE> |
| Update Interval | 12 Hours |
| Update Start Time | 00:30 |
| Live Rule Swap on Update | ✅ |
→ Update klicken und Regelwerk einmalig runterladen (dauert 1-3 Min).
Das war der einzige GUI-Klick in dieser Anleitung. Ab jetzt: PHP.
Schritt 2: Suppress-Liste migrieren
Das gute zuerst: Das Suppress-Format ist zwischen Snort und Suricata identisch. Kein Umschreiben, kein Konvertieren.
# Von Snort exportieren:
ssh root@<DEIN-GATEWAY>
cat /usr/local/etc/snort/snort_*/threshold.conf
Suricata erwartet die Suppress-Liste allerdings in der config.xml, base64-encoded. Das per PHP erledigen:
<?php
require_once("config.inc");
$config = parse_config(true);
$suppress_content = <<<'SUPPRESS'
# HTTP-Inspect Noise
suppress gen_id 120, sig_id 3
# Bekannter Scanner
suppress gen_id 1, sig_id 2003068, track by_src, ip 192.168.66.42
# Komplett supprimieren
suppress gen_id 1, sig_id 2100498
SUPPRESS;
$uuid = uniqid("suppress_");
$item = [
"uuid" => $uuid,
"name" => $uuid,
"descr" => "Migriert von Snort",
"suppresspassthru" => base64_encode($suppress_content),
];
if (!isset($config['installedpackages']['suricata']['suppress']['item'])) {
$config['installedpackages']['suricata']['suppress'] = ['item' => []];
}
$config['installedpackages']['suricata']['suppress']['item'][] = $item;
// WAN Interface auf neue Liste setzen
foreach ($config['installedpackages']['suricata']['rule'] as &$rule) {
if ($rule['interface'] === 'wan') {
$rule['suppresslistname'] = $uuid;
}
}
unset($rule);
write_config("Suricata: Suppress-Liste migriert");
echo "UUID: $uuid\n";
?>
# Ausführen:
php /tmp/suricata_suppress.php
Schritt 3: Interface + Rulesets konfigurieren
Das ist wo die meiste Arbeit steckt — und die meisten Fallen lauern.
<?php
require_once("config.inc");
require_once("suricata/suricata_defs.inc"); // ← PFLICHT
require_once("suricata/suricata.inc"); // ← PFLICHT (nicht services.inc!)
$config = parse_config(true);
// Backup
copy("/conf/config.xml", "/conf/config.xml.bak." . date("Ymd_His"));
$et_rules = [
"emerging-attack_response.rules",
"emerging-botcc.rules",
"emerging-compromised.rules",
"emerging-current_events.rules",
"emerging-dos.rules",
"emerging-dshield.rules",
"emerging-drop.rules",
"emerging-exploit.rules",
"emerging-malware.rules",
"emerging-phishing.rules",
"emerging-scan.rules",
"emerging-shellcode.rules",
"emerging-sql.rules",
"emerging-tor.rules",
"emerging-web_client.rules",
"emerging-web_server.rules",
"emerging-worm.rules",
];
$snort_rules = [
"snort_botnet-cnc.rules",
"snort_dos.rules",
"snort_exploit.rules",
"snort_malware-cnc.rules",
"snort_malware-other.rules",
"snort_scan.rules",
"snort_server-webapp.rules",
"snort_sql.rules",
"snort_virus.rules",
"snort_web-attacks.rules",
];
$existing = explode("||", $config['installedpackages']['suricata']['rule'][0]['rulesets'] ?? "");
$all_rules = array_unique(array_merge($existing, $et_rules, $snort_rules));
$all_rules = array_filter($all_rules);
sort($all_rules);
foreach ($config['installedpackages']['suricata']['rule'] as &$rule) {
if ($rule['interface'] === 'wan') {
$rule['rulesets'] = implode("||", $all_rules);
$rule['killstates'] = 'on';
$rule['blockip'] = 'both';
$rule['enable'] = 'on';
echo "SET: " . count($all_rules) . " Rulesets, killstates=on, blockip=both\n";
}
}
unset($rule);
write_config("Suricata WAN: Rulesets gesetzt");
// ← DAS ist die kritische Zeile (siehe Fallen unten)
$rebuild_rules = true;
sync_suricata_package_config();
echo "DONE\n";
?>
php /tmp/suricata_finalize.php
Die drei Fallen
Falle 1: Falsche include-Dateien
// FALSCH (nicht laden was nicht existiert):
require_once("suricata/suricata_generate_yaml.inc");
// RICHTIG:
require_once("suricata/suricata_defs.inc");
require_once("suricata/suricata.inc");
suricata_generate_yaml() ist direkt in suricata.inc enthalten. Es gibt keine eigene Datei dafür. Diesen Fehler zu debuggen kostet Zeit — der PHP-Error sagt nur “No such file” und Suricata läuft trotzdem.
Falle 2: Regeln werden nicht neu generiert
Nach write_config() ist die config.xml aktualisiert — aber Suricata nutzt sein eigenes Rule-File /var/db/suricata/suricata_vtnet0.rules. Das wird nicht automatisch aktualisiert.
// FALSCH (Regeln bleiben alt):
write_config("...");
// RICHTIG (Regeln neu generieren):
write_config("...");
$rebuild_rules = true;
sync_suricata_package_config();
Ohne $rebuild_rules = true und sync_suricata_package_config() hatte ich 389 Zeilen in der Rules-Datei (nur die internen Event-Rules) statt 39.000+. Suricata startet problemlos, erkennt aber fast nichts.
Falle 3: HOME_NET Dropdown ist leer
Suricata auf pfSense hat einen “IP List” Mechanismus für Reputation-Listen. Das ist nicht das gleiche wie HOME_NET. Wenn du eine eigene IP-Liste für HOME_NET erstellst, taucht sie im Dropdown nicht auf.
Lösung: Das default-Setting lassen. pfSense befüllt HOME_NET automatisch aus den konfigurierten Interface-Netzwerken. Wenn du zusätzliche Netze brauchst, fügst du sie als Custom IP List im Suricata Global Settings Reputation-Bereich hinzu — die fließen dann automatisch in default ein.
Schritt 4: Umschalten
# Snort stoppen
pkill -x snort
# oder: /usr/local/etc/rc.d/snort.sh stop
# Suricata starten (via PHP)
php -r "
require_once('config.inc');
require_once('suricata/suricata_defs.inc');
require_once('suricata/suricata.inc');
\$config = parse_config(true);
suricata_start(\$config['installedpackages']['suricata']['rule'][0]);
echo 'Started\n';
"
# Läuft?
ps aux | grep suricata | grep -v grep
Detection-Gap: ~10-30 Sekunden. Kein Traffic-Ausfall, nur die IDS-Überwachung pausiert kurz.
Verifikation
# Regeln geladen?
grep "rules loaded" /var/log/suricata/suricata_vtnet0*/suricata.log
# Pakete werden dekodiert?
tail -5 /var/log/suricata/suricata_vtnet0*/stats.log | grep decoder.pkts
# Test-Alert generieren:
curl -s http://testmynids.org/uid/index.html > /dev/null
# Alert geloggt? (SID 2100498 = NIDS Test Rule)
grep "2100498" /var/log/suricata/suricata_vtnet0*/eve.json | tail -3
Nach wenigen Minuten tauchen echte Alerts auf — TOR Exit Nodes, Dshield Blocklist, Spamhaus. Das EVE JSON Format ist deutlich besser als Snorts Log-Format und lässt sich prima mit jq auswerten:
# Top 10 Angreifer-IPs:
cat /var/log/suricata/suricata_vtnet0*/eve.json | \
jq -r 'select(.event_type=="alert") | .src_ip' | \
sort | uniq -c | sort -rn | head -10
Rollback (falls nötig)
Snort bleibt installiert — das ist Absicht. Erst nach 7 Tagen Burn-In deinstallieren.
# Sofort-Rollback:
pkill suricata
/usr/local/etc/rc.d/snort.sh start
ps aux | grep -E "snort|suricata" | grep -v grep
Lessons Learned
sync_suricata_package_config()mit$rebuild_rules = trueist nicht optional. Ohne diese Zeile läuft Suricata mit minimalem Ruleset und du merkst es erst wenn du die Regelanzahl prüfst.Legacy IPS ist die richtige Wahl für VPS. Inline/Netmap klingt besser, ist es aber nicht wenn die Hardware (virtio NIC) es nicht unterstützt.
Das Suppress-Format ist 1:1 kompatibel. Snort
threshold.conf→ Suricata suppress → kein Umschreiben nötig.PHP statt GUI spart Stunden. Die Suricata-GUI auf pfSense hat Dutzende Pflichtfelder mit Placeholdern die man einzeln befüllen muss. Via PHP sind es 50 Zeilen Code die alles setzen.
Burn-In einhalten. 7 Tage mit beiden Systemen installiert (Suricata aktiv, Snort gestoppt) bevor Snort deinstalliert wird. Falsch-Positiv-Rate muss erst kalibriert werden.