Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for IPv6 #56

Merged
merged 2 commits into from
Sep 1, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ suites:
run_list:
- recipe[smoke]
attributes:
- name: ipv6_default
run_list:
- recipe[smoke::ipv6_default]
attributes:
- name: list_of_tables
run_list:
- recipe[smoke::tables]
attributes:
- name: ipv6_list_of_tables
run_list:
- recipe[smoke::ipv6_tables]
attributes:
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,37 @@ COMMIT
# Completed
```

`IPv6` support
--------------

To support IPv6, you will need to add `ipv6` the attribute like:
default["simple_iptables"]["ip_versions"] = ["ipv4", "ipv6"]

When using `simple_iptables_policy` or `simple_iptables_rule` resources, you
can enable the policy/rule for either `:ipv4`, `:ipv6` or `:both` using the
`ip_version` parameter. For example:

simple_iptables_rule "management_interface" do
direction "INPUT"
chain_condition "-i eth1"
rule [ "-p tcp --dport 80", "-p tcp --dport 443" ]
jump "ACCEPT"
ip_version :both
end

will set the rule for both IPv4 and IPv6,

simple_iptables_rule "management_interface" do
direction "INPUT"
chain_condition "-i eth1"
rule [ "-p tcp --dport 80", "-p tcp --dport 443" ]
jump "ACCEPT"
ip_version :ipv6
end

will set it for IPv6 only. The default is to set the rule/policy for ipv4 only.


Example
=======

Expand Down
13 changes: 9 additions & 4 deletions attributes/default.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
default["simple_iptables"]["rules"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
default["simple_iptables"]["chains"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
default["simple_iptables"]["policy"] = {"filter" => {}, "nat" => {}, "mangle" => {}, "raw" => {}}
default["simple_iptables"]["ipv4"]["rules"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
default["simple_iptables"]["ipv4"]["chains"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
default["simple_iptables"]["ipv4"]["policy"] = {"filter" => {}, "nat" => {}, "mangle" => {}, "raw" => {}}
default["simple_iptables"]["ipv6"]["rules"] = {"filter" => [], "mangle" => [], "raw" => []}
default["simple_iptables"]["ipv6"]["chains"] = {"filter" => [], "mangle" => [], "raw" => []}
default["simple_iptables"]["ipv6"]["policy"] = {"filter" => {}, "mangle" => {}, "raw" => {}}

default["simple_iptables"]["tables"] = %w(filter nat mangle raw)
default["simple_iptables"]["ipv4"]["tables"] = %w(filter nat mangle raw)
default["simple_iptables"]["ipv6"]["tables"] = %w(filter mangle raw)
default["simple_iptables"]["ip_versions"] = ["ipv4"]
13 changes: 11 additions & 2 deletions providers/policy.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
action :set do
Chef::Log.debug("setting policy for #{new_resource.chain} to #{new_resource.policy}")
node.set["simple_iptables"]["policy"][new_resource.table][new_resource.chain] = new_resource.policy
if [:ipv4, :both].include?(new_resource.ip_version)
handle_policy(new_resource, "ipv4")
end
if [:ipv6, :both].include?(new_resource.ip_version)
handle_policy(new_resource, "ipv6")
end
end

def handle_policy(new_resource, ip_version)
Chef::Log.debug("[#{ip_version}] setting policy for #{new_resource.chain} to #{new_resource.policy}")
node.set["simple_iptables"][ip_version]["policy"][new_resource.table][new_resource.chain] = new_resource.policy
new_resource.updated_by_last_action(true)
end
27 changes: 18 additions & 9 deletions providers/rule.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
require 'chef/mixin/shell_out'
include Chef::Mixin::ShellOut


action :append do
if [:ipv4, :both].include?(new_resource.ip_version)
handle_rule(new_resource, "ipv4")
end
if [:ipv6, :both].include?(new_resource.ip_version)
handle_rule(new_resource, "ipv6")
end
end

def handle_rule(new_resource, ip_version)
if new_resource.rule.kind_of?(String)
rules = [new_resource.rule]
else
rules = new_resource.rule
end

if not node["simple_iptables"]["chains"][new_resource.table].include?(new_resource.chain)
node.set["simple_iptables"]["chains"][new_resource.table] = node["simple_iptables"]["chains"][new_resource.table].dup << new_resource.chain unless ["PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING"].include?(new_resource.chain)
if not node["simple_iptables"][ip_version]["chains"][new_resource.table].include?(new_resource.chain)
node.set["simple_iptables"][ip_version]["chains"][new_resource.table] = node["simple_iptables"][ip_version]["chains"][new_resource.table].dup << new_resource.chain unless ["PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING"].include?(new_resource.chain)
unless new_resource.chain == new_resource.direction || new_resource.direction == :none
node.set["simple_iptables"]["rules"][new_resource.table] << {:rule => "-A #{new_resource.direction} #{new_resource.chain_condition} --jump #{new_resource.chain}", :weight => new_resource.weight}
node.set["simple_iptables"][ip_version]["rules"][new_resource.table] << {:rule => "-A #{new_resource.direction} #{new_resource.chain_condition} --jump #{new_resource.chain}", :weight => new_resource.weight}
end
end

# Then apply the rules to the node
rules.each do |rule|
new_rule = rule_string(new_resource, rule, false)
if not node["simple_iptables"]["rules"][new_resource.table].include?({:rule => new_rule, :weight => new_resource.weight})
node.set["simple_iptables"]["rules"][new_resource.table] << {:rule => new_rule, :weight => new_resource.weight}
node.set["simple_iptables"]["rules"][new_resource.table].sort! {|a,b| a[:weight] <=> b[:weight]}
if not node["simple_iptables"][ip_version]["rules"][new_resource.table].include?({:rule => new_rule, :weight => new_resource.weight})
node.set["simple_iptables"][ip_version]["rules"][new_resource.table] << {:rule => new_rule, :weight => new_resource.weight}
node.set["simple_iptables"][ip_version]["rules"][new_resource.table].sort! {|a,b| a[:weight] <=> b[:weight]}
new_resource.updated_by_last_action(true)
Chef::Log.debug("added rule '#{new_rule}'")
Chef::Log.debug("[#{ip_version}] added rule '#{new_rule}'")
else
Chef::Log.debug("ignoring duplicate simple_iptables_rule '#{new_rule}'")
Chef::Log.debug("[#{ip_version}] ignoring duplicate simple_iptables_rule '#{new_rule}'")
end
end
end
Expand Down
114 changes: 62 additions & 52 deletions recipes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@
# Before executing the simple_iptables_* resources, reset the
# node attributes to their defaults. This gives "action :delete"
# semantics for free by removing a resource from a recipe.
node.set["simple_iptables"]["chains"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
node.set["simple_iptables"]["rules"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
node.set["simple_iptables"]["policy"] = {"filter" => {}, "nat" => {}, "mangle" => {}, "raw" => {}}
node.set["simple_iptables"]["ipv4"]["chains"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
node.set["simple_iptables"]["ipv4"]["rules"] = {"filter" => [], "nat" => [], "mangle" => [], "raw" => []}
node.set["simple_iptables"]["ipv4"]["policy"] = {"filter" => {}, "nat" => {}, "mangle" => {}, "raw" => {}}

node.set["simple_iptables"]["ipv6"]["chains"] = {"filter" => [], "mangle" => [], "raw" => []}
node.set["simple_iptables"]["ipv6"]["rules"] = {"filter" => [], "mangle" => [], "raw" => []}
node.set["simple_iptables"]["ipv6"]["policy"] = {"filter" => {}, "mangle" => {}, "raw" => {}}
# Then run all the simple_iptables_* resources
run_context.resource_collection.each do |resource|
if resource.kind_of?(Chef::Resource::SimpleIptablesRule)
Expand All @@ -55,66 +58,73 @@
end
end

case node['platform_family']
when 'debian'
iptable_rules = '/etc/iptables-rules'
when 'rhel', 'fedora'
iptable_rules = '/etc/sysconfig/iptables'
end
# maps protocol version to a character that will be used to differentiate
# iptables* (ipv4) and ip6tables* (ipv6)
v2s = {'ipv4' => '', 'ipv6' => '6'}

ruby_block "test-iptables" do
block do
cmd = Mixlib::ShellOut.new("iptables-restore --test < #{iptable_rules}",
:user => "root")
cmd.run_command
if !Array(cmd.valid_exit_codes).include?(cmd.exitstatus)
msg = <<-eos
iptables-restore exited with code #{cmd.exitstatus} while testing new rules
node["simple_iptables"]["ip_versions"].each do |ip_version|
v = v2s[ip_version]
case node['platform_family']
when 'debian'
iptable_rules = "/etc/ip#{v}tables-rules"
when 'rhel', 'fedora'
iptable_rules = "/etc/sysconfig/ip#{v}tables"
end

ruby_block "test-ip#{v}tables" do
block do
cmd = Mixlib::ShellOut.new("ip#{v}tables-restore --test < #{iptable_rules}",
:user => "root")
cmd.run_command
if !Array(cmd.valid_exit_codes).include?(cmd.exitstatus)
msg = <<-eos
ip#{v}tables-restore exited with code #{cmd.exitstatus} while testing new rules
STDOUT:
#{cmd.stdout}
STDERR:
#{cmd.stderr}
eos
match = cmd.stderr.match /line:?\s*(\d+)/
if match
line_no = match[1].to_i
msg << "Line #{line_no}: #{IO.readlines(iptable_rules)[line_no-1]}"
match = cmd.stderr.match /line:?\s*(\d+)/
if match
line_no = match[1].to_i
msg << "Line #{line_no}: #{IO.readlines(iptable_rules)[line_no-1]}"
end
# Delete the file so that the next Chef run is forced to recreate it
# and retest it. Otherwise, if the rules remain unchanged, the template
# resource won't recreate the file, won't notify the test resource,
# and the Chef run will be allowed to complete successfully despite
# and invalid rule being present.
File.delete(iptable_rules)
raise msg
end
# Delete the file so that the next Chef run is forced to recreate it
# and retest it. Otherwise, if the rules remain unchanged, the template
# resource won't recreate the file, won't notify the test resource,
# and the Chef run will be allowed to complete successfully despite
# and invalid rule being present.
File.delete(iptable_rules)
raise msg
end
notifies :run, "execute[reload-ip#{v}tables]"
action :nothing
end
notifies :run, "execute[reload-iptables]"
action :nothing
end

execute "reload-iptables" do
command "iptables-restore < #{iptable_rules}"
user "root"
action :nothing
end

template iptable_rules do
source "iptables-rules.erb"
cookbook "simple_iptables"
notifies :create, "ruby_block[test-iptables]"
action :create
end

case node['platform_family']
when 'debian'
execute "reload-ip#{v}tables" do
command "ip#{v}tables-restore < #{iptable_rules}"
user "root"
action :nothing
end

# TODO: Generalize this for other platforms somehow
file "/etc/network/if-up.d/iptables-rules" do
owner "root"
group "root"
mode "0755"
content "#!/bin/bash\niptables-restore < #{iptable_rules}\n"
template iptable_rules do
source "ip#{v}tables-rules.erb"
cookbook "simple_iptables"
notifies :create, "ruby_block[test-ip#{v}tables]"
action :create
end

case node['platform_family']
when 'debian'
# TODO: Generalize this for other platforms somehow
file "/etc/network/if-up.d/ip#{v}tables-rules" do
owner "root"
group "root"
mode "0755"
content "#!/bin/bash\nip#{v}tables-restore < #{iptable_rules}\n"
action :create
end
end
end

9 changes: 8 additions & 1 deletion recipes/redhat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,47 @@

simple_iptables_policy "INPUT" do
policy "ACCEPT"
ip_version :ipv4
end

simple_iptables_rule "established" do
chain "INPUT"
rule "-m conntrack --ctstate ESTABLISHED,RELATED"
jump "ACCEPT"
weight 1
ip_version :ipv4
end

simple_iptables_rule "icmp" do
chain "INPUT"
rule "--proto icmp"
jump "ACCEPT"
weight 2
ip_version :ipv4
end

simple_iptables_rule "loopback" do
chain "INPUT"
rule "--in-interface lo"
jump "ACCEPT"
weight 3
ip_version :ipv4
end

simple_iptables_rule "ssh" do
chain "INPUT"
rule "--proto tcp --dport 22 -m conntrack --ctstate NEW"
jump "ACCEPT"
weight 70
ip_version :ipv4
end

simple_iptables_rule "reject" do
chain "INPUT"
rule ""
jump "REJECT --reject-with icmp-host-prohibited"
weight 90
ip_version :ipv4
end

simple_iptables_rule "reject" do
Expand All @@ -46,4 +52,5 @@
rule ""
jump "REJECT --reject-with icmp-host-prohibited"
weight 90
end
ip_version :ipv4
end
1 change: 1 addition & 0 deletions resources/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
attribute :chain, :name_attribute => true, :equal_to => ["INPUT", "FORWARD", "OUTPUT", "PREROUTING", "POSTROUTING"], :default => "INPUT"
attribute :table, :equal_to => ["filter", "nat", "mangle", "raw"], :default => "filter"
attribute :policy, :equal_to => ["ACCEPT", "DROP"], :required => true
attribute :ip_version, :equal_to => [:ipv4, :ipv6, :both], :default => :ipv4


def initialize(*args)
Expand Down
1 change: 1 addition & 0 deletions resources/rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
attribute :direction, :equal_to => ["INPUT", "FORWARD", "OUTPUT", "PREROUTING", "POSTROUTING", :none], :default => "INPUT"
attribute :chain_condition, :kind_of => [String]
attribute :weight, :kind_of => Integer, :default => 50
attribute :ip_version, :equal_to => [:ipv4, :ipv6, :both], :default => :ipv4

def initialize(*args)
super
Expand Down
46 changes: 46 additions & 0 deletions templates/default/ip6tables-rules.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<% if node["simple_iptables"]["ipv6"]["tables"].include?('mangle') %>
# This file generated by Chef. Changes will be overwritten.
*mangle
:PREROUTING <%= node["simple_iptables"]["ipv6"]["policy"]["mangle"]["PREROUTING"] || "ACCEPT" %> [0:0]<% if Gem::Version.new(/\d+(\.\d+(.\d+)?)?/.match(node["kernel"]["release"])[0]) > Gem::Version.new("2.6.35") -%>
:INPUT <%= node["simple_iptables"]["ipv6"]["policy"]["mangle"]["INPUT"] || "ACCEPT" %> [0:0]<% end -%>
:FORWARD <%= node["simple_iptables"]["ipv6"]["policy"]["mangle"]["FORWARD"] || "ACCEPT" %> [0:0]
:OUTPUT <%= node["simple_iptables"]["ipv6"]["policy"]["mangle"]["OUTPUT"] || "ACCEPT" %> [0:0]
:POSTROUTING <%= node["simple_iptables"]["ipv6"]["policy"]["mangle"]["POSTROUTING"] || "ACCEPT" %> [0:0]
<% node["simple_iptables"]["ipv6"]["chains"]["mangle"].each do |chain| -%>
:<%= chain %> - [0:0]
<% end -%>
<% node["simple_iptables"]["ipv6"]["rules"]["mangle"].each do |rule| -%>
<%= rule[:rule] %>
<% end -%>
COMMIT
# Completed
<% end %>
<% if node["simple_iptables"]["ipv6"]["tables"].include?('filter') %>
# This file generated by Chef. Changes will be overwritten.
*filter
:INPUT <%= node["simple_iptables"]["ipv6"]["policy"]["filter"]["INPUT"] || "ACCEPT" %> [0:0]
:FORWARD <%= node["simple_iptables"]["ipv6"]["policy"]["filter"]["FORWARD"] || "ACCEPT" %> [0:0]
:OUTPUT <%= node["simple_iptables"]["ipv6"]["policy"]["filter"]["OUTPUT"] || "ACCEPT" %> [0:0]
<% node["simple_iptables"]["ipv6"]["chains"]["filter"].each do |chain| -%>
:<%= chain %> - [0:0]
<% end -%>
<% node["simple_iptables"]["ipv6"]["rules"]["filter"].each do |rule| -%>
<%= rule[:rule] %>
<% end -%>
COMMIT
# Completed
<% end %>
<% if node["simple_iptables"]["ipv6"]["tables"].include?('raw') %>
# This file generated by Chef. Changes will be overwritten.
*raw
:PREROUTING <%= node["simple_iptables"]["ipv6"]["policy"]["raw"]["PREROUTING"] || "ACCEPT" %> [0:0]
:OUTPUT <%= node["simple_iptables"]["ipv6"]["policy"]["raw"]["OUTPUT"] || "ACCEPT" %> [0:0]
<% node["simple_iptables"]["ipv6"]["chains"]["raw"].each do |chain| -%>
:<%= chain %> - [0:0]
<% end -%>
<% node["simple_iptables"]["ipv6"]["rules"]["raw"].each do |rule| -%>
<%= rule[:rule] %>
<% end -%>
COMMIT
# Completed
<% end %>
Loading