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