使用 ModSecurity 阻止 IP。
使用 mod_security 阻止任意数量的 IP 地址。我们可以动态地进行,而无需每次都重新启动 Apache Web 服务器。
我们可以使用 Apache 模块 mod_security 动态地将任何 IP 添加到黑名单(或白名单)。在互联网上搜索后,我们发现没有现成的解决方案,这真的很令人惊讶,但阻止任何 IP 的此功能非常重要,因为与所有其他网站一样,我们每天也会收到大量来自少数 IP 的不希望的 HTTP 请求。因此,我们继续实施了一个阻止此类 IP 的解决方案,我们很乐意与您分享该解决方案。
通常在 Linux 中,iptables
用于阻止任何不需要的 IP 或 IP 范围。下面给出了一些使用 iptables
阻止 IP 的示例用法。
# Blocks the specified IP
iptables -A INPUT -s 0.0.0.1 -j DROP
# Blocks the specified IP range
iptables -A INPUT -m iprange --src-range 0.0.0.0-0.0.0.255 -j DROP
# Same as above, but using CIDR notaion
iptables -A INPUT -s 0.0.0.0/24 -j DROP
如果机器直接连接到互联网,没有代理或负载均衡器在前面,那么 iptables
是最方便的。但在 hudku.com,我们前面有 Elastic Load Balancer,因为我们的网站运行在 AWS Elastic Beanstalk 应用程序上,在 Linux 上运行 Apache。所以我们得到的 IP 始终是负载均衡器的 IP,只有在 HTTP 标头“X-Forwarded-For
”中,我们才能获得请求客户端的 IP。iptables
只能处理 IP,我们无法使其使用 HTTP 标头中的值。
因此,我们被迫寻找替代解决方案,并决定使用 mod_security
来实现此功能。该逻辑是通过利用 mod_security
提供的“Collections”来实现的。Collections 不过就是一个 HashMap
。IP 地址是键,值是整数。如果值为 1
,则我们将其解释为白名单 IP,如果值为 2
,则我们认为该 IP 是黑名单 IP。如果键不存在,则 IP 既不在白名单也不在黑名单中,因此被正常处理。
如果我们在白名单中找到一个 IP,就不会再进行任何检查,请求也会被接受。
如果在黑名单中找到一个 IP,我们会丢弃连接并将其记录在 modsecurity
日志文件中。
要将 IP 添加到黑名单,我们只需访问应用程序的特定 URL,并将 IP 作为参数传递。这就像 www.example.com/ip/blacklist?ip=0.0.0.1 一样简单。同样,要将 IP 添加到白名单,则是 www.example.com/ip/whitelist?ip=0.0.0.2。要从白名单和黑名单中删除 IP,命令是 www.example.com/ip/remove?ip=0.0.0.3,它会从 Collection (Map) 中删除 IP(如果存在)。
然后,最重要的是确保这些命令只有在从当前机器,即 localhost 发出时才会被接受。
安装 mod_security
后,它会创建一个名为“modsecurity.d”的目录,并加载该目录中所有带有“.conf”扩展名的文件。
掌握了以上所有信息后,我们现在可以查看位于 modsecurity.d 目录下的 my_rules.conf 文件中的源代码了。
#
# Adding / Removing an ip to Dynamic Whitelist & Blacklist - allowed only for localhost
#
# Remove from Dynamic Whitelist & Blacklist - remove allowed variable
SecRule REQUEST_FILENAME "^/ip/remove$"
"chain,phase:1,t:none,deny,nolog,status:200"
SecRule REMOTE_ADDR "^127.0.0.1$" "chain,t:none"
SecRule ARGS:ip "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"
"t:none,initcol:ip=%{args.ip},setvar:!ip.allowed"
#
# Add to Dynamic Whitelist - allowed value is 1
SecRule REQUEST_FILENAME "^/ip/whitelist$"
"chain,phase:1,t:none,deny,nolog,status:200"
SecRule REMOTE_ADDR "^127.0.0.1$" "chain,t:none"
SecRule ARGS:ip "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"
"t:none,initcol:ip=%{args.ip},setvar:ip.allowed=1"
#
# Add to Dynamic Blacklist - allowed value is 2
SecRule REQUEST_FILENAME "^/ip/blacklist$"
"chain,phase:1,t:none,deny,nolog,status:200"
SecRule REMOTE_ADDR "^127.0.0.1$" "chain,t:none"
SecRule ARGS:ip "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"
"t:none,initcol:ip=%{args.ip},setvar:ip.allowed=2"
# Allow any request from localhost
SecRule REMOTE_ADDR "^127.0.0.1$"
"phase:1,t:none,allow,nolog,ctl:ruleEngine=off"
#
# Initialize IP Collection using the IP address obtained from x-forwarded-for or remote_addr
#
SecRule REQUEST_HEADERS:x-forwarded-for "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"
"phase:1,t:none,pass,nolog,capture,setvar:tx.client_ip=%{tx.1}"
SecRule &TX:CLIENT_IP "@eq 0"
"phase:1,t:none,pass,nolog,setvar:tx.client_ip=%{remote_addr}"
SecRule &TX:CLIENT_IP "!@eq 0"
"phase:1,t:none,pass,nolog,initcol:ip=%{tx.client_ip}"
#
# Process Dynamic Whitelist & Blacklist
#
# Allow if IP is present in Dynamic Whitelist
SecRule IP:ALLOWED "@eq 1" "phase:1,t:none,allow,nolog,ctl:ruleEngine=off"
#
# Drop if IP is present in Dynamic Blacklist
SecRule IP:ALLOWED "@eq 2" "phase:1,t:none,drop,log,logdata:'Dynamic Blacklist'"
第一段代码处理“remove”请求,用于从白名单和黑名单中删除 IP。第二行是进行重要检查的地方,以确保所有这些请求只有在从 localhost (127.0.0.1) 发出时才会被接受。然后,在第三行,IP 从集合中删除。集合的名称是“ip
”,我们使用的变量或计数器的名称是“allowed
”。
同样,下一个块处理包含在白名单中的请求,而下一个块处理黑名单。所有这些块都首先初始化集合“ip
”,然后设置变量“allowed
”的值。
然后,我们做一个简单的检查,允许所有来自 localhost 的请求,而不应用任何进一步的规则。
下一节实际上初始化了 modsecurity
集合。我们首先从 HTTP 标头中提取 IP
X-Forwarded-For
并将其存储在 TX:CLIENT_IP
中。此集合“TX
”属于 modsecurity
,并且通常可用。
如果 X-Forwarded-For
不包含任何 IP,那么我们尝试使用 remote_addr
变量中的可用值。
下一行使用 mod_security
操作符“initcol”并提供当前 IP 地址来初始化我们名为“ip
”的集合。
在最后一个块中,第一行检查变量“allowed
”的值。如果为 1
,则表示这是一个白名单 IP,因此允许其继续进行 HTTP 请求,而无需进一步检查,方法是关闭规则引擎。
最后一行检查它是否是黑名单 IP。如果是,则丢弃连接并记录信息。
以下是将 IP 添加到黑名单、白名单或从两者中删除的示例用法。我们使用 Linux curl
命令来访问 URL,我们只对 HTTP 响应代码感兴趣,可以使用“http_code
”选项获取。
# Blacklist few IPs
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/blacklist?ip=0.0.0.1
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/blacklist?ip=0.0.0.2
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/blacklist?ip=0.0.0.3
# Whitelist few IPs
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/whitelist?ip=0.0.1.1
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/whitelist?ip=0.0.1.2
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/whitelist?ip=0.0.1.3
# Remove IP from both the lists if present
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/remove?ip=127.0.0.1
对白名单或黑名单所做的更改即使在重启后仍然有效,因为我们使用的是 mod_security
的持久化集合。它将此数据存储在指令 SecDataDir 中指定的目录中。但是,如果我们想将此列表维护在数据库中,那么我们可以在执行 curl
命令之前或之后使用一些 bash 脚本来完成此工作。
就这样,大家。我们现在可以使用 mod_security
成功阻止任意数量的 IP 地址。我们可以动态地进行,而无需每次都重新启动 Apache Web 服务器。
如果您发现此帖子有用,请使用社交按钮“分享”。