Block unsolicited inbound network traffic using iptables
Content |
Tested on |
Debian (Lenny) |
Ubuntu (Precise, Trusty) |
Objective
To block unsolicited inbound network traffic using iptables, without blocking return traffic associated with outbound connections
Background
Most machines that are connected to the Internet have no need to act as a server. This provides the opportunity for a large class of undesirable network traffic to be blocked using a firewall without interfering with the normal operation of the machine. To do that, the firewall must be able to distinguish between solicited and unsolicited inbound traffic.
For TCP-based protocols there are two types of inbound packet that need to be let through:
- those which form part of an outbound connection (such as the response to an outbound HTTP request), and
- those which form part of an inbound connection that is related to an outbound connection (such as a return connection from an FTP server in response to a download request).
The first case is handled generically by the IPv4 connection tracking module. The second case requires the use of a helper module that understands the specific protocol in question (for example, nf_conntrack_ftp
for FTP).
UDP is a connectionless protocol, therefore deterministic connection tracking like that done for TCP is not possible. What the connection tracking module does instead is assume that datagrams are related to each other if they use the same addresses and port numbers within a defined time interval (typically 30 seconds). This avoids the need for every UDP-based protocol to be explicitly supported by a helper module.
ICMP is a special case because it is an integral part of the Internet Protocol which all hosts are supposed to support. It can be argued that blocking ICMP provides some minor security benefits, however in most situations this is likely to cause more inconvenience to legitimate users and administrators than it would to a potential attacker. For this reason, the method described here allows inbound ICMP traffic even if it is unsolicited.
Scenario
Suppose that you wish to firewall a machine that is used only as a workstation. It is not required to provide any network services to other machines.
(For machines that are intended to act as servers the firewall described here cannot be used as it stands, but it can be used as a starting point if further rules are added to open specific inbound ports.)
Prerequisities
These instructions assume that you have a working installation of iptables and some means of persisting the ruleset. They also assume that the INPUT
chain of the filter
table is initially empty. If this is not already the case then it can be emptied by flushing that particular chain:
iptables -t filter -F INPUT
or by flushing the whole of the filter
table:
iptables -t filter -F
Method
Configure iptables
The required effect can be achieved using the following ruleset:
iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -t filter -A INPUT -p icmp -j ACCEPT iptables -t filter -A INPUT -i lo -j ACCEPT iptables -t filter -A INPUT -j REJECT
The first rule accepts solicited inbound traffic by inspecting the state assigned by the connection tracking module. Packets are accepted when the state is:
-
ESTABLISHED
(packets forming part of an existing connection that the firewall has previously decided to allow) or -
RELATED
(packets that would start a new connection, but are known to be related to an existing connection).
Packets are not accepted by this particular rule if they have a state of:
-
NEW
(packets that would start start a new connection and are not known to be related to any existing connection) or -
INVALID
(packets that cannot be tracked because they are malformed in some way).
The second rule accepts all ICMP traffic, including unsolicited inbound traffic, for the reason given above.
The third rule accepts all traffic from the loopback interface. In this case that has been done by filtering on the interface name, but it would be equally acceptable to filter on the IP address (127.0.0.1). Although this is implemented as a special case, it is not an exception to the stated policy: any traffic received through the loopback interface must have originated on the local machine, therefore (by one means or another) it must have been solicited by the local machine.
The fourth rule blocks all traffic that has not already been accepted by one of the first three rules. There is a choice to be made here between the REJECT
target and the DROP
target:
-
REJECT
causes connections to be explicitly rejected by issuing an ICMP port unreachable message. This is very useful for troubleshooting, because it is not otherwise possible to distinguish between a network failure and the effect of a firewall rule. -
DROP
has the security advantage of making port scans more difficult.
Load any required connection tracking modules
If you wish to use any protocols that require a helper module for connection tracking then the relevant modules will need to be loaded. This can
be done non-persistently using the modprobe
command, for example:
modprobe nf_conntrack_ftp
You should also arrange for the modules to be re-loaded at boot time. On Debian-based systems this can be done by listing them in the file /etc/modules
.
The module names are of the form nf_conntrack_ftp
from version 2.6.20 of the kernel onwards. Prior to this they are of
the form ip_conntrack_ftp
.
Testing
The firewall configuration can be tested by attempting to make connections from another machine. An easy way to do this is by using the network
scanning tool nmap
:
nmap -sS -sU -p 0-65535 workstation1.example.com
This will check all TCP and UDP ports. Be warned that this can be quite time-consuming if you have chosen to drop unwanted traffic (as opposed to rejecting it).
To ensure that the traffic is being blocked by the machine under test (and not by any intermediate device) it may be useful to scan with the firewall both enabled and disabled. If necessary you could temporarily open a port using netcat to provide something for the scan to find. Do not do this when connected to the public Internet unless you are confident that the machine will survive unscathed.
Where practicable it is a good idea to check that the new configuration survives a reboot, otherwise there is a risk that when the machine does reboot it will return to operation in an unprotected state.
Variations
Allowing inbound connections on specific ports
The firewall described here is not directly suitable for use on a server, but as noted above it can be used as a starting point. You should add an ACCEPT
rule for each port that needs to be left open, placing it prior to the final REJECT
or DROP
rule. For example, if you were running a DNS server then you could enable TCP and UDP connections to port 53 by adding the following pair of rules:
iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -t filter -A INPUT -p icmp -j ACCEPT iptables -t filter -A INPUT -i lo -j ACCEPT iptables -t filter -A INPUT -p tcp --dport 53 -j ACCEPT iptables -t filter -A INPUT -p udp --dport 53 -j ACCEPT iptables -t filter -A INPUT -j REJECT
Alternatives
Avoid running unnecessary services
By far the most effective way to prevent unwanted connections is to avoid running unnecessary network services in the first place. If there is no process listening on a given TCP port then the network stack will unceremoniously reject any inbound connection attempts (typically by issuing an RST). Inbound UDP datagrams will be similarly rebuffed (typically with an ICMP destination port unreachable message).
The main drawback of using this policy as the sole line of defence is the difficulty of enforcing it on a long-term basis. You can determine what ports are open at a given moment in time using the netstat
command:
netstat -lntu
However this does nothing to prevent ports from being opened in the future. A firewall provides a single point of control, and cannot normally be changed by unprivileged users.
Avoid binding to the external network interface
Most network services provide a method for configuring which address they bind to when listening for connections. Normally this would be either:
- the wildcard address (0.0.0.0), which binds the service to all interfaces and allows connections from anywhere, or
- the loopback address (127.0.0.1), which binds the service to the loopback interface and allows local connections only.
If you choose the second option then the effect, so far as external connections are concerned, is as if the service were not running.
Avoid giving the machine a public IP address
From a security perspective, the use of network address translation (NAT) has an almost identical effect to the firewall described here. It has the added benefits of failing safe (without NAT all traffic is blocked) and conserving public IP addresses.