]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.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)
committerJohn Mulligan <jmulligan@redhat.com>
Wed, 25 Mar 2026 14:22:46 +0000 (10:22 -0400)
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>
src/pybind/mgr/smb/handler.py

index 1ae364cd1a95d5980a0bb54a5edc801d173550ca..ef66cb9206225c216c5d80f8c79efd5eb022b89f 100644 (file)
@@ -28,6 +28,8 @@ from .enums import (
     CephFSStorageProvider,
     HostAccess,
     JoinSourceType,
+    KeyBridgePeerPolicy,
+    KeyBridgeScopeType,
     LoginAccess,
     LoginCategory,
     State,
@@ -72,6 +74,7 @@ _DOMAIN = 'domain'
 _CLUSTERED = 'clustered'
 _CEPHFS_PROXY = 'cephfs-proxy'
 _REMOTE_CONTROL = 'remote-control'
+_KEYBRIDGE = 'keybridge'
 log = logging.getLogger(__name__)
 
 
@@ -861,12 +864,20 @@ def _generate_share(conf: _ShareConf) -> Dict[str, Dict[str, str]]:
                 opts[f"{vfs_rl}:{field}"] = str(value)
     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)
     _generate_share_hosts_access(share, cfg)
@@ -986,6 +997,10 @@ def _generate_config(conf: _ClusterConf) -> Dict[str, Any]:
         },
         '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:
@@ -1017,6 +1032,8 @@ def _generate_smb_service_spec(
     if cluster.remote_control_is_enabled:
         assert cluster.remote_control is not None
         features.extend(cluster.remote_control.enabled_features())
+    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.
@@ -1048,6 +1065,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
+            )
     ceph_cluster_configs = None
     if ext_ceph_cluster:
         exo = checked(ext_ceph_cluster.cluster)
@@ -1077,11 +1114,41 @@ 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,
         ceph_cluster_configs=ceph_cluster_configs,
         tunables=_service_spec_tunables(cluster),
     )
 
 
+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,