防火墙ACL配置自动化 - 配置解析
配置解析,是将不同品牌防火墙,将关键信息提取出来,解析到统一的表格中。
这样,即可开展后续的ACL策略分析,以及自动生成配置。
同时,这些表格数据,还可以用于其他途径,例如:
1)返回NAT信息,不用再逐一登录防火墙查询NAT信息
2)服务器下线时,提取所有关联ACL,NAT配置
3)网络运维,经常会遇到被要求查询某个地址/某个网段开过的所有策略,涉及到异构防火墙,靠人工几乎无法实现,有了统一的防火墙表格数据,就可以轻松实现
1,防火墙解析表格设计
分为四个表格:
address:地址表,记录每个地址组,以及其成员地址
port:端口表,记录每个端口组,以及其成员
nat:NAT表,记录防火墙映射关系
acl:策略表,记录防火墙每条acl信息,需要从address表/port表获取其成员
表格采用json格式存储
每张表的column字段设计如下:
address: ['FwName', 'FwIP', 'ID', 'Zone', 'Type', 'Name', 'Members', 'Conf']
port: ['FwName', 'FwIP', 'ID', 'Type', 'Name', 'Protocol', 'Members1', 'Members2', 'Conf']
nat: ['FwName', 'FwIP', 'ID', 'Type', 'SrcZone', 'DstZone', 'SrcName', 'SrcMembers', 'SrcTransName', 'SrcTransMembers', 'DstName', 'DstMembers', 'DstTransName', 'DstTransMembers', 'Conf']
acl: ['FwName', 'FwIP', 'ID', 'PolicyName', 'Action', 'Protocol', 'SrcArea', 'DstArea', 'SrcName', 'SrcMembers', 'DstName', 'DstMembers', 'PortName', 'PortMembers1', 'PortMembers2', 'Conf']
2,防火墙解析 - 基类
每个品牌防火墙一个类,继承自基类,目标是从不同品牌防火墙中提取基类中公共字段(四张表)。
有些内容不支持解析,对以后的防火墙配置自动化可能产生的后果:1)漏开通策略(有deny acl未被解析) 2)多开通策略(已有permit acl未被解析)
但是,如果后续都是按照自动化脚本去生成配置,那就不会存在没法解析的内容
不支持解析的内容有:
1)思科
address(object-network):成员是range的,没有成员的
address(object-group network):没有成员的
待补充
防火墙基类:
class BaseFw: ADDRESS_COLUMNS = ['FwName', 'FwIP', 'ID', 'Zone', 'Type', 'Name', 'Members', 'Conf'] PORT_COLUMNS = ['FwName', 'FwIP', 'ID', 'Type', 'Name', 'Protocol', 'Members1', 'Members2', 'Conf'] NAT_COLUMNS = ['FwName', 'FwIP', 'ID', 'Type', 'SrcZone', 'DstZone', 'SrcName', 'SrcMembers', 'SrcTransName', 'SrcTransMembers', 'DstName', 'DstMembers', 'DstTransName', 'DstTransMembers', 'Conf'] ACL_COLUMNS = ['FwName', 'FwIP', 'ID', 'PolicyName', 'Action', 'Protocol', 'SrcArea', 'DstArea', 'SrcName', 'SrcMembers', 'DstName', 'DstMembers', 'PortName', 'PortMembers1', 'PortMembers2', 'Conf'] def __init__(self, fw_name, fw_ip, buff, port_pres): self.fw_name = fw_name # 防火墙名称 self.fw_ip = fw_ip # 防火墙IP self.buff = buff # 防火墙配置 self.port_pres = [] # 防火墙的端口预配置,是个字典列表,可以从json转换而来 for port in port_pres: self.port_pres.append({ "FwName": self.fw_name, "FwIP": self.fw_ip, "ID": "", "Type": "", "Name": port.get("name"), "Protocol": port.get("protocol"), "Members1": str(port.get("port")), # int -> string "members2": str(port.get("port")), "Conf": port.get("conf") })
思科端口预配置(截取部分)
[ { "name": "aol", "protocol": "tcp", "port": 5190, "conf": "思科预配置端口:aol" }, { "name": "bgp", "protocol": "tcp", "port": 179, "conf": "思科预配置端口:bgp" }, { "name": "biff", "protocol": "tcp", "port": 512, "conf": "思科预配置端口:biff" }, { "name": "bootpc", "protocol": "tcp", "port": 68, "conf": "思科预配置端口:bootpc" }, { "name": "bootps", "protocol": "tcp", "port": 67, "conf": "思科预配置端口:bootps" } ]
3,防火墙解析 - 地址组解析
以思科地址组为例,解析所有地址组成员
class FwCisco(BaseFw): def __init__(self, fw_name, fw_ip, buff, port_pres): super().__init__(fw_name, fw_ip, buff, port_pres) self.port_pres_map = {port_pre.get("name"): port_pre.get("port") for port_pre in self.port_pres} @LazyProperty def address(self): items = [] # object network configs = re.findall(r'^(object network (.*?)\n (?:host|subnet) (.*?)\n)', self.buff, re.M) # configs = re.findall(r'^(object network (.*?)\n (?:host|subnet|range) (.*?))$', self.buff, re.M) # 支持range for conf, name, member in configs: item = [self.fw_name, self.fw_ip, '', '', 'object', name.strip(), member.strip(), conf.strip()] items.append({column: value for column, value in zip(self.ADDRESS_COLUMNS, item)}) # object-group network configs = re.finditer(r'^object-group network .*?\n(?=\S)', self.buff, re.M | re.S) for conf in configs: conf = conf.group() members = re.findall(r'network-object (?:host )?(.*?)\n', conf) group_objs = re.findall(r'group-object (.*?)\s*$', conf, re.M) if group_objs: # 如果有地址组嵌套地址组, 从被嵌套的地址组中获取成员 for group_obj in [i for i in items if i.get("Name") in group_objs]: members += [i for i in group_obj.get("Members").split(",") if i not in members] members = ','.join(members) if not members: continue name = re.search(r'object-group network (.*?)\n', conf).group(1) item = [self.fw_name, self.fw_ip, '', '', 'object-group', name.strip(), members.strip(), conf.strip()] items.append({column: value for column, value in zip(self.ADDRESS_COLUMNS, item)}) return items
未完待续