relinker: Add option to drop privileges

Some deployments may need to run the relinker as root -- for example,
because /etc/swift/swift.conf is only readable by root. In that case,
we need to drop back to the swift user before relinking, or the
newly-created partitions will not be readable by the object-server.

Co-Authored-By: Alistair Coles <alistairncoles@gmail.com>
Change-Id: Ifb9a6acdbc81b68788aa167b4e0eb915620b92f4
This commit is contained in:
Tim Burke 2021-01-25 10:27:19 -08:00
parent 1b7dd34d38
commit 7e6f9e7bf0
2 changed files with 58 additions and 1 deletions

View File

@ -25,7 +25,7 @@ from swift.common.storage_policy import POLICIES
from swift.common.exceptions import DiskFileDeleted, DiskFileNotExist, \
DiskFileQuarantined
from swift.common.utils import replace_partition_in_path, config_true_value, \
audit_location_generator, get_logger, readconf
audit_location_generator, get_logger, readconf, drop_privileges
from swift.obj import diskfile
@ -333,6 +333,8 @@ def main(args):
dest='swift_dir', help='Path to swift directory')
parser.add_argument('--devices', default=None,
dest='devices', help='Path to swift device directory')
parser.add_argument('--user', default=None, dest='user',
help='Drop privileges to this user before relinking')
parser.add_argument('--device', default=None, dest='device',
help='Device name to relink (default: all)')
parser.add_argument('--skip-mount-check', default=False,
@ -348,9 +350,15 @@ def main(args):
conf = readconf(args.conf_file, 'object-relinker')
if args.debug:
conf['log_level'] = 'DEBUG'
user = args.user or conf.get('user')
if user:
drop_privileges(user)
logger = get_logger(conf)
else:
conf = {}
if args.user:
# Drop privs before creating log file
drop_privileges(args.user)
logging.basicConfig(
format='%(message)s',
level=logging.DEBUG if args.debug else logging.INFO,

View File

@ -15,6 +15,7 @@ import binascii
import errno
import fcntl
import json
from contextlib import contextmanager
import logging
from textwrap import dedent
@ -95,6 +96,54 @@ class TestRelinker(unittest.TestCase):
shutil.rmtree(self.testdir, ignore_errors=1)
storage_policy.reload_storage_policies()
def _do_test_relinker_drop_privileges(self, command):
@contextmanager
def do_mocks():
# attach mocks to call_capture so that call order can be asserted
call_capture = mock.Mock()
with mock.patch('swift.cli.relinker.drop_privileges') as mock_dp:
with mock.patch('swift.cli.relinker.' + command,
return_value=0) as mock_command:
call_capture.attach_mock(mock_dp, 'drop_privileges')
call_capture.attach_mock(mock_command, command)
yield call_capture
# no user option
with do_mocks() as capture:
self.assertEqual(0, relinker.main([command]))
self.assertEqual([(command, mock.ANY, mock.ANY)],
capture.method_calls)
# cli option --user
with do_mocks() as capture:
self.assertEqual(0, relinker.main([command, '--user', 'cli_user']))
self.assertEqual([('drop_privileges', ('cli_user',), {}),
(command, mock.ANY, mock.ANY)],
capture.method_calls)
# cli option --user takes precedence over conf file user
with do_mocks() as capture:
with mock.patch('swift.cli.relinker.readconf',
return_value={'user': 'conf_user'}):
self.assertEqual(0, relinker.main([command, 'conf_file',
'--user', 'cli_user']))
self.assertEqual([('drop_privileges', ('cli_user',), {}),
(command, mock.ANY, mock.ANY)],
capture.method_calls)
# conf file user
with do_mocks() as capture:
with mock.patch('swift.cli.relinker.readconf',
return_value={'user': 'conf_user'}):
self.assertEqual(0, relinker.main([command, 'conf_file']))
self.assertEqual([('drop_privileges', ('conf_user',), {}),
(command, mock.ANY, mock.ANY)],
capture.method_calls)
def test_relinker_drop_privileges(self):
self._do_test_relinker_drop_privileges('relink')
self._do_test_relinker_drop_privileges('cleanup')
def test_conf_file(self):
config = """
[DEFAULT]