Move execs of tee to privsep.
Instead of calling tee to write to files as root, we should just write to files as root. Change-Id: Ic48087fdf283b3ba503294a944be91be0c338132
This commit is contained in:
parent
d83e9c0b17
commit
0952f80d01
@ -37,10 +37,6 @@ blkid: CommandFilter, blkid, root
|
||||
# nova/virt/disk/mount/nbd.py: 'blockdev', '--flushbufs', device
|
||||
blockdev: RegExpFilter, blockdev, root, blockdev, (--getsize64|--flushbufs), /dev/.*
|
||||
|
||||
# nova/virt/libvirt/guest.py: 'tee',
|
||||
# nova/virt/libvirt/vif.py: utils.execute('tee',
|
||||
tee: CommandFilter, tee, root
|
||||
|
||||
# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
|
||||
# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
|
||||
# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
|
||||
@ -204,6 +200,7 @@ privsep-rootwrap-os_brick: RegExpFilter, privsep-helper, root, privsep-helper, -
|
||||
|
||||
privsep-rootwrap-dac_admin: RegExpFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, nova.privsep.dac_admin_pctxt, --privsep_sock_path, /tmp/.*
|
||||
|
||||
privsep-rootwrap-dacnet_admin: RegExpFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, nova.privsep.dacnet_admin_pctxt, --privsep_sock_path, /tmp/.*
|
||||
|
||||
# nova/virt/libvirt/storage/dmcrypt.py:
|
||||
cryptsetup: CommandFilter, cryptsetup, root
|
||||
|
@ -15,13 +15,14 @@
|
||||
|
||||
"""Setup privsep decorator."""
|
||||
|
||||
from oslo_privsep import capabilities
|
||||
from oslo_privsep import priv_context
|
||||
|
||||
# NOTE(tonyb): DAC == Discriminatory Access Control. Basically this context
|
||||
# can bypass permissions checks in the file-system.
|
||||
dac_admin_pctxt = priv_context.PrivContext(
|
||||
'nova',
|
||||
cfg_section='nova_privileged',
|
||||
cfg_section='nova_dac_admin',
|
||||
pypath=__name__ + '.dac_admin_pctxt',
|
||||
# NOTE(tonyb): These map to CAP_CHOWN, CAP_DAC_OVERRIDE,
|
||||
# CAP_DAC_READ_SEARCH and CAP_FOWNER. Some do not have
|
||||
@ -29,3 +30,12 @@ dac_admin_pctxt = priv_context.PrivContext(
|
||||
# for more information
|
||||
capabilities=[0, 1, 2, 3],
|
||||
)
|
||||
|
||||
|
||||
# NOTE(mikal): DAC + CAP_NET_ADMIN, required for network sysfs changes
|
||||
dacnet_admin_pctxt = priv_context.PrivContext(
|
||||
'nova',
|
||||
cfg_section='nova_dacnet_admin',
|
||||
pypath=__name__ + '.dacnet_admin_pctxt',
|
||||
capabilities=[0, 1, 2, 3, capabilities.CAP_NET_ADMIN],
|
||||
)
|
||||
|
@ -55,3 +55,25 @@ def _last_bytes_inner(file_like_object, num):
|
||||
|
||||
remaining = file_like_object.tell()
|
||||
return (file_like_object.read(), remaining)
|
||||
|
||||
|
||||
@nova.privsep.dacnet_admin_pctxt.entrypoint
|
||||
def enable_hairpin(interface):
|
||||
"""Enable hairpin mode for a libvirt guest."""
|
||||
with open('/sys/class/net/%s/brport/hairpin_mode' % interface, 'w') as f:
|
||||
f.write('1')
|
||||
|
||||
|
||||
@nova.privsep.dacnet_admin_pctxt.entrypoint
|
||||
def disable_multicast_snooping(interface):
|
||||
"""Disable multicast snooping for a bridge."""
|
||||
with open('/sys/class/net/%s/bridge/multicast_snooping' % interface,
|
||||
'w') as f:
|
||||
f.write('0')
|
||||
|
||||
|
||||
@nova.privsep.dacnet_admin_pctxt.entrypoint
|
||||
def disable_ipv6(interface):
|
||||
"""Disable ipv6 for a bridge."""
|
||||
with open('/proc/sys/net/ipv6/conf/%s/disable_ipv' % interface, 'w') as f:
|
||||
f.write('1')
|
||||
|
@ -14154,17 +14154,18 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
domain=fake_domain)
|
||||
self.assertTrue(self.log_error_called)
|
||||
|
||||
def test_create_domain_enable_hairpin_fails(self):
|
||||
@mock.patch('nova.privsep.libvirt.enable_hairpin')
|
||||
def test_create_domain_enable_hairpin_fails(self, mock_writefile):
|
||||
"""Tests that the xml is logged when enabling hairpin mode for the
|
||||
domain fails.
|
||||
"""
|
||||
# Guest.enable_hairpin is only called for nova-network.
|
||||
# TODO(mikal): remove this test when nova-net goes away
|
||||
self.flags(use_neutron=False)
|
||||
fake_xml = "<test>this is a test</test>"
|
||||
fake_domain = FakeVirtDomain(fake_xml)
|
||||
|
||||
def fake_execute(*args, **kwargs):
|
||||
raise processutils.ProcessExecutionError('error')
|
||||
mock_writefile.side_effect = IOError
|
||||
|
||||
def fake_get_interfaces(*args):
|
||||
return ["dev"]
|
||||
@ -14180,14 +14181,11 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.create_fake_libvirt_mock()
|
||||
self.mox.ReplayAll()
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
self.stubs.Set(nova.utils, 'execute', fake_execute)
|
||||
self.stubs.Set(
|
||||
nova.virt.libvirt.guest.Guest, 'get_interfaces',
|
||||
fake_get_interfaces)
|
||||
|
||||
self.assertRaises(processutils.ProcessExecutionError,
|
||||
drvr._create_domain,
|
||||
domain=fake_domain,
|
||||
self.assertRaises(IOError, drvr._create_domain, domain=fake_domain,
|
||||
power_on=False)
|
||||
self.assertTrue(self.log_error_called)
|
||||
|
||||
|
@ -24,7 +24,6 @@ from nova import context
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.unit.virt.libvirt import fakelibvirt
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import guest as libvirt_guest
|
||||
from nova.virt.libvirt import host
|
||||
@ -85,26 +84,23 @@ class GuestTestCase(test.NoDBTestCase):
|
||||
self.assertRaises(test.TestingException, self.guest.launch)
|
||||
self.assertEqual(1, mock_safe_decode.called)
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch('nova.privsep.libvirt.enable_hairpin')
|
||||
@mock.patch.object(libvirt_guest.Guest, 'get_interfaces')
|
||||
def test_enable_hairpin(self, mock_get_interfaces, mock_execute):
|
||||
def test_enable_hairpin(self, mock_get_interfaces, mock_writefile):
|
||||
mock_get_interfaces.return_value = ["vnet0", "vnet1"]
|
||||
self.guest.enable_hairpin()
|
||||
mock_execute.assert_has_calls([
|
||||
mock.call(
|
||||
'tee', '/sys/class/net/vnet0/brport/hairpin_mode',
|
||||
run_as_root=True, process_input='1', check_exit_code=[0, 1]),
|
||||
mock.call(
|
||||
'tee', '/sys/class/net/vnet1/brport/hairpin_mode',
|
||||
run_as_root=True, process_input='1', check_exit_code=[0, 1])])
|
||||
mock_writefile.assert_has_calls([
|
||||
mock.call('vnet0'),
|
||||
mock.call('vnet1')]
|
||||
)
|
||||
|
||||
@mock.patch.object(encodeutils, 'safe_decode')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch('nova.privsep.libvirt.enable_hairpin')
|
||||
@mock.patch.object(libvirt_guest.Guest, 'get_interfaces')
|
||||
def test_enable_hairpin_exception(self, mock_get_interfaces,
|
||||
mock_execute, mock_safe_decode):
|
||||
mock_writefile, mock_safe_decode):
|
||||
mock_get_interfaces.return_value = ["foo"]
|
||||
mock_execute.side_effect = test.TestingException('oops')
|
||||
mock_writefile.side_effect = test.TestingException
|
||||
|
||||
self.assertRaises(test.TestingException, self.guest.enable_hairpin)
|
||||
self.assertEqual(1, mock_safe_decode.called)
|
||||
|
@ -960,21 +960,15 @@ class LibvirtVifTestCase(test.NoDBTestCase):
|
||||
run_as_root=True),
|
||||
mock.call('brctl', 'stp', 'qbrvif-xxx-yyy', 'off',
|
||||
run_as_root=True),
|
||||
mock.call('tee', ('/sys/class/net/qbrvif-xxx-yyy'
|
||||
'/bridge/multicast_snooping'),
|
||||
process_input='0', run_as_root=True,
|
||||
check_exit_code=[0, 1]),
|
||||
mock.call('tee', ('/proc/sys/net/ipv6/conf'
|
||||
'/qbrvif-xxx-yyy/disable_ipv6'),
|
||||
process_input='1', run_as_root=True,
|
||||
check_exit_code=[0, 1]),
|
||||
mock.call('ip', 'link', 'set', 'qbrvif-xxx-yyy', 'up',
|
||||
run_as_root=True),
|
||||
mock.call('brctl', 'addif', 'qbrvif-xxx-yyy',
|
||||
'qvbvif-xxx-yyy', run_as_root=True)],
|
||||
'create_ivs_vif_port': [mock.call('qvovif-xxx-yyy', 'aaa-bbb-ccc',
|
||||
'ca:fe:de:ad:be:ef',
|
||||
'f0000000-0000-0000-0000-000000000001')]
|
||||
'f0000000-0000-0000-0000-000000000001')],
|
||||
'disable_ipv6': [mock.call('qbrvif-xxx-yyy')],
|
||||
'multicast_snoop': [mock.call('qbrvif-xxx-yyy')]
|
||||
}
|
||||
with test.nested(
|
||||
mock.patch.object(linux_net, 'device_exists',
|
||||
@ -982,15 +976,20 @@ class LibvirtVifTestCase(test.NoDBTestCase):
|
||||
mock.patch.object(utils, 'execute'),
|
||||
mock.patch.object(linux_net, '_create_veth_pair'),
|
||||
mock.patch.object(linux_net, 'create_ivs_vif_port'),
|
||||
mock.patch.object(os.path, 'exists', return_value=True)
|
||||
mock.patch.object(os.path, 'exists', return_value=True),
|
||||
mock.patch('nova.privsep.libvirt.disable_multicast_snooping'),
|
||||
mock.patch('nova.privsep.libvirt.disable_ipv6')
|
||||
) as (device_exists, execute, _create_veth_pair, create_ivs_vif_port,
|
||||
path_exists):
|
||||
path_exists, disable_ipv6, disable_multicast_snooping):
|
||||
d = vif.LibvirtGenericVIFDriver()
|
||||
d.plug(self.instance, self.vif_ivs)
|
||||
device_exists.assert_has_calls(calls['device_exists'])
|
||||
_create_veth_pair.assert_has_calls(calls['_create_veth_pair'])
|
||||
execute.assert_has_calls(calls['execute'])
|
||||
create_ivs_vif_port.assert_has_calls(calls['create_ivs_vif_port'])
|
||||
disable_multicast_snooping.assert_has_calls(
|
||||
calls['multicast_snoop'])
|
||||
disable_ipv6.assert_has_calls(calls['disable_ipv6'])
|
||||
|
||||
def test_unplug_ivs_hybrid(self):
|
||||
calls = {
|
||||
|
@ -40,7 +40,7 @@ import six
|
||||
from nova.compute import power_state
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova import utils
|
||||
from nova.privsep import libvirt as libvirt_privsep
|
||||
from nova.virt import hardware
|
||||
from nova.virt.libvirt import compat
|
||||
from nova.virt.libvirt import config as vconfig
|
||||
@ -200,12 +200,7 @@ class Guest(object):
|
||||
interfaces = self.get_interfaces()
|
||||
try:
|
||||
for interface in interfaces:
|
||||
utils.execute(
|
||||
'tee',
|
||||
'/sys/class/net/%s/brport/hairpin_mode' % interface,
|
||||
process_input='1',
|
||||
run_as_root=True,
|
||||
check_exit_code=[0, 1])
|
||||
libvirt_privsep.enable_hairpin(interface)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error('Error enabling hairpin mode with XML: %s',
|
||||
|
@ -582,19 +582,8 @@ class LibvirtGenericVIFDriver(object):
|
||||
utils.execute('brctl', 'addbr', br_name, run_as_root=True)
|
||||
utils.execute('brctl', 'setfd', br_name, 0, run_as_root=True)
|
||||
utils.execute('brctl', 'stp', br_name, 'off', run_as_root=True)
|
||||
utils.execute('tee',
|
||||
('/sys/class/net/%s/bridge/multicast_snooping' %
|
||||
br_name),
|
||||
process_input='0',
|
||||
run_as_root=True,
|
||||
check_exit_code=[0, 1])
|
||||
disv6 = '/proc/sys/net/ipv6/conf/%s/disable_ipv6' % br_name
|
||||
if os.path.exists(disv6):
|
||||
utils.execute('tee',
|
||||
disv6,
|
||||
process_input='1',
|
||||
run_as_root=True,
|
||||
check_exit_code=[0, 1])
|
||||
nova.privsep.libvirt.disable_multicast_snooping(br_name)
|
||||
nova.privsep.libvirt.disable_ipv6(br_name)
|
||||
|
||||
if not linux_net.device_exists(v2_name):
|
||||
mtu = vif['network'].get_meta('mtu')
|
||||
|
@ -3,6 +3,9 @@ upgrade:
|
||||
- |
|
||||
A dac-admin privsep daemon has been added and needs to be included in your
|
||||
rootwrap configuration.
|
||||
- |
|
||||
A dacnet-admin privsep daemon has been added and needs to be included in
|
||||
your rootwrap configuration.
|
||||
- |
|
||||
The following commands are no longer required to be listed in your rootwrap
|
||||
configuration: cat; chown; readlink; touch.
|
||||
configuration: cat; chown; readlink; tee; touch.
|
Loading…
Reference in New Issue
Block a user