Using catalogs: /etc/sgml/sgml-docbook-4.1.cat Using stylesheet: /usr/share/sgml/docbook/dsssl-stylesheets/html/ldp.dsl Working on: /home/joeg/misc/linif/iptc/iptc.sgml
| Some source address spoofing |
| Useful information revealed in response to port scans |
| Malformed broadcast packets used to identify UNIX systems |
| Some forms of network mapping |
| Some denial-of-service attacks |
| Source-routed packets |
| Some forms of fragmentation bombs |
| Local mistakes that affect remote sites |
| Access to private LAN services |
| Additional protection against local server misconfigurations |
Two main options:
Deny everything by default and explicitly allow selected packets through.
Accept everything by default and explicitly deny selected packets.
Deny everything is best - it protects against unanticipated dangers.
Input - Filtering packets received by the interface
Forward - Filtering packets that pass through the interface
Output - Filtering packets that are sent by the interface
In general, packets that do not match our rules are dropped. Responses to invalid packets just increase network traffic and possibly give an attacker more information.
A single system can be secured quite simply using a simple rule set. With IP Tables installed on a system a few simple commands are all that is needed to lock down the system.
First we use the nmap command to scan the system. We use nmapfe which is a GUI front end to nmap. The result of the scan shows open ports.
The first iptables command we will use is the list command:
# iptables --list --verbose Chain INPUT (policy DROP 4989 packets, 215K bytes) pkts bytes target prot opt in out source destination Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 504 packets, 30837 bytes) pkts bytes target prot opt in out source destinationThis command lists the current rule set. We see the three built-in chains along with their default policy.
Each IP Tables command has two versions a shorthand version and a verbose version. We will use the verbose command in our examples and then show the shorthand version also. It may be a good idea for you to learn the verbose version first and then learn the shortcuts. The shorthand version of the list command is:
# iptables -L -vThe verbose versions all use a double dash "--" and the shorthand versions use a single dash "-".
If there are any rules listed we first need to clear them. To do that we use the flush command:
iptables --flush
The shorthand version of the flush command is:
iptables -F
Next we set the default policy for each of the chains:
iptables --policy INPUT DROP iptables --policy FORWARD DROP iptables --policy OUTPUT DROPThis sets our overall policy to drop every packet that we don't explicitly allow.
The shorthand version of the policy command is:
# iptables -P INPUT DROP
The system is very secure now. By re-running nmap we see that the machine does not even show up on the network now. The system can't access the outside world either, however.
# ping localhostThe machine can't even ping itself now.
Next we open up the loopback interface for all internal traffic.
iptables --append INPUT --in-interface lo --jump ACCEPT iptables --append OUTPUT --out-interface lo --jump ACCEPT
The shorthand version of these commands is:
iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT
We can see that "ping localhost" does work now.
The process of opening the loopback interface introduces us to the append chain command. The --in-interface|-i and --out-interface|-o are basic match operations. If no interface is specified, all interfaces are implied. The --jump|-j command accepts ACCEPT, DROP, or a user-defined chain.
To be able to access the outside world we need to allow connections that the machine initiates and those that are related to the machine initiated ones.
iptables --append OUTPUT --out-interface eth0 --jump ACCEPT iptables --append INPUT --match state --state ESTABLISHED,RELATED --jump ACCEPT
The shorthand version of these commands is:
iptables -A OUTPUT -o eth0 -j ACCEPT iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
The machine is now able to access the outside world.
New syntax in the above commands are the --match|-m command. The "-m state" tells iptables that we are going to use the match extensions module to check the state. The "--state ESTABLISHED,RELATED" part is a parameter that tells the states we want to accept. Legal values also include NEW and INVALID.
We will start by quickly reviewing the last session's demo. This will set up our demo machine for us to continue learning the syntax for the iptables program.
We now continue the demo from the last session to keep building our knowledge of iptables syntax.
We will now open up our demo machine to allow ssh access from other machines. This will introduce a little new syntax and allow us to show the remaining demo from one machine.
This single command should do it since we already have related and established connections accepted and connection tracking going.
iptables -A INPUT -i eth0 \ --protocol tcp --source-port 1024:65535 --destination-port 22 \ -m state --state NEW -j ACCEPTThe abbreviated version is:
iptables -A INPUT -i eth0 \ -p tcp --sport 1024:65535 --dport 22 \ -m state --state NEW -j ACCEPT
The new syntax in this command is the --protocol|-p match operation. Here we are specifying the TCP protocol. The --source-port|-sport is a parameter that matches source ports of the specified value 1024-65535. The --destination-port|--dport is a parameter that matches the destination ports specified which in this case is 22, the ssh port.
One should also note that lines that are too long are continued using a "\".
The list command with the verbose option lists the number of packets and bytes associated with each rule or policy. These counters can be cleared by:
iptables --zeroThe abbreviated version is:
iptables -ZThe zero function can be combined with the list function:
iptables -Z -L -vThis lists first then clears the counters.
The list command has an option to show the line number for each rule:
iptables --list --line-numbersThere is no abbreviated version that I know of. Line numbers are handy when deleting a specific rule.
The list command has an option to list ports and IP addresses numerically, rather than by name:
iptables --list --numericThe abbreviated version is:
iptables -L -n
If you are doing much listing in iptables an alias is nice:
alias 'il=iptables -L -v --line-numbers'This is what I like (and will use from now on).
Listing the rules we can see that the earlier rule we put in to allow ssh shows a destination of "anywhere". We can tighten this up by specifying a destination as shown in the following command:
iptables -A INPUT -i eth0 \ -p tcp --sport 1024:65535 --destination 192.168.10.2 --dport 22 \ -m state --state NEW -j ACCEPTThe abbreviated version is:
iptables -A INPUT -i eth0 \ -p tcp --sport 1024:65535 -d 192.168.10.2 --dport 22 \ -m state --state NEW -j ACCEPTNow we see our list shows two commands:
Chain INPUT (policy DROP 11 packets, 2611 bytes) num pkts bytes target prot opt in out source destination 1 26 1692 ACCEPT all -- lo any anywhere anywhere 2 1072 258K ACCEPT all -- any any anywhere anywhere state RELATED,ESTABLISHED 3 1 60 ACCEPT tcp -- eth0 any anywhere anywhere tcp spts:1024:65535 dpt:ssh state NEW 4 0 0 ACCEPT tcp -- eth0 any anywhere insideguy tcp spts:1024:65535 dpt:ssh state NEWNumber 3 and 4 are actually the old and new versions of the same command. We really only want the latest one so we will use the delete command as follows:
iptables --delete INPUT 3The abbreviated version is:
iptables -D INPUT 3The chain is specified and the line number of the command to delete. Okay now the earlier command is gone. What we should have done is used the replace command:
[root@insideguy root]# iptables -R INPUT 3 -i eth0 -p tcp --sport 1024:65535 -d insideguy --dport 22 -m state --state NEW -j ACCEPT [root@insideguy root]# [root@insideguy root]# il Chain INPUT (policy DROP 18 packets, 4260 bytes) num pkts bytes target prot opt in out source destination 1 26 1692 ACCEPT all -- lo any anywhere anywhere 2 1415 284K ACCEPT all -- any any anywhere anywhere state RELATED,ESTABLISHED 3 0 0 ACCEPT tcp -- eth0 any anywhere insideguy tcp spts:1024:65535 dpt:ssh state NEWThe replace commands verbose version is --replace as expected.
The chain and line number must be specified as with the delete command.
Also note that it is allowable to use names like "insideguy" instead of the ip address. Of course the name must be defined at the time it is used.
Specifying sources is as one would expect "--source|-s".
In addition to the INPUT, FORWARD, and OUTPUT chains, users may define their own chains. This has a real advantage for documentation. To add a chain the following command is used:
iptables --new-chain OUTPUTDROPThe abbreviated version is:
iptables -N OUTPUTDROPA listing will show the new chain with no references. Since I really wanted to call the chain OUTACCEPT we will delete it using:
iptables --delete-chain OUTPUTDROPThe abbreviated version is:
iptables -X OUTPUTDROPWe then recreate it using:
iptables -N OUTACCEPTNow we will put two entries in the chain corresponding to the entries we currently have in the OUTPUT chain:
iptables -A OUTACCEPT -o lo -j ACCEPT iptables -A OUTACCEPT -o eth0 -j ACCEPTA listing now shows:
Chain OUTPUT (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 34 2252 ACCEPT all -- any lo anywhere anywhere 2 1651 299K ACCEPT all -- any eth0 anywhere anywhere Chain OUTACCEPT (0 references) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT all -- any lo anywhere anywhere 2 0 0 ACCEPT all -- any eth0 anywhere anywhereThe new chain is still unreferenced so we add the rule:
iptables -I OUTPUT 1 -j OUTACCEPTThis command show the use of the insert command whose verbose version is --insert and requires the chain to be specified and optionally a line number. The default line number is 1 if it is not specified.
This command jumps to OUTACCEPT for anything coming to the OUTPUT chain. Now the output chain rules 2 and 3 are redundant so we delete them.
iptables -D OUTPUT 2 iptables -D OUTPUT 2Note that rule 3 becomes rule 2 when 2 is deleted so we specify 2 in each case. One must be careful to list after each delete unless the rules furthest down in the list are deleted first. Now the listing looks as follows:
Chain INPUT (policy DROP 41 packets, 9711 bytes) num pkts bytes target prot opt in out source destination 1 48 3232 ACCEPT all -- lo any anywhere anywhere 2 2835 757K ACCEPT all -- any any anywhere anywhere state RELATED,ESTABLISHED 3 0 0 ACCEPT tcp -- eth0 any anywhere insideguy tcp spts:1024:65535 dpt:ssh state NEW Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 421 71264 OUTACCEPT all -- any any anywhere anywhere Chain OUTACCEPT (1 references) num pkts bytes target prot opt in out source destination 1 14 980 ACCEPT all -- any lo anywhere anywhere 2 407 70284 ACCEPT all -- any eth0 anywhere anywhere
We can still access the outside world now, so our new chain is working.
A destination for a rule we haven't talked about is the LOG destination. It is used as follows:
iptables -I INPUT 1 -p icmp -j LOGLog entries are recorded in /var/log/messages so we open another window and tail the log:
tail -n 50 -f /var/log/messagesNow from the other machine we ping and we can see the result in the log.
Additionally this command shows how to match the icmp protocol (udp would be done in a similar way.
One bit of syntax we haven't looked at is the NOT operator:
iptables -R INPUT 1 -p ! tcp -j LOGThis rule specifies that any protocol but tcp should be matched. When we ping we should again see the packets in the log.
iptables -R INPUT 1 -p icmp --icmp-type echo-request -j LOG --log-prefix "--->>>"We can match specific types of icmp packet such as an echo-request. The LOG destination also has parameters like the log prefix above.
By typing "iptables -h" all the iptables command and options are listed. There is a special case command "iptables -p icmp -h" that prints all the icmp protocol types that can be matched.
This section is to present to the reader the FORWARD chain of the iptables filter table by example and is not comprehensive as a proper firewall setup should be.
Packet forwarding in Linux is provided at the kernel level. To forward packets you need to indicate to the kernel by placing a "1" (no quotes) in /proc/sys/net/ipv4/ip_forward like this:
echo 1 > /proc/sys/net/ipv4/ip_forward
Packet forward filtering is controlled in iptables primarily on the FORWARD chain on the filter table. We can filter packets on the FORWARD chain just as we have on the INPUT and OUTPUT chains. The main difference to note is that while we are limited to filtering on the input interface on the INPUT chain and the output interface on the OUTPUT chain, we can filter on both input and output interfaces on the FORWARD chain.
A sample network on which forwarding might be performed is pictured below.
|-----------| |-------------| client .2 | eth0 eth1 | |-----------| |--------| | Internet ----------| router |-------------| 192.168.1.0/24 |--------| | 10.0.0.1 .1 | |-----------| |-------------| client .3 | |-----------|10.0.0.1 represents our public interface while the 192.168.1.0/24 network is our internal network. Assume all IPs in this diagram are public in nature.
Following our INPUT ruleset samples we start by setting the default policy to DROP.
iptables -P FORWARD DROP
If all we wish to accomplish is to allow the client machines Internet access, the ruleset might look like the following.
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
This ruleset allows traffic from the internal network to flow out and traffic from the Internet to flow in only if it related to a connection already initiated by an internal machine. Any traffic not matching one of those rules is DROPPED to the bit bucket.
If we want to host a web server on the 192.168.1.3 machine we can add an additional rule.
iptables -A FORWARD -i eth0 -o eth1 -p tcp -d 192.168.1.3 --port 80 -m state --state NEW -j ACCEPT
This rule now allows incoming connection to be initiated to the web server on port 80 using the tcp protocol. If we wanted to additionally run a mail server on the 192.168.1.2 machine we could add this rule.
iptables -A FORWARD -i eth0 -o eth1 -p tcp -d 192.168.1.2 --port 25 -m state --state NEW -j ACCEPT
Note the use of the state limiter. We simply want to add the acceptance of inbound connections with these rules. Doing this allows us to still block what iptables sees as "invalid" packets.
We could further more reduce spoofing risks by inserting two rules that say packets appearing to be from our internal subnet can not enter on the router's eth0 interface and no packets not from our subnet enter via the eth1 interface.
iptables -I FORWARD -i eth0 -s 192.168.1.0/24 -j DROP iptables -I FORWARD -i eth1 -s ! 192.168.1.0/24 -j DROP
The Linux kernel also has anti-spoofing measures built in that can be activated as desired (defense-in-depth). See additional files in /proc/sys/net/ipv4.
As a general rule, it is wise to monitor packets that hit the policy as they do not match any of the rules. This allows you to better monitor unusual and unexpected traffic and gives you a sense of whether or not you are covering all the bases.
iptables -A FORWARD -m limit --limit 1/second -j LOG
In an alternate scenario where we wish to create a DMZ (Zone in which we will host services), we might have a setup that looks as follows.
|----------------| |-------------| private server | | |----------------| | | | |-----------| |-------------| client .2 | eth0 eth1 | |-----------| 10.0.0.1|--------| .1 | Internet ----------| router |-------------| 192.168.1.0/24 |--------| | |eth2 | |-----------| | .1 |-------------| client .3 | | |-----------| |-----------| | public | 192.168.2.0/24 | server .2 | |-----------|
10.0.0.1 represents our public interface while the 192.168.1.0/24 network is our internal network. 192.168.2.0/24 represents the DMZ network. Assume all IPs in this diagram are public in nature.
The basics ideas change little. We just need to allow for two three networks where the Internet is not trusted, our DMZ is not trusted by the internal network and the internal network is generally trusted. The main idea is to move our hosting server(s) or most vulnerable to a direct attack to a separate network. Divide to resist being conquered.
iptables -P FORWARD DROP iptables -A FORWARD -i ! eth0 -o eth0 -j ACCEPT iptables -A FORWARD -i eth1 -o eth2 -j ACCEPT iptables -A FORWARD -i eth0 -o ! eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i eth2 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -p tcp -d 192.168.1.2 --port 25,80 -m state --state NEW -j ACCEPT iptables -I FORWARD -i eth0 -s 192.168.1.0/24 -j DROP iptables -I FORWARD -i eth0 -s 192.168.2.0/24 -j DROP iptables -I FORWARD -i eth1 -s ! 192.168.1.0/24 -j DROP iptables -I FORWARD -i eth2 -s ! 192.168.2.0/24 -j DROP
The largest difficulty to overcome when you move to three or more networks is dealing with all the potential cross traffic patterns. As you can see above, it adds a few rules beyond simply moving the server(s). There are limited security gains from this type of configuration but are beyond the scope of this demonstration.
In this section we will cover NAT (Network Address Translation). NAT is currently defined in RFC 3022 and was created as a means to alleviate the shortage of public Internet addresses until IPv6 was implemented. It has sense become a permanent fixture as the time frame for IPv6 implementation stretches forward indefinitely. NAT rules reside on the PREROUTING and POSTROUTING chains of the NAT table.
There are several forms of NAT. SNAT (the more common implementation) allows for the source address of a packet to be modified and is on the POSTROUTING chain while DNAT (Destination NAT) deals with the destination address and is on the PREROUTING chain.
It is worth noting at this point that NAT simply alters the packets. The actual job of filtering belongs to the FORWARD chain on the filter table.
Most Linux users who have networks at home are familiar with Masquerading and/or SNAT (Source NAT). SNAT provides the ability to use a series of private addresses and allow them public network access. This is by no means the only use for SNAT but by far is the typical usage.
Here is our sample network:
|-----------| |-------------| client .2 | eth0 eth1 | |-----------| |--------| | Internet ----------| router |-------------| 192.168.1.0/24 |--------| | 204.228.204.28 .1 | |-----------| |-------------| client .3 | |-----------|
We wish to provide Internet access to our privately addressed clients using our single public *STATIC* IP.
iptables -P FORWARD DROP iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -I FORWARD -i eth0 -s 192.168.1.0/24 -j DROP iptables -I FORWARD -i eth1 -s ! 192.168.1.0/24 -j DROP iptables -A FORWARD -m limit --limit 1/second -j LOG iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.28
As previously mentioned, we have to allow for forwarding on the FORWARD chain.
Note that for large networks, we may wish to use several IPs to SNAT a large number of private IPs.
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.28 - 204.228.204.29
iptables will attempt to match the outgoing port on the --to-source IP to the original outgoing port on the client machine if possible. If we so desired we could limit the ports that outgoing connections are mapped to like so.
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.28:20000-30000
It is worth noting that since most of the notation usable on the filter table chains is valid on the nat table too, we can do a one to one IP mapping if we so desired.
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.2 -j SNAT --to-source 204.228.204.28 iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.29
Here, we give our 192.168.1.2 its own public IP and place everyone else on the second public IP. This works both where we have multiple public routes and where IP aliasing takes place on a public interface.
If we did not have a static IP such as those who dial in to an ISP or the where the ISP uses DHCP over DSL/wireless we would need to use the special case masquerade SNAT.
|-----------| |-------------| client .2 | ppp0 eth0 | |-----------| |--------| | Internet ----------| router |-------------| 192.168.1.0/24 |--------| | IP? .1 | |-----------| |-------------| client .3 | |-----------|
iptables -P FORWARD DROP iptables -A FORWARD -i eth0 -o ppp0 -j ACCEPT iptables -A FORWARD -i ppp0 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -I FORWARD -i ppp0 -s 192.168.1.0/24 -j DROP iptables -I FORWARD -i eth1 -s ! 192.168.1.0/24 -j DROP iptables -A FORWARD -m limit --limit 1/second -j LOG iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
While you do not specify an IP (how can you if you do not know what it will be?) you can still use the port notation to limit the outgoing ports on ppp0 on which masquerading will take place.
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE --to-ports 20000-30000
If you know your external address, SNAT provides better response times since the masquerade code must lookup the external IP on each packet.
DNAT, on the other hand, provides us a way to let the public IP world into our private IP space in a limited fashion. We can host services on a private IP host and have the firewall redirect incoming packets to the private host.
Given our network.
|-----------| |-------------| client .2 | eth0 eth1 | |-----------| |--------| | Internet ----------| router |-------------| 192.168.1.0/24 |--------| | 204.228.204.28 .1 | |-----------| |-------------| client .3 | |-----------|
Should we want to host a webserver on the 192.168.1.2 client, we would write the following ruleset.
iptables -P FORWARD DROP iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT iptables -A FORWARD -i eth0 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -I FORWARD -i eth0 -s 192.168.1.0/24 -j DROP iptables -I FORWARD -i eth1 -s ! 192.168.1.0/24 -j DROP iptables -A FORWARD -m limit --limit 1/second -j LOG iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.28 iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.1.2:80
Now we can get out on the private IP clients an we can run our public webserver on the 192.168.1.2 private host.
DNAT also has a special case where we can redirect locally. We can grab all traffic coming in on port 80 and redirect it to 3389 (squid). Using this we can transparently proxy and/or filter web traffic through squid with no client configuration required.
Stock DNAT and its special case REDIRECT can also be used on the nat table OUTPUT chain to redirect the LOCAL machine's outbound traffic.
iptables allows for very complex and lengthy rulesets. Improper rule structure can lead to inefficiency in your packet filtering system which can in turn decrease effective bandwidth and serving capabilities. It is important that you carefully consider the order and structure of your packet filter layout.
The ultimate goal of packet filtering is to control and limit traffic to only that which you desire to accept, send, and forward. The secondary goal is to get each packet out of iptables as soon as possible by placing the packet on an ACCEPT or DROP target. While secondary, inefficiencies in your iptables structure can render the packet filtering capabilities useless as effectively throttling your bandwidth can run off your potential users and/or customers.
To the end of goal two, there are several techniques that can be applied. iptables allows you to create your own chains and add them as targets from rules on other (including the default set of) chains. This allows us to create a shallow, wide decision tree instead of a deep and narrow. While keeping in mind that if a packet gets incorrectly filtered, the whole system is useless, the shallower a decision tree, the faster the packets will be filtered.
Example 1a. (FORWARD chain):
Packet start
eth0 (External) -
|
+ Is the packet from an outside IP?
/ \
Yes No - DROP
|
|
ACCEPT -YES- + Is the packet tcp and to port 25 and a new connection?
NO
|
ACCEPT -YES- + Is the packet tcp and to port 53 and a new connection?
NO
|
ACCEPT -YES- + Is the packet udp and to port 53 and a new connection?
NO
|
ACCEPT -YES- + Is the packet tcp and to port 80 and a new connection?
NO
|
ACCEPT -YES- + Is the packet tcp and to port 110 and a new connection?
NO
|
ACCEPT -YES- + Is the packet tcp and to port 143 and a new connection?
NO
|
ACCEPT -YES- + Is the packet tcp and to port 443 and a new connection?
NO
|
ACCEPT -YES- + Is the packet icmp and of the type ICMP_ECHOREPLY?
NO
|
ACCEPT -YES- + Is the packet icmp and of the type ICMP_UNREACH?
NO
|
ACCEPT -YES- + Is the packet icmp and of the type ICMP_TIMXCEED?
NO
|
ACCEPT -YES- + Is the packet icmp and of the type ICMP_SOURCEQUENCH?
NO
|
ACCEPT -YES- + Is the part of an ESTABLISHED or RELATED connection?
NO
|
+ LOG packets here.
|
Policy - DROP
Example 1b. (FORWARD chain):
Packet start
eth0 (External) -
|
+ Is the packet from an outside IP?
/ \
Yes No - DROP
|
ACCEPT -YES- + Is it part of an ESTABLISHED or RELATED connection?
NO
|
Is the packet tcp? + TCP_CHAIN -YES- ---------------------------------------------
NO |
| ACCEPT -YES- + port 25,53,80,110,143,443 and a new connection?
Is the packet udp? + UDP_CHAIN -YES- ----------------------------- NO
NO | |
| | RETURN
Is the packet icmp? + ICMP_CHAIN -YES- -------| ACCEPT -YES- + port 53 and new?
NO | No
| | |
+ LOG packets here. | RETURN
| |
Policy - DROP ---------------------
|
ACCEPT -YES- + type ICMP_ECHOREPLY?
NO
|
ACCEPT -YES- + type ICMP_UNREACH?
NO
|
ACCEPT -YES- + type ICMP_UNREACH?
NO
|
ACCEPT -YES- + type ICMP_UNREACH?
NO
|
RETURN
The changes to the second ruleset give us several optimizations. First, we will receive more related traffic than anything else, so we move the related rule to be right after our spoofing rule. Most of our packets will be targeted by rule two instead of rule 13. Second, we have broken out the TCP, UDP and ICMP rules. What this allows us to to prevent ICMP packets from wasting time on the TCP or UDP new connection rules, UDP from wasting time on TCP and ICMP new connection rules and so on. Ultimately we have shortened the longest possible path from 15 to 11 and made it so that the large majority of packets are targeted in a lot less time.
Debugging iptables rulesets is no different that any other debugging process. When in doubt, print, or in this case, log. While it is wise to keep logging to a minimum for performance reasons, well placed logging rules can be beneficial for tracing the travel of a misdirected packet and for monitoring the effectiveness of rulesets in section and their entirety.
We will start with our optimized ruleset and make a few minor changes. Example 2 (FORWARD chain):
Packet start
eth0 (External) -
|
+ Is the packet from an outside IP?
/ \
Yes No - DROP
|
ACCEPT -YES- + Is the part of an ESTABLISHED or RELATED connection?
NO
|
Is the packet tcp? + TCP_CHAIN -YES- -------------------------------------------------
NO |
| ACCEPT -YES- + port 25,53,80,110,143,443 and a new connection?
Is the packet udp? + UDP_CHAIN -YES- ----------------------------- NO
NO | |
| | + LOG packets with prefix "Bad TCP connreq: "
Is the packet icmp? + ICMP_CHAIN -YES- --------| ACCEPT -YES- + port 53 and new? |
NO | No DROP
| | |
+ LOG packets here with | + LOG packets with prefix "Bad UDP connreq: "
| prefix "Unknown pkt: " | |
| | DROP
Policy - DROP |
--------------------
|
ACCEPT -YES- + type ICMP_ECHOREPLY?
NO
|
ACCEPT -YES- + type ICMP_UNREACH?
NO
|
ACCEPT -YES- + type ICMP_UNREACH?
NO
|
ACCEPT -YES- + type ICMP_UNREACH?
NO
|
+ LOG packets with prefix "Bad UDP connreq: "
|
DROP
What we have done, is to add two additional rules to each branch end. By default, user created chains return the packet to the calling chain if the packet reaches the end of the target chain. The additional rules we added simply logs each packet if it reaches the end of a branch and then DROPs the packet right there.
Ultimately we have added no length to the travel of any single packet as they were all logged and DROPed by the FORWARD chains last rule and policy. What we gained was information placed in the logs telling us what type of unwanted packets we are DROPing. This is a quite regular "log and drop" scenario. It is worth noting that each logged packet includes all the details about the packet, so our example is bit redundant, but it does make spotting patterns easier when tailing the log file. Also, you can apply this log and drop to the end of any branch. If we branch to handle a special case server behind the firewall, we might be well suited to log unwanted packets to that server with a special prefix so we can easily spot them in the logs.
iptables allows us to place any number of log rules anywhere. Log rules can log packets for certain criteria or all packets in general as they obey all the same options, switches, and criteria as other rules.
We may have a ruleset with the following rule:
iptables -A INPUT -p udp --dport 137:138 -j ACCEPTand wonder why we are having problems with samba sharing files. By adding two log rules like so:
iptables -A INPUT -j LOG --log-prefix "Pre SMB: " iptables -A INPUT -p udp --dport 137:138 -j ACCEPT iptables -A INPUT -j LOG --log-prefix "Post SMB: "
We might notice a pattern of port 139 UDP packets that occurs in sync with ports 137 and 138 that cause log entries that start with "Pre SMB: ", but while we also have port 139 packets duplicated in the logs with "Post SMB: ". A quick review and change of our smb entry (sans log rules) to:
iptables -A INPUT -p udp --dport 137:139 -j ACCEPTfixes our problem.
A better example might be a ruleset like the following:
Internal Network: 192.168.1.0/24
External Interface: eth0 - 204.228.204.28
External Interface: eth1 - 192.168.1.1
Mail Server: 192.168.1.3
Web Server: 192.168.1.4
Starting ruleset:
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -j icmp_packets
iptables -A INPUT -p tcp -j tcp_packets
iptables -A INPUT -p udp -j udp_packets
iptables -A INPUT -d 204.228.204.28 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -d 192.168.1.1 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT INPT packet died: " --log-level 6
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j allowed_frwd
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT FRWD packet died: " --log-level 6
iptables -A OUTPUT -o lo -j lo_ntrfce
iptables -A OUTPUT -p udp -m udp --sport 500 --dport 500 -j ACCEPT
iptables -A OUTPUT -s 204.228.204.28 -o eth0 -j ACCEPT
iptables -A OUTPUT -s 192.168.1.1 -o eth1 -j ACCEPT
iptables -A OUTPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT OTPT packet died: " --log-level 6
iptables -A allowed -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j ACCEPT
iptables -A allowed -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A allowed -p tcp -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT allowed bad state: " --log-level 6
iptables -A allowed -p tcp -j DROP
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.3 --dport 25 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.3 --dport 110 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.3 --dport 143 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.4 --dport 81 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 0 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 3 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 5 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 11 -j ACCEPT
iptables -A icmp_packets -i eth1 -p icmp -m icmp --icmp-type 8 -j ACCEPT
iptables -A icmp_packets -i lo -p icmp -m icmp --icmp-type 8 -j ACCEPT
# This first rule here keeps Windows neighbors from packing my logs (DROP before log).
iptables -A tcp_packets -p tcp -m tcp --dport 137:139 -j DROP
iptables -A tcp_packets -p tcp -m tcp -s 192.168.1.5 -d 192.168.1.1 --dport 22 -i eth1 -j allowed
iptables -A tcp_packets -p tcp -m tcp -s 204.228.204.18 --dport 22 -i eth0 -j allowed
iptables -A udp_packets -p udp -m udp --sport 53 -j ACCEPT
iptables -A udp_packets -p udp -m udp --sport 500 --dport 500 -j ACCEPT
# Noise in the log prevention (DROP before log).
iptables -A udp_packets -p udp -m udp --dport 67:68 -j DROP
iptables -A udp_packets -p udp -m udp --dport 137:139 -j DROP
iptables -A udp_packets -p udp -m udp --dport 1900 -i eth1 -j DROP
iptables -A udp_packets -p udp -m udp --sport 2301 --dport 2301 -i eth1 -j DROP
iptables -A udp_packets -p udp -m udp -s 192.168.1.2 --dport 16024 -i eth1 -j DROP
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.3:25
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 110 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.3:110
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 143 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.3:143
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.4:80
iptables -A PREROUTING -s ! 192.168.1.0/24 -i eth1 -j DROP
iptables -A PREROUTING -s 192.168.1.0/24 -i eth0 -j DROP
iptables -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.28
Problem: The web server is not responding to our external guests, but does for the internal guests. Ruleset with logs rules added for examination of FORWARD packet travel.
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -j icmp_packets
iptables -A INPUT -p tcp -j tcp_packets
iptables -A INPUT -p udp -j udp_packets
iptables -A INPUT -d 204.228.204.28 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -d 192.168.1.1 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT INPT packet died: " --log-level 6
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j allowed_frwd
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT FRWD packet died: " --log-level 6
iptables -A OUTPUT -o lo -j lo_ntrfce
iptables -A OUTPUT -p udp -m udp --sport 500 --dport 500 -j ACCEPT
iptables -A OUTPUT -s 204.228.204.28 -o eth0 -j ACCEPT
iptables -A OUTPUT -s 192.168.1.1 -o eth1 -j ACCEPT
iptables -A OUTPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT OTPT packet died: " --log-level 6
iptables -A allowed -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j ACCEPT
iptables -A allowed -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A allowed -p tcp -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT allowed bad state: " --log-level 6
iptables -A allowed -p tcp -j DROP
iptables -A allowed -p tcp -j LOG --log-prefix "FWD START: "
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.3 --dport 25 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.3 --dport 110 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.3 --dport 143 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.4 --dport 80 -j ACCEPT
iptables -A allowed_frwd -i eth0 -p tcp -m tcp -d 192.168.100.4 --dport 443 -j ACCEPT
iptables -A allowed -p tcp -j LOG --log-prefix "FWD END: "
iptables -A icmp_packets -p icmp -m icmp --icmp-type 0 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 3 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 5 -j ACCEPT
iptables -A icmp_packets -p icmp -m icmp --icmp-type 11 -j ACCEPT
iptables -A icmp_packets -i eth1 -p icmp -m icmp --icmp-type 8 -j ACCEPT
iptables -A icmp_packets -i lo -p icmp -m icmp --icmp-type 8 -j ACCEPT
# This first rule here keeps Windows neighbors from packing my logs (DROP before log).
iptables -A tcp_packets -p tcp -m tcp --dport 137:139 -j DROP
iptables -A tcp_packets -p tcp -m tcp -s 192.168.1.5 -d 192.168.1.1 --dport 22 -i eth1 -j allowed
iptables -A tcp_packets -p tcp -m tcp -s 204.228.204.18 --dport 22 -i eth0 -j allowed
iptables -A udp_packets -p udp -m udp --sport 53 -j ACCEPT
iptables -A udp_packets -p udp -m udp --sport 500 --dport 500 -j ACCEPT
# Noise in the log prevention (DROP before log).
iptables -A udp_packets -p udp -m udp --dport 67:68 -j DROP
iptables -A udp_packets -p udp -m udp --dport 137:139 -j DROP
iptables -A udp_packets -p udp -m udp --dport 1900 -i eth1 -j DROP
iptables -A udp_packets -p udp -m udp --sport 2301 --dport 2301 -i eth1 -j DROP
iptables -A udp_packets -p udp -m udp -s 192.168.1.2 --dport 16024 -i eth1 -j DROP
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.3:25
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 110 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.3:110
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 143 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.3:143
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -d 204.228.204.28 -j DNAT --to-destination 192.168.1.5:80
iptables -A PREROUTING -s ! 192.168.1.0/24 -i eth1 -j DROP
iptables -A PREROUTING -s 192.168.1.0/24 -i eth0 -j DROP
iptables -A POSTROUTING -o eth0 -j SNAT --to-source 204.228.204.28
Greping the logs after a few external access attempts might return the following (sample edited for readability):
grep "DPT=80" /var/log/messages | grep "IN=eth0 OUT=eth1"
Nov 21 13:09:41 linif kernel: FWD START: IN=eth0
OUT=eth1
MAC=ff:ff:ff:ff:ff:ff:00:05:02:89:f9:bc:08:00
SRC=204.228.204.18
DST=192.168.100.5
LEN=115
TOS=0x00
PREC=0x00
TTL=64
ID=12357
PROTO=TCP
SPT=4891
DPT=80
LEN=95
Nov 21 13:09:41 linif kernel: FWD END: IN=eth0
OUT=eth1
MAC=ff:ff:ff:ff:ff:ff:00:05:02:89:f9:bc:08:00
SRC=204.228.204.18
DST=192.168.100.5
LEN=115
TOS=0x00
PREC=0x00
TTL=64
ID=12357
PROTO=TCP
SPT=4891
DPT=80
LEN=95
Inspection of these log entries tells us that we are blocking port 80 requests to 192.168.1.5!? Why are packets coming in on eth0 and leaving on eth1 trying to go to 192.168.1.5 when the server is on .4? Obviously we have a PREROUTING NAT error and a closer inspection of the PREROUTING chain shows that we are DNATing port 80 to 192.168.1.5. To fix this, we simply need to change the incorrect PREROUTING rule and drop the log rules we added that will eventually flood our logs with useless information.
IPSec is becoming more popular as a security tool to link disparate networks. It allows for secured communications between hosts communicating on public networks. One of the requirements to run IPSec, however, is to be able to get the IPSec packets through your firewall. IPSec packets must pass unadulterated. This means that IPSec through NAT is not possible and that you cannot modify the packets in any way with your PREROUTING, POSTROUTING, and OUTPUT chains on the nat and mangle tables.
>From the FreeS/WAN documentation Filtering rules for IPsec packets The basic constraint is that an IPsec gateway must have packet filters that allow IPsec packets, at least when talking to other IPsec gateways:
UDP port 500 for IKE negotiations protocol 50 if you use ESP encryption and/or authentication (the typical case) protocol 51 if you use AH packet-level authenticationYour gateway and the other IPsec gateways it communicates with must be able to exchange these packets for IPsec to work. Firewall rules must allow UDP 500 and at least one of AH or ESP on the interface that communicates with the other gateway. For nearly all FreeS/WAN applications, you must allow UDP port 500 and the ESP protocol.
Here are the specific rules needed to create the following FreeS/WAN setup.
Network 1: 192.168.1.0/24 (north) IP 204.228.204.18 Network 2: 192.168.2.0/24 (south) IP 204.228.204.28
On both machines, eth0 is external, eth1 is internal. Each machine's eth1 is the network's .1 IP. Both internal networks are "trusted".
These are the rules for the firewall/NAT gateway on the north network.
iptables -A INPUT -p 50 -s 204.228.204.18 -j ACCEPT
iptables -A INPUT -p 51 -s 204.228.204.18 -j ACCEPT
iptables -A INPUT -p udp -m udp -s 204.228.204.28 --sport 500 -d 204.228.204.18 --dport 500 -j ACCEPT
iptables -A FORWARD -i ipsec0 -o eth1 -s 192.168.2.0/24 -d 192.168.1.0/24 -j ACCEPT
iptables -A FORWARD -i eth1 -o ipsec0 -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
iptables -A OUTPUT -p 50 -d 204.228.204.28 -j ACCEPT
iptables -A OUTPUT -p 51 -d 204.228.204.28 -j ACCEPT
iptables -A OUTPUT -p udp -m udp -s 204.228.204.18 --sport 500 -d 204.228.204.28 --dport 500 -j ACCEPT
These are the rules for the firewall/NAT gateway on the south network.
iptables -A INPUT -p 50 -s 204.228.204.28 -j ACCEPT
iptables -A INPUT -p 51 -s 204.228.204.28 -j ACCEPT
iptables -A INPUT -p udp -m udp -s 204.228.204.18 --sport 500 -d 204.228.204.28 --dport 500 -j ACCEPT
iptables -A FORWARD -i ipsec0 -o eth1 -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
iptables -A FORWARD -i eth1 -o ipsec0 -s 192.168.2.0/24 -d 192.168.1.0/24 -j ACCEPT
iptables -A OUTPUT -p 50 -d 204.228.204.18 -j ACCEPT
iptables -A OUTPUT -p 51 -d 204.228.204.18 -j ACCEPT
iptables -A OUTPUT -p udp -m udp -s 204.228.204.28 --sport 500 -d 204.228.204.18 --dport 500 -j ACCEPT
While constructing a firewall from iptables for a single host or for a single network may prove to be straight forward, handling multiple networks which may have disparate purposes may not be. The most common small organization scenario is a workstation network and a DMZ. The DMZ is where the organization's publicly accessed servers reside. The idea is to minimize the potential impact of a server compromise but still provide the servers some of the advantages of a gateway firewall.
For our example, we will start with a network of workstations and a network that houses our mail and web servers. For the purposes of this example assume all IPs are RFC globally routable (public). Both internally and externally, clients must connect to 192.168.2.2 and 192.168.2.3 for mail and web services respectively
__________ 192.168.0.254 | | 192.168.1.1 Workstations Internet --------------| router |------------- net 192.168.1.0 eth0 |__________| eth1 | eth2 | 192.168.2.1 DMZ | | __________ | 192.168.2.2 | | |-------------| mail | | |__________| | | ---------- | 192.168.2.3 | | |-------------| web | |__________| Notes: * We trust all outbound traffic from our workstations. (eth1) * We do NOT trust traffic on the public Internet and the DMZ. (eth0, eth2)
We want to allow our workstations full access to the world and full access to the servers as there may be private services (Apache to MySQL) or services only needed internally (IMAP, POP3). We want the world to be able to access the publicly offered services. The servers should have unfettered access to the world to obtain public services (updates, time, etc...). And finally, we need to allow established traffic in all directions.
### INPUT only deals with traffic directed TO the router. # Filter untrusted traffic iptables -A INPUT -i eth0 -p icmp -j icmp_packets iptables -A INPUT -i eth0 -p tcp -j tcp_packets iptables -A INPUT -i eth0 -p udp -j udp_packets iptables -A INPUT -i eth2 -p icmp -j icmp_packets iptables -A INPUT -i eth2 -p tcp -j tcp_packets iptables -A INPUT -i eth2 -p udp -j udp_packets # Allow implicitly trusted traffic. iptables -A INPUT -i eth1 -j ACCEPT iptables -A INPUT -i lo -j ACCEPT # Trust traffic we initiated from untrusted networks. iptables -A INPUT -i eth0 -d 192.168.0.254 -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -i eth2 -d 192.168.2.1 -m state --state RELATED,ESTABLISHED -j ACCEPT # Log dropped traffic. iptables -A INPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT INPUT packet died: " --log-level 6 ### OUTPUT only deals with traffic directed FROM the router. # lo device traffic iptables -A OUTPUT -s 127.0.0.1 -o lo -j ACCEPT # These should not be needed, but I have had occasional back luck without them (YMMV). iptables -A OUTPUT -s 192.168.0.254 -d 192.168.0.254 -o lo -j ACCEPT iptables -A OUTPUT -s 192.168.1.1 -d 192.168.1.1 -o lo -j ACCEPT iptables -A OUTPUT -s 192.168.2.1 -d 192.168.2.1 -o lo -j ACCEPT # Allow outbound traffic over interfaces only if originating from the correct IP for that interface. iptables -A OUTPUT -s 192.168.0.254 -o eth0 -j ACCEPT iptables -A OUTPUT -s 192.168.1.1 -o eth1 -j ACCEPT iptables -A OUTPUT -s 192.168.2.1 -o eth2 -j ACCEPT # Log dropped traffic. iptables -A OUTPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT OUTPUT packet died: " --log-level 6 ### Routed traffic. # We trust traffic passing from the workstations to anywhere (source IP and device verified). iptables -A FORWARD -i eth1 -s 192.168.1.0/24 -j ACCEPT # We (for this example) are trusting server originating traffic heading to the public Internet. iptables -A FORWARD -i eth2 -s 192.168.2.0/24 -o eth0 -j ACCEPT # Allow inbound public server connection traffic (public initiating connections to servers). iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.2 -p tcp -m tcp --dport 25 -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.3 -p tcp -m tcp --dport 80 -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.3 -p tcp -m tcp --dport 443 -j ACCEPT # Allow establish traffic to pass in any direction. iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT # Log dropped traffic. iptables -A FORWARD -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT FORWARD packet died: " --log-level 6 ### Filtered (inbound to the router directly) traffic ## Manage the following TCP traffic inbound to the router directly. # Drop all SMB traffic to prevent log flooding from obvious useless dropped traffic. iptables -A tcp_packets -p tcp -m tcp --dport 137:139 -j DROP # Allow incoming port 22 for SSH connection for remote admin and incoming encrypted ppp tunnels. iptables -A tcp_packets -p tcp -m tcp --dport 22 -j allowed ## Validate potentially allowed tcp traffic. iptables -A allowed -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j ACCEPT iptables -A allowed -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A allowed -p tcp -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT allowed bad state: " --log-level 6 iptables -A allowed -p tcp -j DROP ## Manage the following ICMP traffic inbound to the router directly. iptables -A icmp_packets -p icmp -m icmp --icmp-type 0 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 3 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 5 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 11 -j ACCEPT iptables -A icmp_packets -i eth1 -p icmp -m icmp --icmp-type 8 -j ACCEPT iptables -A icmp_packets -i lo -p icmp -m icmp --icmp-type 8 -j ACCEPT ## Manage the following UDP traffic inbound to the router directly. # Drop all SMB traffic to prevent log flooding from obvious useless dropped traffic. iptables -A udp_packets -p udp -m udp --dport 137:139 -j DROP # Allow potentially untracked valid DNS traffic. iptables -A udp_packets -p udp -m udp --sport 53 -j ACCEPT
In the spirit of demonstrating common scenarios, here is the same example, but with eth1 and eth2 sporting private IPs while eth0 is the router's public interface. In this example assume all 192.168.0.0 address are RFC non-routable while 10.0.0.254 will be routable. The router of course will route between the two private networks within the constraints of the firewall. Internally, clients must connect to 192.168.2.2 and 192.168.2.3 for mail and web services respectively while external users connect to 10.0.0.254 for mail and web services. While logically, the internal clients could connect to 10.0.0.254 for all services, I have, again, had bad luck with such a configuration. A better solution with one virtual interface internally would be to DNAT all services from 192.168.1.1 also. The best solution would simply be to maintain an internal DNS server on the 192.168.1.0 network mapping the names to the actual unNAT'd IPs.
__________ 10.0.0.254 | | 192.168.1.1 Workstations Internet --------------| router |------------- net 192.168.1.0 eth0 |__________| eth1 | eth2 | 192.168.2.1 DMZ | | __________ | 192.168.2.2 | | |-------------| mail | | |__________| | | ---------- | 192.168.2.3 | | |-------------| web | |__________| Notes: * We trust all outbound traffic from our workstations. (eth1) * We do NOT trust traffic on the public Internet and the DMZ. (eth0, eth2)The same desires exist here as with the above example.
### INPUT only deals with traffic directed TO the router. # Filter untrusted traffic iptables -A INPUT -i eth0 -p icmp -j icmp_packets iptables -A INPUT -i eth0 -p tcp -j tcp_packets iptables -A INPUT -i eth0 -p udp -j udp_packets iptables -A INPUT -i eth2 -p icmp -j icmp_packets iptables -A INPUT -i eth2 -p tcp -j tcp_packets iptables -A INPUT -i eth2 -p udp -j udp_packets # Allow implicitly trusted traffic. iptables -A INPUT -i eth1 -j ACCEPT iptables -A INPUT -i lo -j ACCEPT # Trust traffic we initiated from untrusted networks. iptables -A INPUT -i eth0 -d 10.0.0.254 -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -i eth2 -d 192.168.2.1 -m state --state RELATED,ESTABLISHED -j ACCEPT # Log dropped traffic. iptables -A INPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT INPUT packet died: " --log-level 6 ### OUTPUT only deals with traffic directed FROM the router. # lo device traffic iptables -A OUTPUT -s 127.0.0.1 -o lo -j ACCEPT # These should not be needed, but I have had occasional back luck without them (YMMV). iptables -A OUTPUT -s 10.0.0.254 -d 10.0.0.254 -o lo -j ACCEPT iptables -A OUTPUT -s 192.168.1.1 -d 192.168.1.1 -o lo -j ACCEPT iptables -A OUTPUT -s 192.168.2.1 -d 192.168.2.1 -o lo -j ACCEPT # Allow outbound traffic over interfaces only if originating from the correct IP for that interface. iptables -A OUTPUT -s 10.0.0.254 -o eth0 -j ACCEPT iptables -A OUTPUT -s 192.168.1.1 -o eth1 -j ACCEPT iptables -A OUTPUT -s 192.168.2.1 -o eth2 -j ACCEPT # Log dropped traffic. iptables -A OUTPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT OUTPUT packet died: " --log-level 6 ### Routed traffic. # We trust traffic passing from the workstations to anywhere (source IP and device verified). iptables -A FORWARD -i eth1 -s 192.168.1.0/24 -j ACCEPT # We (for this example) are trusting server originating traffic heading to the public Internet. iptables -A FORWARD -i eth2 -s 192.168.2.0/24 -o eth0 -j ACCEPT # Allow inbound public server connection traffic (public initiating connections to servers). iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.2 -p tcp -m tcp --dport 25 -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.3 -p tcp -m tcp --dport 80 -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.3 -p tcp -m tcp --dport 443 -j ACCEPT # Allow establish traffic to pass in any direction. iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT # Log dropped traffic. iptables -A FORWARD -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT FORWARD packet died: " --log-level 6 ### Filtered (inbound to the router directly) traffic ## Manage the following TCP traffic inbound to the router directly. # Drop all SMB traffic to prevent log flooding from obvious useless dropped traffic. iptables -A tcp_packets -p tcp -m tcp --dport 137:139 -j DROP # Allow incoming port 22 for SSH connection for remote admin and incoming encrypted ppp tunnels. iptables -A tcp_packets -p tcp -m tcp --dport 22 -j allowed ## Validate potentially allowed tcp traffic. iptables -A allowed -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j ACCEPT iptables -A allowed -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A allowed -p tcp -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT allowed bad state: " --log-level 6 iptables -A allowed -p tcp -j DROP ## Manage the following ICMP traffic inbound to the router directly. iptables -A icmp_packets -p icmp -m icmp --icmp-type 0 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 3 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 5 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 11 -j ACCEPT iptables -A icmp_packets -i eth1 -p icmp -m icmp --icmp-type 8 -j ACCEPT iptables -A icmp_packets -i lo -p icmp -m icmp --icmp-type 8 -j ACCEPT ## Manage the following UDP traffic inbound to the router directly. # Drop all SMB traffic to prevent log flooding from obvious useless dropped traffic. iptables -A udp_packets -p udp -m udp --dport 137:139 -j DROP # Allow potentially untracked valid DNS traffic. iptables -A udp_packets -p udp -m udp --sport 53 -j ACCEPT ### Set up DNAT for incoming connection requests. iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -j DNAT --to-destination 192.168.2.2:25 iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.2.3:80 iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 192.168.2.3:443
Multiple workstation networks with DMZ. One workstation network connected via IPSec.
__________ 10.0.0.254 | | 192.168.1.1 Workstations Internet --------------| router |------------- net 192.168.1.0 | eth0 |__________| eth1 | | | eth2 | 192.168.2.1 net 192.168.3.0 DMZ | ipsec0 | __________ | 192.168.2.2 | | |-------------| mail | | |__________| | | ---------- | 192.168.2.3 | | |-------------| web | |__________| Notes: * We trust all outbound traffic from our workstations. (eth1, ipsec0) * We do NOT trust traffic on the public Internet and the DMZ. (eth0, eth2)
The same desires exist here as with the above examples with the addition of another IPsec connected trusted network. Note that the IPSec machines cannot talk to the router itself. Even if the firewall allowed it, thick communication is impossible due to the limitations of IPSec via FreeS/WAN. Therefore, the rules to allow such communications have been left out for simplicity of maintenance. We have the IPSec connected machine connecting to a DNS server that maps the server names to their real IPs as with the original workstation network.
### INPUT only deals with traffic directed TO the router. # Filter untrusted traffic iptables -A INPUT -i eth0 -p icmp -j icmp_packets iptables -A INPUT -i eth0 -p tcp -j tcp_packets iptables -A INPUT -i eth0 -p udp -j udp_packets iptables -A INPUT -i eth2 -p icmp -j icmp_packets iptables -A INPUT -i eth2 -p tcp -j tcp_packets iptables -A INPUT -i eth2 -p udp -j udp_packets # Allow incoming IPSec authentication packets. iptables -A INPUT -p 50 -j ACCEPT iptables -A INPUT -p 51 -j ACCEPT # Allow implicitly trusted traffic. iptables -A INPUT -i eth1 -j ACCEPT iptables -A INPUT -i lo -j ACCEPT # Trust traffic we initiated from untrusted networks. iptables -A INPUT -i eth0 -d 10.0.0.254 -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -i eth2 -d 192.168.2.1 -m state --state RELATED,ESTABLISHED -j ACCEPT # Log dropped traffic. iptables -A INPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT INPUT packet died: " --log-level 6 ### OUTPUT only deals with traffic directed FROM the router. # lo device traffic iptables -A OUTPUT -s 127.0.0.1 -o lo -j ACCEPT # Allow outgoing IPSec authentication packets. iptables -A OUTPUT -p 50 -j ACCEPT iptables -A OUTPUT -p 51 -j ACCEPT iptables -A OUTPUT -p udp -m udp --sport 500 --dport 500 -j ACCEPT # These should not be needed, but I have had occasional back luck without them (YMMV). iptables -A OUTPUT -s 10.0.0.254 -d 10.0.0.254 -o lo -j ACCEPT iptables -A OUTPUT -s 192.168.1.1 -d 192.168.1.1 -o lo -j ACCEPT iptables -A OUTPUT -s 192.168.2.1 -d 192.168.2.1 -o lo -j ACCEPT # Allow outbound traffic over interfaces only if originating from the correct IP for that interface. iptables -A OUTPUT -s 10.0.0.254 -o eth0 -j ACCEPT iptables -A OUTPUT -s 192.168.1.1 -o eth1 -j ACCEPT iptables -A OUTPUT -s 192.168.2.1 -o eth2 -j ACCEPT # Log dropped traffic. iptables -A OUTPUT -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT OUTPUT packet died: " --log-level 6 ### Routed traffic. # We trust traffic passing from the workstations to anywhere (source IP and device verified). iptables -A FORWARD -i eth1 -s 192.168.1.0/24 -j ACCEPT iptables -A FORWARD -i ipsec0 -s 192.168.3.0/24 -j ACCEPT # We (for this example) are trusting server originating traffic heading to the public Internet. iptables -A FORWARD -i eth2 -s 192.168.2.0/24 -o eth0 -j ACCEPT # Allow inbound public server connection traffic (public initiating connections to servers). iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.2 -p tcp -m tcp --dport 25 -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.3 -p tcp -m tcp --dport 80 -j ACCEPT iptables -A FORWARD -i eth0 -o eth2 -d 192.168.2.3 -p tcp -m tcp --dport 443 -j ACCEPT # Allow establish traffic to pass in any direction. iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT # Log dropped traffic. iptables -A FORWARD -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT FORWARD packet died: " --log-level 6 ### Filtered (inbound to the router directly) traffic ## Manage the following TCP traffic inbound to the router directly. # Drop all SMB traffic to prevent log flooding from obvious useless dropped traffic. iptables -A tcp_packets -p tcp -m tcp --dport 137:139 -j DROP # Allow incoming port 22 for SSH connection for remote admin and incoming encrypted ppp tunnels. iptables -A tcp_packets -p tcp -m tcp --dport 22 -j allowed ## Validate potentially allowed tcp traffic. iptables -A allowed -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j ACCEPT iptables -A allowed -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A allowed -p tcp -m limit --limit 3/min --limit-burst 3 -j LOG --log-prefix "IPT allowed bad state: " --log-level 6 iptables -A allowed -p tcp -j DROP ## Manage the following ICMP traffic inbound to the router directly. iptables -A icmp_packets -p icmp -m icmp --icmp-type 0 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 3 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 5 -j ACCEPT iptables -A icmp_packets -p icmp -m icmp --icmp-type 11 -j ACCEPT iptables -A icmp_packets -i eth1 -p icmp -m icmp --icmp-type 8 -j ACCEPT iptables -A icmp_packets -i lo -p icmp -m icmp --icmp-type 8 -j ACCEPT ## Manage the following UDP traffic inbound to the router directly. # Drop all SMB traffic to prevent log flooding from obvious useless dropped traffic. iptables -A udp_packets -p udp -m udp --dport 137:139 -j DROP # Allow potentially untracked valid DNS traffic. iptables -A udp_packets -p udp -m udp --sport 53 -j ACCEPT # Allow IPSec authentication traffic. iptables -A udp_packets -p udp -m udp --sport 500 --dport 500 -j ACCEPT ### Set up DNAT for incoming connection requests. iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -j DNAT --to-destination 192.168.2.2:25 iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.2.3:80 iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 192.168.2.3:443
These multi network examples do not reach the highest level of paranoia where nobody trusts anybody. They do however cover common situations that small to medium organizations might usefully engage to offer reasonable security with practical usability.