--- /dev/null
+;;; Directory Local Variables
+;;; See Info node `(emacs) Directory Variables' for more information.
+
+((puppet-mode
+ (puppet-indent-level . 2))
+ (ruby-mode
+ (ruby-indent-level . 2)))
+
Copyright (C) 2011 Puppet Labs, Inc.
Copyright (C) 2011 Jonathan Boyett
+Copyright (C) 2011 Media Temple, Inc.
Some of the iptables code was taken from puppet-iptables which was:
-## puppetlabs-firewall module
+# puppetlabs-firewall module
## User Guide
You may also need to restart Apache, although this shouldn't always be the
case.
+Add the following to your site.pp to make sure class firewall can
+persist iptables rules across reboots:
+
+ Firewall {
+ notify => Exec['firewall-persist'],
+ }
+
+Load the firewall class that provides these helper methods:
+
+ class { 'firewall': }
+
### Examples
Basic accept ICMP request example:
table => 'nat',
}
-You can make firewall rules persistent with the following iptables example:
-
- exec { "persist-firewall":
- command => $operatingsystem ? {
- "debian" => "/sbin/iptables-save > /etc/iptables/rules.v4",
- /(RedHat|CentOS)/ => "/sbin/iptables-save > /etc/sysconfig/iptables",
- }
- refreshonly => true,
- }
- Firewall {
- notify => Exec["persist-firewall"]
- }
-
If you wish to ensure any reject rules are executed last, try using stages.
The following example shows the creation of a class which is where your
last rules should run, this however should belong in a puppet module.
:proto, :gid, :uid, :sport, :dport, :port, :name, :state, :icmp, :limit, :burst, :jump,
:todest, :tosource, :toports, :log_level, :log_prefix, :reject]
+ ## Work-around for older vintages of iptables, where iptables-save
+ ## returns error if the kernel modules are not loaded. Listing
+ ## out the current rules with iptables proper autoloads the kernel modules.
+ def self.iptables_init
+ if ! File.exists?('/proc/net/ip6_tables_names')
+ iptables '-nL'
+ end
+ end
end
rules = []
counter = 1
+ self.iptables_init
+
# String#lines would be nice, but we need to support Ruby 1.8.5
iptables_save.split("\n").each do |line|
unless line =~ /^\#\s+|^\:\S+|^COMMIT/
rules
end
+ ## Work-around for older vintages of iptables, where iptables-save
+ ## returns error if the kernel modules are not loaded. Listing
+ ## out the current rules with iptables proper autoloads the kernel modules.
+ def self.iptables_init
+ if ! File.exists?('/proc/net/ip_tables_names')
+ iptables '-nL'
+ end
+ end
+
def self.rule_to_hash(line, table, counter)
hash = {}
keys = []
--- /dev/null
+# Class: firewall
+#
+# This module manages firewalls.
+#
+# Parameters:
+#
+# Actions:
+#
+# Sets up some resources to manage iptables configs on Linux.
+# on various flavours of Linux.
+# 1. A firewall-persist exec to make rebooting safe.
+# 2. Debian hates you and has no provisions for managing iptables by default. Fixes that.
+#
+# Requires:
+#
+# Sample Usage:
+#
+# Put this in site.pp to ensure proper operation:
+# Firewall {
+# notify => Exec['firewall-persist'],
+# }
+#
+class firewall {
+
+ Exec {
+ path => [ '/bin', '/sbin' ],
+ }
+
+ case $kernel {
+ 'Linux': {
+
+ case $operatingsystem {
+ Debian: {
+ ## The iptables in Lenny definitely works.
+ ## Older versions not tested.
+ ## Meanwhile, Debian 'testing' will break.
+ if $lsbmajdistrelease >= 5 {
+ $firewall_supports_ipv6 = true
+ }
+
+ file { '/etc/iptables':
+ ensure => directory,
+ group => 'root',
+ mode => '0750',
+ owner => 'root',
+ }
+
+ $ip6tables_rules = '/etc/iptables/rules.v6'
+
+ ## Squeeze 'iptables-persistent' package has unique rules file.
+ case $lsbmajdistrelease {
+ 6: { $iptables_rules = '/etc/iptables/rules' }
+ default: { $iptables_rules = '/etc/iptables/rules.v4' }
+ }
+
+ ## Lenny has no intrinsic ability to manage iptables persistence.
+ ## So we magick it up here. Note: this persists IPv6 rules..
+ if $lsbmajdistrelease <= 5 {
+ file { '/etc/init.d/iptables-persistent':
+ content => template('firewall/debian/iptables-persistent.erb'),
+ group => 'root',
+ mode => '0755',
+ owner => 'root',
+ }
+
+ $service_persist_requires = File['/etc/init.d/iptables-persistent']
+ }
+ else {
+ package { 'iptables-persistent':
+ ensure => present,
+ }
+ $service_persist_requires = Package['iptables-persistent']
+ }
+
+ service { 'iptables-persistent':
+ enable => true,
+ require => $service_persist_requires,
+ }
+
+ ## Squeeze 'iptables-persistent' package is ignorant of IPv6.
+ ## So, we slap in a separate IPv6 persistence script just for it.
+ if $firewall_supports_ipv6 {
+
+ file { $ip6tables_rules:
+ group => 'root',
+ mode => '0600',
+ owner => 'root',
+ require => Exec['firewall-persist'],
+ }
+
+ if $lsbmajdistrelease == 6 {
+ file { '/etc/init.d/ip6tables-persistent':
+ content => template('firewall/debian/iptables-persistent.erb'),
+ group => 'root',
+ mode => '0755',
+ owner => 'root',
+ }
+
+ service { 'ip6tables-persistent':
+ enable => true,
+ require => File['/etc/init.d/ip6tables-persistent'],
+ }
+
+ }
+ }
+ }
+ ## Since this RedHat section makes assumptions about release version numbering
+ ## It only makes sense for RHEL-alike OSes.
+ RedHat,CentOS,CloudLinux: {
+ ## ip6tables in CentOS 5.x does not support comments
+ ## The provider needs comments, so we play dumb on older versions.
+ if $lsbmajdistrelease >= 6 {
+ $firewall_supports_ipv6 = true
+ }
+
+ $ip6tables_rules = '/etc/sysconfig/ip6tables'
+ $iptables_rules = '/etc/sysconfig/iptables'
+
+ package { 'iptables':
+ ensure => present,
+ }
+
+ service { 'iptables':
+ enable => true,
+ }
+
+ package { 'iptables-ipv6':
+ ensure => present,
+ }
+
+ service { 'ip6tables':
+ enable => true,
+ }
+
+ ## RHEL supports IPv6, but older versions do not support comments.
+ ## The provider requires comments, so we just force a REJECT on all filter chains there.
+ if $firewall_supports_ipv6 {
+ file { $ip6tables_rules:
+ group => 'root',
+ mode => '0600',
+ owner => 'root',
+ require => Exec['firewall-persist'],
+ }
+ }
+ else {
+ file { $ip6tables_rules:
+ content => template('firewall/redhat/ip6tables.erb'),
+ group => 'root',
+ mode => '0600',
+ owner => 'root',
+ require => Exec['firewall-persist'],
+ }
+ exec { 'set-ipv6-iptables-policy':
+ command => '/sbin/service ip6tables restart',
+ subscribe => File[$ip6tables_rules],
+ refreshonly => true,
+ }
+ warning("On '${lsbdistdescription}', the firewall provider does not support ip6tables. Setting a default REJECT rule")
+ }
+ }
+ default: { fail("$operatingsystem is not supported by the firewall class") }
+ }
+
+ file { $iptables_rules:
+ group => 'root',
+ mode => '0600',
+ owner => 'root',
+ require => Exec['firewall-persist'],
+ }
+
+ ## Tell site.pp (see docs) to notify this exec for all firewall rules
+ ## Thus, ensuring good rules at boot time.
+ exec { 'firewall-persist':
+ command => "iptables-save |sed -e '/^#/ d' > ${iptables_rules}; ip6tables-save |sed -e '/^#/ d' > ${ip6tables_rules}",
+ refreshonly => true,
+ }
+
+ }
+ default: {
+ warning("'firewall-persist' is currently a noop on ${kernel}")
+ exec { 'firewall-persist':
+ command => '/bin/true',
+ refreshonly => true,
+ }
+ }
+ }
+}
--- /dev/null
+#!/usr/bin/env rspec
+require 'spec_helper'
+include PuppetSpec::Files
+
+describe 'firewall', :type => :class do
+
+ before do
+ @puppetdir = tmpdir('firewall')
+ manifestdir = File.join(@puppetdir, 'manifests')
+ Dir.mkdir(manifestdir)
+ FileUtils.touch(File.join(manifestdir, 'site.pp'))
+ Puppet[:confdir] = @puppetdir
+ end
+
+ after do
+ FileUtils.remove_entry_secure(@puppetdir)
+ end
+
+ ## Same results will apply for CentOS & CloudLinux
+ describe "RedHat generic tests" do
+ let(:facts) {
+ {
+ :operatingsystem => 'RedHat',
+ :kernel => 'Linux',
+ :lsbmajdistrelease => 5,
+ }
+ }
+ it { should contain_exec('firewall-persist') }
+ it { should contain_file('/etc/sysconfig/ip6tables').with_mode('0600') }
+ it { should contain_file('/etc/sysconfig/iptables').with_mode('0600') }
+ it { should contain_package('iptables').with_ensure('present') }
+ it { should contain_package('iptables-ipv6').with_ensure('present') }
+ it { should contain_service('iptables').with_enable(true) }
+ it { should contain_service('ip6tables').with_enable(true) }
+ end
+
+ ## Same results will apply for CentOS & CloudLinux
+ describe "RedHat 5.x tests" do
+ let(:facts) {
+ {
+ :operatingsystem => 'RedHat',
+ :kernel => 'Linux',
+ :lsbmajdistrelease => 5,
+ }
+ }
+ it { should contain_exec('set-ipv6-iptables-policy') }
+ end
+
+ describe "Debian generic tests" do
+ let(:facts) {
+ {
+ :operatingsystem => 'Debian',
+ :kernel => 'Linux',
+ }
+ }
+ it { should contain_exec('firewall-persist') }
+ it { should contain_file('/etc/iptables/rules.v6').with_mode('0600') }
+ it { should contain_service('iptables-persistent').with_enable(true) }
+ end
+ describe "Debian Lenny tests" do
+ let(:facts) {
+ {
+ :operatingsystem => 'Debian',
+ :kernel => 'Linux',
+ :lsbmajdistrelease => 5,
+ }
+ }
+ it { should contain_file('/etc/init.d/iptables-persistent') }
+ it { should contain_file('/etc/iptables/rules.v4').with_mode('0600') }
+
+ end
+ describe "Debian Squeeze tests" do
+ let(:facts) {
+ {
+ :operatingsystem => 'Debian',
+ :kernel => 'Linux',
+ :lsbmajdistrelease => 6,
+ }
+ }
+ it { should contain_file('/etc/init.d/ip6tables-persistent') }
+ it { should contain_file('/etc/iptables/rules').with_mode('0600') }
+ it { should contain_package('iptables-persistent').with_ensure('present') }
+ it { should contain_service('ip6tables-persistent').with_enable(true) }
+ end
+ describe "Debian Wheezy tests" do
+ let(:facts) {
+ {
+ :operatingsystem => 'Debian',
+ :kernel => 'Linux',
+ :lsbmajdistrelease => 7,
+ }
+ }
+ it { should contain_file('/etc/iptables/rules.v4').with_mode('0600') }
+ it { should contain_file('/etc/iptables/rules.v6').with_mode('0600') }
+ it { should contain_package('iptables-persistent').with_ensure('present') }
+ end
+end
require 'mocha'
gem 'rspec', '>=2.0.0'
require 'rspec/expectations'
+require 'rspec-puppet'
# So everyone else doesn't have to include this base constant.
module PuppetSpec
RSpec.configure do |config|
include PuppetSpec::Fixtures
+ config.module_path = File.join(File.dirname(__FILE__), '../../')
+
config.mock_with :mocha
config.before :each do
# Set the confdir and vardir to gibberish so that tests
# have to be correctly mocked.
- Puppet[:confdir] = "/dev/null"
Puppet[:vardir] = "/dev/null"
# Avoid opening ports to the outside world
--- /dev/null
+#!/bin/sh
+#
+<% version = lsbmajdistrelease.to_i -%>
+### BEGIN INIT INFO
+# Provides: <% if version <= 5 %>iptables<% else %>ip6tables<%end%>-persistent
+# Required-Start: mountkernfs $local_fs
+# Required-Stop: $local_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# X-Start-Before: $network
+# X-Stop-After: $network
+# Short-Description: Set up iptables rules
+### END INIT INFO
+
+. /lib/lsb/init-functions
+
+rc=0
+IP6TABLES_RULES=<%= ip6tables_rules %>
+<% if version <= 5 -%>
+IPTABLES_RULES=<%= iptables_rules %>
+<% end -%>
+
+case "$1" in
+ start|restart|reload|force-reload)
+ log_action_begin_msg "Loading iptables rules"
+
+<% if version <= 5 -%>
+ if [ -f $IPTABLES_RULES ]; then
+ log_action_cont_msg " IPv4"
+ iptables-restore < $IPTABLES_RULES 2> /dev/null
+ if [ $? -ne 0 ]; then
+ rc=1
+ fi
+ fi
+<% end -%>
+
+ if [ -f $IP6TABLES_RULES ]; then
+ log_action_cont_msg " IPv6"
+ ip6tables-restore < $IP6TABLES_RULES 2> /dev/null
+ if [ $? -ne 0 ]; then
+ rc=1
+ fi
+ fi
+
+ log_action_end_msg $rc
+ ;;
+ stop)
+ ;;
+ *)
+ echo "Usage: $0 {start|restart|reload|force-reload|save}" >&2
+ exit 1
+ ;;
+esac
+
+exit $rc
--- /dev/null
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -j REJECT --reject-with icmp6-port-unreachable
+-A FORWARD -j REJECT --reject-with icmp6-port-unreachable
+-A OUTPUT -j REJECT --reject-with icmp6-port-unreachable
+COMMIT
--- /dev/null
+node default {
+ class { 'firewall': }
+}