config/sysinv/sysinv/sysinv/sysinv/common/fernet.py

190 lines
6.3 KiB
Python

#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from eventlet.green import subprocess
import os
import shutil
import stat
from grp import getgrnam
from pwd import getpwnam
from oslo_config import cfg
from oslo_log import log as logging
from sysinv._i18n import _
from sysinv.common import exception
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
fernet_group = cfg.OptGroup(
'fernet_repo',
title='fernet repo Options',
help="Configuration options for the fernet key repository")
fernet_opts = [
cfg.StrOpt('key_repository',
default='/etc/keystone/fernet-keys',
help="The fernet key repository."),
]
CONF.register_group(fernet_group)
CONF.register_opts(fernet_opts, group=fernet_group)
KEYSTONE_USER = 'keystone'
KEYSTONE_GROUP = 'keystone'
class FernetOperator(object):
"""Class to encapsulate Fernet Key operations for System Inventory"""
def __init__(self, keystone_user_id=None, keystone_group_id=None):
self.key_repository = CONF.fernet_repo.key_repository
self.keystone_user_id = keystone_user_id
self.keystone_group_id = keystone_group_id
def _set_user_group(self):
if self.keystone_user_id is None:
self.keystone_user_id = getpwnam(KEYSTONE_USER).pw_uid
if self.keystone_group_id is None:
self.keystone_group_id = getgrnam(KEYSTONE_GROUP).gr_gid
def _check_key_directory(self):
"""Check if the key directory exists and attempt to create it if it
doesn't.
"""
if not os.access(self.key_repository, os.F_OK):
LOG.info(_("key_repository:(%s) does not exist; attempting to "
"create it") % self.key_repository)
try:
os.makedirs(self.key_repository, 0o700)
except OSError:
LOG.error(_("Failed to create key_repository"))
return False
self._set_user_group()
os.chown(self.key_repository, self.keystone_user_id,
self.keystone_group_id)
return True
def _create_key_file(self, id, key):
"""Create a tmp key file."""
self._set_user_group()
temp_key_file = os.path.join(self.key_repository, str(id) + '.tmp')
real_key_file = os.path.join(self.key_repository, str(id))
create = False
try:
with open(temp_key_file, 'w') as f:
f.write(key)
f.flush()
create = True
os.chmod(temp_key_file, stat.S_IRUSR | stat.S_IWUSR)
os.chown(temp_key_file, self.keystone_user_id, self.keystone_group_id)
except IOError:
LOG.error('Failed to create new temporary key: %s' %
temp_key_file)
raise
finally:
if not create and os.access(temp_key_file, os.F_OK):
os.remove(temp_key_file)
return False
os.rename(temp_key_file, real_key_file)
LOG.debug('Created a new key: %s', real_key_file)
return True
def _get_key_files(self):
# read the list of key files
key_files = dict()
for filename in os.listdir(self.key_repository):
path = os.path.join(self.key_repository, str(filename))
if os.path.isfile(path):
try:
key_id = int(filename)
except ValueError:
pass
else:
key_files[key_id] = path
return key_files
def _validate_key_repository(self):
"""Validate permissions on the key repository directory."""
# ensure current user has sufficient access to the key repository
is_valid = os.access(self.key_repository, os.R_OK)
if not is_valid:
LOG.error(_("Either (%s) key_repository does not exist or we "
"don't have sufficient permission to access it." %
self.key_repository))
return is_valid
def update_fernet_keys(self, new_keys):
new_key_ids = []
if not self._check_key_directory():
raise exception.SysinvException(_(
"Error checking key repository."))
try:
for key in new_keys:
self._create_key_file(key['id'], key['key'])
new_key_ids.append(key['id'])
# remove excess keys
key_files = self._get_key_files()
for key in key_files.keys():
if key not in new_key_ids:
key_to_purge = key_files[key]
LOG.info('Purge excess key: %s', key_to_purge)
os.remove(key_to_purge)
except Exception as e:
msg = _("Failed to update fernet keys: %s") % e.message
LOG.exception(msg)
raise exception.SysinvException(msg)
def reset_fernet_keys(self):
try:
if os.path.isdir(self.key_repository):
LOG.info("Remove fernet repo")
shutil.rmtree(self.key_repository)
except OSError as e:
LOG.exception(e)
with open(os.devnull, "w") as fnull:
try:
LOG.info("Re-setup fernet repo")
subprocess.check_call(['/usr/bin/keystone-manage', # pylint: disable=not-callable
'fernet_setup',
'--keystone-user',
KEYSTONE_USER,
'--keystone-group',
KEYSTONE_GROUP],
stdout=fnull, stderr=fnull)
except subprocess.CalledProcessError as e:
msg = _("Failed to setup fernet keys: %s") % e.message
LOG.exception(msg)
raise exception.SysinvException(msg)
def get_fernet_keys(self, key_id=None):
keys = []
if not self._validate_key_repository():
return keys
key_files = self._get_key_files()
for k, v in key_files.items():
key = dict()
key['id'] = k
with open(v, 'r') as key_file:
key['key'] = key_file.read()
keys.append(key)
if key_id is not None and key_id == k:
break
return keys