tips for iptables

iptables basic
从第一次整理完在到重新挪到新的blog这里,好久没有使用过iptables相关的命令了,总之算是占个坑吧,内容不断完善中。。。

相关发展背景

理解iptables与firewall的关系,可以参考这个(http://www.aboutyun.com/forum.php?mod=viewthread&tid=9649&highlight=%BF%AA%B7%A2%C8%CB%D4%B1%B1%D8%B6%C1openstack%CD%F8%C2%E7%BB%F9%B4%A1)

相关基本概念

首先明白几个概念的层次关系:tables , chains , rules 。可以认为tables是一系列chains的集合,chains是一系列rules的集合,这样其实chain的概念比较容器理解了,好多节点组成一条chain,每个节点相当于是一个rule。

几个默认的tables。

packet通过iptables的流程图(todo)
packet通过iptables的流程图

后面的内容都以这个图为参考进行分析。

Filter tables

INPUT chain 过滤所有目标地址是本机的数据包(对进入本机数据包的过滤)根据上图可以看到,只有dstip为本机ip的时候才进入Input链。
OUTPUT chain 过滤所有本机产生的数据包(对源地址得数据包的过滤)根据上图可以看到,本机产生的数据包要经过Filter的Output链。
FORWARD chain 过滤所有路过本机的数据包(源地址和目标地址都不是本机的数据包)可以看到,进入forward操作的packet(srcip以及dstip都不是本机,这样的packet也要经过filter的forward链)
关于转发功能的补充:

当linux收到了一个目的ip地址不是本地的包,Linux默认会把这个包丢弃,因为默认情况下,Linux的三层包转发功能是关闭的,如果要让我们的linux实现转发,则需要打开这个转发功能,可以改变它的一个系统参数,使用sysctl net.ipv4.ip_forward=1或者echo “1” > /proc/sys/net/ipv4/ip_forward命令可以打开转发功能。

NAT table

PREROUTING chain 数据包到达防火墙时改变包的目的地址。
POSTROUTING chain 改变本地产生数据包的目标地址。
OUTPUT chain 在数据包离开防火墙时改变数据包的源地址。

Mangle table

mangle表主要用于修改数据包的TOS(Type Of Service,服务类型)、TTL(Time To Live,生存周期)指以及为数据包设置Mark标记,以实现Qos(Quality Of Service,服务质量),不是太经常使用。可以看到,Mangle table能够包含5条链中的每一条。

展现不同table中所有的chain的命令:

iptables -t filter –list 相当于 iptables –list

iptables -t mangle –list

iptables -t nat –list

iptables -t nat –list-rules(这一条命令会比较好的把按照每条规则 把iptables的命令模式显示出来 查看起来比较方便)

如果不指定-t option,默认的情况下会采用 filter表的相关chain。

Iptables rules

关于构成chain的具体的rules有以下几点要特别留意:

rules包含 criteria 以及 target
如果creteria匹配 就按照rules中指定的规则进行,或者执行target中所指定的特殊的action。
如果creteria没有匹配到,就移动到下一条rule

target value

以下几个值是在target中可以指定的几个值,taeget实际的含义就是对匹配到的数据包所进行的操作(感觉称为action还比较恰当)

ACCEPT Firewall will accept the packet
DROP Firewall will drop the packet 将数据包丢弃
QUEUE Firewall will pass the packet to the userspace Firewall允许数据包进入userspace
RETURN 对于当前的这个packet,Firewall 会停止执行在当前chain中的起于的rules的集合,控制状态会返回调用这个chain的地方。
五链(五条规则链):
在内核空间中的5个位置对数据包进行处理:
1.内核空间中:从一个网络接口进来,到另一个网络接口去的
2.数据包从内核流入用户空间的
3.数据包从用户空间流出的
4.进入/离开本机的外网接口
5.进入/离开本机的内网接口

这五个位置也被称为五个钩子函数(hook functions),也叫五个规则链。
1.PREROUTING (路由前)
2.INPUT (数据包流入口)
3.FORWARD (转发管卡)
4.OUTPUT(数据包出口)
5.POSTROUTING(路由后)

任何数据包,只要经过本机,必将经过5条链中的其中一个链。

iptables的三个default chain

Input 用于对 incomming connection 进行控制。
Foward 转发链 用于那些转发的规则
Output 用于对 outgoing connections 进行控制。
在iptables工具安装好之后,这是哪个chain就会默认存在。

当然还可以自定义添加新的chain

规则的书写

我们已经解释了什么是规则,在内核看来,规则就是决定如何处理一个包的语句。

如果一个包符合所有的条件(就是符合matche语句),我们就运行target或jump指令。说白了做的事情也很简单,就是让具有某些固定特征的packet去做某些固定的事情,书写规则的语法格式是:
iptables [-t table] command [match] [target/jump]

如果你不想用标准的表,就要在[table]处指定表名(比如使用docker这个表)。一般情况下没有必要指定使用的表,因为iptables 默认使用filter表来执行所有的命令。也没有必要非得在这里指定表名,实际上几乎可在规则的任何地方。 当然,把表名在开始处已经是约定俗成的标准。

command告诉程序该做什么,比如:插入一个规则,还是在链的末尾增加一个规则,还是删除一个规则。

match细致地描述了包的某个特点,以使这个包区别于其它所有的包。在这里,我们可以指定包的来源IP 地址,网络接口,端口,协议类型,或者其他什么。

最后是数据包的目标所在。若数据包符合所有的match,内核就用target来处理它,或者说把包发往 target。比如,我们可以让内核把包发送到当前表中的其他链(可能是我们自己建立的),或者只是丢弃这个包而没有什么处理,或者向发送者返回某个特殊的应答。

-t中所使用的表,就是我们在前面提到的filter,nat,以及mangle当然也可以进行自定义。filter主要是过滤包的,nat主要是进行SNAT以及DNAT的转换,mangle主要是修改包头的一些内容,具体细节再具体看。

列出一些常见的,具体细节可以参考这里(https://www.frozentux.net/iptables-tutorial/cn/iptables-tutorial-cn-1.1.19.html#prelude),这里不对每个命令的具体情况进行列出,因为相关的文档已经写的极其详细了,只求可以根据相关资料看懂具体的每个规则,直接用实例进行分析:

这个是自己的虚拟机上,使用iptables-save命令得到的结果,iptables-save用来把当前的规则存入一个文件里以备iptables-restore使用,默认情况下,会把当前所有的表都显示出来,简单介绍下输出的格式,#后面的是注释。表都以 开始,例如 mangle。每个表都包含链和规则,链的详细说明是: [:]。例如,链的名字是 PREROUTING,策略是ACCEPT,然后是包记数器和字节计数器,这两个计数器和iptables -L -v输出中用到的计数器一样。每个表的描述都以关键字COMMIT结 束根据上面的iptables构成的方式,来逐条分析下:


#Generated by iptables-save v1.4.21 on Tue Feb 2 23:52:20 2016
*raw
:PREROUTING ACCEPT [4315:390114]
:OUTPUT ACCEPT [4574:355814]
COMMIT
# Completed on Tue Feb 2 23:52:20 2016
# Generated by iptables-save v1.4.21 on Tue Feb 2 23:52:20 2016
*nat
:PREROUTING ACCEPT [9:1052]
:INPUT ACCEPT [4:505]
:OUTPUT ACCEPT [253:16251]
:POSTROUTING ACCEPT [253:16251]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 192.168.122.0/24 -d 224.0.0.0/24 -j RETURN
-A POSTROUTING -s 192.168.122.0/24 -d 255.255.255.255/32 -j RETURN
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE
-A POSTROUTING -s 172.17.0.1/32 -d 172.17.0.1/32 -p tcp -m tcp --dport 8080 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 4194 -j DNAT --to-destination 172.17.0.1:8080
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.17.0.2:5000
COMMIT
# Completed on Tue Feb 2 23:52:20 2016
# Generated by iptables-save v1.4.21 on Tue Feb 2 23:52:20 2016
*mangle
:PREROUTING ACCEPT [198811:251002177]
:INPUT ACCEPT [181227:246899630]
:FORWARD ACCEPT [41:3725]
:OUTPUT ACCEPT [117195:115178820]
:POSTROUTING ACCEPT [134166:117729363]
-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
COMMIT
# Completed on Tue Feb 2 23:52:20 2016
# Generated by iptables-save v1.4.21 on Tue Feb 2 23:52:20 2016
*filter
:INPUT ACCEPT [485:42514]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [610:45091]
:DOCKER - [0:0]
-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT
-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A FORWARD -i virbr0 -o virbr0 -j ACCEPT
-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -o virbr0 -p udp -m udp --dport 68 -j ACCEPT
-A DOCKER -d 172.17.0.1/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8080 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
COMMIT
#Completed on Tue Feb 2 23:52:20 2016

首先可以看到一个基本的表:raw,nat,mangle,filter。以filter为例进行分析:
前4条比较类:

  • command -A INPUT 在INPUT链的链尾添加规则
  • match -i virbr0 通用匹配,以本包进入本机所使用的网络接口来匹配包,表示从这个接口进来的包都被match到。-p udp -m udp –dport 53 采用的是upd的网络协议,目的端口是53的packet 会被match到。 -m 参数含义?(其实只用 -p tcp 了话, iptables也会默认的使用 -m tcp 来调用 tcp模块提供的功能。但是 -p tcp 和 -m tcp是两个不同层面的东西,一个是说当前规则作用于 tcp 协议包,而后一是说明要使用iptables的tcp模块的功能 (–dport 等) 参考这里(http://bbs.chinaunix.net/thread-3674773-1-1.html))
  • target -j ACCEPT 表示实际所进行的操作,这里的ACCEPT表示接受对应的包。
    整理一下可以看到,前面四句话的意思是,对于从virbr0 interface进来的,目的端口为53与67的,采用协议为TCP以及UDP的packet,Filter表中的Input链采取ACCEPT的策略,允许其进入本机的网络栈。
-A FORWARD -o docker0 -j DOCKER

这句话的含义,对FORWARD表进行APPEND操作插入规则,具体规则是,从docker0网桥离开的包(匹配方式),跳到DOCKER链(操作方式)。

-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  • -m conntrack –ctstate RELATED,ESTABLISHED ???
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT

从docker0网桥进入的packet,并且不是从docker0网桥出去的packet,以及从docker0网桥出去的packet,全部采用ACCEPT策略。这里的FORWARD仅仅是体现了处理packet的rule的位置,每个rule的规定方式与其他的表中的规定方式是一样的,实际进行转发的操作是由内核中的相关机制进行的

实际问题

最常见的就是 host no reachable,比如在A机器上启动一个服务,之后再B机器上访问,总是报这个错(可以ping通,使用curl命令访问无法连通),好多时候可能是A机器的iptables规则配置出现了问题,由于一般在测试环境下,也没有什么特别的iptables限制规则,想想也知道,肯定是把不过滤掉的packet给过滤掉了,通常可以尝试一下清空Filter表iptables -t filter -F这应该是个小问题,但是总是遇到,有好几次捉急不知道怎么回事。

比如又一次启动flannel,就一直卡在那里,日志也没有输出,实际上是访问etcd访问不通,就是上面的原因。这就涉及到一个prerequest的问题,应该先确保etcd能访问通再启动flannel,实际上手工操作往往忽略这些看似基本但实际上又很重要的步骤。

推荐文章