@@ -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.SecurityKeyNVMEOFManager
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,9 +153,9 @@ 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 () )
158+ cr , err := util .NewUserCredentialsWithMigration (secrets )
147159 if err != nil {
148160 return nil , status .Error (codes .InvalidArgument , err .Error ())
149161 }
@@ -173,10 +185,13 @@ func (ns *NodeServer) NodeStageVolume(
173185 if err != nil {
174186 return nil , status .Errorf (codes .InvalidArgument , "invalid volume context: %v" , err )
175187 }
188+ // Get authentication KMS ID from volume context.
189+ // can be empty! it will take default KMS in that case
190+ authKMSID := volumeContext [authenticationKMSID ]
176191
177192 // perform the actual staging and if this fails, have undoStagingTransaction
178193 // cleans up for us
179- txn , err := ns .stageTransaction (ctx , req , connectionInfo )
194+ txn , err := ns .stageTransaction (ctx , req , connectionInfo , cr , secrets , authKMSID )
180195 defer func () {
181196 if err != nil {
182197 ns .undoStagingTransaction (ctx , req , txn )
@@ -426,13 +441,16 @@ func (ns *NodeServer) stageTransaction(
426441 ctx context.Context ,
427442 req * csi.NodeStageVolumeRequest ,
428443 connectionInfo * NvmeConnectionInfo ,
444+ cr * util.Credentials ,
445+ secrets map [string ]string ,
446+ authKMSID string ,
429447) (* stageTransaction , error ) {
430448 transaction := & stageTransaction {}
431449
432450 var err error
433451
434452 // perform the actual staging
435- devicePath , err := ns .connectToSubsystem (ctx , connectionInfo )
453+ devicePath , err := ns .connectToSubsystem (ctx , req . GetVolumeId (), connectionInfo , cr , secrets , authKMSID )
436454 if err != nil {
437455 return transaction , err
438456 }
@@ -579,6 +597,10 @@ func (ns *NodeServer) getNvmeConnection(volumeContext, publishContext map[string
579597 if ! ok || hostNQN == "" {
580598 return nil , errors .New ("missing host NQN in publish context" )
581599 }
600+ dhchapMode , ok := volumeContext [nvmeofdhchapMode ]
601+ if ! ok || dhchapMode == "" || dhchapMode == "none" {
602+ dhchapMode = ""
603+ }
582604
583605 return & NvmeConnectionInfo {
584606 SubsystemNQN : subsystemNQN ,
@@ -587,11 +609,19 @@ func (ns *NodeServer) getNvmeConnection(volumeContext, publishContext map[string
587609 Listeners : listeners ,
588610 HostNQN : hostNQN ,
589611 Transport : defaultTransport ,
612+ DhchapMode : dhchapMode ,
590613 }, nil
591614}
592615
593616// connectToSubsystem connects to the NVMe-oF subsystem and returns device path.
594- func (ns * NodeServer ) connectToSubsystem (ctx context.Context , info * NvmeConnectionInfo ) (string , error ) {
617+ func (ns * NodeServer ) connectToSubsystem (
618+ ctx context.Context ,
619+ volumeID string ,
620+ info * NvmeConnectionInfo ,
621+ cr * util.Credentials ,
622+ secrets map [string ]string ,
623+ authKMSID string ,
624+ ) (string , error ) {
595625 // Create connect request
596626 connectReq := & nvmeof.ConnectRequest {
597627 SubsystemNQN : info .SubsystemNQN ,
@@ -600,6 +630,13 @@ func (ns *NodeServer) connectToSubsystem(ctx context.Context, info *NvmeConnecti
600630 HostNQN : info .HostNQN ,
601631 }
602632
633+ // Setup DH-CHAP authentication if required
634+ if info .DhchapMode != nvmeof .DHCHAPEmpty && info .DhchapMode != nvmeof .DHCHAPModeNone {
635+ if err := ns .setupDHCHAPAuth (ctx , volumeID , info , cr , secrets , authKMSID , connectReq ); err != nil {
636+ return "" , err
637+ }
638+ }
639+
603640 // Connect to subsystem
604641 _ , err := ns .initiator .ConnectSubsystem (ctx , connectReq )
605642 if err != nil {
@@ -732,3 +769,70 @@ func (ns *NodeServer) getDeviceFromMount(ctx context.Context, mountPath string)
732769
733770 return "" , fmt .Errorf ("no mount found for path %s" , mountPath )
734771}
772+
773+ func (cs * NodeServer ) getOrInitSecurityKeys (
774+ ctx context.Context ,
775+ kmsID string ,
776+ credentials map [string ]string ,
777+ ) (* nvmeof.SecurityKeyNVMEOFManager , error ) {
778+ if cs .securityKeys != nil {
779+ return cs .securityKeys , nil
780+ }
781+
782+ var err error
783+ securityKeys , err := nvmeof .InitSecurityKeyManager (ctx , kmsID , credentials )
784+
785+ // Only cache if it doesn't need external DEKStore (like Vault)
786+ // (RBD Metadata KMS needs fresh RBD volume each call)
787+ if ! errors .Is (err , nvmeof .ErrDEKStoreNeeded ) {
788+ cs .securityKeys = securityKeys
789+ }
790+
791+ return securityKeys , err
792+ }
793+
794+ // setupDHCHAPAuth configures DH-CHAP authentication for the connection.
795+ func (ns * NodeServer ) setupDHCHAPAuth (
796+ ctx context.Context ,
797+ volumeID string ,
798+ info * NvmeConnectionInfo ,
799+ cr * util.Credentials ,
800+ secrets map [string ]string ,
801+ authKMSID string ,
802+ connectReq * nvmeof.ConnectRequest ,
803+ ) error {
804+ // Initialize security key manager
805+ securityKeys , err := ns .getOrInitSecurityKeys (ctx , authKMSID , secrets )
806+
807+ // Setup DEK store if needed (for metadata KMS)
808+ if errors .Is (err , nvmeof .ErrDEKStoreNeeded ) {
809+ rbdVol , err := rbdutil .GenVolFromVolID (ctx , volumeID , cr , secrets )
810+ if err != nil {
811+ return fmt .Errorf ("failed to get volume: %w" , err )
812+ }
813+ defer rbdVol .Destroy (ctx )
814+
815+ dekStore := nvmeof .NewRBDVolumeDEKStore (rbdVol )
816+ securityKeys .SetDEKStore (dekStore )
817+ } else if err != nil {
818+ return fmt .Errorf ("failed to initialize security key manager: %w" , err )
819+ }
820+
821+ // Get host key
822+ hostKey , err := securityKeys .GetOrCreateDHCHAPHostKey (ctx , ns .nodeID , info .SubsystemNQN )
823+ if err != nil {
824+ return fmt .Errorf ("failed to get DHCHAP host key: %w" , err )
825+ }
826+ connectReq .HostDhchapKey = hostKey
827+
828+ // Get subsystem key for bidirectional mode
829+ if info .DhchapMode == nvmeof .DHCHAPModeBiDirectional {
830+ subsystemKey , err := securityKeys .GetOrCreateDHCHAPSubsystemKey (ctx , ns .nodeID , info .SubsystemNQN )
831+ if err != nil {
832+ return fmt .Errorf ("failed to get DH-CHAP subsystem key: %w" , err )
833+ }
834+ connectReq .SubsystemDhchapKey = subsystemKey
835+ }
836+
837+ return nil
838+ }
0 commit comments