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