[lug] iptables

Rob Nagler nagler at bivio.biz
Wed Mar 27 18:00:53 MST 2002


> iptables -t nat -A PREROUTING -p tcp --dport 6000:7000 -j DROP

I wouldn't use the nat table.  You probably want to stick to the
INPUT, OUTPUT, and FORWARD tables for filtering.  This protects the
box itself, which I don't believe nat does.

You probably want something like:

iptables -A INPUT -i eth0 -p tcp -m state --state NEW \
   --destination-port 6000:7000 -j DROP

I like to log everything (almost :), so you might want something:

iptables -A INPUT -i eth0 -p tcp -m state --state NEW \
   --destination-port 6000:7000 -j LOG --log-prefix 'INPUT invalid: '

If you want to block forwarding X, just replace INPUT with FORWARD.

There's more to iptables than blocking X11.  In general, you want to
block everything coming in unless you specifically allow it.

I recommend the following page:

http://www.cs.princeton.edu/~jns/security/iptables/

I attached the Perl program we use to configure iptables.  I also
modified rc.d/init.d/iptables to not save counters.  This allows us to
diff more easily.  The script tries to recover if the new config is
bogus.  The script looks for the custom configuration in
/etc/sysconfig/iptables-local.PL.  It doesn't need to be there, but it
if it is, it allows the local host to provide more services,
e.g. samba.  You can see what it does by typing:
perl -w iptables.PL -n

Rob
----------------------------------------------------------------
#!/usr/bin/perl -w
# $Id: iptables.PL,v 1.2 2002/03/23 04:47:06 nagler Exp $
use strict;

=head1 NAME

iptables.PL - initialize iptables using configuration

=head1 SYNOPSIS

  perl -w iptables.PL [-verbose] [-noexecute]

=head1 DESCRIPTION

C<iptables.PL> initializes iptables.  You run this once
and then use C<iptables-save>.

I got some of this from: http://www.cs.princeton.edu/~jns/security/iptables/

We should have an error trap on this.  You should not debug this script
when you don't have access to computer locally.

=head1 CONFIGURATION

Configuration is a hash in F</etc/sysconfig/iptables-local.PL> (need not be
present) with the following boolean attributes:

=over 4

=item http

Allow incoming http and https from anywhere.

=item local_pop

Allow incoming pop3 and pop3s  on local net only.

=item pop

Allow incoming pop3 and pop3s from anywhere.

=item samba

Allow incoming samba on local net only.

=item http

Allow incoming smtp from anywhere.

=back

=back

=cut

#=IMPORTS

#=VARIABLES
my($_VERBOSE) = grep($_ =~ /^-(v|verbose)$/, @ARGV);
my($_NO_EXECUTE) = grep($_ =~ /^-(n|noexecute)$/, @ARGV) && ($_VERBOSE = 1);
my($_CFG) = do('/etc/sysconfig/iptables-local.PL') || {};
my($_LOCAL_NET) = _local_net();
_main();

#=PRIVATE METHODS

# _basic_filter(string chain, string dir, array accept_interfaces)
#
# accept_interfaces are not filtered.  On the rest of the interfaces,
# allow established,related and drop invalid.
#
sub _basic_filter {
    my($chain, $dir, @accept_interfaces) = @_;
    foreach my $ifc (@accept_interfaces) {
    	_do("-A $chain $dir $ifc -j ACCEPT");
    }

    # Basic protection
    _do("-A $chain -m state --state ESTABLISHED,RELATED -j ACCEPT");
    _stop_and_log($chain, '-m state --state INVALID', 'DROP', 'invalid');
    return;
}

# _cfg_forward()
#
# Forward filter is completely closed.
#
sub _cfg_forward {
    _basic_filter('FORWARD', '-i', 'lo');
    _stop_and_log('FORWARD', '', '', 'default');
    return;
}

# _cfg_input()
#
# INPUT filter
#
sub _cfg_input {
    # lo is a safe interface.  We accept anything from it.
    _basic_filter('INPUT', '-i', 'lo');
    _cfg_input_tcp();
    _cfg_input_udp();
    _cfg_input_eth0('-p icmp -j ACCEPT');
    _stop_and_log('INPUT', '', '', 'default');
    return;
}

# _cfg_input_eth0(string args)
#
# shorthand for input -i eth0 commands.
#
sub _cfg_input_eth0 {
    my($args) = @_;
    _do("-A INPUT -i eth0 $args");
    return;
}

# _cfg_input_tcp()
#
# Input TCP ports
#
sub _cfg_input_tcp {
    my($ports) = '';
    $ports .= ',http,https' if $_CFG->{http};
    $ports .= ',smtp' if $_CFG->{smtp};
    $ports .= ',pop3,pop3s' if $_CFG->{pop};
    _cfg_input_eth0('-p tcp -m state --state NEW -m multiport'
	. ' --destination-port ssh'.$ports
	. ' -j ACCEPT');
    _cfg_input_eth0('-p tcp -m state --state NEW --destination-port 8000:8999'
        . ' -j ACCEPT');
    $ports = '';
    $ports .= 'netbios-ns,netbios-dgm,netbios-ssn,' if $_CFG->{samba};
    $ports .= 'pop3,pop3s,' if $_CFG->{local_pop};
    chop($ports);
    _cfg_input_eth0('-p tcp -m state --state NEW -m multiport'
        . " -s $_LOCAL_NET -d $_LOCAL_NET --destination-port"
        . ' -j ACCEPT')
	if $ports;

    # Reject ident probes with a tcp reset.  Somef servers won't do well if you
    # don't do this.
    _cfg_input_eth0('-p tcp --dport auth -j REJECT --reject-with tcp-reset');

    # LOG port scans
    _stop_and_log('INPUT',
	'-p tcp --tcp-flags SYN,ACK,RST ACK -m state --state NEW',
	'REJECT --reject-with tcp-reset',
	'ACK scan');

    # We'll see this with trace route
    _stop_and_log('INPUT',
	'-p tcp ! --syn -m state --state NEW',
	'DROP',
	'scan');
    _stop_and_log('INPUT',
	'-p tcp --tcp-option 64',
	'DROP',
	'tcpopt 64');
    _stop_and_log('INPUT',
	'-p tcp --tcp-option 128',
	'DROP',
	'tcpopt 128');
    return;
}

# _cfg_input_udp()
#
# INPUT UDP configuration
#
sub _cfg_input_udp {
    _cfg_input_eth0('-p udp --source-port domain -m state'
	. ' --state ESTABLISHED -j ACCEPT');
    _cfg_input_eth0('-p udp --source-port ntp -m state'
	. ' --state ESTABLISHED -j ACCEPT');
    _cfg_input_eth0('-p udp  -m multiport --port bootpc,bootps -j ACCEPT');
    _cfg_input_eth0('-p udp -m multiport'
	. " -s $_LOCAL_NET -d $_LOCAL_NET --destination-port"
	. ' netbios-ns,netbios-dgm,netbios-ssn'
	. ' -j ACCEPT')
	if $_CFG->{samba};
    # Ignore route packets
    _do('-A INPUT -p udp --dport route -j DROP');
    return;
}

# _cfg_output()
#
# Output filter is wide open for now.
#
sub _cfg_output {
    _basic_filter('OUTPUT', '-o', 'lo', 'eth0');
    _stop_and_log('OUTPUT', '', '', 'default');
    return;
}

# _do(string cmd)
#
# Dies if system($cmd) doesn't work.  Inserts iptables in prefix if $cmd
# begins with '-'.   Observes noexecute and verbose flags.
#
sub _do {
    my($cmd) = @_;
    $cmd = 'iptables '.$cmd if $cmd =~ /^-/;
    print($cmd, "\n") if $_VERBOSE;
    return if $_NO_EXECUTE;
    system($cmd) == 0 || die("$cmd FAILED");
    return;
}

# _init_modules()
#
# Loade modules needed by iptables
#
sub _init_modules {
    foreach my $mod (qw(
	ip_tables
	iptable_nat
	ip_conntrack
	ip_conntrack_ftp
	ip_conntrack_irc
	iptable_filter
	iptable_mangle
	ipt_LOG
	ipt_REJECT
	ipt_state)) {
        _do("modprobe $mod");
    }
    return;
}

# _init_tables()
#
# initializes the policies.
#
sub _init_tables {
    #
    # POLICY (before flush): DROP
    #
    _do('-P INPUT DROP');
    _do('-P OUTPUT DROP');
    _do('-P FORWARD DROP');

    #
    # FLUSH
    #
    _do('-F');
    # _do('-F -t nat');
    # _do('-F -t mangle');
    _do('-X');
    _do('-Z');

    #
    # POLICY (after flush): DROP
    #
    _do('-P INPUT DROP');
    _do('-P OUTPUT DROP');
    _do('-P FORWARD DROP');
    return;
}

# _local_net() : string
#
# Determine the local network and mask
#
sub _local_net {
    foreach my $line (split(/\n/, `netstat -r`)) {
	return "$1/$2\n"
	    if $line =~ /^((?:\d+\.){3}\d+)\s*\S*\s*(255.255.255.\d+)/;
    }
    die('unable to determine local network');
    # DOES NOT RETURN
}

# _main()
#
# Initializes then executes configurations
#
sub _main {
    my(@ARGV) = @_;
    unless (_yes(10, 'Reconfigure iptables?')) {
	print("\nNOT CONFIGURING\n");
	return;
    }
    my($save) = '/etc/sysconfig/iptables';
    _do('/etc/rc.d/init.d/iptables save') unless -f $save;
    my($old) = "$save.rpmsave";
    _do("cp -pf $save $old");
    eval {
	_init_modules();
	_init_tables();
	_cfg_input();
	_cfg_output();
	_cfg_forward();
	# minimize counter differences in diff
	_do('-Z');
	_do('/etc/rc.d/init.d/iptables save');
	print(grep(s/^([<>] )\[\d+:\d+] /$1/, `diff $old $save`));
	# Give more time to review changes
	die("not OK\n") unless _yes(30, 'Reconfiguration OK?')
    };
    return unless $@;
    print(STDERR $@, "RESTORING OLD CONFIGURATION\n");
    # If the copy fails, we have to blow up
    _do("cp -pf $old $save");
    _do('/etc/rc.d/init.d/iptables restart');
    return;
}

# _stop_and_log(string chain, string rule, string jump string log_prefix) : 
#
# Log the rule on chain with prefix and rewrite rule if jump is not
# empty.
#
sub _stop_and_log {
    my($chain, $rule, $jump, $log_prefix) = @_;
    _do("-A $chain $rule -m limit --limit 2/m --limit-burst 1"
	. " -j LOG --log-prefix '$chain $log_prefix: '");
    _do("-A $chain $rule -j $jump") if $jump;
    return;
}

# _yes(int timeout, string prompt) : boolean
#
# Returns true if ok to proceed.  Times out if no answer in 15 seconds.
#
sub _yes {
    my($timeout, $prompt) = @_;
    print($prompt, "  You have $timeout seconds to enter y[es]: ");
    my($answer);
    eval {
	local($SIG{ALRM}) = sub { die("alarm\n"); };
	alarm($timeout);
	$answer = <STDIN>;
	alarm(0);
    };
    return 0 unless $answer;
    chomp($answer);
    $answer =~ s/^\s+|\s+$//g;
    return $answer =~ /^(y|yes)$/ ? 1 : 0;
}

=head1 VERSION

$Id: iptables.PL,v 1.2 2002/03/23 04:47:06 nagler Exp $

=cut

=comment

#
# SNAT
#
# Make everything going out eth0 look like it is going out correct IP addr
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 204.188.99.210

#
# DNAT
#
# Make incoming port 80 map to bivio.com (testing)
# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 \
#	-j DNAT --to 10.1.1.128

#
# INPUT (destined for this box)
#


stop_and_log INPUT '' '' default

#
# OUTPUT (locally generated packets)
#
# Allow anything from this box out to eth0 and lo.  Our servers are friendly.


## Outgoing UDP
#iptables -A OUTPUT -o eth0 -p udp -m multiport \
#    --destination-port domain,ntp -j ACCEPT
## traceroute uses lots of different ports
#iptables -A OUTPUT -o eth0 -p udp \
#    --destination-port 33435:33500 -j ACCEPT
## Outgoing TCP 
#iptables -A OUTPUT -o eth0 -p tcp -m multiport \
#    --destination-port $TCP_SERVICES,ntp,smtp,auth -j ACCEPT
#
## Outgoing ICMP
#iptables -A OUTPUT -o eth0 -p icmp -j ACCEPT

# log unwanted attempts, not flooding the logfile

#
# FORWARD (passing through box)
#
#basic_filter FORWARD -i lo

# # Outgoing TCP
# iptables -A FORWARD -i eth1 -p tcp -m multiport \
#     --destination-port $TCP_SERVICES -j ACCEPT

# Outgoing TCP on invalid ports, we reject
# stop_and_log FORWARD \
#    '-i eth1' \
#    'REJECT --reject-with icmp-proto-unreachable' \
#    'internal proto'

# # Outgoing ICMP
# iptables -A FORWARD -i ! eth0 -p icmp -j ACCEPT

# log unwanted attempts, not flooding the logfile
# stop_and_log FORWARD '' '' default

=cut





More information about the LUG mailing list