nftables #41
3 changed files with 198 additions and 0 deletions
72
bundles/nftables/files/nftables.conf
Normal file
72
bundles/nftables/files/nftables.conf
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#!/usb/sbin/nft -f
|
||||||
|
|
||||||
|
flush ruleset
|
||||||
|
|
||||||
|
table inet filter {
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority 0
|
||||||
|
policy drop
|
||||||
|
|
||||||
|
tcp flags syn tcp option maxseg size 1-500 drop
|
||||||
|
|
||||||
|
ct state { established, related } accept
|
||||||
|
ct state invalid drop
|
||||||
|
|
||||||
|
iif lo accept
|
||||||
|
|
||||||
|
icmp type timestamp-request drop
|
||||||
|
icmp type timestamp-reply drop
|
||||||
|
ip protocol icmp accept
|
||||||
|
|
||||||
|
ip6 nexthdr ipv6-icmp accept
|
||||||
|
% for ruleset, rules in sorted(node.metadata.get('nftables/rules/input', {}).items()):
|
||||||
|
|
||||||
|
# ${ruleset}
|
||||||
|
% for rule in rules:
|
||||||
|
${rule}
|
||||||
|
% endfor
|
||||||
|
# / ${ruleset}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type filter hook output priority 0
|
||||||
|
policy accept
|
||||||
|
}
|
||||||
|
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority 0
|
||||||
|
policy drop
|
||||||
|
|
||||||
|
icmp type timestamp-request drop
|
||||||
|
icmp type timestamp-reply drop
|
||||||
|
|
||||||
|
% for ruleset, rules in sorted(node.metadata.get('nftables/rules/forward', {}).items()):
|
||||||
|
|
||||||
|
# ${ruleset}
|
||||||
|
% for rule in rules:
|
||||||
|
${rule}
|
||||||
|
% endfor
|
||||||
|
# / ${ruleset}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table nat {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority -100
|
||||||
|
|
||||||
|
% for rule in node.metadata.get('nftables/rules/nat_prerouting', []):
|
||||||
|
${rule}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
chain postrouting {
|
||||||
|
type nat hook postrouting priority 100
|
||||||
|
|
||||||
|
% for rule in node.metadata.get('nftables/rules/nat_postrouting', []):
|
||||||
|
${rule}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include "/etc/nftables-rules.d/*-*"
|
35
bundles/nftables/items.py
Normal file
35
bundles/nftables/items.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
if node.has_bundle('pacman'):
|
||||||
|
package = 'pkg_pacman:nftables'
|
||||||
|
else:
|
||||||
|
package = 'pkg_apt:nftables'
|
||||||
|
|
||||||
|
directories = {
|
||||||
|
# used by other bundles
|
||||||
|
'/etc/nftables-rules.d': {
|
||||||
|
'purge': True,
|
||||||
|
'triggers': {
|
||||||
|
'svc_systemd:nftables:reload',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
files = {
|
||||||
|
'/etc/nftables.conf': {
|
||||||
|
'content_type': 'mako',
|
||||||
|
'needs': {
|
||||||
|
'directory:/etc/nftables-rules.d',
|
||||||
|
},
|
||||||
|
'triggers': {
|
||||||
|
'svc_systemd:nftables:reload',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_systemd = {
|
||||||
|
'nftables': {
|
||||||
|
'needs': {
|
||||||
|
'file:/etc/nftables.conf',
|
||||||
|
package,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
91
bundles/nftables/metadata.py
Normal file
91
bundles/nftables/metadata.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
from bundlewrap.exceptions import BundleError
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
'apt': {
|
||||||
|
'packages': {
|
||||||
|
'nftables': {},
|
||||||
|
|
||||||
|
# XXX remove after all systems have been migrated
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'pacman': {
|
||||||
|
'packages': {
|
||||||
|
'nftables': {},
|
||||||
|
'iptables-nft': {
|
||||||
|
# uninstalls iptables automatically
|
||||||
|
'needed_by': {
|
||||||
|
'pkg_pacman:nftables',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if not node.has_bundle('vmhost'):
|
||||||
|
# see comment in bundles/vmhost/items.py
|
||||||
|
defaults['apt']['packages']['iptables'] = {
|
||||||
|
'installed': False,
|
||||||
|
'needed_by': {
|
||||||
|
'pkg_apt:nftables',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@metadata_reactor.provides(
|
||||||
|
'nftables/rules/input/port_rules',
|
||||||
|
)
|
||||||
|
def port_rules_to_nftables(metadata):
|
||||||
|
# Using this, bundles can simply set up port based rules. This
|
||||||
|
# reactor will then take care of converting those rules to actual
|
||||||
|
# nftables rules
|
||||||
|
ruleset = set()
|
||||||
|
|
||||||
|
# Plese note we do not set any defaults for ports. Bundles are
|
||||||
|
# expected to know themselves which default to use.
|
||||||
|
for portdef, targets in metadata.get('firewall/port_rules', {}).items():
|
||||||
|
if '/' in portdef:
|
||||||
|
port, proto = portdef.split('/', 2)
|
||||||
|
|
||||||
|
if proto not in {'udp'}:
|
||||||
|
raise BundleError(f'firewall/port_rules: illegal identifier {portdef} in metadata for {node.name}')
|
||||||
|
else:
|
||||||
|
port = portdef
|
||||||
|
proto = 'tcp'
|
||||||
|
|
||||||
|
for target in targets:
|
||||||
|
if port == '*' and target == '*':
|
||||||
|
raise BundleError('firewall/port_rules: setting both port and target to * is unsupported')
|
||||||
|
|
||||||
|
comment = f'# port_rules {target}'
|
||||||
|
|
||||||
|
if port != '*':
|
||||||
|
if ':' in port:
|
||||||
|
parts = port.split(':')
|
||||||
|
port_str = f'dport {{ {parts[0]}-{parts[1]} }} '
|
||||||
|
else:
|
||||||
|
port_str = f'dport {port} '
|
||||||
|
prefix = ''
|
||||||
|
else:
|
||||||
|
port_str = ''
|
||||||
|
prefix = 'meta l4proto '
|
||||||
|
|
||||||
|
if target == '*':
|
||||||
|
ruleset.add(f'{prefix}{proto} {port_str}accept {comment}')
|
||||||
|
else:
|
||||||
|
resolved = repo.libs.tools.resolve_identifier(repo, target)
|
||||||
|
|
||||||
|
for address in resolved['ipv4']:
|
||||||
|
ruleset.add(f'{prefix}{proto} {port_str}ip saddr {address} accept {comment}')
|
||||||
|
|
||||||
|
for address in resolved['ipv6']:
|
||||||
|
ruleset.add(f'{prefix}{proto} {port_str}ip6 saddr {address} accept {comment}')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'nftables': {
|
||||||
|
'rules': {
|
||||||
|
# order does not matter here.
|
||||||
|
'input': {
|
||||||
|
'port_rules': sorted(ruleset),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in a new issue