Hacking Linux Exposed

About
Authors
Contents
Reviews
Foreword
Purchase

Articles
Books
Sourcecode
Tools
Errata

Home

 


previous article
index
next article
Ten minute host firewall, Part 1
By Bri Hatch.

Summary: Create a simple but effective host firewall for your machine in ten minutes or less.

One of my friends finds himself in a very annoying situation: he started a new job and now has a Windows machine on his desk. Worse yet, he's not allowed, by corporate policy, to wipe it clean and install Linux on it "for security reasons". Being that we both live up here in Seattle, close to the belly of the beast itself, it's not surprising that the Microsoft FUD machine is strong.

The IT department grudgingly agreed that he could use Linux if he had an adequate firewall installed. They intend to port scan him and, if all ports refuse inbound connections, they'll consider his machine secure.

Now this is not my definition of a very secure firewall.[1] However it is immeasurably better than no firewall rules at all, and it takes no more than a few minutes to set it up.

First, let's assume we're on a 2.4 kernel that has Netfilter (iptables) support compiled in. I'll assume you're using a 2.4 kernel for these examples, but I've restricted myself to commands that can be munged into their ipchains counterparts, meaning I've ignored Netfilter's ability to do connection tracking and the like. If you want to create a more secure firewall, then there are various pre-canned firewall scripts you can use, or you can read up on Netfilter's finer details online or in books such as Ziegler's "Linux Firewalls".

In this minimal setup, we want to allow all outbound access, and deny all inbound access that isn't in response to one of our outbound connections. This is surprisingly easy to manage.

We'll be adding rules to the INPUT chain. First, let's make the default rule (the 'policy') be to to DROP packets. This throws away the packet without informing the sender.

 # iptables -P INPUT DROP

You now have a very secure machine - nothing can get in. Perhaps we should open things up a bit...

There are three protocols we'll want to support, TCP, UDP, and ICMP. TCP is the IP protocol that does the majority of the work on the Internet. It's a connection-based protocol that underlies all the major services, such as web (HTTP/HTTPS), mail (SMTP, POP, IMAP), remote login (SSH), and file transfer (FTP).

Any TCP connection has the oft-mentioned "three way handshake". The machine that wants to create a connection sends a packet to the destination with the SYN (synchronise) flag set. The server responds with a packet that has both SYN and ACK (acknowledge) set, and thereafter the SYN bit is never seen again. If we configure our firewall ACLs to drop any packets which contain the SYN bit without that ACK, we can prevent the first packet of TCP connections from reaching our machine, and no TCP connections can be created at all.

Iptables[2] can selectively block packets based on the SYN flag using the --syn flag, for example:

  # iptables -A INPUT -p tcp ! --syn -j ACCEPT

Voila! No machines can connect to your machine with TCP, but you can make outbound connections and the associated packets will be allowed back in. You can't be a server (no SSH to your machine, for example) but all outbound TCP stuff should work fairly well.

Well, that's true about most 'well behaved' TCP-based protocols. Some protocols like to use more than one port. FTP is the classic example of a bastardly-designed protocol. Each time you type 'get', 'put', or 'ls' a new TCP connection is initiated to snag the data.

Note also that even though outsiders can't connect to your TCP ports, scanning tools such as Nmap will be able to see that the port is open if they are used in any mode other than straight TCP connection scanning. Newer Nmap versions use stealth scanning by default when run as root.

If the protocol you use wants to have the client (your newly firewalled machine) make an additional connection to the server, then your --syn firewall won't be a problem. However if it wants to have the server contact the client, these connections will be denied.

Protocols that create these ephemeral side connections include many P2P protocols, and most notably FTP. Most of these offer both a 'client connects to server' and 'server connects back to client' option. In the case of FTP, the former is called "Passive", while the latter is called "Active". Active was the original (really annoying for firewalls and proxies) model, and Passive has been favoured for some time now. Most FTP clients let you choose Passive if it's not already the default. For example typing pftp instead of ftp on the command line, or including --passive-ftp as an argument to wget.[3]

Now, to be a bit more restrictive, let's block UDP as well. UDP is a connectionless protocol, so it's harder to proxy and firewall. However it's also less commonly used. DNS is the the most important UDP protocol. Without DNS you're going to need to memorise many IP addresses, so we best open this up.

DNS servers live on UDP port 53.[4] Our machine will need to send packets to port 53 on one or more DNS servers, and receive packets that come from port UDP 53. So, restricting on the source port (the port on the DNS server) you'd have

  # iptables -A INPUT -p udp --source-port 53 -j ACCEPT

This will allow any DNS servers to reply to you, which may be helpful if you get your DNS servers from DHCP and want to be lazy. If you want to hard code your DNS server's IP addresses to be most restrictive, you can use the following

  # iptables -A INPUT --source DNS.SVR1.IP.ADDR -p udp --source-port 53 -j ACCEPT
  # iptables -A INPUT --source DNS.SVR2.IP.ADDR -p udp --source-port 53 -j ACCEPT

If your machine needs to get an IP address from the network using BOOTP or DHCP, then you'll need the following rule as well:

  # iptables -A INPUT -p udp --destination-port 68 -j ACCEPT

Any packets that aren't for DNS or BOOTP/DHCP will be dropped by the INPUT chain's policy.

What services are likely to brake when you're blocking UDP? Most streaming media uses UDP for it's highest-speed connections, but may offer TCP based connections as well. Filesharing protocols such as NFS may die, but again may offer TCP versions that get around this limitation. It all depends on your setup.

Now we haven't listed which interfaces these rules apply on. If you want them to apply to all interfaces, this is probably good. However you should allow packets that go on the localhost interface to work universally, lest you break some daemons that expect to be able to communicate within your machine, or features like SSH port forwarding. So, as the very first rule, you should include the following:

  # for interface in /proc/sys/net/ipv4/conf/*/rp_filter
      do
          echo 1> $interface
      done

  # iptables -A INPUT -i lo -j ACCEPT

The iptables rule says "allow any packets that come into localhost without any filtering". The for loop sets Reverse Path filtering on all interfaces, which will block packets that come in from the wrong interface.[5]

Lastly, we can lock down allowed ICMP packets, which are used in conjunction with TCP and UDP protocols to indicate error conditions and the like. Here I'll be a bit more permissive than may be necessary in order to fit the needs of a wider audience. To get a full list of possible ICMP codes, run iptables -p icmp --help. We'll allow the following types for greatest network stability:

 # iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type source-quench -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type parameter-problem -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type redirect -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type router-advertisement -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
 # iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT

You may wish to exclude some of these, such as echo-request (prevent your machine from being pinged), redirect and router-advertisement (if you have only one router, and no dynamic routes.)

Next week I'll bring this all together in one big script and show you how to let these rules be created at reboot automatically.[6]

NOTES:

[1] I much prefer to have my firewall rules explicitly define what packets are allowed to go in *and go out*. This helps block outbound access that could be created by trojans, viruses, or evil hax0rs.

[2] Iptable's predecessors and other firewall/filtering products have also used this trick for years.

[3] Netfilter has modules that you can load as well to support specific protocols such as FTP or IRC that do wacky things like this.

[4] They may also listen on TCP port 53 for larger queries and DNS zone transfers, but outbound TCP is already covered by our previous --syn rule.

[5] For more information about this and other helpful /proc interface files, see Firewalling proc entries

[6] I understand the irony of having a 10 minute firewall discussion spread out over two weeks...


Bri Hatch is Chief Hacker at Onsight, Inc and author of Hacking Linux Exposed and Building Linux VPNs. In the rare times where he gets uninterrupted time to write, he never seems to stop. The muses smiled upon him today. Bri can be reached at bri@hackinglinuxexposed.com.


Copyright Bri Hatch, 2003


This is the July 03, 2003 issue of the Linux Security: Tips, Tricks, and Hackery newsletter. If you wish to subscribe, visit http://lists.onsight.com/ or send email to Linux_Security-request@lists.onsight.com.