Last active
November 7, 2021 18:08
-
-
Save pathcl/306d77d0c2206eb802a37e97ea5e41cd to your computer and use it in GitHub Desktop.
Basic firewall on nftables && share internet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import nftables | |
import json | |
from string import Template | |
def main(wan, lan): | |
''' | |
USE AT YOUR OWN RISK :) | |
''' | |
nfrules = Template(""" | |
{ | |
"nftables": [ | |
{ | |
"metainfo": { | |
"json_schema_version": 1, | |
"release_name": "E.D.S.", | |
"version": "0.9.8" | |
} | |
}, | |
{ | |
"table": { | |
"family": "ip", | |
"handle": 13, | |
"name": "filter" | |
} | |
}, | |
{ | |
"chain": { | |
"family": "ip", | |
"handle": 1, | |
"hook": "input", | |
"name": "input", | |
"policy": "accept", | |
"prio": 0, | |
"table": "filter", | |
"type": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"ct": { | |
"key": "state" | |
} | |
}, | |
"op": "==", | |
"right": { | |
"set": [ | |
"established", | |
"related" | |
] | |
} | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 10, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "lo" | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 11, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"payload": { | |
"field": "dport", | |
"protocol": "tcp" | |
} | |
}, | |
"op": "==", | |
"right": 22 | |
} | |
}, | |
{ | |
"counter": { | |
"bytes": 1754, | |
"packets": 29 | |
} | |
}, | |
{ | |
"log": null | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 12, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "$lan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"payload": { | |
"field": "dport", | |
"protocol": "tcp" | |
} | |
}, | |
"op": "==", | |
"right": { | |
"set": [ | |
22, | |
53, | |
80, | |
443, | |
445 | |
] | |
} | |
} | |
}, | |
{ | |
"counter": { | |
"bytes": 0, | |
"packets": 0 | |
} | |
}, | |
{ | |
"log": null | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 14, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "$lan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"payload": { | |
"field": "dport", | |
"protocol": "udp" | |
} | |
}, | |
"op": "==", | |
"right": { | |
"set": [ | |
53, | |
67, | |
68 | |
] | |
} | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 16, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "$lan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"payload": { | |
"field": "protocol", | |
"protocol": "ip" | |
} | |
}, | |
"op": "==", | |
"right": "icmp" | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 17, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "input", | |
"expr": [ | |
{ | |
"counter": { | |
"bytes": 3523, | |
"packets": 71 | |
} | |
}, | |
{ | |
"drop": null | |
} | |
], | |
"family": "ip", | |
"handle": 18, | |
"table": "filter" | |
} | |
}, | |
{ | |
"chain": { | |
"family": "ip", | |
"handle": 2, | |
"hook": "output", | |
"name": "output", | |
"policy": "accept", | |
"prio": 0, | |
"table": "filter", | |
"type": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "output", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"ct": { | |
"key": "state" | |
} | |
}, | |
"op": "==", | |
"right": { | |
"set": [ | |
"established", | |
"related", | |
"new" | |
] | |
} | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 20, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "output", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "lo" | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 21, | |
"table": "filter" | |
} | |
}, | |
{ | |
"chain": { | |
"family": "ip", | |
"handle": 3, | |
"hook": "forward", | |
"name": "forward", | |
"policy": "accept", | |
"prio": 0, | |
"table": "filter", | |
"type": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "forward", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "$wan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "oif" | |
} | |
}, | |
"op": "==", | |
"right": "$lan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"ct": { | |
"key": "state" | |
} | |
}, | |
"op": "==", | |
"right": { | |
"set": [ | |
"established", | |
"related" | |
] | |
} | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 6, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "forward", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "$lan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "oif" | |
} | |
}, | |
"op": "==", | |
"right": "$wan" | |
} | |
}, | |
{ | |
"accept": null | |
} | |
], | |
"family": "ip", | |
"handle": 7, | |
"table": "filter" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "forward", | |
"expr": [ | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "iif" | |
} | |
}, | |
"op": "==", | |
"right": "$wan" | |
} | |
}, | |
{ | |
"match": { | |
"left": { | |
"meta": { | |
"key": "oif" | |
} | |
}, | |
"op": "==", | |
"right": "$lan" | |
} | |
}, | |
{ | |
"counter": { | |
"bytes": 0, | |
"packets": 0 | |
} | |
}, | |
{ | |
"drop": null | |
} | |
], | |
"family": "ip", | |
"handle": 8, | |
"table": "filter" | |
} | |
}, | |
{ | |
"chain": { | |
"family": "ip", | |
"handle": 4, | |
"hook": "postrouting", | |
"name": "postrouting", | |
"policy": "accept", | |
"prio": 0, | |
"table": "filter", | |
"type": "filter" | |
} | |
}, | |
{ | |
"table": { | |
"family": "ip", | |
"handle": 14, | |
"name": "nat" | |
} | |
}, | |
{ | |
"chain": { | |
"family": "ip", | |
"handle": 1, | |
"hook": "postrouting", | |
"name": "postrouting", | |
"policy": "accept", | |
"prio": 100, | |
"table": "nat", | |
"type": "nat" | |
} | |
}, | |
{ | |
"rule": { | |
"chain": "postrouting", | |
"expr": [ | |
{ | |
"masquerade": null | |
} | |
], | |
"family": "ip", | |
"handle": 2, | |
"table": "nat" | |
} | |
} | |
] | |
} | |
""") | |
# we can map wan/lan interfaces | |
mapping = {'wan': wan, 'lan': lan} | |
# we need an string object rather than Template | |
rules = nfrules.substitute(**mapping) | |
nft = nftables.Nftables() | |
try: | |
data_structure = json.loads(rules) | |
except json.decoder.JSONDecodeError as e: | |
print(f"ERROR: failed to decode JSON: {e}") | |
exit(1) | |
try: | |
nft.json_validate(data_structure) | |
except Exception as e: | |
print(f"ERROR: failed validating json schema: {e}") | |
exit(1) | |
print(f"INFO: running json cmd: {data_structure}") | |
rc, output, error = nft.json_cmd(data_structure) | |
if rc != 0: | |
# do proper error handling here, exceptions etc | |
print(f"ERROR: running json cmd: {error}") | |
exit(1) | |
if len(output) != 0: | |
print(f"WARNING: output: {output}") | |
exit(0) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-w", "--wan", help="WAN interface", action="store", required=True) | |
parser.add_argument("-l", "--lan", help="LAN interface", action="store", required=True) | |
args = parser.parse_args() | |
main(args.wan, args.lan) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment