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