Set owner of drive-audit recon cache to swift user

Fixies this problem:
* swift-drive-audit needs to be run by root, because only root have
  "umount" permission
* swift-object servers typically runs as user swift
* if swift-drive-audit is run by root, /var/cache/swift/drive.recon is
  owned by root, with 0o600
* recon middleware (inside swift-object-server) can't read this cache
  file: swift-object: Error reading recon cache file

This patch adds "user" option to drive-audit config file. Recon cache
is chowned to this user.

Change-Id: Ibf20543ee690b7c5a37fabd1540fd5c0c7b638c9
This commit is contained in:
Ondřej Nový 2016-10-17 19:46:57 +02:00 committed by Pete Zaitcev
parent a79d8508df
commit 9847796f01
5 changed files with 32 additions and 2 deletions

View File

@ -208,7 +208,8 @@ if __name__ == '__main__':
total_errors += count total_errors += count
recon_file = recon_cache_path + "/drive.recon" recon_file = recon_cache_path + "/drive.recon"
dump_recon_cache(recon_errors, recon_file, logger) dump_recon_cache(recon_errors, recon_file, logger)
dump_recon_cache({'drive_audit_errors': total_errors}, recon_file, logger) dump_recon_cache({'drive_audit_errors': total_errors}, recon_file, logger,
set_owner=conf.get("user", "swift"))
if unmounts == 0: if unmounts == 0:
logger.info("No drives were unmounted") logger.info("No drives were unmounted")

View File

@ -264,6 +264,8 @@ settings:
================== ============== =========================================== ================== ============== ===========================================
Option Default Description Option Default Description
------------------ -------------- ------------------------------------------- ------------------ -------------- -------------------------------------------
user swift Drop privileges to this user for non-root
tasks
log_facility LOG_LOCAL0 Syslog log facility log_facility LOG_LOCAL0 Syslog log facility
log_level INFO Log level log_level INFO Log level
device_dir /srv/node Directory devices are mounted under device_dir /srv/node Directory devices are mounted under

View File

@ -1,4 +1,5 @@
[drive-audit] [drive-audit]
# user = swift
# device_dir = /srv/node # device_dir = /srv/node
# #
# You can specify default log routing here if you want: # You can specify default log routing here if you want:

View File

@ -3037,13 +3037,15 @@ def put_recon_cache_entry(cache_entry, key, item):
cache_entry[key] = item cache_entry[key] = item
def dump_recon_cache(cache_dict, cache_file, logger, lock_timeout=2): def dump_recon_cache(cache_dict, cache_file, logger, lock_timeout=2,
set_owner=None):
"""Update recon cache values """Update recon cache values
:param cache_dict: Dictionary of cache key/value pairs to write out :param cache_dict: Dictionary of cache key/value pairs to write out
:param cache_file: cache file to update :param cache_file: cache file to update
:param logger: the logger to use to log an encountered error :param logger: the logger to use to log an encountered error
:param lock_timeout: timeout (in seconds) :param lock_timeout: timeout (in seconds)
:param set_owner: Set owner of recon cache file
""" """
try: try:
with lock_file(cache_file, lock_timeout, unlink=False) as cf: with lock_file(cache_file, lock_timeout, unlink=False) as cf:
@ -3062,6 +3064,8 @@ def dump_recon_cache(cache_dict, cache_file, logger, lock_timeout=2):
with NamedTemporaryFile(dir=os.path.dirname(cache_file), with NamedTemporaryFile(dir=os.path.dirname(cache_file),
delete=False) as tf: delete=False) as tf:
tf.write(json.dumps(cache_entry) + '\n') tf.write(json.dumps(cache_entry) + '\n')
if set_owner:
os.chown(tf.name, pwd.getpwnam(set_owner).pw_uid, -1)
renamer(tf.name, cache_file, fsync=False) renamer(tf.name, cache_file, fsync=False)
finally: finally:
if tf is not None: if tf is not None:

View File

@ -1377,6 +1377,28 @@ class TestUtils(unittest.TestCase):
finally: finally:
rmtree(testdir_base) rmtree(testdir_base)
def test_dump_recon_cache_set_owner(self):
testdir_base = mkdtemp()
testcache_file = os.path.join(testdir_base, 'cache.recon')
logger = utils.get_logger(None, 'server', log_route='server')
try:
submit_dict = {'key1': {'value1': 1, 'value2': 2}}
_ret = lambda: None
_ret.pw_uid = 100
_mock_getpwnam = MagicMock(return_value=_ret)
_mock_chown = mock.Mock()
with patch('os.chown', _mock_chown), \
patch('pwd.getpwnam', _mock_getpwnam):
utils.dump_recon_cache(submit_dict, testcache_file,
logger, set_owner="swift")
_mock_getpwnam.assert_called_once_with("swift")
self.assertEqual(_mock_chown.call_args[0][1], 100)
finally:
rmtree(testdir_base)
def test_dump_recon_cache_permission_denied(self): def test_dump_recon_cache_permission_denied(self):
testdir_base = mkdtemp() testdir_base = mkdtemp()
testcache_file = os.path.join(testdir_base, 'cache.recon') testcache_file = os.path.join(testdir_base, 'cache.recon')