We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you a podcast all about Citrix Workspace, moving to the cloud, and analytics & intelligence. Episode 2 coming soon!Listen Now

x

IPTABLES router with traffic accounting

Medium Priority
2,016 Views
Last Modified: 2013-11-16
Hi everyone, I'm trying to build up a complete, stable, and working as expected router/firewall for my network.  I've searched extensively and now I'm kinda stuck, as I think I understand how it works and I'm not sure if I do really, so here I am.  The actual code running at system statup is included below.  I'm running on the most recent debian release I installed not so long ago (linux-2.6.18-6-686) which includes iptables 1.3.6.  I didn't recompile the kernel or anything.  What I'm asking for is a simple check of my current scripts, and what must be changed, what should be changed, etc.  I want a simple firewall behavior, nothing complicated, as you can see below, only a couple port forwardings, that's it.  When this is done there is the accounting part, the goal is to know how much bandwidth which IPs used during a certain period of time (dumping values with iptables -L -v -n -x, this part of the accounting I can work on myself, I'm just asking for iptables related stuff), so the possibilities are, authorized MAC/IP combinations, traffic aimed at the firewall itself, and unknown traffic.  I want to know exactly of much data pass through eth0, at all times, I don't want to skip unknown/dropped traffic as it passed through my modem and I'm billed for it.  That's where I'm lost, when I forward ports with DNAT, do they still go through INPUT or they go through FORWARD as they should?  I couldn't find any information on this anywhere.

Thanks for the help!
modprobe ip_conntrack
modprobe ip_conntrack_ftp
modprobe ip_conntrack_irc
modprobe ip_nat_ftp
modprobe ip_nat_irc
 
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -t raw -F
iptables -X
iptables -t nat -X
iptables -t mangle -X
iptables -t raw -X
 
# Disable traffic while making rules
iptables -A INPUT -j DROP
iptables -A FORWARD -j DROP
iptables -A OUTPUT -j DROP
 
echo "1" > /proc/sys/net/ipv4/ip_forward
echo "1" > /proc/sys/net/ipv4/conf/all/rp_filter
echo "1" > /proc/sys/net/ipv4/ip_dynaddr
 
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 25 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.50
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.50
iptables -t nat -A PREROUTING -i eth0 -p 47 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.50
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 1723 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.50
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 4422 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.75
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8880:8889 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.75
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 9990:9999 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.75
 
iptables -A FORWARD -o eth0 -m mac --mac-source XX:XX:XX:XX:XX:XX -s 10.25.50.75 -j ACCEPT
iptables -A FORWARD -o eth0 -m mac --mac-source XX:XX:XX:XX:XX:XX -s 10.25.50.50 -j ACCEPT
iptables -A FORWARD -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.50 -p tcp --dport 25 -m state --state NEW -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.50 -p tcp --dport 80 -m state --state NEW -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.50 -p 47 -m state --state NEW -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.50 -p tcp --dport 1723 -m state --state NEW -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.75 -p tcp --dport 4422 -m state --state NEW -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.75 -p tcp --dport 8880:8889 -m state --state NEW -j ACCEPT
iptables -A FORWARD -i eth0 -d 10.25.50.75 -p udp --dport 9990:9999 -m state --state NEW -j ACCEPT
iptables -A FORWARD -j DROP
 
iptables -A INPUT -i eth0 -m state --state INVALID,NEW -j DROP
 
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
 
# Accounting
iptables -I FORWARD 2 -s 10.25.50.50 
iptables -I FORWARD 2 -d 10.25.50.50
iptables -I FORWARD 2 -s 10.25.50.75
iptables -I FORWARD 2 -d 10.25.50.75
iptables -I INPUT 2 -i eth0
iptables -I OUTPUT 2 -o eth0
 
# Enable traffic
iptables -D INPUT 1
iptables -D FORWARD 1
iptables -D OUTPUT 1

Open in new window

Comment
Watch Question

> .. when I forward ports with DNAT, do they still go through INPUT or they go through FORWARD as they should?
if you do DNAT in the PREROUTING chain (as you did), then the package is passed to the FORWARD chain (unless you defined you own IP, which is useless, somehow), INPUT and OUTPUT chain is not used for these packages

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts

Author

Commented:
Thank you very much for this information, that clears up that part of my problem.  Can you comment on the script itself, anything I should add, any options not activated in the system variables that would add security or protect against attacks?  I don't want any traffic getting to the firewall itself except responses to packets sent by it (dns queries, ISP's DHCP, etc)

Thanks!

And sorry for the amount of points, a related question of mine did not get any answers, I requested a deletion yesterday, I will increase points in this one to 500 when it is deleted, which takes 4 days apparently.
I'm just wondering why you delete some rules at the end

Author

Commented:
I delete the first rule of each chain, that I added at the top of the script to stop all traffic.

Here's the code I speak of at the top and at the end.
# Disable traffic while making rules
iptables -A INPUT -j DROP
iptables -A FORWARD -j DROP
iptables -A OUTPUT -j DROP
 
# Enable traffic
iptables -D INPUT 1
iptables -D FORWARD 1
iptables -D OUTPUT 1

Open in new window

hmm, the script then depends on the current configuration to work proper
To make it behave the same all times I'd change the DROP rules like:

iptables -I INPUT 1 -j DROP

depending on the usage of that script, for example as rc-script, I'd add a default policy for each chain at the beginning like:

iptables -P INPUT DROP

Author

Commented:
Thanks for the help I'll replace those lines with a policy oriented approach, can you provide insight on the rest of the scripts?  Would it be working as expected, secure, etc?
> ... secure, etc?
hmm secure is a wide definition ...
What I see so far:
* if you trust all you clients, then MASQUERADE is ok, otherwise you open the door from inside to outside
* also I can't speak about the other open/DNATted ports
* if port 80 means a web server, HTTP protocol, then your network is wide, wide open unless you take addition care there

Author

Commented:
80 is a web server, IIS 6.  Do you mean my forwarded port 80 is wide open on the destination internal host?  If so, well that's what I want, I mean the outside being able to reach my web server.  If you mean that the linux box is wide open can you give me some directions as to what is open and how?

Thanks!
I meant that most attack are against web servers today
a packetfilter won't detect much there
So my comment about port 80 might or might not be serious:)

Author

Commented:
Ok yeah I know that, I even closed my FTP and SQL ports because I don't really need them, they were steadily brute-forced.  I had no worries my passwords are strong but it was generating very unwanted traffic and clogging my old router sometimes.

Anyway, since nobody sees security risks in my script or things I could improve I guess I got it mostly right :)

Thanks for the help, I'll reward you 500 points in a few days for your first answer concerning DNAT going through INPUT or not as this was my big question.

Author

Commented:
Ok I updated the points, I'll post my complete solution for others to use when I get some spare time in some days.

Author

Commented:
So here are my scripts, the first is the firewall script, that you load at startup, and the second is the accounting data dumping (on a MSSQL server, I use isql for this) that you can run with cron, I run it every minute.  I didn't comment the code cause I'm lazy but I'm sure you can figure it out as I'm myself a beginner with iptables.  My lastest addition is the interface "lo" lines in the stop script cause at shutdown bind would hang for like 2 minutes.

Enjoy!
#! /bin/sh
 
case "$1" in
  start)
    echo "Starting firewall"
 
    iptables -P INPUT DROP
    iptables -P FORWARD DROP
    iptables -P OUTPUT DROP
 
    iptables -F
    iptables -t nat -F
    iptables -t mangle -F
    iptables -t raw -F
    iptables -X
    iptables -t nat -X
    iptables -t mangle -X
    iptables -t raw -X
 
    modprobe ip_conntrack
    modprobe ip_conntrack_ftp
    modprobe ip_conntrack_irc
    modprobe ip_nat_ftp
    modprobe ip_nat_irc
 
    echo "1" > /proc/sys/net/ipv4/conf/all/rp_filter
    echo "1" > /proc/sys/net/ipv4/ip_dynaddr
 
    iptables -N ACCOUNTING
    iptables -A ACCOUNTING -d 10.25.50.50 -j RETURN
    iptables -A ACCOUNTING -s 10.25.50.50 -j RETURN
    iptables -A ACCOUNTING -d 10.25.50.75 -j RETURN
    iptables -A ACCOUNTING -s 10.25.50.75 -j RETURN
    iptables -A ACCOUNTING -d 10.25.50.225 -j RETURN
    iptables -A ACCOUNTING -s 10.25.50.225 -j RETURN
    iptables -A ACCOUNTING -d 10.25.50.226 -j RETURN
    iptables -A ACCOUNTING -s 10.25.50.226 -j RETURN
    iptables -A ACCOUNTING -d 10.25.50.227 -j RETURN
    iptables -A ACCOUNTING -s 10.25.50.227 -j RETURN
    iptables -A ACCOUNTING -i eth0 -j RETURN
    iptables -A ACCOUNTING -o eth0 -j RETURN
 
    iptables -A FORWARD -j ACCOUNTING
    iptables -A OUTPUT -o eth0 -j ACCOUNTING
    iptables -A INPUT -i eth0 -j ACCOUNTING
 
    iptables -A OUTPUT -j ACCEPT
    iptables -A INPUT -i ! eth0 -j ACCEPT
    iptables -A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
 
    iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 25 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.50
    iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -m state --state NEW,RELATED,ESTABLISHED -j DNAT --to-destination 10.25.50.50
 
    iptables -A FORWARD -o eth0 -m mac --mac-source 00:11:22:33:44:55 -s 10.25.50.50 -j ACCEPT
    iptables -A FORWARD -o eth0 -m mac --mac-source 00:11:22:33:44:55 -s 10.25.50.75 -j ACCEPT
    iptables -A FORWARD -o eth0 -m mac --mac-source 00:11:22:33:44:55 -s 10.25.50.225 -j ACCEPT
    iptables -A FORWARD -o eth0 -m mac --mac-source 00:11:22:33:44:55 -s 10.25.50.226 -j ACCEPT
    iptables -A FORWARD -o eth0 -m mac --mac-source 00:11:22:33:44:55 -s 10.25.50.227 -j ACCEPT
    iptables -A FORWARD -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -A FORWARD -i eth0 -d 10.25.50.50 -p tcp --dport 25 -m state --state NEW -j ACCEPT
    iptables -A FORWARD -i eth0 -d 10.25.50.50 -p tcp --dport 80 -m state --state NEW -j ACCEPT
 
    iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
 
    echo "1" > /proc/sys/net/ipv4/ip_forward
 
    echo "Done"
    ;;
  stop)
    echo "Stopping firewall"
 
    echo "0" > /proc/sys/net/ipv4/ip_forward
 
    iptables -F
    iptables -t nat -F
    iptables -t mangle -F
    iptables -t raw -F
    iptables -X
    iptables -t nat -X
    iptables -t mangle -X
    iptables -t raw -X
 
    iptables -A INPUT -i lo -j ACCEPT
    iptables -A OUTPUT -o lo -j ACCEPT
 
    echo "Done"
    ;;
  reload|restart|force-reload)
    /etc/init.d/firewall stop
    /etc/init.d/firewall start
    ;;
  *)
    echo "Usage: /etc/init.d/firewall {start|stop|reload|restart|force-reload}"
    exit 1
    ;;
esac
 
exit 0
 
------------------------------------------------------------------------
 
#! /bin/sh
 
RAWDATA=`iptables -Z -x -v -n -L ACCOUNTING`
 
WHILEI=0
WHILERECORDI=0
 
SQLQUERIES=$(
  echo "$RAWDATA" |
  {
    while read WHILECURRENTLINE; do
      ((WHILEI++));
      if [ "$WHILEI" -gt 2 ]; then
        if [ "$WHILERECORDI" -eq 0 ]; then
          CURRENTDOWNBYTES=`echo "$WHILECURRENTLINE" | egrep -o "[0-9]{1,}" | head -n 2 | tail -n 1`;
          CURRENTIP=`echo "$WHILECURRENTLINE" | egrep -o "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}" | tail -n 1`;
          WHILERECORDI=1;
        else
          CURRENTUPBYTES=`echo "$WHILECURRENTLINE" | egrep -o "[0-9]{1,}" | head -n 2 | tail -n 1`;
          if [ "$CURRENTUPBYTES" -gt 0 ] || [ "$CURRENTDOWNBYTES" -gt 0 ]; then
            WHILESQLQUERIES=`echo "INSERT INTO tbl_data (ip, databytesup, databytesdown) VALUES ('$CURRENTIP', $CURRENTUPBYTES, $CURRENTDOWNBYTES)\r\n"$WHILESQLQUERIES`;
          fi;
          WHILERECORDI=0;
        fi;
      fi;
    done
    echo -e "$WHILESQLQUERIES"
  }
)
 
INSERTRETURN=`echo "$SQLQUERIES" | isql SERVER_CONNECTION username password -b | grep -c -m 1 "1"`
 
if [ "$INSERTRETURN" != "1" ]; then
  echo -e "`date +"%F %T"` SQL INSERT ERROR\r" > /dev/tty1;
fi
 
exit 0

Open in new window

Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.