| 1 |
#!/usr/bin/perl -w |
|---|
| 2 |
our $ID = q$Id: rebuild-iptables 344 2006-10-04 02:48:30Z digant $; |
|---|
| 3 |
# |
|---|
| 4 |
# rebuild-iptables -- Construct an iptables rules file from fragments. |
|---|
| 5 |
# |
|---|
| 6 |
# Written by Russ Allbery <rra@stanford.edu> |
|---|
| 7 |
# Adapted by Digant C Kasundra <digant@stanford.edu> |
|---|
| 8 |
# Copyright 2005, 2006 Board of Trustees, Leland Stanford Jr. University |
|---|
| 9 |
# |
|---|
| 10 |
# Constructs an iptables rules file from the prefix, standard, and suffix |
|---|
| 11 |
# files in the iptables configuration area, adding any additional modules |
|---|
| 12 |
# specified in the command line, and prints the resulting iptables rules to |
|---|
| 13 |
# standard output (suitable for saving into /var/lib/iptables or some other |
|---|
| 14 |
# appropriate location on the system). |
|---|
| 15 |
|
|---|
| 16 |
############################################################################## |
|---|
| 17 |
# Modules and declarations |
|---|
| 18 |
############################################################################## |
|---|
| 19 |
|
|---|
| 20 |
require 5.006; |
|---|
| 21 |
use strict; |
|---|
| 22 |
|
|---|
| 23 |
use Getopt::Long qw(GetOptions); |
|---|
| 24 |
|
|---|
| 25 |
# Path to the iptables template area. |
|---|
| 26 |
our $TEMPLATE = '/afs/ir/service/jumpstart/data/iptables'; |
|---|
| 27 |
|
|---|
| 28 |
############################################################################## |
|---|
| 29 |
# Installation |
|---|
| 30 |
############################################################################## |
|---|
| 31 |
|
|---|
| 32 |
# Return the prefix |
|---|
| 33 |
sub prefix { |
|---|
| 34 |
my $type = shift; |
|---|
| 35 |
my $data; |
|---|
| 36 |
if ( $type eq 'filter' ) { |
|---|
| 37 |
( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm; |
|---|
| 38 |
*filter |
|---|
| 39 |
:INPUT ACCEPT |
|---|
| 40 |
:FORWARD ACCEPT |
|---|
| 41 |
:OUTPUT ACCEPT |
|---|
| 42 |
END_OF_PREFIX |
|---|
| 43 |
} |
|---|
| 44 |
elsif ( $type eq 'mangle' ) { |
|---|
| 45 |
( $data = <<'END_OF_PREFIX' ) =~ s/^\s+//gm; |
|---|
| 46 |
*nat |
|---|
| 47 |
:PREROUTING ACCEPT |
|---|
| 48 |
:POSTROUTING ACCEPT |
|---|
| 49 |
:OUTPUT ACCEPT |
|---|
| 50 |
END_OF_PREFIX |
|---|
| 51 |
} |
|---|
| 52 |
return $data; |
|---|
| 53 |
} |
|---|
| 54 |
|
|---|
| 55 |
# Return the suffix |
|---|
| 56 |
sub suffix { |
|---|
| 57 |
my $data; |
|---|
| 58 |
( $data = <<'END_OF_SUFFIX' ) =~ s/^\s+//gm; |
|---|
| 59 |
COMMIT |
|---|
| 60 |
END_OF_SUFFIX |
|---|
| 61 |
|
|---|
| 62 |
return $data; |
|---|
| 63 |
} |
|---|
| 64 |
# Read in a file, processing includes as required. Returns the contents of |
|---|
| 65 |
# the file as an array. |
|---|
| 66 |
sub read_iptables { |
|---|
| 67 |
my ($file) = @_; |
|---|
| 68 |
my @data; |
|---|
| 69 |
$file = $TEMPLATE . '/' . $file unless $file =~ m%^\.?/%; |
|---|
| 70 |
open my $MODULE, '<', $file or die "$0: cannot open $file: $!\n"; |
|---|
| 71 |
local $_; |
|---|
| 72 |
while (<$MODULE>) { |
|---|
| 73 |
if (/^\s*include\s+(\S+)$/) { |
|---|
| 74 |
my $included = $1; |
|---|
| 75 |
$included = $TEMPLATE . '/' . $included |
|---|
| 76 |
unless $included =~ m%^\.?/%; |
|---|
| 77 |
if ($file eq $included) { |
|---|
| 78 |
die "$0: include loop in $file, line $.\n"; |
|---|
| 79 |
} |
|---|
| 80 |
push (@data, "\n"); |
|---|
| 81 |
push (@data, read_iptables ($included)); |
|---|
| 82 |
push (@data, "\n"); |
|---|
| 83 |
} elsif (/^\s*include\s/) { |
|---|
| 84 |
die "$0: malformed include line in $file, line $.\n"; |
|---|
| 85 |
} else { |
|---|
| 86 |
# strip comments/whitespace/blank lines out of module |
|---|
| 87 |
$_ =~ s/\s*#.*$//; |
|---|
| 88 |
$_ =~ s/^\s*//; |
|---|
| 89 |
if ( $_ !~ /^\s*$/ ) { |
|---|
| 90 |
push (@data, $_); |
|---|
| 91 |
} |
|---|
| 92 |
} |
|---|
| 93 |
} |
|---|
| 94 |
close $MODULE; |
|---|
| 95 |
return @data; |
|---|
| 96 |
} |
|---|
| 97 |
|
|---|
| 98 |
# Write a file carefully. |
|---|
| 99 |
# Consider using File::Temp |
|---|
| 100 |
sub write_iptables { |
|---|
| 101 |
my ($file, @data) = @_; |
|---|
| 102 |
open my $NEW, '>', "$file.new" or die "$0: cannot create $file.new: $!\n"; |
|---|
| 103 |
print $NEW @data or die "$0: cannot write to $file.new: $!\n"; |
|---|
| 104 |
close $NEW or die "$0: cannot flush $file.new: $!\n"; |
|---|
| 105 |
rename ("$file.new", $file) |
|---|
| 106 |
or die "$0: cannot install new $file: $!\n"; |
|---|
| 107 |
} |
|---|
| 108 |
|
|---|
| 109 |
# Install iptables on a Red Hat system. Takes the array containing the new |
|---|
| 110 |
# iptables data. |
|---|
| 111 |
sub install_redhat { |
|---|
| 112 |
my (@data) = @_; |
|---|
| 113 |
write_iptables ('/etc/sysconfig/iptables', @data); |
|---|
| 114 |
system('/sbin/service', 'iptables', 'restart'); |
|---|
| 115 |
} |
|---|
| 116 |
|
|---|
| 117 |
# Install iptables on a Debian system. Take the array containing the new |
|---|
| 118 |
# iptables data. |
|---|
| 119 |
sub install_debian { |
|---|
| 120 |
my (@data) = @_; |
|---|
| 121 |
unless (-d '/etc/iptables') { |
|---|
| 122 |
mkdir ('/etc/iptables', 0755) |
|---|
| 123 |
or die "$0: cannot mkdir /etc/iptables: $!\n"; |
|---|
| 124 |
} |
|---|
| 125 |
write_iptables ('/etc/iptables/general', @data); |
|---|
| 126 |
system('/sbin/iptables-restore < /etc/iptables/general'); |
|---|
| 127 |
} |
|---|
| 128 |
|
|---|
| 129 |
############################################################################## |
|---|
| 130 |
# Main routine |
|---|
| 131 |
############################################################################## |
|---|
| 132 |
|
|---|
| 133 |
# Fix things up for error reporting. |
|---|
| 134 |
$| = 1; |
|---|
| 135 |
my $fullpath = $0; |
|---|
| 136 |
$0 =~ s%.*/%%; |
|---|
| 137 |
|
|---|
| 138 |
# Parse command-line options. |
|---|
| 139 |
my ($help, $version); |
|---|
| 140 |
Getopt::Long::config ('bundling', 'no_ignore_case'); |
|---|
| 141 |
GetOptions ('h|help' => \$help, |
|---|
| 142 |
'v|version' => \$version) or exit 1; |
|---|
| 143 |
if ($help) { |
|---|
| 144 |
print "Feeding myself to perldoc, please wait....\n"; |
|---|
| 145 |
exec ('perldoc', '-t', $fullpath); |
|---|
| 146 |
} elsif ($version) { |
|---|
| 147 |
my $version = join (' ', (split (' ', $ID))[1..3]); |
|---|
| 148 |
$version =~ s/,v\b//; |
|---|
| 149 |
$version =~ s/(\S+)$/($1)/; |
|---|
| 150 |
$version =~ tr%/%-%; |
|---|
| 151 |
print $version, "\n"; |
|---|
| 152 |
exit; |
|---|
| 153 |
} |
|---|
| 154 |
my ( @filter_modules, @mangle_modules ); |
|---|
| 155 |
|
|---|
| 156 |
if ( -d '/etc/iptables.d' ) { |
|---|
| 157 |
@filter_modules = </etc/iptables.d/filter-*>; |
|---|
| 158 |
@mangle_modules = </etc/iptables.d/mangle-*>; |
|---|
| 159 |
print "Filter modules found: " . join(', ', @filter_modules) . "\n"; |
|---|
| 160 |
print "Mangle modules found: " . join(', ', @mangle_modules) . "\n"; |
|---|
| 161 |
} |
|---|
| 162 |
|
|---|
| 163 |
# Concatenate everything together. |
|---|
| 164 |
my @data; |
|---|
| 165 |
push (@data, prefix('filter')); |
|---|
| 166 |
push (@data, "\n"); |
|---|
| 167 |
for my $module (@filter_modules) { |
|---|
| 168 |
push (@data, read_iptables($module)); |
|---|
| 169 |
push (@data, "\n"); |
|---|
| 170 |
} |
|---|
| 171 |
push (@data, suffix()); |
|---|
| 172 |
|
|---|
| 173 |
push (@data, prefix('mangle')); |
|---|
| 174 |
push (@data, "\n"); |
|---|
| 175 |
for my $module (@mangle_modules) { |
|---|
| 176 |
push (@data, read_iptables($module)); |
|---|
| 177 |
push (@data, "\n"); |
|---|
| 178 |
} |
|---|
| 179 |
push (@data, suffix()); |
|---|
| 180 |
|
|---|
| 181 |
if (-f '/etc/debian_version') { |
|---|
| 182 |
install_debian (@data); |
|---|
| 183 |
} elsif (-f '/etc/redhat-release') { |
|---|
| 184 |
install_redhat (@data); |
|---|
| 185 |
} else { |
|---|
| 186 |
die "$0: cannot figure out whether this is Red Hat or Debian\n"; |
|---|
| 187 |
} |
|---|
| 188 |
|
|---|
| 189 |
exit 0; |
|---|
| 190 |
__END__ |
|---|
| 191 |
|
|---|
| 192 |
############################################################################## |
|---|
| 193 |
# Documentation |
|---|
| 194 |
############################################################################## |
|---|
| 195 |
|
|---|
| 196 |
=head1 NAME |
|---|
| 197 |
|
|---|
| 198 |
rebuild-iptables - Construct an iptables rules file from fragments |
|---|
| 199 |
|
|---|
| 200 |
=head1 SYNOPSIS |
|---|
| 201 |
|
|---|
| 202 |
rebuild-iptables [B<-hv>] |
|---|
| 203 |
|
|---|
| 204 |
=head1 DESCRIPTION |
|---|
| 205 |
|
|---|
| 206 |
B<rebuild-iptables> constructs an iptables configuration file by concatenating |
|---|
| 207 |
various modules found in F</etc/iptables.d>. The resulting iptables |
|---|
| 208 |
configuration file is written to the appropriate file for either Red Hat or |
|---|
| 209 |
Debian (determined automatically) and iptables is restarted. |
|---|
| 210 |
|
|---|
| 211 |
Each module is just a text file located in the directory mentioned above that |
|---|
| 212 |
contains one or more iptables configuration lines (basically the arguments to |
|---|
| 213 |
an B<iptables> invocation), possibly including comments. |
|---|
| 214 |
|
|---|
| 215 |
NOTE: the module name needs to be prefixed with either filter- or mangle- . For |
|---|
| 216 |
example: /etc/iptables.d/filter-foo. This is required so the rules can be put |
|---|
| 217 |
in the appropriate table. |
|---|
| 218 |
|
|---|
| 219 |
WARNING: if the module name is not prefixed with filter- or mangle- it WILL be |
|---|
| 220 |
ignored. |
|---|
| 221 |
|
|---|
| 222 |
Along with the modules in the directory specified, a standard prefix and suffix |
|---|
| 223 |
is added. |
|---|
| 224 |
|
|---|
| 225 |
Normally, the contents of each module are read in verbatim, but a module may |
|---|
| 226 |
also contain the directive: |
|---|
| 227 |
|
|---|
| 228 |
include <module> |
|---|
| 229 |
|
|---|
| 230 |
on a separate line, where <module> is the path to another module to include, |
|---|
| 231 |
specified the same way as modules given on the command line (hence, either a |
|---|
| 232 |
file name relative to F</afs/ir/service/jumpstart/data/iptables> or an |
|---|
| 233 |
absolute path). Such a line will be replaced with the contents of the named |
|---|
| 234 |
file. Be careful when using this directive to not create loops; files |
|---|
| 235 |
including themselves will be detected, but more complex loops will not and |
|---|
| 236 |
will result in infinite output. |
|---|
| 237 |
|
|---|
| 238 |
=head1 OPTIONS |
|---|
| 239 |
|
|---|
| 240 |
=over 4 |
|---|
| 241 |
|
|---|
| 242 |
=item B<-h>, B<--help> |
|---|
| 243 |
|
|---|
| 244 |
Print out this documentation (which is done simply by feeding the script to |
|---|
| 245 |
C<perldoc -t>). |
|---|
| 246 |
|
|---|
| 247 |
=item B<-v>, B<--version> |
|---|
| 248 |
|
|---|
| 249 |
Print out the version of B<rebuild-iptables> and exit. |
|---|
| 250 |
|
|---|
| 251 |
=back |
|---|
| 252 |
|
|---|
| 253 |
=head1 FILES |
|---|
| 254 |
|
|---|
| 255 |
=over 4 |
|---|
| 256 |
|
|---|
| 257 |
=item F</etc/iptables.d> |
|---|
| 258 |
|
|---|
| 259 |
The default module location. |
|---|
| 260 |
|
|---|
| 261 |
=item F</etc/debian_version> |
|---|
| 262 |
|
|---|
| 263 |
If this file exists, the system is assumed to be a Debian system for |
|---|
| 264 |
determining the installation location when B<-i> is used. |
|---|
| 265 |
|
|---|
| 266 |
=item F</etc/iptables/general> |
|---|
| 267 |
|
|---|
| 268 |
The install location of the generated configuration file on Debian. |
|---|
| 269 |
|
|---|
| 270 |
=item F</etc/redhat-release> |
|---|
| 271 |
|
|---|
| 272 |
If this file exists, the system is assumed to be a Red Hat system for |
|---|
| 273 |
determining the installation location when B<-i> is used. |
|---|
| 274 |
|
|---|
| 275 |
=item F</etc/sysconfig/iptables> |
|---|
| 276 |
|
|---|
| 277 |
The install location of the generated configuration file on Red Hat. |
|---|
| 278 |
|
|---|
| 279 |
=back |
|---|
| 280 |
|
|---|
| 281 |
=head1 AUTHOR |
|---|
| 282 |
|
|---|
| 283 |
Russ Allbery <rra@stanford.edu> |
|---|
| 284 |
Digant C Kasundra <digant@stanford.edu> |
|---|
| 285 |
|
|---|
| 286 |
=head1 SEE ALSO |
|---|
| 287 |
|
|---|
| 288 |
iptables(8) |
|---|
| 289 |
|
|---|
| 290 |
=cut |
|---|
| 291 |
|
|---|