ZNHOO Whatever you are, be a good one!

iptables

iptables is a program used to configure and manage the kernels netfilter modules. It basically serves as a firewall to filter network traffic. 入门教程,必读!.

1 ABCs

  1. Linux firewall is implemented in kernel by netfilter. How to interact with kernel netfilter? Ip[6]tables!
  2. iptables and ip6tables are used to set up, maintain, and inspect the tables of IPv4 and IPv6 packet filter rules in the Linux kernel. Several different tables may be defined. Each table contains a number of built-in chains and may also contain user-defined chains.
  3. Each chain is a list of rules which can match a set of packets. Each rule specifies what to do with a packet that matches. This is called a target, which may be a jump to a user-defined chain in the same table.
  4. A firewall rule specifies criteria for a packet and a target. If the packet does not match, the next rule in the chain is examined; if it does match, then the next rule is specified by the value of the target, which can be the name of a user-defined chain, one of the targets described in iptables-extensions(8), or one of the special values ACCEPT, DROP or RETURN.
  5. The schematic diagram:

    diagram 1

    diagram 2

    diagram 3

  6. Ip[6]tables has different tables namely: filter nat mangle raw of which filter is the default one.

    Each table contains different chains - builtin chains or user-defined chains. You can think of chain as the direction of traffic flows to. Take filter table for example, it has three builtin chains namely: INPUT FORWARD OUTPUT.

    Each chain consists of rules determing action on packets. Ip[6]tables take action based on the first matched rule. If no rules match, use the default policy.

    Each rule mainly consists of two parts: criteria and target. Criteria matches packet by different arguments like protocol, port number, interface, state etc. Target is actually the action took on the matched packet. It's an action instead of of intuitively an object.

  7. There is a file /etc/services recording system's default services/ports.

Configuration

  1. Launch

    ~ # rc-sevice ip[6]tables start
    ~ # rc-update add ip[6]tables default
    

    Without save first, you cannot start the service.

  2. Rules backup

    If you ever configured iptables before, then save it before any update. By default, iptables itself assume its default but simple rules.

    # rc-service ip[6]tables save, to /var/lib/ip[6]tables/rules-save by default
    or
    # ip[6]tables-save > /var/lib/ip[6]tables/rules-save
    
    # cp /var/lib/ip[6]tables/rules-save /path/to/ip[6]tables-rules-save-backup
    

    Upon exit (i.e. system shutdown/reboot etc), ip[6]tables will save rules to /var/lib/ip[6]tables/rules-save automatically (possibly override previous rules).

  3. Restore rule set:

    # ip[6]tables-apply -t 60 /path/to/ip[6]tables-rules-save-backup
    or
    # ip[6]tables-restore < /path/to/ip[6]tables-rules-save-backup
    
    # ip[6]tables-save > /var/lib/ip[6]tables/rules-save
    or
    # rc-service ip[6]tables save
    

    This happens usually if you want to start from scratch by apply a set of rules while discarding all current rules.

Update rules

  1. Refer to the 1st reference, it supplies a basic script (say iptables-rules.sh) for Generating firewall rules for client.

     # chmod u+x /path/to/iptables-rules
     # ./path/to/iptables-rules
     # rc-service ip[6]tables save
    

    The contents of iptables-rules.sh are bash commands to add rules while /var/lib/ip[6]tables/rules-save are real rules. There is no much syntax difference between commands and rules-ave file. But they do differ in real usage (discussed in ip[6]tables-apply).

  2. Remove a rule

    Suppose you have a bunch of rules, but only want to remove one single rule instead of setting from stratch. Use -A -D.

     # iptables -t nat -A OUTPUT -p TCP --dport 53 -j DNAT --to-destination 127.0.0.1:9053
    

    Above we -A append a rule, and how to remove it?

     # iptables -t nat -D OUTPUT -p TCP --dport 53 -j DNAT --to-destination 127.0.0.1:9053
    

    Just switch -A to -D - delete a rule.

ip[6]tables-apply

Always update Iptables rules by ip[6]tables-apply instead of terminal command or bash script!

As mentioned, Iptables automatically save rules to /var/lib/ip[6]tables/rules-save.

  1. When start again from scratch, ip[6]tables-apply -t 60 /var/lib/ip[6]tables/rules-ave.
  2. When updating (-A -D -I etc.), update rules-save first and then run command again.

Reason to use ip[6]tables-apply:

  1. It is a wrapper of ip[6]tables-restore with confirmation.
    1. If it cuts down network, roll back to previous rule set;
    2. If it find no problem, wait for user's confirmation during -t timeout;
    3. If timeouts, roll back to previous rule set.
  2. Problems of updating rules on terminal:
    1. If -A -D -I rule, it may obstruct ongoing packets filtering in Iptables.
    2. It difficult to overview the whole rule set for adjustting new rule.
    3. With IPTables rules, order matters.

So when testing all examples below, convert the command line into rules-save format. Most of the time, you only need to omit the iptables [-t nat] part. Don't worry, ip[6]tables-apply will roll back if errors found.

Check

  1. At any time, you can check current rules that ip[6]tables is using:

     # ip[6]tables [-t filter | nat | mangle | raw | security] -S
     # ip[6]tables [-t filter | nat | mangle | raw | security] -L -nv -Z
    

    If -t argument is not supplied, then only filter is printed.

Transparent proxy

Some applications (like Bitorrent) do NOT support proxy and others do NOT support your proxy type. For example, ss is a sock5 proxy, but command line like wget only supports http proxy. How to let them connect through ss?

  1. proxychain - a package.
  2. transparent proxy by ip[6]tables.

Specify rules to force applications' traffic through that proxy! Application does NOT know about this enforcement for it's carried out in kernel netfilter directly without its notice. Please refer to transparent proxy in post tor.

First you need a proxy already running. Then use Iptables to redirect application traffic to that proxy nat table OUTPUT chian DNAT target. It's nat table instead of other table! nat table is only consulted on NEW TCP connection. After connection established, no need to and CANNOT DNAT/REDIRECT any more. So we DNAT/REDIRECT in nat table.

The difference between DNAT and REDIRECT, refer to DNAT vs REDIRECT and dnat redirect. According to netfilter documentation, redirection is a specialized case of destination NAT. REDIRECT is equivalent to doing DNAT to the incoming interface (for imcoming traffic including locally generated traffic). Think of DNAT as the case of destination NAT.

Transparent proxy usually plays in two directions: incoming traffic and outgoing traffic.

For example, you are running a HTTP server at port 2000 while offering access by port 80. Then on your sever, you should REDIRECT incoming traffic from 80 to 2000: iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 2000. If the real HTTP service on another host, then use DNAT --to-destination instead. Most of time, when handling outgoing traffic, use DNAT since the destination address will be changed.

For example, your application want to send traffic outside of localhost. But you want to let the traffic go through a proxy first: iptables -t nat -A OUTPUT -p tcp -m owner ! --uid-owner tor -j DNAT --to-destination 127.0.0.1:9040.

To refine the transparent proxy control, you can define personal chain like:

# iptables -t nat -N TRANSPROXY
# iptables -t nat -A OUTPUT -p tcp -m owner ! --uid-owner 105 -j TRANSPROXY
# iptables -t nat -A TRANSPROXY -p tcp -d imap.gmail.com -m tcp --dport 993 -j DNAT --to-destination 127.0.0.1:9040

There is one issue on -d imap.google.com part. On the man page:

Please note that specifying any name to be resolved with a remote query such as DNS is a really bad idea.

What does that mean? If you specify a hostname instead of an IP address, then Iptables needs DNS resolving before appending the rule. Suppose Iptables was added to system boot script. When reading this rule (from /var/lib/ip[6]tables/rules-save), it will first call DNS to resolve *imap.gmail.com. Then:

  1. Iptables does not filter packets based on hostnames (DNS is not on TCP or IP layer but above that). Iptables (netfilter in kernel) are based on IPs and ports.

    Iptables is just userspace tool to interact with kernel. The real firewall is netfilter in kernel. However, kernel does NOT understand hostname.

  2. When run that terminal command, userspace iptables will first resolve imapt.gmail.com. Append the rule by replacing imap.gmail.com with IP.

    The real problem is: most services providers use dynamic IPs (by CDN / cache servers). If IP changes, rule will be invalid.

  3. If you put the rule in /var/lib/iptables/rules-save which is the default location for Iptables to save/load rules, it will be a little different.

    On boot, Iptables load rules from /var/lib/iptables/rules-save and try resolve imap.gmx.com. Similarly, this happens when execute iptables-restore or iptables-apply by using that path. Issues during boot:

    1. DNS is called before Iptables is fully launched on boot. Security risk! Any network should occur only after Iptables start.
    2. If DNS service is not ready when Iptables loads, then Iptables loading fails.

    Actually, this issue does NOT happen on my Gentoo system. Why? Iptables will automatically save rules to /var/lib/iptables/rules-save on server stop.

  4. So put that rule in somewhere else. First, it won't be overriden by Iptables on service stop. Second, avoid DNS resolve on boot.

    A compromise proposal is to manually resolve IP. First ping imap.gmail.com and then use the resolved IP.

  5. Most service provider like Google, Microsoft etc. use CDN / cache servers to dynamically deliver services. IPs vary from time to time. Added rule might be invalid in a few days.
  6. In order to make use of custom chain (-N), you'll have to jump (-j) to it from somewhere

A possible solution uses Iptables' string match module to match hostname instead of specifying -d hostname:

# iptables -t nat -A OUTPUT -p tcp -m string --string "imap.gmail.com" --algo bm --dport 993 -j DNAT --to-destination 127.0.0.1:2000
or
#iptables -A OUTPUT -p tcp -tcp --tcp-flags SYN -m string --string --algo bm "imap.gmail.com" --dport 993 -j DNAT --to-destination 127.0.0.1:2000

But neither work:

  1. In 1st example, nat table is only consulted for new TCP connection (SYN packet without any contents). No hostname string imap.gmail.com in SYN packet. Connection not established yet for contents delivery.
  2. The 2nd example moves to filter table. But –tcp-flags SYN has the same issue as 1st. By the way, imap.gmail.com use SSL connection. The packets conents are encrypted. So we cannot do string match.

To summary:

  1. Top-down: http/smtp/imap/ftp - ssl (tls)/socks - tcp - ip - link - physical.
  2. Iptables works mainly on IP and port.
  3. Only new TCP SYN packet consults nat table - to establish TCP connection. Established connections bypass nat table - TCP is a stateful protocol.

    DNAT/REDIRECT works only on nat table. In other words, DNAT/REDIRECT interferes during connection establishment. After establishment, there is no need to DNAT/REDIRECT any more.

  4. string match:
    1. Match packet contents. Cannot match strings in nat table since it's only consulted by SYN packet without payload. Usually use string match in filter table.
    2. IMAP SSL encrypts TCP connection. TCP payload is encrypted and even in filter table, hence you cannot match strings from higher layer IMAP.

      Hostname imap.gmail.com is a application layer concept. It's in IMAP header - encrypted! Cannot match hostname of IMAP SSL.

Reference

  1. wiki iptables
  2. iptables 实践
  3. arch wiki
  4. 入门教程,必读!
  5. wiki ports preference