nginx批量封禁黑名单ip

nginx批量封禁黑名单ip
  昨天搞到差不多1点,今天又是忙到6点半,连我领导都说“搞得我们加一好憔悴呀”。有很长一段时间没更新博客了,想着怎么做个人IP。。。谋出路
 
一、需求介绍
 废话少说,需求就是怎么批量封禁别人给来的一大堆黑名单ip。甲方每天不定期发来几百、上千个ip,我们的系统前面均有web应用防火墙,之前一直从云厂商的防火墙去封禁,不但要登录云控制台,如果以前添加过一样的ip,会报错重复添加,再者就是ip个数的限制。

 

  如下图就是阿里的自定义规则模版,能添加100个规则,每个规则限制只能添加100个ip。

   所以之前我们手动处理的策略是:

(1)把甲方给来的所有文件 IP 数汇总成一个文件(给来的文件会有中文,然后每个 IP 为一行),然后排序、去重

(2)对 IP 进行分割,阿里、电信用英文逗号,联通是空行

(3)根据平台的限制,手动添加IP数,譬如来了500个 IP,需要先选择处理好的100个 分割好的IP,然后像上图那样设置
可谓烦不胜烦,最恶心的是,人为操作会遗漏、失误,还有最致命一点,基本无法溯源!!!在云控制台查找添加过的重复IP,需要你手动点到每个规则里,逐个查找。阿里是不会告诉你是哪个IP重复添加过的!
 
  昨晚加班就是因为有大范围的IP无法正常访问网站。阿里我们有两个系统需要添加黑名单,其中一个系统添加没事,另一个我已经对着没事的系统比对过,没发出什么端倪,最后折中的方法只能是一边报障一边添加白名单,白名单优先级会高于黑名单。
 
 
二、问题解决

  甲方要我们封禁,不一定要用云厂商的防火墙,封禁可以从操作系统、nginx去处理。试过操作系统的 iptables,没有效果,估计跟防火墙、云安全组的配置有点关系,因为赶着处理,所以决定从nginx出发。

  注意,这里不能直接在location 写 deny,allow,因为服务器前面是防火墙,你这样添加来源都是防火墙的IP。我们应该从真实客户端 IP 去出发。可以通过 nginx 的 X-Forwarded-For 来识别原始请求者的真实 IP 地址,然后提取出第一个 IP 地址,并将其存储在变量 $real_remote_addr,最后定义一个名为 $access_check 的变量映射,用于根据 $real_remote_addr 来决定是否允许或拒绝访问。

  下面是nginx配置文件片段

 1 nginx配置文件nginx.conf,有配置: 
 2 
 3 http {
 4     ...
 5     map $http_x_forwarded_for  $real_remote_addr {
 6     ""    $remote_addr;
 7           ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
 8     }
 9     
10     map $real_remote_addr $access_check {
11         default "allow";
12         "xxx" "deny";
13     }
14     ...
15 }
16 
17 
18 然后虚拟主机配置文件,有配置
19 server
20     ...
21     location / {
22          if ($access_check = "deny") {
23              return 403;
24           }
25     }
26     ...
27 }

  之前 IP 少的时候,我是直接添加  "xxx" "deny";   但如果几千上万的话呢,一会导致文件不好看(密密麻麻的 IP 铺排整个nginx主配置文件),二会维护成本高,于是我问chatgpt,希望以文件方式存储需要封禁 的 黑名单 IP,然后nginx去加载。

  分享链接如下:https://chatgpt.com/share/dbdb364f-7931-4def-ba98-5f7600e1c372

 后面就是它给的参考代码,我直接贴上来,其中需要设置 map 的空间大小参数,不然添加不了那么多IP,而且设置的位置也有考究,具体参考文档:nginx: [emerg] “map_hash_bucket_size” directive is duplicate in /etc/nginx/nginx.conf:60

  前面说到需要对原始 IP 进行处理:排序、去重,然后有个数据库存着到当前为止甲方给来的所有 IP,对于新增 IP,我们需要先看下是否在数据库存在过,不存在需要插入数据库,最后会得到一个处理好的 blocked_ips.conf,格式如下:(这是开发帮忙弄的,感激感激~~)

 我只需要加载这个nginx文件,做相应的配置就行。后续说是开发的程序把处理好的文件放到oss上,服务器定时拉取这个文件,重新加载nginx。

1、nginx主配置文件 nginx.conf

 1 http {
 2     ...
 3     ##   map 指令生成的哈希表需要更多的空间来存储映射关系
 4     ## 放在 map $http_x_forwarded_for 前设置
 5     map_hash_max_size 2048;   
 6     map_hash_bucket_size 2048;
 7 
 8      map $http_x_forwarded_for  $real_remote_addr {
 9     ""    $remote_addr;
10           ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
11     }
12 
13     include /path/blocked_ips.conf;
14      ...
15 }

 

 2、虚拟主机配置文件

server{
        ...
        location / {
             if ($access_check = "deny") {
                   return 403;
                }
              proxy_pass http://后端服务;

         ...
        error_page   404 403 /haha_404;
        location ~* /haha_404 {
               root /path;
               index index.html;
        }
}

 

最后我贴下 chatgpt 的脚本 generate_nginx_conf.sh

blocked_ips.txt 是一个你去重完的 ip 文件,格式是

1.11.11.11
192.168.1.100
10.0.0.1
...

 

 generate_nginx_conf.sh

 1 #!/bin/bash
 2 
 3 # 定义输入和输出文件路径
 4 INPUT_FILE="blocked_ips.txt"
 5 OUTPUT_FILE="blocked_ips.conf"
 6 
 7 # 初始化输出文件
 8 echo "map \$real_remote_addr \$access_check {" > $OUTPUT_FILE
 9 echo "    default \"allow\";" >> $OUTPUT_FILE
10 
11 # 读取输入文件并生成输出文件内容
12 while IFS= read -r ip
13 do
14   echo "    \"$ip\" \"deny\";" >> $OUTPUT_FILE
15 done < "$INPUT_FILE"
16 
17 # 关闭 map 块
18 echo "}" >> $OUTPUT_FILE

 实际就是把 ip 文件变成 这种方式:

map $real_remote_addr $access_check {
default "allow";
"被封禁 IP" "deny";
"被封禁 IP-2" "deny";

}

 只是现在都让开发帮处理了,哈哈
 
posted @ 2024-07-26 22:52  windysai  阅读(91)  评论(0编辑  收藏  举报