前面了解了创建安全组时neutron server侧的具体流程,当给虚机绑定安全组时我们看下具体做了啥
- 创建一个自定义的安全组,虚机使用该安全组,然后变更该安全组
#neutron security-group-create sg-debug
#neutron security-group-rule-create sg-debug --direction ingress --protocol tcp --port-range-min 22 --port-range-max 22 --ethertype IPv4
-
neutron/db/securitygroups_rpc_base.py
由前面安全组代码实现一中,我们知道,最终执行了ML2 plugin的create_security_group_rule 方法,由类继承我们知道最终执行了 SecurityGroupServerNotifierRpcMixin.create_security_group_rule, 函数中执行了父类sg_db.SecurityGroupDbMixin 中的create_security_group_rule
class SecurityGroupServerNotifierRpcMixin(sg_db.SecurityGroupDbMixin):
.......
def create_security_group_rule(self, context, security_group_rule):
rule = super(SecurityGroupServerNotifierRpcMixin,
self).create_security_group_rule(context,
security_group_rule)
sgids = [rule['security_group_id']]
self.notifier.security_groups_rule_updated(context, sgids)
return rule
-
neutron/db/securitygroups_db.py
数据库中插入rule记录,并notify通知
class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
rbac_mixin.RbacPluginMixin):
..............
@db_api.retry_if_session_inactive()
def create_security_group_rule(self, context, security_group_rule):
res = self._create_security_group_rule(context, security_group_rule)
registry.notify(
resources.SECURITY_GROUP_RULE, events.AFTER_CREATE, self,
context=context, security_group_rule=res)
return res
def _create_security_group_rule(self, context, security_group_rule,
validate=True):
if validate:
sg_id = self._validate_security_group_rule(context,
security_group_rule)
rule_dict = security_group_rule['security_group_rule']
remote_ip_prefix = rule_dict.get('remote_ip_prefix')
if remote_ip_prefix:
remote_ip_prefix = utils.AuthenticIPNetwork(remote_ip_prefix)
protocol = rule_dict.get('protocol')
if protocol:
# object expects strings only
protocol = six.text_type(protocol)
args = {
'id': (rule_dict.get('id') or uuidutils.generate_uuid()),
'project_id': rule_dict['tenant_id'],
'security_group_id': rule_dict['security_group_id'],
'direction': rule_dict['direction'],
'remote_group_id': rule_dict.get('remote_group_id'),
'ethertype': rule_dict['ethertype'],
'protocol': protocol,
'remote_ip_prefix': remote_ip_prefix,
'description': rule_dict.get('description'),
}
port_range_min = self._safe_int(rule_dict['port_range_min'])
if port_range_min is not None:
args['port_range_min'] = port_range_min
port_range_max = self._safe_int(rule_dict['port_range_max'])
if port_range_max is not None:
args['port_range_max'] = port_range_max
kwargs = {
'context': context,
'security_group_rule': args
}
self._registry_notify(resources.SECURITY_GROUP_RULE,
events.BEFORE_CREATE,
exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
with db_api.CONTEXT_WRITER.using(context):
if validate:
self._check_for_duplicate_rules(context, sg_id,
[security_group_rule])
sg_rule = sg_obj.SecurityGroupRule(context, **args)
sg_rule.create()
# fetch sg_rule from db to load the sg rules with sg model
# otherwise a DetachedInstanceError can occur for model extensions
sg_rule = sg_obj.SecurityGroupRule.get_object(context,
id=sg_rule.id)
res_rule_dict = self._make_security_group_rule_dict(sg_rule.db_obj)
kwargs['security_group_rule'] = res_rule_dict
self._registry_notify(
resources.SECURITY_GROUP_RULE,
events.PRECOMMIT_CREATE,
exc_cls=ext_sg.SecurityGroupConflict, **kwargs)
return res_rule_dict
- neutron/db/securitygroups_rpc_base.py
self.notifier.security_groups_rule_updated(context, sgids) 最终发送了rpc广播给l2 agent
def create_security_group_rule(self, context, security_group_rule):
rule = super(SecurityGroupServerNotifierRpcMixin,
self).create_security_group_rule(context,
security_group_rule)
sgids = [rule['security_group_id']]
self.notifier.security_groups_rule_updated(context, sgids)
return rule
class SecurityGroupAgentRpcApiMixin(object):
def security_groups_rule_updated(self, context, security_groups):
"""Notify rule updated security groups."""
if not security_groups:
return
cctxt = self.client.prepare(version=self.SG_RPC_VERSION,
topic=self._get_security_group_topic(),
fanout=True)
cctxt.cast(context, 'security_groups_rule_updated',
security_groups=security_groups)
- neutron/agent/securitygroups_rpc.py
agent收到消息后,self.devices_to_refilter 中加入了update的device,最后经ovs agent rpc_loop时检测到了,执行了process_network_ports 更新了port的安全组规则
def security_groups_rule_updated(self, security_groups):
LOG.info("Security group "
"rule updated %r", security_groups)
self._security_group_updated(
security_groups,
'security_groups',
'sg_rule')
def _security_group_updated(self, security_groups, attribute, action_type):
devices = []
sec_grp_set = set(security_groups)
for device in self.firewall.ports.values():
if sec_grp_set & set(device.get(attribute, [])):
devices.append(device['device'])
if devices:
if self.use_enhanced_rpc:
self.firewall.security_group_updated(action_type, sec_grp_set)
if self.defer_refresh_firewall:
LOG.debug("Adding %s devices to the list of devices "
"for which firewall needs to be refreshed",
devices)
#最终将device加到devices_to_refilter中
self.devices_to_refilter |= set(devices)
else:
self.refresh_firewall(devices)
-
neutron/plugins/ml/drivers/openvswitchagent/ovs_neutron_agent.py
ovs在loop 循环中,因 devices_to_refilter变更,导致需要process_network_ports进而setup_port_filters,执行了 refresh_firewall
def firewall_refresh_needed(self):
return self.global_refresh_firewall or self.devices_to_refilter
.....
def rpc_loop(self, polling_manager):
.....................................
while self._check_and_handle_signal():
...........................
# Secure and wire/unwire VIFs and update their status
# on Neutron server
if (self._port_info_has_changes(port_info) or
self.sg_agent.firewall_refresh_needed() or
ovs_restarted):
LOG.debug("Starting to process devices in:%s",
port_info)
provisioning_needed = (
ovs_restarted or bridges_recreated)
failed_devices = self.process_network_ports(
port_info, provisioning_needed)
if need_clean_stale_flow:
self.cleanup_stale_flows()
need_clean_stale_flow = False
LOG.debug("Agent rpc_loop - iteration:%(iter_num)d - "
"ports processed. Elapsed:%(elapsed).3f",
{'iter_num': self.iter_num,
'elapsed': time.time() - start})
def process_network_ports(self, port_info, provisioning_needed):
failed_devices = {'added': set(), 'removed': set()}
.............
self.sg_agent.setup_port_filters(added_ports,
port_info.get('updated', set()))
- neutron/agent/securitygroups_rpc.py
最终执行了refresh_firewall , 最终调用firewall driver更新
def setup_port_filters(self, new_devices, updated_devices):
.................
if updated_devices:
LOG.debug("Refreshing firewall for %d devices",
len(updated_devices))
self.refresh_firewall(updated_devices)
@skip_if_noopfirewall_or_firewall_disabled
def refresh_firewall(self, device_ids=None):
LOG.info("Refresh firewall rules")
if not device_ids:
device_ids = self.firewall.ports.keys()
if not device_ids:
LOG.info("No ports here to refresh firewall")
return
self._apply_port_filter(device_ids, update_filter=True)
def _apply_port_filter(self, device_ids, update_filter=False):
step = common_constants.AGENT_RES_PROCESSING_STEP
if self.use_enhanced_rpc:
devices = {}
security_groups = {}
security_group_member_ips = {}
for i in range(0, len(device_ids), step):
#这里调用rpc
devices_info = self.plugin_rpc.security_group_info_for_devices(
self.context, list(device_ids)[i:i + step])
devices.update(devices_info['devices'])
security_groups.update(devices_info['security_groups'])
security_group_member_ips.update(devices_info['sg_member_ips'])
else:
#调用父类SecurityGroupInfoAPIMixin的函数
devices = self.plugin_rpc.security_group_rules_for_devices(
self.context, list(device_ids))
trusted_devices = self._get_trusted_devices(device_ids, devices)
with self.firewall.defer_apply():
if self.use_enhanced_rpc:
LOG.debug("Update security group information for ports %s",
devices.keys())
self._update_security_group_info(
security_groups, security_group_member_ips)
for device in devices.values():
if update_filter:
LOG.debug("Update port filter for %s", device['device'])
#最终调用firewall driver更新防火墙规则
self.firewall.update_port_filter(device)
else:
LOG.debug("Prepare port filter for %s", device['device'])
self.firewall.prepare_port_filter(device)
self.firewall.process_trusted_ports(trusted_devices)
- neutron/api/rpc/handlers/securitygroups_rpc.py
security_group_info_for_ports调用父类SecurityGroupInfoAPIMixin的函数
- neutron/db/securitygroups_rpc_base.py
从db中查询port的安全组信息
def security_group_info_for_ports(self, context, ports):
sg_info = {'devices': ports,
'security_groups': {},
'sg_member_ips': {}}
rules_in_db = self._select_rules_for_ports(context, ports)
remote_security_group_info = {}
for (port_id, rule_in_db) in rules_in_db:
remote_gid = rule_in_db.get('remote_group_id')
security_group_id = rule_in_db.get('security_group_id')
ethertype = rule_in_db['ethertype']
if ('security_group_source_groups'
not in sg_info['devices'][port_id]):
sg_info['devices'][port_id][
'security_group_source_groups'] = []
if remote_gid:
if (remote_gid
not in sg_info['devices'][port_id][
'security_group_source_groups']):
sg_info['devices'][port_id][
'security_group_source_groups'].append(remote_gid)
if remote_gid not in remote_security_group_info:
remote_security_group_info[remote_gid] = {}
if ethertype not in remote_security_group_info[remote_gid]:
# this set will be serialized into a list by rpc code
remote_security_group_info[remote_gid][ethertype] = set()
direction = rule_in_db['direction']
rule_dict = {
'direction': direction,
'ethertype': ethertype}
for key in ('protocol', 'port_range_min', 'port_range_max',
'remote_ip_prefix', 'remote_group_id', 'action', 'priority'):
if rule_in_db.get(key) is not None:
if key == 'remote_ip_prefix':
direction_ip_prefix = DIRECTION_IP_PREFIX[direction]
rule_dict[direction_ip_prefix] = rule_in_db[key]
continue
rule_dict[key] = rule_in_db[key]
else:
if key == 'action':
rule_dict[key] = 'allow'
if key == 'priority':
rule_dict[key] = 1000
if security_group_id not in sg_info['security_groups']:
sg_info['security_groups'][security_group_id] = []
if rule_dict not in sg_info['security_groups'][security_group_id]:
sg_info['security_groups'][security_group_id].append(
rule_dict)
# Update the security groups info if they don't have any rules
sg_ids = self._select_sg_ids_for_ports(context, ports)
for (sg_id, ) in sg_ids:
if sg_id not in sg_info['security_groups']:
sg_info['security_groups'][sg_id] = []
sg_info['sg_member_ips'] = remote_security_group_info
# the provider rules do not belong to any security group, so these
# rules still reside in sg_info['devices'] [port_id]
self._apply_provider_rule(context, sg_info['devices'])
return self._get_security_group_member_ips(context, sg_info)
- neutron/agent/linux/iptables_firewall.py
我们主要还是用的iptables,所以这里跳到iptables driver, , 先remove掉iptables相关chain,再新建相关chain
class IptablesFirewallDriver(firewall.FirewallDriver):
.......................
def update_port_filter(self, port):
LOG.debug("Updating device (%s) filter", port['device'])
if port['device'] not in self.ports:
LOG.info('Attempted to update port filter which is not '
'filtered %s', port['device'])
return
#清空iptables chain
self._remove_chains()
self._set_ports(port)
self._setup_chains()
return self.iptables.apply()
def _setup_chain(self, port, DIRECTION):
self._add_chain(port, DIRECTION)
#将规则格式化后加到安全组中
self._add_rules_by_security_group(port, DIRECTION)
def _add_rules_by_security_group(self, port, direction):
# select rules for current port and direction
security_group_rules = self._select_sgr_by_direction(port, direction)
security_group_rules += self._select_sg_rules_for_port(port, direction)
# split groups by ip version
# for ipv4, iptables command is used
# for ipv6, iptables6 command is used
ipv4_sg_rules, ipv6_sg_rules = self._split_sgr_by_ethertype(
security_group_rules)
ipv4_iptables_rules = []
ipv6_iptables_rules = []
# include fixed egress/ingress rules
if direction == constants.EGRESS_DIRECTION:
self._add_fixed_egress_rules(port,
ipv4_iptables_rules,
ipv6_iptables_rules)
elif direction == constants.INGRESS_DIRECTION:
ipv6_iptables_rules += self._accept_inbound_icmpv6()
# include IPv4 and IPv6 iptable rules from security group
#按照规则生成iptables规则命令
ipv4_iptables_rules += self._convert_sgr_to_iptables_rules(
ipv4_sg_rules)
ipv6_iptables_rules += self._convert_sgr_to_iptables_rules(
ipv6_sg_rules)
# finally add the rules to the port chain for a given direction
self._add_rules_to_chain_v4v6(self._port_chain_name(port, direction),
ipv4_iptables_rules,
ipv6_iptables_rules)
def _convert_sgr_to_iptables_rules(self, security_group_rules):
iptables_rules = []
self._allow_established(iptables_rules)
seen_sg_rules = set()
for rule in sorted(security_group_rules, key=lambda security_group_rules: (security_group_rules['priority'], len(security_group_rules['action']))):
#_convert_sg_rule_to_iptables_args 是具体rule转为iptables命令的方法
args = self._convert_sg_rule_to_iptables_args(rule)
if args:
rule_command = ' '.join(args)
if rule_command in seen_sg_rules:
# since these rules are from multiple security groups,
# there may be duplicates so we prune them out here
continue
seen_sg_rules.add(rule_command)
iptables_rules.append(rule_command)
self._drop_invalid_packets(iptables_rules)
iptables_rules += [comment_rule('-j $sg-fallback',
comment=ic.UNMATCHED)]
return iptables_rules