Mastering Linux Security and Hardening
上QQ阅读APP看书,第一时间看更新

Mastering the basics of iptables

iptables consists of five tables of rules, each with its own distinct purpose:

  • Filter table: For basic protection of our servers and clients, this might be the only table that we use.
  • Network Address Translation (NAT) table: NAT is used to connect the public internet to private networks.
  • Mangle table: This is used to alter network packets as they go through the firewall.
  • Raw table: This is for packets that don't require connection tracking.
  • Security table: The security table is only used for systems that have SELinux installed.

Since we're currently only interested in basic host protection, we will only look at the filter table for the time being. (In a few moments, I'll show you a couple of fancy tricks that we can do with the mangle table.) Each table consists of chains of rules, and the filter table consists of the INPUT, FORWARD, and OUTPUT chains. Since our CentOS machines use Red Hat's firewalld, we'll look at this on our Ubuntu machine.

While it's true that Red Hat Enterprise Linux 7/8 and their offspring do come with the iptables service already installed, it's disabled by default so that we can use firewalld. It's not possible to have both the iptables service and the firewalld service running at the same time, because they're two totally different animals that are completely incompatible. So, if you need to run the iptables service on a Red Hat 7/8 system, you can do so, but you must disable firewalld first.

However, if your organization is still running its network with version 6 of either Red Hat or CentOS, then your machines are still running with iptables, since firewalld isn't available for them.

First, we'll look at our current configuration by using the sudo iptables -L command:

 donnie@ubuntu:~$ sudo iptables -L
[sudo] password for donnie:
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
donnie@ubuntu:~$

Remember that we said that you need a separate component of iptables to deal with IPv6? Here, we'll use the sudo ip6tables -L command:

 donnie@ubuntu:~$ sudo ip6tables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
donnie@ubuntu:~$

In both cases, you can see that there are no rules and that the machine is wide open. Unlike the SUSE and Red Hat folk, the Ubuntu folk expect you to do all the work of setting up a firewall. We'll start by creating a rule that will allow us to pass incoming packets from servers that our host has requested a connection to:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Here's the breakdown of this command:

  • -A INPUT-A places a rule at the end of the specified chain, which in this case is the INPUT chain. We would have used -I had we wanted to place the rule at the beginning of the chain.
  • -m: This calls in an iptables module. In this case, we're calling in the conntrack module to track connection states. This module allows iptables to determine whether our client has made a connection to another machine, for example.
  • --ctstate: The ctstate, or connection state, portion of our rule is looking for two things. First, it's looking for a connection that the client established with a server. Then, it looks for the related connection that's coming back from the server in order to allow it to connect to the client. So, if a user were to use a web browser to connect to a website, this rule would allow packets from the web server to pass through the firewall to get to the user's browser.
  • -j: This stands for jump. Rules jump to a specific target, which in this case is ACCEPT. (Please don't ask me who came up with this terminology.) So, this rule will accept packets that have been returned from the server that the client has requested a connection to.

Our new ruleset looks like this:

 donnie@ubuntu:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
donnie@ubuntu:~$

Next, we'll open up port 22 so that we can connect through Secure Shell:

sudo iptables -A INPUT -p tcp --dport ssh -j ACCEPT

Here's the breakdown:

  • -A INPUT: As before, we want to place this rule at the end of the INPUT chain with -A.
  • -p tcp-p indicates the protocol that this rule affects. This rule affects the TCP protocol, which Secure Shell is a part of.
  • --dport ssh: When an option name consists of more than one letter, we need to precede it with two dashes, instead of just one. The --dport option specifies the destination port that we want this rule to operate on. (Note that we could have also listed this portion of the rule as --dport 22 since 22 is the number of the SSH port.)
  • -j ACCEPT: If we put this all together with -j ACCEPT, then we have a rule that allows other machines to connect to this one through Secure Shell.

Now, let's say that we want this machine to be a DNS server. For that, we need to open port 53 for both the TCP and the UDP protocols:

sudo iptables -A INPUT -p tcp --dport 53 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT

Finally, we have an almost complete, usable ruleset for our INPUT chain:

 donnie@ubuntu:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate
RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
DROP all -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
donnie@ubuntu:~$

However, this is only almost complete, because there's still one little thing that we forgot. That is, we need to allow traffic for the loopback interface. This is okay because it gives us a good chance to see how to insert a rule where we want it if we don't want it at the end. In this case, we'll insert the rule at INPUT 1, which is the first position of the INPUT chain:

sudo iptables -I INPUT 1 -i lo -j ACCEPT
Before you inserted the  ACCEPT rule for the lo interface, you may have noticed that sudo commands were taking a long time to complete and that you were getting sudo: unable to resolve host. . .Resource temporarily unavailable messages. That's because sudo needs to know the machine's hostname so that it can know which rules are allowed to run on a particular machine. It uses the loopback interface to help resolve the hostname. If the lo interface is blocked, it takes longer for sudo to resolve the hostname.

Our ruleset now looks like this:

donnie@ubuntu:~$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
ACCEPT tcp -- anywhere anywhere tcp dpt:domain
ACCEPT udp -- anywhere anywhere udp dpt:domain

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
donnie@ubuntu:~$

Note how port 53 is listed as the domain port. To see port numbers instead of port names, we can use the -n switch:

donnie@ubuntu3:~$ sudo iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
donnie@ubuntu3:~$

Now, as things currently stand, we're still allowing everything to get through, because we still haven't created a rule that blocks what we haven't specifically allowed. Before we do that, though, let's look at a few more things that we might want to allow.