Implement key rotation for OSD

This patchset implements key rotation for OSD's. It does so by
receiving an update in the relation data bag from ceph-mons (where
the actions is started), which informs the OSD units for which
OSD the key needs to be rotated and the key itself. The OSD units
then check if they are managing the ID specified, and if so,
proceed to rotate the key.

Change-Id: I382a0a657b31c172a036ce7ca62facbbce32b4a0
This commit is contained in:
Luciano Lo Giudice 2024-04-11 13:37:19 -03:00
parent bd57a326e9
commit 4c84bc6530
4 changed files with 64 additions and 4 deletions

View File

@ -85,6 +85,7 @@ from utils import (
import_osd_upgrade_key,
import_osd_removal_key,
import_client_crash_key,
import_pending_key,
get_host_ip,
get_networks,
assert_charm_supports_ipv6,
@ -738,8 +739,28 @@ def get_bdev_enable_discard():
"bdev-enable-discard: %s") % bdev_enable_discard)
def handle_pending_key(pending_key):
ix = pending_key.find(':')
if ix < 0:
log('invalid pending key: %s' % pending_key)
return
osd_id = pending_key[:ix]
if not os.path.exists('/var/lib/ceph/osd/ceph-%s' % osd_id):
log('pending key not meant for this unit - skipping')
return
import_pending_key(pending_key[ix + 1:], osd_id)
service_restart('ceph-osd@%s' % osd_id)
@hooks.hook('mon-relation-changed')
def mon_relation():
pending_key = relation_get('pending_key')
if pending_key:
handle_pending_key(pending_key)
return
bootstrap_key = relation_get('osd_bootstrap_key')
upgrade_key = relation_get('osd_upgrade_key')
removal_key = relation_get('osd_disk_removal_key')

View File

@ -85,15 +85,17 @@ def is_osd_bootstrap_ready():
return os.path.exists(_bootstrap_keyring)
def _import_key(key, path, name):
if not os.path.exists(path):
def _import_key(key, path, name, override=False):
exists = os.path.exists(path)
if not exists or override:
create = ['--create-keyring'] if not exists else []
cmd = [
'sudo',
'-u',
ceph.ceph_user(),
'ceph-authtool',
path,
'--create-keyring',
path
] + create + [
'--name={}'.format(name),
'--add-key={}'.format(key)
]
@ -140,6 +142,19 @@ def import_client_crash_key(key):
_import_key(key, _client_crash_keyring, 'client.crash')
def import_pending_key(key, osd_id):
"""
Import a pending key, used for key rotation.
:param key: The pending cephx key that will replace the current one.
:type key: str
:param osd_id: The OSD id whose key will be replaced.
:type osd_id: str
:raises: subprocess.CalledProcessError"""
_import_key(key, '/var/lib/ceph/osd/ceph-%s/keyring' % osd_id,
'osd.%s' % osd_id, override=True)
def render_template(template_name, context, template_dir=TEMPLATES_DIR):
"""Render Jinja2 template.

View File

@ -835,6 +835,18 @@ class CephHooksTestCase(unittest.TestCase):
level=ceph_hooks.ERROR,
)
@patch.object(ceph_hooks, 'service_restart')
@patch.object(ceph_hooks, 'import_pending_key')
@patch.object(ceph_hooks.os.path, 'exists')
def test_handle_pending_key(self, exists, import_pending_key,
service_restart):
exists.return_value = True
pending_key = '0:some-key'
ceph_hooks.handle_pending_key(pending_key)
exists.assert_called_with('/var/lib/ceph/osd/ceph-0')
import_pending_key.assert_called_with('some-key', '0')
service_restart.assert_called_with('ceph-osd@0')
@patch.object(ceph_hooks, 'local_unit')
@patch.object(ceph_hooks, 'relation_get')

View File

@ -352,3 +352,15 @@ cset.uuid 57add9da-e5de-47c6-8f39-3e16aafb8d31
}]
}'''
self.assertEqual(utils.get_parent_device('/dev/loop1p1'), '/dev/loop1')
@patch.object(utils.ceph, 'ceph_user')
@patch.object(utils.subprocess, 'check_call')
@patch.object(utils.os.path, 'exists')
def test_import_pending_key(self, exists, check_call, ceph_user):
ceph_user.return_value = 'ceph'
exists.return_value = True
utils.import_pending_key('some-key', '0')
exists.assert_called_with('/var/lib/ceph/osd/ceph-0/keyring')
check_call.assert_called_with(['sudo', '-u', 'ceph', 'ceph-authtool',
'/var/lib/ceph/osd/ceph-0/keyring',
'--name=osd.0', '--add-key=some-key'])