#!/usr/bin/perl -w
#
# ipts (IPTables-Setup.pl) by joe
#
#   easy setup tools for iptables

# $Id: iptables-setup.pl,v 1.6 2006/08/16 12:07:04 sierrajuliett Exp $

###############################################################
# description
#
#  subroutines : ask : ask user a question and get an answer
#                setup_simple_rules : setup for client
#                open_ports_for_server : additional setting for server
#                sub setup_masquarade : additional setting for router
#                setup_rules : caller of the 3 functions above
#
#  constants : $SCRIPT_FNAME : script file name
#              $CFG_FNAME : config file name
#              $DEFAULT_DIRECTORY : default directory
#              $DEFAULT_SCRIPT_FNAME : default shell file name
#              $DEFAULT_CFG_FNAME : default config file name
#              @COMMENT_0 : comment of shell script
#              @CURSE_MOD : commands for setting of modules
#              @CURSE_PROC : commands for setting of /proc
#              @CURSE_COMMAND : commands for initialization (flush chains)
#              @SIMPLE_SETTING : commands for setting of client
#              @CURSE_MASQUERADE : commands for setting of router
#              @HOST_TYPE : client, server or router?
#              $CMD_STR : "/sbin/iptables -A"
#              @TCP_PORT : tcp ports to open for server use
#              @UDP_PORT : udp ports to open for server use
###############################################################

use Getopt::Long;

$Getopt::Long::autoabbrev = 1;


###############################################################
# definitions
###############################################################

my $OPT_HELP = 0;
my $OPT_VERSION = 0;
my $OPT_RPM = 0;
my $OPT_DEBIAN = 0;
my $OPT_DIRECTORY = "";
my $OPT_SCRIPT = "";

my $DEFAULT_DIRECTORY = "/etc/sysconfig/";
my $DEFAULT_SCRIPT_FNAME = "myiptables-setup.sh";
my $DEFAULT_CFG_FNAME = "iptables";

my $SCRIPT_FNAME = "myiptables-setup.sh";
my $CFG_FNAME = "iptables";

my @COMMENT_0 = ("#!/bin/sh",
                 "#",
                 "# myiptables-setup.sh",
                 "#",
                 "# created by ipts (iptables-setup.pl)",
                 "",
                 "IPTABLES=\"/sbin/iptables\"");

my @CURSE_MOD = ("#",
                 "# setup modules",
                 "#",
                 "/sbin/depmod -a",
                 "/sbin/modprobe ip_tables",
                 "/sbin/modprobe ip_conntrack",
                 "/sbin/modprobe iptable_filter",
                 "/sbin/modprobe iptable_mangle",
                 "/sbin/modprobe iptable_nat",
                 "/sbin/modprobe ipt_LOG",
                 "/sbin/modprobe ipt_limit",
                 "/sbin/modprobe ipt_MASQUERADE",
                 "/sbin/modprobe ipt_state",
                 "/sbin/modprobe ipt_owner",
                 "/sbin/modprobe ipt_REJECT",
                 "/sbin/modprobe ip_conntrack_ftp");

my @CURSE_PROC = ("#",
                  "# proc setup",
                  "#",
                  "echo \"0\" > /proc/sys/net/ipv4/ip_forward",
                  "",
#                  "# syn cockie を有効にする",
                  "# syn flood measures",
#                  "#sysctl -w net.ipv4.tcp_syncookies=1",
                  "echo \"1\" > /proc/sys/net/ipv4/tcp_syncookies",
                  "",
#                  "# PING ブロードキャストに答えない",
                  "# ping broadcast ignore",
#                  "#sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1",
                  "echo \"1\" > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts");

my @CURSE_COMMAND = ("#",
                     "# flush iptables",
                     "#",
                     "\$IPTABLES -F",
                     "\$IPTABLES -X",
                     "",
                     "#",
                     "# basic rules (drop all packets)",
                     "#",
                     "\$IPTABLES -P INPUT DROP",
                     "\$IPTABLES -P FORWARD DROP",
                     "\$IPTABLES -P OUTPUT DROP",
                     "",
                     "#",
                     "# make user defined chains",
                     "#",
                     "\$IPTABLES -N bad_tcp_packets",
                     "\$IPTABLES -N allowed",
                     "\$IPTABLES -N icmp_packets",
                     "\$IPTABLES -N tcp_packets",
                     "\$IPTABLES -N udpincoming_packets");

my @SIMPLE_SETTING = (
  "# allow all packets from localloopback",
  "\$IPTABLES -A allowed -i lo -j ACCEPT",
  "",
  "\$IPTABLES -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j DROP",
  "\$IPTABLES -A allowed -p TCP --syn -j ACCEPT",
  "\$IPTABLES -A allowed -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT",
  "\$IPTABLES -A allowed -p TCP -j DROP",
  "",
  "# udp settings",
  "",
  "\$IPTABLES -A udpincoming_packets -p UDP --dport 53 -j ACCEPT",
  "\$IPTABLES -A udpincoming_packets -p UDP --sport 53 -j ACCEPT",
  "",
  "\$IPTABLES -A icmp_packets -p ICMP --icmp-type 0 -j ACCEPT",
  "\$IPTABLES -A icmp_packets -p ICMP --icmp-type 3 -j ACCEPT",
  "\$IPTABLES -A icmp_packets -p ICMP --icmp-type 8 -j ACCEPT",
  "\$IPTABLES -A icmp_packets -p ICMP --icmp-type 11 -j ACCEPT",
  "",
  "",
  "# INPUT chain",
  "",
  "\$IPTABLES -A INPUT -p tcp -j bad_tcp_packets",
  "",
  "\$IPTABLES -A INPUT -p ALL -i \$IFACE_GW -m state --state ESTABLISHED,RELATED -j ACCEPT",
  "\$IPTABLES -A INPUT -p TCP -i \$IFACE_GW -j tcp_packets",
  "\$IPTABLES -A INPUT -p UDP -i \$IFACE_GW -j udpincoming_packets",
  "\$IPTABLES -A INPUT -p ICMP -i \$IFACE_GW -j icmp_packets",
  "",
  "",
  "# OUTPUT chain",
  "",
  "\$IPTABLES -A OUTPUT -p tcp -j bad_tcp_packets",
  "\$IPTABLES -A OUTPUT -p ALL -j ACCEPT");

my @CURSE_MASQUERADE = (
  "#",
  "# setting of masquerading",
  "#",
  "# this should be in POSTROUTING chain",
  "",
  "\$IPTABLES -t nat -A POSTROUTING -o \$IFACE_GW -d 0.0.0.0/0.0.0.0 -j MASQUERADE");


#my @STR_IFACE = ("IFACE_0", "IFACE_1", "IFACE_2");
my $STR_IFACE_LO = "IFACE_LO";

my @HOST_TYPE = ("JUNK", "client", "server", "router");

my $IFACE_LO = "lo";
#my @question = ();

my $CMD_STR = "\$IPTABLES -A";
my @TCP_PORT = ("80", "20", "21", "25", "53", "110", "143",
                "443", "2049");
my @UDP_PORT = ("2049");

###############################################################
# global variable
###############################################################
my $OptHelp = 0;
my $OptDirectory = "";
my $OptScriptonly = 0;
#my $OptVersion = 0;
#my $OptRpm = 0;
#my $OptDebian = 0;

###############################################################
# sub routines
###############################################################

###############################################################
# sub routine get_opt
#
#    get options
###############################################################

sub get_option
{
#    my @optl = ("help", "rpm", "debian", "directory=s", "script=s");
#    my $USAGE = "Usage : $0 [-help][-rpm][-debian][-directory][-script <fname>]\n";

    my @optl = ("help", "directory=s", "scriptonly");

    my $USAGE = "Usage : $0 [-help][-version][-directory <dir>][-scriptonly]\n";

    die $USAGE unless GetOptions @optl;

#    if ($opt_rpm && $opt_debian) {
#        print "debian != rpm \n";
#    }
#
#    if ($opt_rpm) {
#        $OptRpm = 1;
#        print "opt = rpm\n";
#    }
#        
#    if ($opt_debian) {
#        $OptDebian = 1;
#        print "opt = debian\n";
#    }
#
#    if ($opt_version) {
#        $OptVersion = $opt_version;
#        print "version\n";
#    }

    if ($opt_help) {
        $OptHelp = $opt_help;
        print $USAGE;
        print "\n";
        print "if -scriptonly option is present, just create a script file,\n";
        print "and exit. (not to excute the script for testing)\n";
        exit();
    }

    if ($opt_scriptonly) {
        $OptScriptonly = $opt_scriptonly;
#        print "OptScriptonly : $OptScriptonly\n";
    }

    if ($opt_directory) {
        $OptDirectory = $opt_directory;
    }
}
    


###############################################################
# sub routine ask
#
#    ask user a question and return answer to caller
#    argument : @question
###############################################################

sub ask
{
    my (@arg) = @_;

    my $i = 0;
    for ($i = 0; $i <= $#arg; $i++) {
        print("$arg[$i]");
    }

    $line = readline(*STDIN);
    chomp($line);
    my $answer = $line;
    printf("\n%s", $line);

    for (;;) {
        print (" : are you sure? [y/n/q] ");

        $line = readline(*STDIN);
        chomp($line);

        if ($line eq "y") {
            return $answer;
        } elsif ($line eq "n") {
            $answer = "";
            return $answer;
        } elsif ($line eq "q") {
            exit(1);
        } else {
            print ("answer y or n");
        }
    }
}

###############################################################
# sub routine setup_simple_rules
#
#    setup rules for client
#           and write it down to a file /etc/sysconfig/iptables
#    argument : hosttype ("client", "server", "router")
#               iface_name ("eth0", "eth1"..., "ppp0");
#               shell_fname
###############################################################

sub setup_simple_rules
{
    my ($iface_gw, $script_fname) = @_;

    open($script_fname, ">$script_fname")
        || die "could not open file $script_fname : $!\n";

    my $i;

    for ($i = 0; $i <= $#COMMENT_0; $i++) {
        print $script_fname "$COMMENT_0[$i]\n";
    }

    print $script_fname "\n";
    print $script_fname "IFACE_GW=\"$iface_gw\"\n";
    print $script_fname "\n";

    for ($i = 0; $i <= $#CURSE_PROC; $i++) {
        print $script_fname "$CURSE_PROC[$i]\n";
    }

    print $script_fname "\n";

    for ($i = 0; $i <= $#CURSE_MOD; $i++) {
        print $script_fname "$CURSE_MOD[$i]\n";
    }

    print $script_fname "\n";

    for ($i = 0; $i <= $#CURSE_COMMAND; $i++) {
        print $script_fname "$CURSE_COMMAND[$i]\n";
    }

    print $script_fname "\n";
    print $script_fname "\n";

    for ($i = 0; $i <= $#SIMPLE_SETTING; $i++) {
        print $script_fname "$SIMPLE_SETTING[$i]\n";
    }

    close($script_fname);
}

###############################################################
# sub routine open_ports_for_server
#
#    set iptables and write it down to a file /etc/sysconfig/iptables
#    argument : shell_fname
#               (iface_name ("eth0", "eth1"..., "ppp0"))
#               (hosttype ("client", "server", "router"))
###############################################################

sub open_ports_for_server
{
    my ($script_fname) = @_;

    open($script_fname, ">>$script_fname")
        || die "could not open file $script_fname : $!\n";

    print $script_fname "\n";
    print $script_fname "\#\n";
    print $script_fname "\# settings for server\n";
    print $script_fname "\#\n";
    print $script_fname "\n";

    my $i;

    for ($i = 0; $i <= $#TCP_PORT; $i++) {
        print $script_fname
            "$CMD_STR tcp_packets -p TCP --dport $TCP_PORT[$i] -j allowed\n";
    }

    print $script_fname "\n";

    for ($i = 0; $i <= $#UDP_PORT; $i++) {
        print $script_fname
            "$CMD_STR udpincoming_packets -p UDP --dport $UDP_PORT[$i] -j ACCEPT\n";
    }

    close($script_fname);
}

###############################################################
# sub routine setup_masquarade
#
#    set iptables and write it down to a file /etc/sysconfig/iptables
#    argument : (iface_name ("eth0", "eth1"..., "ppp0"))
#               shell_fname
###############################################################

sub setup_masquarade
{
#
# commented code is for prerouting
#
#  @question = (
#        "\n",
#        "--[Q2]---------------------------------------------\n",
#        "\n",
#        "enter interface name for lan\n",
#        "\n",
#        "   e.g. eth0, ppp0, etc\n",
#        "\n");
#
#  for ($res = ""; $res eq ""; ) {
#    $res = ask(@question);
#  }
#
#  my $iface_lan_0 = $res;
#
#  @question = (
#        "\n",
#        "--[Q3]---------------------------------------------\n",
#        "\n",
#        "enter subnetwork of lan\n",
#        "\n",
#        "   e.g. xxx.xxx.xxx.xxx/24\n",
#        "\n");
#
#  for ($res = ""; $res eq ""; ) {
#    $res = ask(@question);
#  }
#
#  my $subnet_lan_0 = $res;

    my ($script_fname) = @_;

    open($script_fname, ">>$script_fname")
        || die "could not open file $script_fname : $!\n";

    print $script_fname "\n";

    my $i;

    for ($i = 0; $i <= $#CURSE_MASQUERADE; $i++) {
        print $script_fname "$CURSE_MASQUERADE[$i]\n";
    }

    close($script_fname);
}

###############################################################
# sub routine setup_rules
#
#    set iptables and write it down to a file /etc/sysconfig/iptables
#    argument : hosttype ("client", "server", "router")
#               iface_name ("eth0", "eth1"..., "ppp0")
#               shell_fname
#               cfg_fname
#               flg_scriptonly
###############################################################

sub setup_rules
{
    my ($host_type, $iface_gw, $script_fname, $cfg_fname, $flg_scriptonly) = @_;

    if ($host_type eq $HOST_TYPE[1]) { # standalone client
        setup_simple_rules($iface_gw, $script_fname);
    } elsif ($host_type eq $HOST_TYPE[2]) { # server
        print "\n";
        print "------------------------------------------------------\n";
        print "i don't ask what kind of server you're going to setup\n";
        print "\n";
        print "this script just opens tcp80, tcp20, tcp21, tcp25, tcp53, tcp110, tcp143, tcp443, tcp2049, udp2049.\n";
        print "------------------------------------------------------\n";
        #    print "if needed, modify $SCRIPT_FNAME and issue,\n";
        #    print "\# $SCRIPT_FNAME\n";
        #    print "\# iptables-save \> $CFG_FNAME\n";
        print "\n";

        setup_simple_rules($iface_gw, $script_fname);
        open_ports_for_server($script_fname);
    } elsif ($host_type eq $HOST_TYPE[3]) { # router
        #    print "\n";
        #    print "ok.  you're going to setup masquarading.\n";
        #    print "\n";
        #    print "you should give your correct interface name and subnetwork.\n";
        print "\n";

        setup_simple_rules($iface_gw, $script_fname);
        setup_masquarade($script_fname);
    }

    my $mode = oct(750);
    chmod $mode, $script_fname;

    if ($flg_scriptonly) {
        print "\nfinished.\n\n";
        print "make sure that following files created\n\n";
        print "$script_fname\n";
        exit(0);
    }

    print "executing iptables...\n";

    if (system("sh $script_fname") ne 0) {
        die "script execution failed";
    }

    print "saving iptables rules to $cfg_fname\n";
    if (system("/sbin/iptables-save > $cfg_fname")) {
        die "$cfg_fname not created";
    }

    $mode = oct(640);
    chmod $mode, $cfg_fname;

    print "\nfinished.\n\n";
    print "make sure that following files created\n\n";
    print "$script_fname\n";
    print "$cfg_fname\n";
}


###############################################################
# main routine
###############################################################

my $dir = $DEFAULT_DIRECTORY;
my $script_fname = $DEFAULT_DIRECTORY . $DEFAULT_SCRIPT_FNAME;
my $cfg_fname = $DEFAULT_DIRECTORY . $DEFAULT_CFG_FNAME;

# get options

get_option();

$uid = `id`;

if ( $uid =~ /^uid=0/) {
    $uid = 0
}


if ($OptScriptonly eq 0 && $uid ne 0) {
    print "you're not a root.  issue as a root.\n";
    exit(1);
}

if ($OptDirectory ne "") {
    $script_fname = $OptDirectory . "/" . $DEFAULT_SCRIPT_FNAME;
    $cfg_fname = $OptDirectory . "/" .  $DEFAULT_CFG_FNAME;

    $_ = $script_fname;
    s/\/\//\//;
    $script_fname = $_;

    $_ = $cfg_fname;
    s/\/\//\//;
    $cfg_fname = $_;
}
    
# opening

print "\n";
print "----------------------------------------------------\n";
print "this script is going to setup iptables rules.\n";
print "----------------------------------------------------\n";
print "\n";
print "when you're ready, hit enter. or press C-c\n";

my $line = readline(*STDIN);

# confirm if /etc/sysconfig/iptables exists
if (-e $cfg_fname) {
    print "$cfg_fname exists.\n";
    print "make a backup!\n";
    exit;
}

if (-e $script_fname) {
    print "$script_fname exists.\n";
    print "make a backup!\n";
    exit;
}

# ask standalone client or server or router

my @question = (
                "\n",
                "--[Q1]---------------------------------------------\n",
                "\n",
                "type of your host\n",
                "\n",
                "1 : standalone client host\n",
                "2 : server\n",
                "3 : router\n",
                "\n",
                "pleas anser 1 or 2 or 3\n",
                "\n");

my $res = "";
my $host_type = "";
my $iface_gw = "";

for ($res = ""; $res eq ""; ) {
    $res = ask(@question);
}

$host_type = $res;

# ask interface of gw
@question = (
             "\n",
             "--[Q2]---------------------------------------------\n",
             "\n",
             "enter interface name for gateway\n",
             "\n",
             "   e.g. eth0, ppp0, etc\n",
             "\n");

for ($res = ""; $res eq ""; ) {
    $res = ask(@question);
}

$iface_gw = $res;

if ($host_type eq "1" || $host_type eq "2" || $host_type eq "3") {
    setup_rules($HOST_TYPE[$host_type], $iface_gw, $script_fname, $cfg_fname,
                $OptScriptonly);
} else {
    print "\n";
    print "answer should be 1, 2 or 3\n";
}

### end of file ###
