267 changes: 145 additions & 122 deletions README.markdown

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ require 'puppetlabs_spec_helper/rake_tasks'
require 'puppet-lint/tasks/puppet-lint'

PuppetLint.configuration.fail_on_warnings
PuppetLint.configuration.send('relative')
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.send('disable_class_inherits_from_params_class')
PuppetLint.configuration.send('disable_class_parameter_defaults')
PuppetLint.configuration.send('disable_documentation')
PuppetLint.configuration.send('disable_single_quote_string_with_variables')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
3 changes: 2 additions & 1 deletion lib/puppet/provider/firewall/ip6tables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def self.iptables_save(*args)
:stat_probability => '--probability',
:state => "-m state --state",
:table => "-t",
:tcp_flags => "-m tcp --tcp-flags",
:todest => "--to-destination",
:toports => "--to-ports",
:tosource => "--to-source",
Expand Down Expand Up @@ -133,7 +134,7 @@ def self.iptables_save(*args)
# I put it when calling the command. So compability with manual changes
# not provided with current parser [georg.koester])
@resource_list = [:table, :source, :destination, :iniface, :outiface,
:proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :gid, :uid, :sport, :dport,
:proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :tcp_flags, :gid, :uid, :sport, :dport,
:port, :pkttype, :name, :state, :ctstate, :icmp, :hop_limit, :limit, :burst,
:recent, :rseconds, :reap, :rhitcount, :rttl, :rname, :rsource, :rdest,
:jump, :todest, :tosource, :toports, :log_level, :log_prefix, :reject,
Expand Down
19 changes: 13 additions & 6 deletions lib/puppet/type/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,16 +428,24 @@ def should_to_s(value)
# Interface specific matching properties
newproperty(:iniface, :required_features => :interface_match) do
desc <<-EOS
Input interface to filter on.
Input interface to filter on. Supports interface alias like eth0:0.
To negate the match try this:
iniface => '! lo',
EOS
newvalues(/^[a-zA-Z0-9\-\._\+]+$/)
newvalues(/^!?\s?[a-zA-Z0-9\-\._\+\:]+$/)
end

newproperty(:outiface, :required_features => :interface_match) do
desc <<-EOS
Output interface to filter on.
Output interface to filter on. Supports interface alias like eth0:0.
To negate the match try this:
outiface => '! lo',
EOS
newvalues(/^[a-zA-Z0-9\-\._\+]+$/)
newvalues(/^!?\s?[a-zA-Z0-9\-\._\+\:]+$/)
end

# NAT specific properties
Expand Down Expand Up @@ -1066,10 +1074,9 @@ def should_to_s(value)

if value(:set_mark)
unless value(:jump).to_s =~ /MARK/ &&
value(:chain).to_s =~ /PREROUTING|OUTPUT/ &&
value(:table).to_s =~ /mangle/
self.fail "Parameter set_mark only applies to " \
"the PREROUTING or OUTPUT chain of the mangle table and when jump => MARK"
"the mangle table and when jump => MARK"
end
end

Expand Down
10 changes: 7 additions & 3 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# Default: running
#
class firewall (
$ensure = running
) {
$ensure = running,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {
case $ensure {
/^(running|stopped)$/: {
# Do nothing.
Expand All @@ -26,7 +28,9 @@
case $::kernel {
'Linux': {
class { "${title}::linux":
ensure => $ensure,
ensure => $ensure,
service_name => $service_name,
package_name => $package_name,
}
}
default: {
Expand Down
30 changes: 19 additions & 11 deletions manifests/linux.pp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# Default: running
#
class firewall::linux (
$ensure = running
) {
$ensure = running,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {
$enable = $ensure ? {
running => true,
stopped => false,
Expand All @@ -27,23 +29,29 @@
'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos',
'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer': {
class { "${title}::redhat":
ensure => $ensure,
enable => $enable,
require => Package['iptables'],
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
'Debian', 'Ubuntu': {
class { "${title}::debian":
ensure => $ensure,
enable => $enable,
require => Package['iptables'],
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
'Archlinux': {
class { "${title}::archlinux":
ensure => $ensure,
enable => $enable,
require => Package['iptables'],
ensure => $ensure,
enable => $enable,
package_name => $package_name,
service_name => $service_name,
require => Package['iptables'],
}
}
default: {}
Expand Down
22 changes: 12 additions & 10 deletions manifests/linux/archlinux.pp
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,30 @@
# Default: true
#
class firewall::linux::archlinux (
$ensure = 'running',
$enable = true
) {
service { 'iptables':
ensure => $ensure,
enable => $enable,
hasstatus => true,
$ensure = 'running',
$enable = true,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {
if $package_name {
package { $package_name:
ensure => $ensure,
}
}

service { 'ip6tables':
service { $service_name:
ensure => $ensure,
enable => $enable,
hasstatus => true,
}

file { '/etc/iptables/iptables.rules':
ensure => present,
before => Service['iptables'],
before => Service[$service_name],
}

file { '/etc/iptables/ip6tables.rules':
ensure => present,
before => Service['ip6tables'],
before => Service[$service_name],
}
}
23 changes: 14 additions & 9 deletions manifests/linux/debian.pp
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,36 @@
# Default: true
#
class firewall::linux::debian (
$ensure = running,
$enable = true
) {
package { 'iptables-persistent':
ensure => present,
$ensure = running,
$enable = true,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {

if $package_name {
package { $package_name:
ensure => present,
}
}

if($::operatingsystemrelease =~ /^6\./ and $enable == true
and versioncmp($::iptables_persistent_version, '0.5.0') < 0 ) {
and versioncmp($::iptables_persistent_version, '0.5.0') < 0) {
# This fixes a bug in the iptables-persistent LSB headers in 6.x, without it
# we lose idempotency
exec { 'iptables-persistent-enable':
logoutput => on_failure,
command => '/usr/sbin/update-rc.d iptables-persistent enable',
unless => '/usr/bin/test -f /etc/rcS.d/S*iptables-persistent',
require => Package['iptables-persistent'],
require => Package[$package_name],
}
} else {
# This isn't a real service/daemon. The start action loads rules, so just
# needs to be called on system boot.
service { 'iptables-persistent':
service { $service_name:
ensure => undef,
enable => $enable,
hasstatus => true,
require => Package['iptables-persistent'],
require => Package[$package_name],
}
}
}
34 changes: 19 additions & 15 deletions manifests/linux/redhat.pp
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,42 @@
# Default: true
#
class firewall::linux::redhat (
$ensure = running,
$enable = true
) {
$ensure = running,
$enable = true,
$service_name = $::firewall::params::service_name,
$package_name = $::firewall::params::package_name,
) inherits ::firewall::params {

# RHEL 7 and later and Fedora 15 and later require the iptables-services
# package, which provides the /usr/libexec/iptables/iptables.init used by
# lib/puppet/util/firewall.rb.
if ($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0) {
service { "firewalld":
if ($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0)
or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0) {
service { 'firewalld':
ensure => stopped,
enable => false,
before => Package['iptables-services']
before => Package[$package_name],
}
}

package { 'iptables-services':
ensure => present,
before => Service['iptables'],
if $package_name {
package { $package_name:
ensure => present,
before => Service[$service_name],
}
}

service { 'iptables':
service { $service_name:
ensure => $ensure,
enable => $enable,
hasstatus => true,
require => File['/etc/sysconfig/iptables'],
}

file { '/etc/sysconfig/iptables':
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
ensure => present,
owner => 'root',
group => 'root',
mode => '0600',
}
}
41 changes: 41 additions & 0 deletions manifests/params.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class firewall::params {
case $::osfamily {
'RedHat': {
case $::operatingsystem {
'Archlinux': {
$service_name = ['iptables','ip6tables']
$package_name = undef
}
'Fedora': {
if versioncmp($::operatingsystemrelease, '15') >= 0 {
$package_name = 'iptables-services'
} else {
$package_name = undef
}
$service_name = 'iptables'
}
default: {
if versioncmp($::operatingsystemrelease, '7.0') >= 0 {
$package_name = 'iptables-services'
} else {
$package_name = undef
}
$service_name = 'iptables'
}
}
}
'Debian': {
if $::operatingsystem == 'Debian' and versioncmp($::operatingsystemrelease, '8.0') >= 0 {
$service_name = 'netfilter-persistent'
$package_name = 'netfilter-persistent'
} else {
$service_name = 'iptables-persistent'
$package_name = 'iptables-persistent'
}
}
default: {
$package_name = undef
$service_name = 'iptables'
}
}
}
5 changes: 3 additions & 2 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-firewall",
"version": "1.2.0",
"version": "1.3.0",
"author": "Puppet Labs",
"summary": "Manages Firewalls such as iptable",
"license": "Apache-2.0",
Expand Down Expand Up @@ -42,7 +42,8 @@
{
"operatingsystem": "SLES",
"operatingsystemrelease": [
"11 SP1"
"11 SP1",
"12"
]
},
{
Expand Down
24 changes: 24 additions & 0 deletions spec/acceptance/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,30 @@ class { '::firewall': }
end
end
end

describe 'tcp_flags' do
context 'FIN,SYN ACK' do
it 'applies' do
pp = <<-EOS
class { '::firewall': }
firewall { '593 - test':
proto => tcp,
action => accept,
tcp_flags => 'FIN,SYN ACK',
provider => 'ip6tables',
}
EOS

apply_manifest(pp, :catch_failures => true)
end

it 'should contain the rule' do
shell('ip6tables-save') do |r|
expect(r.stdout).to match(/-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN ACK -m comment --comment "593 - test" -j ACCEPT/)
end
end
end
end
end

describe 'limit' do
Expand Down
26 changes: 24 additions & 2 deletions spec/acceptance/rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ class { '::firewall': }
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { "011 reject local traffic not on loopback interface":
iniface => '! lo',
proto => 'all',
destination => '127.0.0.1/8',
action => 'reject',
}
firewall { '012 accept loopback':
iniface => 'lo',
action => 'accept',
Expand All @@ -158,7 +164,14 @@ class { '::firewall': }
action => 'accept',
before => Firewallchain['INPUT:filter:IPv4'],
}
firewall { '025 smtp':
outiface => '! eth0:2',
chain => 'OUTPUT',
proto => 'tcp',
dport => '25',
state => 'NEW',
action => 'accept',
}
firewall { '013 icmp echo-request':
proto => 'icmp',
icmp => 'echo-request',
Expand All @@ -175,12 +188,18 @@ class { '::firewall': }
icmp => 'time-exceeded',
action => 'accept',
}
firewall { '443 ssl on aliased interface':
proto => 'tcp',
dport => '443',
state => 'NEW',
action => 'accept',
iniface => 'eth0:3',
}
firewall { '999 reject':
action => 'reject',
reject => 'icmp-host-prohibited',
}
firewallchain { 'LOCAL_INPUT_PRE:filter:IPv4': }
firewall { '001 LOCAL_INPUT_PRE':
jump => 'LOCAL_INPUT_PRE',
Expand Down Expand Up @@ -238,11 +257,14 @@ class { '::firewall': }
/LOCAL_INPUT_PRE/,
/-A INPUT -m comment --comment \"001 LOCAL_INPUT_PRE\" -j LOCAL_INPUT_PRE/,
/-A INPUT -m comment --comment \"010 INPUT allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/,
/-A INPUT -d 127.0.0.0\/(8|255\.0\.0\.0) (! -i|-i !) lo -m comment --comment \"011 reject local traffic not on loopback interface\" -j REJECT --reject-with icmp-port-unreachable/,
/-A INPUT -i lo -m comment --comment \"012 accept loopback\" -j ACCEPT/,
/-A INPUT -p icmp -m comment --comment \"013 icmp destination-unreachable\" -m icmp --icmp-type 3 -j ACCEPT/,
/-A INPUT -s 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"013 icmp echo-request\" -m icmp --icmp-type 8 -j ACCEPT/,
/-A INPUT -p icmp -m comment --comment \"013 icmp time-exceeded\" -m icmp --icmp-type 11 -j ACCEPT/,
/-A INPUT -p tcp -m multiport --dports 22 -m comment --comment \"020 ssh\" -m state --state NEW -j ACCEPT/,
/-A OUTPUT (! -o|-o !) eth0:2 -p tcp -m multiport --dports 25 -m comment --comment \"025 smtp\" -m state --state NEW -j ACCEPT/,
/-A INPUT -i eth0:3 -p tcp -m multiport --dports 443 -m comment --comment \"443 ssl on aliased interface\" -m state --state NEW -j ACCEPT/,
/-A INPUT -m comment --comment \"900 LOCAL_INPUT\" -j LOCAL_INPUT/,
/-A INPUT -m comment --comment \"999 reject\" -j REJECT --reject-with icmp-host-prohibited/,
/-A FORWARD -m comment --comment \"010 allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/
Expand Down
7 changes: 6 additions & 1 deletion spec/acceptance/standard_usage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ class my_fw::pre {
iniface => 'lo',
action => 'accept',
}->
firewall { '002 accept related established rules':
firewall { "0002 reject local traffic not on loopback interface":
iniface => '! lo',
destination => '127.0.0.1/8',
action => 'reject',
}->
firewall { '003 accept related established rules':
proto => 'all',
ctstate => ['RELATED', 'ESTABLISHED'],
action => 'accept',
Expand Down
36 changes: 36 additions & 0 deletions spec/fixtures/iptables/conversion_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,24 @@
:iniface => 'eth0',
},
},
'iniface_1_negated' => {
:line => '-A INPUT ! -i eth0 -m comment --comment "060 iniface" -j DROP',
:table => 'filter',
:params => {
:action => 'drop',
:chain => 'INPUT',
:iniface => '! eth0',
},
},
'iniface_1_aliased' => {
:line => '-A INPUT -i eth0:1 -m comment --comment "060 iniface" -j DROP',
:table => 'filter',
:params => {
:action => 'drop',
:chain => 'INPUT',
:iniface => 'eth0:1',
},
},
'iniface_with_vlans_1' => {
:line => '-A INPUT -i eth0.234 -m comment --comment "060 iniface" -j DROP',
:table => 'filter',
Expand Down Expand Up @@ -355,6 +373,24 @@
:outiface => 'eth0',
},
},
'outiface_1_negated' => {
:line => '-A OUTPUT ! -o eth0 -m comment --comment "060 outiface" -j DROP',
:table => 'filter',
:params => {
:action => 'drop',
:chain => 'OUTPUT',
:outiface => '! eth0',
},
},
'outiface_1_aliased' => {
:line => '-A OUTPUT -o eth0:2 -m comment --comment "060 outiface" -j DROP',
:table => 'filter',
:params => {
:action => 'drop',
:chain => 'OUTPUT',
:outiface => 'eth0:2',
},
},
'outiface_with_vlans_1' => {
:line => '-A OUTPUT -o eth0.234 -m comment --comment "060 outiface" -j DROP',
:table => 'filter',
Expand Down
6 changes: 6 additions & 0 deletions spec/unit/classes/firewall_linux_archlinux_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
require 'spec_helper'

describe 'firewall::linux::archlinux', :type => :class do
let(:facts) do
{
:osfamily => 'RedHat',
:operatingsystem => 'Archlinux'
}
end
it { should contain_service('iptables').with(
:ensure => 'running',
:enable => 'true'
Expand Down
86 changes: 77 additions & 9 deletions spec/unit/classes/firewall_linux_debian_spec.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,87 @@
require 'spec_helper'

describe 'firewall::linux::debian', :type => :class do
it { should contain_package('iptables-persistent').with(
:ensure => 'present'
)}
it { should contain_service('iptables-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[iptables-persistent]'
)}
context "Debian 7" do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '7.0'
}}
it { should contain_package('iptables-persistent').with(
:ensure => 'present'
)}
it { should contain_service('iptables-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[iptables-persistent]'
)}
end

context 'enable => false' do
context 'deb7 enable => false' do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '7.0'
}}
let(:params) {{ :enable => 'false' }}
it { should contain_service('iptables-persistent').with(
:enable => 'false'
)}
end

context "Debian 8" do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => 'jessie/sid'
}}
it { should contain_package('netfilter-persistent').with(
:ensure => 'present'
)}
it { should contain_service('netfilter-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[netfilter-persistent]'
)}
end

context 'deb8 enable => false' do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => 'jessie/sid'
}}
let(:params) {{ :enable => 'false' }}
it { should contain_service('netfilter-persistent').with(
:enable => 'false'
)}
end

context "Debian 8, alt operatingsystem" do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '8.0'
}}
it { should contain_package('netfilter-persistent').with(
:ensure => 'present'
)}
it { should contain_service('netfilter-persistent').with(
:ensure => nil,
:enable => 'true',
:require => 'Package[netfilter-persistent]'
)}
end

context 'deb8, alt operatingsystem, enable => false' do
let(:facts) {{
:osfamily => 'Debian',
:operatingsystem => 'Debian',
:operatingsystemrelease => '8.0'
}}
let(:params) {{ :enable => 'false' }}
it { should contain_service('netfilter-persistent').with(
:enable => 'false'
)}
end
end
2 changes: 2 additions & 0 deletions spec/unit/classes/firewall_linux_redhat_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
oldreleases.each do |osrel|
context "os #{os} and osrel #{osrel}" do
let(:facts) {{
:osfamily => 'RedHat',
:operatingsystem => os,
:operatingsystemrelease => osrel
}}
Expand All @@ -20,6 +21,7 @@
newreleases.each do |osrel|
context "os #{os} and osrel #{osrel}" do
let(:facts) {{
:osfamily => 'RedHat',
:operatingsystem => os,
:operatingsystemrelease => osrel
}}
Expand Down
8 changes: 8 additions & 0 deletions spec/unit/puppet/type/firewall_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,14 @@
@resource[iface] = 'eth1'
@resource[iface].should == 'eth1'
end
it "should accept a negated #{iface} value as a string" do
@resource[iface] = '! eth1'
@resource[iface].should == '! eth1'
end
it "should accept an interface alias for the #{iface} value as a string" do
@resource[iface] = 'eth1:2'
@resource[iface].should == 'eth1:2'
end
end
end

Expand Down