diff --git a/bundles/dhcpd/metadata.py b/bundles/dhcpd/metadata.py index 6c13ea3..bdf47b7 100644 --- a/bundles/dhcpd/metadata.py +++ b/bundles/dhcpd/metadata.py @@ -37,18 +37,20 @@ def get_static_allocations(metadata): @metadata_reactor.provides( - 'iptables/bundle_rules/dhcpd', + 'nftables/rules/input/dhcpd', ) -def iptables(metadata): +def nftables(metadata): rules = set() - for subnet in node.metadata.get('dhcpd/subnets', {}): - rules.add('iptables -A INPUT -i {} -p udp --dport 67:68 -j ACCEPT'.format(subnet)) + for iface in node.metadata.get('dhcpd/subnets', {}): + rules.add(f'udp dport {{ 67, 68 }} iif {iface} accept') return { - 'iptables': { - 'bundle_rules': { - # can't use port_rules here, because we're generating interface based rules. - 'dhcpd': sorted(list(rules)), + 'nftables': { + 'rules': { + 'input': { + # can't use port_rules here, because we're generating interface based rules. + 'dhcpd': sorted(rules), + }, }, } } diff --git a/bundles/dovecot/metadata.py b/bundles/dovecot/metadata.py index 82adb0a..bd1427e 100644 --- a/bundles/dovecot/metadata.py +++ b/bundles/dovecot/metadata.py @@ -76,13 +76,13 @@ def import_database_settings_from_postfixadmin(metadata): @metadata_reactor.provides( - 'iptables/port_rules/143', - 'iptables/port_rules/993', - 'iptables/port_rules/4190', + 'firewall/port_rules/143', + 'firewall/port_rules/993', + 'firewall/port_rules/4190', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { # imap(s) '143': atomic(metadata.get('dovecot/restrict-to', {'*'})), diff --git a/bundles/icinga2/metadata.py b/bundles/icinga2/metadata.py index 48b441f..9bf7d26 100644 --- a/bundles/icinga2/metadata.py +++ b/bundles/icinga2/metadata.py @@ -103,11 +103,11 @@ def add_users_from_json(metadata): @metadata_reactor.provides( - 'iptables/port_rules/5665', + 'firewall/port_rules/5665', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '5665': atomic(metadata.get('icinga2/restrict-to', set())), }, diff --git a/bundles/kodi/metadata.py b/bundles/kodi/metadata.py index 0d888f9..f21afff 100644 --- a/bundles/kodi/metadata.py +++ b/bundles/kodi/metadata.py @@ -44,11 +44,11 @@ defaults = { @metadata_reactor.provides( - 'iptables/port_rules/8080', + 'firewall/port_rules/8080', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '8080': atomic(metadata.get('kodi/restrict-to', {'*'})), }, diff --git a/bundles/mosquitto/metadata.py b/bundles/mosquitto/metadata.py index 3834b86..6c2b3d1 100644 --- a/bundles/mosquitto/metadata.py +++ b/bundles/mosquitto/metadata.py @@ -26,9 +26,9 @@ defaults = { @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): sources = metadata.get('mosquitto/restrict-to', {'*'}) result = {} @@ -36,7 +36,7 @@ def iptables(metadata): result[listener] = atomic(sources) return { - 'iptables': { + 'firewall': { 'port_rules': result, }, } diff --git a/bundles/netdata/metadata.py b/bundles/netdata/metadata.py index 9dea2b4..feb00ff 100644 --- a/bundles/netdata/metadata.py +++ b/bundles/netdata/metadata.py @@ -19,11 +19,11 @@ defaults = { @metadata_reactor.provides( - 'iptables/port_rules/19999', + 'firewall/port_rules/19999', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '19999': atomic(metadata.get('netdata/restrict-to', set())), }, diff --git a/bundles/nfs-server/metadata.py b/bundles/nfs-server/metadata.py index b0bbd32..d203560 100644 --- a/bundles/nfs-server/metadata.py +++ b/bundles/nfs-server/metadata.py @@ -10,9 +10,9 @@ defaults = { @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): ips = set() for share_items in metadata.get('nfs-server/shares', {}).values(): @@ -20,7 +20,7 @@ def iptables(metadata): ips.add(share_target) return { - 'iptables': { + 'firewall': { 'port_rules': { '111': atomic(ips), '111/udp': atomic(ips), diff --git a/bundles/nginx/metadata.py b/bundles/nginx/metadata.py index 2432f42..bfd19ff 100644 --- a/bundles/nginx/metadata.py +++ b/bundles/nginx/metadata.py @@ -169,12 +169,12 @@ def monitoring(metadata): @metadata_reactor.provides( - 'iptables/port_rules/80', - 'iptables/port_rules/443', + 'firewall/port_rules/80', + 'firewall/port_rules/443', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '80': atomic(metadata.get('nginx/restrict-to', {'*'})), '443': atomic(metadata.get('nginx/restrict-to', {'*'})), diff --git a/bundles/oidentd/metadata.py b/bundles/oidentd/metadata.py index 5d997b4..f9d4390 100644 --- a/bundles/oidentd/metadata.py +++ b/bundles/oidentd/metadata.py @@ -10,11 +10,11 @@ defaults = { @metadata_reactor.provides( - 'iptables/port_rules/113', + 'firewall/port_rules/113', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '113': atomic(metadata.get('oidentd/restrict-to', {'*'})), }, diff --git a/bundles/openssh/metadata.py b/bundles/openssh/metadata.py index a7a8424..3cad1b9 100644 --- a/bundles/openssh/metadata.py +++ b/bundles/openssh/metadata.py @@ -16,11 +16,11 @@ defaults = { } @metadata_reactor.provides( - 'iptables/port_rules/22', + 'firewall/port_rules/22', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '22': atomic(metadata.get('openssh/restrict-to', {'*'})), }, diff --git a/bundles/postfix/files/main.cf b/bundles/postfix/files/main.cf index 24eaa80..890428d 100644 --- a/bundles/postfix/files/main.cf +++ b/bundles/postfix/files/main.cf @@ -18,7 +18,7 @@ alias_maps = hash:/etc/aliases relayhost = ${node.metadata['postfix']['relayhost']} % endif -% if node.has_bundle('postfixadmin') or node.has_bundle('iptables'): +% if node.has_bundle('postfixadmin') or node.has_bundle('nftables'): inet_interfaces = all % else: inet_interfaces = 127.0.0.1 diff --git a/bundles/postfix/metadata.py b/bundles/postfix/metadata.py index 7d2b821..cea5cf1 100644 --- a/bundles/postfix/metadata.py +++ b/bundles/postfix/metadata.py @@ -100,11 +100,11 @@ def letsencrypt(metadata): @metadata_reactor.provides( - 'iptables/port_rules/25', - 'iptables/port_rules/587', - 'iptables/port_rules/2525', + 'firewall/port_rules/25', + 'firewall/port_rules/587', + 'firewall/port_rules/2525', ) -def iptables(metadata): +def firewall(metadata): if node.has_bundle('postfixadmin'): default = {'*'} else: @@ -119,7 +119,7 @@ def iptables(metadata): rules['2525'] = atomic(metadata.get('postfix/restrict-to', default)) return { - 'iptables': { + 'firewall': { 'port_rules': rules, }, } diff --git a/bundles/powerdns/metadata.py b/bundles/powerdns/metadata.py index ff087a1..2cd0e53 100644 --- a/bundles/powerdns/metadata.py +++ b/bundles/powerdns/metadata.py @@ -182,11 +182,11 @@ def hosts_entries_for_all_dns_servers(metadata): @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '53': atomic(metadata.get('powerdns/restrict-to', {'*'})), '53/udp': atomic(metadata.get('powerdns/restrict-to', {'*'})), diff --git a/bundles/pppd/files/ip-down b/bundles/pppd/files/ip-down index 6bdf74c..7cdcd09 100644 --- a/bundles/pppd/files/ip-down +++ b/bundles/pppd/files/ip-down @@ -1,6 +1,6 @@ #!/bin/bash -rm /etc/iptables-rules.d/90-pppd +rm /etc/nftables-rules.d/90-pppd rm /etc/sysctl.d/90-pppd.conf -/usr/local/sbin/iptables-enforce +systemctl reload nftables diff --git a/bundles/pppd/files/ip-up b/bundles/pppd/files/ip-up index beca42a..fa00fe6 100644 --- a/bundles/pppd/files/ip-up +++ b/bundles/pppd/files/ip-up @@ -2,9 +2,9 @@ INTERFACE=$1 -echo "iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE" > /etc/iptables-rules.d/90-pppd +echo "add rule nat postrouting oif $INTERFACE masquerade" > /etc/nftables-rules.d/90-pppd echo "net.ipv6.conf.$INTERFACE.accept_ra=2" > /etc/sysctl.d/90-pppd.conf -/usr/local/sbin/iptables-enforce +systemctl reload nftables rdisc6 $INTERFACE diff --git a/bundles/pppd/items.py b/bundles/pppd/items.py index 7e2cf6e..f358b5c 100644 --- a/bundles/pppd/items.py +++ b/bundles/pppd/items.py @@ -32,7 +32,7 @@ directories = { } files = { - '/etc/iptables-rules.d/90-pppd': { + '/etc/nftables-rules.d/90-pppd': { 'content_type': 'any', }, '/etc/ppp/chap-secrets': { @@ -53,11 +53,11 @@ files = { 'svc_systemd:pppoe:restart', }, }, - '/etc/ppp/ip-down.d/iptables': { + '/etc/ppp/ip-down.d/nftables': { 'source': 'ip-down', 'mode': '0755', }, - '/etc/ppp/ip-up.d/iptables': { + '/etc/ppp/ip-up.d/nftables': { 'source': 'ip-up', 'mode': '0755', }, diff --git a/bundles/transmission/metadata.py b/bundles/transmission/metadata.py index 3ee0ad9..5f5c682 100644 --- a/bundles/transmission/metadata.py +++ b/bundles/transmission/metadata.py @@ -50,11 +50,11 @@ if node.has_bundle('telegraf'): @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { str(metadata.get('transmission/config/peer-port')): atomic({'*'}), str(metadata.get('transmission/config/peer-port')) + '/udp': atomic({'*'}), diff --git a/bundles/unbound/files/unbound.conf b/bundles/unbound/files/unbound.conf index 8d520fe..ef0cbf8 100644 --- a/bundles/unbound/files/unbound.conf +++ b/bundles/unbound/files/unbound.conf @@ -10,8 +10,8 @@ server: num-threads: ${threads} -% if node.has_bundle('iptables') and not node.has_bundle('vmhost'): - # Use iptables to manage access to this service +% if node.has_bundle('nftables') and not node.has_bundle('vmhost'): + # Use nftables to manage access to this service interface: 0.0.0.0 interface: ::0 access-control: 0.0.0.0/0 allow diff --git a/bundles/unbound/metadata.py b/bundles/unbound/metadata.py index efa54d2..caf3d04 100644 --- a/bundles/unbound/metadata.py +++ b/bundles/unbound/metadata.py @@ -56,11 +56,11 @@ def cpu_cores_to_config_values(metadata): @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { '53': atomic(metadata.get('unbound/restrict-to', set())), '53/udp': atomic(metadata.get('unbound/restrict-to', set())), diff --git a/bundles/vmhost/items.py b/bundles/vmhost/items.py index 52ad0ec..c8ff9e1 100644 --- a/bundles/vmhost/items.py +++ b/bundles/vmhost/items.py @@ -3,3 +3,24 @@ files = { 'mode': '0755', }, } + +if node.has_bundle('nftables'): + # libvirt on debian depends on either iptables or firewalld. Since + # we're managing firewall rules using bundlewrap, we don't want either + # of thos to interfere. So we install firewalld, then ensure it is + # never running. After that, we ensure the bundlewrap managed rules + # are active. + svc_systemd['firewalld'] = { + 'running': False, + 'enabled': False, + 'masked': True, + 'needs': { + 'pkg_apt:firewalld', + }, + 'needed_by': { + 'svc_systemd:nftables', + }, + 'triggers': { + 'svc_systemd:nftables:reload', + }, + } diff --git a/bundles/vmhost/metadata.py b/bundles/vmhost/metadata.py index c5e7a59..a01e0dc 100644 --- a/bundles/vmhost/metadata.py +++ b/bundles/vmhost/metadata.py @@ -28,3 +28,10 @@ if node.os == 'debian' and node.os_version[0] < 11: if node.has_bundle('zfs'): defaults['apt']['packages']['libvirt-daemon-driver-storage-zfs'] = {} + +if node.has_bundle('nftables'): + defaults['apt']['packages']['firewalld'] = { + 'needed_by': { + 'pkg_apt:libvirt-daemon-system', + }, + } diff --git a/bundles/webfs/metadata.py b/bundles/webfs/metadata.py index dc2bb72..ca6356f 100644 --- a/bundles/webfs/metadata.py +++ b/bundles/webfs/metadata.py @@ -16,11 +16,11 @@ defaults = { @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): return { - 'iptables': { + 'firewall': { 'port_rules': { str(metadata.get('webfs/port')): atomic(metadata.get('webfs/restrict-to', {'*'})), }, diff --git a/bundles/wide-dhcp6c/metadata.py b/bundles/wide-dhcp6c/metadata.py index 04495db..f1a6957 100644 --- a/bundles/wide-dhcp6c/metadata.py +++ b/bundles/wide-dhcp6c/metadata.py @@ -4,12 +4,14 @@ defaults = { 'wide-dhcpv6-client': {}, }, }, - 'iptables': { - 'bundle_rules': { - 'wide-dhcp6c': [ - 'ip6tables -A INPUT -p udp -s ff00::/12 -j ACCEPT', - 'ip6tables -A INPUT -p udp -s fe80::/10 -j ACCEPT', - ], + 'nftables': { + 'rules': { + 'input': { + 'wide-dhcp6c': [ + 'udp dport { 546, 547 } ip6 saddr ff00::/12 accept', + 'udp dport { 546, 547 } ip6 saddr fe80::/10 accept', + ], + }, }, }, 'icinga2_api': { diff --git a/bundles/wireguard/metadata.py b/bundles/wireguard/metadata.py index 14e68d6..de360e5 100644 --- a/bundles/wireguard/metadata.py +++ b/bundles/wireguard/metadata.py @@ -18,12 +18,14 @@ defaults = { }, }, }, - 'iptables': { - 'bundle_rules': { - 'wireguard': [ - 'iptables_both -A FORWARD -i wg0 -j ACCEPT', - 'iptables_both -A FORWARD -o wg0 -j ACCEPT', - ], + 'nftables': { + 'rules': { + 'forward': { + 'wireguard': [ + 'iif wg0 accept', + 'oif wg0 accept', + ], + }, }, }, 'wireguard': { @@ -149,9 +151,9 @@ def icinga2(metadata): @metadata_reactor.provides( - 'iptables/port_rules', + 'firewall/port_rules', ) -def iptables(metadata): +def firewall(metadata): sources = set(metadata.get('wireguard/restrict-to', set())) for peer_name in metadata.get('wireguard/peers'): try: @@ -162,7 +164,7 @@ def iptables(metadata): sources.add(peer_name) return { - 'iptables': { + 'firewall': { 'port_rules': { '51820/udp': atomic(sources), }, diff --git a/groups/os.py b/groups/os.py index b320906..36b237c 100644 --- a/groups/os.py +++ b/groups/os.py @@ -20,7 +20,7 @@ groups['linux'] = { 'bundles': { 'basic', 'cron', - 'iptables', + 'nftables', 'openssh', 'postfix', 'sshmon', @@ -39,7 +39,7 @@ groups['linux'] = { 'backup-client': { 'server': 'franzi-home.kunbox.net:2022', }, - 'iptables': { + 'firewall': { 'port_rules': { '*': { 'ovh.icinga2', diff --git a/nodes/home/nas.py b/nodes/home/nas.py index 7c48bbd..e4ef65d 100644 --- a/nodes/home/nas.py +++ b/nodes/home/nas.py @@ -55,7 +55,7 @@ nodes['home.nas'] = { 'groups': { 'nas': {}, }, - 'iptables': { + 'firewall': { 'port_rules': { '4679': { # Dell ULNM '172.19.136.0/25', diff --git a/nodes/home/router.py b/nodes/home/router.py index cf1c9df..c13d5d5 100644 --- a/nodes/home/router.py +++ b/nodes/home/router.py @@ -94,24 +94,27 @@ nodes['home.router'] = { 'vars.notification.sms': True }, - 'iptables': { - 'custom_rules': [ - # This is a router. Allow forwarding traffic for internal networks. - 'iptables_both -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT', - 'iptables_both -A FORWARD -i enp1s0.23 -o ppp0 -j ACCEPT', - 'iptables_both -A FORWARD -i enp1s0.42 -j ACCEPT', + 'nftables': { + 'rules': { + 'forward': { + 'router': [ + # This is a router. Allow forwarding traffic for internal networks. + 'ct state { related, established } accept', + 'iif enp1s0.23 oif ppp0 accept', + 'iif enp1s0.42 accept', - # External port 2022 should be home.nas - 'iptables -t nat -A PREROUTING -p tcp --dport 2022 -j DNAT --to 172.19.138.20:22', - 'iptables -A FORWARD -p tcp -d 172.19.138.20 --dport 22 -j ACCEPT', - - # use MASQUERADE for tun0 (c3voc) - 'iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE', - - # yaaaaay, IPv6! No NAT! - 'ip6tables -A FORWARD -p ipv6-icmp -j ACCEPT', - 'ip6tables -A FORWARD -p tcp --dport 22 -j ACCEPT', - ], + # yaaaaay, IPv6! No NAT! + 'ip6 nexthdr ipv6-icmp accept', + 'tcp dport 22 accept', + ], + }, + 'nat_prerouting': [ + 'tcp dport 2022 dnat 172.19.138.20:22', + ], + 'nat_postrouting': [ + 'oif tun0 masquerade', + ], + }, }, 'netdata': { 'restrict-to': { diff --git a/nodes/htz-cloud/miniserver.py b/nodes/htz-cloud/miniserver.py index e1f5933..d893fd8 100644 --- a/nodes/htz-cloud/miniserver.py +++ b/nodes/htz-cloud/miniserver.py @@ -83,13 +83,6 @@ nodes['htz-cloud.miniserver'] = { 'icinga_options': { 'vars.notification.sms': False, }, - 'iptables': { - 'custom_rules': [ - 'iptables_both -A INPUT -p udp --dport 60000:61000 -j ACCEPT', # mosh - 'iptables_both -A INPUT -p tcp --dport 9001 -j ACCEPT', # weechat - - ], - }, 'letsencrypt': { 'concat_and_deploy': { 'sophie-weechat': { @@ -151,6 +144,16 @@ nodes['htz-cloud.miniserver'] = { 'bot_token': '""', }, }, + 'nftables': { + 'rules': { + 'input': { + 'sophie-weechat': [ + 'udp dport { 60000-61000 } accept', + 'tcp dport 9001 accept', + ], + }, + }, + }, 'nginx': { 'vhosts': { #'dimension.sophies-kitchen.eu': { diff --git a/nodes/htz/ex42-1048908.py b/nodes/htz/ex42-1048908.py index 95fd9b2..4602d9b 100644 --- a/nodes/htz/ex42-1048908.py +++ b/nodes/htz/ex42-1048908.py @@ -137,24 +137,6 @@ nodes['htz.ex42-1048908'] = { 'icinga_options': { 'pretty_name': 'kunsmann.eu', }, - 'iptables': { - # TODO move to bundles - 'custom_rules': [ - 'iptables_both -A INPUT -p udp --dport 60000:61000 -j ACCEPT', # mosh - 'iptables_both -A INPUT -p tcp --dport 9001 -j ACCEPT', # weechat - - # libvirt rules. These are also added by libvirt itself, - # but they would be overridden by our own iptables - # management. - 'iptables -A INPUT -i virbr0 -p udp --dport 53 -j ACCEPT', - 'iptables -A INPUT -i virbr0 -p tcp --dport 53 -j ACCEPT', - 'iptables -A INPUT -i virbr0 -p udp --dport 67:68 -j ACCEPT', - 'iptables -A INPUT -i virbr0 -p tcp --dport 67:68 -j ACCEPT', - 'iptables -A FORWARD -i virbr0 -j ACCEPT', - 'iptables -A FORWARD -o virbr0 -j ACCEPT', - 'iptables -t nat -A POSTROUTING -o enp0s31f6 -j MASQUERADE', - ], - }, 'letsencrypt': { 'concat_and_deploy': { 'kunsi-weechat': { @@ -247,6 +229,30 @@ nodes['htz.ex42-1048908'] = { '@.*:franzi\\\\.business', }, }, + 'nftables': { + 'rules': { + 'input': { + 'kunsi-weechat': [ + 'udp dport { 60000-61000 } accept', + 'tcp dport 9001 accept', + ], + 'libvirt': [ + 'tcp dport 53 iif virbr0 accept', + 'udp dport 53 iif virbr0 accept', + 'udp dport { 67, 68 } iif virbr0 accept', + ], + }, + 'forward': { + 'libvirt': [ + 'iif virbr0 accept', + 'oif virbr0 accept', + ], + }, + 'nat_postrouting': { + 'oif enp0s31f6 masquerade', + }, + }, + }, 'nginx': { 'vhosts': { # TODO maybe some of this can be moved to a bundle? diff --git a/nodes/kunsi-t470.py b/nodes/kunsi-t470.py index 621c28e..9555c3a 100644 --- a/nodes/kunsi-t470.py +++ b/nodes/kunsi-t470.py @@ -31,7 +31,7 @@ nodes['kunsi-t470'] = { }, # there is also wlp4s0, but that's managed by netctl }, - 'iptables': { + 'firewall': { 'port_rules': { # For the occasional file-share using `python -m http.server` '8000': {'*'},