1. 首页 > 百货 > 其他百货

OpenStack 深入浅出解析 安全组实现原理

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

联系我们

QQ号:***

微信号:***

工作日:9:30-18:30,节假日休息