您可能没有意识到,但您的网站正受到持续威胁。 如果它运行的是 WordPress,机器人会试图向您发送垃圾邮件。 如果它有一个登录页面,就会存在暴力密码攻击。 您可能还会将搜索引擎蜘蛛视为不受欢迎的访问者。
保护您的网站免受不必要的、可疑的和恶意的活动的侵害并非易事。 Web应用防火墙(例如我们的合作伙伴提供的NGINX App Protect和NGINX Plus 认证模块)是有效的工具,必须将其视为安全堆栈的一部分。 对于大多数环境来说,不存在过度的安全,而多层方法始终是最有效的。
在这篇博文中,我们将讨论使用fail2ban作为Web 安全堆栈的另一层。 Fail2ban 是一种入侵检测系统 (IDS),它持续监控日志文件中的可疑活动,然后采取一个或多个预配置的操作。 通常,fail2ban 会监控失败的登录尝试,然后在一段时间内阻止(禁止)有问题的 IP 地址。 这是针对暴力密码攻击的简单但有效的防御方法。
在NGINX Plus R13中,我们引入了原生键值存储和新的 NGINX Plus API。这允许提供丰富的动态配置解决方案,其中 NGINX Plus 配置和行为可以由外部系统驱动。 在这篇博文中,我们还将展示如何使用 fail2ban 自动重新配置 NGINX Plus 以忽略导致多次身份验证失败事件的 IP 地址的请求。
编辑器 - 在NGINX Plus R16及更高版本中,键值存储可以在集群中的所有 NGINX Plus 实例之间同步。 (集群中的状态共享也适用于其他 NGINX Plus 功能。) 有关详细信息,请参阅我们的博客和NGINX Plus 管理指南。
NGINX Plus 键值存储是原生的内存存储,具有三个主要特征:
键值存储是通过创建由keyval_zone
指令命名的共享内存区域来定义的。 然后,可以使用 HTTP POST
方法向 API 提交 JSON 对象,用一组初始值填充此键值存储。然后, keyval
指令定义将使用哪个现有变量作为查找键(示例中为$remote_addr
),以及将根据该键的对应值进行评估的新变量的名称( $num_failures
)。
通过将位置
块指定为 NGINX Plus API 端点来启用该 API。
服务器 {
听 1111;
allow 127.0.0.1; # 仅允许从本地主机访问,
拒绝所有;#并阻止远程访问。
位置/api {
api write=on; # NGINX Plus API 端点处于读/写模式
}
}
keyval_zone区域=拒绝列表:1M;
键值 $remote_addr $num_failures zone=denylist;
服务器 {
听80;
地点 / {
根目录/usr/share/nginx/html;
如果 ($num_failures) {
返回 403;
}
}
}
vim:语法=nginx
在我们向其中添加键值对之前,对拒绝名单存储内容的请求会返回一个空的 JSON 对象。
$ curl http://localhost:1111/api/6/http/keyvals/denylist {}
现在可以使用 HTTP POST
方法(以curl
命令的-d
参数的形式)向键值存储中填充初始键值对,以提交新的 JSON 对象。 可以将多个键值对通过 POST
发送到一个空的键值存储中,然后再单独发送。
$ curl -iX POST -d '{"10.0.0.1":"1"}' http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 201 已创建...
通过使用null
值对键进行PATCH
来删除键值对。
$ curl -iX PATCH -d '{"10.0.0.1":null}' http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 204 无内容...
通过发送DELETE
方法可以从键值存储中删除所有键值对。
$ curl -iX DELETE http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 204 无内容...
现在可以配置 IP 拒绝列表的简单实现。
服务器 {
听 1111;
allow 127.0.0.1; # 仅允许从本地主机访问,
拒绝所有;#并阻止远程访问。
位置/api {
api write=on; # NGINX Plus API 端点处于读/写模式
}
}
keyval_zone区域=拒绝列表:1M;
键值 $remote_addr $num_failures zone=denylist;
服务器 {
听80;
地点 / {
根目录/usr/share/nginx/html;
如果 ($num_failures) {
返回 403;
}
}
}
vim:语法=nginx
此配置片段将端口 80 配置为 Web 服务器。 第 18 行将$num_failures
变量评估为与$remote_addr
变量(客户端 IP 地址)对应的键匹配的拒绝列表共享内存区域中键值对的值部分。 评估$num_failures
的过程可以更清楚地按顺序表达:
$remote_addr
的值传递给拒绝列表键值存储$remote_addr
与某个键完全匹配,则获取该键值对的值$num_failures
因此,如果客户端 IP 地址已通过POST
发送到键值存储,则所有请求都会导致 HTTP403
禁止
错误。 与使用地
图块实现相同目的相比,这种方法的优点在于 IP 拒绝列表可以由外部系统控制。
如上所述,fail2ban 通常用于检测日志文件中的可疑和/或恶意活动,然后采取措施保护系统。 默认操作是配置iptables
以丢弃来自已记录 IP 地址的所有数据包。 这种方法虽然可以有效阻止恶意行为者,但会给真正的用户带来非常糟糕的用户体验,尤其是在他们忘记密码的情况下。 对于这些用户来说,该网站似乎根本就不可用。
以下配置描述了 fail2ban操作,该操作将有问题的 IP 地址提交给 NGINX Plus拒绝列表键值存储。 然后,NGINX Plus 会显示一个更有用的错误页面,同时对该 IP 地址应用请求速率限制,以便如果该操作是攻击的一部分,则可以有效地消除攻击。
我们假设 fail2ban 默认安装在与 NGINX Plus 相同的主机上,所有配置都在/etc/fail2ban目录下。
以下 fail2ban 操作使用 NGINX Plus API 在拒绝列表键值存储中添加和删除“被禁止”的 IP 地址,方式与上面的简单示例相同。 我们将nginx-plus-denylist.conf文件放在/etc/fail2ban/action.d目录中。
[定义]
actionban = curl -s -o /dev/null -d '{"":""}' http://localhost:1111/api/6/http/keyvals/denylist
actionunban = curl -s -o /dev/null -X PATCH -d '{"":null}' http://localhost:1111/api/6/http/keyvals/denylist
以下 fail2ban jail启用内置过滤器,该过滤器使用 NGINX 的HTTP 基本身份验证模块检测失败的登录尝试,并应用nginx-plus-denylist.conf中定义的操作。 还有很多其他内置过滤器可用,可以轻松创建以检测 NGINX 访问日志中的不需要的活动。
[默认]
bantime = 120
banaction = nginx-plus-denylist
[nginx-http-auth]
enabled = true
以下 NGINX Plus 配置代码片段将 HTTP Basic 身份验证应用于默认的“欢迎使用 NGINX”页面。 我们将password_site.conf文件放在/etc/nginx/conf.d目录中。
服务器 {
听 1111;
allow 127.0.0.1; # 仅允许从本地主机访问,
拒绝所有;#并阻止远程访问。
位置/api {
api write=on; # NGINX Plus API 端点处于读/写模式
}
}
keyval_zone区域=denylist:1M状态=denylist.json;
键值 $remote_addr $num_failures zone=denylist;
limit_req_zone $binary_remote_addr 区域=20permin:10M 速率=20r/m;
服务器 {
听80;
根目录/usr/share/nginx/html;
地点 / {
auth_basic“关闭网站”;
auth_basic_user_file用户.htpasswd;
如果 ($num_failures) {
重写^.*/banned.html;
}
}
位置 = /banned.html {
limit_req区域=20permin突发=100;
}
}
vim:语法=nginx
第 11 行定义keyval_zone
与上面的denylist_keyval.conf相同,但增加了state
参数,该参数指定一个文件,其中存储键值存储的状态,并且在 NGINX Plus 停止和重新启动时仍然存在。 键值存储在常规配置重新加载后仍然存在,无需指定状态文件。 (第 1-10 行未显示,但与denylist_keyval.conf中的相同。)
第 14 行创建一个名为20permin的共享内存区域,并指定每个客户端 IP 地址的最大请求率为每分钟 20 个请求。 当 fail2ban 将 IP 地址添加到拒绝列表时,将应用此速率限制(第 30 行)。
服务器
块定义了一个在默认端口(80)上监听的 Web 服务器,其中所有请求都由位置
/
块匹配(第 20 行)。 root
指令(第 18 行)指定了我们的 Web 内容所在的位置。 第 21-22 行指定此站点需要 HTTP 基本身份验证,并且授权用户的密码数据库位于文件users.htpasswd中。 auth_basic_user_file
指令的文档描述了该文件的格式。 请注意,为了测试目的,此配置未加密,任何使用 HTTP 基本身份验证的生产网站都应使用SSL/TLS来确保传输安全。
当客户端 IP 地址被列入黑名单时(第 24-26 行),不会返回 HTTP403
错误代码,我们将请求重写为/banned.html ,然后在与该 URI 完全匹配的位置
块中进行处理(第 29 行)。 这将返回一个静态网页,说明由于登录失败次数过多,用户的 IP 地址已被阻止。 它还应用限制速率限制(第 30 行),防止恶意客户端不必要地消耗系统资源。
(此示例网页的 HTML 可在本帖的 GitHub 存储库中以banned.html的形式获取。)
我们可以模拟连续失败的登录尝试和相应的fail2ban活动,如下所示。
$ curl -i http://admin:password@www.example.com/HTTP/1.1 401 未授权...
$ curl -i http://admin:admin@www.example.com/
HTTP/1.1 401 未授权...
$ curl -i http://admin:pass1234@www.example.com/
HTTP/1.1 401 未授权...
$ curl -i http://admin:letmein@www.example.com/
HTTP/1.1 401 未授权...
$ curl -i http://admin:fred@www.example.com/
HTTP/1.1 401 未授权...
$ curl http://admin:P@ssw0rd@www.example.com/
<!DOCTYPE html>
<html>
<head>
<title>禁止</title>
...
$ 尾部-f /var/log/fail2ban.log
2017-09-15 13:55:18,903 fail2ban.filter [28498]: 信息 [nginx-http-auth] 找到 172.16.52.1 2017-09-15 13:55:28,836 fail2ban.filter [28498]: 信息 [nginx-http-auth] 找到 172.16.52.1 2017-09-15 13:57:49,228 fail2ban.filter [28498]: 信息 [nginx-http-auth] 找到 172.16.52.1 2017-09-15 13:57:50,286 fail2ban.filter [28498]: 信息 [nginx-http-auth] 找到 172.16.52.1 2017-09-15 13:57:52,015 fail2ban.filter [28498]: 信息 [nginx-http-auth] 找到 172.16.52.1 2017-09-15 13:57:52,088 fail2ban.actions [28498]: 通知 [nginx-http-auth] 禁止 172.16.52.1 2017-09-15 13:59:52,379 fail2ban.actions [28498]: 通知 [nginx-http-auth] 解除对 172.16.52.1 的禁令
此动态 IP 拒绝列表解决方案现在可以运行,无需任何进一步的配置更改。 Fail2ban 监视 NGINX 日志文件,并使用 API 将禁止的 IP 地址添加到 NGINX Plus 键值存储中。120 秒后(在jail.local中配置的禁止时间
),再次使用 NGINX Plus API 将违规 IP 地址从拒绝列表中删除,并再次接受来自该地址的登录尝试。
NGINX Plus API 和键值存储使这种类型的集成成为可能。 现在无需构建 NGINX Plus 配置文件和执行重新加载即可实现高级配置解决方案。 我们很乐意了解您如何使用这些功能来创建自己的动态配置解决方案。 请在下面添加评论。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”