Iptables

From Helpful

These are primarily notes
This is probably not going to be complete in any real sense, and exists to contain bits of useful information.

See also Networking notes.

Contents

Organization, operation

Packet paths

This article/section is a stub — probably a pile of half-sorted notes and assertions some of which may well be wrong, and not verified as a whole. Feel free to add or refine.

The short story

Some of us never really need to know more than that packets usually follow one of the following routes:

  • network → PREROUTING → INPUT → local process (data coming in for your apps)
  • local process → OUTPUT → POSTROUTING → network (data going out, from your apps)
  • network → PREROUTING → FORWARD → POSTROUTING → network (data passing through)

This means INPUT, OUTPUT, and FORWARD are usually the interesting chains, while PREROUTING and POSTROUTING are there for doing more complex things than the filtering that most people use it for. The more complex things include packet alteration for fun, profit, routing, NAT, and other such things.


Networking has to be fast, so rules are actually in very simple to evaluate binary form - they usually consist of little more than a few bitmasks and comparisons. (Speed is also served with a "use only the first thing in the list of rules that applies, nothing else," logic (...that you also see in many ACL systems, and like them, may be simple or used in more convoluted ways))

Some of the more complex rules (e.g. l7 stuff) takes a little longer, so if you care about speed you'll usually place them after simpler rules that are also more likely to apply to more packets.


A slightly longer story; Tables and chains from a few perspectives

This article/section is a stub — probably a pile of half-sorted notes and assertions some of which may well be wrong, and not verified as a whole. Feel free to add or refine.

When you want to do fancier things, you want to know about the structure of things.


Netfilter/iptables largely consists of:

  • tables (e.g. filter, nat, mangle, conntrack), which have a set of...
  • chains (a fixed subset of PREROUTING, INPUT, FORWARD, OUTPUT, and POSTROUTING), which in turn each consist mostly of a list of...
  • rules belonging to each chain. Each rule tends to represents a single case of filtering, alteration or such. Many have the function of throwing a packet over to another chain.


Actually, the chains mentioned above are just the ones important to getting packets in, out, and past, and in an understandable way.

They can apparently be organized in any manner(verify), but rarely seem to deviate much from the images you see around various explanations. To add yet another of those images:

first version; probably contains mistakes


There are some limitations of what type of rules are accepted in what chains in what tables, although half of the things that you could theoretically do make no real sense, you wouldn't even think of, or are rarely done because they're unnecessarily complex things of getting things done and/or may make behaviour very hard to predict. As such, most configurations are simple enough to understand after a little practice.


Tables are mostly for organization. Each table is a runtime-loadable module - you can assume you always have filter (also the default for iptables commands), which is also all you need for an incoming-packet firewall.

You can plug in tables like nat, mangle, and conntrack at will. These tables rarely introduce new chain names (though they rarely use all of them), which keeps configuration simple.


On the common tables:

  • conntrack
    • only applies in PREROUTING and OUTPUT, and can't really be controlled direcltly (verify)
  • mangle
    • PREROUTING, INPUT,
    • supports MARK, TOS, TTL targets
    • mangle/PREROUTING is used for things like altering TOS
    • mangle/INPUT is for mangling the packet just before it goes to the process -- not often a critical feature.
  • nat
    • PREROUTING, POSTROUTING, OUTPUT
    • often for DNAT (in PREROUTING), SNAT (in POSTROUTING)
  • filter
    • mostly used to filter out packets in the stages INPUT, FORWARD, and/or OUTPUT. (You can do it in other tables, but this tends to be unnecessary)



In terms of the places that packets go and what happens to them, there are roughly three paths that packets can take through the system (as already mentioned). Within each chain in a path, each table could apply. That is, any table may have that chain, and if it does, it will be used. (in a predefined order, apparently conntrack, mangle, nat, filter (unsure -- (verify)))

The three packet paths in a little more detail:


  • packets generated by a local process go via OUTPUT to POSTROUTING, often:
    • mangle/OUTPUT (often unused)
    • nat/OUTPUT
    • filter/OUTPUT (if you think this useful)
    • mangle/POSTROUTING
    • nat/POSTROUTING (mostly for SNAT, MASQUARADE)


  • packets destined for a local process go via PREROUTING and INPUT to that process
    • mangle/PREROUTING
    • nat/PREROUTING (mostly for DNAT)
    • mangle/INPUT
    • filter/INPUT (the place where most firewalling for this node happens)
  • packets routed through this node go from PREROUTING to FORWARD to POSTROUTING and onto some network again, often specifically:
    • mangle/PREROUTING
    • nat/PREROUTING (mostly for DNAT)
    • mangle/FORWARD (only necessary if you want to mangle in a way that affects routing decisions in the path -- otherwise it can go into another mangle step)
    • filter/FORWARD
    • mangle/POSTROUTING
    • nat/POSTROUTING (mostly for SNAT, MASQUARADE)


(note that PREROUTING can divert a packet to FORWARD through DNATting, in which case it's actually the next case...). As such, there is only a difference after PREROUTING -- there is a routing decision there that checks whether the packet is targeted at this node or not, and sends it to to INPUT or FORWARD accordingly.


Note also that in promiscuous mode, all packets are received, not just those for us or passing through us.



Managing rules

Useful command types:

  • -I: Insert on top of list
  • -F: flush all, or a chain
  • -A: Append to end of list
  • -D: Delete rule by rule number (1-based) in a specific chain, or by repeating the exact rule, so either:
    • iptables -D INPUT 7
    • iptables -D INPUT -s 192.168.0.0/255.255.0.0 -p udp -m udp -m multiport --dports 135,137,138,139,445 -j ACCEPT
  • -L: list chain contets (you may need to specify a table too(verify))


Most operating systems will do some work around shutdown and bootup so that rules persist over reboots. They usually use iptables-save and iptables-restore, which serializes and overwrites the current state of the tables.

I personally prefer doing the same thing over worrying over -I and -A. After one

iptables-save > /etc/savetable

...I have the convenience of, whenever I want to change the firewall, editing that file and running

iptables-restore /etc/temptable

to make the rules current. (The tables are (by default) flushed by iptables-restore before inserting the rules, so this is an idempotent thing. The -A lines are more or less stuck on iptables verbatim, while the other lines are organisation, the bracketed numbers seem to be packet and byte counters - I'm not sure whether they're restored.

The handy side effect is that you always have a record of it in case your system gets forgetful, or for when you quickly want to pick out rules for another firewalls on.

Note that your distribution may have some automatic, easily configurable firewalling program - that may also overwrite whatever you just changed whenever it wants to and may or may not consider its own rules to be the master set. If so, use that instead - in general, change the rules at the easiest-to-administer and most auhoritative place.

Rule format (the intersting bit)

Rules look something like like:

iptables -A INPUT -i eth0 -j ACCEPT
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 6001 -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.0.0/255.255.255.0 -o eth0 -j SNAT --to-source 123.45.66.77
iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --sport 21 -j MARK --set-mark 0x4

The first three are filtering rules (the first two of which are very similar in effect), the last two are more complex.

In simple firewalls it's fairly common to match on the fact that something is a tcp or udp packet, often in combination with the ports involved. In an INPUT rule, destination means us, and dport indicates the local service the connection is trying to access, so source address and destination is what you most commonly filter on.


Anyway, let's pick apart the rule structure and list common components.

When creating chain contents, each rule has:

  • -t table. It's 'filter' by default, so can be omitted on firewall rules
  • -A chain to append to a chain (or -I to insert, possibly -R to replace)
  • -j: what target to jump the packet to. (There is also -g for goto, ((verify) the difference)


It quite likely has one of the general match options

  • -s and/or -d: source and destination address or network, specifically for IP (any subprotocol)
  • -p protocol: ommited means 'all'; the other options are tcp, udp, and icmp.
  • -i interface: incoming interface (optional; default is all. Note that you can do things like eth+ to match eth0, eth1, etc.).
  • (-o interface analogously, for outgoing packets)


Most other options are arguments to a specific alterntive match type (-m matchtype ) you decide to use. (These rules can also contain , of course)

Match types are usually a specific type of filter. You can use more than one match in a rule. They can of course be combined with the general match options.

They are pluggable at kernel/kernel-module level, so may or may not be available to you without some kernel compiling fun (though central things like tcp and udp), or without getting a cutting-edge kernel or patch.


The common match types that you can count on are tcp and udp, and also mport or multiport - these will serve all your port filtering needs.

Since you can construct illogical match combinations, there is the occasionaly apparent redundancy, and there may be dependencies. For example, when using -m tcp (or udp, mport, multiport(verify)), you also have to specify -p tcp or -p udp.


A not commonly seen but still interesting detail is that you can use ! directly before many parameter values (sometimes the entire parameter), where sensible, to invert its sense.


match types

Specified with -m. Many of these have to be compiled into the kernel and some are OS-specific. The most useful are:

  • tcp, udp: all packets of respective transport protocol
  • mport, multiport, in case you want ranges or lists of ports at a time
  • state: NEW, ESTABLISHED, RELATED, INVALID, useful to blindly allow packets belonging to connections which we scrutinized at establish time


Some of the following are generally interesting, a few depends on extra services, and you'll probably never use most:

  • recent: automatically maintain a blacklist. For example allows blockign people that try many connections in a short amount of time (e.g. to avoid being brute forced)
  • connbytes, connrate, limit, dstlimit, connlimit, hashlimit: allows matching/limiting of total connection trasfer, byte/packet rate/size or connection count, per service/net/etc.
  • psd: attempts to match on packets coming from portscans



  • iprange, mac: IP ranges, source MAC address. Mostly useful for filtering.
  • tcpmss, tos, ttl, conntrack, length, mark, connmark, pkttype: various packet property matches, including the MARK (see mangling). pkttype is link-layer type: unicast, broadcast, or multicast
  • u32: u32 matches up to 4-byte values in a packet, which is useful to pick out packet details without specific support


  • time: time-of-day, on weekdays, 'inside date range'.
  • random, nth: probability-based packet matcher, or every-n packet matcher
  • osf: passive OS fingerprinting
  • account, quota: packet counters, either for feedback via proc or for a quota system
  • owner: match uid, gid, command of locally generated packets

Details

Targets

Targets can be basic chains: (or custom ones)

  • PREROUTING
  • INPUT
  • OUTPUT
  • FORWARD
  • POSTROUTING

...special targets (common policies):

  • ACCEPT basically means "Sure, right. Just let it through to the next chain in line"
  • DROP is a packet trash can. It will make the client wait for a response, slowing it down. Used to be called DENY
  • REJECT: (common extension) respond with an ICMP-based rejection / problem signal packet
  • RETURN returns the packet to the previous chain it came from (allows complex as well as confusing logic)
  • QUEUE is a pass to userspace

...NATting:

  • SNAT (in nat-postrouting): Source Network Address Translation: source IP is changed, source port can be.
  • DNAT (in nat-postrouting): Destinaton Network Address Transation
  • MASQUERADE: dynamic version of the above
  • BALANCE: round-robin DNATting over a range of target IPs


(fancy stuff you'll probably never use)

...and others, various of which may need kernel support.

  • LOG, ULOG: Log to syslogd/syslog-ng/otehers, or a socket-based logger like ulogd (useful to e.g. redirect into a database). There are analysis tools for some of these targers (like there are for webserver logs)
  • TARPIT: accepts the initial connection, but no data. This slows down certain types of scans.
  • TOS, TTL, TCPMSS, MARK, CONNMARK, IPMARK: Alter packet details
  • REDIRECT, NETMAP, SAME, MIRROR: More packet mangling, mostly IP address/net-related
  • ROUTE: override routing decision details
  • ...and more.

Note: Some of these have the effect of doing -j RETURN themselves. For example, you can -j LOG and it will continue processing in the chain that -j LOG is in.(verify)



Simple firewall

(Glossing over a lot of details...)

A simple and common approach is to whitelist things you want to allow, and deny everything else, for example:

Allow packets from previously approved connections
Allow everything for localhost
Allow ssh from anywhere
Allow web requests from anywhere (assuming this computer has a web server)
Allow anything from my workstation, by IP address 192.168.1.100
Allow SMB file sharing for my housemates (on 192.168.0.0/16)
Allow ICMP traffic  (Ping and such)
Allow anything from my handheld device, by MAC address 01:23:45:67:89:ab
Deny everything else (could also be set as a table's default policy instead of an explicit last rule)

...which translated to iptables rules would look something like:

iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -m tcp -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -m tcp -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -s 192.168.1.100 -j ACCEPT
iptables -A INPUT -m tcp -p tcp -s 192.168.0.0/255.255.0.0 -m multiport --dports 139,445 -j ACCEPT
iptables -A INPUT -m udp -p udp -s 192.168.0.0/255.255.0.0 -m multiport --dports 137,138 -j ACCEPT
iptables -A INPUT -m icmp -p icmp -j ACCEPT
iptables -A INPUT -m mac --mac-source 01:23:45:67:89:ab -j ACCEPT
iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable

Notes:

  • Multiport is vaguely recent; if you don't have it you may need to write multiple rules to the same effect.
  • Ports 137/udp, 138/udp, 139/tcp, 445/tcp (and arguably 135/tcp) are ports for SMB file sharing
  • Rejecting is usually done for all protocols (including the potentially useful ICMP, for pinging), or done per-protocol. The above allows all ICMP, some TCP and UDP ports, and rejects everything else (including protocols other than icmp, tcp, and udp, if you get/use any)
  • REJECT tells off the other end, DROP means no response is sent at all (meaning the connection has to time out on the client side, which will will slow down scanners a bit, but also honestly misguided connections)

Logging

This article/section is a stub — probably a pile of half-sorted notes and assertions some of which may well be wrong, and not verified as a whole. Feel free to add or refine.

When you log from your firewall, you are probably also going for an analyzer, which probably means you want to redirect this output to a log made for only this purpose.

How to configure that depends on which logger you use (system log (LOG) or a userspace logger (ULOG), whether you want to dump into one or more files, and other preferences, but often comes down to a simple string match.


You may want to look for fishing expeditions too, such as connections to port 8080 and other common ports (e.g. P2P you do not use) when there has never been anything there.



LOG (syslog)

Logs to whatever you're using for your syslogs.

For example, when I add:

-A INPUT -j LOG --log-prefix "iptables_INPUT "

...just before the final reject, I get packet reports in my syslog, where iptables_INPUT is a nice and specific thing I can test for.

Since I have metalog installed, I added:

Firewall log : 
  regex    = "iptables_INPUT"
  logdir   = "/var/log/firewall"

...to /etc/metalog.conf. I also made a script that parses these log lines and reports semi-nicely.

You may want to filter this out of the general kernel log, using neg_regex.


ULOG (userspace log)

Instead of using the syslog calls, ULOG multicasts the messages on a netlink socket, and most likely use ulogd to receive this. That daemon can decide to do one of various things, including storing the packet reports in a big database (ULOG log analysers often expect that)

ULOG can store the entire packet rather than just print a summary as LOG does.

On the iptables side, you use this much likg LOG, though options, if you used any, are different. Example:

-A INPUT -j ULOG

Related software

This article/section is a stub — probably a pile of half-sorted notes and assertions some of which may well be wrong, and not verified as a whole. Feel free to add or refine.

Log analysis:


See also