Add radvd_user config option

In some deployments, the "neutron" user does not have the permissions
to modify the kernel interfaces. In those cases the radvd user should
be defined. This patch introduces a new config option: "radvd_user".

This config option is the username passed to radvd, used to drop root
privileges and change user ID to username and group ID to the primary
group of username. If no user specified (by default is an empty string),
the user executing the L3 agent will be passed. If "root" specified,
because radvd is spawned as root, no "username" parameter will be
passed.

Conflicts:
      neutron/tests/unit/agent/l3/test_agent.py

Change-Id: Ie9a6fbf04d453a3c1c0bddf9ecaa3d4d6467e8ff
Closes-Bug: #1844688
(cherry picked from commit 6a5a75d5a6)
(cherry picked from commit 5b6b040d07)
(cherry picked from commit 9921c96218)
This commit is contained in:
Rodolfo Alonso Hernandez 2019-09-19 17:12:59 +00:00
parent f57e0e6029
commit 0b13f70f57
4 changed files with 52 additions and 19 deletions

View File

@ -143,8 +143,12 @@ class DaemonMonitor(object):
def _spawn_radvd(self, radvd_conf):
def callback(pid_file):
# drop radvd daemon privileges and run as the neutron user
radvd_user = pwd.getpwuid(os.geteuid()).pw_name
if not self._agent_conf.radvd_user:
radvd_user = pwd.getpwuid(os.geteuid()).pw_name
elif self._agent_conf.radvd_user == 'root':
radvd_user = None
else:
radvd_user = self._agent_conf.radvd_user
# we need to use -m syslog and f.e. not -m stderr (the default)
# or -m stderr_syslog so that radvd 2.0+ will close stderr and
# exit after daemonization; otherwise, the current thread will
@ -153,8 +157,9 @@ class DaemonMonitor(object):
radvd_cmd = [RADVD_SERVICE_CMD,
'-C', '%s' % radvd_conf,
'-p', '%s' % pid_file,
'-u', '%s' % radvd_user,
'-m', 'syslog']
if radvd_user:
radvd_cmd += ['-u', '%s' % radvd_user]
return radvd_cmd
pm = self._get_radvd_process_manager(callback)

View File

@ -101,6 +101,14 @@ OPTS = [
help=_('Iptables mangle mark used to mark ingress from '
'external network. This mark will be masked with '
'0xffff so that only the lower 16 bits will be used.')),
cfg.StrOpt('radvd_user',
default='',
help=_('The username passed to radvd, used to drop root '
'privileges and change user ID to username and group ID '
'to the primary group of username. If no user specified '
'(by default), the user executing the L3 agent will be '
'passed. If "root" specified, because radvd is spawned '
'as root, no "username" parameter will be passed.')),
]
OPTS += config.EXT_NET_BRIDGE_OPTS

View File

@ -16,6 +16,8 @@
import copy
from itertools import chain as iter_chain
from itertools import combinations as iter_combinations
import os
import pwd
import eventlet
import fixtures
@ -3093,10 +3095,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self.assertFalse(ri.remove_floating_ip.called)
@mock.patch('os.geteuid', return_value='490')
@mock.patch('pwd.getpwuid', return_value=FakeUser('neutron'))
def test_spawn_radvd(self, geteuid, getpwuid):
router = l3_test_common.prepare_router_data(ip_version=6)
@mock.patch.object(os, 'geteuid', return_value=mock.ANY)
@mock.patch.object(pwd, 'getpwuid')
def test_spawn_radvd(self, mock_getpwuid, *args):
router = l3_test_common.prepare_router_data(
ip_version=lib_constants.IP_VERSION_6)
conffile = '/fake/radvd.conf'
pidfile = '/fake/radvd.pid'
@ -3122,19 +3125,25 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent.process_monitor,
l3_test_common.FakeDev,
self.conf)
radvd.enable(router['_interfaces'])
cmd = execute.call_args[0][0]
self.assertIn('radvd', cmd)
_join = lambda *args: ' '.join(args)
cmd = _join(*cmd)
self.assertIn(_join('-C', conffile), cmd)
self.assertIn(_join('-p', pidfile), cmd)
self.assertIn(_join('-u', 'neutron'), cmd)
self.assertIn(_join('-m', 'syslog'), cmd)
test_users = [('', 'stack', '-u stack'),
('neutron', mock.ANY, '-u neutron'),
('root', mock.ANY, None)]
for radvd_user, os_user, to_check in test_users:
self.conf.set_override('radvd_user', radvd_user)
mock_getpwuid.return_value = FakeUser(os_user)
radvd.enable(router['_interfaces'])
cmd = execute.call_args[0][0]
_join = lambda *args: ' '.join(args)
cmd = _join(*cmd)
self.assertIn('radvd', cmd)
self.assertIn(_join('-C', conffile), cmd)
self.assertIn(_join('-p', pidfile), cmd)
self.assertIn(_join('-m', 'syslog'), cmd)
if to_check:
self.assertIn(to_check, cmd)
else:
self.assertNotIn('-u', cmd)
def test_generate_radvd_mtu_conf(self):
router = l3_test_common.prepare_router_data()

View File

@ -0,0 +1,11 @@
---
other:
- |
A new config option, ``radvd_user``, was added to l3_agent.ini for the L3
agent. This option defines the username passed to radvd, used to drop
"root" privileges and change user ID to username and group ID to the
primary group of the user. If no user specified (by default), the user
executing the L3 agent will be passed. If "root" specified, because radvd
is spawned as root, no "username" parameter will be passed.
(For more information see bug `1844688
<https://bugs.launchpad.net/neutron/+bug/1844688>`_.)