Sfoglia il codice sorgente

nftabes: Generate rules for uRPF

  This adds support for generating rules to enforce unicast reverse path
  forwarding/filtering on edge interfaces.  The uRPF rules are generated
  in "natural order" with respect to interface name (vlan305 < vlan3030)
  and drop rules are put in place for any address family which is not in
  use, too.  This way no IPv6 packets could be leaked from currenty IPv4
  only management networks for example.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
Maximilian Wilhelm 2 anni fa
parent
commit
c17fadd54f
2 ha cambiato i file con 88 aggiunte e 0 eliminazioni
  1. 61 0
      _modules/ffho_netfilter.py
  2. 27 0
      nftables/nftables.conf.tmpl

+ 61 - 0
_modules/ffho_netfilter.py

@@ -3,6 +3,9 @@
 #
 
 import ipaddress
+import re
+
+import ffho_net
 
 def generate_service_rules (services, acls, af):
 	rules = []
@@ -142,3 +145,61 @@ def generate_nat_policy (roles, config_context):
 					np[4][chain] = cc_nat[chain][str (af)]
 
 	return np
+
+
+def generate_urpf_policy (interfaces):
+	urpf = {}
+
+	vlan_re = re.compile (r'^vlan(\d+)$')
+
+	for iface in sorted (interfaces.keys ()):
+		iface_config = interfaces[iface]
+
+		# Ignore loopback and VPNs
+		if iface == "lo" or iface.startswith ("ovpn-") or iface.startswith ("wg-"):
+			continue
+
+		# No addres, no uRPF
+		if not iface_config.get ('prefixes'):
+			continue
+
+		# Interface in vrf_external connect to the Internet
+		if iface_config.get ('vrf') in ['vrf_external']:
+			continue
+
+		# Ignore interfaces by VLAN
+		match = vlan_re.search (iface)
+		if match:
+			vid = int (match.group (1))
+
+			# Magic
+			if 900 <= vid <= 999:
+				continue
+
+			# Wired infrastructure stuff
+			if 1000 <= vid <= 1499:
+				continue
+
+			# Wireless infrastructure stuff
+			if 2000 <= vid <= 2299:
+				continue
+
+		# Ok this seems to be and edge interface
+		urpf[iface] = {
+			'iface' : iface,
+			'desc' : iface_config.get ('desc', ''),
+			4 : [],
+			6 : [],
+		}
+
+		# Gather configure prefixes
+		for address in iface_config.get ('prefixes'):
+			pfx = ipaddress.ip_network (address, strict = False)
+			urpf[iface][pfx.version].append ("%s/%s" % (pfx.network_address, pfx.prefixlen))
+
+	sorted_urpf = []
+
+	for iface in ffho_net.get_interface_list (urpf):
+		sorted_urpf.append (urpf[iface])
+
+	return sorted_urpf

+ 27 - 0
nftables/nftables.conf.tmpl

@@ -17,6 +17,7 @@
 
 {%- set forward = salt['ffho_netfilter.generate_forward_policy'](fw_policy, roles, nf_cc) %}
 {%- set nat_policy = salt['ffho_netfilter.generate_nat_policy'](roles, nf_cc) %}
+{%- set urpf = salt['ffho_netfilter.generate_urpf_policy'](node_config['ifaces']) %}
 
 flush ruleset
 
@@ -53,9 +54,22 @@ table ip filter {
 
 	chain forward {
 		type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }}
+{#- custom rules #}
 {%- for rule in forward['rules'].get ('4', []) %}
 		{{ rule }}
 {%- endfor %}
+
+{#- uRPF #}
+{%- for iface_cfg in urpf  %}
+  {%- if loop.first %}
+		# uRPF
+  {%- endif %}
+  {%- for pfx in iface_cfg[4] %}
+		iif {{ iface_cfg['iface'] }} ip saddr {{ pfx }} accept
+  {%- endfor %}
+		iif {{ iface_cfg['iface'] }} counter drop
+{%- endfor %}
+
 {%- if forward['policy'] == 'drop' %}
 		limit rate 1/second burst 3 packets counter log prefix "nf forward: "
 		limit rate 1/second burst 3 packets counter reject with icmp type admin-prohibited
@@ -146,9 +160,22 @@ table ip6 filter {
 
 	chain forward {
 		type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }}
+{#- custom rules #}
 {%- for rule in forward['rules'].get ('6', []) %}
 		{{ rule }}
 {%- endfor %}
+
+{#- uRPF #}
+{%- for iface_cfg in urpf  %}
+  {%- if loop.first %}
+		# uRPF
+  {%- endif %}
+  {%- for pfx in iface_cfg[6] %}
+		iif {{ iface_cfg['iface'] }} ip6 saddr {{ pfx }} accept
+  {%- endfor %}
+		iif {{ iface_cfg['iface'] }} counter drop
+{%- endfor %}
+
 {%- if forward['policy'] == 'drop' %}
 		limit rate 1/second burst 3 packets counter log prefix "nf forward: "
 		limit rate 1/second burst 3 packets counter reject with icmp type admin-prohibited