I just spent more time than I’m willing to admit by looking into the combination of pf, fail2ban, and jails on FreeBSD 12.0. In particular, I am running a mail server in a jail and use pf to redirect all SMTP traffic to it. The setup for the redirection follows a post on the FreeBSD forum that I stumbled upon when I initially set up the server. Shortly after that I installed fail2ban and, as described in the setup instructions, inserted the line anchor f2b/* into /etc/pf.conf. I configured fail2ban to block access to the SSH port for anyone who tried to log in three times without success. That was easy to test and worked perfectly. I also wanted fail2ban to block SMTP access to the mail server for anyone who tried funny things with it (which happens shockingly fast after getting a public DNS entry for the domain). However, I never really tested the SMTP port blocking. Only now, some 6 months later, I noticed that fail2ban never blocked the SMTP port (i.e., the redirection to the mail jail). I could login to the server and (unsuccessfully, of course) try to send spam to other domains as often as I wanted. After trying out all kinds of configurations, I found out that the reason was in the /etc/pf.conf. I had some config like the following:

# router-facing interface 

# LAN IP address

# jail subnet

# NAT for the jails
nat on $ext_if proto {tcp udp icmp} from $jails to any -> $ext_ip

# Redirection to the jails based on port
# The keyword pass in the lines below caused my problems.
# - http
rdr pass on $ext_if proto tcp from any to $ext_ip port http -> 10.1.1.xxx
# - smtp
rdr pass on $ext_if proto tcp from any to $ext_ip port smtp -> 10.1.1.yyy
# etc.

# block all traffic by default
block all

# anchor for fail2ban rules
anchor "f2b/*"

# pass traffic to jails
pass in on $ext_if proto tcp from any to any port {http, smtp} keep state

# pass outbound traffic
pass out quick on $ext_if keep state

The problem was that the rdr directives contain the keyword pass. I copied that from the FreeBSD forum post without really thinking very much about it (pure cargo cult). But what this did was make all traffic to the jails pass before the fail2ban rules in the f2b anchor hierarchy were even checked. The SSH traffic was not redirected to any jail, so it was correctly blocked by the anchor. The SMTP traffic, however, was not checked against the rules created by fail2ban. Just removing pass from the rdr directives fixed this. I even had the directive to pass the traffic to the jails in place already.