正常情况下,暴露于互联网的服务器是不会收到来自目标端口是137,138,139的数据包,若你对Samba协议熟悉的话,这三个端口是Windows系统用于请求NetBIOS主机名称,若短短在10分钟之内,你的服务器收到来自这些莫名奇妙的数据包,基本可以断定是收到来自互联网其他来历不明源IP对你主机的端口扫描。
这些端口扫描,来源与Windows 2000/XP时代NetBIOS服务漏洞,包含netbios-ssn(139/tcp)以及netbios-ns(137/tcp),NetBios 會洩漏远程主机的ID以及的主机名称,入侵者更可以使用密碼破解工具,对主机進行字典攻击的,如下图是笔者的Linux服务器在最近运行以来,收到来自这些恶意IP地址的端口扫描
如果你有阅读我之前的Linux防火墙系列文章,应该了解到如下firewalld的规则的重要性,我们来自可疑的数据包都做了日志记录,但仅有日志记录是远远不够的,还要采取一些主动防御措施
这也是会有本篇随笔的原因,所谓的主动防御就是Linux防火墙从目前的防火墙日志中提取那些恶意的IP地址,并制作一个黑名单写入规则中,永久Ban掉这些恶意IP。
本篇会以防止端口扫描为例,这个案例中会用到这些direct规则
ipv4 mangle PORT_SCANNING 0 -m recent --name portscan --rcheck --seconds 25200 -j DROP
ipv4 mangle PORT_SCANNING 1 -m recent --name portscan --remove
ipv4 mangle PORT_SCANNING 2 -p tcp --dport 139 -m recent --name portscan --set -j LOG --log-prefix port_scan:
ipv4 mangle PORT_SCANNING 3 -p tcp --dport 139 -m recent --name portscan --set -j DROP
ipv4 mangle PORT_SCANNING 4 -p tcp --dport 138 -m recent --name portscan --set -j LOG --log-prefix port_scan:
ipv4 mangle PORT_SCANNING 5 -p tcp --dport 138 -m recent --name portscan --set -j DROP
ipv4 mangle PORT_SCANNING 6 -p tcp --dport 137 -m recent --name portscan --set -j LOG --log-prefix port_scan:
ipv4 mangle PORT_SCANNING 7 -p tcp --dport 137 -m recent --name portscan --set -j DROP
根据前面一篇的日志管理,我们将防火墙日志保存在/var/log/iptables.log这个文件中,也就是上文第一个插图的日志记录。我们需要从日志中提取有用的源IP地址。你应该要想到“Python大法好,信Python得脱身!!”
- 下面的python程序就是从/var/log/iptables.log日志文件中提取每行记录的跟源IP地址的关键字,
- 并将这些源IP地址追加到一个集合中。
- 最后将set集合中的ip地址逐行写入一个叫ipList.txt的文本当中
为什么要用set,而不用list呢?因为set数据接口可以除去重复的IP地址。这个Python程序没什么好说的,一目了然。
#!/usr/bin/python3
import os,re
logFile='/var/log/iptables.log'
blkIpFile='~/iplist.txt'
ip_pat=re.compile(r'SRC=(\d+\.\d+\.\d+\.\d+)')
if not os.path.exists(logFile):
raise FileNotFoundError("{}文件不存在!!".format(logFile))
keyWords=["port_scan","syn_attack"]
res=None
blackIPs=set()
with open(logFile,'r') as f:
data=f.readlines()
for line in data:
for word in keyWords:
if word in line:
res=ip_pat.findall(line)
if len(res):
print("找到源ip:{}".format(res[1]))
blackIPs.add(res[1])
break
#if
#end-for
#end-for
#end-with
if not os.path.exists(blkIpFile):
raise FileNotFoundError("{}文件不存在".format(blkIpFile))
with open(blkIpFile,'w') as f:
if len(blackIPs)==0:
raise ValueError("参数blackIPs为空!!\n")
for item in blackIPs:
f.write("{}\n".format(item))
#end-for
#end-with
运行上面的脚本后,我们可以查看iplist.txt这个文本,笔者都惊呆了,运行短短十多天的服务器居然遭到650多个恶意IP地址
接下来,需要用到firewalld的ipset选项,将上面的iplist.txt文件导入到firewalld的规则中,
ipset指定的IP地址集可以在防火墙区域中用作源地址过滤,也可以用作rich规则中的源地址。 在Red Hat Enterprise Linux 7中,首选方法是在direct规则中使用用firewalld创建的ipset。
以本文为例,创建一个名为“blk_src_ips”的ipset
firewall-cmd --permanent --new-ipset=blk_src_ips --type=hash:net
type选项中的hash:net对应的是ipv4的网络环境。 要创建用于IPv6的IP集,请添加--option = family = inet6选项。
接下来,就是使用--add-entries-from-file选项将iplist.txt的内容导入到blk_src_ips的ipset空间中
irewall-cmd --permanent --ipset=blk_src_ips --add-entries-from-file=~/iplist.txt
跟着,我们在drop区域中定义一条源规则,将blk_src_ips的地址集作为源规则的源IP,我们之所以要使用drop区域是因为drop区域默认就是任何绑定到该区域的IP地址或端口的数据包都一律丢弃。
firewall-cmd --permanent --zone=drop --add-source=ipset:blk_src_ips
最后一步,非常关键,一定要确保drop区域是防火墙最后要被执行的默认区域,因此一定要补上这条命令
firewall-cmd --permanent --set-default-zone=drop
firewall-cmd --reload
运行一段时间,或许你会发现的默认区域的策略已经生效了,PRE_drop这条链中正是匹配ipset:blk_src_ips这个地址集的,刚好有一个数据包被拦截并且丢弃,这样已经说明刚才的配置已经生效了。
由于我们的日志文件可能每天会捕获到来自不同恶意源ip地址的新记录。我们不可能都将上面的python脚本每天都人工执行一遍,此时可以使用crontab的任务计划让python脚本每天设定的时间点执行一次,自动让ipset规则刷新一次
通过sudo crontab -e 命令,配置如下任务计划,让其在每天凌晨0点开始执行
0 0 * * * ~/set_ip.py
小结:
本案例,需要你对TCP/IP协议有一定程度的了解,以及了解firewalld内部区域和iptables规则的内部运行原理。你不是说主动防御吗?是,我还没有给出完整的示例,因为我认为这是读者自己需要去具体思考,你自己切实的安全方案,但我至少开了一个很好的场景示范,以及实现更复杂的安全需求需要用到的知识点,没错,Python,Python,Python重要事情说三遍,即便是从事IT服务架构设计和运维的人员,或多或少都要涉及Linux+Python组合的复杂应用场景,例如:。
- 基于域名的rich规则
- 基于域名的数据包转发
- 基于域名的策略路由(这条与firewalld无关)
没错,以上是firewalld原生的规则设定无法实现的,但通过python写一个封装firewall-cmd的应用逻辑,下面这些基于应用层的防火墙应用都能够实现,因为通过python的第三方工具:域名解析IP的工具,多如牛毛,例如dnspython是灰常不错的工具。上面说的这些都能够用python写扩展模块来实现。如果你面对高并发的防火墙应用,或需要频繁修改firewalld动态规则/Linux内核的动态路由表,python可能会带来某种程度的服务器资源的性能消耗,那么别忘了还有cython来做python坚强的后盾,cython能令python的某些运算量或高I/O的逻辑能高效地执行。