##########################################################################################
## ##
## IPTables Firewall Script ##
## by Satis ##
## ##
##########################################################################################
## ##
## This firewall script is written for a Linux Installation and Administration class ##
## and is designed for an end-computer based on a home network. This end-computer ##
## is running Apache web server and SSH services. The script allows access to these ##
## services to machines running within the network, but denies external access. ##
## ##
## This is based off a firewall script provided at the following web page: ##
## http://iptables-tutorial.frozentux.net/chunkyhtml/index.html ##
## ##
## Additional resources: ##
## /sbin/iptables --help ##
## man iptables ##
## http://www.linuxquestions.org/questions/archive/9/2003/02/4/9801 ##
## ##
## The commented script follows below. ##
## ##
##########################################################################################
##
## Get your current IP address.
##
IP=`/sbin/ifconfig eth0 | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`
## This sets a variable (IP). It calls ifconfig and then greps the line with 'inet addr' in
## it. It then pipes it through an awk getting the second ouput ($2) which returns just the
## inet addr portion. Finally it's piped through sed, which removes everything but the
## actual IP address.
##
## Clear all existing rules and tables.
##
/sbin/iptables -F
## Flush all existing rules on all existing table.
/sbin/iptables -X
## Delete all existing user-tables.
##
## Set default policies
##
/sbin/iptables -P INPUT DROP
## This sets the default policy for the INPUT table to drop. Any packet which doesn't match
## a rule while passing through the INPUT table is dropped.
/sbin/iptables -P OUTPUT DROP
## This sets the default policy for the OUTPUT table to drop.
/sbin/iptables -P FORWARD DROP
## This sets the default policy for the FORWARD table to drop.
##
## Create user-defined tables. User-defined tables need to be created prior to references to
## that table.
##
/sbin/iptables -N bad_tcp_packets
## This creates a table called 'bad_tcp_packets'. This table is used to drop any tcp packets which
## are considered 'bad'. More specific information will be provided in the 'bad_tcp_packets'
## setup.
/sbin/iptables -N allowed
## This creates a table called 'allowed'. This will be final processing of packets that we're
## allowing. See that table's setup below for more specific information.
/sbin/iptables -N tcp_packets
## This creates a table called 'tcp_packets'. TCP packets are forwarded to this table for
## processing. See that table's setup below for more specific information.
/sbin/iptables -N udp_packets
## This creates a table called 'udp_packets'. UDP packets are forwarded to this table for
## processing. See that table's setup below for more specific information.
/sbin/iptables -N icmp_packets
## This creates a table called 'icmp_packets'. ICMP packest are forwarded to this table for
## processing. See that table's setup below for more specific information.
##
## Define the INPUT table. Here we define the rules for our INPUT table. Any packets coming
## into the machine go through this table.
##
/sbin/iptables -A INPUT -p TCP -j bad_tcp_packets
## This adds a rule to the INPUT table (-A), for all TCP packets (-p), that makes them jump (-j)
## to the 'bad_tcp_packets' table. If they make it through that table without being dropped or
## accepted, they then come back to the INPUT table for further processing.
/sbin/iptables -A INPUT -p TCP -i eth0 -s 192.168.1.50 --dport 80 -j DROP
## This adds a rule to the INPUT table for all TCP packets coming into interface eth0 (-i), with
## a source address of 192.168.1.50 (-s), destined for port 80 (--dport) and drop them.
## 192.168.1.50 is my home router's ip address. This will basically take any attempts to reach
## the web server from the router (or through the router from the general internet) and drop them.
/sbin/iptables -A INPUT -p ALL -i lo -s 127.0.0.1 -j ACCEPT
## This adds a rule to the INPUT table for all protocols (-p ALL) going to the local interface
## (-i lo) from a source address of 127.0.0.1 (-s) and accepts them. Basically, any traffic
## coming from the server, to the server, through the local loopback interface, is accepted.
## This will allow the server to talk to itself. This may be required for some services, as well
## as for the server to access its own web server.
/sbin/iptables -A INPUT -p ALL -i lo -s $IP -j ACCEPT
## This adds a rule to the INPUT table for all protocols, going into the local interface
## and coming from $IP, which is the server's actual IP address. All these packest are accepted.
## This is basically a continuation of the previous rule.
/sbin/iptables -A INPUT -p UDP -i eth0 --dport 67 --sport 68 -j DROP
## This adds a rule to the INPUT table, for all UDP packets going into interface eth0 with a
## destination port of 67 and a source port of 68, to drop the packet. These packets will be
## DHCP requests from the local network. Since this server is not a DHCP server (the router
## does that) these packets are simply dropped.
/sbin/iptables -A INPUT -p ALL -d $IP -m state --state ESTABLISHED,RELATED -j ACCEPT
## This adds a rule to the INPUT table, for all protocols with a destination IP of $IP (the
## server's ip address. The stateful packet engine is initialized (-m state) and the state is
## checked to see if it is ESTABLISHED or RELATED. ESTABLISHED connections are connections that
## are already talking. This way, any current, active connections (such as SSH) are only
## processed to this point before being accepted. RELATED connections are connections that are
## spawned by other, existing connections. For instance, FTP begins a connection on port 21,
## but then negotiates a data connection on some arbitrary, high port number. This would allow that
## to work properly.
/sbin/iptables -A INPUT -p TCP -i eth0 -j tcp_packets
## This adds a rule to the INPUT table for TCP packets coming into eth0 to jump to the tcp_packets
## table. Basically once the above rules are met, TCP packets (the most common packet to be
## expected on this server) are immediately sent to the tcp_packets table. The less rules that
## a packet has to traverse, the lower the CPU overhead and the packet latency.
/sbin/iptables -A INPUT -p UDP -i eth0 -j udp_packets
## This adds a rule to the INPUT table for UDP packets coming into eth0 to jump to the udp_packets
## table. UDP packets will be the second-most received packet type, so these are handled here.
/sbin/iptables -A INPUT -p ICMP -i eth0 -j icmp_packets
## This adds a rule to the INPUT table for ICMP packets coming into eth0 to go to the icmp_packets
## table. ICMP packets are the last protocol we're going to be concerned with. Any other
## protocols will be handled below.
/sbin/iptables -A INPUT -i eth0 -d 224.0.0.0/8 -j DROP
## This adds a rule to the INPUT table for packets going into eth0 with a destination IP address
## of 224.0.0.0/8 (-d) to be dropped. Apparently Client for Microsoft Networks generates alot of
## traffic like this, and this rule simply catches and drops these packets before they're logged.
## 224.0.0.0/8 means 224.0.0.0 - 224.0.0.255...the /8 signifies 8 bits for the host portion and
## is just an alternate way of writing a subnet mask. Alternately you could have written
## 224.0.0.0 255.255.255.0.
/sbin/iptables -A INPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level DEBUG --log-prefix "IPT INPUT packet died:"
## This adds a rule to the INPUT table, settinga limit (-m limit), the limit being 3/minute
## (--limit), the initial limit being 3 (--limit_burst), to LOG the packet (-j LOG) with a
## loggging-level of DEBUG (--log-level DEBUG) prefixing the log entry with
## 'IPT INPUT packet died:' (--log-prefix). Basically, we're taking any packet that made it
## this far and logging it. We're limiting it to 3 logs per minute to keep the logs from going
## insane. The logs are appended to the system log (syslogd) and are readable with the dmesg
## command.
##
## Define the OUPUT table. Here we're defining our OUTPUT table. All packets outbound from our
## server to the network have to traverse these rules before being sent.
##
/sbin/iptables -A OUTPUT -p TCP -j bad_tcp_packets
## This appends a rule to the OUTPUT table, for all TCP packets, to jump to the bad_tcp_packets
## table. Basically we're checking our outbound packets to make sure there isn't anything funny
## going on.
/sbin/iptables -A OUTPUT -p ALL -o eth0 -s $IP -j ACCEPT
## This appends a rule to the OUTPUT table, for all protocols going out of interface eth0 with a
## source address of $IP, to be accepted. Basically, if it's originating from our server, with
## our server's IP address, it's accepted.
/sbin/iptables -A OUTPUT -p ALL -o lo -s 127.0.0.1 -j ACCEPT
## This appends a rule to the OUTPUT table, for all protocols going out of the local interface,
## with a source ip of 127.0.0.1 (the loopback address), to be accepted. This is only a
## continuation of the previous rule.
/sbin/iptables -A OUTPUT -p ALL -o lo -s $IP -j ACCEPT
## More of the same. This allows packets from out local interface with our real IP to be sent
## out.
/sbin/iptables -A OUTPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level DEBUG --log-prefix "Illegal Output:"
## This appends to the output table, a limit of 3/minute, starting at 3, to log it with a
## DEBUG level of logging and to prefix the log entry with 'Illegal Output:'. Basically
## this is the same logging command we had on our INPUT table. This logs any packets
## that don't match our OUTPUT accept rules and logs it to our system log (syslogd) with a prefix
## of 'Illegal Output:". Since our Output rules are pretty broad, the only packets that made it
## this far would have a source ip address that weren't 127.0.0.1 or our $IP address. Basically,
## if you have any logs here either I forgot something or somehow the server is trying to spoof
## its IP.
##
## Define the bad_tcp_packets table. Here we're defining our bad_tcp_packets table, which
## basically pre-processes our TCP packets to make sure they're not doing anything wrong.
## A bad packet could denote a mistake or connection issue, but could also be something
## less innocent, such as a port-scan.
/sbin/iptables -A bad_tcp_packets -p tcp --tcp-flags SYN,ACK SYN,ACK -m state --state NEW -j REJECT --reject-with tcp-reset
## This appends to the bad_tcp_packets table, for all TCP packets, (--tcp-flags) with flags SYN
## and ACK set, with a state of NEW, to be REJECTed with a tcp-reset packet (--reject-with).
## First the reason: by standard, any new TCP connections must begin with a packet with just the
## SYN bit set. This is the first part of the 3-way handshake. This rule tests new, inbound
## connections to see if they have both SYN and ACK set, which would denote a non-new connection.
## If a packet does come in with the bits set wrong, we return a tcp-reset packet, which forces
## the host on the other side to reinitialize the 3-way handshake. The --tcp-flags portion is a
## mask/match pattern. Basically, the mask is SYN and ACK, while the match is SYN and ACK. Thus,
## both SYN and ACK must be on to match the rule. If we had simply used SYN,ACK SYN, then the SYN
## bit would have to be on and the ACK bit would have to be off. Similarly, we could have had
## SYN,ACK,FIN SYN,FIN and both SYN and FIN would have to be on, while ACK would have to be off.
## Read up on TCP packet structure for more information about packet flags.
/sbin/iptables -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "New not syn:"
## This appends to the bad_tcp_packets rule, for TCP packets, that do NOT have the syn bit set
## with the ACK and FIN bits cleared and are new, to log them with the "New not syn" prefix.
## Note the ! which inverses the following rule, which is --syn. Basically this logs any new
## packets which do not have SYN on with ACK and FIN cleared. SYN on with ACK and FIN cleared
## is the packet structure that initializes a three-way handshake. This just logs those.
## So, why do we have the previous rule? The reason is that the SYN/ACK set typically means a
## computer believes that it has an active connection, when it does not. This could happen for
## many legitimate reasons, and is thus not logged.
/sbin/iptables -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j DROP
## This appends to the bad_tcp_packets table, for TCP packets that do NOT have the syn bit set\
## with the ACK and FIN bits cleared and are new, to be dropped. This continues on from the
## previous rule, and just drops the packets after the previous rule logs them.
##
## Define allowed table. Here we're defining our allowed packets table. This is basically some
## final processing before we allow a packet to be accepted.
##
/sbin/iptables -A allowed -p TCP --syn -j ACCEPT
## This appends to the allowed table, for all TCP packets that have SYN set with ACK and FIN
## bits cleared, to be allowed. This allows new connections.
/sbin/iptables -A allowed -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT
## This appends to the allowed table, for all TCP packets in the ESTABLISHED,RELATED state, to
## be accepted. Basically, any existing connections that make it this far are accepted. We
## actually already have this rule in our INPUT table, so this is largely redundant, but the
## firewall script this was based on had it, so I decided to leave it as well.
/sbin/iptables -A allowed -p TCP -j DROP
## This appends to the allowed tale, for all TCP packets to be dropped. Basically anything that
## isn't a new connection or an established/related connections will be dropped.
##
## Define the tcp_packets table. Here we do all our TCP processing. A tcp packet will have made
## it through our bad_tcp_packets table, so now we're just defining what we're letting in and
## what we're killing.
##
/sbin/iptables -A tcp_packets -p TCP -s 0/0 --dport 80 -j allowed
## This appends to the tcp_packets table, for TCP packets with a source of anything (-s 0/0) and a
## destination port of 80, to jump to the 'allowed' table. Basically we're making all traffic
## that's going to our web server (port 80) jump to the allowed table for final processing. The
## source of 0/0 is again ip and subnet. This would be equivalent to 0.0.0.0 0.0.0.0.
/sbin/iptables -A tcp_packets -p TCP -s 0/0 --dport 22 -j allowed
## This appents to the tcp_packets table, for all TCP packet, with a source of anything and a
## destination port of 22 to jump to the allowed table. This is the same as above, but for port
## 22, which is our SSH port.
/sbin/iptables -A tcp_packets -p TCP -j DROP
## This appends to the tcp_packets table, for all TCP packets, to be dropped. This kills
## all packets not destined for ports 22 or 80.
##
## Define our UDP_packets table. Here we're defining our rules for any UDP packets.
##
/sbin/iptables -A udp_packets -p UDP -s 0/0 -j DROP
## Append to udp_packets table, for all UDP packets, from all sources, to be dropped. We're not
## allowing any UDP packets at this time. However, the table exists, so if I want to allow
## something at a later date I can.
##
## Define our ICMP_packets table. Here we define our rules for any ICMP packets.
##
/sbin/iptables -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j ACCEPT
## This appends to the icmp_packets table, for all ICMP packets, from anywhere and a type of 8
## (--icmp-type) to be accepted. Type 8 is echo reply, which is used by ping. This basically
## allows hosts to ping me.
/sbin/iptables -A icmp_packets -p ICMP -s 0/0 --icmp-type 11 -j ACCEPT
## This appends to the icmp_packets table, for all ICMP packets from all source IP addresses and a
## type of 11 to be accepted. This allows ICMP type 11, which is "time exceeded". If a
## host/gateway either has ttl hit 0 or is unable to assemble a fragmented packet, it sends this
## packet. This allows the packet to be received.
/sbin/iptables -A icmp_packets -p ICMP -j DROP
## This appends to the icmp_packets table,f or all ICMP packets, to be dropped. Basically all
## remaining ICMP packets are now dropped.
|