Since we recently learned about basic Linux firewalls, I figured that it would be good to cover some more advanced firewall topics. There are a lot of settings that we can use to allow or deny specific traffic from specific hosts. So, let’s jump right in and take a look!
If you take a look at the image above, you can see how data flows through a typical Linux firewall. Since we are currently only concerned with traffic destined for our Linux box, all data flows through the “Yes” side of the “Data for the firewall?” question. In the future we will look at the other side when we configure a Linux router.
Let’s start with a description of what we are doing, and then I’ll show what it looks like. Within IPTables there are different tables and chains, which are referenced in the picture above. We want to look primarily at the Input chain in the Filter table. The first thing we are going to do is create a new chain at the top of our input chain, which will accept all predetermined traffic that we know will always be safe. This is data such as related or established traffic, traffic on the local loopback interface, and if you are running a DHCP server on your Linux machine, you would also want to accept UDP traffic on port 67. DHCP requests come from source address 0.0.0.0, which we will be blocking later, so we need to accept it now!
After we accept this traffic, we want to block all traffic that we know is bad. This is traffic from non-existent networks (bogons), traffic with bad TCP flags, and anything else you specifically want to keep out. We will create two chains for this; one to list the traffic to be blocked, and another to block and optionally log the traffic. If you don’t wish to log the traffic, you can simply stick with one chain that will only block the undesired traffic.
Finally we can create another chain to accept traffic that we want to let in. This can be access to a SSH server, DNS server, web server, or anything else you want! After all of these chains, we can also optionally log any traffic that made it this far, before dropping it into oblivion.
Here is what the INPUT table looks like:
-A INPUT -j Firewall-1-INPUT -A INPUT -j Firewall-1-DROP -A INPUT -j Firewall-2-INPUT -A INPUT -m limit --limit 1/sec -j LOG --log-prefix "[INPUT] "
Here the data comes in to the Firewall-1-INPUT chain first. If it is not accepted (or blocked) by this chain, it then moves on to the Firewall-1-DROP chain. If the data makes it past that chain it goes on to the Firewall-2-INPUT chain, and finally if it makes it the whole way through without getting accepted or blocked, it will be logged before performing the default policy, which in our case is to drop the traffic so it can’t come in.
The Firewall-1-INPUT chain looks like this:
-A Firewall-1-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A Firewall-1-INPUT -i lo -j ACCEPT -A Firewall-1-INPUT -p udp -m udp --dport 67 -j ACCEPT
The first line accepts any related or established traffic. The second line accepts any traffic on the local loopback interface, and the third line accepts DHCP address request traffic on UDP port 67. If you are not running a DHCP server, then you won’t need that last line.
Next we move on to the Firewall-1-DROP chain, which drops any specifically unwanted traffic.
-A Firewall-1-DROP -f -j Firewall-2-DROP -A Firewall-1-DROP -p tcp ! --syn -m state --state NEW -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,ACK FIN -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags PSH,ACK PSH -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags ACK,URG URG -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,RST FIN,RST -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,PSH,URG -j Firewall-2-DROP -A Firewall-1-DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,ACK,URG -j Firewall-2-DROP
-A Firewall-1-DROP -s 0.0.0.0/8 -j Firewall-2-DROP -A Firewall-1-DROP -d 0.0.0.0/8 -j Firewall-2-DROP -A Firewall-1-DROP -s 10.0.0.0/8 -j Firewall-2-DROP -A Firewall-1-DROP -d 10.0.0.0/8 -j Firewall-2-DROP -A Firewall-1-DROP -s 127.0.0.0/8 -j Firewall-2-DROP -A Firewall-1-DROP -d 127.0.0.0/8 -j Firewall-2-DROP -A Firewall-1-DROP -s 169.254.0.0/16 -j Firewall-2-DROP -A Firewall-1-DROP -d 169.254.0.0/16 -j Firewall-2-DROP -A Firewall-1-DROP -s 172.16.0.0/12 -j Firewall-2-DROP -A Firewall-1-DROP -d 172.16.0.0/12 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.0.0.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.0.0.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.0.2.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.0.2.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.1.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.1.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.2.0/23 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.2.0/23 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.4.0/22 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.4.0/22 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.8.0/21 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.8.0/21 -j Firewall-2-DROP -A Firewall-1-DROP -s 162.168.16.0/20 -j Firewall-2-DROP -A Firewall-1-DROP -d 162.168.16.0/20 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.32.0/19 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.32.0/19 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.64.0/18 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.64.0/18 -j Firewall-2-DROP -A Firewall-1-DROP -s 192.168.128.0/17 -j Firewall-2-DROP -A Firewall-1-DROP -d 192.168.128.0/17 -j Firewall-2-DROP -A Firewall-1-DROP -s 198.18.0.0/15 -j Firewall-2-DROP -A Firewall-1-DROP -d 198.18.0.0/15 -j Firewall-2-DROP -A Firewall-1-DROP -s 198.51.100.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -d 198.51.100.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -s 203.0.113.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -d 203.0.113.0/24 -j Firewall-2-DROP -A Firewall-1-DROP -s 224.0.0.0/3 -j Firewall-2-DROP -A Firewall-1-DROP -d 224.0.0.0/3 -j Firewall-2-DROP
The first set of lines blocks any incoming packets with fragments, new connections that aren’t SYN packets, and packets with invalid TCP connection flags. This will help to prevent syn flood attacks on your machine. The second set of lines blocks traffic coming from (-s) or going to (-d) addresses in the bogon list, which helps prevent spoof attacks. It is important to note here that if your machine is on a private network using non-routable IP addresses, then you must remove the lines from the list above that correspond to your LAN IP addresses. In the above example, traffic on the 192.168.0.x network is allowed, while all other traffic beginning with 192.168.x.x is blocked. We will continue using this network in the rest of the examples.
If any traffic is caught by these rules, it is then sent to the Firewall-2-DROP chain, which will log the packet then drop it. The Firewall-2-DROP chain will limit the amount of logging (to keep from filling our logs), and will also prefix the log so we know where the traffic was blocked. Below are the lines from the Firewall-2-DROP chain:
-A Firewall-2-DROP -m limit --limit 1/sec -j LOG --log-prefix "[INFW2] " -A Firewall-2-DROP -j DROP
Finally, if the traffic has made it this far and hasn’t been accepted or dropped yet, we can move on to the accept chain.
-A Firewall-2-INPUT -p udp -m udp --dport 53 -j ACCEPT -A Firewall-2-INPUT -p icmp -m icmp -m limit -s 192.168.0.0/24 --icmp-type 8 --limit 1/sec -j ACCEPT -A Firewall-2-INPUT -p tcp -m tcp -m multiport -j ACCEPT --dports 22,53,80,443 -A Firewall-2-INPUT -p tcp -m tcp -m multiport -s 192.168.0.0/24 -j ACCEPT --dports 5000:5999 -A Firewall-2-INPUT -p udp -m udp -m multiport -s 192.168.0.0/24 -j ACCEPT --dports 69,70,71
The first line will accept DNS lookup traffic from anyone (UDP port 53). If you don’t run a DNS server, you won’t need this line. The second line accepts icmp-type 8 traffic (ping request) from the local LAN. This means that your machine will respond to ping requests from clients on the LAN, but not from clients on the internet. The third line accepts TCP traffic on ports 22, 53, 80, and 443 (SSH, DNS, HTTP, and HTTPS respectively); again, if you’re not running these services then you don’t need these ports open. The fourth line accepts traffic on ports 5000-5999; this is just an example to show how to open a range of ports. The fifth line shows us accepting UDP traffic on ports 69, 70, and 71. And finally, the last line will log any traffic that has made it this far, again limiting the amount of logs written to keep from flooding the log files, and prefixing the log lines so we know where it came from.
We also must have the default rule for our input chain, to determine what happens when traffic makes it all the way to the end of our rule list. Here is our default input rule, to drop the traffic:
:INPUT DROP [0:0]
Hopefully this will help you to create a more advanced Linux firewall to fit your needs. Sometime in the future I will show you how to use your Linux box as a router, to handle NAT and routing with your firewall. For now, I’m going to go take a nap; it’s hard being a dogg!
Amazing blog! Do you have any hints for aspiring writers? I’m hoping to start my own blog soon but I’m a little lost on everything. Would you suggest starting with a free platform like WordPress or go for a paid option? There are so many options out there that I’m completely overwhelmed .. Any ideas? Thank you!
Sorry, I don’t really have any tips. I just bought myself a virtual server, and set everything up using free software! I’m not much of a writer, but I enjoy it! I always try to use free and open source software whenever I can.