#!/usr/bin/perl -W # levy.pl v1.22 # a basic perl jobber to dump a skeleton # iptables and define some open # ports. # levy generates a general iptables rulset based # on a given external interface and set of ports to open. # It tries to make "good" decisions about "general" policies # with respect to packet filtering, but makes no claim to # do the right job for all cases. It is more of a template # generator than anything else, with its goal being to give # folks a "sane" foundation to begin with. It can construct # a basic firewall with specified open ports, NAT internal # networks, and stateful tracking. # levy creates -skeleton- rulesets # Copyright 2002,2003 Roger Gregory # Released under the "Artistic License", a copy of which # can be found at: http://www.perl.com/language/misc/Artistic.html # 1.22 # added trusted network allowances # basic cleanup # 1.20 # Folded in diff by Boris Zentner, sort port by number, # prints command line in script output. # user defined options @ICMP = qw (destination-unreachable time-exceeded echo-reply); $command_line = "$0 " . join (' ', @ARGV); # get options use Getopt::Long; GetOptions( "log" => \$log_opt, # set logging for catch-all "help" => \$help, # print usage information "ip=f" => \$firewallip, # specify ip of firewall interface "nat=s" => \$nat, # specify internal mask to NAT for "reserved" => \$reserved, # increase reserve drop list "executable" => \$executable, # create a standalone shell-script "queue" => \$queue, # use queue target for default log "paranoid" => \$paranoid, # set paranoid defaults "trusted=s" => \$trusted, # define trusted network "match" => \$match_opt ); # match ports and protocols for incoming allows # ensure options have been passed if ( $#ARGV < 0 ) { &usage(); exit; } # check given external interface warn "WARNING: I can't find that interface\n" unless (grep /$ARGV[0]/, `/sbin/ifconfig -a`); $EXTERNAL_INTERFACE=$ARGV[0]; # setup die "Cannot determine path to iptables: set manually" unless (chomp($iptables = `which iptables`)); $log = (); $portmatch = (); $SERVICES="/etc/services"; %seen = (); $DATE=localtime; # usage ditty sub usage() { print < Optons: -l log all dropped connections -q use QUEUE target instead of LOG target (must be used with -l option) -m match ports with their listed protocols in /etc/services -r increase reserved drops -i ip of external interface (use this ip for rulesets) -n turn on NAT for given netmask -e setup output as shell script -p set paranoid defaults (no outbound netbios) -t define trusted (unfiltered) network Example: set with logging, match protocols to their ports, open ports 22 and 80, and NAT 192.168.0.0/16: ./levy.pl eth0 22 80 -l -m -n 192.168.0.0/16 DUMP ; } print "$match_opt\n"; # set options $log = "1" unless ! $log_opt; $portmatch = "1" unless ! $match_opt; # set ip of firewall if given if ($firewallip) { $DITTY="-d $firewallip"; # set firewall ip if given } else { $DITTY="-i $EXTERNAL_INTERFACE"; } # dump help if ($help) { &usage; # dump usage ditty if -h exit 1 } # should out output be set # as a shell script print "#!/bin/sh\n" unless ! $executable; # Reserved list from http://www.iana.org/assignments/ipv4-address-space # (dated 2001 September 12) @RESERVED_IANA = qw (0.0.0.0/8 1.0.0.0/8 2.0.0.0/8 5.0.0.0/8 7.0.0.0/8 10.0.0.0/8 23.0.0.0/8 27.0.0.0/8 31.0.0.0/8 36.0.0.0/8 39.0.0.0/8 41.0.0.0/8 42.0.0.0/8 58.0.0.0/8 59.0.0.0/8 60.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 197.0.0.0/8 224.0.0.0/3 240.0.0.0/8); # minimal set of reserved addresses @RESERVED_MINIMAL = qw (127.0.0.0/8 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8); # header print "# Iptables Firewall - created by levy.pl on $DATE\n", "# Created with $command_line\n", "# http://muse.linuxmafia.org/levy\n"; # flush and set default policies print "\n# chain policies\n", "# set default policies\n", "$iptables -P INPUT DROP\n", "$iptables -P OUTPUT ACCEPT\n", "$iptables -P FORWARD DROP\n", "\n# flush tables\n", "$iptables -F\n", "$iptables -F INPUT\n", "$iptables -F OUTPUT\n", "$iptables -F FORWARD\n", "$iptables -F -t mangle\n", "$iptables -X\n", "$iptables -F -t nat\n"; # create basic tables print "\n# create DUMP table\n", "$iptables -N DUMP > /dev/null\n", "$iptables -F DUMP\n"; if ($log) { if ($queue) { print "$iptables -A DUMP -j QUEUE\n", "$iptables -A DUMP -p tcp -j REJECT --reject-with tcp-reset\n", "$iptables -A DUMP -p udp -j REJECT --reject-with icmp-port-unreachable\n", "$iptables -A DUMP -j DROP\n"; } else { print "$iptables -A DUMP -p tcp -j LOG\n", "$iptables -A DUMP -p udp -j LOG\n", "$iptables -A DUMP -p tcp -j REJECT --reject-with tcp-reset\n", "$iptables -A DUMP -p udp -j REJECT --reject-with icmp-port-unreachable\n", "$iptables -A DUMP -j DROP\n"; } } else { print "$iptables -A DUMP -p tcp -j REJECT --reject-with tcp-reset\n", "$iptables -A DUMP -p udp -j REJECT --reject-with icmp-port-unreachable\n", "$iptables -A DUMP -j DROP\n"; } # create and init stateful print "\n# Stateful table\n", "$iptables -N STATEFUL > /dev/null\n", "$iptables -F STATEFUL\n", "$iptables -I STATEFUL -m state --state ESTABLISHED,RELATED -j ACCEPT\n", "$iptables -A STATEFUL -m state --state NEW -i ! $EXTERNAL_INTERFACE -j ACCEPT\n", "$iptables -A STATEFUL -j DUMP\n"; # loopback print "\n# loopback rules\n", "$iptables -A INPUT -i lo -j ACCEPT\n", "$iptables -A OUTPUT -o lo -j ACCEPT\n"; # drop a few obvious reserved addresses print "\n# drop reserved addresses incoming\n"; if ($reserved) { foreach $IANA (@RESERVED_IANA) { print "$iptables -A INPUT -i $EXTERNAL_INTERFACE -s $IANA -j DUMP\n"; } } else { foreach $MINIMAL (@RESERVED_MINIMAL) { print "$iptables -A INPUT -i $EXTERNAL_INTERFACE -s $MINIMAL -j DUMP\n"; } } # ICMP stuff print "\n# allow certain inbound ICMP types\n"; # allow defined icmp foreach $icmp (@ICMP) { print "$iptables -A INPUT -i $EXTERNAL_INTERFACE -p icmp --icmp-type $icmp -j ACCEPT\n"; } # Add opened ports unless invalid # or duplicate. print "\n# opened ports\n"; foreach $port (sort { $a <=> $b } (@ARGV[1 .. $#ARGV])) { add_port($port) unless ($port =~ /\D/) || ($port > 65535) || ($port < 1) || ($seen{$port}++); } # set paranoid defualts if defined if ($paranoid) { &do_paranoid; } # do NAT if defined if ($nat) { &do_nat; } # do trusted network if defined if ($trusted) { &do_trusted; } # dump catch-all print "\n# push everything else to state table\n", "$iptables -A INPUT -j STATEFUL\n"; # subs # define add_port functions # eo's mad service munch sub add_port { if ($portmatch) { open(SVFILE, $SERVICES) or die "Error: $!\n"; $check_match=(); # set toggle for unknown ports while(defined($svline = )) { next if ($svline =~ /^#/); next if ($svline =~ /^\s/); if ($svline =~ /\b$port\b/) { @poof = split(/\t/, $svline); foreach $service (@poof) { if ($service =~ /\btcp\b/) { print "$iptables -A INPUT -p tcp $DITTY --dport $port -j ACCEPT\n"; $check_match++; } if ($service =~ /\budp\b/) { print "$iptables -A INPUT -p udp $DITTY --dport $port -j ACCEPT\n"; $check_match++; } } } } if (!$check_match) { print "$iptables -A INPUT -p tcp $DITTY --dport $port -j ACCEPT\n", "$iptables -A INPUT -p udp $DITTY --dport $port -j ACCEPT\n"; } } else { print "$iptables -A INPUT -p tcp $DITTY --dport $port -j ACCEPT\n", "$iptables -A INPUT -p udp $DITTY --dport $port -j ACCEPT\n"; } } # NAT sub do_nat() { print "\n# Set up NAT for internal network\n", "$iptables -t nat -A POSTROUTING -s $nat -o $EXTERNAL_INTERFACE -j MASQUERADE\n"; } # unfiltered networks sub do_trusted() { print "\n# unfiltered network/host\n", "$iptables -A INPUT -s $trusted -j ACCEPT\n"; } # drop all netbios sub do_paranoid() { # block all netbios leakage print "\n# ensure no netbios leaks\n", "$iptables -A OUTPUT -p tcp -o $EXTERNAL_INTERFACE --dport 137:139 -j DUMP\n", "$iptables -A OUTPUT -p udp -o $EXTERNAL_INTERFACE --dport 137:139 -j DUMP\n", "$iptables -A FORWARD -p tcp -o $EXTERNAL_INTERFACE --dport 137:139 -j DUMP\n", "$iptables -A FORWARD -p udp -o $EXTERNAL_INTERFACE --dport 137:139 -j DUMP\n"; }