From 2e17bd4b4697423f9c124cfdb802424a58c16d80 Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Thu, 9 Mar 2023 18:26:46 +0000 Subject: [PATCH] Refactor firewall rules to simplify IPv4/IPv6 handling --- cookbooks/bind/recipes/default.rb | 10 +- cookbooks/exim/recipes/default.rb | 56 +++------- cookbooks/ftp/recipes/default.rb | 5 +- cookbooks/geodns/recipes/default.rb | 10 +- cookbooks/logstash/recipes/default.rb | 62 +++-------- cookbooks/munin/recipes/default.rb | 24 ++-- cookbooks/networking/recipes/default.rb | 17 +-- .../networking/resources/firewall_rule.rb | 103 ++++++++---------- cookbooks/openssh/recipes/default.rb | 5 +- cookbooks/prometheus/resources/exporter.rb | 6 +- cookbooks/rsyncd/recipes/default.rb | 5 +- cookbooks/snmpd/recipes/default.rb | 53 ++------- roles/lyonix.rb | 3 +- 13 files changed, 124 insertions(+), 235 deletions(-) diff --git a/cookbooks/bind/recipes/default.rb b/cookbooks/bind/recipes/default.rb index 6c596420d..10ab7ce0b 100644 --- a/cookbooks/bind/recipes/default.rb +++ b/cookbooks/bind/recipes/default.rb @@ -62,16 +62,14 @@ end firewall_rule "accept-dns-udp" do action :accept - source "net" - dest "fw" - proto "udp" + context :incoming + protocol :udp dest_ports "domain" end firewall_rule "accept-dns-tcp" do action :accept - source "net" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp dest_ports "domain" end diff --git a/cookbooks/exim/recipes/default.rb b/cookbooks/exim/recipes/default.rb index dbf90bdfb..71309e609 100644 --- a/cookbooks/exim/recipes/default.rb +++ b/cookbooks/exim/recipes/default.rb @@ -229,56 +229,36 @@ prometheus_exporter "exim" do end if node[:exim][:smarthost_name] - node[:exim][:daemon_smtp_ports].each do |port| - firewall_rule "accept-inbound-smtp-#{port}" do - action :accept - source "net" - dest "fw" - proto "tcp" - dest_ports port - source_ports "1024-65535" - end + firewall_rule "accept-inbound-smtp" do + action :accept + context :incoming + protocol :tcp + dest_ports node[:exim][:daemon_smtp_ports] + source_ports "1024-65535" end else - smarthosts_inet = [] - smarthosts_inet6 = [] + smarthosts = [] search(:node, "exim_smarthost_name:*?").each do |host| - smarthosts_inet |= host.ipaddresses(:role => :external, :family => :inet) - smarthosts_inet6 |= host.ipaddresses(:role => :external, :family => :inet6) + smarthosts |= host.ipaddresses(:role => :external) end - node[:exim][:daemon_smtp_ports].each do |port| - firewall_rule "accept-inbound-smtp-#{port}" do - action :accept - family :inet - source "net:#{smarthosts_inet.sort.join(',')}" - dest "fw" - proto "tcp" - dest_ports port - source_ports "1024-65535" - not_if { smarthosts_inet.empty? } - end - - firewall_rule "accept-inbound-smtp-#{port}" do - action :accept - family :inet6 - source "net:#{smarthosts_inet6.sort.join(',')}" - dest "fw" - proto "tcp" - dest_ports port - source_ports "1024-65535" - not_if { smarthosts_inet6.empty? } - end + firewall_rule "accept-inbound-smtp" do + action :accept + context :incoming + protocol :tcp + source smarthosts + dest_ports node[:exim][:daemon_smtp_ports] + source_ports "1024-65535" + not_if { smarthosts.empty? } end end if node[:exim][:smarthost_via] firewall_rule "deny-outbound-smtp" do action :reject - source "fw" - dest "net" - proto "tcp" + context :outgoing + protocol :tcp dest_ports "smtp" end end diff --git a/cookbooks/ftp/recipes/default.rb b/cookbooks/ftp/recipes/default.rb index 14ab725a0..9cd534a0b 100644 --- a/cookbooks/ftp/recipes/default.rb +++ b/cookbooks/ftp/recipes/default.rb @@ -45,9 +45,8 @@ end firewall_rule "accept-ftp-tcp" do action :accept - source "net" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp dest_ports "ftp" helper "ftp" end diff --git a/cookbooks/geodns/recipes/default.rb b/cookbooks/geodns/recipes/default.rb index b40a7643f..a50b71141 100644 --- a/cookbooks/geodns/recipes/default.rb +++ b/cookbooks/geodns/recipes/default.rb @@ -90,16 +90,14 @@ end firewall_rule "accept-dns-udp" do action :accept - source "net" - dest "fw" - proto "udp" + context :incoming + protocol :udp dest_ports "domain" end firewall_rule "accept-dns-tcp" do action :accept - source "net" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp dest_ports "domain" end diff --git a/cookbooks/logstash/recipes/default.rb b/cookbooks/logstash/recipes/default.rb index 56caa7312..34f731af2 100644 --- a/cookbooks/logstash/recipes/default.rb +++ b/cookbooks/logstash/recipes/default.rb @@ -74,54 +74,26 @@ template "/etc/cron.daily/expire-logstash" do mode "755" end -forwarders = search(:node, "recipes:logstash\\:\\:forwarder") +forwarders = [] -forwarders.sort_by { |n| n[:fqdn] }.each do |forwarder| - forwarder.interfaces(:role => :external) do |interface| - firewall_rule "accept-lumberjack-#{forwarder}" do - action :accept - family interface[:family] - source "net:#{interface[:address]}" - dest "fw" - proto "tcp" - dest_ports "5043" - source_ports "1024-65535" - end - - firewall_rule "accept-beats-#{forwarder}" do - action :accept - family interface[:family] - source "net:#{interface[:address]}" - dest "fw" - proto "tcp" - dest_ports "5044" - source_ports "1024-65535" - end +search(:node, "recipes:logstash\\:\\:forwarder").each do |forwarder| + forwarder.interfaces(:role => :external).map do |interface| + forwarders << interface[:address] end end -gateways = search(:node, "roles:gateway") - -gateways.sort_by { |n| n[:fqdn] }.each do |gateway| - gateway.interfaces(:role => :external) do |interface| - firewall_rule "accept-lumberjack-#{gateway}" do - action :accept - family interface[:family] - source "net:#{interface[:address]}" - dest "fw" - proto "tcp" - dest_ports "5043" - source_ports "1024-65535" - end - - firewall_rule "accept-beats-#{gateway}" do - action :accept - family interface[:family] - source "net:#{interface[:address]}" - dest "fw" - proto "tcp" - dest_ports "5044" - source_ports "1024-65535" - end +search(:node, "roles:gateway").each do |forwarder| + forwarder.interfaces(:role => :external).map do |interface| + forwarders << interface[:address] end end + +firewall_rule "accept-logstash" do + action :accept + context :incoming + protocol :tcp + source forwarders + dest_ports %w[5043 5044] + source_ports "1024-65535" + not_if { forwarders.empty? } +end diff --git a/cookbooks/munin/recipes/default.rb b/cookbooks/munin/recipes/default.rb index c1233f9b9..0a2a34452 100644 --- a/cookbooks/munin/recipes/default.rb +++ b/cookbooks/munin/recipes/default.rb @@ -24,22 +24,24 @@ service "munin-node" do supports :status => true, :restart => true, :reload => true end -servers = search(:node, "recipes:munin\\:\\:server") +servers = [] -servers.each do |server| +search(:node, "recipes:munin\\:\\:server").each do |server| server.interfaces(:role => :external) do |interface| - firewall_rule "accept-munin-#{server}" do - action :accept - family interface[:family] - source "net:#{interface[:address]}" - dest "fw" - proto "tcp" - dest_ports "munin" - source_ports "1024-65535" - end + servers << interface[:address] end end +firewall_rule "accept-munin" do + action :accept + context :incoming + protocol :tcp + source servers + dest_ports "munin" + source_ports "1024-65535" + not_if { servers.empty? } +end + template "/etc/munin/munin-node.conf" do source "munin-node.conf.erb" owner "root" diff --git a/cookbooks/networking/recipes/default.rb b/cookbooks/networking/recipes/default.rb index 799871963..f33714900 100644 --- a/cookbooks/networking/recipes/default.rb +++ b/cookbooks/networking/recipes/default.rb @@ -438,17 +438,11 @@ else end if node[:networking][:wireguard][:enabled] - wireguard_source = if node[:roles].include?("gateway") - "net" - else - "osm" - end - firewall_rule "accept-wireguard" do action :accept - source wireguard_source - dest "fw" - proto "udp" + context :incoming + protocol :udp + source :osm unless node[:roles].include?("gateway") dest_ports "51820" source_ports "51820" end @@ -456,9 +450,8 @@ end firewall_rule "accept-http" do action :accept - source "net" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp dest_ports %w[http https] rate_limit node[:networking][:firewall][:http_rate_limit] connection_limit node[:networking][:firewall][:http_connection_limit] diff --git a/cookbooks/networking/resources/firewall_rule.rb b/cookbooks/networking/resources/firewall_rule.rb index 6f429ac5d..4cda0486b 100644 --- a/cookbooks/networking/resources/firewall_rule.rb +++ b/cookbooks/networking/resources/firewall_rule.rb @@ -17,6 +17,8 @@ # limitations under the License. # +require "ipaddr" + resource_name :firewall_rule provides :firewall_rule @@ -25,10 +27,10 @@ unified_mode true default_action :nothing property :rule, :kind_of => String, :name_property => true -property :family, :kind_of => [String, Symbol] -property :source, :kind_of => String, :required => true -property :dest, :kind_of => String, :required => true -property :proto, :kind_of => String, :required => true +property :context, :kind_of => Symbol, :required => true, :is => [:incoming, :outgoing] +property :protocol, :kind_of => Symbol, :required => true, :is => [:udp, :tcp] +property :source, :kind_of => [String, Symbol, Array] +property :dest, :kind_of => [String, Symbol, Array] property :dest_ports, :kind_of => [String, Integer, Array] property :source_ports, :kind_of => [String, Integer, Array] property :rate_limit, :kind_of => String @@ -38,64 +40,37 @@ property :helper, :kind_of => String property :compile_time, TrueClass, :default => true action :accept do - add_rule :accept + add_rule(:accept, "ip") + add_rule(:accept, "ip6") end action :drop do - add_rule :drop + add_rule(:drop, "ip") + add_rule(:drop, "ip6") end action :reject do - add_rule :reject + add_rule(:reject, "ip") + add_rule(:reject, "ip6") end action_class do - def add_rule(action) - if new_resource.family.nil? - add_nftables_rule(action, "inet") - add_nftables_rule(action, "inet6") - elsif new_resource.family.to_s == "inet" - add_nftables_rule(action, "inet") - elsif new_resource.family.to_s == "inet6" - add_nftables_rule(action, "inet6") - end - end - - def add_nftables_rule(action, family) + def add_rule(action, ip) rule = [] - ip = case family - when "inet" then "ip" - when "inet6" then "ip6" - end - - proto = new_resource.proto - - if new_resource.source_ports - rule << "#{proto} sport { #{nftables_source_ports} }" - end - - if new_resource.dest_ports - rule << "#{proto} dport { #{nftables_dest_ports} }" - end - - if new_resource.source == "osm" - rule << "#{ip} saddr @#{ip}-osm-addresses" - elsif new_resource.source =~ /^net:(.*)$/ - addresses = Regexp.last_match(1).split(",").join(", ") - - rule << "#{ip} saddr { #{addresses} }" - end + protocol = new_resource.protocol.to_s - if new_resource.dest == "osm" - rule << "#{ip} daddr @#{ip}-osm-addresses" - elsif new_resource.dest =~ /^net:(.*)$/ - addresses = Regexp.last_match(1).split(",").join(", ") + source = addresses(new_resource.source, ip) + dest = addresses(new_resource.dest, ip) - rule << "#{ip} daddr { #{addresses} }" - end + return if new_resource.source && source.empty? + return if new_resource.dest && dest.empty? - rule << "ct state new" if new_resource.proto == "tcp" + rule << "#{protocol} sport #{format_ports(new_resource.source_ports)}" if new_resource.source_ports + rule << "#{protocol} dport #{format_ports(new_resource.dest_ports)}" if new_resource.dest_ports + rule << "#{ip} saddr #{format_addresses(source, ip)}" if new_resource.source + rule << "#{ip} daddr #{format_addresses(dest, ip)}" if new_resource.dest + rule << "ct state new" if new_resource.protocol == :tcp if new_resource.connection_limit set = "connlimit-#{new_resource.rule}-#{ip}" @@ -119,7 +94,7 @@ action_class do helper = "#{new_resource.rule}-#{new_resource.helper}" node.default[:networking][:firewall][:helpers] << { - :name => helper, :helper => new_resource.helper, :protocol => proto + :name => helper, :helper => new_resource.helper, :protocol => protocol } rule << "ct helper set #{helper}" @@ -131,18 +106,32 @@ action_class do when :reject then "jump log-and-reject" end - if new_resource.source == "fw" - node.default[:networking][:firewall][:outgoing] << rule.join(" ") - elsif new_resource.dest == "fw" - node.default[:networking][:firewall][:incoming] << rule.join(" ") + node.default[:networking][:firewall][new_resource.context] << rule.join(" ") + end + + def addresses(addresses, ip) + if addresses.is_a?(Symbol) + addresses + else + Array(addresses).map do |address| + if ip == "ip" && IPAddr.new(address).ipv4? + address + elsif ip == "ip6" && IPAddr.new(address).ipv6? + address + end + end.compact end end - def nftables_source_ports - Array(new_resource.source_ports).map(&:to_s).join(",") + def format_ports(ports) + "{ #{Array(ports).map(&:to_s).join(', ')} }" end - def nftables_dest_ports - Array(new_resource.dest_ports).map(&:to_s).join(",") + def format_addresses(addresses, ip) + if addresses.is_a?(Symbol) + "@#{ip}-#{addresses}-addresses" + else + "{ #{Array(addresses).map(&:to_s).join(', ')} }" + end end end diff --git a/cookbooks/openssh/recipes/default.rb b/cookbooks/openssh/recipes/default.rb index 6a299d226..8b57aaaef 100644 --- a/cookbooks/openssh/recipes/default.rb +++ b/cookbooks/openssh/recipes/default.rb @@ -84,8 +84,7 @@ end firewall_rule "accept-ssh" do action :accept - source "net" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp dest_ports node[:openssh][:port] end diff --git a/cookbooks/prometheus/resources/exporter.rb b/cookbooks/prometheus/resources/exporter.rb index 0e1d9d6e4..66dcd2b29 100644 --- a/cookbooks/prometheus/resources/exporter.rb +++ b/cookbooks/prometheus/resources/exporter.rb @@ -78,9 +78,9 @@ action :create do firewall_rule "accept-prometheus-#{new_resource.exporter}" do action :accept - source "osm" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp + source :osm dest_ports new_resource.port only_if { node[:prometheus][:mode] == "external" } end diff --git a/cookbooks/rsyncd/recipes/default.rb b/cookbooks/rsyncd/recipes/default.rb index bcc0046e4..89ee45909 100644 --- a/cookbooks/rsyncd/recipes/default.rb +++ b/cookbooks/rsyncd/recipes/default.rb @@ -77,9 +77,8 @@ end firewall_rule "accept-rsync" do action :accept - source "net" - dest "fw" - proto "tcp" + context :incoming + protocol :tcp dest_ports "rsync" source_ports "1024-65535" end diff --git a/cookbooks/snmpd/recipes/default.rb b/cookbooks/snmpd/recipes/default.rb index 6fc4a36aa..f4a57fef2 100644 --- a/cookbooks/snmpd/recipes/default.rb +++ b/cookbooks/snmpd/recipes/default.rb @@ -37,50 +37,11 @@ service "snmpd" do supports :status => true, :restart => true end -if node[:snmpd][:clients] - node[:snmpd][:clients].each do |address| - firewall_rule "accept-snmp" do - action :accept - family "inet" - source "net:#{address}" - dest "fw" - proto "udp" - dest_ports "snmp" - source_ports "1024-65535" - end - end -else - firewall_rule "accept-snmp" do - action :accept - family "inet" - source "net" - dest "fw" - proto "udp" - dest_ports "snmp" - source_ports "1024-65535" - end -end - -if node[:snmpd][:clients6] - node[:snmpd][:clients6].each do |address| - firewall_rule "accept-snmp" do - action :accept - family "inet6" - source "net:#{address}" - dest "fw" - proto "udp" - dest_ports "snmp" - source_ports "1024-65535" - end - end -else - firewall_rule "accept-snmp" do - action :accept - family "inet6" - source "net" - dest "fw" - proto "udp" - dest_ports "snmp" - source_ports "1024-65535" - end +firewall_rule "accept-snmp" do + action :accept + context :incoming + protocol :udp + source node[:snmpd][:clients] if node[:snmpd][:clients] + dest_ports "snmp" + source_ports "1024-65535" end diff --git a/roles/lyonix.rb b/roles/lyonix.rb index b44835628..b37ccb05c 100644 --- a/roles/lyonix.rb +++ b/roles/lyonix.rb @@ -5,8 +5,7 @@ default_attributes( :hosted_by => "LyonIX", :location => "Lyon, France", :snmpd => { - :clients => ["77.95.64.0/21"], - :clients6 => ["2a03:9180::/32", "2001:7f8:47::/48"], + :clients => ["77.95.64.0/21", "2a03:9180::/32", "2001:7f8:47::/48"], :community => "lyonix", :location => "LYON", :contact => "sysadm@rezopole.net" -- 2.43.2