from bundlewrap.exceptions import BundleError defaults = { 'apt': { 'packages': { 'nftables': {}, }, }, 'pacman': { 'packages': { 'nftables': {}, 'iptables': { 'installed': False, 'needed_by': { 'pkg_pacman:iptables-nft', }, }, 'iptables-nft': { '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), }, }, }, }