]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/smb: add support for generating keybridge configuration
authorJohn Mulligan <jmulligan@redhat.com>
Fri, 18 Jul 2025 15:13:32 +0000 (11:13 -0400)
committerAvan Thakkar <athakkar@redhat.com>
Thu, 27 Nov 2025 15:07:27 +0000 (20:37 +0530)
Add support for generating the sambacc configuration section for
keybridge. Add support for configuring smb shares for keybridge access.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
(cherry picked from commit f4961e4ccfab4c37f6ab34eca24bb1eb58f8c9f3)

src/pybind/mgr/smb/handler.py

index ce6795fe7aaa66454ed5e32fc409e1456ba2914c..81c7bd61a19e69379b0d83b04d0c58c291783ca0 100644 (file)
@@ -23,6 +23,8 @@ from .enums import (
     AuthMode,
     CephFSStorageProvider,
     JoinSourceType,
+    KeyBridgePeerPolicy,
+    KeyBridgeScopeType,
     LoginAccess,
     LoginCategory,
     State,
@@ -62,6 +64,7 @@ _DOMAIN = 'domain'
 _CLUSTERED = 'clustered'
 _CEPHFS_PROXY = 'cephfs-proxy'
 _REMOTE_CONTROL = 'remote-control'
+_KEYBRIDGE = 'keybridge'
 log = logging.getLogger(__name__)
 
 
@@ -726,12 +729,20 @@ def _generate_share(
     }
     if share.comment is not None:
         cfg['options']['comment'] = share.comment
-
     if share.max_connections is not None:
         cfg['options']['max connections'] = str(share.max_connections)
-
     if proxy_val:
         cfg['options'][f'{ceph_vfs}:proxy'] = proxy_val
+    if cephfs.fscrypt_key:
+        # enable fscrypt + keybridge
+        opts = cfg['options']
+        opts[f'{ceph_vfs}:keybridge socket'] = 'unix:/run/keybridge.s'
+        opts[f'{ceph_vfs}:keybridge scope'] = str(
+            cephfs.fscrypt_key.scope_identity().qualified()
+        )
+        opts[f'{ceph_vfs}:keybridge name'] = str(cephfs.fscrypt_key.name)
+        opts[f'{ceph_vfs}:keybridge kind'] = 'B64'
+        opts[f'{ceph_vfs}:fscrypt'] = 'keybridge'
     # extend share with user+group login access lists
     _generate_share_login_control(share, cfg)
     # extend share with custom options
@@ -830,6 +841,10 @@ def _generate_config(
         },
         'shares': share_configs,
     }
+    if cluster.keybridge_is_enabled:
+        _kb_default = 'default-keybridge'  # typically only have one
+        cfg['configs'][cluster.cluster_id]['keybridge_config'] = _kb_default
+        cfg['keybridge'] = {_kb_default: _generate_keybridge_config(cluster)}
     # insert global custom options
     custom_opts = cluster.cleaned_custom_smb_global_options
     if custom_opts:
@@ -859,6 +874,8 @@ def _generate_smb_service_spec(
         features.append(_CEPHFS_PROXY)
     if cluster.remote_control_is_enabled:
         features.append(_REMOTE_CONTROL)
+    if cluster.keybridge_is_enabled:
+        features.append(_KEYBRIDGE)
     # only one config uri can be used, the input list should be
     # ordered from lowest to highest priority and the highest priority
     # item that exists in the store will be used.
@@ -890,6 +907,26 @@ def _generate_smb_service_spec(
         rc_ca_cert = _tls_uri(
             cluster.remote_control.ca_cert, tls_credential_entries
         )
+    kb_cert = kb_key = kb_ca_cert = None
+    if cluster.keybridge_is_enabled:
+        assert cluster.keybridge  # type narrow
+        # TODO: current all KMIP scopes must share the same tls creds
+        # and that sucks. need to update keybridge to fetch cert URIs directly
+        # and stop shoveling this all through cephadm.
+        # This is especially important if we ever add non-kmip scopes that
+        # use (m)TLS.
+        kmip_scopes = [
+            s
+            for s in checked(cluster.keybridge.scopes)
+            if s.scope_identity().scope_type == KeyBridgeScopeType.KMIP
+        ]
+        if kmip_scopes:
+            kmip_scope = kmip_scopes[0]
+            kb_cert = _tls_uri(kmip_scope.kmip_cert, tls_credential_entries)
+            kb_key = _tls_uri(kmip_scope.kmip_key, tls_credential_entries)
+            kb_ca_cert = _tls_uri(
+                kmip_scope.kmip_ca_cert, tls_credential_entries
+            )
     return SMBSpec(
         service_id=cluster.cluster_id,
         placement=cluster.placement,
@@ -906,9 +943,39 @@ def _generate_smb_service_spec(
         remote_control_ssl_cert=rc_cert,
         remote_control_ssl_key=rc_key,
         remote_control_ca_cert=rc_ca_cert,
+        keybridge_kmip_ssl_cert=kb_cert,
+        keybridge_kmip_ssl_key=kb_key,
+        keybridge_kmip_ca_cert=kb_ca_cert,
     )
 
 
+def _generate_keybridge_config(cluster: resources.Cluster) -> Dict[str, Any]:
+    """generate the keybridge subsection for the sambacc config."""
+    # NOTE: the tls credentials are handled by cephadm and are passed
+    # to the keybridge server command line (by path). That's why they are
+    # not mentioned here. This could change in the future.
+    scopes: List[Simplified] = []
+    for scope in checked(cluster.keybridge).scopes or []:
+        kbsi = scope.scope_identity()
+        scope_config: Simplified = {'name': str(kbsi.qualified())}
+        if kbsi.scope_type is KeyBridgeScopeType.KMIP:
+            scope_config['hostnames'] = checked(scope.kmip_hosts)
+            if scope.kmip_port:
+                scope_config['port'] = checked(scope.kmip_port)
+        scopes.append(scope_config)
+    cfg: Simplified = {'scopes': scopes}
+    if (
+        checked(cluster.keybridge).use_peer_policy
+        is KeyBridgePeerPolicy.RESTRICTED
+    ):
+        cfg['verify_peer'] = {
+            "check_pid": "1+",
+            "check_uid": "0",
+            "check_gid": "0",
+        }
+    return cfg
+
+
 def _swap_pending_cluster_info(
     store: ConfigStore,
     change_group: ClusterChangeGroup,