1、iptables简介
1.1 iptables概述
在介绍OpenStack安全组前先简单介绍下iptables,其实iptables只是一个用户空间的程序,真正干活的其实是Linux内核netfilter,通过iptables创建新规则,其实就是在netfilter中插入一个hook,从而实现修改数据包、控制数据包流向等,对iptables使用方法不熟悉的可以参考图文并茂理解iptables[1].
简单地说,iptables就是通过一系列规则条件匹配执行指定的动作,因此一条规则就是由条件+动作构成,条件比如源IP地址、四层协议、端口等,动作如拒绝、通过、丢弃、修改包等,动作通常通过-j参数指定。
比如拒绝192.168.1.2访问目标22端口,只需要添加如下iptables规则:
如上:
1.2 iptables匹配条件
除了以上的-s、-p、--dport等参数作为匹配条件外,iptables还支持如-d匹配目标IP地址,-i、-o分别指定从哪个网卡进入的以及从哪个网卡出去的。当然这些匹配条件还不够,甚至都不支持匹配MAC地址。iptables为了满足不同的需求,通过扩展模块支持更多的匹配条件,主要分为如下两类:
不同的扩展模块支持不同的参数,比如mac模块,支持--mac-source参数。
使用扩展模块必须通过-m参数加载,之前我一直以为-m是--module的缩写,看iptables的man手册才发现其实是--match的缩写,不过我们只需要知道是加载扩展模块的功能就可以了。
比如我们不允许MAC地址为FA:16:3E:A0:59:BA通过,通过如下规则配置:
iptables的扩展模块非常多,具体可以通过man iptables-extensions命令查看,不过OpenStack安全组用到的并不多:
1.3 iptables执行动作
前面提到iptables通过-j指定执行的动作(target),iptables常见的target如下:
当然还有实现NAT的SNAT、MASQUERADE、DNAT,因为安全组实现涉及不到,因此不做详细介绍,另外还有RETURN以及指向另一个链的动作,等后面介绍了子链再讨论。
动作通常都是短路的,也就是说一旦匹配规则并执行动作,就不会继续往后去匹配该链的其他规则了,当然这并不是绝对的,比如LOG动作就是例外,执行该动作后会继续匹配下一条规则。
1.4 iptables链
前面提到iptables一共有5条链,并且链可以认为是一个单向链表,问题来了,当接收到一个新包,到底是如何匹配规则的。这里我直接引用图文并茂理解iptables的图[1]:
(1) 数据包首先到达PREROUTING链,然后按照raw、mangle、nat的顺序匹配执行定义在PREROUTING的规则。
(2)接下来经过路由判断,如果包是发给自己的则流向INPUT链,然后由INPUT链发给用户空间进程处理。如果不是发给自己的包,则流向FORWARD表,同样按照raw-> mangle -> nat -> filter表依次匹配执行链上的规则。
(3) 同理,ONPUT链、POSTROUTING链,包流向方向,直接看图,非常清晰,这里不再赘述。
前面提到每条链上都可以插入规则,需要注意的是这些规则是有顺序的,iptables每次匹配时都是从第一条规则开始匹配,依次匹配下一条,一旦匹配其中一条规则,则执行对应的动作。
肯定有人会疑问,如果这条链上的规则都不匹配该怎么办,答案是取决于该链的默认策略(policy)。如果该策略是DROP,则最后没有匹配的包都将丢弃,即该链时白名单列表。如果默认策略是ACCEPT,则最后没有匹配的包都会通过,即该链时黑名单列表。当然通常policy都设置为ACCEPT,因为配置为DROP太危险了,比如清空规则立马就相当于全不通了,如果你通过SSH连接的服务器,则立即中断连接了,不得不通过vnc或者带外console连接重置,所以不建议修改policy。
通过如下命令查看filter表各个链的默认策略:
如果一条链规则特别多且复杂,管理起来非常麻烦,因此很有必要对链根据功能分组。iptables通过自定义链实现。用户可以通过iptables -Nname创建一个新链,然后和内置链一样可以往新链中添加规则。但是需要注意的是,自定义链不能独立存在,必须挂在内置5条链下面,即必须是内置链的子链。
前面1.3节提了下-j可以指定一条新链,这里的新链即子链,即iptables是通过-j把子链挂到某个规则下面。比如创建一个允许SSH访问的白名单列表,可以创建一个新的子链,SSH相关的策略都放在这个新链中:
以上第二条命令表示将所有访问本机端口22的包都放到SSH_Access_List这条子链上处理,然后这条子链上添加了许多白名单规则,由于进到这个子链的一定是目标22端口的,因此规则无需要在指定--dport参数,最后一个DROP表示不在白名单列表中的包直接丢掉。
需要注意的是白名单规则中的动作不是ACCEPT而是RETURN,这两者有什么区别呢?ACCEPT表示允许包直接通过INPUT,不需要再匹配INPUT的其他规则。而RETURN则表示只是不需要再匹配该子链下的后面规则,但需要返回到该子链的母链的规则或者子链继续匹配,能不能通过INPUT关卡取决于后面的规则。
另外需要注意的是,前面提到内置的5条链可以配置policy,当所有规则都不匹配时,使用policy对包进行处置。但是,自定义链是不支持policy的,更确切的说,不支持设置policy,因为自定义链的policy只能是RETURN,即如果子链的规则都不匹配,则一定会返回到母链中继续匹配。
1.5 iptables总结
本小节简单介绍了iptables的功能和用法,总结如下:
1、iptables通过规则匹配决定包的去向,规则由匹配条件+动作构成,规则通过-I、-A插入。
2、五链五表,五链为PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING,五表为raw、mangle、nat、filter、security。链、表、规则都是有顺序的。
3、当链中的所有规则都不匹配时,iptables会根据链设置的默认策略policy处理包,通过policy设置为ACCEPT,不建议配置为DROP。
4、可以创建子链挂在内置链中,子链的policy为RETURN,不支持配置。
5、匹配条件包括基本匹配条件以及扩展模块提供的扩展匹配条件,扩展匹配条件通过-m参数加载,需要记住的扩展模块为comment、tcp、udp、icmp、mac、state、physdev、set。
6、常见的iptables动作(target)为ACCEPT、DROP、RETURN、LOG以及跳转到子链。
2、OpenStack安全组简介
2.1 Neutron安全组 VS Nova安全组
OpenStack安全组最开始是通过Nova管理及配置的,引入Neutron后,新OpenStack安全组则是通过Neutron管理,并且关联的对象也不是虚拟机,而是port。我们在页面上把虚拟机加到某个安全组,其实是把虚拟机的port关联到安全组中。
由于历史的原因,可能还有些版本的Nova依然保留着对安全组规则的操作API,不过不建议使用,建议通过Neutron进行安全组规则管理。
2.2 security group VS firewall
很多刚开始接触OpenStack的用户分不清楚安全组(securitygroup)和防火墙(firewall)的区别,因为二者都是做网络访问控制的,并且社区都是基于iptables实现的。其实二者的区别还是比较大的:
2.3 安全组用法介绍
前面介绍了安全组,安全组其实就是一个集合,需要把安全组规则放到这个集合才有意义。
Neutron通过security-group-create子命令创建安全组,参数只有一个name,即安全组名称:
不过Neutron创建的新安全组并不是一个空规则安全组,而是会自动添加两条默认规则:
即禁止所有的流量访问,允许所有的流量出去。
创建了安全组后,就可以往安全组里面加规则了。Neutron通过security-group-rule-create子命令创建,涉及的参数如下:
创建一条安全组规则,只允许192.168.4.5访问虚拟机SSH 22端口:
需要注意的是创建安全组和安全组规则只是一个逻辑操作,并不会创建任何iptables规则,只有当安全组被关联到port时才会真正创建对应的iptables规则。
关联安全组通过Neutron的port-update命令,比如要把虚拟机uuid为38147993-08f3-4798-a9ab-380805776a40添加到该安全组:
安全组命令操作参数较多,相对复杂,可以通过Dashboard图形界面操作,如图:
具体操作这里不多介绍。
3、安全组实现原理分析
3.1 虚拟机网络流向路径
Linux网络虚拟化支持linux bridge以及openvswitch(简称OVS),OpenStack Neutronml2驱动二者都支持,目前大多数使用的是OVS。
不过早期的iptables不支持OVS bridge以及port,因此为了实现安全组,虚拟机的tap设备并不是直接连接到OVSbridge上,而是中间加了一个Linux bridge,通过veth pair连接Linux bridge以及OVS bridege,这样就可以在Linuxbridge上添加iptables规则实现安全组功能了。
目前大多数的OpenStack环境还遵循如上规则,简化的虚拟机流量路径如下:
其中X、Y、Z为虚拟机port UUID前11位。
3.2 安全组规则挂在iptables哪条链?
根据前面的基础,不难猜出安全组的iptables规则肯定是在filter表实现的,filter表只涉及INPUT、FORWARD、OUTPUT三条链,iptables规则流向图可以简化为:
做过主机防火墙的可能第一直觉会认为安全组规则会挂在INPUT以及OUTPUT链上,但根据上面的流程图,如果包不是发给自己的,根本到不了INPUT以及OUTPUT,因此显然在INPUT、OUTPUT根本实现不了安全组规则,因此安全组的iptables规则肯定是在FORWARD链上实现的,也就是说计算节点不处理虚拟机的包(发给自己的包除外),只负责转发包。
3.3 安全组规则定义
为了便于后面的测试,我提前创建了一台虚拟机int32bit-server-1,IP为192.168.100.10/24,portUUID为3b90700f-1b33-4495-9d64-b41d7dceebd5,并添加到了之前创建的int32bit-test-secgroup-1安全组。
我们先导出本计算节点的所有tap设备对应Neutron的port,该脚本在githubint32bit/OpenStack_Scripts可以下载:
根据前面的分析,虚拟机安全组是定义在filter表的FORWARD链上的,我们查看该链的规则:
FORWARD链先跳到neutron-filter-top子链上,neutron-filter-top链会又跳到neutron-openvswi-local,而neutron-openvswi-local链是空链,因此会返回到母链FORWARD上,因此这里第一条规则其实没啥用。
返回到FORWARD链后继续匹配第2条规则,跳转到了neutron-openvswi-FORWARD,我们查看该链的规则:
该链上一共有4条规则,第1、2台规则对应的tap设备分别为dhcp以及router_interface端口,即允许DHCP以及网关的port通过。
而tap3b90700f-1b显然是虚拟机port对应的tap设备(名称为tap+portUUID前11位),第3、4规则表明无论是从这个tap设备进的还是出的包都进入子链neutron-openvswi-sg-chain处理。
我们继续查看neutron-openvswi-sg-chain查看链:
从规则我们可以看出:
显然neutron-openvswi-i3b90700f-1和neutron-openvswi-o3b90700f-1分别对应安全组的入访规则和出访规则,即虚拟机的入访规则链为neutron-openvswi-i+ port前缀,虚拟机的出访规则链为neutron-openvswi-i + port前缀。
3.4 安全组入访规则
由3.3我们了解到,安全组入访规则链为neutron-openvswi-i3b90700f-1,我们查看该链规则:
一共有6条规则:
安全组入访规则中第1、2、3、5、6都是固定的,当有新的安全组策略时就往第4条规则后面追加。
3.5 安全组出访规则
由3.3我们了解到,安全组入访规则链为neutron-openvswi-o3b90700f-1,我们查看该链规则:
一共有8条规则:
3.6 安全组使用安全组作为匹配条件
前面2.3节提到,安全组不仅支持通过IP地址段作为源或者目标的匹配条件,还支持通过指定另一个安全组,这种情况怎么处理呢。
为了测试我把创建了一个新的安全组int32bit-test-secgroup-2以及新的虚拟机int32bit-server-2(192.168.100.7),并且int32bit-server-2关联了安全组int32bit-test-secgroup-2。
同时在int32bit-test-secgroup-1上增加一条入访规则,允许关联int32bit-test-secgroup-2的虚拟机访问8080端口:
我们查看虚拟机入访规则链neutron-openvswi-i3b90700f-1:
我们发现插入了一条新的规则,编号为4。该规则使用了set扩展模块,前面介绍过set是用来匹配ipset的,后面的参数NIPv4fc83d82a-5b5d-4c90-80b0-为ipset名,显然是由NIPv4+安全组UUID前缀组成。
我们查看该ipset:
可见192.168.100.7在ipset集合中。
因此OpenStack安全组使用安全组作为匹配条件时是通过ipset实现的,每个安全组会对应创建一个ipset集合,关联的虚拟机IP会放到这个集合中,iptables通过ipset匹配实现了安全组匹配功能。
4、安全组anti snoop功能
前面3.5节提到第2条规则,所有的包都会先进入neutron-openvswi-s3b90700f-1子链处理,这个链是干什么的呢?
我们首先查看下里面的规则:
这条链的处理逻辑很简单,只放行IP是192.168.100.10并且MAC地址是FA:16:3E:A0:59:BA的包通过。这其实是Neutron默认开启的反欺骗antisnoop功能,只有IP和MAC地址匹配Neutronport分配的才能通过。换句话说,你起了个虚拟机IP为192.168.3.1,然后自己手动把网卡的IP篡改为192.168.3.2,肯定是不允许通过的。
但是呢,我们业务又往往有virtualip的需求,最常见的如haproxy、pacemaker的vip。OpenStack考虑了这种需求,支持用户添加白名单列表,通过port的allowedaddress pairs配置。
比如我有两个虚拟机,IP分别为192.168.0.10、192.168.0.11,申请了一个port192.168.0.100作为这个两个虚拟机的vip,可以通过Neutron更新port信息实现:
添加后我们再查看下neutron-openvswi-s3b90700f-1链规则:
可见在最前面添加了一条规则允许IP为192.168.0.100的包通过,此时在虚拟机192.168.0.10上把IP改为192.168.0.100也可以ping通了。
5、虚拟机访问宿主机怎么办?
我们已经知道,安全组是在filter表的FORWARD链上实现的,但如果虚拟机的包是去往宿主机时,由于内核判断目标地址就是自己,因此不会流到FORWARD链而是发往INPUT链,那这样岂不就是绕过安全组规则了吗?
解决办法很简单,只需要把neutron-openvswi-o3b90700f-1再挂到INPUT链就可以了。
我们查看INPUT链规则:
即:
有人可能会问,那宿主机发往虚拟机的包会出现问题吗?需要在OUTPUT链上添加规则吗?答案是不需要,因为从OUTPUT直接出去,当作正常流程走就可以了。
6、总结
本文首先简单介绍了下iptables,然后介绍OpenStack安全组,最后详细分析了安全组的实现原理。
另外写了一个脚本可以快速导出虚拟机的iptables规则,需要在计算节点上运行:
付广平,任职某银行云技术管理中心,负责云计算相关技术研究。毕业于北京邮电大学,从2013开始从事OpenStack相关工作,参与了OpenStack Nova、Cinder、Oslo等项目社区开发。
本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载者并注明出处:https://jmbhsh.com/qitabaihuo/34559.html