@@ -32,6 +32,7 @@ import (
3232
3333 csicommon "github.com/ceph/ceph-csi/internal/csi-common"
3434 "github.com/ceph/ceph-csi/internal/nvmeof"
35+ rbdutil "github.com/ceph/ceph-csi/internal/rbd"
3536 "github.com/ceph/ceph-csi/internal/util"
3637 "github.com/ceph/ceph-csi/internal/util/log"
3738)
@@ -45,6 +46,12 @@ type NodeServer struct {
4546 volumeLocks * util.IDLocker
4647
4748 initiator nvmeof.NVMeInitiator
49+
50+ // securityKeys manages DH-CHAP and PSK\TLS keys
51+ securityKeys nvmeof.SecurityKeyManager
52+
53+ // node ID of this node server
54+ nodeID string
4855}
4956
5057// ConnectionInfo holds NVMe-oF connection details.
@@ -55,6 +62,7 @@ type NvmeConnectionInfo struct {
5562 Listeners []nvmeof.GatewayAddress `json:"listeners"`
5663 HostNQN string `json:"hostNQN,omitempty"`
5764 Transport string `json:"transport"`
65+ DhchapMode string `json:"dhchapMode,omitempty"`
5866}
5967
6068// stageTransaction struct represents the state a transaction was when it either completed
@@ -76,6 +84,8 @@ const (
7684 nvmeofListeners = "listeners"
7785 nvmeofHostNQN = "HostNQN"
7886 defaultTransport = "tcp"
87+ nvmeofdhchapMode = "dhchapMode"
88+ authenticationKMSID = "authenticationKMSID"
7989)
8090
8191// NewNodeServer initialize a node server for ceph CSI driver.
@@ -90,6 +100,8 @@ func NewNodeServer(
90100 DefaultNodeServer : * csicommon .NewDefaultNodeServer (d , t , "" , map [string ]string {}, map [string ]string {}),
91101 initiator : nvmeInitiator ,
92102 volumeLocks : util .NewIDLocker (),
103+ securityKeys : nil , // Initialize lazily when needed
104+ nodeID : nodeID ,
93105 }
94106
95107 // Load nvme kernel modules
@@ -141,13 +153,8 @@ func (ns *NodeServer) NodeStageVolume(
141153 if err = util .ValidateNodeStageVolumeRequest (req ); err != nil {
142154 return nil , err
143155 }
144-
156+ secrets := req . GetSecrets ()
145157 volumeID := req .GetVolumeId ()
146- cr , err := util .NewUserCredentialsWithMigration (req .GetSecrets ())
147- if err != nil {
148- return nil , status .Error (codes .InvalidArgument , err .Error ())
149- }
150- defer cr .DeleteCredentials ()
151158 if acquired := ns .volumeLocks .TryAcquire (volumeID ); ! acquired {
152159 log .ErrorLog (ctx , util .VolumeOperationAlreadyExistsFmt , volumeID )
153160
@@ -173,10 +180,13 @@ func (ns *NodeServer) NodeStageVolume(
173180 if err != nil {
174181 return nil , status .Errorf (codes .InvalidArgument , "invalid volume context: %v" , err )
175182 }
183+ // Get authentication KMS ID from volume context.
184+ // can be empty! it will take default KMS in that case
185+ authKMSID := volumeContext [authenticationKMSID ]
176186
177187 // perform the actual staging and if this fails, have undoStagingTransaction
178188 // cleans up for us
179- txn , err := ns .stageTransaction (ctx , req , connectionInfo )
189+ txn , err := ns .stageTransaction (ctx , req , connectionInfo , secrets , authKMSID )
180190 defer func () {
181191 if err != nil {
182192 ns .undoStagingTransaction (ctx , req , txn )
@@ -426,13 +436,15 @@ func (ns *NodeServer) stageTransaction(
426436 ctx context.Context ,
427437 req * csi.NodeStageVolumeRequest ,
428438 connectionInfo * NvmeConnectionInfo ,
439+ secrets map [string ]string ,
440+ authKMSID string ,
429441) (* stageTransaction , error ) {
430442 transaction := & stageTransaction {}
431443
432444 var err error
433445
434446 // perform the actual staging
435- devicePath , err := ns .connectToSubsystem (ctx , connectionInfo )
447+ devicePath , err := ns .connectToSubsystem (ctx , req . GetVolumeId (), connectionInfo , secrets , authKMSID )
436448 if err != nil {
437449 return transaction , err
438450 }
@@ -579,6 +591,10 @@ func (ns *NodeServer) getNvmeConnection(volumeContext, publishContext map[string
579591 if ! ok || hostNQN == "" {
580592 return nil , errors .New ("missing host NQN in publish context" )
581593 }
594+ dhchapMode , ok := volumeContext [nvmeofdhchapMode ]
595+ if ! ok || dhchapMode == "" || dhchapMode == "none" {
596+ dhchapMode = ""
597+ }
582598
583599 return & NvmeConnectionInfo {
584600 SubsystemNQN : subsystemNQN ,
@@ -587,11 +603,18 @@ func (ns *NodeServer) getNvmeConnection(volumeContext, publishContext map[string
587603 Listeners : listeners ,
588604 HostNQN : hostNQN ,
589605 Transport : defaultTransport ,
606+ DhchapMode : dhchapMode ,
590607 }, nil
591608}
592609
593610// connectToSubsystem connects to the NVMe-oF subsystem and returns device path.
594- func (ns * NodeServer ) connectToSubsystem (ctx context.Context , info * NvmeConnectionInfo ) (string , error ) {
611+ func (ns * NodeServer ) connectToSubsystem (
612+ ctx context.Context ,
613+ volumeID string ,
614+ info * NvmeConnectionInfo ,
615+ secrets map [string ]string ,
616+ authKMSID string ,
617+ ) (string , error ) {
595618 // Create connect request
596619 connectReq := & nvmeof.ConnectRequest {
597620 SubsystemNQN : info .SubsystemNQN ,
@@ -600,6 +623,13 @@ func (ns *NodeServer) connectToSubsystem(ctx context.Context, info *NvmeConnecti
600623 HostNQN : info .HostNQN ,
601624 }
602625
626+ // Setup DH-CHAP authentication if required
627+ if info .DhchapMode != nvmeof .DHCHAPEmpty && info .DhchapMode != nvmeof .DHCHAPModeNone {
628+ if err := ns .setupDHCHAPAuth (ctx , volumeID , info , secrets , authKMSID , connectReq ); err != nil {
629+ return "" , err
630+ }
631+ }
632+
603633 // Connect to subsystem
604634 _ , err := ns .initiator .ConnectSubsystem (ctx , connectReq )
605635 if err != nil {
@@ -732,3 +762,75 @@ func (ns *NodeServer) getDeviceFromMount(ctx context.Context, mountPath string)
732762
733763 return "" , fmt .Errorf ("no mount found for path %s" , mountPath )
734764}
765+
766+ func (cs * NodeServer ) getOrInitSecurityKeys (
767+ ctx context.Context ,
768+ kmsID string ,
769+ credentials map [string ]string ,
770+ ) (nvmeof.SecurityKeyManager , error ) {
771+ if cs .securityKeys != nil {
772+ return cs .securityKeys , nil
773+ }
774+
775+ var err error
776+ securityKeys , err := nvmeof .InitSecurityKeyManager (ctx , kmsID , credentials )
777+
778+ // Only cache if it doesn't need external DEKStore (like Vault)
779+ // (RBD Metadata KMS needs fresh RBD volume each call)
780+ if ! errors .Is (err , nvmeof .ErrDEKStoreNeeded ) {
781+ cs .securityKeys = securityKeys
782+ }
783+
784+ return securityKeys , err
785+ }
786+
787+ // setupDHCHAPAuth configures DH-CHAP authentication for the connection.
788+ func (ns * NodeServer ) setupDHCHAPAuth (
789+ ctx context.Context ,
790+ volumeID string ,
791+ info * NvmeConnectionInfo ,
792+ secrets map [string ]string ,
793+ authKMSID string ,
794+ connectReq * nvmeof.ConnectRequest ,
795+ ) error {
796+ // Initialize security key manager
797+ securityKeys , err := ns .getOrInitSecurityKeys (ctx , authKMSID , secrets )
798+
799+ // Setup DEK store if needed (for metadata KMS)
800+ if errors .Is (err , nvmeof .ErrDEKStoreNeeded ) {
801+ cr , err := util .NewUserCredentialsWithMigration (secrets )
802+ if err != nil {
803+ return fmt .Errorf ("failed to get user credentials: %w" , err )
804+ }
805+ defer cr .DeleteCredentials ()
806+
807+ rbdVol , err := rbdutil .GenVolFromVolID (ctx , volumeID , cr , secrets )
808+ if err != nil {
809+ return fmt .Errorf ("failed to get volume: %w" , err )
810+ }
811+ defer rbdVol .Destroy (ctx )
812+
813+ dekStore := nvmeof .NewRBDVolumeDEKStore (rbdVol )
814+ securityKeys .SetDEKStore (dekStore )
815+ } else if err != nil {
816+ return fmt .Errorf ("failed to initialize security key manager: %w" , err )
817+ }
818+
819+ // Get host key
820+ hostKey , err := nvmeof .GetOrCreateDHCHAPHostKey (ctx , securityKeys , ns .nodeID , info .SubsystemNQN )
821+ if err != nil {
822+ return fmt .Errorf ("failed to get DHCHAP host key: %w" , err )
823+ }
824+ connectReq .HostDhchapKey = hostKey
825+
826+ // Get subsystem key for bidirectional mode
827+ if info .DhchapMode == nvmeof .DHCHAPModeBiDirectional {
828+ subsystemKey , err := nvmeof .GetOrCreateDHCHAPSubsystemKey (ctx , securityKeys , ns .nodeID , info .SubsystemNQN )
829+ if err != nil {
830+ return fmt .Errorf ("failed to get DH-CHAP subsystem key: %w" , err )
831+ }
832+ connectReq .SubsystemDhchapKey = subsystemKey
833+ }
834+
835+ return nil
836+ }
0 commit comments