SSH 是连接云服务器的事实上的方法。它耐用且可扩展——随着新的加密标准的开发,它们可用于生成新的 SSH 密钥,确保核心协议保持安全。然而,没有任何协议或软件堆栈是完全万无一失的,SSH 在互联网上如此广泛的部署意味着它代表了一种非常可预测的方式。攻击面 or 攻击向量人们可以通过它尝试获得访问权限。
任何暴露于网络的服务都是这种方式的潜在目标。如果您查看在任何流量广泛的服务器上运行的 SSH 服务的日志,您经常会看到重复的、系统性的登录尝试,这些尝试代表用户和机器人的暴力攻击。尽管您可以对 SSH 服务进行一些优化,以将这些攻击成功的几率降低到接近于零,例如禁用密码身份验证以支持 SSH 密钥,他们仍然可能构成轻微的、持续的责任。
对于大规模生产部署来说,这种责任是完全不可接受的,通常会实施 VPN,例如线卫在他们的 SSH 服务前面,因此如果没有额外的软件抽象或网关,就不可能从外部互联网直接连接到默认的 SSH 端口 22。这些 VPN 解决方案受到广泛信任,但会增加复杂性,并且可能破坏一些自动化或其他小软件挂钩。
在进行完整的 VPN 设置之前或之外,您可以实施一个名为Fail2ban。 Fail2ban 可以通过创建规则来显着减轻暴力攻击,这些规则会在一定次数的不成功登录尝试后自动更改防火墙配置以禁止特定 IP。这将使您的服务器能够针对这些访问尝试进行自我强化,而无需您的干预。
在另一个教程中,我们讨论了如何使用 Fail2ban 保护 SSH。在本指南中,我们将更深入地讨论 Fail2ban 的实际工作原理以及如何使用这些知识来修改或扩展该服务的行为。
Fail2ban 的目的是监视公共服务的日志,以发现身份验证失败的模式。
当fail2ban配置为监视服务的日志时,它会查看filter已针对该服务进行特定配置。该过滤器旨在通过使用复杂的正则表达式来识别该特定服务的身份验证失败。正则表达式是一个用于模式匹配的通用模板语言。它将这些正则表达式模式定义到一个名为的内部变量中failregex
.
默认情况下,Fail2ban 包含常用服务的过滤器文件。当来自任何服务(例如 Web 服务器)的日志与failregex
在其过滤器中,为该服务执行预定义的操作。这action
是一个变量,可以配置为执行许多不同的操作,具体取决于管理员的偏好。
默认操作是通过修改本地防火墙规则来禁止违规主机/IP 地址。您可以扩展此操作,例如向系统管理员发送电子邮件。
默认情况下,10 分钟内检测到 3 次身份验证失败后将采取行动,默认禁止时间为 10 分钟。这是可配置的。
使用默认值时iptables
防火墙,fail2ban
当服务启动时,创建一组新的防火墙规则,也称为链。它向 INPUT 链添加了一条新规则,将所有指向端口 22 的 TCP 流量发送到新链。在新链中,它插入一条返回到 INPUT 链的规则。如果 Fail2ban 服务停止,则链和关联规则将被删除。
Fail2ban 是通过位于以下层次结构中的多个文件进行配置的/etc/fail2ban/
目录。
The fail2ban.conf
file 配置一些操作设置,例如守护进程记录信息的方式,以及它将使用的套接字和 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 策略的默认值。这些选项可以在每个单独服务的配置部分中被覆盖。
删除注释后,整个默认部分看起来像这样:
/etc/fail2ban/jail.local
[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
实施禁令之前的窗口。
-
backend:此条目指定 Fail2ban 如何监视日志文件。的设置
auto
意味着fail2ban会尝试pyinotify
, then gamin
,然后是基于可用内容的轮询算法。inotify
是一个内置的 Linux 内核功能,用于跟踪文件何时被访问,以及pyinotify
是一个Python接口inotify
,由 Fail2ban 使用。
-
usedns:这定义了是否使用反向 DNS 来帮助实施禁令。将其设置为“no”将禁止 IP 本身,而不是其域名主机名。这
warn
设置将尝试查找主机名并禁止这种方式,但会记录活动以供审查。
-
德斯特邮件:如果将您的操作配置为邮件警报,则这是将发送通知邮件的地址。
-
发件人名称:这将在电子邮件发件人字段中用于生成通知电子邮件
-
禁令:设置达到阈值时将使用的操作。这实际上是位于以下位置的文件的路径
/etc/fail2ban/action.d/
called iptables-multiport.conf
。这处理实际的iptables
操纵防火墙来禁止某个 IP 地址。我们稍后会讨论这个。
-
mta:这是将用于发送通知电子邮件的邮件传输代理。
-
protocol:这是实施 IP 禁令时将丢弃的流量类型。这也是发送到新 iptables 链的流量类型。
-
chain:这是将配置跳转规则以将流量发送到fail2ban 漏斗的链。
其余参数定义可以指定的不同操作。它们使用文本字符串中的变量替换传入我们上面定义的一些参数,如下所示:
%(var_name)s
上面的行将替换为以下内容var_name
。使用这个,我们可以知道action
变量设置为action_
默认定义(仅禁止,无邮件警报)。
反过来,这是通过调用iptables-multiport
带有执行禁止所需的参数列表(服务名称、端口、协议和链)的操作。这__name__
替换为下面部分标题指定的服务名称。
在默认部分下方,有一些针对特定服务的部分,可用于覆盖默认设置。这遵循仅修改与正常值不同的参数的约定(约定优于配置)。
每个节标题的指定如下:
[service_name]
任何有该行的部分enabled = true
将被读取并启用。
在每个部分中,都会配置参数,包括用于解析日志的过滤器文件(减去文件扩展名)以及日志文件本身的位置。
请记住这一点,指定 SSH 服务操作的部分如下所示:
/etc/fail2ban/jail.local
[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_
这将禁止使用有问题的 IP 地址iptables-multiport
banaction,它引用了一个名为iptables-multiport.conf
在发现/etc/fail2ban/action.d
.
正如您所看到的,[DEFAULT]
部分应该是通用的和灵活的。使用参数替换以及提供合理默认值的参数将可以在必要时覆盖定义。
为了了解我们的配置中发生了什么,我们需要了解过滤器和操作文件,它们完成了大部分工作。
过滤器文件将确定fail2ban 将在日志文件中查找的行,以识别违规特征。操作文件实现了所需的所有操作,从服务启动时构建防火墙结构,到添加和删除规则,以及服务停止时拆除防火墙结构。
让我们看看我们的 SSH 服务在上面的配置中调用的过滤器文件:
-
sudo nano/etc/fail2ban/filter.d/sshd.conf
/etc/fail2ban/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 =
The [INCLUDES]
节头指定在此文件之前或之后读入的其他过滤器文件。在我们的示例中,common.conf
文件被读入并放置在该文件中的其他行之前。这设置了我们将在配置中使用的一些参数。
接下来,我们有一个[Definition]
定义过滤器匹配的实际规则的部分。首先,我们使用以下命令设置正在监视的守护进程的名称_daemon
范围。
之后我们再进行实际操作failregex
定义,它设置在日志文件中找到匹配行时将触发的模式。这些是根据用户未正确进行身份验证时可能引发的不同错误和失败进行匹配的正则表达式。
该行的部分如%(__prefix_line)s
将被替换为参数设置中的值common.conf
我们获取的文件。这用于匹配操作系统在使用标准方法时写入日志文件的不同前导信息。例如,一些行来自/var/log/auth.log
可能看起来像这样:
/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
删除注释后,该文件看起来像这样:
/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
action 在fail2ban 服务启动时设置 iptables 防火墙。它创建一条新链,向该链添加一条规则以返回到调用链,然后在 INPUT 链的开头插入一条规则,将与正确协议和端口目标匹配的流量传递到新链。
它通过使用我们传入的值来做到这一点action
我们在我们的定义中定义了jail.local
文件。这name
取自每个服务的节标题。这chain
, protocol
, and port
取自action
将其本身写入该文件中。
在这里,通过将参数名称包含在尖括号中来引用其他文件设置的所有参数:
<param_name>
当我们向下移动到同伴处时actionstop
定义,我们可以看到防火墙命令正在实现逆转actionstart
命令。当 Fail2ban 服务停止时,它会彻底删除它添加的所有防火墙规则。
另一个动作称为actioncheck
确保在尝试添加禁令规则之前已创建正确的链。
接下来,我们讨论实际的禁止规则,称为actionban
。该规则的工作原理是向我们创建的链添加一条新规则。该规则与违规客户端的源 IP 地址相匹配 – 该参数是在以下情况下从授权日志中读取的:maxretry
已达到限制。它建立了由以下定义的块blocktype
我们在中获取的参数[INCLUDE]
文件顶部的部分。
The actionunban
规则删除此规则。当禁止时间过去后,fail2ban 会自动完成此操作。
最后,我们到达[Init]
部分。这只是提供一些默认值,以防调用操作文件而不传入所有适当的值。
现在我们已经了解了具体细节,让我们回顾一下fail2ban 启动时发生的过程。
一、主要fail2ban.conf
读取文件以确定主进程应运行的条件。如果需要,它会创建套接字、pid 和日志文件并开始使用它们。
接下来,fail2ban 读取jail.conf
文件以获取配置详细信息。接下来,它会按字母顺序读取在jail.d
结尾为的目录.conf
。它将这些文件中找到的设置添加到其内部配置中,使新值优先于中描述的值jail.conf
file.
然后它会搜索一个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.*
文件还包括日志文件的位置以及用于检查文件的轮询机制(这是由backend
范围)。它还包括一个过滤器,用于确定日志中的行是否表示失败。
Fail2ban 看起来在filter.d
目录查找以结尾的匹配过滤器文件.conf
。它读取此文件来定义可用于匹配违规行的模式。然后它会搜索以结尾的匹配过滤器文件.local
查看是否有任何默认参数被覆盖。
它在读取服务的日志文件时使用这些文件中定义的正则表达式。它尝试每一个failregex
中定义的行filter.d
文件针对写入服务日志文件的每个新行。
如果正则表达式返回匹配项,它将根据由定义的正则表达式检查该行ignoreregex
。如果这也匹配,fail2ban 会忽略它。如果该行与中的表达式匹配failregex
但与中的表达式不匹配ignoreregex
,对于引起该线路的客户端,内部计数器会递增,并为该事件创建关联的时间戳。
由于设置的时间窗口findtime
中的参数jail.*
文件到达(由事件时间戳确定),内部计数器再次递减,并且该事件不再被视为与禁止策略相关。
如果随着时间的推移,记录了其他身份验证失败,则每次尝试都会增加计数器。如果计数器达到由maxretry
参数在配置的时间窗口内,fail2ban 通过调用actioncheck
中定义的服务操作action.d/
服务的文件。这是为了判断是否actionstart
行动建立了必要的结构。然后它调用actionban
采取措施禁止违规客户。它还为此事件设置时间戳。
当指定的时间已经过去时bantime
参数,fail2ban 通过调用取消禁止客户端actionunban
action.
现在您已经相当深入地了解了fail2ban 的运作方式。当您偏离标准配置时,了解fail2ban 的功能会很有帮助,以便以可预测的方式操纵其行为。
要了解如何使用fail2ban保护其他服务,您可以阅读如何在 Ubuntu 22.04 上使用 Fail2Ban 保护 Nginx 服务器.