Quellcode durchsuchen

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 vor 2 Jahren
Ursprung
Commit
c17fadd54f
2 geänderte Dateien mit 88 neuen und 0 gelöschten Zeilen
  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 ipaddress
+import re
+
+import ffho_net
 
 
 def generate_service_rules (services, acls, af):
 def generate_service_rules (services, acls, af):
 	rules = []
 	rules = []
@@ -142,3 +145,61 @@ def generate_nat_policy (roles, config_context):
 					np[4][chain] = cc_nat[chain][str (af)]
 					np[4][chain] = cc_nat[chain][str (af)]
 
 
 	return np
 	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 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 nat_policy = salt['ffho_netfilter.generate_nat_policy'](roles, nf_cc) %}
+{%- set urpf = salt['ffho_netfilter.generate_urpf_policy'](node_config['ifaces']) %}
 
 
 flush ruleset
 flush ruleset
 
 
@@ -53,9 +54,22 @@ table ip filter {
 
 
 	chain forward {
 	chain forward {
 		type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }}
 		type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }}
+{#- custom rules #}
 {%- for rule in forward['rules'].get ('4', []) %}
 {%- for rule in forward['rules'].get ('4', []) %}
 		{{ rule }}
 		{{ rule }}
 {%- endfor %}
 {%- 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' %}
 {%- if forward['policy'] == 'drop' %}
 		limit rate 1/second burst 3 packets counter log prefix "nf forward: "
 		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
 		limit rate 1/second burst 3 packets counter reject with icmp type admin-prohibited
@@ -146,9 +160,22 @@ table ip6 filter {
 
 
 	chain forward {
 	chain forward {
 		type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }}
 		type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }}
+{#- custom rules #}
 {%- for rule in forward['rules'].get ('6', []) %}
 {%- for rule in forward['rules'].get ('6', []) %}
 		{{ rule }}
 		{{ rule }}
 {%- endfor %}
 {%- 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' %}
 {%- if forward['policy'] == 'drop' %}
 		limit rate 1/second burst 3 packets counter log prefix "nf forward: "
 		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
 		limit rate 1/second burst 3 packets counter reject with icmp type admin-prohibited