]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
python-common/cryptotools: create module for selecting crypto caller
authorJohn Mulligan <jmulligan@redhat.com>
Thu, 24 Apr 2025 19:17:50 +0000 (15:17 -0400)
committerJohn Mulligan <jmulligan@redhat.com>
Mon, 7 Jul 2025 13:34:08 +0000 (09:34 -0400)
Add a module to select a desired crypto caller. Update the callers
to use the crypto caller interface.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
src/pybind/mgr/dashboard/services/access_control.py
src/pybind/mgr/mgr_util.py
src/python-common/ceph/cryptotools/remote.py
src/python-common/ceph/cryptotools/select.py [new file with mode: 0644]

index ac3aee461487c4317c6adcdb8eeca0f593df644f..440c125b81b32f70c96820f044ac3bc12ceda0c4 100644 (file)
@@ -23,7 +23,7 @@ from ..exceptions import PasswordPolicyException, PermissionNotValid, \
 from ..security import Permission, Scope
 from ..settings import Settings
 
-import ceph.cryptotools.remote
+from ceph.cryptotools.select import get_crypto_caller
 
 logger = logging.getLogger('access_control')
 DEFAULT_FILE_DESC = 'password/secret'
@@ -931,7 +931,7 @@ def ac_user_set_password_hash(_, username: str, inbuf: str):
     try:
         # make sure the hashed_password is actually a bcrypt hash
         # catch a ValueError if hashed_password is not valid.
-        cc = ceph.cryptotools.remote.CryptoCaller()
+        cc = get_crypto_caller()
         cc.verify_password('', hashed_password)
 
         user = mgr.ACCESS_CTRL_DB.get_user(username)
index b343f1a038475edf1e541a62a37521f2cd9cbebe..8574bf5f728a6b1a2a4e647829be2d1a6e6d2bc4 100644 (file)
@@ -32,7 +32,7 @@ else:
 from typing import Tuple, Any, Callable, Optional, Dict, TYPE_CHECKING, TypeVar, List, Iterable, Generator, Generic, Iterator
 
 from ceph.deployment.utils import wrap_ipv6
-import ceph.cryptotools.remote
+from ceph.cryptotools.select import get_crypto_caller
 
 T = TypeVar('T')
 
@@ -637,7 +637,7 @@ def create_self_signed_cert(organisation: str = 'Ceph',
     else:
         dname = {"O": organisation, "CN": common_name}
 
-    cc = ceph.cryptotools.remote.CryptoCaller()
+    cc = get_crypto_caller()
     pkey = cc.create_private_key()
     cert = cc.create_self_signed_cert(dname, pkey)
     return cert, pkey
@@ -645,7 +645,7 @@ def create_self_signed_cert(organisation: str = 'Ceph',
 
 def certificate_days_to_expire(crt: str) -> int:
     try:
-        cc = ceph.cryptotools.remote.CryptoCaller()
+        cc = get_crypto_caller()
         return cc.certificate_days_to_expire(crt)
     except ValueError as err:
         raise ServerConfigException(f'Invalid certificate: {err}')
@@ -670,7 +670,7 @@ def verify_cacrt(cert_fname):
 
 def get_cert_issuer_info(crt: str) -> Tuple[Optional[str], Optional[str]]:
     """Basic validation of a ca cert"""
-    cc = ceph.cryptotools.remote.CryptoCaller()
+    cc = get_crypto_caller()
     try:
         return cc.get_cert_issuer_info(crt)
     except ValueError as err:
@@ -679,7 +679,7 @@ def get_cert_issuer_info(crt: str) -> Tuple[Optional[str], Optional[str]]:
 
 def verify_tls(crt, key):
     # type: (str, str) -> int
-    cc = ceph.cryptotools.remote.CryptoCaller()
+    cc = get_crypto_caller()
     try:
         days_to_expiration = cc.certificate_days_to_expire(crt)
         cc.verify_tls(crt, key)
@@ -926,5 +926,5 @@ def password_hash(password: Optional[str], salt_password: Optional[str] = None)
     if not salt_password:
         salt_password = ''
 
-    cc = ceph.cryptotools.remote.CryptoCaller()
+    cc = get_crypto_caller()
     return cc.password_hash(password, salt_password)
index 76438b3d1321c9bd02d6b9f226bb59133d0b5c34..2574b4ecdac215624e43312a799564dfed81d4fe 100644 (file)
@@ -1,5 +1,6 @@
 """Remote execution of cryptographic functions for the ceph mgr
 """
+
 # NB. This module exists to enapsulate the logic around running
 # the cryptotools module that are forked off of the parent process
 # to avoid the pyo3 subintepreters problem.
@@ -23,18 +24,16 @@ import json
 import logging
 import subprocess
 
+from .caller import CryptoCaller, CryptoCallError
+
 
 _ctmodule = 'ceph.cryptotools.cryptotools'
 
 logger = logging.getLogger('ceph.cryptotools.remote')
 
 
-class CryptoCallError(ValueError):
-    pass
-
-
-class CryptoCaller:
-    """CryptoCaller encapsulates cryptographic functions used by the
+class ProcessCryptoCaller(CryptoCaller):
+    """ProcessCryptoCaller encapsulates cryptographic functions used by the
     ceph mgr into a suite of functions that can be executed in a
     different process.
     Running the crypto functions in a separate process avoids conflicts
diff --git a/src/python-common/ceph/cryptotools/select.py b/src/python-common/ceph/cryptotools/select.py
new file mode 100644 (file)
index 0000000..989382c
--- /dev/null
@@ -0,0 +1,51 @@
+from typing import Dict
+
+import os
+
+from .caller import CryptoCaller
+
+
+_CC_ENV = 'CEPH_CRYPTOCALLER'
+_CC_KEY = 'crypto_caller'
+_CC_REMOTE = 'remote'
+_CC_INTERNAL = 'internal'
+
+_CACHE: Dict[str, CryptoCaller] = {}
+
+
+def _check_name(name: str) -> None:
+    if name and name not in (_CC_REMOTE, _CC_INTERNAL):
+        raise ValueError(f'unexpected crypto caller name: {name}')
+
+
+def choose_crypto_caller(name: str = '') -> None:
+    _check_name(name)
+    if not name:
+        name = os.environ.get(_CC_ENV, '')
+        _check_name(name)
+    if not name:
+        name = _CC_REMOTE
+
+    if name == _CC_REMOTE:
+        import ceph.cryptotools.remote
+
+        _CACHE[_CC_KEY] = ceph.cryptotools.remote.ProcessCryptoCaller()
+        return
+    if name == _CC_INTERNAL:
+        import ceph.cryptotools.internal
+
+        _CACHE[_CC_KEY] = ceph.cryptotools.internal.InternalCryptoCaller()
+        return
+    # should be unreachable
+    raise RuntimeError('failed to setup a valid crypto caller')
+
+
+def get_crypto_caller() -> CryptoCaller:
+    """Return the currently selected crypto caller object."""
+    caller = _CACHE.get(_CC_KEY)
+    if not caller:
+        choose_crypto_caller()
+        caller = _CACHE.get(_CC_KEY)
+        if caller is None:
+            raise RuntimeError('failed to select crypto caller')
+    return caller