cookbooks/fb_iptables/libraries/default.rb (53 lines of code) (raw):

# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2 # # Copyright (c) 2016-present, Facebook, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # module FB module Iptables # rubocop:disable Style/MutableConstant # We let this particular constant remain mutable in case we need to shove in # a nat table or something. TABLES_AND_CHAINS = { 'mangle' => %w{PREROUTING INPUT OUTPUT FORWARD POSTROUTING}, 'filter' => %w{INPUT OUTPUT FORWARD}, 'raw' => %w{PREROUTING OUTPUT}, } # rubocop:enable Style/MutableConstant # Facebook, like others does not have NAT in their kernels. However many # people do, so provide an easy way to initialize the various structures # with NAT entries. # # Note this is idempotent - you can call it many times, it will not # overwrite user rules def self.enable_nat(node) TABLES_AND_CHAINS['nat'] ||= %w{PREROUTING INPUT OUTPUT POSTROUTING} # normally this would be auto-vivified, but since we want to check # fb_iptables.nat.$chain below, this has to exist unless node['fb_iptables']['nat'] node.default['fb_iptables']['nat'] = {} end TABLES_AND_CHAINS['nat'].each do |chain| next if node['fb_iptables']['nat'][chain] node.default['fb_iptables']['nat'][chain] = { 'policy' => 'ACCEPT', 'rules' => {}, } end unless node['fb_iptables']['dynamic_chains']['nat'] node.default['fb_iptables']['dynamic_chains']['nat'] = {} end end # Is the given rule valid for the give ip version def self.rule_supports_ip_version?(rule, version) return true unless rule['ip'] return true if rule['ip'] == version rule['ip'].is_a?(Array) && rule['ip'].include?(version) end def self.each_table(for_ip, node) FB::Iptables::TABLES_AND_CHAINS.each_key do |table| chains = node['fb_iptables'][table].to_hash # 'only' is a purposefully not publicly documented attribute # thou can to a table, to have this table only used for a # specific ip version # It allows you for example to remove any mention of the # *nat table in ip6tables if the box only have the ipv4 nat # module installed. # # node.default['fb_iptables']['nat']['only'] = 4 # The nat table chains/policies won't be written in # /etc/sysconfig/ip6tables # We delete it not to have a 'only' table only = chains.delete('only') if only.nil? || only == for_ip yield(table, chains) end end end # walk dynamic chains and return all dynamic chains enabled for this # table/chain combination. # # For example, for filter/INPUT, we include any chains under 'filter' with # 'INPUT' in it's enabled list. def self.get_dynamic_chains(table, chain, node) dynamic = node['fb_iptables']['dynamic_chains'][table].to_hash dynamic.map do |dynamic_chain, enabled_for| dynamic_chain if enabled_for.include?(chain) end.compact end # Return true if the iptables modules are loaded for the specified # IP version. This check is based on examining # /proc/net/ip#_tables_names; if the file has contents then the # iptables modules are loaded. def self.iptables_active?(ip_version) if ip_version == 4 procfile = '/proc/net/ip_tables_names' else procfile = '/proc/net/ip6_tables_names' end File.exist?(procfile) && !File.read(procfile).empty? end end end