最近,我的一个小网站被攻击了,今天跟大家讲讲是咋回事?
故事背景
我的一个小网站,部署在 4 核 8G 单机上,最近总是收到云监控报警。
查了 log 发现,我是被流量攻击了。
调研了一下云平台的安全服务,还挺贵,弃疗了。
我还是自己写吧。
实现思路
我之前的思路是配置 nginx 的黑名单,每次添加新的黑名单之后,还要重启 Nginx。
后来发现不用这么麻烦,直接用 Linux 的 iptables 防火墙,既高效又简单。连 403 都不要那些坏人看到!
我的实现思路如下:
-
监测 log 日志,分析访问 ip。 -
首先,我的小网站访问量不高。如果某个 ip 一天访问超过 10 次,我就觉得有鬼了。于是,我决定,每天 ip 的访问次数超过 100 就果断封掉。(为了避免误伤,我暂时把这个阈值设置成 100,后面看情况动态调整。)
功能拆解
-
编写脚本,按日期拆分 access.log(之前犯懒没做日志拆分,只是定时删除了访问日志)。 -
编写定时任务,每天 0 点拆分访问日志。 -
编写脚本,分析 access.log 访问日志,封禁当天访问次数超过 100 的 ip。 -
编写定时任务,每 10 分钟执行一次封禁 ip 脚本。
具体实现
1. 日志拆分脚本
具体代码如下(其中LOG_PATH
和PID
换成你自己的路径):
# 每天0点执行日志按日期分隔脚本 0 0 * * * cd /www/Home/ && ./log_cut.sh
#!/bin/bash
#此脚本⽤于⾃动分割Nginx的⽇志,包括access.log
#每天00:00执⾏此脚本将前⼀天的access.log重命名为access-xxxx-xx-xx.log格式,并重新打开⽇志⽂件
#Nginx⽇志⽂件所在⽬录 todo 换成你自己的
LOG_PATH=/data/logs/nginx/
#获取昨天的⽇期
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
#获取pid⽂件路径 todo 换成你自己的
PID=/var/run/nginx.pid
#分割⽇志
mv ${LOG_PATH}access.log ${LOG_PATH}access-${YESTERDAY}.log
#向Nginx主进程发送USR1信号,重新打开⽇志⽂件
kill -USR1 `cat ${PID}`
2. 编写定时任务
# 每天0点执行日志按日期分隔脚本
0 0 * * * cd /www/Home/ && ./log_cut.sh
-
&& 表示将两个命令连接在一起,先执行前面的,再执行后面的。 -
如果你不知道 crontab 的定时任务怎么编写,强烈建议你使用这个工具:「Crontab 在线工具」,使用效果如下:
3. 编写封禁 ip 脚本
注释写得很清楚了:
-
logdir
替换成你自己的地址. -
/tmp/nginx_deny.log
也可以设置成你自己的地址,或者用这个地址也可以。
# 每十分钟执行一次封禁ip脚本 */10 * * * * cd /www/Home/ && ./blackip.sh
#!/bin/bash
logdir=/data/logs/nginx/access.log #nginx访问日志文件路径
port=443
#循环遍历日志文件取出访问量大于100的ip(忽略自己本地ip)
for drop_ip in $(cat $logdir | grep -v '127.0.0.1' | awk '{print $1}' | sort | uniq -c | sort -rn | awk '{if ($1>100) print $2}'); do
# 避免重复添加
num=$(grep ${drop_ip} /tmp/nginx_deny.log | wc -l)
if [ $num -ge 1 ]; then
continue
fi
# shellcheck disable=SC2154
iptables -I INPUT -p tcp --dport ${port} -s ${drop_ip} -j DROP
echo ">>>>> $(date '+%Y-%m-%d %H%M%S') - 发现攻击源地址 -> ${drop_ip} " >>/tmp/nginx_deny.log #记录log
done
4. 编写封禁 ip 定时任务
# 每十分钟执行一次封禁ip脚本
*/10 * * * * cd /www/Home/ && ./blackip.sh
封禁效果
好了,妈妈再也不用担心我被莫名其妙地攻击了。
思考
如果自己人的 ip 不小心被封了怎么办呢?
别担心,可以用下面的命令:
#清空屏蔽IP
iptables -t filter -D INPUT -s 1.2.3.4 -j DROP
#一键清空所有规则
iptables -F
为了满足爱学习的小伙伴,附上 iptables 常用命令。
iptables 常用命令
1.清除 iptables(常用)
iptables -F
2. 备份 iptables(常用)
iptables-save > iptables.txt
3. 导入 iptables(常用)
iptables-restore
4. 机器重启自动生效(常用)
service iptables save
5.清空某条规则
iptables -t filter -D INPUT -s 1.2.3.4 -j DROP
6. 禁止某个 ip(下面用 $ip 表示)访问本机
iptables -I INPUT -s $ip -j DROP
7. 禁止某个 ip 段(下面用 $ip/$mask 表示,其中 $mask 是掩码)访问本机
iptables -I INPUT -s $ip/$mask -j DROP
8. 禁止本机访问某个 ip(下面用 $ip 表示)
iptables -A OUTPUT -d $ip -j DROP
9. 禁止某个 ip(下面用 $ip 表示)访问本机的 80 端口的 tcp 服务
iptables -I INPUT -p tcp –dport 80 -s $ip -j DROP
10. 禁止所有 ip 访问本机的 80 端口的 tcp 服务
iptables -A INPUT -p tcp --dport 80 -j DROP
11. 禁止所有 ip 访问本机的所有端口
iptables -A INPUT -j DROP
12. 除了某个 ip(下面用 $ip 表示)之外,其他 ip 都无法访问本机的 3306 端口(常用)
(1)首先,禁止所有:
iptables -I INPUT -p tcp --dport 3306 -j DROP
(2)然后,开放个别:
iptables -I INPUT -s $ip -p tcp --dport 3306 -j ACCEPT
总结
如果你也碰到类似的问题,欢迎复刻我的经验。
如果你有更好的方案,欢迎赐教。
来源 | 程序员升职加薪之旅
0 条评论