从CentOS6迁移到7系列,变化有点多,其中防火墙就从iptables变成了默认Firewalld服务。firewalld网上资料很多,但没有说得太明白的。一番摸索后,总结了这篇文章,用于快速上手。
1.概览
CentOS7.2.1511 默认防火墙如下图:
这里升级到最新的,相对于默认的这里增加了几个配置,是firewalld更灵活。
2.区域(zone)
firewalld是centos7的一大特性,最大的好处有两个:
(1)支持动态更新,不用重启服务;
(2)引入了防火墙的“zone”概念,简单来说就是为用户预先准备了几套防火墙策略模板,然可根据不同场景选择相应的策略,类似于我们常见windows下网络连接时候选择的工作区(工作网络,家庭网络,公共网络)。
关于第二点的特性实现就使用到了一个区域(zone)的概念。对应到上图中的是1号位置
通过下面命令查看所有的区域
下表给出了所有区域解释
3.过滤规则
target:区域放行策略,类似iptables中的target,例如public区域设置target:default,该区域的规则为白名单模式。如果target:ACCEPT,则该区域的网络接口全放行(为什么这里是说的网络接口?下面interface项详细解释)。设置target使用下面命令:
这里有两个问题:
1.第一次命令失败,提示必须要加上参数--permanent
在firewalld-cmd执行的命令中,如果是规则相关的,加上--permanent参数不会立即生效,只会被记录到配置文件。如果要生效需要调用firewall-cmd --reload重载;如果不加--permanent则是立即生效,但如果--reload会被清掉。
此外,在firewall-cmd命令中有些命令需要必须加--permanent参数,比如这里的--set-target。下面执行--reload
icmp-block-inversion:这个是一个禁ping的参数,默认关闭
interfaces:该区域绑定的网络接口。firewalld的优势就是分了不同的区域,通过这个参数可以为不同的网络接口指定规则。图中ens192网卡被指定到了public区域,所有这个网卡上的请求都应用这个区域的规则。
使用命令 firewall-cmd --change-interface=ens192 --zone=public 修改网卡绑定区域
关于这个有两点注意:
1.网路接口可以不被指定在任何区域,在这种情况下,没有被指定区域的网络接口使用默认zone。下面两条命令获取/设置默认zone
2.interfaces的优先级别没有source高。优先级为:source>interface>firewalld.conf
firewalld是封装的iptables,所以看iptables规则更直接。例如有以下配置,网络接口在public中,trusted区域中放行10.10.10.10源,其他区域默认不修改:
public (active) target: default icmp-block-inversion: no interfaces: ens192 sources: ... trusted (active) target: ACCEPT icmp-block-inversion: no interfaces: sources: 10.10.10.10/32 ...
通过iptables查看source区域在前面,所以优先级比interface高。
sources:根据源ip放行
services:根据服务进行放行,默认的里面有ssh和dhcpv6-client两个服务。ssh就是22端口。
如果需要自定义一个服务规则,怎么做呢?
在/etc/firewalld/services下面添加自定义配置test.xml,如
<?xml version="1.0" encoding="utf-8"?> <service> <short>Test Port</short> <port protocol="tcp" port="5000"/> </service>
然后就能通过--add-service以服务名test添加规则
ports:根据端口放行
protocols:协议类型,默认为空
masquerade:地址隐藏,默认是no,如果设置成yes,使用lo本地地址访问时候,会被转换成物理ip
forward-ports:
source-ports:
icmp-blocks:
rich rules:复杂规则。使用复杂规则能很统一的控制规则。一条完整的规则如下,规则中source,destination, port项都是可以根据需要缺省的
rule family="ipv4" source address="0.0.0.0" destination address="192.168.136.190" port port="3306" protocol="tcp" accept
添加规则使用命令:firewall-cmd --add-rich-rule='...'
同样使用--permanent参数为指定永久规则
4.firewalld配置
firewalld的配置在/etc/firewall下
在zones目录下,存放有所有区域的配置。进入到zones目录下,没有看到所有文件?
原因是firewalld在这个目录下没有找到配置的时候,会使用/usr/lib/firewalld/zones下配置。该目录下是服务默认配置,在/etc下不能找到相关配置则使用/usr/lib下的
5.接口封装
如果在接口中使用firewalld服务,调用shell命令的方式效率低。平均添加一条rich-rule需要0.7s的时间(8c,12G,CentOS7)。
这里推荐一种方式是,直接去操作/etc/firewalld/zone下的 xml 配置文件,最后使用 --reload 加载规则。这种方式下,写文件的时间可以忽略,所有的时间都耗费在 reload上。总共时间测试下来在2s以内。
示例文件:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Public</short>
<description>Nothing</description>
<service name="ssh"/>
<rule family="ipv4">
<source address="10.10.10.10"/>
<destination address="10.10.10.10"/>
<port protocol="tcp" port="1000"/>
<accept/>
</rule>
</zone>
import xml.etree.cElementTree as ET tree = ET.ElementTree(file="/etc/firewalld/zones/public.xml") root = tree.getroot() # 获取rich rule规则 for each_rule in root.findall('rule'): # 打印地址协议 print 'family:' + str(each_rule.attrib) # 打印源地址 source_obj = each_rule.find('source') if source_obj is not None: print 'source:' + str(source_obj.attrib) # 打印目的地址 destination_obj = each_rule.find('destination') if destination_obj is not None: print 'destination:' + str(destination_obj.attrib) # 打印端口 port_obj = each_rule.find('port') if port_obj is not None: print 'port:' + str(port_obj.attrib) # 删除rich rule规则,删除参数为rich rule字符串,记为 remove_str # decode_obj(element) 将xml结构解析成字符串,返回值为字符串,注意字符串赋值顺序,语法细节参考添加规则 for each_rule in root: if decode_obj(each_rule) == remove_str: root.remove(each_rule) # 剩下的修改功能、查找功能可以参照上面实现,这里主要说明的是xml库的使用方法
这里再针对xml库说明下,从<zone>开始,每一层都是一个节点,然后形成的一棵树。每个节点是 ET.Element(tag, attrib={}),比如这里short, description, service , rule都是tag, family是attrib
需要访问节点的 tag 和 attrib,直接xmlobj.tag,xmlobj.attrib方式访问
获取子节点使用xmlobj.getchildren(),或者是获取指定tag的节点xmlobj.findall(tag)
,