#!/usr/bin/perl -w # ipfSkel.pl # a basic perl jobber to dump a skeleton # ipfilter firewall and define some open # ports. Coded by Roger Gregory # ipfSkel generates a general ipfilter 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, and in some cases does things # simply for a perceived sake of convenience (such as keeping state on # defined incoming connections) # ipfSkel has several run-time options to control # what sorts of rulesets to generate: # -l set default catch-all rule as a logging rule (default is no log on last match) # -m try to match ports with their protocols (for opened ports - thus, only open TCP for auth, etc) # -r increase what is considered "reserved" to the full IANA list # -n do not allow netbios traffic out (prevent stateful leaks of netbios info) # -e use the external ip instead of "any" for many rules (instead of "any to any") # -f set the tcp flag used for incoming connections (defaults to S/SA) # -i set internal interface and allow all connections internally # yser defined options @ICMP = qw (echo echorep timex); # define allowed incoming icmp # ensure options have been passed if ( $#ARGV < 0 ) { &usage(); exit; } # setup $log = ""; $portmatch = 0; $SERVICES="/etc/services"; # file we use to match protocols and ports %seen = (); $DATE=localtime; # get options use Getopt::Long; GetOptions( "log" => \$log_opt, # set logging for catch-all "help" => \$help, # print usage information "reserved" => \$reserved, # increase reserve drop list "e=f" => \$firewallip, # specify ip of firewall interface "nosmb" => \$block_samba, # stop netbios info from leaking out "flag=s" => \$flag, # define "general" TCP flag for stateful inspection "i=s" => \$internal, # define internal interface "match" => \$match_opt ); # match ports and protocols for incoming allows # usage ditty sub usage() { print < [ external interface ] < ports to open > Options: -l log all dropped connections -m match ports with their listed protocols in /etc/services -r increase reserved drops to all IANA defined addresses -e set and use the external ip of the firewall for most rules -i set internal interface and allow all traffic -f set default TCP flag used for allowed incoming connections -n no smb/netbios allowed outbound DUMP ; } # set logging option if ($log_opt) { $log = "log"; } # set matching option if ($match_opt) { $portmatch = 1; # default to no port matches } # set ip of firewall if given if ($firewallip) { $destination = $firewallip; # set firewall ip if given } else { $destination = "any"; } # set default TCP flag for state if ($flag) { $DEFAULT_FLAG = $flag; } else { $DEFAULT_FLAG = "S\/SA"; # default to S/SA TCP flag } # dump help if ($help) { &usage; # dump usage ditty if -h exit 1 } # Full reserved list from ipf-howto @RESERVED_IANA = qw (0.0.0.0/7 1.0.0.0/8 2.0.0.0/8 5.0.0.0/8 10.0.0.0/8 23.0.0.0/8 27.0.0.0/8 31.0.0.0/8 67.0.0.0/8 68.0.0.0/6 72.0.0.0/5 80.0.0.0/4 96.0.0.0/3 127.0.0.0/8 128.0.0.0/16 128.66.0.0/16 169.254.0.0/16 172.16.0.0/12 191.255.0.0/16 192.0.0.0/16 192.168.0.0/16 197.0.0.0/8 201.0.0.0/8 204.152.64.0/23 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); # ensure options have been passed if ( $#ARGV < 0 ) { &usage(); exit; } # check given external interface die "Error: I can't find that interface\n" unless (grep /$ARGV[0]/, `/sbin/ifconfig -a`); $EXTERNAL_INTERFACE=$ARGV[0]; # define add_port functions # eo's mad service munch sub add_port { if ($portmatch) { open(SVFILE, $SERVICES) or die "Error: $!\n"; $check_match=(); 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 "pass in quick on $EXTERNAL_INTERFACE proto tcp from any to $destination port = $port flags $DEFAULT_FLAG keep state keep frags\n"; $check_match++; } if ($service =~ /\budp\b/) { print "pass in quick on $EXTERNAL_INTERFACE proto udp from any to $destination port = $port keep state\n"; $check_match++; } } } } if (!$check_match) { print "pass in quick on $EXTERNAL_INTERFACE proto tcp from any to $destination port = $port flags $DEFAULT_FLAG keep state keep frags\n", "pass in quick on $EXTERNAL_INTERFACE proto udp from any to $destination port = $port keep state\n"; } } else { print "pass in quick on $EXTERNAL_INTERFACE proto tcp from any to $destination port = $port flags $DEFAULT_FLAG keep state keep frags\n", "pass in quick on $EXTERNAL_INTERFACE proto udp from any to $destination port = $port keep state\n"; } } # header print "# ipf.rules - created by ifpSkel.pl on $DATE\n", "# this is a -skeleton- ruleset-- adapt as needed.\n"; # keep state outgoing # only tcp/udp/icmp is passed if ($block_samba) { print "\n# block outgoing smb connections\n", "block out quick on $EXTERNAL_INTERFACE from any to any port = 137\n", "block out quick on $EXTERNAL_INTERFACE from any to any port = 138\n", "block out quick on $EXTERNAL_INTERFACE from any to any port = 139\n", "block out quick on $EXTERNAL_INTERFACE from any port = 137 to any\n", "block out quick on $EXTERNAL_INTERFACE from any port = 138 to any\n", "block out quick on $EXTERNAL_INTERFACE from any port = 139 to any\n"; } print "\n# keep state on outgoing connections\n", "pass out quick on $EXTERNAL_INTERFACE proto tcp from any to any keep state keep frags\n", "pass out quick on $EXTERNAL_INTERFACE proto udp from any to any keep state keep frags\n", "pass out quick on $EXTERNAL_INTERFACE proto icmp from any to any keep state keep frags\n", "block out quick on $EXTERNAL_INTERFACE all\n"; # local print "\n# loopback rules\n", "pass out quick on lo0\n", "pass in quick on lo0\n"; if ($reserved) { print "\n # drop all IANA listed reserved addresses\n"; foreach $IANA (@RESERVED_IANA) { print "block in quick on $EXTERNAL_INTERFACE from $IANA to any\n"; } } else { print "\n# drop basic reserved addresses\n"; foreach $MINIMAL (@RESERVED_MINIMAL) { print "block in quick on $EXTERNAL_INTERFACE from $MINIMAL to any\n"; } } # misc options from FAQ/mailing list # logging is enabled on these print "\n# block misc oddities\n", "block in log quick on $EXTERNAL_INTERFACE all with opt lsrr\n", "block in log quick on $EXTERNAL_INTERFACE all with opt ssrr\n", "block in log quick on $EXTERNAL_INTERFACE all with ipopts\n", "block in log quick on $EXTERNAL_INTERFACE proto icmp all with frag\n", "block in log quick on $EXTERNAL_INTERFACE proto tcp all with short\n", "block in log quick on $EXTERNAL_INTERFACE proto tcp from any to any flags FUP\n"; # ICMP stuff print "\n# allow certain inbound ICMP types\n"; foreach $icmp (@ICMP) { print "pass in quick on $EXTERNAL_INTERFACE proto icmp from any to $destination icmp-type $icmp\n"; } # Add opened ports unless invalid # or duplicate. print "\n# opened ports\n"; foreach $port (sort(@ARGV[1 .. $#ARGV])) { add_port($port) unless ($port =~ /\D/) || ($port > 65535) || ($port < 1) || ($seen{$port}++); } # dump catch-all # return reset for tcp connections, port unreachable for udp print "\n# drop everything else\n", "block return-rst in $log quick on $EXTERNAL_INTERFACE proto tcp from any to any\n", "block return-icmp-as-dest(port-unr) in $log quick on $EXTERNAL_INTERFACE proto udp from any to any\n", "block in $log quick on $EXTERNAL_INTERFACE all\n"; # opt for ensuring interal traffic is allowed on internal # interface if ($internal) { print "\n# allow in/out on internal interface\n", "pass out quick on $internal proto tcp from any to any\n", "pass out quick on $internal proto udp from any to any\n", "pass out quick on $internal proto icmp from any to any\n", "pass in quick on $internal proto tcp from any to any\n", "pass in quick on $internal proto udp from any to any\n", "pass in quick on $internal proto icmp from any to any\n"; }