OpenStack安全组源码分析

作者简介:Mr Huang,SDN/NFV研发工程师,技术擅长:SDN、NFV、Openstack

1 概述

本文以Queens版本为基础对Openstack Security Group功能模块的源码进行分析,希望通过源码分析能够帮助我们更好地掌握其背后实现的原理,进一步提升我们的运维、二次开发等能力。

在阅读这篇文章之前,建议先阅读作者的前一篇文章
《iptables在Openstack安全组中的应用分析》,这篇文章主要介绍了一些安全组的基础知识及实验操作分析过程。

2总体框架

如上图所示,ML2 Plugin收到用户发来的安全组资源请求时,校验通过后会将资源信息存储到数据库(DB)上。接着通过RPC通道将资源更新信息通知到其他节点(计算节点、网络节点)的Neutron L2 Agent(Neutron Linuxbridge Agent或Neutron Openvswitch Agent)。通过配置好的driver将安全组资源信息转换为iptables规则或openflow流表并下发至相关的Linuxbridge或Openvswitch桥。

3类关系

这里的类设计主要分析几个比较重要的类。下面图中的类图关系箭头,除了红色箭头之外,其他箭头含义都是遵循标准UML类图进行建模。

3.1 Plugin/DB类

3.2 RPC类



下面这个类关系图主要是针对Linuxbridge Agent的场景。

下面这个类关系图主要是针对Openvswitch Agent的场景。

3.3 Agent类

下面这个类关系图主要是针对Linuxbridge Agent的场景。

下面这个类关系图主要是针对Openvswitch Agent的场景。

3.4 Driver类

4 代码结构

主要分为如下几个部分:

  • Plugin
    • 安全组模块并没有独立的一个Plugin,它是从属于ML2 Plugin。在neutron/plugins/ml2/plugin.py源码文件中,类class Ml2Plugin通过继承neutron/db/securitygroups_rpc_base.py源码文件中的类class SecurityGroupServerRpcMixin来执行安全组资源的CRUD操作。
    • neutron/extensions/securitygroup.py
      该源码文件主要定义了安全组、安全组规则各个数据字段对应在restapi接口中的操作属性。即:数据字段是否允许创建、是否允许更新、是否返回给用户呈现等。同时还定义了Plugin基类class SecurityGroupPluginBase,该基类定义了安全组、安全组规则CRUD的抽象方法。
  • DB
    定义了安全组资源的数据库表格及其相应的数据库CRUD操作。同时发出相应的资源本地通告事件。
    • neutron/db/models/securitygroup.py
      定义了几个数据库表,包含:SecurityGroup(安全组)、DefaultSecurityGroup(默认安全组)、SecurityGroupPortBinding(Neutron Port与SecurityGroup关联)、SecurityGroupRule(安全组规则)。
    • neutron/db/port_security/models.py
      该源码文件与安全组有强关联,因此这里也提出来进行阐述。主要定义了几个数据表,包含:PortSecurityBinding(主要记录了Neutron Port是否开启了安全组特性)、NetworkSecurityBinding(主要记录了Neutron Network是否开启了安全组特性,如果开启了安全组特性,则在该Network上创建的Neutron Port都自动开启安全组特性)。
    • neutron/db/securitygroups_db.py
      该源码文件是安全组资源操作数据库的核心文件。这里定义了安全组、安全组规则、默认安全组资源的CRUD方法。实现了从Plugin接收资源请求、执行校验、操作数据库、发出资源本地通告等流程。
    • neutron/objects/securitygroup.py
      该源码文件用于构建安全组、安全组规则、默认安全组资源对象。
  • Config
    在源码文件neutron/conf/agent/securitygroups_rpc.py定义了安全组相关的配置选项。如果想进一步增加其他新配置选项,可以在这个文件里进行添加即可。
  • RPC
    RPC主要是用于Plugin和Agent之间的交互。对于安全组模块而言,其RPC交互代码较为隐藏,对于Linuxbridge Agent和Openvswitch Agent而言,对应的RPC交互又存在不同。因此安全组的RPC交互也是本文的重点分析之一。
    • neutron/api/rpc/handlers/securitygroup_rpc.py
      • class SecurityGroupServerRpcApi
        RPC客户端(对应的服务端为class SecurityGroupServerRpcCallback),用在Agent端。在neutron/plugins/ml2/drivers/agent/_common_agent.py源码文件中setup_rpc方法中,创建了该类的对象,对象传递给了SecurityGroupAgentRpc,用来向Plugin请求安全组相关的信息。
      • class SecurityGroupServerRpcCallback
        RPC服务端(对应的客户端为class SecurityGroupServerRpcApi),用在Plugin端。
      • class SecurityGroupAgentRpcApiMixin
        RPC客户端(对应的服务端为class SecurityGroupAgentRpcCallbackMixin),用在Plugin端。在neutron/plugins/ml2/plugin.py源码文件中的_start_rpc_notifiers方法中,创建了class AgentNotifierApi,其继承了class SecurityGroupAgentRpcApiMixin。在neutron/db/securitygroups_rpc_base.py源码文件中class SecurityGroupServerNotifierRpcMixin中会使用class AgentNotifierApi对象调用class SecurityGroupAgentRpcApiMixin里的方法,进而通过RPC通知到服务端。
      • class SecurityGroupAgentRpcCallbackMixin
        RPC服务端(对应的客户端为class SecurityGroupAgentRpcApiMixin),用在Agent端。在neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py源码文件中的class LinuxBridgeRpcCallbacks继承了此类。class LinuxBridgeRpcCallbacks对象会作为rpc topic=’q-agent-notifier-security_group-update’的endpoints。因此Plugin通过RPC通告安全组资源操作最终会回调class SecurityGroupAgentRpcApiMixin里的方法。
      • class SecurityGroupServerAPIShim
        Agent端。用来取代class SecurityGroupServerRpcApi。class SecurityGroupServerAPIShim这个类中有个成员rcache,该成员是class RemoteResourceCache类对象(neutron/agent/resource_cache.py)。要分析清楚这里的RPC流程,需要回到Openvswitch agent的初始化阶段:在neutron/plugin/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py源码文件中的类class OVSNeutronAgent的setup_rpc方法,创建了class OVSPluginApi对象,创建此对象的过程中会创建class RemoteResourceCache类对象,之后调用了class RemoteResourceCache类的start_watcher方法。start_watcher方法中会创建class RemoteResourceWatcher(neutron/agent/resource_cache.py),class RemoteResourceWatcher类方法_init_rpc_listeners会监听topic=’neutron-vo-%(resource_type)s-%(version)s’的rpc通知。对于安全组资源而言,其中的resource_type为SecurityGroup或SecurityGroupRule。最终会回调到class SecurityGroupServerAPIShim类中注册的函数。具体流程参见第6节。
    • neutron/plugins/ml2/ovo_rpc.py
      • class OVOServerRpcInterface
        该类是class _ObjectChangeHandler的封装类,仅此而已。
      • class _ObjectChangeHandler
        该类会针对多类资源(网络、子网、端口、安全组、安全组规则)订阅各类事件的本地通告(本地,非RPC)回调函数。事件涉及有AFTER_CREATE, AFTER_UPDATE, AFTER_DELETE。当收到本地事件通告后,会调用class ResourcePushRpcApi(下面会介绍)将事件通过RPC推送到Agent端。对于安全组资源而言,在neutron/db/securitygroups_db.py中CRUD安全组或安全组规则资源后,都会发出对应的事件通告。class _ObjectChangeHandler收到本地通告后,再通过RPC推送到Agent端,其中rpc topic= topic=’neutron-vo-%(resource_type)s-%(version)s’。
    • neutron/api/rpc/handlers/resources_rpc.py
      • class ResourcesPullRpcApi
        Agent端,RPC客户端(对应服务端为class ResourcesPullRpcCallback)。用于主动从Neutron Server拉取资源信息。在class RemoteResourceCache(neutron/agent/resource_cache.py)有用到。
      • class ResourcesPullRpcCallback
        Plugin端,RPC服务端(对应客户端为class ResourcesPullRpcApi)。
      • class ResourcesPullRpcApi
        Plugin端,RPC客户端(对应服务端为class ResourcesPullRpcCallback)。用于从Plugin端推送资源更新到Agent端。在class OVOServerRpcInterface(neutron/plugins/ml2/ovo_rpc.py)有用到。
      • class ResourcesPullRpcCallback
        Agent端,RPC服务端(对应客户端为class ResourcesPullRpcApi)。在class RemoteResourceWatcher(neutron/agent/resource_cache.py)有用到。
  • Agent
    和Plugin一样,安全组模块也没有独立的一个Agent,它是从属于Neutron L2 Agent。例如Neutron Linuxbridge Agent或Neutron Openvswitch Agent。这篇文章我们重点分析Neutron Linuxbridge Agent和Neutron Openvswitch Agent。其他L2 Agent应该也是类似的。
    • Neutron Openvswitch Agent
      在neutron/plugin/ml2/drivers/openvswitch/agent/ovsneutronagent.py源码文件中的类class OVSNeutronAgent,在其__init方法中创建了类class SecurityGroupAgentRpc(在neutron/agent/securitygroups_rpc.py源码文件)对象,该对象我们可以理解为安全组模块的Agent。
    • Neutron Linuxbridge Agent
      在neutron/plugins/ml2/drivers/agent/_common_agent.py源码文件中的类class CommonAgentLoop(这个类对象会是在linuxbridge agent main函数中创建的,会被linuxbridge agent引用的)setup_rpc方法中创建了类class SecurityGroupAgentRpc(在neutron/agent/securitygroups_rpc.py源码文件)对象,该对象我们可以理解为安全组模块的Agent。
  • Driver
    在Agent中,class SecurityGroupAgentRpc的_init_方法中会根据我们的配置文件加载对应的安全组driver。主要有四个driver,如下:
    • iptables
      此driver对应在neutron/agent/linux/iptables_firewall.py源码文件中的类class IptablesFirewallDriver。该driver主要用在linuxbridge作为ml2机制驱动的时候,底层是用iptables+connection track来实现的。
    • iptables_hybrid
      此driver对应在neutron/agent/linux/iptables_firewall.py源码文件中的类class OVSHybridIptablesFirewallDriver。该类继承了IptablesFirewallDriver。该driver主要用在openvswitch作为ml2机制驱动的时候,底层是用iptables+connection track来实现的。
    • noop
      此driver对应在neutron/agent/firewall.py源码文件中的类class NoopFirewallDriver。该driver实现的是一个空驱动,即没有任何的具体实现。因此,如果想不要安全组的功能特性,可以将driver配置为noop。
    • openvswitch
      此driver对应在neutron/agent/linux/openvswitch_firewall.py源码文件中的类class OVSFirewallDriver。该driver主要用在openvswitch作为ml2机制驱动的时候,底层是用openflow+connection track来实现的。

5 本地回调

回顾上面第4节的内容,有部分类图关系中标注了”本地通告”字样,这个”本地通告”的作用就是本地进程注册监听某类资源事件回调的过程。

关于本地回调,源码上对应有两处,如下:

  • neutron/api/rpc/callbacks
    此目录下,对于安全组模块,涉及重要的源码文件有如下:
    • resource_manager.py
      此源码文件中,首先定义了一个基础类ResourceCallbacksManager。class ProducerResourceCallbacksManager和class ConsumerResourceCallbacksManager分别继承了此基类。这两个子类中都有一个重要的成员_callbacks,用于记录下对应资源的回调函数。因此这个源码文件的作用是用来维护管理各类资源类型对应的回调函数,包含:注册回调函数、解注册回调函数、获取回调函数信息以及清除回调函数信息。
    • consumer/registry.py
      此源码文件主要针对被动消费者。消费者通过调用注册函数register或subscribe提前注册对某类资源的回调函数,这个时候消费者并不会去主动获取资源信息,而是由生产者主动通过函数push回调消费者之前注册的函数(允许一类资源注册多个回调函数),从而将资源推送给消费者。此源码文件在安全组模块中的class RemoteResourceWatcher(neutron/agent/resource_cache.py)类中用到,在class RemoteResourceWatcher类中用于注册各类资源(含网络、子网、端口、安全组、安全组规则等)的回调,提供的回调方法是resource_change_handler,对应的生产者为class ResourcesPushRpcCallback(neutron/api/rpc/handlers/resources_rpc.py),class ResourcesPushRpcCallback类为RPC服务端的endpoint,通过rpc调用push函数,将rpc的消息转换为本地通告推送给class RemoteResourceWatcher。
    • producer/registry.py
      此源码文件主要针对主动消费者。生产者首先调用provide函数注册对某类资源的回调函数,当消费者需要某类资源时,主动调用pull函数获取回调函数(只允许一类资源注册一个回调函数,因为资源的提供者只能有一个)进而调用回调函数获取具体的资源信息。此源码文件在安全组模块中暂未看到有哪个地方有使用。
  • neutron/callbacks
    • manager.py
      此源码文件提供了class CallbackManager类,该类有一个重要的成员_callbacks,用于记录某类资源某类事件对应注册的回调函数。一个回调函数支持注册多类资源多个事件,一类资源的某类事件支持接受多个回调函数的注册。该类的重要成员记录回调函数的方法为:self._callbacks[resource][event][callback_id] = callback。class CallbackManager类不仅提供了注册函数、解注册函数,还提供了通告/发布函数(publish/notify)。通告/发布函数会根据某类资源的具体事件获取到已经注册的回调函数(可能有多个),遍历每个回调函数并调用,将资源的事件通告信息推送给消费者。
    • registry.py
      对class CallbackManager类的封装。上层应用主要通过此源码文件提供的方法进行操作。此源码文件还提供了一个函数receives,该方法在neutron的源码中大量使用,作用就是通过注解的方法来完成回调函数的注册,如下面给出的示例(如下图所示):通过添加注解,调用receives函数,实现对PORT资源,多个事件的回调函数注册,对应的回调函数即为notifysgonportchange方法。此源码文件在安全组模块中大量被使用,如在class SecurityGroupServerAPIShim中的__init和register_legacy_sg_notification_callbacks方法中就注册了对安全组、安全组规则更新和删除事件的回调。对应的生产者则在class RemoteResourceCache中的record_resource_update和record_resource_delete方法。又如在class _ObjectChangeHandler中__init方法针对多类资源(含网络、子网、端口、安全组、安全组规则等)多个事件注册了回调函数(handle_event),对应的生产者为class SecurityGroupDbMixin中的_registry_notify函数。

6 业务流

这节主要分析安全组资源从ML2 CRUD操作到Agent处理的整个业务流程。主要分两个场景阐述,即:Linuxbridge Agent场景和Openvswitch Agent场景。

6.1 Linuxbridge Agent场景

6.1.1 Plugin端

6.1.2 Agent端

6.2 Openvswitch Agent场景

6.2.1 Plugin端

6.2.2 Agent端

7 总结

本文主要从代码结构、总体框架、类关系、本地回调以及业务流程对openstack安全组模块进行了详细的阐述。通过分析能够更加清楚openstack安全组模块背后的实现细节。希望能够对想深入理解openstack安全组的朋友有较好的帮助。同时对文章所述内容及观点,如有不恰当或不正确之处,还希望各位前辈指正,万分感谢!

关于上述6.1和6.2节描述的从Plugin到Agent之间的安全组业务流程。这个流程并未给出L2 Agent在处理Neutron Port(创建、更新、删除)时是如何与安全组Agent、Driver之间进行交互的。由于篇幅原因,这个流程将放到下一篇文章进行详细阐述,敬请期待!


  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接https://www.sdnlab.com/23893.html
分享到:
相关文章
条评论

登录后才可以评论

pingcuit 发表于20-03-03
3