Browse Source

nftables: Protect management networks

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
Maximilian Wilhelm 1 year ago
parent
commit
d03d94daa2
2 changed files with 51 additions and 0 deletions
  1. 37 0
      _modules/ffho_netfilter.py
  2. 14 0
      nftables/nftables.conf.tmpl

+ 37 - 0
_modules/ffho_netfilter.py

@@ -274,6 +274,43 @@ def generate_forward_policy (fw_config, node_config):
 	return fp
 
 
+def generate_mgmt_config (fw_config, node_config):
+	# If this box is not a router, it will not be responsible for providing
+	# access to any management network, so there's nothing to do here.
+	roles = node_config.get ('roles', [])
+	if 'router' not in roles:
+		return None
+
+	# Get management prefixes from firewall configuration.
+	# If there are no prefixes defined, there's nothing we can do here.
+	mgmt_prefixes = fw_config.get ('acls', {}).get ('Management networks', {})
+	if not mgmt_prefixes:
+		return None
+
+	# We only care for IPv4 prefixes for now.
+	if 4 not in mgmt_prefixes:
+		return None
+
+	config = {
+		'ifaces': [],
+		'prefixes': mgmt_prefixes,
+	}
+
+	mgmt_interfaces = []
+	interfaces = node_config['ifaces']
+	for iface in interfaces.keys ():
+		match = vlan_re.match (iface)
+		if match:
+			vlan_id = int (match.group (1))
+			if vlan_id >= 3000 and vlan_id < 3099:
+				config['ifaces'].append (iface)
+
+	if len (config['ifaces']) == 0:
+		return None
+
+	return config
+
+
 def generate_nat_policy (node_config):
 	roles = node_config.get ('roles', [])
 	nf_cc = node_config.get ('nftables', {})

+ 14 - 0
nftables/nftables.conf.tmpl

@@ -15,6 +15,7 @@
 {%- set services = salt['ffho_netfilter.generate_service_rules'](fw_config, node_config) %}
 {%- set forward = salt['ffho_netfilter.generate_forward_policy'](fw_config, node_config) %}
 {%- set nat_policy = salt['ffho_netfilter.generate_nat_policy'](node_config) %}
+{%- set mgmt_config = salt['ffho_netfilter.generate_mgmt_config'](fw_config, node_config) %}
 {%- set urpf = salt['ffho_netfilter.generate_urpf_policy'](node_config) %}
 {%- set ospf_ifaces = salt['ffho_netfilter.get_ospf_active_interface'](node_config) %}
 {%- set vxlan_ifaces = salt['ffho_netfilter.get_vxlan_interfaces'](node_config['ifaces']) %}
@@ -67,6 +68,9 @@ table ip filter {
 {%- if urpf %}
 		jump urpf
 {%- endif %}
+{%- if mgmt_config and mgmt_config['prefixes'][4] %}
+		ip daddr { {{ mgmt_config['prefixes'][4]|join(', ') }} } oifname { {{ mgmt_config['ifaces']|join(', ') }} } jump mgmt
+{%- endif %}
 {%- for rule in forward['rules'].get ('4', []) %}
 		{{ rule }}
 {%- endfor %}
@@ -95,6 +99,16 @@ table ip filter {
 	}
 {%- endif %}
 
+{%- if mgmt_config %}
+	chain mgmt {
+		ct state related,established counter accept
+		jump admin_access
+		jump icmp_chain
+		jump monitoring
+		jump log-reject
+	}
+{%- endif %}
+
 	chain monitoring {
 {%- for ip in icinga2_queriers if not ":" in ip %}
 		ip saddr {{ ip }} counter accept comment "Icinga2"