本文将针对ovn实现安全组进行技术总结,分为ovn插件安全组实现,ovn逻辑流表,openflow流表三部分进行介绍,其中还会补充介绍ovn相关的一些协议和技术。
ovn基础概念
开头部分介绍几个ovn(ovs)的基础概念。
tunnel_key
ovn会在创建ovn datapath的时候,为它们分配一个唯一的tunnel _key,并同步到南向流表(详见ovn代码build_datapath
函数),如下datapath逻辑路由器和逻辑网络,可以看到share_router的key是3,share_net的key是1:
()[root@ovn-ovsdb-sb-0 /]# ovn-sbctl list Datapath_Binding |
创建ovn port时,为port分配其所在datapath下唯一的tunnel_key,并同步到南向流表(详见ovn代码build_ports函数),如下port在南向的数据,三张网卡属于同一个switch,其中两个是子网接口:
()[root@ovn-ovsdb-sb-0 /]# ovn-sbctl list Port_Binding |
以上资源归纳如下:
tunnel_key在ovn流表中起到了标识流的来源和去向的作用,怎么理解呢,ovn使用了如下三个寄存器:
- reg14:logical input port field,标识入口tunnel_key
- reg15:logical output port field,标识出口tunnel_key
- metadata:logical datapath field,标识所属的datapath的tunnel_key
ovn-controller在转换成openflow流表时,通过metadata+reg14标识流的来源,通过metadata+reg15标识流的去向。
举个例子:上面提到的网卡b5ca6f69-eb6d-4496-8df0-e558e1552167
的tunnel_key是2,所属datapath的tunnel_key是1,上联的是tapb5ca6f69
。
在openflow table0中标记了入口reg14=0x2,metadata=0x1:
cookie=0x0, duration=967010.862s, table=0, n_packets=93, n_bytes=16890, priority=100, |
在table25标记了出口reg15=0x2:
cookie=0x0, duration=1057442.127s, table=65, n_packets=118, n_bytes=19390, priority=100, |
logical port
上面提到的logical port,这种port主要是通过neutron create_port方式创建后的端口。
除此之外还有以下类型的端口,需要注意:
- localport端口:逻辑交换机和VIF之间的本地连接点。比如说使用Localport端口将metedata提供给驻留在每个hypervisor上的虚拟机。
- localnet端口:逻辑交换机和物理网络之间的连接点。即连接br-xx和br-int的patch端口,比如创建vlan network后,会生成命名为provnet-xxx的逻辑port(geneve网络没有),在ovs上会看到br-int与br-xx建立了patch。该patch就用于与物理网络通信。
- patch端口:表示逻辑交换机和逻辑路由器之间的连接点,比如创建子网接口后,会自动生成逻辑path端口用于子网和路由器,目前看来逻辑patch不会在ovs上生成。
pipeline
ovn通过流水线pipleline的方式处理逻辑流表,分为ingress和egress两个阶段:
- ingress的逻辑流表从table0开始(对应ovs流表从table8开始),安全组部分从table0~table6会涉及
- egress的逻辑流表从table0开始(对应ovs流表从table40开始),安全组部分从table0~table4会涉及
逻辑flow标记了所处的stage(ovn源码PIPELINE_STAGES),通过stage名称可以帮助了解flow的作用,类似这种:
table=0 (ls_in_port_sec_l2 )
ovs conntrack
ovs的conntrack功能增加了ct流表的概念,将需要跟踪状态的报文提交进ct里去,标记连接状态,供后续报文查询连接状态使用。
每个以”+“为前缀的标志,表示必须设置,或者以”-“为前缀的标志表示不能设置。这里面的zone用来隔离不同的跟踪会话,避免与其他会话冲突,ovn同步给ovs时使用reg13标记zone:
- reg13:Logical conntrack zone for lports
不过报文不走ct会对性能有很大提升,所以对性能有要求的场景建议使用无状态的acl。
ovn插件实现安全组
最新neutron已经将networking-ovn插件合并,纳入为一种ml2 driver,并实现了安全组功能。安全组的实现里引入了ovn的port group的功能。
ovn ml2 driver会在创建安全组的过程中,先创建好port group,然后配置acl规则到该port group,新增或删除port的时候,都会调用port group命令调整内部列表。所以port就不会直接与安全组打交道,而只是在port group内进行增删。
创建安全组: |
接下来,介绍几个处理细节。
规则direction
社区的安全组规则对应到ovn的acl时,实际的acl是作用到虚机上联的br-int上,所以作用的方向需要注意,常见的ovn acl命令如下:
from-lport 1002 (inport == @pg_99ead3ac_2539_49d2_b8fa_f46116a22955 && ip4) allow-related |
虚机端口出来的流量即egress,对应logic port就是入方向,到acl上是from-lport并指定logic port为inport;
进入虚机端口的流量即ingress,对应logic port就是出方向,到acl上是to-lport并指定logic port为outport。
规则优先级
安全组规则在配置给ovn时,指定了一个较高的优先级,目前插件侧设置为:
ACL_PRIORITY_ALLOW = 1002 |
这里有个需要注意的地方,查看acl规则对应logic流表,会发现优先级变成了2002和2001:
table=4 (ls_out_acl), priority=2002 , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (outport == @pg_f3a6bf24_858f_482f_81e8_eb450c527605 && ip4 && ip4.src == $pg_f3a6bf24_858f_482f_81e8_eb450c527605_ip4)), action=(next;) |
原因是ovn进行了修改增加了1000,避免与其它硬编码的flow优先级冲突:
/* Due to various hard-coded priorities need to implement ACLs, the |
allowed_address_pairs
插件调用ovn_client处理create_port时,会先获取port的addresses列表,包括allowed_address_pairs。
在调用ovn-client进行port创建/更新时,会将address配置进logic port里去。所以在ovn实现安全防护的时候,它只会允许addresses列表里的ip/mac通过。
举个例子,给port配置了allowed_address_pairs地址为10.0.0.3:
()[root@busybox-openstack-74787f576-pkffm /]# neutron port-show instance-DNWPye_share_net_5a03dfba -c allowed_address_pairs -c fixed_ips |
然后查看logic 流表,生成了允许10.0.0.3和192.168.111.175通过的规则,其余ip将被丢弃:
ingress: |
remote_group_id
插件配置远端安全组时,通过获取远端安全组的port group,然后将远端安全组的ip集合作为匹配规则中源ip和目的ip,类似这样‘‘pg_f3a6bf24_858f_482f_81e8_eb450c527605_ip4’’:
match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (outport == @pg_f3a6bf24_858f_482f_81e8_eb450c527605 && ip4 && ip4.src == $pg_f3a6bf24_858f_482f_81e8_eb450c527605_ip4))) |
远端安全组的ip集合命名如下:
def ovn_pg_addrset_name(sg_id, ip_version): |
neutron_pg_drop
插件启动之初,也就是进程pre_fork阶段,会首先创建一个名为neutron_pg_dro
p的port group,配置的acl动作是drop。加入该port group需要满足以下条件:
- 非trusted port,即device_owner不为空,且值不是以
network:
开头的 - port _security为enable
总结来说,虚机的port是会被纳入到neutron_pg_drop
中的,当访问虚机相关的流量没有匹配到安全组规则时,将会匹配neutron_pg_drop
对应的规则,即报文丢弃。不过该设计会导致单个pg_drop性能问题,我们对其进行了修改,不使用唯一port-group去存,而是以network为单位创建pg_drop。
逻辑流表实现安全组
以网卡b5ca6f69-eb6d-4496-8df0-e558e1552167
为例子,reg14=0x2
,metadata=0x1
,分析ingress逻辑流表。
table0(ls_in_port_sec_l2),ovs参照table8:
build_lswitch_lflows_admission_control: |
table1(ls_in_port_sec_ip),ovs参照table9:
build_port_security_ip(port_security,allowed-address-pairs): |
table2(ls_in_port_sec_nd),ovs参照table10:
build_port_security_nd: |
table3(ls_in_pre_acl),ovs参照table11:
build_pre_acls: |
table4(ls_in_pre_lb),ovs参照table12:
build_pre_lb: |
table5(ls_in_pre_stateful),ovs参照table13:
build_pre_stateful: |
table6(ls_in_acl),ovs参照table14:
build_acls: |
ovs流表实现安全组
上文提到的逻辑流表,对照openflow流表如下:
table8: |
个人分析,欢迎指正,若转载请注明出处!