Squoggle

Mac's tech blog

Monthly Archives: May 2020

IPTables

These are my notes on IP tables. Maybe at some point I may do a complete tutorial or How To but don’t hold your breath.

Chains:

There are typically 3 chains in a standard setup. They are:

INPUT
FORWARD
OUTPUT

Input is things coming into the server.
Forward are things that are forwarded by the server.
Output are things that are leaving the server.

Policies:

There is a default policy (rule) set up for each chain. CentOS somes standard with the default policy of ACCEPT for each of these chains. The command line to set the policy is like this:

# iptables -P INPUT ACCEPT

Pretty simple really. The -P is the flag to set the policy.

Flush:

The command that flushes the iptables is -F. This deletes the rules in the table.

# iptables -F

If changing rules from a remote host, the tutorial says to first put the INPUT policy to ACCEPT especially if you are going to flush the table. The flush command flushes everything except the default policy so if you have set it to ACCEPT then you won’t lock yourself out. Be sure to undo the ACCEPT on the INPUT policy or it will basically be wide open unless you have some other rule locking it down.

So the first two commands, accept on the input policy and flush on the table leave you with a pretty much blank rule set.

Saving:

It’s important to understand that the commands take effect immediately so if you do a wrong command you can lock yourself out. However they are not permanently stored until you save them with this:

# service iptables save

If you did lock yourself out then theoretically you could reboot the server before saving and it would revert back to whatever it was on the last save. I have not tested this yet but that is what I understand.

Showing:

You need to be able to see the results of your commands so you can show your tables like this:

iptables -L

This leaves a bit to be desired as it shows everything and may be too much information. This will show all Chains. If you just want to list one of the Chains you can do it like this:

# iptables -L [CHAIN]

For example:

# iptables -L INPUT

Even more useful is to list with Line Numbers. This is helpful if you want to insert a rule after a certain existing rule. That command looks like this:

# iptables -L --line-numbers

or

# iptables -L INPUT --line-numbers

Even better is using the -v or the verbose flag. That’s probably the best.

# iptables -L INPUT -v --line-numbers

Adding Rules:

Rules are added or deleted to the table by the -A or -D flag. The -A appends a rule and the -D deletes a rule. For example:

# iptables -A INPUT -i lo -j ACCEPT

This will allow everything to reach the lo interface. This is a good idea as programs running on the server interact with the lo interface.

By the way, the -i flag specifies an interface. The -j flag is the jump flag.  In the above example if something comes in (INPUT) on the lo interface, then jump to ACCEPT.

It is also important to note that rules are put into the table in the order they are typed in using the -A (append) command. You need to be sure you do not give permissions to something and then take it away later. It is also a good idea to set the policies for INPUT and FORWARD to drop then specifically set up the exceptions to this with the rules.

Deleting Rules:

The -D flag can be used with line numbers and is useful to delete specific lines in your IPTables config. The Delete command is done like this:

# iptables -D INPUT 4

In other words, deleting from the INPUT chain rule number 4.

Inserting Rules:

Rules need to be in certain orders or you could cause problems. You can insert a rule to the table with this:

# iptables -I INPUT 3 -p tcp --dport 23 -j ACCEPT

This inserts at line 3 the rule to accept telnet in the INPUT chain.

IP Addresses:

You can also specify IP addresses in a rule. For example if I wanted to specify that I wanted to accept connections coming from a certain source IP address I would use something like this:

# iptables -A INPUT -s 192.168.0.4 -j ACCEPT

The -s means source IP.

You can also specify entire networks like this:

# iptables -A INPUT -s 192.168.0.0/24 -j ACCEPT

Comments:

Comments can also be added. This is useful if you are putting the lines into a script or something. Everything after the “#” is ignored.

# iptables -A INPUT -s 192.168.0.0/24 -j ACCEPT  # using standard slash notation

Mac Addresses:

You can also filter by mac addresses in a rule. Something like this:

# iptables -A INPUT -m mac --mac-source 00:26:B9:D1:D9:6B -j ACCEPT

Anything from the specified source mac address will be accepted by the above rule.

You can also add an IP address as well as a mac address for further filtering:

# iptables -A INPUT -s 192.168.0.4 -m mac --mac-source 00:50:8D:FD:E6:32 -j ACCEPT

The above will append the rule to the end of the chain. It is probably better to insert it somewhere like this:

# iptables -I INPUT 75 -s 192.168.0.4 -m mac --mac-source 00:50:8D:FD:E6:32 -j ACCEPT

Protocols & Ports:

To further refine you really need protocols and ports defined in a lot of the rules. Going back to this example:

# iptables -I INPUT 3 -p tcp --dport 23 -j ACCEPT

The -p means protocol, in this case TCP and the --dport means destination port, in this case port 23 or telnet.

To get more granular on the rules you will want to put them together. For ecample I want to accept on the INPUT chain connections coming from 192.168.0.0/24 and port 23 or telnet. The rule would look like this:

# -A INPUT -s 192.168.0.0/24 -p tcp --dport 23 -j ACCEPT

Drop & Reject:

The default configuration of IP Tables for CentOS is to have the INPUT, FORWARD & OUTPUT policies set to ACCEPT. This is probably not a good idea. Depending on your security posture it might be OK for your OUTPUT policy, unless you want to limit what goes out of your system. The way you solve this issue is to add some REJECT rules to the end of your configs. A REJECT rule looks something like this:

# iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
# iptables -A FORWARD -j REJECT --reject-with icmp-host-prohibited

A REJECT will send a TCP Reject whereas a DROP simply drops the connection. Depending on your security posture a DROP might be a better decision.

States:

The State Match

The most useful match criterion is supplied by the `state’ extension, which interprets the connection-tracking analysis of the `ip_conntrack’ module. This is highly recommended.

Specifying `-m state’ allows an additional `–state’ option, which is a comma-separated list of states to match (the `!’ flag indicates not to match those states). These states are:

NEW: A packet which creates a new connection.
ESTABLISHED: A packet which belongs to an existing connection (i.e., a reply packet, or outgoing packet on a connection which has seen replies).
RELATED: A packet which is related to, but not part of, an existing connection, such as an ICMP error, or (with the FTP module inserted), a packet establishing an ftp data connection.
INVALID: A packet which could not be identified for some reason: this includes running out of memory and ICMP errors which don’t correspond to any known connection. Generally these packets should be dropped.

An example of this powerful match extension would be:

# iptables -A FORWARD -i ppp0 -m state ! --state NEW -j DROP

The default iptables configuration has the ESTABLISHED and RELATED states set to ACCEPT on the INPUT chain like this:

# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Probably a good idea to keep that and put it first in the list. This will keep any connections going that have already been established. For example if you have an ssh connection going and you change the iptables for SSH and lock yourself out the connection will remain until you close the connection. This has saved me at least once but you could potentially keep bad connections going as well so use with care.

I have also seen that it is a good idea to use states with regular rules like this:

# iptables -A INPUT -s 192.168.0.0/24 -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT

I don’t completely understand it myself. Things should work without it. I’ve just seen posts about it and think it is useful to mention here.

Another good thing to use might be the INVALID state. INVALID means a packet that could not be identified somehow. Just drop them:

# iptables -A INPUT -m state --state INVALID -j DROP
# iptables -A FORWARD -m state --state INVALID -j DROP
# iptables -A OUTPUT -m state --state INVALID -j DROP

Script:

Here’s a little script that sets up pretty much what I talked about on this page:

#!/bin/bash

# iptables example configuration script
# Flush all current rules from iptables
 iptables -F

# Set default policies for INPUT, FORWARD and OUTPUT chains
 iptables -P INPUT DROP
 iptables -P FORWARD DROP
 iptables -P OUTPUT ACCEPT

# Drop invalid packets
 iptables -A INPUT -m state --state INVALID -j DROP
 iptables -A FORWARD -m state --state INVALID -j DROP
 iptables -A OUTPUT -m state --state INVALID -j DROP

# Accept packets belonging to established and related connections
 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow private lan to ping
 iptables -A INPUT -s 192.168.0.0/24 -p icmp -j ACCEPT

# Set access for localhost
 iptables -A INPUT -i lo -j ACCEPT

# Allow private lan to ssh
 iptables -A INPUT -s 192.168.0.0/24 -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT

# Save settings
 /sbin/service iptables save

# List rules
 iptables -L -v --line-numbers