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:
PREROUTING
(packets first hit PREROUTING before any routing decision)INPUT
(packets destined for the local system hit INPUT)OUTPUT
(packets that originate from the local system hit OUTPUT)FORWARD
(packets not destined for the local system hit FORWARD)POSTROUTING
(packets leaving the systems hit POSTROUTING after the routing decision)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:
filter
is the default table. It includes the INPUT
, FORWARD
, and OUPUT
chains.nat
for NATingmangle
for specialized packet alterationraw
for packet marking and connection trackingsecurity
for MAC (mandatory access control). Used by modules like SELinux.When dealing with an individual box, rather than a firewall or router, most rules fall into the filter
table.
# 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]
-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.)
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.
# 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
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
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:
A packet arrives from the network.
It’s evaluated against the raw table PREROUTING chain rules.
It’s evaluated against the mangle table PREROUTING chain rules.
It’s evaluated against the nat table PREROUTING chain rules.
A “routing decision” happens: the packet either goes to INPUT or FORWARD or OUTPUT.
For FORWARD (generally traffic that uses this host as a router), the packet goes to:
For INPUT (generally traffic destined to this host), the packet goes to:
For OUTPUT (generally traffic originating from this host), the packet goes to:
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
works on arp traffic. It’s similar to iptables. See man ebtables
.
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