Paranoid Penguin - Application Proxying with Zorp, Part II
Listing 1. TPROXY Rules
iptables -t tproxy -P PREROUTING ACCEPT iptables -t tproxy -A PREROUTING -i eth1 -j PRblue iptables -t tproxy -A PREROUTING -i eth2 -j PRpurple iptables -t tproxy -A PREROUTING -i eth0 -j PRred iptables -t tproxy -P OUTPUT ACCEPT iptables -t tproxy -N PRblue iptables -t tproxy -A PRblue -p tcp --dport 80 \ -j TPROXY --on-port 50080 iptables -t tproxy -A PRblue -p tcp --dport 22 \ ! -d firewall.example.net -j TPROXY --on-port 50022 iptables -t tproxy -N PRpurple iptables -t tproxy -N PRred iptables -t tproxy -A PRred -p tcp --dport 80 \ -j TPROXY --on-port 50080
Several things are worth pointing out in Listing 1. First, notice that the tproxy table contains its own PREROUTING and OUTPUT output chains. In Zorp, we use the tproxy/PREROUTING chain to route packets to the proper custom proxy chain (PRblue), based on the interface each packet enters. As with any custom iptables chain, if a packet passes through one of these without matching a rule, it's sent back to the line immediately following the rule that sent the packet to the custom chain. This is why custom chains don't have default targets.
In the PRblue chain, we've got two rules, one for each type of transaction allowed to originate from the internal network. All outbound HTTP material is proxied, that is, handed to a proxy process listening on port 50080. But in the SSH rule, we tell Netfilter to proxy all outbound SSH traffic unless it's destined for the firewall itself. Although Figure 1 doesn't show such a data flow (Blue→SSH→firewall), we need it in order to administer the firewall. This flow also requires a rule in the regular filter table's INPUT chain. In this example scenario, our DMZed Web server isn't permitted to initiate any connections itself, so we've created a PRpurple chain without actually populating it.
Now we move on to the regular filter table, this is the Netfilter table most of us are used to dealing with—it's the default when you omit the -t option with iptables. Listing 2 shows our example firewall's filter table's INPUT rules.
Listing 2. Filter Table INPUT Chain
iptables -P INPUT DROP iptables -A INPUT -j noise iptables -A INPUT -j spoof iptables -A INPUT -m tproxy -j ACCEPT iptables -A INPUT -m state \ --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -i eth1 -j LOblue iptables -A INPUT -i eth0 -j LOred iptables -A INPUT -i eth2 -j LOpurple iptables -A INPUT -j LOG --log-prefix "INPUT DROP: " iptables -A INPUT -j DROP
The first few lines check packets against some custom chains that check for spoofed IP addresses; if they pass those checks, they continue down the INPUT chain. Packets generated by the TPROXY module itself are accepted, as are packets belonging to established allowed transactions and loopback packets (lines 4–6, respectively). Next, as with the tproxy table's PREROUTING chain, we route packets to custom chains based on ingress interface. This time, the custom chains are for packets with local destinations, as opposed to proxied ones, so I've named them LOblue and so forth. Next come our filter table's custom chains (Listing 3).
Listing 3. Custom Chains in the Filter Table
iptables -N LOblue iptables -A LOblue -p tcp --dport 22 --syn -j ACCEPT iptables -A LOblue -p udp --dport 53 -j ACCEPT iptables -A LOblue -p tcp --dport 25 --syn -j ACCEPT iptables -A LOblue -j LOG --log-prefix "LOblue DROP: " iptables -A LOblue -j DROP iptables -N LOpurple iptables -A LOpurple -p udp --dport 53 -j ACCEPT iptables -A LOpurple -j LOG \ --log-prefix "LOpurple DROP: " iptables -A LOpurple -j DROP iptables -N LOred iptables -A LOred -p udp -s upstream.dns.server \ -sport 53 -j ACCEPT iptables -A LOred -p tcp --dport 25 --syn -j ACCEPT iptables -A LOred -j LOG --log-prefix "LOred DROP: " iptables -A LOred -j DROP iptables -N noise iptables -A noise -p udp --dport 137:139 -j DROP iptables -A noise -j RETURN iptables -N spoof iptables -A spoof -i lo -j RETURN iptables -A spoof ! -i lo -s 127.0.0.0/8 -j spoofdrop iptables -A spoof -i eth1 ! -s 10.0.1.0/24 \ -j spoofdrop iptables -A spoof ! -i eth1 -s 10.0.1.0/24 \ -j spoofdrop iptables -A spoof -i eth2 ! -s 192.168.1.0/24 \ -j spoofdrop iptables -A spoof ! -i eth2 -s 192.168.1.0/24 \ -j spoofdrop iptables -A spoof -j RETURN iptables -N spoofdrop iptables -A spoofdrop -j LOG \ --log-prefix "Spoofed packet: " iptables -A spoofdrop -j DROP
The first three of these custom chains are the most important: LOblue, LOpurple and LOred tell Netfilter how to process packets destined for the firewall itself, based on in which interface the packets arrive. In LOblue, we're accepting DNS queries, SSH connections and SMTP connections. In LOpurple, we're accepting only DNS queries. And in LOred, we're accepting DNS replies from our ISP's DNS server (upstream.dns.server) and SMTP connections. The last three of these custom chains are the simplest: noise filters NETBIOS packets, those notorious clutterers of Linux firewall logs; spoof filters for packets with obviously spoofed, that is, impossible, source IP addresses; and spoofdrop logs and drops packets caught by the spoof chain.
Listing 4 shows the remainder of our example iptables script, an essentially empty FORWARD chain with a default DROP policy and an empty OUTPUT chain with a default ACCEPT chain. Again, this is a proxying firewall, so it won't forward anything. You may be uneasy with the default ACCEPT policy for firewall-originated packets, but this is both necessary and safe on a Zorp firewall.