From a2d63960662f6a402b0797c350d0852a4d3db27c Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Fri, 18 Jul 2025 11:13:32 -0400 Subject: [PATCH] mgr/smb: add support for generating keybridge configuration Add support for generating the sambacc configuration section for keybridge. Add support for configuring smb shares for keybridge access. Signed-off-by: John Mulligan (cherry picked from commit f4961e4ccfab4c37f6ab34eca24bb1eb58f8c9f3) --- src/pybind/mgr/smb/handler.py | 71 ++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/pybind/mgr/smb/handler.py b/src/pybind/mgr/smb/handler.py index ce6795fe7aa..81c7bd61a19 100644 --- a/src/pybind/mgr/smb/handler.py +++ b/src/pybind/mgr/smb/handler.py @@ -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, -- 2.47.3