过去一直以为,neutron的安全组是由iptables实现,网上不少文章也印证这个想法,他们的依据如下图:
ovs的Port不具备安全功能,因此接入一个qbr-xxx的bridge,这个bridge的实现载体是Linux Bridge,借助iptables实现防火墙功能——原本我也是这么认为的。
直到有一次,在查找具体的安全组iptables规则时,并没有发现相关的rules,对此产生了怀疑。
查看ml2_conf.ini的配置文件后,发现我们环境中的firewall driver是openvswitch,那不用说肯定是通过流表实现安全组了。firewall_driver属性在ml2/plugins/ml2_conf.ini配置文件中:
以流表形式存在的安全组,对比iptables的实现,是更具备性能优势的。iptables规则的存在,意味着数据包需要走一遍内核协议栈,对性能的损耗十分明显。
在我们的生产环境中,每增加一条iptables规则,就会损失2w的PPS,iptables规则的数量是需要得到控制的。查看neutron(R版)安全组相关的代码:
neutron的通信机制是以neutron-server接受restful API请求,操作DB,并转发给各类agent工作的流程。图中代码位于ovs-agent部分,文件路径是neutron/agent/securitygroups_rpc.py。
neutron-server收到的安全组更新请求,在操作完数据库后,通过rabbitmq发送给ovs-agent,异步调用此函数,通过驱动实现抽象方法,调用到具体的驱动中去干活。
可以看到,neutron支持iptables和ovs两种安全组驱动,用户通过firewall_driver可以选择。第一条是抽象类方法,第二条是驱动实现的模板,后面的IptablesFirewallDriver和OVSFirewallDriver才是实现的驱动。
二者都是通过刷新缓存,去调用命令行添加流表或iptables rules。
通过一条命令,为环境中的某个port添加一条安全组:
openstack port set 90ee4f00-3fe9-4e71-85da-e8ce19901091 --security-group fe66a3c9-723c-4faf-bbbd-d7cffe78d663
在ovs-agent的日志中抓到如下内容:
梳理一下日志内容,意思是添加这样一条安全组规则:
'direction': 'ingress', 'protocol': 6, 'ethertype': 'IPv4', 'port_range_max': 65535, 'source_ip_prefix': '0.0.0.0/0', 'port_range_min': 1
会为添加如下的流表:
- dl_type网络类型:2048即十六进制0x800,指代IPv4协议
- reg_port记录端口:目标端口在openflow中的编号,指的是添加安全组的端口
- nw_proto协议:6代表tcp
- tcp_dst:端口按位匹配,这个端口是匹配的端口,即安全组规则中指定的1-65535
(openflow流表在表示端口范围时,有两种选择,例如1-65535,要么它添加65535条流表;要么通过port/mask的方式,1到65535按二进制展开,以掩码记录一个范围,与CIDR相似,如上案例中所有tcp_dst组合起来刚好是1-65535) - table 82:流表添加到82表
- resubmit(,83):转发到83表
- priority 77:优先级77
所有能匹配到流表规则的,都会被转发到83表,即代表着已经通过安全组了,83表可以转发给连接安全组的tap口。