Python Firewall Rules Simplifier 防火牆規則簡化器

2022-05-12

使用 Python 設計簡化防火牆設定規則的演算 Script。

logo

說明

測試資料

rules = [
    {
        "from": ["1.1.1.1"],
        "to": ["10.10.10.10"]
    },
    {
        "from": ["2.2.2.2"],
        "to": ["11.11.11.11", "12.12.12.12"]
    },
    {
        "from": ["3.3.3.3"],
        "to": ["13.13.13.13"]
    },
    {
        "from": ["4.4.4.4"],
        "to": ["13.13.13.13"]
    },
    {
        "from": ["5.5.5.5"],
        "to": ["15.15.15.15", "16.16.16.16"]
    },
    {
        "from": ["6.6.6.6"],
        "to": ["15.15.15.15"]
    },
    {
        "from": ["6.6.6.6"],
        "to": ["16.16.16.16"]
    },
]

Coding

簡化的原理是將原本可能重複出現、未整合資料,全部打散為一對一關係的規則 (tuple),比較使用 Set 排除掉重複的 tuple。

接著將資料分為一對一、一對多與多對一。其中以多來源 (防火牆允許來源 From) 到單一目標 (防火牆允許目標 To) 為處理優先,處理的結果儲存為 Dictionary 並以變數 oneToMany 保存;

接著處理單一來源 (防火牆允許來源 From) 到多目標 (防火牆允許目標 To),此步驟會將一對一關係的防火牆規則一併處理,處理的結果儲存為 Dictionary 並以變數 manyToOne 保存。

最後要處理的情況是多對多,這種情況會發生在先處理的 oneToMany 當中的防火牆規則,因為 List、Dict 以及 Set 本身都無法計算雜湊值,為了要找出多對多關係,必須判斷是否有不同單一 To,卻有相同 From List 的防火牆規則。

所以土炮設計 listId 作為 list 取 hash value 的方式,最後用兩次迴圈順利處理,並存成 Dictionary 其中 key 就是 From List 的雜湊值,Value 則是另一個 Dictionary,分別記載 From List 以及 To List 以構成多對多關係。保存為變數 manyToMany

manyToMany 會包含 oneToMany 的防火牆規則,所以在表列上就不另外處理 oneToMany 😉

ruleSets = set()

for rule in rules:
    for _from in rule['from']:
        for to in rule['to']:
            ruleSets.add((_from, to))

# one TO to many FROM
oneToMany = {}

# many FROM to one TO
manyToOne = {}

# many to many
manyToMany = []

for to in set([r[1] for r in ruleSets]):
    fromlist = [r[0] for r in ruleSets if r[1] == to]
    if len(fromlist) > 1:
        oneToMany[to] =  fromlist
    else:
        singleFrom = fromlist[0]
        if singleFrom in manyToOne:
            manyToOne[singleFrom].append(to)
        else:
            manyToOne[singleFrom] = [to]

def listId(lst):
    return ','.join([v for v in set(lst)])

manyToMany = {}
for lst in [v for v in oneToMany.values()]:
    manyToMany.setdefault(listId(lst), {
        "from": lst,
        "to": []
    })

for to, _from in oneToMany.items():
    manyToMany[listId(_from)]['to'].append(to)

# Including one TO to many FROM scenario
for m2m in manyToMany.values():
     print('from: \n  ', end='')
     print('\n  '.join(m2m['from']))
     print('to: \n  ', end='')
     print('\n  '.join(m2m['to']))
     print('-' * 30)

for _from in manyToOne:
     print(f'from:\n  {_from}')
     print('to: \n  ', end='')
     print('\n  '.join(manyToOne[_from]))
     print('-' * 30)

計算結果

from:
  5.5.5.5
  6.6.6.6
to:
  16.16.16.16
  15.15.15.15
------------------------------
from:
  3.3.3.3
  4.4.4.4
to:
  13.13.13.13
------------------------------
from:
  1.1.1.1
to:
  10.10.10.10
------------------------------
from:
  2.2.2.2
to:
  12.12.12.12
  11.11.11.11
------------------------------