Skip to content

Instantly share code, notes, and snippets.

@pathcl
Last active November 7, 2021 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pathcl/306d77d0c2206eb802a37e97ea5e41cd to your computer and use it in GitHub Desktop.
Save pathcl/306d77d0c2206eb802a37e97ea5e41cd to your computer and use it in GitHub Desktop.
Basic firewall on nftables && share internet
#!/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