minsec

Filters

Built-in filters for the usual services, and fail2ban-shaped regexes for everything else.

A filter is a named detection rule: where to read, what a failure looks like, and how to pull the attacker's address out of it. Names matter beyond your own server — in multiplayer mode the filter name is the one field that says anything about what the attacker did.

Built-in filters

minsec filters        # * marks enabled

The built-in set covers what the Virtualmin stack installs: sshd, postfix, postfix-sasl, dovecot, proftpd, webmin, apache-auth, nginx-auth, wordpress, and more. Enable the ones matching services you run:

sudo minsec enable sshd dovecot
sudo minsec disable wordpress

Both write /etc/minsec/conf.d/<name>.toml and preserve any other settings already in that drop-in. Restart to apply.

Overriding a built-in

Anything under [filters.<name>] in your configuration overrides the built-in definition for that filter:

[filters.sshd]
enabled  = true
maxretry = 3
files    = ["/var/log/auth.log"]   # override the journal default

Writing your own

A custom filter is a TOML file at /etc/minsec/filters/<name>.toml:

name = "myapp"
files = ["/var/log/myapp.log"]
journal = { identifiers = ["myapp"] }

# A cheap literal check run before the regex. Lines without it are skipped
# without ever touching the regex engine — this is most of why minsec is fast.
prefilter = ["login failed"]

patterns = [
  'login failed for <F-USER>\S+</F-USER> from <HOST>',
]

Patterns are regular expressions with fail2ban-style tokens:

TokenMatches
<HOST>, <ADDR>, <IP>an IPv4 or IPv6 address
<IP4> / <IP6>one family only
<F-USER>…</F-USER>a named capture returned as the username

They are matched anywhere in the line, so the same pattern works against a syslog file and a raw journal message. \d, \s and \w are ASCII.

A custom filter placed at filters/<name>.toml with the name of a built-in replaces that built-in entirely.

Test before you enable

minsec test myapp /var/log/myapp.log
minsec test myapp -q < /var/log/myapp.log     # per-address totals
minsec test myapp --json                      # one object per match

test is textual matching only — it deliberately does not apply thresholds, allowlists, IPv6 aggregation, escalation, or any firewall action, so what you see is exactly what the pattern did.

Then compile everything, including filters that are not enabled:

sudo minsec check --all

Cost

Each enabled filter compiles to one combined regex behind its literal prefilter, so the marginal cost of an extra filter is small — roughly 200 KB of heap for a typical one, and no additional pass over the log. Ten filters is an ordinary configuration, not an aggressive one.

Naming, and what it means for the crowd

If you report to the crowd, the filter name is sent as-is. Built-in names map to an abuse category through our signature registry; names we do not recognise are counted but never published, and never reach the DNSBL. If a custom rule's name is distinctive to your installation and you would rather not send it at all, disable reporting for that filter — see multiplayer mode and privacy.

Full syntax: minsec-filter.toml(5).