diff --git a/examples/kubernetes/encryption/specs/storageclass.yaml b/examples/kubernetes/encryption/specs/storageclass.yaml index 75eb23bf..66e9f5d6 100644 --- a/examples/kubernetes/encryption/specs/storageclass.yaml +++ b/examples/kubernetes/encryption/specs/storageclass.yaml @@ -6,10 +6,11 @@ metadata: provisioner: bsu.csi.outscale.com volumeBindingMode: WaitForFirstConsumer parameters: - encrypted: "true" + encrypted: 'true' luks-cipher: aes-xts-plain64 type: io1 - iopsPerGB: "50" + iopsPerGB: '50' csi.storage.k8s.io/node-stage-secret-name: luks-key csi.storage.k8s.io/node-stage-secret-namespace: encryption - + csi.storage.k8s.io/node-expand-secret-name: luks-key + csi.storage.k8s.io/node-expand-secret-namespace: encryption \ No newline at end of file diff --git a/pkg/driver/luks/luks.go b/pkg/driver/luks/luks.go index 2f410439..c58d3de7 100644 --- a/pkg/driver/luks/luks.go +++ b/pkg/driver/luks/luks.go @@ -12,6 +12,6 @@ type LuksService interface { CheckLuksPassphrase(devicePath string, passphrase string) bool LuksOpen(devicePath string, encryptedDeviceName string, passphrase string) (bool, error) IsLuksMapping(devicePath string) (bool, string, error) - LuksResize(deviceName string) error + LuksResize(deviceName string, passphrase string) error LuksClose(deviceName string) error -} +} \ No newline at end of file diff --git a/pkg/driver/luks_util.go b/pkg/driver/luks_util.go index 188790c6..0b249f9c 100644 --- a/pkg/driver/luks_util.go +++ b/pkg/driver/luks_util.go @@ -86,10 +86,17 @@ func IsLuksMapping(exec k8sExec.Interface, devicePath string) (bool, string, err return false, "", nil } -func LuksResize(exec k8sExec.Interface, deviceName string) error { +func LuksResize(exec k8sExec.Interface, deviceName string, passphrase string) error { cryptsetupArgs := []string{"--batch-mode", "resize", deviceName} + resizeCmd := exec.Command("cryptsetup", cryptsetupArgs...) + passwordReader := strings.NewReader(passphrase) + resizeCmd.SetStdin(passwordReader) + + if out, err := resizeCmd.CombinedOutput(); err != nil { + return fmt.Errorf("unable to resize LUKS volume on device %s: %w, output: %s", deviceName, err, string(out)) + } - return exec.Command("cryptsetup", cryptsetupArgs...).Run() + return nil } func LuksClose(mounter Mounter, encryptedDeviceName string) error { diff --git a/pkg/driver/luks_util_test.go b/pkg/driver/luks_util_test.go index cba2a3ab..e39feb44 100644 --- a/pkg/driver/luks_util_test.go +++ b/pkg/driver/luks_util_test.go @@ -2,6 +2,8 @@ package driver import ( "fmt" + "io" + "strings" "testing" "github.com/golang/mock/gomock" @@ -268,33 +270,58 @@ func TestIsLuksMapping(t *testing.T) { func TestLuksResize(t *testing.T) { mockCtl := gomock.NewController(t) + defer mockCtl.Finish() + devicePath := "fake_crypt" + passphrase := "fake_passphrase" // Check normal success mockCommand := mocks.NewMockInterface(mockCtl) mockRun := mocks.NewMockCmd(mockCtl) + + // Expect Command to be called with only four arguments, as passphrase is passed via stdin mockCommand.EXPECT().Command( gomock.Eq("cryptsetup"), gomock.Eq("--batch-mode"), gomock.Eq("resize"), gomock.Eq(devicePath), ).Return(mockRun) - mockRun.EXPECT().Run().Return(nil) - assert.Equal(t, nil, LuksResize(mockCommand, devicePath)) + // Expect SetStdin to be called with passphrase + mockRun.EXPECT().SetStdin(gomock.Any()).DoAndReturn(func(r io.Reader) { + // Check that the reader contains the passphrase + buf := new(strings.Builder) + io.Copy(buf, r) + assert.Equal(t, passphrase, buf.String()) + }) + + // Expect CombinedOutput instead of Run + mockRun.EXPECT().CombinedOutput().Return([]byte(""), nil) - // Check failure + // Call LuksResize with the mock command and passphrase + assert.Equal(t, nil, LuksResize(mockCommand, devicePath, passphrase)) + + // Check failure case mockCommand = mocks.NewMockInterface(mockCtl) mockRun = mocks.NewMockCmd(mockCtl) + mockCommand.EXPECT().Command( gomock.Eq("cryptsetup"), gomock.Eq("--batch-mode"), gomock.Eq("resize"), gomock.Eq(devicePath), ).Return(mockRun) - mockRun.EXPECT().Run().Return(fmt.Errorf("Error")) - assert.NotEqual(t, nil, LuksResize(mockCommand, devicePath)) + mockRun.EXPECT().SetStdin(gomock.Any()).DoAndReturn(func(r io.Reader) { + buf := new(strings.Builder) + io.Copy(buf, r) + assert.Equal(t, passphrase, buf.String()) + }) + + // Expect CombinedOutput to return an error + mockRun.EXPECT().CombinedOutput().Return([]byte(""), fmt.Errorf("Error")) + + assert.NotEqual(t, nil, LuksResize(mockCommand, devicePath, passphrase)) } func TestLuksClose(t *testing.T) { diff --git a/pkg/driver/mocks/mock_mounter.go b/pkg/driver/mocks/mock_mounter.go index 523eb387..2a7cf5e2 100644 --- a/pkg/driver/mocks/mock_mounter.go +++ b/pkg/driver/mocks/mock_mounter.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: ./pkg/driver/mount.go +// Source: pkg/driver/mount.go // Package mocks is a generated GoMock package. package mocks @@ -297,17 +297,17 @@ func (mr *MockMounterMockRecorder) LuksOpen(devicePath, encryptedDeviceName, pas } // LuksResize mocks base method. -func (m *MockMounter) LuksResize(deviceName string) error { +func (m *MockMounter) LuksResize(deviceName, passphrase string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LuksResize", deviceName) + ret := m.ctrl.Call(m, "LuksResize", deviceName, passphrase) ret0, _ := ret[0].(error) return ret0 } // LuksResize indicates an expected call of LuksResize. -func (mr *MockMounterMockRecorder) LuksResize(deviceName interface{}) *gomock.Call { +func (mr *MockMounterMockRecorder) LuksResize(deviceName, passphrase interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LuksResize", reflect.TypeOf((*MockMounter)(nil).LuksResize), deviceName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LuksResize", reflect.TypeOf((*MockMounter)(nil).LuksResize), deviceName, passphrase) } // MakeDir mocks base method. diff --git a/pkg/driver/mount.go b/pkg/driver/mount.go index 610d276d..50149721 100644 --- a/pkg/driver/mount.go +++ b/pkg/driver/mount.go @@ -111,8 +111,8 @@ func (m *NodeMounter) IsLuksMapping(devicePath string) (bool, string, error) { return IsLuksMapping(m, devicePath) } -func (m *NodeMounter) LuksResize(deviceName string) error { - return LuksResize(m, deviceName) +func (m *NodeMounter) LuksResize(deviceName string, passphrase string) error { + return LuksResize(m, deviceName, passphrase) } func (m *NodeMounter) LuksClose(deviceName string) error { diff --git a/pkg/driver/node.go b/pkg/driver/node.go index 0fffbf97..2ec66126 100644 --- a/pkg/driver/node.go +++ b/pkg/driver/node.go @@ -19,13 +19,14 @@ package driver import ( "context" "fmt" - "golang.org/x/sys/unix" "os" "path/filepath" "regexp" "strconv" "strings" + "golang.org/x/sys/unix" + csi "github.com/container-storage-interface/spec/lib/go/csi" "github.com/outscale-dev/osc-bsu-csi-driver/pkg/cloud" "github.com/outscale-dev/osc-bsu-csi-driver/pkg/driver/internal" @@ -386,7 +387,12 @@ func (d *nodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV } if isLuksMapping { - if err := d.mounter.LuksResize(mappingName); err != nil { + passphrase, ok := req.Secrets[LuksPassphraseKey] + if !ok { + klog.Errorf("NodeStageVolume: no passphrase key has been provided in req.Secrets: %+v", req.Secrets) + return nil, status.Error(codes.InvalidArgument, "no passphrase key has been provided") + } + if err := d.mounter.LuksResize(mappingName, passphrase); err != nil { return nil, status.Errorf(codes.Internal, "Could not resize Luks volume %q: %v", volumeID, err) } } diff --git a/pkg/driver/sanity_test.go b/pkg/driver/sanity_test.go index 605095b7..68865b2b 100644 --- a/pkg/driver/sanity_test.go +++ b/pkg/driver/sanity_test.go @@ -393,7 +393,7 @@ func (m *fakeMounter) IsLuksMapping(devicePath string) (bool, string, error) { return false, "", nil } -func (m *fakeMounter) LuksResize(deviceName string) error { +func (m *fakeMounter) LuksResize(deviceName string, passphrase string) error { return nil }