Option for root_helper when checking namespace

Adds a configuration option to use the root helper in the ip netns list
command executed by the IP library when checking for the existence of a
namespace.  This prevents an unprivileged l3 agent from erroneously trying
to create another namespace when one already exists. This is necessary in
environments with constrained permissions on /var/run/netns via umask or
other access controls.

However, due to the overhead incurred by calling sudo every time on systems
where this restriction isn't in place, this configuration won't be desired
all of the time. So this patch also adds a sanity check that reports back
whether or not the root_helper is required for a deployment.

DocImpact

Closes-Bug: #1348812
Closes-Bug: #1311804
Change-Id: If7560161de3be6066af0d9866e6b5cd7c7247c33
This commit is contained in:
Kevin Benton 2014-07-25 14:27:00 -07:00
parent 2494da2200
commit 9833364fbd
7 changed files with 71 additions and 9 deletions

View File

@ -554,6 +554,11 @@ lock_path = $state_path/lock
# each rule's purpose. (System must support the iptables comments module.)
# comment_iptables_rules = True
# Use the root helper when listing the namespaces on a system. This may not
# be required depending on the security configuration. If the root helper is
# not required, set this to False for a performance improvement.
# use_helper_for_ns_read = True
# =========== items for agent management extension =============
# seconds between nodes reporting state to server; should be less than
# agent_down_time, best if it is half or less than agent_down_time

View File

@ -27,6 +27,10 @@ LOG = logging.getLogger(__name__)
ROOT_HELPER_OPTS = [
cfg.StrOpt('root_helper', default='sudo',
help=_('Root helper application.')),
cfg.BoolOpt('use_helper_for_ns_read',
default=True,
help=_('Use the root helper to read the namespaces from '
'the operating system.')),
]
AGENT_STATE_OPTS = [

View File

@ -554,8 +554,11 @@ class IpNetnsCommand(IpCommandBase):
check_exit_code=check_exit_code, extra_ok_codes=extra_ok_codes)
def exists(self, name):
output = self._parent._execute('o', 'netns', ['list'])
root_helper = self._parent.root_helper
if not cfg.CONF.AGENT.use_helper_for_ns_read:
root_helper = None
output = self._parent._execute('o', 'netns', ['list'],
root_helper=root_helper)
for line in output.split('\n'):
if name == line.strip():
return True

View File

@ -15,12 +15,14 @@
import netaddr
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ip_link_support
from neutron.agent.linux import ovs_lib
from neutron.agent.linux import utils as agent_utils
from neutron.common import utils
from neutron.i18n import _LE
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants as const
from neutron.plugins.openvswitch.common import constants as ovs_const
@ -107,3 +109,16 @@ def vf_management_supported(root_helper):
"ip link command"))
return False
return True
def netns_read_requires_helper(root_helper):
ipw = ip_lib.IPWrapper(root_helper)
nsname = "netnsreadtest-" + uuidutils.generate_uuid()
ipw.netns.add(nsname)
try:
# read without root_helper. if exists, not required.
ipw_nohelp = ip_lib.IPWrapper()
exists = ipw_nohelp.netns.exists(nsname)
finally:
ipw.netns.delete(nsname)
return not exists

View File

@ -17,7 +17,7 @@ import sys
from neutron.cmd.sanity import checks
from neutron.common import config
from neutron.i18n import _LE
from neutron.i18n import _LE, _LW
from neutron.openstack.common import log as logging
from oslo.config import cfg
@ -31,6 +31,8 @@ cfg.CONF.import_group('ml2_sriov',
class BoolOptCallback(cfg.BoolOpt):
def __init__(self, name, callback, **kwargs):
if 'default' not in kwargs:
kwargs['default'] = False
self.callback = callback
super(BoolOptCallback, self).__init__(name, **kwargs)
@ -54,6 +56,27 @@ def check_ovs_patch():
return result
def check_read_netns():
required = checks.netns_read_requires_helper(
root_helper=cfg.CONF.AGENT.root_helper)
if not required and cfg.CONF.AGENT.use_helper_for_ns_read:
LOG.warning(_LW("The user that is executing neutron can read the "
"namespaces without using the root_helper. Disable "
"the use_helper_for_ns_read option to avoid a "
"performance impact."))
# Don't fail because nothing is actually broken. Just not optimal.
result = True
elif required and not cfg.CONF.AGENT.use_helper_for_ns_read:
LOG.error(_LE("The user that is executing neutron does not have "
"permissions to read the namespaces. Enable the "
"use_helper_for_ns_read configuration option."))
result = False
else:
# everything is configured appropriately
result = True
return result
def check_nova_notify():
result = checks.nova_notify_supported()
if not result:
@ -85,17 +108,18 @@ def check_vf_management():
# Define CLI opts to test specific features, with a calback for the test
OPTS = [
BoolOptCallback('ovs_vxlan', check_ovs_vxlan, default=False,
BoolOptCallback('ovs_vxlan', check_ovs_vxlan,
help=_('Check for vxlan support')),
BoolOptCallback('ovs_patch', check_ovs_patch, default=False,
BoolOptCallback('ovs_patch', check_ovs_patch,
help=_('Check for patch port support')),
BoolOptCallback('nova_notify', check_nova_notify, default=False,
BoolOptCallback('nova_notify', check_nova_notify,
help=_('Check for nova notification support')),
BoolOptCallback('arp_responder', check_arp_responder, default=False,
BoolOptCallback('arp_responder', check_arp_responder,
help=_('Check for ARP responder support')),
BoolOptCallback('vf_management', check_vf_management, default=False,
BoolOptCallback('vf_management', check_vf_management,
help=_('Check for VF management support')),
BoolOptCallback('read_netns', check_read_netns,
help=_('Check netns permission settings')),
]
@ -118,6 +142,8 @@ def enable_tests_from_config():
cfg.CONF.set_override('arp_responder', True)
if cfg.CONF.ml2_sriov.agent_required:
cfg.CONF.set_override('vf_management', True)
if not cfg.CONF.AGENT.use_helper_for_ns_read:
cfg.CONF.set_override('read_netns', True)
def all_tests_passed():

View File

@ -15,7 +15,9 @@
import random
import netaddr
from oslo.config import cfg
from neutron.agent.common import config
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ovs_lib
from neutron.agent.linux import utils
@ -44,6 +46,10 @@ def get_rand_veth_name():
class BaseLinuxTestCase(functional_base.BaseSudoTestCase):
def setUp(self):
super(BaseLinuxTestCase, self).setUp()
config.register_root_helper(cfg.CONF)
def check_command(self, cmd, error_text, skip_msg, root_helper=None):
try:
utils.execute(cmd, root_helper=root_helper)

View File

@ -55,3 +55,6 @@ class SanityTestCaseRoot(functional_base.BaseSudoTestCase):
def test_vf_management_runs(self):
checks.vf_management_supported(self.root_helper)
def test_namespace_root_read_detection_runs(self):
checks.netns_read_requires_helper(self.root_helper)