mef (Malware.Expert Firewall) is a lightweight Linux firewall and multi-layer automatic IP ban engine.
It combines:
- Persistent firewall management (nftables preferred, iptables fallback)
- Fail2ban-style log monitoring and automatic IP blocking
- Built-in DDoS/FLOOD guard (Stage 1 kernel
THROTTLE, Stage 2 optional runtimeBAN) - DNS-based RBL/DNSBL enforcement
- Optional Community Cloud threat intelligence
- Built-in Port Scan Detection (PS)
- Native systemd / FreeBSD service integration
Designed as a simple, modern alternative to UFW, CSF and Fail2Ban stacks, with additional DNS and community-driven protection layers.
mef consists of two standalone services that work together or separately:
- Loads persistent firewall policy rules from
/etc/mef/mef.rules - Runs once at boot (oneshot service)
- Use this if you want permanent firewall rules
- Works with nftables or iptables backends
- Monitors logs (journal/files) for failed authentication attempts
- Automatically bans offending IPs (fail2ban-style)
- Runs continuously as a daemon
- Per-service rules in
/etc/mef/rules.d/*.conf
Both services are independent - you can use:
- Both together (firewall + auto-banning)
- Only mefdaemon (fail2ban replacement)
- Only mef (persistent firewall rules)
- Firewall management without complex rule syntax (
mef.rules) - Automatic IP bans from logs (SSH, Postfix, etc.)
- Two-stage DDoS/FLOOD protection (
THROTTLEin kernel, optional escalation to runtimeBAN) - Nftables first, iptables fallback
- Lightweight and scalable, no libsystemd dependency
- Servers that want a simple and clear firewall + auto‑ban setup
- A lightweight alternative to UFW/CSF/Fail2Ban‑style stacks
- Journal and file log inputs (
source=journal/file) - One clear config per service (no separate jail/filter)
- Bans via nftables/iptables backends
- Built-in Port Scan Detection (PS) with packet/conntrack/journal sources
- Built-in DDoS/FLOOD guard (
THROTTLEepisodes from kernel set + optional Stage 2 runtimeBAN) - RBL/DNSBL profiles with port-scoped bans
- Configurable RBL cache TTLs (
positive_ttl,negative_ttl,error_ttl) - Optional Community Cloud protection (
community_cloud_protection) - Safe defaults, easy install
mef is split into two independent components:
- mef → firewall rules loader (persistent policy service)
- mefdaemon → log parser + dynamic IP ban engine
Firewall backends:
- nftables (primary)
- iptables (fallback)
The daemon maintains in-memory IP sets and updates firewall rules dynamically.
Runtime timeout bans (mefbanned_*)
- nftables: Yes
- iptables: Yes
- Notes: Both are kernel-enforced.
Permanent blacklist sync (/etc/mef/blacklist/*.conf)
- nftables: Yes
- iptables: Yes
- Notes: Same source-of-truth files, different backend implementation.
Per-rule dynamic ban sets (banned_<rule>_*)
- nftables: Yes
- iptables: Yes
- Notes: Created on demand when rules trigger.
Two-stage DDoS guard (ddos_*)
- nftables: Yes
- iptables: Yes (requires ipset/SET target support)
- Notes: Stage 2 escalation source is kernel throttle set state (not firewall log parsing).
Fastpath (blacklist_fastpath)
- nftables: Yes (Linux, nft
netdevtable) - iptables: No
- Notes: Requires
firewall_backend=nftables.
mefctl bans list --fastpath
- nftables: Yes
- iptables: No
- Notes: Command is nft-only.
source=ipset:<name> in mef.rules
- nftables: No
- iptables: Yes
- Notes: Native nft backend does not support external ipset source references.
mefctl rules clear --all
- nftables: Yes
- iptables: No
- Notes:
--allis nft-table destructive clear.
mefctl rules migrate
- nftables: Yes
- iptables: Limited
- Notes: Current migrate flow targets nft rulesets.
Notes:
- On many modern distros,
iptablesmay run iniptables-nftcompat mode. That is still treated asiptablesbackend behavior in mef. - For new distro targets, prefer
firewall_backend=nftables; keepiptablesas compatibility fallback.
Use these as known-good combinations:
A) Full feature set (recommended)
firewall_backend=nftablesps_source_order=packet,conntrack,journalps_packet_kernel_drop=trueblacklist_fastpath=auto(ortc/xdp)ddos_enabled=true- Result: packet visibility + blocked-IP optimization + optional ingress fastpath.
B) Lower PS CPU usage
firewall_backend=nftablesps_source_order=conntrack,journalblacklist_fastpath=auto(optional)- Result: lighter userspace path, but less scan visibility than
packet.
C) Compatibility mode
firewall_backend=iptablesps_source_order=packet,conntrack,journal(or subset)blacklist_fastpath=disabled(effective behavior on iptables backend)ddos_enabled=true(requires ipset + SET target)- Result: core mef features work, fastpath is not active.
D) Minimal fallback
firewall_backend=nftablesoriptablesps_source_order=journalfirewall_log_enabled=true- Result: PS/RBL fallback via firewall logs only.
Important behavior notes:
blacklist_fastpath=auto|xdp|tcrequires Linux +firewall_backend=nftables.ps_packet_kernel_droponly affects runtime whenpacketexists inps_source_order.ps_packet_rx_mode=recvfromis the default for stable legacy behavior;autois available as opt-in.ps_packet_fanout_sockets>1applies only topacketsource and opens multiple packet sockets per interface.mefctl bans list --fastpathworks only with nftables backend on Linux.ps_source_orderis shared by both PS and RBL/CLOUD source events.- if
rbl_enabled=true, changing onlyps_enabled=falsedoes not stop packet-source event collection. - with
ps_source_order=conntrackandps_exclude_ports=auto, PS visibility can drop sharply because local listening service ports are auto-excluded from counting. firewall_log_enabled=trueis rate-limited byfirewall_log_rate/firewall_log_burstto prevent kernel/journal overload under flood.- firewall LOG limits are applied per firewall LOG rule (not per source IP).
Use this section when validating upgrades or incident cleanup.
Symptom: high %si / ksoftirqd / journald load under flood when firewall logging is enabled
- Root cause: firewall
LOGrules were not rate-limited, so dropped packet floods could generate excessive kernel log volume. - Fix: logging now uses token-bucket limits (
firewall_log_rate,firewall_log_burst) in nftables and iptables paths; DROP remains unconditional.
Symptom: DDOS stage event lines visible in daemon journal
- Root cause: DDOS stage notices were written via daemon logger path.
- Fix: DDOS stage event notices are no longer emitted as daemon journal notice lines; event records stay in
ban_logand optionaldebug_log.
Symptom: DDOS BAN observability inconsistencies
- Root cause: Stage 2 emitted
BANbefore apply success was guaranteed; stage1 throttle logging could continue for already runtime-banned IP. - Fix:
BANis emitted only after successful apply; apply failures emitBAN_FAIL; duplicate stage1THROTTLEfor active runtime DDOS bans is suppressed.
Symptom: mefctl pinned update version fails due metadata URL mismatch
- Root cause: strict
updates.jsonasset URL version segment validation blocked valid requested-version installs. - Fix: mismatched metadata asset URL is treated as non-fatal and updater falls back to computed
vX.Y.Zraw tag URL.
Symptom: packet source high CPU under heavy PPS
- Root cause: packet parser hot path parsed packet content twice and allocated event strings too early.
- Fix: single-pass parser path (PS-005) with deferred event/raw string build after blocked-source early check.
Symptom: mmap packet source could amplify/unintentionally include outgoing frames on some kernels
- Root cause: missing
PACKET_IGNORE_OUTGOINGsupport in certain kernel environments. - Fix: mmap mode now fails/falls back in
automode to prevent unsafe outgoing-frame behavior.
When ps_source_order includes packet, mefdaemon reads traffic via Linux AF_PACKET raw socket.
packetsource receives a packet copy in userspace.- Kernel firewall (
nft/iptables) still enforces drop/accept in netfilter. - Because of the socket copy path, packet source can still consume CPU under flood even when source IP is already banned.
Controls and behavior:
ps_packet_kernel_drop=true(default): enables packet-source blocked-IP optimization path (kernel socket filter + early userspace skip).ps_packet_kernel_drop=false: packet source runs without blocked-IP kernel-drop optimization.ps_packet_rx_mode=auto|recvfrom|mmapcontrols packet receive implementation:auto: try PACKET_MMAP/TPACKET_V3 first, fallback to recvfrommmap: require ring path (startup fails if unsupported)recvfrom: force classic recvfrom loop
ps_packet_fanout_sockets=1..64controls packet socket parallelism per interface (>1enables PACKET_FANOUT hash split).blacklist_fastpath(auto|xdp|tc|disabled) is a separate ingress fastpath layer and independent fromps_packet_kernel_drop.
Practical guidance:
- Highest flood resilience:
firewall_backend=nftables, keepps_packet_kernel_drop=true, and enable fastpath (blacklist_fastpath=auto/tc). - Lowest userspace PS CPU (with tradeoff in scan visibility): use
ps_source_order=conntrack,journal. - If packet CPU is high during DDOS tests, move
ps_source_orderaway frompacket(for exampleconntrack,journal) or disable RBL/CLOUD while testing pure DDOS stage behavior. - If you use
conntracksource and need stronger PS detection, avoid broadps_exclude_ports=auto; use tighter manual excludes.
ddos_enabled=true activates two-stage protection:
- Stage 1 (kernel soft throttle):
- backend-native per-port limits (
syn_rate,newconn_rate,connlimit) - mode is controlled by
ddos_stage1_mode:throttle: only over-limit packets/connections are droppeddrop: once source enters throttle set, all traffic from that source is dropped untilddos_stage1_timeout
- source IP is written into throttle kernel set on limit hit
- daemon emits
THROTTLElog events for new throttle episodes (RULE=DDOS,SOURCE=KERNEL) - DDOS event records are written to
ban_log(default/var/log/mef.log) and optionaldebug_log; they are not emitted as daemon journal notice lines THROTTLE/BANmsgincludes active Stage 1 trigger details when available:- protected destination port (
<metric>@<port>) - exceeded limit family (
syn,newconn,connlimit) - configured threshold values (
ddos_syn_rate,ddos_syn_burst,ddos_newconn_rate,ddos_newconn_burst,ddos_connlimit)
- protected destination port (
throttle_hitsis an episode counter (hit_model=episodes), not raw packet count:- increments when source re-enters Stage 1 state (set transition absent -> present)
- does not increment for every over-limit packet while source stays continuously in throttle set
- Stage 2 (daemon hard-ban):
- daemon polls throttle kernel sets (IPv4/IPv6)
- poll cadence is controlled by
ddos_poll_interval(default5s) - controlled by
ddos_escalation_enabled:true: Stage 2 runtime DDOS bans are allowedfalse: throttle/drop only mode (no DDOS runtime bans)
- repeated throttle episodes within
ddos_escalation_windowescalate to normal runtime ban flow (RULE=DDOS,SOURCE=KERNEL) BANis logged only after runtime ban apply succeeds- apply failures are logged as
BAN_FAIL(RULE=DDOS,SOURCE=KERNEL) - while DDOS runtime ban is active, new
THROTTLElogs for that same IP are suppressed - when
community_report=true, only successful DDOSBANevents are reported (service=ddos) - DDOS
THROTTLE/BAN_FAILevents are local-only and never community-reported - detail trigger context collection is controlled by
ddos_signal_details_enabled:true(default): includes metric+port trigger context in DDOSmsgfalse: skips detail-set writes/reads to lower overhead, logs showsignals=disabled
Recommended enable pattern:
- keep
ddos_enabled=falseuntil tuned for your traffic profile - protect only publicly exposed ports
- default baseline is
ddos_ports=80,443 - add ports like
22,25,465,587only when those services are internet-facing
Config keys:
ddos_portsddos_syn_rate,ddos_syn_burstddos_newconn_rate,ddos_newconn_burstddos_connlimitddos_stage1_modeddos_stage1_timeoutddos_poll_intervalddos_signal_details_enabledddos_escalation_enabledddos_escalation_window,ddos_escalation_hits,ddos_bantime- optional per-port overrides:
ddos_port_<port>_*
Units and scope:
ddos_syn_rate: per second (1/s), per source IP, per protected destination portddos_newconn_rate: per second (1/s), per source IP, per protected destination portddos_syn_burst,ddos_newconn_burst: burst allowance count (no time unit)ddos_connlimit: concurrent connection count per source IP (no time unit)ddos_stage1_mode: Stage 1 action (throttle= drop only over-limit traffic,drop= temporary full drop while in throttle set)ddos_stage1_timeout: Stage 1 throttle-set timeout durationddos_poll_interval: Stage 2 kernel-set poll interval (5sdefault; higher lowers CPU overhead)ddos_signal_details_enabled: enable/disable per-hit detail-set context (falsereduces overhead, DDOS stays active)ddos_escalation_enabled: Stage 2 toggle (true= escalate to DDOS runtime ban,false= throttle/drop only)ddos_escalation_window: duration (for example10m,1h)ddos_escalation_hits: number of throttle episodes withinddos_escalation_windowddos_bantime: Stage 2 ban duration, orpermanentfor persistent blacklist ban
cd /tmp/mef-release
chmod +x install.sh update.sh uninstall.sh
sudo ./install.sh
# After install - both services are DISABLED by default
# Test firewall rules first:
mefctl rules validate
mefctl rules apply # 30-second rollback window, type "yes" if SSH works
# When SSH access is verified, enable services:
mefctl enable mef # Enable firewall rules at boot
mefctl enable mefdaemon # Enable auto-banning at boot
# Or enable both: mefctl enable
# Verify they're enabled:
mefctl statusFor uninstall:
sudo ./uninstall.shLegacy update script (for older installs without mefctl update):
sudo ./update.sh
sudo ./update.sh --force
sudo ./update.sh --version 1.0.8 --force# Replace URL/version as needed
curl -L -o /tmp/mef-release.tar.gz https://github.com/jwillberg/mef/archive/refs/tags/v1.0.8.tar.gz
mkdir -p /tmp/mef-release
tar -xzf /tmp/mef-release.tar.gz -C /tmp/mef-release --strip-components=1
cd /tmp/mef-releasesudo mkdir -p /usr/local/sbin /etc/mef /etc/mef/rules.d /etc/mef/whitelist /etc/mef/blacklist /etc/mef/cache
sudo cp -f bin/mefdaemon /usr/local/sbin/mefdaemon
sudo cp -f bin/mefctl /usr/local/sbin/mefctl
sudo chmod 0755 /usr/local/sbin/mefdaemon /usr/local/sbin/mefctl
sudo cp -n conf/mef.conf /etc/mef/mef.conf
sudo cp -n conf/mef.rules /etc/mef/mef.rules
sudo cp -n conf/rules.d/*.conf /etc/mef/rules.d/
sudo cp -n conf/whitelist/example.conf /etc/mef/whitelist/example.conf
sudo cp -n conf/blacklist/example.conf /etc/mef/blacklist/example.conf
# Install both services (both DISABLED by default)
sudo cp -f services/systemd/mef.service /etc/systemd/system/mef.service
sudo cp -f services/systemd/mefdaemon.service /etc/systemd/system/mefdaemon.service
sudo systemctl daemon-reload
# Both services are disabled by default to prevent lockout
# Test firewall rules first:
mefctl rules validate
mefctl rules apply # 30-second rollback window, type "yes" if SSH works
# When SSH access is verified, enable services:
mefctl enable mef # Enable firewall rules at boot
mefctl enable mefdaemon # Enable auto-banning at boot
# Verify they're enabled:
mefctl statussudo install -d /usr/local/sbin /etc/mef /etc/mef/rules.d /etc/mef/whitelist /etc/mef/blacklist /etc/mef/cache
sudo install -m 0755 bin/mefdaemon /usr/local/sbin/mefdaemon
sudo install -m 0755 bin/mefctl /usr/local/sbin/mefctl
sudo install -m 0644 conf/mef.conf /etc/mef/mef.conf
sudo install -m 0644 conf/mef.rules /etc/mef/mef.rules
sudo install -d /etc/mef/rules.d /etc/mef/whitelist /etc/mef/blacklist
sudo install -m 0644 conf/rules.d/*.conf /etc/mef/rules.d/
sudo install -m 0644 conf/whitelist/example.conf /etc/mef/whitelist/example.conf
sudo install -m 0644 conf/blacklist/example.conf /etc/mef/blacklist/example.conf
sudo install -m 0755 services/freebsd/mef /usr/local/etc/rc.d/mef
sudo install -m 0755 services/freebsd/mefdaemon /usr/local/etc/rc.d/mefdaemon
# Both services are disabled by default to prevent lockout
# Test firewall rules first:
mefctl rules validate
mefctl rules apply # 30-second rollback window, type "yes" if SSH works
# When SSH access is verified, enable services:
mefctl enable mef # Enable firewall rules at boot
mefctl enable mefdaemon # Enable auto-banning at boot
# Verify they're enabled:
mefctl statusmefctl rules <action> [FILE] # default FILE: /etc/mef/mef.rules
fmt # Normalize/pretty-print rules file
validate # Validate syntax + interfaces (recommended before apply)
apply # Apply rules to firewall
clear # Remove mefctl rules
migrate # Export running nftables table to mef.rules format
clear --all # Delete whole nftables table (DANGEROUS)
clear --all --force # Skip confirmation
mefctl bans <action>
list # List current bans grouped by category/set
# Use --ddos for kernel DDoS throttle sets, --fastpath for fastpath sets
add <IP[/CIDR]> # Add manual ban (runtime by default)
delete <IP[/CIDR]> # Delete ban (runtime by default)
clear # Clear runtime timeout ban sets (supports --ddos/--all)
mefctl lists <type>
whitelist # Show whitelist entries
blacklist # Show file-based permanent blacklist entries
recidive # Show persistent recidive counters
mefctl config <action>
check [FILE] # Audit mef.conf [global] keys (missing/unknown)
mefctl status [--verbose] [fastpath]
# Show service + firewall status
# fastpath target: fastpath-only lifecycle/debug view
mefctl enable [mef|mefdaemon]
mefctl disable [mef|mefdaemon]
mefctl start <mef|mefdaemon>
mefctl stop <mef|mefdaemon>
mefctl reload <mef|mefdaemon>
mefctl restart <mef|mefdaemon>
mefctl update [--force] [--version X.Y.Z] # Install release binaries to /usr/local/sbinNotes:
[FILE]is optional. If omitted, mefctl uses/etc/mef/mef.rules.rules fmtdoes NOT modify firewall state.rules validateis recommended beforerules apply.rules applyauto-rolls back after--timeoutif noyesconfirmation is received.rules applynow verifies rollback restore results; if restore fails, it attempts backend clear fallback and reports rollback errors.- manual
rules applydoes not activate boot lifecycle by itself; ifmef.serviceis inactive/disabled,stop mefmay not clear those manually-loaded rules and rules may not load on reboot. - for managed lifecycle/persistence use:
mefctl enable mef && mefctl start mef(manual cleanup remainsmefctl rules clear). rules migratereads running nftables rules and prints mef.rules text to stdout by default.rules migrateexcludes the mef.conf managed nft table by default.bans add --permanentwrites toblacklist_dir/*.confand survives reboot.bans add --permanentrejects entries that overlap whitelist CIDRs.bans deleteremoves runtime + DDoS throttle sets by default.bans delete --permanentremoves only permanent sets andblacklist_dir/*.conf.bans delete --ddosremoves only DDoS throttle sets (mefddos_throttle_v4/v6).bans delete --allremoves runtime + DDoS throttle + permanent sets andblacklist_dir/*.conf.bans clear --ddosclears only DDoS throttle sets.bans clear --allclears runtime + DDoS throttle sets.bans list(default) groups output toRuntime,DDoS Throttle,Permanent,Per-Rule, andOthersections.bans list --permanentshows only permanent sets.bans list --ddosshows only DDoS throttle sets.bans list --allexplicitly selects the default "all sets" mode.bans list --fastpathshows kernel fastpath sets only (netdev mef_fastpath).bans listandbans list --permanentinclude permanent entries fromblacklist_dir/*.confeven when fastpath is active.bans list --fastpathrequires Linux +nftablesbackend (not available withiptablesbackend).bans list --ips-onlyprints only unique IP/CIDR values.bans list --verboseprints raw backend rows with source set.config checkaudits[global]keys and reports missing known keys (defaults in effect) plus unknown keys (possible typo/legacy).config checkdoes not require hidden optional keys (community_report_token,community_report_insecure_tls) and does not warn when they are omitted.--permanent,--ddos, and--allare mutually exclusive scope selectors inbans listandbans delete.status --verbose fastpathprints fastpath lifecycle/debug details (kernel_table, source-of-truth, restart/crash behavior, management hints).updatefetches release metadata fromupdates.jsonand installs/usr/local/sbin/mefdaemonand/usr/local/sbin/mefctl.- Binary download prefers
updates.jsonplatform asset URLs (raw tag paths) and falls back to raw tag/main URLs (github.com/.../raw/refs/tags/vX.Y.Z/bin/...,github.com/.../raw/refs/heads/main/bin/...) when needed. - If metadata asset URL version does not match requested
--version, updater ignores that metadata URL and uses computedvX.Y.Zraw tag URLs. update --version X.Y.Zinstalls a specific version.- Version pinning is bounded by metadata:
min_version <= X.Y.Z <= latest_version. - Downgrade requires
--force. updaterequires root privileges.
rules fmt
--write
--out <FILE>
rules apply
--timeout <duration>
--yes
rules migrate
--write # write to /etc/mef/rules.migrate
--out <FILE>
--family <ip|ip6|inet>
--table <NAME>
bans add
--timeout <duration>
--ports <port[,port,...]>
--permanent
bans delete
--ports <port[,port,...]>
--permanent
--ddos
--all
bans list
--permanent
--ddos
--all
--fastpath
--ips-only
--verbose
bans clear
--ddos
--all
config check
[FILE] # default /etc/mef/mef.conf
status
--verbose
fastpath
update
--force
--version <X.Y.Z># systemd (Linux)
systemctl status mef mefdaemon
systemctl reload mef # Reload firewall rules
systemctl reload mefdaemon # Reload daemon config
# FreeBSD
service mef status
service mefdaemon status
service mef reload # Reload firewall rules
service mefdaemon reload # Reload daemon configmefctl enable mefdaemon note:
- prints PS prerequisite output only when attention is needed (warnings).
- when PS prerequisites are OK, it stays quiet (no extra
PSD: ... readyline).
- Global config:
/etc/mef/mef.conf - Per-service rules:
/etc/mef/rules.d/*.conf - Startup behavior: mefdaemon now starts even when
rules.dhas no enabled rules (or no rule files). In that mode, only global detectors/features (PS, RBL, CLOUD) run if enabled.
Rule note (source=journal):
programssupports exact names and wildcards, e.g.sshd,sshd*,postfix/*, or*.- Use
mefctl reload mefdaemonfor config changes only; after binary upgrade, usemefctl restart mefdaemon.
Repo default config template: conf/mef.conf
Global config (INI-style):
rules_dir(default/etc/mef/rules.d)whitelist_dir(default/etc/mef/whitelist)whitelist_reload(default1m, supports10s,1m, or integer seconds)blacklist_dir(default/etc/mef/blacklist)blacklist_reload(default1m, supports10s,1m, or integer seconds)blacklist_fastpath(Linux permanent-blacklist acceleration:auto|xdp|tc|disabled, defaultauto;autoprefers XDP and falls back totc)blacklist_fastpath_xdp_mode(XDP attach preference:auto|native|generic, defaultauto)cache_dir(default/etc/mef/cache, daemon runtime state)debug(enable debug logging, defaultfalse)debug_log(log file path when debug is enabled, default/tmp/mef.txt)ban_log_enabled(enable dedicated BAN audit log, defaultfalse)ban_log_path(BAN audit log path, default/var/log/mef.log)community_report(optional community reporting for structured ban indicators, defaultfalse; startup logscommunity reporting disabled/enabled; enabled mode logs batch interval + short server ID; delivery is best-effort/non-blocking, includes transient retry attempts, and does not affect local bans)community_cloud_protection(optional cloud DNS protection, defaultfalse; requirescommunity_report=true; emitsRULE=CLOUD | SOURCE=DNSbans)community_cloud_ports(cloud DNS protection port scope:all,22,443,100-2000; defaultall)community_cloud_bantime(cloud DNS protection ban duration, default1h)community_cloud_timeout(cloud DNS lookup timeout, default2s)community_cloud_positive_ttl(cloud DNS cache TTL for listed results, default1h)community_cloud_negative_ttl(cloud DNS cache TTL for not-listed results, default15m)community_cloud_error_ttl(cloud DNS cache TTL for lookup errors/timeouts, default30s)rbl_enabled(enable optional DNSBL checks, defaultfalse)rbl_<key>_enabled(enable profile, key uses[a-z0-9]+)rbl_<key>_zone(DNSBL zone, default profile usesbl.blocklist.de)rbl_<key>_answer_match(response IP glob list, e.g.127.0.0.*,127.*,*)rbl_<key>_ports(port scope:all,22,443,100-2000)rbl_<key>_bantime(RBL ban duration)rbl_<key>_timeout(DNS lookup timeout)rbl_<key>_positive_ttl(cache TTL for listed results)rbl_<key>_negative_ttl(cache TTL for not-listed results)rbl_<key>_error_ttl(cache TTL for lookup errors/timeouts)clear_bans_on_stop(flush active ban sets when daemon stops, defaultfalse)journal_since(default2 min ago)ps_enabled(enable Port Scan Detection, Linux-only in current version)ps_limit(ban threshold for unique destination ports within interval)ps_interval(PS sliding window, Go duration or integer seconds)ps_bantime(PS temporary ban duration, Go duration or integer seconds)ps_interface(PS interface filter:auto, single interface likeeth0, or CSV likeeth0,eth1)ps_interface_exclude(PS interface exclude list, defaultlo; supportsloorlo,eth1)ps_stats(emit periodic PS stats logs while source is active, defaulttrue)ps_stats_interval(PS stats log interval, default30s; supports30s,1m,5mor integer seconds)ps_exclude_ports(exclude destination ports from PS counting; defaultauto:auto, manual22,443,100-500, or combinedauto,22,443)ps_exclude_ports_refresh(refresh interval forps_exclude_ports=auto, default1m)ps_source_order(source priority list, defaultpacket,conntrack,journal)ps_packet_udp(include UDP inpacketsource tracking, defaultfalse; whentrue, mefdaemon auto-manages/etc/mef/whitelist/auto-whitelist.confwith DNS resolvers, default gateways, and DHCP server IPs to reduce false positives)ps_packet_kernel_drop(enable packet-source blocked-IP kernel-drop/early-skip path, defaulttrue; independent fromblacklist_fastpath, setfalseto run packet source without this optimization)ps_packet_rx_mode(packet receive path:auto|recvfrom|mmap, defaultrecvfrom;autotries PACKET_MMAP/TPACKET_V3 first and falls back torecvfrom)ps_packet_fanout_sockets(packet sockets per interface, default1, valid1..64; values>1enable PACKET_FANOUT hash distribution)ps_escalation_enabled(enable PS second-stage permanent blacklist escalation)ps_escalation_window(PS escalation window)ps_permanent_threshold(first-stage PS bans before permanent blacklist)file_recent_limit(startup cap forsource=filematched logs, default200,0= disabled)file_recent_window(startup recency window forsource=file, default24h,0= disabled)firewall_backend(auto|nftables|iptables)firewall_family(nftables table family, defaultinet)firewall_table(nftables table name, defaultmef)firewall_chain(nftables chain name / iptables chain, defaultmef)firewall_hook(nftables hook, defaultinput)firewall_priority(nftables hook priority, default-100)firewall_log_enabled(enable LOG before DROP, defaulttrue)firewall_log_prefix(LOG prefix, defaultMEF_)firewall_log_level(LOG level, defaultwarn)firewall_log_rate(LOG rate limit in logs/second, default1; scope is per firewall LOG rule, not per source IP)firewall_log_burst(LOG burst bucket size, default5)
PS is a global detector (not a per-service rules.d rule):
- Detects source IPs that hit many different destination ports in a short window.
- Intended for port scanning behavior (
ps_limit+ps_interval). - Not intended to replace service-specific brute-force detection on a single port (SSH/SMTP/HTTP), which still belongs in
rules.dfailregex rules.
Linux v1 source order (default):
- Primary:
packet(raw socket, requiresCAP_NET_RAW; default tracks TCPSYN && !ACK, optional UDP viaps_packet_udp=true) packetsource uses kernel-level cBPF prefiltering and (whenps_interfaceis notall) binds sockets to resolved interface set; filter/socket set is reloaded automatically whenps_interfaceor autops_exclude_portsresolution changes.- Fallback:
conntrack - Last fallback:
journal(requires firewall logging and matching prefix)
Source behavior differences:
packetsees raw inbound packets early and catches scans against dropped/closed ports, but can use more CPU under heavy flood traffic.packetparser path is single-pass in the reader loop (key + event from one decode), reducing per-packet userspace parse overhead versus older double-parse behavior.packetreceive path can run in PACKET_MMAP ring mode (ps_packet_rx_mode=auto|mmap) with optional multi-socket fanout (ps_packet_fanout_sockets>1) for higher PPS workloads.packetreader can use blocked-IP early skip path (minimal parse + snapshot lookup) whenps_packet_kernel_drop=true, avoiding full event/channel/tracker work for already blocked sources.conntrackis lighter but only sees flows that reach conntrackNEWtracking; scans against dropped/non-tracked ports may be invisible.- With
ps_exclude_ports=auto, local listening service ports (for example22,25,80,443,465,587) are excluded from PS counting for all sources. - PS ban threshold is strict: ban triggers when
unique_ports > ps_limit(not>=).
Linux package prerequisite for conntrack source:
- Debian/Ubuntu:
apt install conntrack - RHEL/Rocky/Alma/Fedora:
dnf install conntrack-tools(oryum install conntrack-tools) - If not installed, PS can still run with
packetsource (ifCAP_NET_RAWis available) or journal fallback (firewall_log_enabled=true).
Recommended test config (/etc/mef/mef.conf):
ps_enabled=true
ps_limit=10
ps_interval=300
ps_bantime=3600
ps_interface=auto
ps_interface_exclude=lo
ps_stats=true
ps_stats_interval=1m
ps_exclude_ports=auto,22,443
ps_exclude_ports_refresh=1m
ps_source_order=packet,conntrack,journal
ps_packet_udp=false
ps_packet_kernel_drop=true
ps_packet_rx_mode=recvfrom
ps_packet_fanout_sockets=1
ps_escalation_enabled=true
ps_escalation_window=24h
ps_permanent_threshold=3Apply and verify:
mefctl restart mefdaemon
journalctl -u mefdaemon -fExpected runtime logs:
port scan source=packet active(orsource=conntrack/source=journalfallback)port scan excluded ports active=22,25,53,80,443(example)[PORTSCAN] ... HIT ... unique_ports=X/Y(only when a new unique destination port is observed)[PORTSCAN] ... BAN ...after threshold is exceeded- Optional
[PORTSCAN] ... PERM_BAN ...when escalation threshold is reached
Expected mef.log BAN entries (when ban_log_enabled=true):
RULE=PORTSCAN | SOURCE=PACKET | BAN | ip=... | bantime=1h | action=ban | msg="unique_ports=11 limit=10 interval=5m ports=22,25,80,443,..."- Optional escalation:
RULE=PORTSCAN | SOURCE=PACKET | PERM_BAN | ip=...
Quick checks:
command -v conntrack
journalctl -u mefdaemon -n 100 --no-pager | grep -E "port scan source=|PORTSCAN"
grep "RULE=PORTSCAN" /var/log/mef.logFirewall verification examples:
# nftables backend
nft list set inet mef mefbanned_v4
nft list set inet mef mefbanned_v6
# permanent blacklist set (if escalation triggered)
nft list set inet mef mefpermbanned_v4
nft list set inet mef mefpermbanned_v6Notes:
mefctl reload mefdaemonreloads config only; userestartafter binary upgrades.- Whitelist is always checked before PS counting/banning.
- FreeBSD currently runs PS as no-op (Linux-only in this version).
ps_interface=autoselects default-route interface(s); explicit lists (eth0,eth1) restrict PS to those interfaces.ps_interface_excluderemoves interfaces from tracking (defaultlo, so loopback is excluded by default).ps_stats=falsedisables periodicport scan stats ...log lines.ps_stats_intervalcontrols stats frequency; counters are runtime-only and reset on daemon/source restart.- Stats include
repeat_portfor repeated same-port events that do not increaseunique_ports. - Packet source stats include
skip_banned_earlyfor packets dropped in the reader before full event processing (ps_packet_kernel_drop=true). ps_exclude_ports=autodetects local listening server ports (TCP+UDP) and excludes them from PS counting.ps_packet_udp=trueenables UDP tracking forpacketsource and auto-generates/etc/mef/whitelist/auto-whitelist.conf(resolvers, gateways, DHCP servers).- If
ps_packet_udpis disabled (or PS is disabled), that auto-generated whitelist file is removed. - Specific bind IPs (including public IPs) are matched against
ps_interface/ps_interface_exclude;0.0.0.0/::applies to tracked interfaces.
RBL is optional and can trigger from:
-
PS source pipeline (
packet/conntrack/journalfirewall logs) -
rules.d/*rule hits (source=journalandsource=file) -
Runs asynchronously (fail-open); lookup latency does not block local detection flow.
-
Uses per-profile cache (
positive_ttl,negative_ttl,error_ttl) and pending-query deduplication. -
Supports multiple profiles via dynamic keys:
rbl_<key>_*(key:[a-z0-9]+). -
Supports port-scoped bans per profile via
rbl_<key>_ports. -
PS source stack is Linux-only;
rules.dtrigger path works with normal rule sources (file/journal). -
Apply RBL config changes with
mefctl restart mefdaemon(reload alone does not restart source workers). -
Malware.Expert cloud DNS profile is handled via
community_cloud_protection=true(logged asRULE=CLOUD), not viarbl_<key>_zone.
Default profile (blocklist) in /etc/mef/mef.conf:
rbl_enabled=true
rbl_blocklist_enabled=true
rbl_blocklist_zone=bl.blocklist.de
rbl_blocklist_answer_match=127.0.0.*,127.*,*
rbl_blocklist_ports=all
rbl_blocklist_bantime=1h
rbl_blocklist_timeout=2s
rbl_blocklist_positive_ttl=1h
rbl_blocklist_negative_ttl=15m
rbl_blocklist_error_ttl=30sExpected mef.log RBL BAN entry:
RULE=RBL | SOURCE=DNS | BAN | ip=1.2.3.4 | bantime=1h | action=ban | profile=blocklist | msg="zone=bl.blocklist.de answer=127.0.0.2 src=1.2.3.4 dport=22 proto=tcp dst=95.217.31.237 iface=eth0"(trigger context fields are included when available)
Whitelist files are read from /etc/mef/whitelist/*.conf (configurable via whitelist_dir).
Repo example whitelist: conf/whitelist/example.conf
Format: one entry per line. Supported forms:
IPCIDRIP/MASK
Comments are allowed:
- Full line comments with
#or; - Inline comments, e.g.
1.2.3.4 # office
Whitelist entries are loaded into memory and reloaded periodically (default 1m, configurable via whitelist_reload).
- Reserved auto-managed file:
/etc/mef/whitelist/auto-whitelist.conf(created only whenps_enabled=trueandps_packet_udp=true).
Blacklist files are read from /etc/mef/blacklist/*.conf (configurable via blacklist_dir).
Repo example blacklist: conf/blacklist/example.conf
Format: one entry per line (IP or CIDR).
Blacklist entries are enforced as permanent bans and reloaded periodically (default 1m, configurable via blacklist_reload).
Set mapping:
auto-permanent.conf->mefpermbanned_v4/mefpermbanned_v6- other
*.conffiles ->mefbl_<filename>_v4/mefbl_<filename>_v6
Reload behavior:
- reload is file-granular (only changed blacklist files are re-synced)
- small changes use add/delete diff updates
- large changes use atomic tmp+swap set sync with batched nft element loading
Whitelist has precedence: if an address matches both, whitelist wins.
Fastpath is managed by mefdaemon from the same source-of-truth as permanent blacklist enforcement.
Active fastpath enforcement requires Linux with nftables backend (iptables backend keeps fastpath disabled).
Inspect:
mefctl status --verbose fastpath
mefctl bans list --fastpath --ips-onlyTypical output fields:
active/configured mode(tc,xdp,disabled)ifacesentries_v4,entries_v6prefixes(CIDR prefixes, non-host entries)last_sync
Lifecycle:
mefctl reload mefdaemonormefctl restart mefdaemonre-syncs fastpath state from blacklist files and runtime ban snapshots.- If daemon crashes,
table netdev mef_fastpathmay remain in kernel until next daemon sync or manual delete. - Runtime timeout ban mirror has short periodic sync lag (not strictly per-event instant).
Ban management through mefctl:
- List active kernel fastpath entries:
mefctl bans list --fastpathmefctl bans list --fastpath --ips-only
- Remove runtime timeout bans:
mefctl bans clear
- List active DDoS throttle entries:
mefctl bans list --ddosmefctl bans list --ddos --ips-only
- Remove DDoS throttle entry only:
mefctl bans delete <ip> --ddos
- Clear DDoS throttle sets:
mefctl bans clear --ddos
- Remove one permanent entry:
mefctl bans delete <ip-or-cidr> --permanent
- Remove one entry from everywhere:
mefctl bans delete <ip-or-cidr> --all
Emergency reset:
nft delete table netdev mef_fastpath
mefctl restart mefdaemonDifference vs inet mef:
netdev mef_fastpath(tcingress) drops early before userspace packet parsing.inet mefcontains normal firewall/runtime/per-rule enforcement path.
- nftables (preferred)
- iptables (fallback)
- Uses iptables/ip6tables; IPv6 rules require
ip6tablesin PATH. - Address
setreferences requireipsetto be installed. - Multiple negated interfaces in one
iif/oiflist are not supported by iptables (e.g.!lo,!docker0).
Rules can specify action and ports:
action=banoraction=detectports=allorports=22,25orports=22-2222backend=auto|nftables|iptables
Optional escalation keys (fail2ban recidive style):
escalation_enabled=true|falseescalation_window=24h(Go duration or integer seconds)permanent_threshold=5(number of first-level bans before permanent ban)
Example: enable escalation only for nginx (/etc/mef/rules.d/nginx.conf):
escalation_enabled=true
escalation_window=24h
permanent_threshold=5Keep other service rules with escalation_enabled=false.
mef includes an optional community reporting feature that can contribute structured security indicators to the Malware.Expert - Attack Intelligence database.
Community reporting is disabled by default. It must be explicitly enabled by the system administrator.
All firewall decisions and bans are always executed locally. Enabling community reporting does not affect local ban logic.
Participation is voluntary and independent from core firewall functionality. When enabled, reports are sent to a built-in cloud endpoint.
If enabled, the following structured data may be transmitted:
- Attacker IP address
- Service identifier (e.g. ssh, nginx, postfix, portscan, ddos, rbl, cloud)
- Timestamp (last seen)
- Pseudonymous reporter identifier (one-way hash)
- Structured rule metadata (
detailsJSON object; fields vary by detection type)RBL/CLOUD:zone,answer,dport(if available)PORTSCAN:ports,interval,limit- Other services (e.g.
SSHD): omitted when no structured details are available
- Service normalization: all non-cloud RBL profiles use
service=rbl(notrbl_<key>); cloud usesservice=cloud. - DDOS reporting scope: only successful
BANevents are reported (service=ddos);THROTTLEis not reported.
The reporter identifier is generated locally. Malware.Expert receives only the hash value, never the underlying server identifier.
mef does NOT transmit:
- Raw log entries
- Usernames
- Authentication attempts
- Message contents
- Target server IP addresses
- Application payload data
Submitted data is used solely for:
- Aggregating malicious IP intelligence
- Improving automated threat detection
- Providing an IP reputation feed for legitimate security purposes
See:
- Privacy Notice: https://malware.expert/privacy-notice/
- Terms of Service: https://malware.expert/terms-of-service/
Keeping the code well-documented is important for this project. Please add clear file headers and comments for non-obvious logic so future maintenance and search are easy.
linux firewall, nftables, iptables, fail2ban alternative, intrusion prevention, ip blocking, log monitoring, ssh protection, postfix protection, systemd firewall service, freebsd firewall daemon, csf
Threat intelligence data (if used) is provided "as is" without warranties of accuracy, completeness, or fitness for a particular purpose.