From: John Mulligan Date: Fri, 18 Jul 2025 15:13:32 +0000 (-0400) Subject: mgr/smb: add support for generating keybridge configuration X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=645877331c103b116edf55c05e950ce384b331f4;p=ceph.git 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 --- diff --git a/src/pybind/mgr/smb/handler.py b/src/pybind/mgr/smb/handler.py index 1ae364cd1a95..ef66cb920622 100644 --- a/src/pybind/mgr/smb/handler.py +++ b/src/pybind/mgr/smb/handler.py @@ -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,