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
|
# nova/virt/disk/mount/nbd.py: 'blockdev', '--flushbufs', device
|
||||||
blockdev: RegExpFilter, blockdev, root, blockdev, (--getsize64|--flushbufs), /dev/.*
|
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', 'tuntap', 'add', dev, 'mode', 'tap'
|
||||||
# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
|
# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
|
||||||
# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
|
# 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-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:
|
# nova/virt/libvirt/storage/dmcrypt.py:
|
||||||
cryptsetup: CommandFilter, cryptsetup, root
|
cryptsetup: CommandFilter, cryptsetup, root
|
||||||
|
@ -15,13 +15,14 @@
|
|||||||
|
|
||||||
"""Setup privsep decorator."""
|
"""Setup privsep decorator."""
|
||||||
|
|
||||||
|
from oslo_privsep import capabilities
|
||||||
from oslo_privsep import priv_context
|
from oslo_privsep import priv_context
|
||||||
|
|
||||||
# NOTE(tonyb): DAC == Discriminatory Access Control. Basically this context
|
# NOTE(tonyb): DAC == Discriminatory Access Control. Basically this context
|
||||||
# can bypass permissions checks in the file-system.
|
# can bypass permissions checks in the file-system.
|
||||||
dac_admin_pctxt = priv_context.PrivContext(
|
dac_admin_pctxt = priv_context.PrivContext(
|
||||||
'nova',
|
'nova',
|
||||||
cfg_section='nova_privileged',
|
cfg_section='nova_dac_admin',
|
||||||
pypath=__name__ + '.dac_admin_pctxt',
|
pypath=__name__ + '.dac_admin_pctxt',
|
||||||
# NOTE(tonyb): These map to CAP_CHOWN, CAP_DAC_OVERRIDE,
|
# NOTE(tonyb): These map to CAP_CHOWN, CAP_DAC_OVERRIDE,
|
||||||
# CAP_DAC_READ_SEARCH and CAP_FOWNER. Some do not have
|
# CAP_DAC_READ_SEARCH and CAP_FOWNER. Some do not have
|
||||||
@ -29,3 +30,12 @@ dac_admin_pctxt = priv_context.PrivContext(
|
|||||||
# for more information
|
# for more information
|
||||||
capabilities=[0, 1, 2, 3],
|
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()
|
remaining = file_like_object.tell()
|
||||||
return (file_like_object.read(), remaining)
|
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)
|
domain=fake_domain)
|
||||||
self.assertTrue(self.log_error_called)
|
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
|
"""Tests that the xml is logged when enabling hairpin mode for the
|
||||||
domain fails.
|
domain fails.
|
||||||
"""
|
"""
|
||||||
# Guest.enable_hairpin is only called for nova-network.
|
# Guest.enable_hairpin is only called for nova-network.
|
||||||
|
# TODO(mikal): remove this test when nova-net goes away
|
||||||
self.flags(use_neutron=False)
|
self.flags(use_neutron=False)
|
||||||
fake_xml = "<test>this is a test</test>"
|
fake_xml = "<test>this is a test</test>"
|
||||||
fake_domain = FakeVirtDomain(fake_xml)
|
fake_domain = FakeVirtDomain(fake_xml)
|
||||||
|
|
||||||
def fake_execute(*args, **kwargs):
|
mock_writefile.side_effect = IOError
|
||||||
raise processutils.ProcessExecutionError('error')
|
|
||||||
|
|
||||||
def fake_get_interfaces(*args):
|
def fake_get_interfaces(*args):
|
||||||
return ["dev"]
|
return ["dev"]
|
||||||
@ -14180,14 +14181,11 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
self.create_fake_libvirt_mock()
|
self.create_fake_libvirt_mock()
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
self.stubs.Set(nova.utils, 'execute', fake_execute)
|
|
||||||
self.stubs.Set(
|
self.stubs.Set(
|
||||||
nova.virt.libvirt.guest.Guest, 'get_interfaces',
|
nova.virt.libvirt.guest.Guest, 'get_interfaces',
|
||||||
fake_get_interfaces)
|
fake_get_interfaces)
|
||||||
|
|
||||||
self.assertRaises(processutils.ProcessExecutionError,
|
self.assertRaises(IOError, drvr._create_domain, domain=fake_domain,
|
||||||
drvr._create_domain,
|
|
||||||
domain=fake_domain,
|
|
||||||
power_on=False)
|
power_on=False)
|
||||||
self.assertTrue(self.log_error_called)
|
self.assertTrue(self.log_error_called)
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ from nova import context
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.virt.libvirt import fakelibvirt
|
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 config as vconfig
|
||||||
from nova.virt.libvirt import guest as libvirt_guest
|
from nova.virt.libvirt import guest as libvirt_guest
|
||||||
from nova.virt.libvirt import host
|
from nova.virt.libvirt import host
|
||||||
@ -85,26 +84,23 @@ class GuestTestCase(test.NoDBTestCase):
|
|||||||
self.assertRaises(test.TestingException, self.guest.launch)
|
self.assertRaises(test.TestingException, self.guest.launch)
|
||||||
self.assertEqual(1, mock_safe_decode.called)
|
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')
|
@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"]
|
mock_get_interfaces.return_value = ["vnet0", "vnet1"]
|
||||||
self.guest.enable_hairpin()
|
self.guest.enable_hairpin()
|
||||||
mock_execute.assert_has_calls([
|
mock_writefile.assert_has_calls([
|
||||||
mock.call(
|
mock.call('vnet0'),
|
||||||
'tee', '/sys/class/net/vnet0/brport/hairpin_mode',
|
mock.call('vnet1')]
|
||||||
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.patch.object(encodeutils, 'safe_decode')
|
@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')
|
@mock.patch.object(libvirt_guest.Guest, 'get_interfaces')
|
||||||
def test_enable_hairpin_exception(self, mock_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_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.assertRaises(test.TestingException, self.guest.enable_hairpin)
|
||||||
self.assertEqual(1, mock_safe_decode.called)
|
self.assertEqual(1, mock_safe_decode.called)
|
||||||
|
@ -960,21 +960,15 @@ class LibvirtVifTestCase(test.NoDBTestCase):
|
|||||||
run_as_root=True),
|
run_as_root=True),
|
||||||
mock.call('brctl', 'stp', 'qbrvif-xxx-yyy', 'off',
|
mock.call('brctl', 'stp', 'qbrvif-xxx-yyy', 'off',
|
||||||
run_as_root=True),
|
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',
|
mock.call('ip', 'link', 'set', 'qbrvif-xxx-yyy', 'up',
|
||||||
run_as_root=True),
|
run_as_root=True),
|
||||||
mock.call('brctl', 'addif', 'qbrvif-xxx-yyy',
|
mock.call('brctl', 'addif', 'qbrvif-xxx-yyy',
|
||||||
'qvbvif-xxx-yyy', run_as_root=True)],
|
'qvbvif-xxx-yyy', run_as_root=True)],
|
||||||
'create_ivs_vif_port': [mock.call('qvovif-xxx-yyy', 'aaa-bbb-ccc',
|
'create_ivs_vif_port': [mock.call('qvovif-xxx-yyy', 'aaa-bbb-ccc',
|
||||||
'ca:fe:de:ad:be:ef',
|
'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(
|
with test.nested(
|
||||||
mock.patch.object(linux_net, 'device_exists',
|
mock.patch.object(linux_net, 'device_exists',
|
||||||
@ -982,15 +976,20 @@ class LibvirtVifTestCase(test.NoDBTestCase):
|
|||||||
mock.patch.object(utils, 'execute'),
|
mock.patch.object(utils, 'execute'),
|
||||||
mock.patch.object(linux_net, '_create_veth_pair'),
|
mock.patch.object(linux_net, '_create_veth_pair'),
|
||||||
mock.patch.object(linux_net, 'create_ivs_vif_port'),
|
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,
|
) as (device_exists, execute, _create_veth_pair, create_ivs_vif_port,
|
||||||
path_exists):
|
path_exists, disable_ipv6, disable_multicast_snooping):
|
||||||
d = vif.LibvirtGenericVIFDriver()
|
d = vif.LibvirtGenericVIFDriver()
|
||||||
d.plug(self.instance, self.vif_ivs)
|
d.plug(self.instance, self.vif_ivs)
|
||||||
device_exists.assert_has_calls(calls['device_exists'])
|
device_exists.assert_has_calls(calls['device_exists'])
|
||||||
_create_veth_pair.assert_has_calls(calls['_create_veth_pair'])
|
_create_veth_pair.assert_has_calls(calls['_create_veth_pair'])
|
||||||
execute.assert_has_calls(calls['execute'])
|
execute.assert_has_calls(calls['execute'])
|
||||||
create_ivs_vif_port.assert_has_calls(calls['create_ivs_vif_port'])
|
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):
|
def test_unplug_ivs_hybrid(self):
|
||||||
calls = {
|
calls = {
|
||||||
|
@ -40,7 +40,7 @@ import six
|
|||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import utils
|
from nova.privsep import libvirt as libvirt_privsep
|
||||||
from nova.virt import hardware
|
from nova.virt import hardware
|
||||||
from nova.virt.libvirt import compat
|
from nova.virt.libvirt import compat
|
||||||
from nova.virt.libvirt import config as vconfig
|
from nova.virt.libvirt import config as vconfig
|
||||||
@ -200,12 +200,7 @@ class Guest(object):
|
|||||||
interfaces = self.get_interfaces()
|
interfaces = self.get_interfaces()
|
||||||
try:
|
try:
|
||||||
for interface in interfaces:
|
for interface in interfaces:
|
||||||
utils.execute(
|
libvirt_privsep.enable_hairpin(interface)
|
||||||
'tee',
|
|
||||||
'/sys/class/net/%s/brport/hairpin_mode' % interface,
|
|
||||||
process_input='1',
|
|
||||||
run_as_root=True,
|
|
||||||
check_exit_code=[0, 1])
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error('Error enabling hairpin mode with XML: %s',
|
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', 'addbr', br_name, run_as_root=True)
|
||||||
utils.execute('brctl', 'setfd', br_name, 0, 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('brctl', 'stp', br_name, 'off', run_as_root=True)
|
||||||
utils.execute('tee',
|
nova.privsep.libvirt.disable_multicast_snooping(br_name)
|
||||||
('/sys/class/net/%s/bridge/multicast_snooping' %
|
nova.privsep.libvirt.disable_ipv6(br_name)
|
||||||
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])
|
|
||||||
|
|
||||||
if not linux_net.device_exists(v2_name):
|
if not linux_net.device_exists(v2_name):
|
||||||
mtu = vif['network'].get_meta('mtu')
|
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
|
A dac-admin privsep daemon has been added and needs to be included in your
|
||||||
rootwrap configuration.
|
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
|
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