Blacklistd is a daemon listening to sockets to receive notifications from other daemons about connection attempts that failed or were successful. It is most widely used in blocking too many connection attempts on open ports. A prime example is SSH running on the internet getting a lot of requests from bots or scripts trying to guess passwords and gain access. Using blacklistd, the daemon can notify the firewall to create a filter rule to block excessive connection attempts from a single source after a number of tries. Blacklistd was first developed on NetBSD and appeared there in version 7. FreeBSD 11 imported blacklistd from NetBSD.
This chapter describes how to set up blacklistd, configure it, and provides examples on how to use it. Readers should be familiar with basic firewall concepts like rules. For details, refer to the firewall chapter. PF is used in the examples, but other firewalls available on FreeBSD should be able to work with blacklistd, too.
The main configuration for blacklistd is stored in
blacklistd.conf(5). Various command line options are
also available to change blacklistd's run-time behavior.
Persistent configuration across reboots should be stored
in /etc/blacklistd.conf
. To enable
the daemon during system boot, add a
blacklistd_enable
line to
/etc/rc.conf
like this:
#
sysrc blacklistd_enable=yes
To start the service manually, run this command:
#
service blacklistd start
Rules for blacklistd are configured in
blacklistd.conf(5) with one entry per line. Each
rule contains a tuple separated by spaces or tabs. Rules
either belong to a local
or a
remote
, which applies to the machine
where blacklistd is running or an outside source,
respectively.
An example blacklistd.conf entry for a local rule looks like this:
[local] ssh stream * * * 3 24h
All rules that follow the [local]
section are treated as local rules (which is the
default), applying to the local machine. When a
[remote]
section is encountered, all
rules that follow it are handled as remote machine
rules.
Seven fields define a rule separated by either tabs
or spaces. The first four fields identify the traffic
that should be blacklisted. The three fields that
follow define backlistd's behavior. Wildcards are
denoted as asterisks (*
), matching
anything in this field. The first field defines the
location. In local rules, these are the network ports.
The syntax for the location field is as follows:
[address
|interface
][/mask
][:port
]
Adressses can be specified as IPv4 in numeric format
or IPv6 in square brackets. An interface name like
can also
be used.em0
The socket type is defined by the second field. TCP
sockets are of type stream
, whereas UDP
is denoted as dgram
. The example above
uses TCP, since SSH is using that protocol.
A protocol can be used in the third field of a
blacklistd rule. The following protocols can be used:
tcp
, udp
,
tcp6
, udp6
, or
numeric. A wildcard, like in the example, is typically
used to match all protocols unless there is a reason to
distinguish traffic by a certain protocol.
In the fourth field, the effective user or owner of the daemon process that is reporting the event is defined. The username or UID can be used here, as well as a wildcard (see example rule above).
The packet filter rule name is declared by the fifth
field, which starts the behavior part of the rule. By
default, blacklistd puts all blocks under a pf anchor
called blacklistd
in
pf.conf
like this:
anchor "blacklistd/*" in on $ext_if block in pass out
For separate blacklists, an anchor name can be used in
this field. In other cases, the wildcard will suffice.
When a name starts with a hyphen (-
) it
means that an anchor with the default rule name prepended
should be used. A modified example from the above using
the hyphen would look like this:
ssh stream * * -ssh 3 24h
With such a rule, any new blacklist rules are added to
an anchor called blacklistd-ssh
.
To block whole subnets for a single rule violation, a
/
in the rule name can be used. This
causes the remaining portion of the name to be interpreted
as the mask to be applied to the address specified in
the rule. For example, this rule would block every
address adjoining /24
.
22 stream tcp * */24 3 24h
It is important to specify the proper
protocol here. IPv4 and IPv6 treat /24 differently,
that is the reason why *
cannot be
used in the third field for this rule.
This rule defines that if any one host in that network is misbehaving, everything else on that network will be blocked, too.
The sixth field, called nfail
, sets
the number of login failures required to blacklist the
remote IP in question. When a wildcard is used at this
position, it means that blocks will never happen. In the
example rule above, a limit of three is defined meaning
that after three attempts to log into
SSH on one connection, the IP
is blocked.
The last field in a blacklistd rule definition
specifies how long a host is blacklisted. The default
unit is seconds, but suffixes like m
,
h
, and d
can also be
specified for minutes, hours, and days,
respectively.
The example rule in its entirety means that after
three times authenticating to
SSH will result in a new PF
block rule for that host. Rule matches are performed by
first checking local rules one after another, from most
specific to least specific. When a match occurs, the
remote
rules are applied and the name,
nfail
, and disable fields are changed
by the remote
rule that matched.
Remote rules are used to specify how blacklistd changes its behavior depending on the remote host currently being evaluated. Each field in a remote rule is the same as in a local rule. The only difference is in the way blacklistd is using them. To explain it, this example rule is used:
[remote] 203.0.113.128/25 * * * =/25 = 48h
The address field can be an IP address (either v4 or v6), a port or both. This allows setting special rules for a specific remote address range like in this example. The fields for type, protocol and owner are identically interpreted as in the local rule.
The name fields is different though: the equal sign
(=
) in a remote rule tells blacklistd
to use the value from the matching local rule. It means
that the firewall rule entry is taken and the
/25
prefix (a
netmask of 255.255.255.128
) is added.
When a connection from that address range is blacklisted,
the entire subnet is affected. A PF anchor name can also
be used here, in which case blacklistd will add rules for
this address block to the anchor of that name. The
default table is used when a wildcard is specified.
A custom number of failures in the
nfail
column can be defined for an
address. This is useful for exceptions to a specific
rule, to maybe allow someone a less strict application
of rules or a bit more leniency in login tries.
Blocking is disabled when an asterisk is used in this
sixth field.
Remote rules allow a stricter enforcement of limits on attempts to log in compared to attempts coming from a local network like an office.
There are a few software packages in FreeBSD that can
utilize blacklistd's functionality. The two most
prominent ones are ftpd(8) and sshd(8) to block
excessive connection attempts. To activate blacklistd in
the SSH daemon, add the following line to
/etc/ssh/sshd_config
:
UseBlacklist yes
Restart sshd afterwards to make these changes take effect.
Blacklisting for ftpd(8) is enabled using
-B
, either in
/etc/inetd.conf
or as a
flag in /etc/rc.conf
like
this:
ftpd_flags="-B"
That is all that is needed to make these programs talk to blacklistd.
Blacklistd provides the user with a management utility
called blacklistctl(8). It displays blocked
addresses and networks that are blacklisted by the rules
defined in blacklistd.conf(5). To see the
list of currently blocked hosts, use
dump
combined with -b
like this.
#
blacklistctl dump -b
address/ma:port id nfail last access 213.0.123.128/25:22 OK 6/3 2019/06/08 14:30:19
This example shows that there were 6 out of three
permitted attempts on port 22 coming from the address
range 213.0.123.128/25
. There
are more attempts listed than are allowed because SSH
allows a client to try multiple logins on a single TCP
connection. A connection that is currently going on is
not stopped by blacklistd. The last connection attempt is
listed in the last access
column of the
output.
To see the remaining time that this host will be on
the blacklist, add -r
to the previous
command.
#
blacklistctl dump -br
address/ma:port id nfail remaining time 213.0.123.128/25:22 OK 6/3 36s
In this example, there are 36s seconds left until this host will not be blocked any more.
Sometimes it is necessary to remove a host from the
block list before the remaining time expires.
Unfortunately, there is no functionality in blacklistd to
do that. However, it is possible to remove the address
from the PF table using pfctl. For each blocked port,
there is a child anchor inside the blacklistd anchor
defined in /etc/pf.conf
. For
example, if there is a child anchor for blocking port 22
it is called blacklistd/22
. There is a
table inside that child anchor that contains the blocked
addresses. This table is called port followed by the port
number. In this example, it would be called
port22
. With that information at hand,
it is now possible to use pfctl(8) to display all
addresses listed like this:
#
pfctl -a
... 213.0.123.128/25 ...blacklistd/22
-tport22
-T show
After identifying the address to be unblocked from the list, the following command removes it from the list:
#
pfctl -a
blacklistd/22
-tport22
-T delete213.0.123.128/25
The address is now removed from PF, but will still show up in the blacklistctl list, since it does not know about any changes made in PF. The entry in blacklistd's database will eventually expire and be removed from its output eventually. The entry will be added again if the host is matching one of the block rules in blacklistd again.
All FreeBSD documents are available for download at https://download.freebsd.org/ftp/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.