Roll Your Own Linux Firewall Script
BRIEF INTRODUCTION TO FIREWALLS
Over the years I have learned how to roll my own firewall script and call it from /etc directory. Of course, my firewall is only INPUT based, instead of INPUT and OUTPUT based, but I find that building an INPUT/OUTPUT based firewall is tremendously difficult and not really all that necessary if you use good download practices on your Linux server or PC and/or if you're already behind a NAT router (such as a home-based DSL or cable router or wireless router) or other firewall.
If you're scratching your head on what I mean by INPUT and OUTPUT, then just think about you being inside a house that has a front door (INPUT), and a backdoor (OUTPUT). When you surf the web, you first start sending packets of data out your backdoor (OUTPUT).
Then, packets return and come in your front door and then you see them. In some cases, some packets need to travel back out your backdoor (OUTPUT) in order to establish or continue a connection. To complicate things, imagine multiple front doors and backdoors, and your ability to direct traffic through these doorways through something called rules, which we explain in a moment.
On Linux, they have a module loaded into the core base of the operating system (the kernel) called NetFilter. It filters network traffic and can block out the bad stuff if you want. You can interface with it using a command-line tool called iptables. Using iptables, you can establish what holes you want to leave in your firewall, and block everything else. Usually the lingo we use is that when we use an iptables command that have the keywords INPUT, OUTPUT, or FORWARD on the line, we are establishing "rules".
Note also that a more advanced topic is a FORWARD connection in your firewall. That's another discussion on a different thread.
Remember that house model I used? Well, a FORWARD connection would be like a chute you install in your house that connects to another house. You can opt for certain packets to get sent out to your FORWARD connection, and you can set up multiple ones to various locations. This is often used for a concept called IP masquerading with a NAT router, which means that your workstation behind a NAT router can act as if it has the same address as the NAT router, or vice-versa. Anyway, that's a discussion for a different thread. It's an advanced topic and not very easy to set up.
Anyway, the combinations of these INPUT and OUTPUT doorways and your FORWARD chutes make up are the whole sum of your "firewall" on Linux.
- GETTING STARTED
Would you like to get started? Okay, great.
- DOING RESEARCH
1. Start by identifying the services your PC will need to either connect out for or host for connections in. Don't forget things like VPN, NTP, HTTP, FTP, DNS, DHCP (if you need it), etc. Normally you won't have to worry about things your PC connects out for EXCEPT in the case of VPN, NTP, and DHCP. There are rare cases that I have found beyond that. If you don't know what IP Address, intranet, TCP, UDP, VPN, NTP, HTTP, FTP, DNS, or DHCP are, then check a tech acronym directory on the web. You'll need to know what those concepts are before proceeding in this how to doc. I suggest using WikiPedia.org as your second choice, and keywords on Google as your first choice.
2. Using cat and grep on /etc/services, identify what the port numbers would be that you need to poke holes for. For instance:
cat /etc/services | grep -i "ntp"
...gives you 123. Write these numbers down. Note that in the case of VPN and DNS, you have to do things slightly different than this and I'll explain that in a moment.
3. Identify your DNS servers. DNS stands for Domain Name Server. When you surf the web and type in a name into your browser like nuxified.org, a DNS server receives this request and translates it to a physical address like 45.61.61.32 (not the actual one, of course, in this example). To find your DNS servers, you can usually do:
cat /etc/resolv.conf
...to get these, or look at perhaps your NAT router's settings, or perhaps your network control panel if your distro has this. Anyway, when you find these, write these down.
- STARTING YOUR FIRST SCRIPT
4. Now let's get started. Create a firewall script as /etc/firewall.sh for now. If you know the more advanced technique of building an entry in /etc/init.d (another article, another day), then so be it. But for now, let's stick with /etc/firewall.sh.
5. At the top, put:
#!/bin/bash
iptables -F
iptables -A INPUT -i lo -j ACCEPT
... in the above example, I flush any existing filter that may already be established and start a fresh one. I then permit you to do something called loopback connections, where your PC wants to talk back to itself which should normally be enabled.
- PERMITTING DHCP CLIENT
6. Now, do you have a DHCP address or a static address? If your system depends on DHCP for an address, then add these lines:
iptables -A INPUT -p udp -m udp -s 0/0 --sport 67:68 -d 0/0 --dport 67:68 -i eth0 -j ACCEPT
iptables -A INPUT -p udp -m udp -s 0/0 --sport 67:68 -d 0/0 --dport 67:68 -i eth1 -j ACCEPT
- POKING HOLES FOR SERVICES AND CERTAIN KINDS OF CONNECTIONS THAT REQUIRE SPECIAL HOLES
7. Now follow this with port numbers that you wrote down that you think you want to permit through. Do this for all except VPN and NTP, which you have to do a little differently. The format is:
iptables -A INPUT -p tcp -m tcp --dport ZZZ --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport ZZZ -j ACCEPT
...but replace ZZZ with the port number. Now, note that in doing this you are enabling both tcp protocol and udp protocol. If you don't know what those are, then just enable both for now because we'll come back to this later in a moment.
8. Repeat the previous step for each port number that you wrote down, appending lines, changing ZZZ to the port number. However, below are a few steps to help you shortcut this a bit.
9. Do you want to host a web server? If so, then append these lines:
iptables -A INPUT -p tcp -m tcp --dport 80 --syn -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 80 -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 443 -j ACCEPT
10. Do you want to host SSH connections into your PC? SSH stands for Secure Session Host. It's used for when you want to enable remote command-line connections to your PC or server. If so, then append these lines:
iptables -A INPUT -p tcp -m tcp --dport 22 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 22 -j ACCEPT
11. Do you want to host POP connections into your PC? POP stands for Post Office Protocol. It's used for hosting a mail server where other workstations can download mail from you. If so, then append these lines:
iptables -A INPUT -p tcp -m tcp --dport 110 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 995 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 110 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 995 -j ACCEPT
12. Do you want to host SMTP connections into your PC? SMTP stands for Simple Mail Transport Protocol. It's for hosting a server that permits you to send out mail to another host on the Internet or on an intranet. If so, then append these lines:
iptables -A INPUT -p tcp -m tcp --dport 25 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 465 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 25 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 465 -j ACCEPT
13. Do you want to host FTP connections into your PC? FTP is for File Transfer Protocol. It's for when you want to host files in a very common way so that they may be easily accessed by other workstations. If so, then append these lines:
iptables -A INPUT -p tcp -m tcp --dport 21 --syn -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 115 --syn -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 989 --syn -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 990 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 21 -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 115 -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 989 -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 990 -j ACCEPT
14. Do you want to host LDAP connections into your PC? LDAP is a way to centralize your login accounts in your Linux, Unix, Mac, Mainframe, and Windows systems into a central database. If so, then append these lines:
iptables -A INPUT -p tcp -m tcp --dport 389 --syn -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 636 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 389 -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 636 -j ACCEPT
15. Do you want to establish chat connections from your PC to a remote IRC server? If so, then you probably need these lines:
iptables -A INPUT -p tcp -m tcp --dport 8888 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 8888 -j ACCEPT
- POKING A SPECIAL HOLE FOR VPN
16. Remember what I said about holding off on VPN? Okay, if you want to enable your workstation to establish VPN connections to a remote PC, such as making a connection from your home to office, enable it by adding this:
iptables -A INPUT -p tcp -m tcp --dport 500 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 500 -j ACCEPT
Note that this VPN connection isn't perfect. You may have special needs that require other ports. For that discussion, try using this forum as a discussion item as well as on Usenet (groups.google.com is an option for you).
- POKING A SPECIAL HOLE FOR NTP
17. Now do you need NTP access? This is for time server synchronization to keep your time up to date. Note that usually you can use the formula in step 7 to set this up, but I found that you have to do something special in Debian with the GNOME time control panel when you check off time server synchronization. Use these two lines instead:
iptables -A INPUT -p udp -m udp --sport 123 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --sport 123 --syn -j ACCEPT
...and note that instead of "dport", we use "sport". Going into why this is necessary is beyond this discussion. I'm trying to keep you from getting a major headache. At a later date you can dig on the web and find out how all these iptables commands actually work.
- POKING A SPECIAL HOLE FOR DNS QUERIES BY YOUR PC
18. Now, remember that DNS stuff you wrote down? You need to ping those addresses if you only have names, and get their physical IP address. Then, put them below, replacing MYDNS1 and MYDNS2 with those addresses.
iptables -A INPUT -p udp -m udp -s MYDNS1 --sport 53 -d 0/0 -j ACCEPT
iptables -A INPUT -p udp -m udp -s MYDNS2 --sport 53 -d 0/0 -j ACCEPT
...and append this to the /etc/firewall.sh file that we've been building. Note that in some cases you may have DNS problems because the addresses have changed, so you might in that case, if this happens frequently, use the name version instead of the IP address.
Note, however, that this causes a speed hit for every time you want to surf the web or do something on the Internet.
- REJECTING EVERYTHING ELSE
19. Now we block everything else with these three lines:
iptables -A INPUT -p tcp -m tcp --syn -j REJECT
iptables -A INPUT -p udp -m udp -j REJECT
iptables -L
...Note that we use the keyword REJECT here instead of DROP. Later on, we may test with DROP to see if this is possible in your case.
Anyway, the first two statements block everything else that we haven't poked a hole for already and which we did not initiate the transaction. The last line simply lists out the existing iptables because I like it that way.
- TESTING -- THIS IS KEY!
20. Now save this /etc/firewall.sh file and let's run it to see what happens. Do this at command line:
sudo chmod u+x /etc/firewall.sh
sudo /etc/firewall.sh
You should see on your screen the firewall script begin. If you typed something wrong, you'll probably get a nasty note, so go back and edit the file until you see your blunder and change that to what it should properly be. Note also that when it gets to the DNS part in your script, it may take about 2-3 seconds to re-establish this connection before continuing. Therefore, you may see a pause on the screen as it is doing this.
21. Now start testing things that you poked holes for. Here's what you do when it still doesn't work:
a. Check your syntax.
b. Research on the web some port choices you could use to enable that special gizmo you want to poke a hole for. Consider using those.
c. Try using sport instead of dport on the tcp or udp lines.
d. Try commenting it out with #, re-running the script, and seeing if at least the rest of your script works.
e. Post a note to this forum or USENET (groups.google.com is a choice for that) to ask for help.
f. After about 24 hours and no help, start looking on Google for other docs or web-based forums that may have an answer for you. Note also that there are some IRC chats you can hop in for working out firewall issues.
22. Now we need to restrict this firewall even more. I recommend backing up your firewall script with:
sudo cp /etc/firewall.sh /etc/firewall.TEST
23. Now remember those lines where we had a tcp version and a udp version? You may not need to enable both. Therefore, with one port number at a time, comment out one of these, test connections, and then switch to comment out the other other and uncomment the other one out, and test again. This will let you know whether you need both tcp and udp, or just udp, or just tcp poked through. Don't forget that after commenting this out, you must re-run the script with:
sudo /etc/firewall.sh
24. Now, in any case above where I provided more than one set of port numbers, you may not need all those port numbers enabled. Therefore, comment out the tcp/udp set beneath the first one and see if that breaks your connection. For example, here's an option:
iptables -A INPUT -p tcp -m tcp --dport 21 --syn -j ACCEPT
# iptables -A INPUT -p tcp -m tcp --dport 115 --syn -j ACCEPT
# iptables -A INPUT -p tcp -m tcp --dport 989 --syn -j ACCEPT
# iptables -A INPUT -p tcp -m tcp --dport 990 --syn -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 21 -j ACCEPT
# iptables -A INPUT -p udp -m udp --dport 115 -j ACCEPT
# iptables -A INPUT -p udp -m udp --dport 989 -j ACCEPT
# iptables -A INPUT -p udp -m udp --dport 990 -j ACCEPT
In this case, I have enabled only 21 on tcp and udp. I could then test my FTP service to see if remote users can connect on my intranet. Again, don't forget to re-run your script -- simply editing this file won't help you without running it.
25. Last, try changing these lines from REJECT to DROP:
iptables -A INPUT -p tcp -m tcp --syn -j REJECT
iptables -A INPUT -p udp -m udp -j REJECT
...as:
iptables -A INPUT -p tcp -m tcp --syn -j DROP
iptables -A INPUT -p udp -m udp -j DROP
...and see if this works by re-running your script. If it does, great. If not, then stick with REJECT. With DROP, your net card doesn't send a message back to the originator saying, "I am not letting you in." Instead, it drops this and acts more stealth. With REJECT, it says, "I'm here on the network, but I'm blocking you from getting in on that port. Try some other ports to hack on." Can you see the difference in messages and why you would want DROP instead of REJECT? However, in some cases, with some kinds of port traffic, this won't work and you have to use REJECT, unfortunately. Only your test will determine this.
26. Now you need to get this script to fire up when everything else on your PC has concluded loading. The way I do that, instead of /etc/init.d, which is a slightly more complicated discussion, is to call the script from this file:
/etc/X11/gdm/Init/Default
...right after the PATH statement. However, this works in my chosen distro, Ubuntu, and may not work for you in yours. Essentially this script is called when my Ubuntu login screen loads up. All I had to do was add after the PATH statement this line:
/bin/bash /etc/firewall.sh
...and when I reboot and do "iptables -L" at command line, I see it is loaded properly with all my rules.
CONCLUSION
By now, you have built your first firewall script. You should feel happy that you're a little more secure. However, I would suggest you read some other things on the Internet to take this to the next level:
* How do I implement this as a service in /etc/init.d?
* Visit Shields-Up at grc.com (Gibson Research) and use that feature to see if you have exposed ports that you didn't want.
* Use nmap and another Linux workstation on your intranet to see if you have exposed ports that you didn't want. (Never ask a buddy to do this.)
* How do these iptables commands work? man iptables
* What is a FORWARD rule and how do I build one?
* What is IP masquerading?
* How do I block a SYN flood?
* How do I block IP spoofing?
* How do I block the ping of death?
* How do I block a particular IP address range from reaching certain ports?
* How do I share my home workstation's web server over the Internet if I'm behind a DSL/Cable Modem router?
* What are the licensing agreements with NetFilter? (Note, this was a recent problem in the news in late 2005 where someone tried to build a product for sale and bundle it with NetFilter without permission, and they got busted at a technology conference!)
* How do I host VPN connections and build a firewall script to support it?
* How do I build OUTPUT-based firewall rules in combination or in replace of INPUT-based firewall rules, and still keep things from breaking?
* What does lokkit and shorewall do that my script doesn't do? Where do these programs store their iptables scripts?
* Are there people out there on the Internet who can give you totally bogus information about building firewall rules just to hack you, make your life miserable, or tell you stupid stuff because they don't know what they're talking about? Unfortunately, yes, I believe so. I'm not one of them, though. I use this same set of rules for my own systems and I am triple safe because my ISP implements special rules, my firewall/router device has more rules, and then my workstation has the iptables firewall script for even more security. And how did I get my information to teach you this here? I reverse-engineered how lokkit worked by asking a lot of questions and checking the results, then testing how strong my firewall was.