#!/usr/sbin/nft -f # # /etc/nftables.conf - FFHO packet filter configuration # {%- set node_config = salt['pillar.get']('nodes:' ~ grains['id']) %} {%- set roles = node_config.get ('roles', []) %} {%- set fw_config = salt['pillar.get']('firewall', {}) %} {%- set admin_access = fw_config.get ('admin_access') %} {%- set ssh = fw_config.get ('ssh') %} {%- set icinga2_queriers = salt['pillar.get']('monitoring:icinga2:queriers', []) %} {%- set nms_list = salt['pillar.get']('globals:snmp:nms_list', []) %} {%- 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 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']) %} flush ruleset table ip filter { set ibgp-peers { type ipv4_addr elements = { 10.132.255.1, # cr01.in.ffho.net 10.132.255.2, # cr02.in.ffho.net 10.132.255.3, # cr03.in.ffho.net } } chain input { type filter hook input priority 0; policy drop; iifname "lo" counter accept udp dport 0 counter drop tcp dport 7 counter drop comment "Ignore echo protocol queries" {%- if vxlan_ifaces %} udp dport 4789 jump vxlan {%- endif %} {%- if urpf %} jump urpf {%- endif %} ip protocol icmp jump icmp_chain ct state invalid counter drop jump admin_access jump monitoring tcp dport 22 counter jump ssh {%- if ospf_ifaces %} {#- ifname sets are introduced in nftables 2.11 #} meta l4proto ospf iifname { {{ ospf_ifaces|join(', ') }} } counter accept {%- endif %} {%- if 'router' in roles %} tcp dport 179 counter jump bgp {%- endif %} ct state related,established counter accept jump services meta pkttype broadcast counter drop comment "Drop broadcasts before logging" limit rate 1/second burst 3 packets counter log prefix "nf input: " limit rate 1/second burst 3 packets counter reject with icmp type admin-prohibited counter drop } chain forward { type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }} {%- if urpf %} jump urpf {%- endif %} {%- for rule in forward['rules'].get ('4', []) %} {{ rule }} {%- 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 {%- endif %} } chain icmp_chain { icmp type { echo-request, destination-unreachable, time-exceeded } counter accept } chain admin_access { {%- for pfx in admin_access[4].keys()|sort %} {%- set comment = admin_access[4][pfx] %} ip saddr {{ pfx }} counter accept comment "{{ comment }}" {%- endfor %} } {%- if 'router' in roles %} chain bgp { ip saddr @ibgp-peers counter accept comment "iBGP peers" # TODO: Add external BGP peers, if any } {%- endif %} chain monitoring { {%- for ip in icinga2_queriers if not ":" in ip %} ip saddr {{ ip }} counter accept comment "Icinga2" {%- endfor %} {%- for ip in nms_list if not ":" in ip %} ip saddr {{ ip }} udp dport 161 counter accept comment "NMS" {%- endfor %} } chain ssh { {%- for pfx in ssh[4].keys()|sort %} {%- set comment = ssh[4][pfx] %} ip saddr {{ pfx }} counter accept comment "{{ comment }}" {%- endfor %} } chain services { {%- for rule in services[4] %} {{ rule }} {%- endfor %} } {%- if urpf %} chain urpf { {%- for iface_cfg in urpf %} {%- for pfx in iface_cfg[4] %} iifname {{ iface_cfg['iface'] }} ip saddr {{ pfx }} return {%- endfor %} iifname {{ iface_cfg['iface'] }} counter drop {%- endfor %} } {%- endif %} {%- if vxlan_ifaces %} chain vxlan { {%- for iface in vxlan_ifaces %} iifname {{ iface }} accept {%- endfor %} counter drop } {%- endif %} chain log-drop { limit rate 1/second burst 3 packets counter log prefix "netfilter: " counter drop } chain log-reject { limit rate 1/second burst 3 packets counter log prefix "netfilter: " limit rate 1/second burst 3 packets counter reject with icmp type admin-prohibited counter drop } } table ip6 filter { set ibgp-peers { type ipv6_addr elements = { 2a03:2260:2342:ffff::1, # cr01.in.ffho.net 2a03:2260:2342:ffff::2, # cr02.in.ffho.net 2a03:2260:2342:ffff::3, # cr03.in.ffho.net } } chain input { type filter hook input priority 0; policy drop; iifname "lo" counter accept tcp dport 7 counter drop comment "Ignore echo protocol queries" {%- if vxlan_ifaces %} udp dport 4789 jump vxlan {%- endif %} {%- if urpf %} jump urpf {%- endif %} meta l4proto icmpv6 jump icmp_chain ct state invalid counter drop jump admin_access jump monitoring tcp dport 22 counter jump ssh {%- if ospf_ifaces %} {#- ifname sets are introduced in nftables 2.11 #} meta l4proto ospf iifname { {{ ospf_ifaces|join(', ') }} } counter accept {%- endif %} {%- if 'router' in roles %} tcp dport 179 counter jump bgp {%- endif %} ct state related,established counter accept counter jump services limit rate 1/second burst 3 packets counter log prefix "netfilter: " limit rate 1/second burst 3 packets counter reject with icmpv6 type admin-prohibited counter drop } chain forward { type filter hook forward priority 0; policy {{ forward['policy'] }}; # {{ forward['policy_reason'] }} {%- if urpf %} jump urpf {%- endif %} {%- for rule in forward['rules'].get ('6', []) %} {{ rule }} {%- 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 icmpv6 type admin-prohibited {%- endif %} } chain icmp_chain { icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply } counter accept icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert } ip6 hoplimit 255 counter accept icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-reduction } ip6 saddr fe80::/64 counter accept } chain admin_access { {%- for pfx in admin_access[6].keys()|sort %} {%- set comment = admin_access[6][pfx] %} ip6 saddr {{ pfx }} counter accept comment "{{ comment }}" {%- endfor %} } {%- if 'router' in roles %} chain bgp { ip6 saddr @ibgp-peers counter accept comment "iBGP peers" # TODO: Add external BGP peers, if any } {%- endif %} chain monitoring { {%- for ip in icinga2_queriers if ":" in ip %} ip6 saddr {{ ip }} counter accept comment "Icinga2" {%- endfor %} {%- for ip in nms_list if ":" in ip %} ip6 saddr {{ ip }} udp dport 161 counter accept comment "NMS" {%- endfor %} } chain ssh { {%- for pfx in ssh[6].keys()|sort %} {%- set comment = ssh[6][pfx] %} ip6 saddr {{ pfx }} counter accept comment "{{ comment }}" {%- endfor %} } chain services { {%- for rule in services[6] %} {{ rule }} {%- endfor %} } {%- if urpf %} chain urpf { ip6 saddr fe80::/64 return {%- for iface_cfg in urpf %} {%- for pfx in iface_cfg[6] %} iifname {{ iface_cfg['iface'] }} ip6 saddr {{ pfx }} return {%- endfor %} iifname {{ iface_cfg['iface'] }} counter drop {%- endfor %} } {%- endif %} {%- if vxlan_ifaces %} chain vxlan { {%- for iface in vxlan_ifaces %} iifname {{ iface }} accept {%- endfor %} counter drop } {%- endif %} chain log-drop { limit rate 1/second burst 3 packets counter log prefix "netfilter: " counter drop } chain log-reject { limit rate 1/second burst 3 packets counter log prefix "netfilter: " limit rate 1/second burst 3 packets counter reject with icmpv6 type admin-prohibited counter drop } } {#- # NAT #} {%- for af in [ 4, 6 ] %} {%- if nat_policy[af] %} {%- set af_name = "ip" if af == 4 else "ip6" %} table {{ af_name }} nat { {%- for chain in ['output', 'prerouting', 'postrouting'] if chain in nat_policy[af] %} chain {{ chain }} { type nat hook {{ chain }} priority 0; policy accept; {%- for rule in nat_policy[af][chain] %} {{ rule }} {%- endfor %} } {%- endfor %} } {%- endif %} {%- endfor %}