在CentOS上使用iptables自动封IP 配合ipset自动封IP教程

之前搬主题有介绍利用系统的自带防火墙手动封IP的教程,有的人觉得很麻烦,这里搬主题再分享一下在CentOS上使用iptables自动封IP及配合ipset自动封IP教程。如果利用好的话,实际是功能很强大的。

Linux上用iptables自动封ip的bash脚本,这个还是有一定效果的。CentOS 内置了一个非常强劲的防火墙,统称为 iptables,但更正确的名称是 iptables/netfilter。iptables 是一个用户空间的模块。作为用户,你在命令行就是通过它将防火墙规则放进缺省的表里。netfilter 是一个核心模块,它内置于内核中,进行实际的过滤。iptables 有很多前端图像界面可以让用户新增或定义规则,但它们很多时不及使用命令行般有灵活性,而且限制用户了解实际发生的事情。

1. 安先装iptables

这里搬主题就不多描述了,如果是CentOS 6的话,自带iptables防火墙,如果是CentOS 7的话,那还需要卸掉原有防火墙,然后安装iptables防火墙。

2. 下面的脚本保存为drop_ips.sh

#!/bin/bash                        
     
    ###########################################
    # 封锁ip 用iptables                 
    # usage:                            
    # 
    # create date 2010-11-11            
    # update date 2010-11-12            
    ###########################################
     
     
    # 定义端口                          
    CHK_PORT="80 25" 
     
    # 定义输出文件                      
    IPTABLE_OUTPUT=/tmp/ip_drop_tables  
    # 定义输出文件备份  
    IPTABLE_OUTPUT_BAK=/tmp/ip_drop_tables.bak  
     
    # 扫描ip的 间隔时间  
    SCAN_HTTP_IP_TIMEOUT=20  
     
    # 处理ip的间隔时间  
    HANDLE_IP_TIMEOUT=120  
     
    # 连接数量最大限制  
    MAX_CONNECT_IP_NUM=100  
     
    # 排除在外的ip  
    ACCEPT_IP="203.95.110.2" 
     
    # 已经封锁的ip   
    DROP_IP_RECORD_FILE=/tmp/drop_ip_record  
     
    #################################################################  
    #定义方法  
    #################################################################  
    # 输出ip到文件  
    output_ip_table()  
    {  
            # 拿到端口号  
            port_num=$1  
            # 查此端口上的连接ip 输出到指定的目录  
            #echo "start scan ......"  
            netstat -na --tcp| grep ESTABLISHED | awk '{ if ( index($4,":"'"$port_num"'"") ) print $5}' | awk -F ':' '{print $1}' | sort  >> $IPTABLE_OUTPUT  
            #echo "scan end ......"  
    }  
     
     
    # 把需要观测端口列出                                 
    check_port()  
    {  
            for port_td in $CHK_PORT  
            do 
            # echo "port : "$port_td  
                # 扫描此端口  
            output_ip_table $port_td  
        done  
    }  
     
     
    # 封杀ip  
    drop_ip_from_table()  
    {  
        iptables -I INPUT -s "$1" -j DROP  
    }  
     
    # 排除ip  
    accept_ip()  
    {  
        for access_ip in $ACCEPT_IP  
        do 
            iptables -I INPUT -s "$access_ip" -j ACCEPT  
        done  
    }  
     
    # 提取需要的ip  
    get_iptable()  
    {  
        # 如果已经存在就删除  
        if [ -e $IPTABLE_OUTPUT_BAK ] ; then  
            rm -rf $IPTABLE_OUTPUT_BAK  
        fi  
        # copy 一份出去   
        cp $IPTABLE_OUTPUT $IPTABLE_OUTPUT_BAK  
        # 排序 数组  
        declare -a ip_array_org=($(cat ${IPTABLE_OUTPUT_BAK} | sort))  
        # 循环  
        # 比对用的ip 初始化  
        tmp_ip=0.0.0.0  
        let "tmp_ip_count=1" 
        for tmp_element in "${ip_array_org[@]}" 
        do            
            # 初始化 没有特殊设置为排除ip  
            is_not_set_accept="true" 
            # 初始化 是否已封杀了  
            is_not_drop="true" 
            # 如果相等  
            if [ "$tmp_ip" = "$tmp_element" ] ; then   
                let "tmp_ip_count+=1" 
            else              
                # 打印  
                echo "ip: $tmp_ip count: $tmp_ip_count"   
                # 如果大于某个数字 就封杀  
                if (( $tmp_ip_count >= $MAX_CONNECT_IP_NUM )) ; then  
                    # 如果没有记录就封杀  
                    if  cat /tmp/drop_ip_record | grep "$tmp_ip" > /dev/null   ; then  
                        echo "this ip $tmp_ip has been mask !" 
                        is_not_drop="false" 
                    else      
                        # 循环 需要排除ip  
                            for tmp_access_ip in $ACCEPT_IP  
                        do 
                            # 如果排除ip里有 就去封锁此ip  
                            if [ "$tmp_access_ip" = "$tmp_ip" ] ; then   
                                echo "this ip $tmp_ip was mark to accept !" 
                                is_not_set_accept="false" 
                            fi  
                        done   
                    fi  
                    if [ $is_not_set_accept = "true" ] && [ $is_not_drop = "true"] ; then  
                        echo "add a new ip to drop : $tmp_ip" 
                        drop_ip_from_table $tmp_ip  
                        # 记录ip  
                        echo "$tmp_ip" >> $DROP_IP_RECORD_FILE  
                    fi  
                fi  
                # 归零  
                let "tmp_ip_count=1" 
                tmp_ip=$tmp_element  
            fi  
        done  
        # 全部处理完了 删除原件  
        rm -rf $IPTABLE_OUTPUT  
        # 排除ip  
        # accept_ip  
          
    }  
     
    # 扫描ip  
    scan_http_access_ip()  
    {  
        # 获取当前时间作为开始时间  
        start_time=`date +%s`  
            # 循环开始    
        while true 
        do 
            # 开始检查 扫描ip  
            check_port  
            # 线程停止  
            sleep $SCAN_HTTP_IP_TIMEOUT  
            # 获取当前时间  
            cur_time=`date +%s`  
            # 时间差  
            let "time_out=$cur_time-$start_time" 
            echo "time_out : "$time_out  
            # 超过2分钟  
            if (( $time_out >= $HANDLE_IP_TIMEOUT )) ; then  
                # 整理一次ip表  
                echo " times up"              
                get_iptable           
                # 重置开始时间  
                start_time=`date +%s`  
            fi  
        done      
    }  
     
    # 程序执行入口  
    main_app()  
    {  
        # 定时扫描ip  
        scan_http_access_ip  
    }  
     
    main_app 

然后执行下面的脚本,让其自动在后台运行。

sh ~/scripts/drop_ips.sh &

以上是封少量的IP的教程,但是大家知道,iptables封掉少量ip处理是没什么问题的,但是当有大量ip攻击的时候性能就跟不上了,iptables是O(N)的性能。而ipset就像一个集合,把需要封闭的ip地址放入这个集合中,ipset 是O(1)的性能,用的hash方式所以特别快。之前搬主题也介绍了【WordPress设置CDN被CC攻击设置防火墙封IP防护图文教程

一、软件及安装

    1、iptables(一般Linux都已经安装好的)

    2、ipset:

ubuntu:apt-get install ipset

二、ipset的使用

1、查看ip集的列表信息

ipset list

2、创建ip集

ipset create XXXX hash:ip maxelem 100000 timeout 3600

XXXX:ip集的名字

hash:ip :为指定类型,还有其他好些类型,比如hash:net,hash:net,net等,具体可以man ipset

100000:为最大保存ip的数量 

timeout: 为封闭ip的默认时间,这个参数可以不写,这样就永不解封,除非手动解封

3、增加ip地址到ip集

ipset add xxxx 1.1.1.1

增加网段

ipset add xxxx 1.1.1.0/24

4、删除指定的ip或网段

ipset del xxxx 1.1.1.1
ipset del xxxx 1.1.1.0/24

5、保存ip集到文件

ipset save xxxx>ipset_list.txt
6、还原ip集

ipset restore <ipset_list.txt

三、自动封禁和解封

1、创建ip集

ipset create forbidip hash:ip timeout 172800

2、创建iptables条目

iptables -A INPUT -p tcp -m set --match-set forbidip src -m multiport --dports 443,80 -j DROP

或者

iptables -A INPUT -p tcp -m set --match-set forbidip src -m multiport -j DROP

3、根据条件判断需要封闭的ip

    条件:在上一分钟的nginx的请求中,单一ip请求数超过1000及request_uri中包含passwd字符串的ip都直接封禁,1小时后自动解禁。脚本如下

#!/bin/bash
FILES="/data/nginx_log/xxxxx/access.log"
DATE=`date -d '1 minutes ago' +%Y:%H:%M`
grep ${DATE} ${FILES}|awk -F'|' '{print $3}'|sort -n|uniq -c |sort -nr|head -1>/tmp/ips
grep ${DATE} ${FILES}|grep -i passwd|awk -F'|' '{print $3}'|sort -n|uniq>/tmp/ippwd
NUM=`awk '{print $1}' /tmp/ips`
IP=`awk '{print $2}' /tmp/ips`
IP2=`cat /tmp/ippwd`
threshold=1000
if [[ $NUM -gt $threshold ]];then
/sbin/ipset -! add forbidip $IP timeout 3600
fi
if [ -s /tmp/ippwd ];then
for i in $IP2
do
/sbin/ipset -! add forbidip $i
done
fi

4、脚本自动运行

在crontab中添加此脚本的自动运行

*/1 * * * * bash /path/to/script.sh
千百度
© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容