Fail2Ban 如何保护 Linux 服务器上的服务
介绍
SSH 是连接到云服务器的实际方法。它经久耐用且可扩展——随着新加密标准的开发,它们可用于生成新的 SSH 密钥,确保核心协议保持安全。然而,没有任何协议或软件栈是万无一失的,SSH 在互联网上的广泛部署意味着它代表了一个非常可预测的攻击面或攻击向量,人们可以通过它尝试获得访问权限。
任何暴露于网络的服务都是这种方式的潜在目标。如果您查看运行在任何被广泛访问的服务器上的 SSH 服务的日志,您会经常看到重复的、系统的登录尝试,这些尝试代表了用户和类似机器人的暴力攻击。尽管您可以对 SSH 服务进行一些优化以将这些攻击成功的可能性降低到接近于零,例如禁用密码身份验证以支持 SSH 密钥,但它们仍然会造成轻微的、持续的责任。
完全不能接受这种责任的大规模生产部署通常会在其 SSH 服务之前实施 WireGuard 等 VPN,因此如果没有额外的软件抽象或网关。这些 VPN 解决方案受到广泛信任,但会增加复杂性,并可能破坏某些自动化或其他小软件挂钩。
在进行完整的 VPN 设置之前或之外,您可以实施一个名为 Fail2ban 的工具。 Fail2ban 可以通过创建规则来显着减轻暴力攻击,这些规则会在登录尝试失败一定次数后自动更改防火墙配置以禁止特定 IP。这将允许您的服务器在没有您干预的情况下针对这些访问尝试加强自身。
在另一个教程中,我们讨论了如何使用 Fail2ban 保护 SSH。在本指南中,我们将更深入地讨论 Fail2ban 的实际工作原理以及如何使用这些知识来修改或扩展此服务的行为。
Fail2ban 的基本原理
Fail2ban 的目的是监控常见服务的日志以发现身份验证失败的模式。
当 fail2ban 配置为监视服务日志时,它会查看针对该服务配置的过滤器。该过滤器旨在通过使用复杂的正则表达式来识别该特定服务的身份验证失败。正则表达式是一种用于模式匹配的通用模板语言。它将这些正则表达式模式定义到一个名为 failregex
的内部变量中。
默认情况下,Fail2ban 包括用于公共服务的过滤器文件。当来自任何服务(如 Web 服务器)的日志与其过滤器中的 failregex
匹配时,将为该服务执行预定义的操作。 action
是一个变量,可以根据管理员的喜好配置它来做很多不同的事情。
默认操作是通过修改本地防火墙规则来禁止违规主机/IP 地址。例如,您可以将此操作扩展为向您的系统管理员发送电子邮件。
默认情况下,10分钟内检测到3次认证失败后才会采取行动,默认封禁时间为10分钟。这是可配置的。
使用默认的 iptables
防火墙时,fail2ban
会在服务启动时创建一组新的防火墙规则,也称为链。它向 INPUT 链添加一条新规则,将指向端口 22 的所有 TCP 流量发送到新链。在新链中,它插入一个返回到 INPUT 链的规则。如果 Fail2ban 服务停止,链和相关规则将被删除。
探索 Fail2ban 服务设置
Fail2ban 通过位于 /etc/fail2ban/
目录下的层次结构中的几个文件进行配置。
fail2ban.conf
文件配置了一些操作设置,例如守护程序记录信息的方式,以及它将使用的套接字和 pid 文件。然而,主要配置在定义每个应用程序“jails”的文件中指定。
默认情况下,fail2ban 附带一个 jail.conf
文件。但是,这可能会在更新中被覆盖,因此您应该将此文件复制到 jail.local
文件并在那里进行调整。
如果您已经有一个 jail.local
文件,请使用 nano
或您最喜欢的文本编辑器打开它:
- sudo nano /etc/fail2ban/jail.local
如果您还没有 jail.local
文件,或者您打开的文件是空白的,请复制 jail.conf
文件,然后打开新文件:
- sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
- sudo nano /etc/fail2ban/jail.local
我们将查看此处可用的选项,并查看此文件如何与系统上的其他配置文件交互。
默认部分
该文件的第一部分将定义 fail2ban 策略的默认值。这些选项可以在每个单独服务的配置部分中被覆盖。
删除注释后,整个默认部分看起来像这样:
[DEFAULT]
ignoreip = 127.0.0.1/8
bantime = 10m
findtime = 10m
maxretry = 3
backend = auto
usedns = warn
destemail = root@localhost
sendername = Fail2Ban
banaction = iptables-multiport
mta = sendmail
protocol = tcp
chain = INPUT
action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s", sendername="%(sendername)s"]
action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s", sendername="%(sendername)s"]
action = %(action_)s
让我们回顾一下其中的一些含义:
- ignoreip:此参数标识应被禁止系统忽略的 IP 地址。默认情况下,这只是设置为忽略来自机器本身的流量,这样您就不会填满自己的日志或将自己锁在门外。
- bantime:此参数设置禁令的长度,以秒为单位。默认值为 10 分钟。
- findtime:此参数设置 Fail2ban 在查找重复失败的身份验证尝试时将注意的窗口。默认设置为 10 分钟,这意味着软件将计算最近 10 分钟内失败的尝试次数。
- maxretry:这设置了在开始禁止之前
findtime
窗口内可以容忍的失败尝试次数。 - 后端:此条目指定 Fail2ban 将如何监控日志文件。
auto
的设置意味着 fail2ban 将尝试pyinotify
,然后是gamin
,然后是基于可用内容的轮询算法。inotify
是一个内置的 Linux 内核功能,用于跟踪文件何时被访问,而pyinotify
是inotify
的 Python 接口,由 Fail2ban 使用。 - usedns:这定义了是否使用反向 DNS 来帮助实施禁令。将此设置为“否”将禁止 IP 本身而不是其域主机名。
warn
设置将尝试查找主机名并以这种方式禁止,但会记录活动以供审查。 - destemail:如果将您的操作配置为邮件警报,这是将发送通知邮件的地址。
- 发件人姓名:这将在生成的通知电子邮件的电子邮件发件人字段中使用
- banaction:设置达到阈值时将使用的操作。这实际上是位于
/etc/fail2ban/action.d/
中名为iptables-multiport.conf
的文件的路径。这会处理实际的iptables
防火墙操作以禁止 IP 地址。我们稍后再看。 - mta:这是将用于发送通知电子邮件的邮件传输代理。
- 协议:这是实施 IP 禁令时将丢弃的流量类型。这也是发送到新 iptables 链的流量类型。
- 链:这是将配置跳转规则以将流量发送到 fail2ban 漏斗的链。
其余参数定义了可以指定的不同操作。他们使用文本字符串中的变量替换传递我们上面定义的一些参数,如下所示:
%(var_name)s
上面的行将替换为 var_name
的内容。使用这个,我们可以知道 action
变量默认设置为 action_
定义(仅禁止,没有邮件警报)。
反过来,这是通过使用执行禁令所需的参数列表(服务名称、端口、协议和链)调用 iptables-multiport
操作来配置的。 __name__
将替换为以下部分标题指定的服务名称。
服务特定部分
在默认部分下方,有特定服务的部分可用于覆盖默认设置。这遵循仅修改与正常值不同的参数的约定(约定优于配置)。
每个部分的标题都是这样指定的:
[service_name]
任何包含 enabled=true
行的部分都将被读取和启用。
在每个部分中,配置参数,包括应该用于解析日志的过滤器文件(减去文件扩展名)和日志文件本身的位置。
请记住这一点,指定 SSH 服务操作的部分如下所示:
[SSH]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 6
这将启用此部分并将端口设置为默认的 \ssh 端口(端口 22)。它告诉 Fail2ban 查看位于 /var/log/auth.log
的日志以获取此部分并使用 /etc/fail2ban/filters.d
目录中名为 sshd.conf
的文件中定义的过滤机制来解析日志。
它需要的所有其他信息都取自 [DEFAULT]
部分中定义的参数。例如,操作将设置为 action_
,这将使用 iptables-multiport
banaction 禁止违规 IP 地址,该操作引用名为 iptables-multiport.xml 的文件。 conf
在 /etc/fail2ban/action.d
中找到。
如您所见,[DEFAULT]
部分中的操作应该是通用且灵活的。使用参数替换以及提供合理默认值的参数,可以在必要时覆盖定义。
检查过滤器文件
为了了解我们的配置中发生了什么,我们需要了解过滤器和操作文件,它们完成了大部分工作。
过滤器文件将确定 fail2ban 将在日志文件中查找的行,以识别违规特征。操作文件实现所有需要的操作,从服务启动时构建防火墙结构,到添加和删除规则,以及服务停止时拆除防火墙结构。
让我们看一下我们的 SSH 服务在上面的配置中调用的过滤器文件:
- sudo nano /etc/fail2ban/filter.d/sshd.conf
[INCLUDES]
before = common.conf
[Definition]
_daemon = sshd
failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from <HOST>( via \S+)?\s*$
^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
^%(__prefix_line)sFailed \S+ for .*? from <HOST>(?: port \d*)?(?: ssh\d*)?(: (ruser .*|(\S+ ID \S+ \(serial \d+\) CA )?\S+ %(__md5hex)s(, client user ".*", client host ".*")?))?\s*$
^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
ignoreregex =
[INCLUDES]
节标头指定在此文件之前或之后读入的其他过滤器文件。在我们的示例中,common.conf
文件被读入并放置在该文件的其他行之前。这会设置一些我们将在配置中使用的参数。
接下来,我们有一个 [Definition]
部分,它定义了我们的过滤器匹配的实际规则。首先,我们使用 _daemon
参数设置我们正在监控的守护进程的名称。
之后,我们通过实际的 failregex
定义,它设置在日志文件中找到匹配行时将触发的模式。这些是根据用户未正确验证时可能抛出的不同错误和失败进行匹配的正则表达式。
%(__prefix_line)s
等行的部分将替换为我们获取的 common.conf
文件中的参数设置值。用于匹配操作系统在使用标准方法时写入日志文件的不同前导信息。例如,/var/log/auth.log
中的某些行可能如下所示:
May 6 18:18:52 localhost sshd[3534]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=101.79.130.213
May 6 18:18:54 localhost sshd[3534]: Failed password for invalid user phil from 101.79.130.213 port 38354 ssh2
May 6 18:18:54 localhost sshd[3534]: Received disconnect from 101.79.130.213: 11: Bye Bye [preauth]
突出显示的部分是操作系统插入以提供更多上下文的标准模式。之后,iptables 防火墙服务将失败尝试写入日志的方式有很多种。
我们在上面的前两行中看到两个单独的失败(PAM 身份验证错误和密码错误)。过滤器中定义的正则表达式旨在匹配任何可能的失败行。您不必调整这些行中的任何一行,但您应该意识到,如果您必须自己创建过滤器文件,则需要捕获所有表示您试图保护的应用程序发生未经授权使用错误的日志条目.
在底部,您可以看到一个 ignoreregex
参数,目前该参数为空。这可用于排除更具体的模式,这些模式通常会匹配失败条件,以防您想要在某些情况下取消 fail2ban 的失败触发器。我们不会对此进行调整。
完成检查后保存并关闭文件。
检查操作文件
现在,让我们看一下操作文件。该文件负责设置防火墙,其结构允许修改以禁止恶意主机,并根据需要添加和删除这些主机。
我们的 SSH 服务调用的操作称为 iptables-multiport
。现在打开关联文件:
- sudo nano /etc/fail2ban/action.d/iptables-multiport.conf
删除注释后,该文件看起来像这样:
[INCLUDES]
before = iptables-blocktype.conf
[Definition]
actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
actioncheck = iptables -n -L <chain> | grep -a 'fail2ban-<name>[ \t]'
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
[Init]
name = default
port = ssh
protocol = tcp
chain = INPUT
该文件从另一个名为 iptables-blocktype.conf
的操作文件开始,该文件定义了 blocktype
参数,该参数配置了在禁止客户端时将设置的限制。默认情况下,blocktype
设置为拒绝数据包并回复被禁止的客户端发送的 ping 请求,并显示端口无法访问的拒绝消息。我们将在下面的禁令规则中使用它。
接下来,我们进入规则定义本身。 actionstart
操作在启动 fail2ban 服务时设置 iptables 防火墙。它创建一个新链,向该链添加一个规则以返回到调用链,然后在 INPUT 链的开头插入一个规则,将匹配正确协议和端口目标的流量传递到新链。
它通过使用我们在 jail.local
文件中定义的 action
传递的值来实现这一点。 name
取自每个服务的部分标题。 chain
、protocol
和 port
取自该文件中的 action
行本身。
在这里,通过将参数名称包含在尖括号中来引用其他文件设置的所有参数:
<param_name>
当我们向下移动到伴随的 actionstop
定义时,我们可以看到防火墙命令正在执行 actionstart
命令的逆转。当 Fail2ban 服务停止时,它会彻底删除它添加的所有防火墙规则。
另一个名为 actioncheck
的操作确保在尝试添加禁令规则之前已经创建了正确的链。
接下来,我们进入实际的禁止规则,称为 actionban
。该规则通过向我们创建的链添加新规则来起作用。该规则与违规客户端的源 IP 地址相匹配——当达到 maxretry
限制时,将从授权日志中读取此参数。它建立由我们在文件顶部的 [INCLUDE]
部分中获取的 blocktype
参数定义的块。
actionunban
规则删除了这条规则。当禁令时间过去后,这是由 fail2ban 自动完成的。
最后,我们到达 [Init]
部分。这只是提供了一些默认值,以防在没有传递所有适当值的情况下调用操作文件。
Fail2ban 服务如何处理配置文件以实施禁令
现在我们已经了解了细节,让我们回顾一下 fail2ban 启动时发生的过程。
加载初始配置文件
首先,读取主 fail2ban.conf
文件以确定主进程应该运行的条件。如有必要,它会创建套接字、pid 和日志文件并开始使用它们。
接下来,fail2ban 读取 jail.conf
文件以获取配置详细信息。随后,它按字母顺序读取 jail.d
目录中以 .conf
结尾的所有文件。它将在这些文件中找到的设置添加到其内部配置中,使新值优先于 jail.conf
文件中描述的值。
然后它搜索一个 jail.local
文件并重复这个过程,以适应新的值。最后,它再次搜索 jail.d
目录,按字母顺序读取以 .local
结尾的文件。
在我们的例子中,我们只有一个 jail.conf
文件和一个 jail.local
文件。在我们的 jail.local
文件中,我们只需要定义与 jail.conf
文件不同的值。 fail2ban 进程现在将一组指令加载到内存中,这些指令代表它找到的所有文件的组合。
它检查每个部分并搜索 enabled=true
指令。如果找到一个,它会使用该部分下定义的参数来构建策略并决定需要执行哪些操作。服务部分中未找到的任何参数都使用 [DEFAULT]
部分中定义的参数。
解析操作文件以确定启动操作
Fail2ban 寻找一个 action
指令来确定调用什么动作脚本来实施禁止/取消禁止策略。如果没有找到,它会退回到上面确定的默认操作。
动作指令包含将读取的动作文件的名称,以及传递这些文件所需参数的键值字典。通过引用服务部分中配置的设置,这些值通常采用参数替换的形式。 \name 键通常传递特殊 __name__
变量的值,该变量将被设置为节标题的值。
Fail2ban 然后使用此信息在 action.d
目录中查找相关文件。它首先查找以 .conf
结尾的关联操作文件,然后使用随附的 .local
文件中包含的任何设置修改在那里找到的信息,该文件也在 中找到action.d
目录。
它解析这些文件以确定它需要采取的操作。它读取 actionstart
值以查看设置环境时应采取的操作。这通常包括创建防火墙结构以适应未来的禁止规则。
此文件中定义的操作使用从 action
指令传递给它的参数。它将使用这些值来动态创建适当的规则。如果某个变量没有设置,它可以查看动作文件中设置的默认值来填补空白。
解析过滤器文件以确定过滤规则
jail.*
文件中的服务参数还包括日志文件的位置以及用于检查文件的轮询机制(这由 后端定义
参数)。它还包括一个过滤器,用于确定日志中的一行是否表示失败。
Fail2ban 在 filter.d
目录中查找以 .conf
结尾的匹配过滤器文件。它读取此文件以定义可用于匹配违规行的模式。然后它会搜索以 .local
结尾的匹配过滤器文件,以查看是否有任何默认参数被覆盖。
它在读取服务的日志文件时使用这些文件中定义的正则表达式。它针对写入服务日志文件的每个新行尝试在 filter.d
文件中定义的每个 failregex
行。
如果正则表达式返回匹配项,它会根据 ignoreregex
定义的正则表达式检查该行。如果这也匹配,fail2ban 将忽略它。如果该行与 failregex
中的表达式匹配但与 ignoreregex
中的表达式不匹配,则会为导致该行的客户端增加一个内部计数器,并且关联的时间戳是为事件创建。
当到达由 jail.*
文件中的 findtime
参数设置的时间窗口时(由事件时间戳确定),内部计数器再次递减并且事件不再被视为与禁止政策相关。
如果在一段时间内记录了额外的身份验证失败,则每次尝试都会增加计数器。如果计数器在配置的时间窗口内达到 maxretry
参数设置的值,fail2ban 通过调用 actioncheck
中定义的服务操作来禁止>action.d/ 服务的文件。这是判断actionstart
动作是否设置必要的结构。然后调用 actionban
操作来禁止违规客户端。它还为此事件设置时间戳。
当 bantime
参数指定的时间过去后,fail2ban 通过调用 actionunban
操作来取消禁止客户端。
结论
到目前为止,您对 fail2ban 的运作方式有了相当深入的了解。当您偏离标准配置时,了解 fail2ban 的功能以便以可预测的方式操纵其行为会很有帮助。
要了解如何使用 fail2ban 保护其他服务,您可以阅读如何在 Ubuntu 22.04 上使用 Fail2Ban 保护 Nginx 服务器。