iptables原理
iptables是什么
iptables是Linux内核默认的防火墙。防火墙,其实说白了讲,就是用于实现Linux下访问控制的功能的,它分为硬件的或者软件的防火墙两种。无论是在哪个网络中,防火墙工作的地方一定是在网络的边缘。而我们的任务就是需要去定义到底防火墙如何工作,这就是防火墙的策略,规则,以达到让它对出入网络的IP、数据进行检测。
对于TCP/IP的七层模型来讲,我们知道第三层是网络层,三层的防火墙会在这层对源地址和目标地址进行检测。
iptables的前身叫ipfirewall (内核1.x时代),这是一个作者从freeBSD上移植过来的,能够工作在内核当中的,对数据包进行检测的一款简易访问控制工具。但是ipfirewall工作功能极其有限。当内核发展到2.x系列的时候,软件更名为ipchains,它可以定义多条规则,将他们串起来,共同发挥作用,而现在,它叫做iptables,可以将规则组成一个列表,实现绝对详细的访问控制功能。
对于TCP/IP的七层模型来讲,我们知道第三层是网络层,三层的防火墙会在这层对源地址和目标地址进行检测。
iptables的前身叫ipfirewall (内核1.x时代),这是一个作者从freeBSD上移植过来的,能够工作在内核当中的,对数据包进行检测的一款简易访问控制工具。但是ipfirewall工作功能极其有限。当内核发展到2.x系列的时候,软件更名为ipchains,它可以定义多条规则,将他们串起来,共同发挥作用,而现在,它叫做iptables,可以将规则组成一个列表,实现绝对详细的访问控制功能。
规则(rules)
规则是iptables对数据包进行操作的基本单元。即“当数据包符合规则定义的条件时,就按照规则中定义的动作去处理”。
规则中定义的条件一般包括**源地址/端口、目的地址/端口、传输协议(TCP/UDP/ICMP)**等。
而规则定义的动作一般有:
ACCEPT
:允许数据包通过;DROP
: 直接丢弃数据包,不给任何回应信息;REJECT
:拒绝数据包通过,必要时会给数据发送端一个响应的信息。
iptables -A INPUT -s 1.1.1.1 -j DROP
matching component: -s 1.1.1.1
target component: -j DROP
链(chains)
链是数据包传播的路径,每条链中都有若干个规则。
当一个数据包到达一条链时,iptables会按照规则的顺序,从该链的第一条规则开始往下检查,如果有条件匹配的规则,则按照规则定义的动作执行;否则继续检查下一条规则。如果该数据包和链中所有的规则都不匹配,则iptables会根据该链预先定义的默认策略来处理数据包。
PREROUTING
: Packets will enter this chain before a routing decision is made.INPUT
: Packet is going to be locally delivered. It does not have anything to do with processes having an opened socket; local delivery is controlled by the “local-delivery” routing table:ip route show table local
.FORWARD
: All packets that have been routed and were not for local delivery will traverse this chain.OUTPUT
: Packets sent from the machine itself will be visiting this chain.POSTROUTING
: Routing decision has been made. Packets enter this chain just before handing them off to the hardware.
表(tables)
iptables内置了4个表,即 filter表(default table)、nat 表、mangle 表、raw 表,分别用于实现包过滤、网络地址转换、包修改和数据跟踪处理等功能。
处于链中的不同规则可以被定义在不同的表中,且各个表在同一个链中有不同的优先级,这也意味着处于同一个链中位于不同表的规则有不同优先级。
-
filter
表(default table)- This is the default table (if no -t option is passed). It contains the built-in chains INPUT (for packets destined to local sockets), FORWARD (for packets being routed through the box), and OUTPUT (for locally-generated packets).
-
nat
表- This table is consulted when a packet that creates a new connection is encountered. It consists of three built-ins: PREROUTING (for altering packets as soon as they come in), OUTPUT (for altering locally-generated packets before routing), and POSTROUTING (for altering packets as they are about to go out).
-
mangle
表- This table is used for specialized packet alteration. Until kernel 2.4.17 it had two built-in chains: PREROUTING (for altering incoming packets before routing) and OUTPUT (for altering locally-generated packets before routing). Since kernel 2.4.18, three other built-in chains are also supported: INPUT (for packets coming into the box itself), FORWARD (for altering packets being routed through the box), and POSTROUTING (for altering packets as they are about to go out).
-
raw
表- This table is used mainly for configuring exemptions from connection tracking in combination with the NOTRACK target. It registers at the netfilter hooks with higher priority and is thus called before ip_conntrack, or any other IP tables. It provides the following built-in chains: PREROUTING (for packets arriving via any network interface) OUTPUT (for packets generated by local processes)
4个表的优先级为:raw > mangle > nat > filter。
raw表优先级最高。因此,可以对数据包在进入PREROUTING链的 nat 表之前,对消息进行处理。
Traversal Order
分析
当数据包到达网卡时,首先会进入PREROUTING链(注意,在PREROUTING链中的规则存在于不同表中,这些规则存在优先顺序,如上图所示),完成PREROUTING链中规则的匹配和执行后,iptables会根据数据包的目的IP是否为本机地址,判断是否需要将该数据包转发出去。若为本机,则进入OUTPUT链,否则直接进入POSTROUTING链。最终输出。
综上,数据包在iptables中的传输链路有两种情况:
- 第一种:PREROUTING chain -> FORWARD chain -> POSTROUTING chain
- 第二种:PREROUTING chain -> INPUT chain -> LOCALHOST -> OUTPUT chain -> PUSTROUTING chain
所以,要想对数据包进行控制,主要可以在上面几条链路中添加规则。
iptables 使用
iptables 的开关
Ubuntu
# get current status of iptables
$ sudo ufw status
Status : inactive
# Disable or Stop iptables
$ sudo ufw disable
# Start or Enable iptables
$ sudo ufw enable
# Reload iptables
$ sudo ufw reload
CentOS7
CentOS从7开始,使用systemctl来管理服务和程序,包括了service和chkconfig。
#打开
$ systemctl start firewalld.service
#关闭
$ systemctl stop firewalld.service
#重启
$ systemctl restart firewalld.service
Openwrt
#打开
$ service firewall start
#关闭
$ service firewall stop
#重启
$ service firewall restart
-L
- 列出规则
iptables对规则的操作方法有:
列出默认table(filter
)的所有chain中的规则
$ iptables -L -n -v --line-numbers
列出默认table(filter
)的指定chain中的规则
$ iptables -L [chain [rulenum]] -n -v
列出指定table中的规则
$ iptables -L [-t tables] [-nv]
# 查看nat表中的所有规则
$ iptables -t nat -L -n -v --line-numbers
-t
:指定table名,例如nat或filter,若省略,则使用默认的filter-L
:列出所有规则-n
:不进行IP到HOSTNAME的 DNS 反查(reverse DNS lookups),显示信息速度回快很多。-v
:列出更多的信息,包括通过该规则的数据包总位数、相关的网络接口等
target
:代表进行的操作,ACCEPT是放行,而REJECT则是拒绝,此外,还有DROP表示丢弃prot
:代表使用的数据包协议,主要有TCP、UDP以及ICMP3种数据包格式opt
:额外选项说明source
:代表此规则是针对哪个来源IP进行限制destination
:代表此规则是针对哪个目标进行限制
-F
- 清除规则
$ iptables [-t tables] [-FXZ]
选项与参数:
-F
:清除所有规则-X
:清除所有用户“自定义”的chain-Z
:将所有chain的计数与流量统计都归零
比如:
# 清除所有的规则
$ iptables -F
# 清除nat表的规则
$ iptables -F -t nat
# Using below set of commands, delete your currently configured rules from iptables.
$ iptables -F INPUT
$ iptables -F OUTPUT
$ iptables -F FORWARD
-A
/-I
- 增加规则
-A
: append a rule to the end of the chain-I
add a rule to the top of the chain
Note that an upper has the higher priority:
$ iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP tcp -- 192.168.0.0/24 anywhere tcp dpt:smtp
ACCEPT all -- 192.168.0.66 anywhere
# In such a case the last rule doesn't work, because the second rule (DROP) has higher priority than the last rule.
# To solve it, we could
# delete the 3rd rule
$ iptables -D INPUT 2
$ iptables -I INPUT -s 192.168.0.66 -j ACCEPT
$ iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 192.168.0.66 anywhere
DROP tcp -- 192.168.0.0/24 anywhere tcp dpt:smtp
以下命令用于增加一条规则到特定的chain中:
$ iptables --append [chain]
# 或者
$ iptables -A [chain]
常用的用于描述规则细节的参数有:
-s, --src, --source
:匹配的源地址-d, --dst, --destination
:匹配的目的地址--sport, --source-port
:匹配的源端口--dport, --destination-port
:匹配的目的端口-p, --protocol
:匹配的协议,比如tcp
、udp
等-i, --in-interface
:匹配输入的网卡,用来描述匹配要匹配该规则,该包是从哪块网卡进入,可以使用通配字符 + 来做大范围匹配-o, --out-interface
:匹配输出的网卡!
:取反-m state —state
:用来匹配连接状态, 连接状态共有四种:INVALID、ESTABLISHED、NEW 和 RELATED。- INVALID 表示该封包的连接编号(Session ID)无法辨识或编号不正确。
- ESTABLISHED 表示该封包属于某个已经建立的连接。
- NEW 表示该封包想要起始一个连接(重设连接或将连接重导向)。
- RELATED 表示该封包是属于某个已经建立的连接,所建立的新连接。例如:FTP-DATA 连接必定是源自某个 FTP 连接。
-j
:对该规则执行的动作,ACCEPT
: means to let the packet through,进行完此处理动作后,将不再匹配该chain的其它规则,直接跳往下一个 Chain。REJECT
: Don’t allow the connection, but send back an error. This is best if you don’t want a particular source to connect to your system, but you want them to know that your firewall blocked them.- 拦阻该封包,并传送封包通知对方,可以传送的封包有几个选择:ICMP port-unreachable、ICMP echo-reply 或是tcp-reset(这个封包会要求对方关闭连接),进行完此处理动作后,将不再匹配其它规则,直接中断过滤程序。
- 范例如下:
iptables -A FORWARD -p TCP --dport 22 -j REJECT --reject-with tcp-reset
DROP
: Drop the connection, act like it never happened. This is best if you don’t want the source to realize your system exists.- 丢弃封包不予处理,进行完此处理动作后,将不再匹配其它规则,直接中断过滤程序。
REDIRECT
: 将封包重新导向到另一个端口(PNAT),进行完此处理动作后,将会继续匹配其它规则。- 这个功能可以用来实现透明代理,例如:
iptables -t nat -A PREROUTING -p tcp -m set --match-set gfwlist -j REDIRECT --to-ports 8080
,即如果访问IP在gfwlist
IP Set中,就将该数据包转发到 8080 端口
- 这个功能可以用来实现透明代理,例如:
RETURN
: means stop traversing this chain and resume at the next rule in the previous (calling) chain. If the end of a built-in chain is reached or a rule in a built-in chain with target RETURN is matched, the target specified by the chain policy determines the fate of the packet.
--tcp-flags
:
Example
比如,
$ iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT #允许本地回环接口(即运行本机访问本机)
$ iptables -A OUTPUT -j ACCEPT #允许所有从本机向外部的访问
$ iptables -A INPUT -p tcp --dport 22 -j ACCEPT #允许访问22端口
$ iptables -A INPUT -j reject #禁止其他未允许的规则访问
屏蔽IP
$ iptables -I INPUT -s 123.45.6.7 -j DROP #屏蔽特定IP发来的数据包
$ iptables -I INPUT -s 123.0.0.0/8 -j DROP #封整个段即从123.0.0.1到123.255.255.254的命令
$ iptables -I INPUT -s 124.45.0.0/16 -j DROP #封IP段即从123.45.0.1到123.45.255.254的命令
$ iptables -I INPUT -s 123.45.6.0/24 -j DROP #封IP段即从123.45.6.1到123.45.6.254的命令是
拒绝转发来自192.168.1.10主机的数据,允许转发来自192.168.0.0/24网段的数据:
$ iptables -A FORWARD -s 192.168.1.11 -j REJECT
$ iptables -A FORWARD -s 192.168.0.0/24 -j ACCEPT
拒绝进入防火墙的所有ICMP协议数据包:
$ iptables -I INPUT -p icmp -j REJECT
允许防火墙转发除ICMP协议以外的所有数据包:
$ iptables -A FORWARD -p ! icmp -j ACCEPT
说明:使用 !
可以将条件取反。
在filter表的INPUT链中追加一条规则,规则的匹配条件是数据包的源IP为192.168.1.10,执行动作为允许(ACCEPT),即允许源IP为192.168.1.10的主机访问本机:
$ iptables -t filter -A INPUT -s 192.168.1.10 -j ACCEPT
允许指定IP地址访问公网:
$ iptables -A FORWARD -s 192.168.1.92/24 -j ACCEPT
允许访问指定的IP地址:
$ iptables -A FORWARD -d 192.168.1.92 -j ACCEPT
-N
- 定义新的规则链
以下命令用于定义一个新的chain:
$ iptables -N [chain]
-P
- Set defualt Policy by Chain
# Set accept all policy to all connections
$ iptables -P INPUT ACCEPT
$ iptables -P OUTPUT ACCEPT
$ iptables -P FORWARD ACCEPT
-D
- Remove a Rule
# list rules with line numbers
$ iptables -L -t nat -nv --line-numbers
# e.g., iptables -t nat -D OUTPUT 1
$ iptables -t <table> -D <chain> <line-number-of-the-rule>
# iptables -t nat -D PREROUTING 17
-R
- Replace a Rule
-D, --delete chain rule-specification
-D, --delete chain rulenum
iptables-save
- 保存
永久生效
方法1
即即使在host重启后,执行的规则仍然生效
$ iptables-save
方法2
# save the current rules set to a file
$ iptables-save > /etc/iptables-script
# 恢复规则
$ iptables-restore > /etc/iptables-script
# 开机自动恢复规则,把恢复命令添加到启动脚本
$ echo '/sbin/iptables-restore /etc/iptables-script' >>/etc/rc.d/rc.local
方法3
You can install iptables-persistent
which will automatically handle loading and saving iptables rules during system startup and shutdown. To install it, use the following command:
sudo apt-get install iptables-persistent
恢复
# save the current rules to a file
$ sudo iptables-save | /etc/iptables/rules.v4
# Restoring iptables Rules Manually
sudo iptables-restore < /etc/iptables/rules.v4
Debug
Port Forward
Forward A Local Port to A Local/External Port
Enable Forwarding in Kernel
Before using packet forwarding, you must instruct the system to allow it. To enable forwarding for the current session, type:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -p tcp \
--dport ${LOCAL UNPRIV PORT} -j DNAT --to-destination ${ANOTHER SYSTEM}:${REMOTE PORT}
iptables -t nat -A POSTROUTING -p tcp \
--dst ${ANOTHER SYSTEM} --dport ${REMOTE PORT} -j SNAT --to-source ${LOCAL SYSTEM}
# e.g.,
# to forward traffic from an external origin to a remote port, the iptables DNAT rule should be in the PREROUTING chain
sudo iptables -t nat -A PREROUTING -p tcp --dport 6389 -j DNAT --to-destination 192.168.18.32:6379;
# to forward locally originated traffic to a remote port, you'll need a similar rule in the OUTPUT chain of the nat table.
sudo iptables -t nat -A OUTPUT -p tcp --dport 6389 -j DNAT --to-destination 192.168.18.32:6379;
sudo iptables -t nat -A POSTROUTING -p tcp --dst 192.168.18.32 --dport 6379 -j SNAT --to-source 127.0.0.1
Verify
# view
sudo iptables -L -t nat -nv
# check the direct connection
redis-cli -h 192.168.18.32 -p 6379
192.168.18.32:6379> ping
PONG
# check the redirection
redis-cli -h 192.168.18.120 -p 6389
Forward An External Port to An External Port
My scenarios is:
- The IP of 1.1.1.1 is xx.rwlb.singapore.rds.aliyuncs.com
- My local development env is not able to directly access 1.1.1.1 due to security concerns
- My DBA sets a jump server (2.2.2.2:3306) as a proxy to allow my local development env to indirectly access 1.1.1.1
- However, all middleware addresses are hard coded in my code repo, i.e., 1.1.1.1
- I wanna not modify the code and am still able to connect to all middleware
Idea: map 1.1.1.1:32111 to 2.2.2.2:3306 on kernel level, so that connecting 1.1.1.1:32111 actually is forwarded to 2.2.2.2:3306. As a result, my code doesn’t need any
$ sudo iptables -t nat -A OUTPUT -p tcp --dport 32111 -d 1.1.1.1 -j DNAT --to-destination 2.2.2.2:3306;
# or
$ iptables -t nat -A PREROUTING -d 1.1.1.1 -p tcp --dport 8887 -j DNAT --to-destination 2.2.2.2:3306;
# Allow forwarding from the source to the destination
$ iptables -A FORWARD -d 2.2.2.2 -p tcp --dport 3306 -j ACCEPT
# view the current config
$ sudo iptables -L -t nat -nv
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 1339 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
0 0 DNAT tcp -- * * 0.0.0.0/0 1.1.1.1 tcp dpt:32111 to:2.2.2.2:3306
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
# to verify
$ telnet 1.1.1.1 32111