Skip to content

Instantly share code, notes, and snippets.

Last active October 23, 2020 01:29
Show Gist options
  • Save Thermi/e749c8b046195f9c515ffd32a0b0981d to your computer and use it in GitHub Desktop.
Save Thermi/e749c8b046195f9c515ffd32a0b0981d to your computer and use it in GitHub Desktop.

A quick run down on iptables rules

What is it?

iptables is a cli frontend to netfilter, the Linux kernel firewall/nat implementation for OSI layer 3 and 4.

Okay, give me more details

  • When you run "iptables", the actual file being executed is "xtables-multi". That is, because the file name "iptables" is symlinked to "xtables-multi".
  • What is xtables-multi? A binary that contains code for a couple of utilities, like ip6tables, ip(6)tables-restore, ip(6)tables-save

Basic rules

  1. The rules in a chain are applied top to bottom.
  2. The *nat table is only called for connections with ctstate NEW
  3. There are terminating and non-terminating targets in iptables. If a packet hits a terminating target. the rule examination for that packet ends there.
  4. NAT relies on conntrack. Untracked packets can not be natted.
  5. The order of calling for the different chains and tables is defined here (png link)
  6. iptables -L lies. Use iptables-save instead.

Rule structure

iptables rules actually only contain anything after the binary name and the table name. This is how they are referred to in this document. That is, because it corresponds to how the kernel actually stores them and how "iptables-save" prints them.

An iptables rule is structured like this:

<OPERATION> <CHAIN NAME> [builtin matches [builtin matches parameters]] [<-m> <match module> [<match module parameters> ]] [<-j> <TARGET> [<TARGET PARAMETERS>]]

  • <> denote a mandatory parameter
  • [] denote optional parameters
  • You can structure differently, but I strongly suggest you structure them as described above to make it readable and understandable.
  • Anything between <CHAIN NAME> and the "-j" is a selector which restricts which packets are handled by the target. All of its parts have to evaluate to TRUE for the packet to be handled by the target.
  • You can invert certain matches by prepending an exclamation mark (!) to the match. The corresponding section in the man page for iptables-extensions explicitely mention what you can invert.

Rule examples

Rules that would fullfill the structure requirements could look like this:

  1. (in filter table) -A INPUT -i lo -j ACCEPT Other common rules that fullfill this criteria:
  2. (in filter table) -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  3. (in nat table) -A PREROUTING -i eth0 -d -p tcp --dport 443 -j DNAT --to-destination
  4. (in filter table) -A FORWARD -i eth0 -m policy --pol none --dir in -j DROP

Explanation of rule ...

  1. In the INPUT chain, ACCEPT anything where the input interface is "lo".
  2. In the INPUT chain, ACCEPT anything where the match module "conntrack" with the parameters "--ctstate" and "RELATED,ESTABLISHED" evaluate to "true"
  3. In the PREROUTING chain, DNAT anything to the IP, where the input interface is "eth0", the destination is "", the protocol is "tcp" and the destination port is "443".
  4. In the FORWARD chain, DROP anything where the input interface is "eth0" and the "policy" match module with the parameters "--pol", "none" "--dir" and "in".

Interpretation of rule ...

  1. Accept anything from the loopback adapter
  2. Accept any packet that is a reply to a connection, for which conntrack has already seen a reply (basically a connection which conntrack considers established, e.g. a TCP SYN+ACK, when the previous packet was a TCP SYN) or any packet that relates to an already established connection. The "established" in the latter does not refer to the mentioning of the ESTABLISHED parameter before!
  3. DNAT any packet to that is received on eth0, is destined to and is a tcp packet to port 443.
  4. First a little bit of background on the "policy" match module. It matches if the parameter to it are true for this packet. If you're using "-m policy --pol ipsec --dir in", that means that it will match if there is a matching IPsec policy for the INBOUND direction. That would be true if a corresponding IPsec policy exists. Usually IPsec policies have the "required" parameter set. That means that unprotected packets that match the corresponding policy are dropped. You can use the "-pol" parameter to specify if you want this match to be true if there is a policy (--pol ipsec) or if there is no policy (--pol none). Hence this rule drops all packets that arrive on the eth0 interface for which there is no (--pol none) IPsec policy for the INBOUND direction.


What types of packets can I filter using iptables?

"iptables" itself is for IPv4 traffic. There is the equivalent "ip6tables", which is for IPv6 traffic.

How do I load my rules the best way?

Write them in "iptables-save" format and then load them using "iptables-restore". Why? See here (Mirror)

Where's the documentation?

There are man pages for "iptables" and "iptables-extensions". The order of chains and tables is explained [here](http:// .de/images/nf-packet-flow.svg) (png link) and upstream kernel description of sysctl ip options is here

Can I use aliases for interface filters?

Nope. Aliases aren't real interfaces.

What about bridging?

The behaviour of bridging depends on the exact settings. Sometimes, the input and output interfaces that you expect aren't what the kernel actually thinks it is.

Invalid TCP flag filtering?

Conntrack does that for you already. It does not filter explicitely, but such packets are considered "invalid". "-m conntrack --ctstate INVALID" matches on such packets.

Do I need to filter packets from wrong sources, like packets where the source IP is a private IP, but I get them on the interface towards the Internet?

That depends on the family and if you use policy based routing or not. For IPv4, the kernel uses the main routing table to figure out if a packet is a martian (comes from an unexpected direction) or not. If you use policy based routing, that obviously does not work anymore. In that case, you need to disable the rp_filter (reverse path filter) or set it to 2 for the interfaces that take part in the policy based routing. If you do not use policy based routing, the kernel drops martians (packets coming from an unexpected direction) by default already.

For IPv6, you unconditionally need to, because there is no builtin rp_filter for it.

You can use the "rpfilter" match module in iptables to figure out if a packet comes from an unexpected direction. ee the man page for iptables-extensions for details.

Where do I find good rule sets to start with?

Take a look here

How do I log packets?

You can use the LOG target for that. You can send packets to userspace using the NFLOG target and an application listening on the NFLOG group, for example tcpdump or dumpcap.

How do I debug my rule set?

You can either use strategically set rules with the LOG target or use the TRACE target in the *raw table to get a line in the kernel log for every chain policy or rule that matched the packet.

I don't get any packets printed in dmesg/kern.log/journalctl when the TRACE/LOG target is hit. What's wrong?

Load the nf_log_ipv4 and nf_log_ipv6 kernel modules. They provide the logging capability for the respective IP protocol versions. After that, you should see the packets in the logs.

What IS conntrack?

Conntrack is first and foremost a subsystem of the netfilter system of the kernel which tracks all sorts of packets and interprets them as connections, to which packets are correlated against. It gives you the essential component to build stateful firewall rules. It is also the name of a match module, which uses the kernel subsystem to figure out the state of the connection the packet belongs to.

Can I do if-else statements?

Yes, use custom chains for that and call them, like you would a target. You return from custom chains by using the RETURN target or by reaching the end of the custom chain.

Why should I restrict the MASQUERADE and SNAT targets to an interface?

Because otherwise, they will MASQUERADE and SNAT traffic over the loopback adapter with the source IP the main routing table uses for the best fitting route (probably the default route in that case). That breaks any service on localhost that checks the source IP, like email spam filters or other software.

Copy link

mk-pmb commented Aug 5, 2017

I fixed the angle brackets in the rule syntax part, and a typo. I can't find the pull request button, hope it helps anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment