PF Fail2Ban 1.1.x with PF - need help before I lose it. Where to find docs or examples?

Hi,


Another annoying question from a boomer lammer here.
I’m trying to set up Fail2Ban 1.1.x with PF on FreeBSD.
I know PF can do blocking alone, but I want to get Fail2Ban working with it because it’s driving me nuts.


I’ve read the config files and comments but still don’t get it.
I’m no genius, more like a 13-year-old when it comes to this stuff.


Does anyone know where to find documentation, forum threads, usenet posts, or any example configs that explain how to do this?


Thanks a lot for the community spirit and the shared knowledge.
 
Does https://dbdemon.com/pf_and_fail2ban/
help

To *not* answer your question, there's also sshguard, which has a man page of how to do it with pf.

It's pretty straightforward, IMHO.
Thanks for the link, but unfortunately it didn’t help me solve the problem. Still, I really appreciate you taking the time to respond and share it — big thanks!

I still keep getting these damn errors about namespace collision, like:



Code:
root@ovh:/usr/local/etc/fail2ban # pfctl -f /etc/pf.conf
pfctl: warning: namespace collision with <f2b-postfix> global table.
root@ovh:/usr/local/etc/fail2ban # pfctl -t f2b-postfix -T flush
1 addresses deleted.
root@ovh:/usr/local/etc/fail2ban # pfctl -f /etc/pf.conf
pfctl: warning: namespace collision with <f2b-postfix> global table.

my pf .conf
Code:
# Variables
ext_if = "igb0"
jail_net = "10.0.0.0/24"
mail_jail = "10.0.0.2"    # Mail jail IP
web_jail  = "10.0.0.3"    # Webmail jail IP

# Packet normalization
scrub in on $ext_if all fragment reassemble

# Skip loopback interfaces
set skip on lo0
set skip on lo1

# Tables (optional here)
#table <fail2ban> persist
table <f2b> persist
table <jails> persist
nat on $ext_if from <jails> to any -> ($ext_if:0)
rdr-anchor "rdr/*"

## Incoming redirects (rdr) to jails - must be before block rules
# Mail (SMTP, submission, IMAPS, POP/IMAP)
rdr pass on $ext_if proto tcp from any to ($ext_if) port 25  -> $mail_jail
rdr pass on $ext_if proto tcp from any to ($ext_if) port 587 -> $mail_jail
rdr pass on $ext_if proto tcp from any to ($ext_if) port 993 -> $mail_jail
rdr pass on $ext_if proto tcp from any to ($ext_if) port 110 -> $mail_jail
rdr pass on $ext_if proto tcp from any to ($ext_if) port 143 -> $mail_jail
rdr pass on $ext_if proto tcp from any to ($ext_if) port 465 -> $mail_jail

# Webmail / HTTP(S)
rdr pass on $ext_if proto tcp from any to ($ext_if) port 80  -> $web_jail
rdr pass on $ext_if proto tcp from any to ($ext_if) port 443 -> $web_jail

## Filtering rules - fail2ban quick block

anchor "f2b/*"

# Allow SSH admin first (before blocking)
#pass in quick on $ext_if proto tcp from $admin_ip to any port $ssh_port keep state
# SSH access outside the house
pass in quick on $ext_if proto tcp from any to any port $ssh_port keep state

block in quick from <fail2ban> to any
#block in log quick on $ext_if from <f2b> to any
block drop in log quick on $ext_if from <f2b> to any

## Allow incoming SMTP, Submission (587), SMTPS and IMAPS/POP3S traffic to mail jail
pass in on igb0 proto tcp from any to (igb0) port { smtp, submission, smtps, imaps, pop3s } keep state


block in all
pass out quick keep state
antispoof for $ext_if inet
 
Maybe you want to take a look at blacklistd(8). It's a native solution that doesn't rely on python scripts grep'ing through logfiles, directly acts upon events (i.e. failed logins) on services like ssh or postfix and hooks into pf.
mail/postfix in pkg repositories is built with blacklistd support by default
Thanks for the tip about blacklistd! I’ll definitely check it out.
If you have any docs or tutorials for those of us who were sleeping in the back row during Security and Network 101, I’m all ears.
But let’s be honest, in a month, a year, a few years, or maybe even 10, I’ll probably want to fix Fail2ban someday anyway, it’s become a personal thing.
 
I've given up on fail2ban many years ago because it broke more than once, was overly complicated/bloated and relies on the IMHO flawed concept of scanning logs - the blacklistd approach is much cleaner and less error prone (especially because no python is involved...)

I don't know of any tutorial, but there's not much to it - the daemon listens on /var/run/blacklistd.sock and blacklistd-aware services connect to it and report authentication failures.
/etc/blacklistd.conf defines thresholds for max fails/duration of blacklisting for various services.
By default it uses pf, so you only need to create an anchor ( anchor "blacklistd/*" in on egress) and you're done.
To retain blacklists across reboots, run blacklistd with the '-r' flag.
Postfix automagically reports to the socket if available, ssh needs the Option 'UseBlacklist' in sshd_config set to 'yes' (already present with a default of 'no').

The manpages for blacklist and its config file/tools are pretty thorough even if they are rather short - but that's the beauty of it, as there is not much to configure or worry about. It 'just works'™

edit:
I just found this quick guide that covers the basics (i.e. pretty much all there is to know): https://obsessivecomputingdisorder.com/posts/blacklistd-guide/
 
I still keep getting these damn errors about namespace collision
Remove the table <f2b> persist from your pf.conf.

Also, remove the pass from your rdr pass ... lines. Using rdr pass will cause the traffic to always be accepted and the rest of your ruleset (including any blocking done by fail2ban) to be ignored.
 
Hi guys! I eventually found a solution and it's been working for a few weeks now.


My architecture:


  • Blacklistd for SSH on the host
  • Fail2ban for my mail and web services in jails

I tried to make blacklistd work for everything, but since I want to keep only one PF instance centralized on the host, and since blacklistd works at the socket level, it's hard to share it between jails and host. I tried, I failed.


So I decided to mount my jail logs on the host:

Code:
# /etc/fstab
/usr/local/bastille/jails/mail/root/var/log    /var/log/jail-mail    nullfs ro 0 0
/usr/local/bastille/jails/webmail/root/var/log /var/log/jail-webmail nullfs ro 0 0

here is my working pf conf

Code:
# /etc/pf.conf -
# Variables
ext_if = "igb0"
jail_net = "10.0.0.0/24"
mail_jail = "10.0.0.2"
web_jail = "10.0.0.3"
ssh_port = 22222  # Custom SSH port (change this!)

# Options
set skip on lo0
set skip on lo1

# Tables
table <fail2ban> persist
table <f2b> persist
table <blacklistd> persist
table <rate_abuse_postfix> persist
table <rate_abuse_dovecot> persist
table <rate_abuse_https> persist

# Normalisation
scrub in on $ext_if all fragment reassemble

# NAT
nat on $ext_if from $jail_net to any -> ($ext_if:0)

# Redirections (WITHOUT pass! Critical!)
# Mail services - ALL ports
rdr on $ext_if proto tcp from any to ($ext_if) port 25 -> $mail_jail      # SMTP
rdr on $ext_if proto tcp from any to ($ext_if) port 110 -> $mail_jail     # POP3
rdr on $ext_if proto tcp from any to ($ext_if) port 143 -> $mail_jail     # IMAP
rdr on $ext_if proto tcp from any to ($ext_if) port 465 -> $mail_jail     # SMTPS
rdr on $ext_if proto tcp from any to ($ext_if) port 587 -> $mail_jail     # Submission
rdr on $ext_if proto tcp from any to ($ext_if) port 993 -> $mail_jail     # IMAPS
rdr on $ext_if proto tcp from any to ($ext_if) port 11334 -> $mail_jail   # Rspamd

# Web services
rdr on $ext_if proto tcp from any to ($ext_if) port 80 -> $web_jail       # HTTP
rdr on $ext_if proto tcp from any to ($ext_if) port 443 -> $web_jail      # HTTPS

# Filtering
anchor "f2b/*"
anchor "blacklistd/*" in on $ext_if

# SSH priority (no rate limiting to avoid self-lockout!)
pass in quick on $ext_if proto tcp from any to any port $ssh_port keep state

# Block rate abusers
block drop in quick from <rate_abuse_postfix> to any
block drop in quick from <rate_abuse_dovecot> to any
block drop in quick from <rate_abuse_https> to any

# Fail2ban blocks (pre-defined rules, tables populated by fail2ban)
block quick from <f2b-postfix>
block quick from <f2b-dovecot>
block quick from <f2b-dovecot-ssl>
block quick from <f2b-rspamd>
block quick from <f2b-nginx-webmail-auth>
block quick from <f2b-nginx-webmail-badreq>
block quick from <f2b-nginx-webmail-forbidden>
block quick from <f2b-nginx-webmail-limit-req>
block quick from <f2b-nginx-webmail-botsearch>
block quick from <f2b-recidive>

# Mail services with rate limiting
pass in quick on $ext_if proto tcp from any to any port { 25, 465, 587 } \
    keep state (source-track rule, max-src-conn 8, max-src-conn-rate 15/300, overload <rate_abuse_postfix>)

# IMAP/POP - NO rate limiting (too restrictive for multi-account users - learned this in production!)
pass in log quick on $ext_if proto tcp from any to any port { 110, 143, 993, 11334 } \
    keep state

# Web services with rate limiting
pass in quick on $ext_if proto tcp from any to any port 80 \
    keep state (source-track rule, max-src-conn 25, max-src-conn-rate 60/60, overload <rate_abuse_https>)

pass in quick on $ext_if proto tcp from any to any port 443 \
    keep state (source-track rule, max-src-conn 30, max-src-conn-rate 80/60, overload <rate_abuse_https>)

# Anti-spoofing (adjust to your network)
block drop in on ! $ext_if inet from 10.0.0.0/24 to any
block drop in inet from YOUR.SERVER.IP to any

# Default policy
block drop in all
pass out quick keep state

# Protection anti-spoofing
antispoof for $ext_if inet
Important: Fail2ban is only populating tables and PF is already configured to block them, because letting F2B modify pf.conf was a disaster (it would delete my custom rules!).

I know that I'm still learning and this is not perfect, but it's been working solidly for weeks now. Hope this helps someone else struggling with the same issues!
Thanks again to everyone for your help!
 
I tried to make blacklistd work for everything, but since I want to keep only one PF instance centralized on the host, and since blacklistd works at the socket level, it's hard to share it between jails and host. I tried, I failed.
create the blacklistd socket on the host and share it to the jails (via devfs ruleset), this way services in jails can talk to the blacklistd instance on the host.
 
create the blacklistd socket on the host and share it to the jails (via devfs ruleset), this way services in jails can talk to the blacklistd instance on the host.
Thanks for your reply sko . Trust me I tried, but it does not work.
Maybe I didn't go about it the right way, but I spent quite a bit of time on it. Only it's been several weeks and I didn't write down what I tested
 
Thanks for your reply sko . Trust me I tried, but it does not work.
Maybe I didn't go about it the right way, but I spent quite a bit of time on it. Only it's been several weeks and I didn't write down what I tested

Taken from a host where I run blacklistd on the host and various services in jails that are blacklistd-enabled:

/etc/devfs.rules on host:
Code:
[devfs_rules_jail_blacklistd=6]
add include $devfsrules_jail
add path 'bpf*' unhide
add path 'blacklistd*' unhide
(not necessary; see edit)

/etc/blacklistd.sockets on host:
Code:
/var/run/blacklistd.sock
/iocell/jails/<UUID>/root/var/run/blacklistd.sock
[...]

and in /etc/rc.conf:
Code:
blacklistd_flags="-r -P /etc/blacklistd.sockets"


now that I look at that configuration, I'm not sure if the devfs rule is actually necessary or if this is some remnant of me trying to get it to work (yes, I also had some hard time with that due to the relatively scarce documentation). I think the entry in blacklistd.sockets is sufficient...

Edit: just tested it; that devfs rule is bogus, I must have forgotten to remove it after some testing. Sorry for the wrong hint in this direction - the '-P' option for blacklistd is the way to go.
 
Thanks again, sko . As soon as I have enough time, I’ll give it a try and let you know how it goes.

From my initial research, it looks like I’ll need to make a few changes to my dovecot.conf and master.cf to make them work with Blacklistd (adding some -B flag) .
However, I’m not sure if this will work with NGINX am I wrong?
 
Back
Top