Commit cc3f842c authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Ben Walker
Browse files

sma: initial crypto definitions



This patch defines the interface for crypto engines, which provide
support for configuring crypto on a given volume.  Only a single crypto
engine can be active at a time and it's selected in the "crypto" section
of the config file.  Similarly to device managers, external crypto
engines can be loaded from plugins.

Signed-off-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Id942ef876e070816827d7ad1937eb510a85c8f8d
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13869


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatar <sebastian.brzezinka@intel.com>
parent b30edf64
Loading
Loading
Loading
Loading
+11 −6
Original line number Diff line number Diff line
@@ -10,3 +10,8 @@ from .device import DeviceManager # noqa
from .device import NvmfTcpDeviceManager     # noqa
from .device import VhostBlkDeviceManager    # noqa
from .device import NvmfVfioDeviceManager    # noqa
from .volume import CryptoEngine             # noqa
from .volume import CryptoException          # noqa
from .volume import set_crypto_engine        # noqa
from .volume import get_crypto_engine        # noqa
from .volume import register_crypto_engine   # noqa
+5 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import grpc
import logging
from .device import DeviceException
from .volume import VolumeException, VolumeManager
from .volume import crypto
from .proto import sma_pb2 as pb2
from .proto import sma_pb2_grpc as pb2_grpc

@@ -138,3 +139,7 @@ class StorageManagementAgent(pb2_grpc.StorageManagementAgentServicer):
            context.set_details(ex.message)
            context.set_code(ex.code)
        return response


crypto.register_crypto_engine(crypto.CryptoEngineNop())
crypto.set_crypto_engine('nop')
+5 −0
Original line number Diff line number Diff line
from .volume import VolumeException
from .volume import VolumeManager
from .crypto import CryptoEngine
from .crypto import CryptoException
from .crypto import set_crypto_engine
from .crypto import get_crypto_engine
from .crypto import register_crypto_engine
+87 −0
Original line number Diff line number Diff line
import grpc
import logging


log = logging.getLogger(__name__)


class CryptoException(Exception):
    def __init__(self, code, message):
        self.code = code
        self.message = message


class CryptoEngine:
    def __init__(self, name):
        self.name = name

    def init(self, client, params):
        """Initialize crypto engine"""
        self._client = client

    def setup(self, volume_id, key, cipher, key2=None):
        """Set up crypto on a given volume"""
        raise NotImplementedError()

    def cleanup(self, volume_id):
        """
        Disable crypto on a given volume.  If crypto was not configured on that volume, this method
        is a no-op and shouldn't raise any exceptions.
        """
        raise NotImplementedError()

    def verify(self, volume_id, key, cipher, key2=None):
        """
        Verify that specified crypto parameters match those that are currently deployed on a given
        volume.  If key is None, this mehtod ensures that the volume doesn't use crypto.  If
        something is wrong (e.g. keys don't match, different cipher is used, etc.), this method
        raises CryptoException.
        """
        raise NotImplementedError()

    def get_crypto_bdev(self, volume_id):
        """
        Return the name of a crypto bdev on a given volume.  This method might return volume_id if
        crypto engine doesn't create a separate crypto bdev to set up crypto.  If crypto is
        disabled on a given volue, this method returns None.
        """
        raise NotImplementedError()


class CryptoEngineNop(CryptoEngine):
    def __init__(self):
        super().__init__('nop')

    def setup(self, volume_id, key, cipher, key2=None):
        raise CryptoException(grpc.StatusCode.INVALID_ARGUMENT, 'Crypto is disabled')

    def cleanup(self, volume_id):
        pass

    def verify(self, volume_id, key, cipher, key2=None):
        pass

    def get_crypto_bdev(self, volume_id):
        return None


_crypto_engine = None
_crypto_engines = {}


def get_crypto_engine():
    return _crypto_engine


def set_crypto_engine(name):
    global _crypto_engine
    engine = _crypto_engines.get(name)
    if engine is None:
        raise ValueError(f'Unknown crypto engine: {name}')
    log.info(f'Setting crypto engine: {name}')
    _crypto_engine = engine


def register_crypto_engine(engine):
    global _crypto_engines
    _crypto_engines[engine.name] = engine
+21 −0
Original line number Diff line number Diff line
@@ -67,6 +67,22 @@ def register_devices(agent, devices, config):
        agent.register_device(device_manager)


def init_crypto(config, client):
    crypto_config = config.get('crypto')
    if crypto_config is None:
        return
    name = crypto_config.get('name')
    if name is None:
        logging.error('Crypto engine name is missing')
        sys.exit(1)
    try:
        sma.set_crypto_engine(name)
        sma.get_crypto_engine().init(client, crypto_config.get('params', {}))
    except ValueError:
        logging.error(f'Invalid crypto engine: {name}')
        sys.exit(1)


def load_plugins(plugins, client):
    devices = []
    for plugin in plugins:
@@ -74,6 +90,10 @@ def load_plugins(plugins, client):
        for device in getattr(module, 'devices', []):
            logging.debug(f'Loading external device: {plugin}.{device.__name__}')
            devices.append(device(client))
        for engine_class in getattr(module, 'crypto_engines', []):
            engine = engine_class()
            logging.debug(f'Loading external crypto engine: {plugin}.{engine.name}')
            sma.register_crypto_engine(engine)
    return devices


@@ -125,5 +145,6 @@ if __name__ == '__main__':
    devices += load_plugins(config.get('plugins') or [], client)
    devices += load_plugins(filter(None, os.environ.get('SMA_PLUGINS', '').split(':')),
                            client)
    init_crypto(config, client)
    register_devices(agent, devices, config)
    run(agent)
Loading