From 173b538c2b520867dc2103be4ff91a66f0cbf76f Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 2 Jul 2025 17:44:45 -0400 Subject: [PATCH] mgr/smb: add a new tls credential resource type Add a new TLS credential resource type that can currently store one TLS certificate, TLS key, or TLS CA certificate. This is a new top-level resource so that they can be use across clusters or managed independently by different people and or private information can be kept out a cluster resource stored in a file committed somewhere. Signed-off-by: John Mulligan --- src/pybind/mgr/smb/resources.py | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/pybind/mgr/smb/resources.py b/src/pybind/mgr/smb/resources.py index b00c20b9ca48e..1320619650bf9 100644 --- a/src/pybind/mgr/smb/resources.py +++ b/src/pybind/mgr/smb/resources.py @@ -1,6 +1,7 @@ from typing import Dict, List, Optional, Tuple, Union, cast import base64 +import dataclasses import errno import json @@ -24,6 +25,7 @@ from .enums import ( LoginCategory, PasswordFilter, SMBClustering, + TLSCredentialType, UserGroupSourceType, ) from .proto import Self, Simplified @@ -623,6 +625,50 @@ class UsersAndGroups(_RBase): ) +@resourcelib.resource('ceph.smb.tls.credential') +class TLSCredential(_RBase): + """Contains a TLS certificate or key that can be used to configure + SMB services that make use of TLS/SSL. + """ + + tls_credential_id: str + intent: Intent = Intent.PRESENT + credential_type: Optional[TLSCredentialType] = None + value: Optional[str] = None + # linked resources can only be used by the resource they are linked to + # and are automatically removed when the "parent" resource is removed + linked_to_cluster: Optional[str] = None + + def validate(self) -> None: + if not self.tls_credential_id: + raise ValueError('tls_credential_id requires a value') + validation.check_id(self.tls_credential_id) + if self.linked_to_cluster is not None: + validation.check_id(self.linked_to_cluster) + if self.intent is Intent.PRESENT: + if self.credential_type is None: + raise ValueError('credential_type must be specified') + if not self.value: + raise ValueError('a value must be specified') + + @resourcelib.customize + def _customize_resource(rc: resourcelib.Resource) -> resourcelib.Resource: + rc.value.wrapper_type = BigString + return rc + + def convert(self, operation: ConversionOp) -> Self: + """When hiding sensitive data hide TLS/SSL certs too. However, the + BASE64 filter enum will act as a no-op. Our certs are already long + Base64 encoded strings that are resistant to casual shoulder-surfing. + """ + if ( + operation == (PasswordFilter.NONE, PasswordFilter.HIDDEN) + and self.value + ): + return dataclasses.replace(self, value=_MASKED) + return self + + # SMBResource is a union of all valid top-level smb resource types. SMBResource = Union[ Cluster, @@ -631,6 +677,7 @@ SMBResource = Union[ RemovedShare, Share, UsersAndGroups, + TLSCredential, ] -- 2.39.5