IPTables Module
The iptables module illustrated will help administer iptables rules. There are essentially three parts: the module manifest, which declares an iptables class and a defined-type for adding fragments, the rebuild-iptables script that rebuilds the iptables rules when a fragment is added or removed, and an area for fragments.
modules/iptables/manifests/init.pp
# Handles iptables concerns. See also ipt_fragment definition
define ipt_fragment($ensure) {
case $ensure {
absent: {
file { "/etc/iptables.d/$name":
ensure => absent,
}
}
present: {
file {
"/etc/iptables.d/$name":
source => "puppet://puppet/iptables/fragments/$name",
notify => Exec[rebuild_iptables],
}
}
}
}
class iptables {
package { "iptables":
ensure => present
}
exec { "rebuild_iptables":
command => "/usr/sbin/rebuild-iptables",
refreshonly => true,
require => File["/usr/sbin/rebuild-iptables"],
}
file {
"/etc/iptables.d":
ensure => directory,
purge => true,
notify => Exec["rebuild_iptables"];
"/usr/sbin/rebuild-iptables":
source => "puppet://puppet/iptables/rebuild-iptables";
}
}
modules/iptables/files/rebuild-iptables
#!/usr/bin/perl -w
our $ID = q$Id: rebuild-iptables 344 2006-10-04 02:48:30Z digant $;
#
# rebuild-iptables -- Construct an iptables rules file from fragments.
#
# Written by Russ Allbery <rra@stanford.edu>
# Adapted by Digant C Kasundra <digant@stanford.edu>
# Copyright 2005, 2006 Board of Trustees, Leland Stanford Jr. University
#
# Constructs an iptables rules file from the prefix, standard, and suffix
# files in the iptables configuration area, adding any additional modules
# specified in the command line, and prints the resulting iptables rules to
# standard output (suitable for saving into /var/lib/iptables or some other
# appropriate location on the system).
##############################################################################
# Modules and declarations
##############################################################################
require 5.006;
use strict;
use Getopt::Long qw(GetOptions);
# Path to the iptables template area.
our $TEMPLATE = '/afs/ir/service/jumpstart/data/iptables';
##############################################################################
# Installation
##############################################################################
# Return the prefix
sub prefix {
my $data;
( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm;
*filter
:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT
:SUL -
-A INPUT -j SUL
-A SUL -i lo -j ACCEPT
END_OF_PREFIX
return $data;
}
# Return the suffix
sub suffix {
my $data;
( $data = <<'END_OF_SUFFIX' ) =~ s/^\s+//gm;
# Rejects all remaining connections with port-unreachable errors.
-A SUL -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable
-A SUL -p udp -j REJECT --reject-with icmp-port-unreachable
COMMIT
END_OF_SUFFIX
return $data;
}
# Read in a file, processing includes as required. Returns the contents of
# the file as an array.
sub read_iptables {
my ($file) = @_;
my @data;
$file = $TEMPLATE . '/' . $file unless $file =~ m%^\.?/%;
open my $MODULE, '<', $file or die "$0: cannot open $file: $!\n";
local $_;
while (<$MODULE>) {
if (/^\s*include\s+(\S+)$/) {
my $included = $1;
$included = $TEMPLATE . '/' . $included
unless $included =~ m%^\.?/%;
if ($file eq $included) {
die "$0: include loop in $file, line $.\n";
}
push (@data, "\n");
push (@data, read_iptables ($included));
push (@data, "\n");
} elsif (/^\s*include\s/) {
die "$0: malformed include line in $file, line $.\n";
} else {
push (@data, $_);
}
}
close $MODULE;
return @data;
}
# Write a file carefully.
# Consider using File::Temp
sub write_iptables {
my ($file, @data) = @_;
open my $NEW, '>', "$file.new" or die "$0: cannot create $file.new: $!\n";
print $NEW @data or die "$0: cannot write to $file.new: $!\n";
close $NEW or die "$0: cannot flush $file.new: $!\n";
rename ("$file.new", $file)
or die "$0: cannot install new $file: $!\n";
}
# Install iptables on a Red Hat system. Takes the array containing the new
# iptables data.
sub install_redhat {
my (@data) = @_;
write_iptables ('/etc/sysconfig/iptables', @data);
system('/sbin/service', 'iptables', 'restart');
}
# Install iptables on a Debian system. Take the array containing the new
# iptables data.
sub install_debian {
my (@data) = @_;
unless (-d '/etc/iptables') {
mkdir ('/etc/iptables', 0755)
or die "$0: cannot mkdir /etc/iptables: $!\n";
}
write_iptables ('/etc/iptables/general', @data);
system('/sbin/iptables-restore < /etc/iptables/general');
}
##############################################################################
# Main routine
##############################################################################
# Fix things up for error reporting.
$| = 1;
my $fullpath = $0;
$0 =~ s%.*/%%;
# Parse command-line options.
my ($help, $version);
Getopt::Long::config ('bundling', 'no_ignore_case');
GetOptions ('h|help' => \$help,
'v|version' => \$version) or exit 1;
if ($help) {
print "Feeding myself to perldoc, please wait....\n";
exec ('perldoc', '-t', $fullpath);
} elsif ($version) {
my $version = join (' ', (split (' ', $ID))[1..3]);
$version =~ s/,v\b//;
$version =~ s/(\S+)$/($1)/;
$version =~ tr%/%-%;
print $version, "\n";
exit;
}
my @modules;
if ( -d '/etc/iptables.d' ) {
@modules = </etc/iptables.d/*>;
}
# Concatenate everything together.
my @data;
push (@data, prefix());
push (@data, "\n");
for my $module (@modules) {
push (@data, read_iptables($module));
push (@data, "\n");
}
push (@data, suffix());
if (-f '/etc/debian_version') {
install_debian (@data);
} elsif (-f '/etc/redhat-release') {
install_redhat (@data);
} else {
die "$0: cannot figure out whether this is Red Hat or Debian\n";
}
exit 0;
__END__
##############################################################################
# Documentation
##############################################################################
=head1 NAME
rebuild-iptables - Construct an iptables rules file from fragments
=head1 SYNOPSIS
rebuild-iptables [B<-hv>]
=head1 DESCRIPTION
B<rebuild-iptables> constructs an iptables configuration file by concatenating
various modules found in F</etc/iptables.d>. The resulting iptables
configuration file is written to the appropriate file for either Red Hat or
Debian (determined automatically) and iptables is restarted.
Each module is just a text file located in the directory mentioned above that
contains one or more iptables configuration lines (basically the arguments to
an B<iptables> invocation), possibly including comments.
Along with the modules in the directory specified, a standard prefix and suffix
is added.
Normally, the contents of each module are read in verbatim, but a module may
also contain the directive:
include <module>
on a separate line, where <module> is the path to another module to include,
specified the same way as modules given on the command line (hence, either a
file name relative to F</afs/ir/service/jumpstart/data/iptables> or an
absolute path). Such a line will be replaced with the contents of the named
file. Be careful when using this directive to not create loops; files
including themselves will be detected, but more complex loops will not and
will result in infinite output.
=head1 OPTIONS
=over 4
=item B<-h>, B<--help>
Print out this documentation (which is done simply by feeding the script to
C<perldoc -t>).
=item B<-v>, B<--version>
Print out the version of B<rebuild-iptables> and exit.
=back
=head1 FILES
=over 4
=item F</etc/iptables.d>
The default module location.
=item F</etc/debian_version>
If this file exists, the system is assumed to be a Debian system for
determining the installation location when B<-i> is used.
=item F</etc/iptables/general>
The install location of the generated configuration file on Debian.
=item F</etc/redhat-release>
If this file exists, the system is assumed to be a Red Hat system for
determining the installation location when B<-i> is used.
=item F</etc/sysconfig/iptables>
The install location of the generated configuration file on Red Hat.
=back
=head1 AUTHOR
Russ Allbery <rra@stanford.edu>
Digant C Kasundra <digant@stanford.edu>
=head1 SEE ALSO
iptables(8)
=cut
modules/iptables/files/fragments/ftp (sample)
# FTP setup, requiring passive mode. -A SUL -s 171.64.0.0/14 -p tcp -m tcp --dport 20 --syn -j ACCEPT -A SUL -s 172.24.0.0/14 -p tcp -m tcp --dport 20 --syn -j ACCEPT -A SUL -s 171.64.0.0/14 -p tcp -m tcp --dport 21 --syn -j ACCEPT -A SUL -s 172.24.0.0/14 -p tcp -m tcp --dport 21 --syn -j ACCEPT
Discussion
Thanks for the recipe, i have taken what you have done here and modified it to be more versatile. The rebuild-iptables has been modified. this is the diff output from your original to my modified, I have also included the modified version as an attachment.
Main changes include:
- removed the suffix dropping all traffic to a drop table
- added type to prefix as we now handle table type (ie filter or mangle)
- added support to have comments/whitespace to fragments
- changed module globbing to use filename prefix of filter- or mangle- this is so we can have rules for both tables in multiple modules
- added some documentation to reflect the filename requirements
--- rebuild-iptables 2008-07-02 10:06:43.000000000 +1000
+++ rebuild-iptables-new 2008-07-02 10:04:43.000000000 +1000
@@ -31,17 +31,24 @@
# Return the prefix
sub prefix {
+ my $type = shift;
my $data;
- ( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm;
- *filter
- :INPUT ACCEPT
- :FORWARD ACCEPT
- :OUTPUT ACCEPT
- :SUL -
- -A INPUT -j SUL
- -A SUL -i lo -j ACCEPT
+ if ( $type eq 'filter' ) {
+ ( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm;
+*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
END_OF_PREFIX
-
+ }
+ elsif ( $type eq 'mangle' ) {
+ ( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm;
+*nat
+:PREROUTING ACCEPT
+:POSTROUTING ACCEPT
+:OUTPUT ACCEPT
+END_OF_PREFIX
+ }
return $data;
}
@@ -49,10 +56,6 @@
sub suffix {
my $data;
( $data = <<'END_OF_SUFFIX' ) =~ s/^\s+//gm;
- # Rejects all remaining connections with port-unreachable errors.
-
- -A SUL -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j REJECT --reject-with icmp-port-unreachable
- -A SUL -p udp -j REJECT --reject-with icmp-port-unreachable
COMMIT
END_OF_SUFFIX
@@ -80,7 +83,12 @@
} elsif (/^\s*include\s/) {
die "$0: malformed include line in $file, line $.\n";
} else {
- push (@data, $_);
+ # strip comments/whitespace/blank lines out of module
+ $_ =~ s/\s*#.*$//;
+ $_ =~ s/^\s*//;
+ if ( $_ !~ /^\s*$/ ) {
+ push (@data, $_);
+ }
}
}
close $MODULE;
@@ -143,17 +151,30 @@
print $version, "\n";
exit;
}
-my @modules;
+my ( @filter_modules, @mangle_modules );
if ( -d '/etc/iptables.d' ) {
- @modules = </etc/iptables.d/*>;
+ @filter_modules = </etc/iptables.d/filter-*>;
+ @mangle_modules = </etc/iptables.d/mangle-*>;
}
# Concatenate everything together.
my @data;
-push (@data, prefix());
+push (@data, prefix('filter'));
push (@data, "\n");
-for my $module (@modules) {
+for my $module (@filter_modules) {
+ push (@data, read_iptables($module));
+ push (@data, "\n");
+}
+push (@data, suffix());
+
+push (@data, prefix('mangle'));
+push (@data, "\n");
+for my $module (@mangle_modules) {
push (@data, read_iptables($module));
push (@data, "\n");
}
@@ -193,6 +214,13 @@
contains one or more iptables configuration lines (basically the arguments to
an B<iptables> invocation), possibly including comments.
+NOTE: the module name needs to be prefixed with either filter- or mangle- . For
+example: /etc/iptables.d/filter-foo. This is required so the rules can be put
+in the appropriate table.
+
+WARNING: if the module name is not prefixed with filter- or mangle- it WILL be
+ignored.
+
Along with the modules in the directory specified, a standard prefix and suffix
is added.
Attachments
- rebuild-iptables-modified (8.1 kB) -
modified rebuilt-iptables
, added by mrproper on 07/02/08 02:53:46.