2

Everybody, hello!

I'm trying to setup my server firewall using iptables (I have to admit that last time I used iptables was a year ago), but iptables acts contrary to what I ask.

Here is my test script :

#!/bin/sh
IPT="/sbin/iptables"

echo -n "Loading iptables rules..."

# Flush old rules
$IPT --flush
$IPT --delete-chain

# Allow incoming and outgoing for loopback interfaces
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT
# Allow incoming traffic for HTTP(S), SSH and SMTP
$IPT -A INPUT -p tcp --dport 80 -i eth0 -j ACCEPT
$IPT -A INPUT -p tcp --dport 443 -i eth0 -j ACCEPT
$IPT -A INPUT -p tcp --dport 22 -j ACCEPT
$IPT -A INPUT -p tcp --dport 25 -i eth0 -j ACCEPT
# Allow ICMP requests
$IPT -A INPUT -p icmp -i eth0 -j ACCEPT
$IPT -A OUTPUT -p icmp -o eth0 -j ACCEPT
# Allow outgoing traffic for SMTP, DNS, NTP, PgSQL, SolR, and SSH
$IPT -A OUTPUT -p tcp --dport 25 -o eth0 -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 53 -o eth0 -j ACCEPT
$IPT -A OUTPUT -p udp --dport 53 -o eth0 -j ACCEPT
$IPT -A OUTPUT -p udp --dport 123 -o eth0 -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 5433 -o eth0.2654 -j ACCEPT
$IPT -A OUTPUT -p udp --dport 5433 -o eth0.2654 -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 8983 -o eth0.2654 -j ACCEPT
$IPT -A OUTPUT -p udp --dport 8983 -o eth0.2654 -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 22 -o eth0 -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 22 -o eth0.2654 -j ACCEPT
# Deny web server user outgoing connections
$IPT -A OUTPUT -o eth0 -m owner --uid-owner www-data -j DROP

# Drop everything else
$IPT -A INPUT -j DROP
$IPT -A OUTPUT -j DROP
$IPT -A FORWARD -j DROP

echo "rules loaded."
# Print rules as understood, then flush to avoid lockout
sleep 10
$IPT -L
# Flush old rules
$IPT --flush
$IPT --delete-chain

With this script, the server no more aswers to any request but ping (ICMP), then, after 10 seconds, prints following text and exits :

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  100  5920 ACCEPT     all  --  lo     any     anywhere             anywhere            
    0     0 ACCEPT     tcp  --  eth0   any     anywhere             anywhere            tcp dpt:www 
    0     0 ACCEPT     tcp  --  eth0   any     anywhere             anywhere            tcp dpt:https 
    1    52 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:ssh 
    0     0 ACCEPT     tcp  --  eth0   any     anywhere             anywhere            tcp dpt:smtp 
    0     0 ACCEPT     icmp --  eth0   any     anywhere             anywhere            
    0     0 DROP       all  --  any    any     anywhere             anywhere            

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       all  --  any    any     anywhere             anywhere            

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  100  5920 ACCEPT     all  --  any    lo      anywhere             anywhere            
    0     0 ACCEPT     icmp --  any    eth0    anywhere             anywhere            
    0     0 ACCEPT     tcp  --  any    eth0    anywhere             anywhere            tcp dpt:smtp 
    0     0 ACCEPT     tcp  --  any    eth0    anywhere             anywhere            tcp dpt:domain 
    0     0 ACCEPT     udp  --  any    eth0    anywhere             anywhere            udp dpt:domain 
    0     0 ACCEPT     udp  --  any    eth0    anywhere             anywhere            udp dpt:ntp 
    0     0 ACCEPT     tcp  --  any    eth0.2654  anywhere             anywhere            tcp dpt:5433 
    0     0 ACCEPT     udp  --  any    eth0.2654  anywhere             anywhere            udp dpt:5433 
    0     0 ACCEPT     tcp  --  any    eth0.2654  anywhere             anywhere            tcp dpt:8983 
    0     0 ACCEPT     udp  --  any    eth0.2654  anywhere             anywhere            udp dpt:8983 
    0     0 ACCEPT     tcp  --  any    eth0    anywhere             anywhere            tcp dpt:ssh 
    0     0 ACCEPT     tcp  --  any    eth0.2654  anywhere             anywhere            tcp dpt:ssh 
    0     0 DROP       all  --  any    eth0    anywhere             anywhere            owner UID match www-data 
   14  2061 DROP       all  --  any    any     anywhere             anywhere            

First disturbing element, I noticed that INPUT and OUTPUT first rules are ACCEPT all packets whereas I did not asked to do so. In addition, I tried to set policy of INPUT and OUTPUT to DROP (using $IPT -P INPUT DROP and $IPT -P OUTPUT DROP), but doing this definitively locked me out, even after the ten seconds timeout, and the server then only answers to ICMP, which force me to hard reboot the server. Same effect if I set policy settings at the beginning of the script.

I suspect my error to be obvious for regular iptables users, but I searched the solution for hours now, and, as always in this kind of situations, the answer will only be obvious to me when somebody will point it out. Please, any do-gooder to help me?

eth0.2654 is a VLAN used for communications with our PgSQL server. Concerning HTTP answers, I was under the impression that they use the connection the client opened, which is allowed by my rule. Was I wrong?

4
  • When listing your rules, do iptables -L -v. Things like interface binding (your first two rules are attached to the loopback interface) aren't visible without the verbose flag. That is misleading you. Mar 2, 2012 at 14:26
  • 1
    You have no rule to allow outbound HTTP traffic. What good does it do to let requests in if responses can't get out? Mar 2, 2012 at 14:29
  • I would suggest allowing states RELATED,ESTABLISHED right in the beginning as well.
    – 3molo
    Mar 2, 2012 at 14:56
  • What would RELATED,ESTABLISHED change, apart of avoiding the wild shutdwon of my SSH connection?
    – Penegal
    Mar 2, 2012 at 15:10

2 Answers 2

3

Concerning HTTP answers, I was under the impression that they use the connection the client opened, which is allowed by my rule. Was I wrong?

Yes, you were wrong. It is most likely that you want to accept connection that are established or related to incoming connections. Thus, anything that is accepted on the input rule can be answered. With a stateless firewall, one would have to open the source port up explicitly. Since IPtables is stateful, you don't need to do that. It will track the state of connections for you and automatically allow outbound connections the way you're thinking, but only if you tell it to do so. The rule for this is iptables -A INPUT -p ALL -m state --state ESTABLISHED,RELATED -j ACCEPT. Put that at the top of your output list. If you want to be particularly aggressive, you could bind that rule to specific ports.

If you were to look at this in a stateless way, you'd have to remember where packets are coming from and going to. You have your server currently setup to allow you to SSH to another machine. Since you don't allow any traffic that has port 22 as the source, your server cannot answer incoming connections. Again, the "ESTABLISHED,RELATED" rule solves this for permitting traffic to reply to inbound connections as would allowing traffic sourced from port 22, but unlike opening 22 on a stateless firewall, this setup won't allow new connections to start from there.

2
  • Apparently, my first problem (no response with any protocol) was due to the default DROP policy; I changed this to ACCEPT, with authorized packets explicitly authorized and everything else dropped. Nevertheless, the state rule proved helpful, as HTTP answers effectively use this rule to go to the client. Thank you very much for your help, Jeff! I have a last problem : HTTP traffic is slowed down when my rules are active. Must I post the new data as a new post, an edit of the first post or a new topic?
    – Penegal
    Mar 5, 2012 at 10:42
  • @Penegal You could do either, but I think opening a new question would be most effective. Mar 5, 2012 at 14:57
0

Your approach is bad. You're allowing all input connections and then stopping them at output. This will not prevent DDoS attacks. The right way is to stop the input connections and allow the output.

Reading through your code I've tried to remake. Here is how I think it should look:

#!/bin/sh

IPT=/usr/sbin/iptables

echo "Clear firewall rules..."
$IPT -F
$IPT -Z
$IPT -t nat -F
$IPT -t nat -Z
$IPT -t mangle -F
$IPT -t mangle -Z
$IPT -X

echo "Setting firewall policy..."
$IPT -P INPUT   DROP    # Deny  all incoming connections
$IPT -P OUTPUT  ACCEPT  # Allow all outgoing connections
$IPT -P FORWARD DROP    # Deny  all forwaring

echo "Allow connections from: lo, eth0, eth0.2654"
$IPT -I INPUT -i lo   -j ACCEPT
$IPT -I INPUT -i eth0 -j ACCEPT

echo "Allow icmp requests from eth0"
$IPT -A INPUT -p icmp -i eth0 -j ACCEPT

echo "Allow traffic for HTTP(S), SSH, SMTP, DNS, NTP, PgSQL, SolR"
$IPT -A INPUT -p tcp --dport 22                 -j ACCEPT
$IPT -A INPUT -p tcp --dport 25   -i eth0       -j ACCEPT
$IPT -A INPUT -p tcp --dport 53   -i eth0       -j ACCEPT
$IPT -A INPUT -p udp --dport 53   -i eth0       -j ACCEPT
$IPT -A INPUT -p tcp --dport 80   -i eth0       -j ACCEPT
$IPT -A INPUT -p udp --dport 123  -i eth0       -j ACCEPT
$IPT -A INPUT -p tcp --dport 443  -i eth0       -j ACCEPT
$IPT -A INPUT -p tcp --dport 5433 -i eth0.2654  -j ACCEPT
$IPT -A INPUT -p udp --dport 5433 -i eth0.2654  -j ACCEPT
$IPT -A INPUT -p tcp --dport 8983 -i eth0.2654  -j ACCEPT
$IPT -A INPUT -p udp --dport 8983 -i eth0.2654  -j ACCEPT

echo "Deny web server user outgoing connections; eth0"
$IPT -A INPUT -o eth0 -m owner --uid-owner www-data -j DROP

echo "Drop everything."
$IPT -A INPUT -s 0/0 -j DROP

echo "Firewall loaded."
sleep 20
$IPT -nvL

echo "Clear firewall rules..."
$IPT -F
$IPT -Z
$IPT -t nat -F
$IPT -t nat -Z
$IPT -t mangle -F
$IPT -t mangle -Z
$IPT -X

I haven't tested so keep that in mind. I'm not sure if "-i eth0.2654" would work on virtual interfaces.

If you want - try it and let me know if it works or doesn't.

2
  • Besides the edits I made, there's an upside to limiting outbound connections other than the expected. The asker probably should open with a -m state --state ESTABLISHED,RELATED -j ACCEPT on the output and then allow the desired services. Also, first glance at you Deny web server user outgoing connections; eth0 rule really makes me scratch my head. Mar 2, 2012 at 15:59
  • Thank you for the flushing commands, but I had to modify your script because no external connections were allowed with a policy set to DROP; I was forced to set policy to ACCEPT, explicitly allow authorized traffic and drop everything else to be able to open connections to the serve; is that normal?
    – Penegal
    Mar 5, 2012 at 10:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .