paulgorman.org/technical

Linux iptables

Iptables is a linux firewall.

See iptables(8) and iptables-extensions(8).

Iptables uses the Netfilter framework. The Netfilter framework provides an interface to the stages of the Linux kernel’s networking stack. Netfilter exposes each stage to iptables with a hook. Iptables pre-defines five chains, each chain corresponding to one of the hooks:

This diagram represents packet flow through the hooks:

                                       Local
                                      process
                                        ^  |      .-----------.
               .-----------.            |  |      |  Routing  |
               |           |---> INPUT /    \---> |  Decision |---> OUTPUT \
PREROUTING --->|  Routing  |                      .-----------.             \
               | Decision  |                                                  --> POSTROUTING
               |           |                                                 /
               |           |-------------> FORWARD --------------------------
               .-----------.

In addition to the built-in chains, users may define custom chains.

Rules attach to chains. A chain holds a list of rules to test packets against. Each rule specifies a criteria and a target. A target can be a verdict like ACCEPT or DROP, or one of the special targets described in iptables-extensions(8) like CT or LOG.

Iptables tests a packet against each rule in a chain, from top to bottom, until a rule matches with a final verdict. Note that one or more rules with non-final targets might match the packet before it hits a rule with a final verdict. A rule match may cause a “jump” to another chain, for example.

Iptables is a first-match-wins firewall (unlike PF where the last matching rule applies). Evaluation of rules ends once a packet matches a rule with a target like ACCEPT or DROP.

The default policy applies if the packet doesn’t match any earlier rule.

Tables group rules according to the type of decisions they make. E.g., a rule that deals with network address translation goes in the nat table. Each tables may contain several built-in or user-defined chains. A chain may be included in multiple tables. Not every table has a chain for every hook.

Systems often predefined these five tables, depending on kernel configuration and loaded modules:

When dealing with an individual box, rather than a firewall or router, most rules fall into the filter table.

Basic Commands

# iptables -t nat -vL

Where a chain is not specified, the filter chain is generally shown by default. To see all chains, run iptables-save.

Where iptables mentions “in” or “in-interface”, it means the interface on which a packet was received. Where iptables mentions “out” or “out-interface”, it means the interface from which a packet will depart.

List all chains, or the rules in the named chain:
iptables -L, --list [chain]

Delete all the rules, or all the rules in the named chain:
iptables -F, --flush [chain]

Zero-out counters:
iptables -Z, --zero [chain [rulenum]]

Create chain “chain”:
iptables -N, --new-chain chain

Delete all user-defined chains or named chain:
iptables -X, --delete-chain [chain]

Examples

-n is numeric output, without DNS lookups:
sudo iptables -nvL

Line numbers can be used to delete rules or insert new rules:
sudo iptables -nvL --line-numbers

Delete rule four from INPUT chain:
iptables -D INPUT 4

Change default policy to DROP:
sudo iptables --policy INPUT DROP

Allow return traffic from connection we establish:
sudo iptables -I INPUT 1 -m state --state ESTABLISHED,RELATED -j ACCEPT

Allow incoming HTTPS connections:
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

Allow responses for mtr:
sudo iptables -A INPUT -p icmp --icmp-type 11 -m state --state ESTABLISHED,RELATED -j ACCEPT

ALLOW pings:
sudo iptables -A INPUT -p icmp --icmp-type 8 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

Allow all localhost connections (on interface lo):
sudo iptables -A INPUT -i lo -j ACCEPT

Otherwise, we can open localhost ports one at a time, like:
sudo iptables -A INPUT -p tcp -i lo --dport 631 -j ACCEPT

Allow local connections to CUPS:
sudo iptables -A INPUT -p udp -i lo --dport 631 -j ACCEPT

COMMENTS!! It’s easy to comment rules, and the comments appear in iptables -L output:

sudo iptables -A INPUT -p tcp --dport 8888 -j ACCEPT -m comment --comment "Allow my dumb web app."

(Use double-quotes for comments; single-quotes don’t work.)

Safe Rules Testing

Use iptables-apply(8) to test a rule set without risk to accidental lockout:

$ sudo iptables-apply -t 90 test_rules.v4

If we fail to respond to the confirmation prompt by the timeout, iptables-apply rolls back to the previous (working) rules.

Making Rules Persist

# mkdir /etc/iptables
# iptables-save > /etc/iptables/rules.v4
# iptables-restore < /etc/iptables/rules.v4
# ip6tables-save > /etc/iptables/rules.v6
# ip6tables-restore < /etc/iptables/rules.v6
# cat << 'EOF' > /etc/network/if-pre-up.d/iptables
#!/bin/bash
/usr/sbin/iptables-restore < /etc/iptables/rules.v4
/usr/sbin/ip6tables-restore < /etc/iptables/rules.v6
EOF
# chmod 0755 /etc/network/if-pre-up.d/iptables

# cat << 'EOF' > /etc/iptables/rules.v6
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [700:49000]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
COMMIT
EOF

Or, instead of the above shell script, install the Debian package “iptables-persistent”, which will handle loading any rules found in /etc/iptables/rules.v4 and /etc/iptables/rules.v6. I think the package does basically the same as the above, but it might be more future-proof.

Check the validity of the config file:

# iptables-restore --test /etc/iptables/rules.v4

Examples:

--- dev !80 ~ %  cat /etc/iptables/rules.v4
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p udp --dport 60000:61000 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp --icmp-type any -j ACCEPT
-A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT
COMMIT

--- dev !81 ~ %  cat /etc/iptables/rules.v6
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [2:144]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p udp --dport 60000:61000 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
COMMIT

State

Not sure if there’s a better way than this, but view current states with:

%  sudo cat /proc/net/nf_conntrack

The output:

If available, the conntrack utility allows us to manually add and delete state entries (and tail changes in real time). If we want to flush the state table for some reason:

%  sudo conntrack -F

Packet Flow

See the diagram at http://www.iptables.info/files/tables_traverse.jpg (from the page http://www.iptables.info/en/structure-of-iptables.html).

INPUT, FORWARD, and OUTPUT are separate. A packet only hits one of the three hooks.

A packet destined for this box hits INPUT. A packet sourced from this box hits OUTPUT. A packet with a source and destination that are not this box hits FORWARD (i.e. this box routes the packet from an outside address to an outside address).

A packet hits each rule on each chain on the appropriate hook until it matches a verdict. So, because the chains on a hook may be grouped into multiple tables, the packet may cross multiple tables. E.g., a packet may well hit the raw then mangle then nat tables, probably followed by the filter table.

The short and somewhat simplified version:

IPv6

iptables is for IPv4. See ‘ip6tables’ for IPv6.

The syntax for ip6tables is very similar to that of iptables.

# ip6tables --policy INPUT DROP
# ip6tables -A INPUT -i lo -j ACCEPT
# ip6tables -A INPUT  -p ipv6-icmp -j ACCEPT
# ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# mkdir -p /etc/iptables
# sh -c "ip6tables-save > /etc/iptables/rules.v6"

ebtables for ARP

ebtables works on arp traffic. It’s similar to iptables. See man ebtables.

Connection Tracking

Netfilter provides connection tracking capabilities, exposed through the CT and conntrack iptables extensions.

Connection tracking filters packets based on criteria that IP header information alone can not provide. In other words: stateful firewalling. Connection tracking keeps facts about a connection — its source and destination addresses, protocol, ports, timeout, etc. A connection may have one of the following states:

These states have nothing to do with TCP states; even UPD connection can be stateful in the sense of connection tracking.

Connection tracking works primarily at layer 3, although some of the modules operate at higher layers.

Connection tracking facilitates some application-layer protocols with hard-to-track properties, like FTP. A connection tracking “helper” has a set of expectations about the properties of connections. The FTP helper expects that, within a given time and from a given source and destination, that a passive FTP connection will open a second high-number port for data transmission. The helper inspects packet contents in order to find the necessary information. The helper is application-aware. In the case of FTP, the helper digs through packet payloads looking for the PORT reply from the server to the client. When its expectations are met, the helper establishes a new state.

Helpers exist for IRC, SIP, SNMP, H323, etc.

Thu Oct 26 12:34:04 EDT 2017