X-Git-Url: https://git.openstreetmap.org./chef.git/blobdiff_plain/abd375c5775dc07eb912a839ddf8bdbfaead285f..e92ed5e09215d67f2bd7dc21a32425d7ec5aa26f:/cookbooks/networking/resources/firewall_rule.rb diff --git a/cookbooks/networking/resources/firewall_rule.rb b/cookbooks/networking/resources/firewall_rule.rb index 75d73dc11..22bde6ca9 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,118 +27,57 @@ 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 :dest_ports, :kind_of => [String, Integer], :default => "-" -property :source_ports, :kind_of => [String, Integer], :default => "-" -property :rate_limit, :kind_of => String, :default => "-" -property :connection_limit, :kind_of => [String, Integer], :default => "-" -property :helper, :kind_of => String, :default => "-" +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 +property :connection_limit, :kind_of => [String, Integer] +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 node[:networking][:firewall][:engine] == "shorewall" - add_shorewall_rule(action) - elsif node[:networking][:firewall][:engine] == "nftables" - 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 - end - - def add_shorewall_rule(action) - rule = { - :action => action.to_s.upcase, - :source => new_resource.source, - :dest => new_resource.dest, - :proto => new_resource.proto, - :dest_ports => new_resource.dest_ports.to_s, - :source_ports => new_resource.source_ports.to_s, - :rate_limit => new_resource.rate_limit, - :connection_limit => new_resource.connection_limit.to_s, - :helper => new_resource.helper - } - - if new_resource.family.nil? - node.default[:networking][:firewall][:inet] << rule - node.default[:networking][:firewall][:inet6] << rule - elsif new_resource.family.to_s == "inet" - node.default[:networking][:firewall][:inet] << rule - elsif new_resource.family.to_s == "inet6" - node.default[:networking][:firewall][:inet6] << rule - else - log "Unsupported network family" do - level :error - end - 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 + protocol = new_resource.protocol.to_s - proto = case new_resource.proto - when "udp" then "udp" - when "tcp", "tcp:syn" then "tcp" - end - - 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(", ") + source = addresses(new_resource.source, ip) + dest = addresses(new_resource.dest, ip) - rule << "#{ip} saddr { #{addresses} }" - end + return if new_resource.source && source.empty? + return if new_resource.dest && dest.empty? - if new_resource.dest == "osm" - rule << "#{ip} daddr @#{ip}-osm-addresses" - elsif new_resource.dest =~ /^net:(.*)$/ - addresses = Regexp.last_match(1).split(",").join(", ") + 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 - rule << "#{ip} daddr { #{addresses} }" - end - - if new_resource.proto == "tcp:syn" - rule << "ct state new" - end - - if new_resource.connection_limit != "-" + if new_resource.connection_limit set = "connlimit-#{new_resource.rule}-#{ip}" - node.default[:networking][:firewall][:sets] << set + node.default[:networking][:firewall][:sets] << { + :name => set, :type => set_type(ip), :flags => %w[dynamic] + } rule << "add @#{set} { #{ip} saddr ct count #{new_resource.connection_limit} }" end @@ -146,29 +87,62 @@ action_class do rate = Regexp.last_match(1) burst = Regexp.last_match(2) - node.default[:networking][:firewall][:sets] << set + node.default[:networking][:firewall][:sets] << { + :name => set, :type => set_type(ip), :flags => %w[dynamic], :timeout => 120 + } rule << "update @#{set} { #{ip} saddr limit rate #{rate}/second burst #{burst} packets }" end + if new_resource.helper + helper = "#{new_resource.rule}-#{new_resource.helper}" + + node.default[:networking][:firewall][:helpers] << { + :name => helper, :helper => new_resource.helper, :protocol => protocol + } + + rule << "ct helper set #{helper}" + end + rule << case action when :accept then "accept" when :drop then "jump log-and-drop" 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.to_s).ipv4? + address + elsif ip == "ip6" && IPAddr.new(address.to_s).ipv6? + address + end + end.compact end end - def nftables_source_ports - new_resource.source_ports.to_s.sub(/:$/, "-65535").gsub(":", "-") + def format_ports(ports) + "{ #{Array(ports).map(&:to_s).join(', ')} }" end - def nftables_dest_ports - new_resource.dest_ports.to_s.sub(/:$/, "-65535").gsub(":", "-") + def format_addresses(addresses, ip) + if addresses.is_a?(Symbol) + "@#{ip}-#{addresses}-addresses" + else + "{ #{Array(addresses).map(&:to_s).join(', ')} }" + end + end + + def set_type(ip) + case ip + when "ip" then "ipv4_addr" + when "ip6" then "ipv6_addr" + end end end