Skip to content

Commit 86b088e

Browse files
authored
Merge pull request #961 from mpatlasov/Use-credentials-mount-option
fix: enable to use secrets with special characters
2 parents 5b1ac85 + 5d48318 commit 86b088e

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed

pkg/smb/nodeserver.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ func (d *Driver) NodeUnpublishVolume(_ context.Context, req *csi.NodeUnpublishVo
124124
return &csi.NodeUnpublishVolumeResponse{}, nil
125125
}
126126

127+
// Returns true if the `word` contains a special character, i.e it can confuse mount command-line if passed as is:
128+
// mount -t cifs -o username=something,password=word,...
129+
// For now, only three such characters are known: "`,
130+
func ContainsSpecialCharacter(word string) bool {
131+
return strings.Contains(word, "\"") || strings.Contains(word, "`") || strings.Contains(word, ",")
132+
}
133+
127134
// NodeStageVolume mount the volume to a staging path
128135
func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
129136
volumeID := req.GetVolumeId()
@@ -231,7 +238,11 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
231238
return nil, status.Error(codes.Internal, fmt.Sprintf("MkdirAll %s failed with error: %v", targetPath, err))
232239
}
233240
if requireUsernamePwdOption && !useKerberosCache {
234-
sensitiveMountOptions = []string{fmt.Sprintf("%s=%s,%s=%s", usernameField, username, passwordField, password)}
241+
if ContainsSpecialCharacter(password) {
242+
sensitiveMountOptions = []string{fmt.Sprintf("%s=%s", usernameField, username), fmt.Sprintf("%s=%s", passwordField, password)}
243+
} else {
244+
sensitiveMountOptions = []string{fmt.Sprintf("%s=%s,%s=%s", usernameField, username, passwordField, password)}
245+
}
235246
}
236247
mountOptions = mountFlags
237248
if !gidPresent && volumeMountGroup != "" {

pkg/smb/nodeserver_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ func TestNodeStageVolume(t *testing.T) {
9292
passwordField: "test_password",
9393
domainField: "test_doamin",
9494
}
95+
secretsSpecial := map[string]string{
96+
usernameField: "test_username",
97+
passwordField: "test\"`,password",
98+
domainField: "test_doamin",
99+
}
95100

96101
tests := []struct {
97102
desc string
@@ -182,6 +187,23 @@ func TestNodeStageVolume(t *testing.T) {
182187
strings.Replace(testSource, "\\", "\\\\", -1), errorMountSensSource),
183188
},
184189
},
190+
{
191+
desc: "[Error] Failed SMB mount mocked by MountSensitive (password with special characters)",
192+
req: &csi.NodeStageVolumeRequest{VolumeId: "vol_1##", StagingTargetPath: errorMountSensSource,
193+
VolumeCapability: &stdVolCap,
194+
VolumeContext: volContext,
195+
Secrets: secretsSpecial},
196+
skipOnWindows: true,
197+
flakyWindowsErrorMessage: fmt.Sprintf("rpc error: code = Internal desc = volume(vol_1##) mount \"%s\" on %#v failed "+
198+
"with NewSmbGlobalMapping(%s, %s) failed with error: rpc error: code = Unknown desc = NewSmbGlobalMapping failed.",
199+
strings.Replace(testSource, "\\", "\\\\", -1), errorMountSensSource, testSource, errorMountSensSource),
200+
expectedErr: testutil.TestError{
201+
DefaultError: status.Errorf(codes.Internal,
202+
"volume(vol_1##) mount \"%s\" on \"%s\" failed with fake "+
203+
"MountSensitive: target error",
204+
strings.Replace(testSource, "\\", "\\\\", -1), errorMountSensSource),
205+
},
206+
},
185207
{
186208
desc: "[Success] Valid request",
187209
req: &csi.NodeStageVolumeRequest{VolumeId: "vol_1##", StagingTargetPath: sourceTest,

pkg/smb/smb_common_linux.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,40 @@ limitations under the License.
2020
package smb
2121

2222
import (
23+
"fmt"
2324
"os"
25+
"strings"
2426

2527
mount "k8s.io/mount-utils"
2628
)
2729

30+
// Returns true if the `options` contains password with a special characters, and so "credentials=" needed.
31+
// (see comments for ContainsSpecialCharacter() in pkg/smb/nodeserver.go).
32+
// NB: implementation relies on the format:
33+
// options := []string{fmt.Sprintf("%s=%s", usernameField, username), fmt.Sprintf("%s=%s", passwordField, password)}
34+
func NeedsCredentialsOption(options []string) bool {
35+
return len(options) == 2 && strings.HasPrefix(options[1], "password=") && ContainsSpecialCharacter(options[1])
36+
}
37+
2838
func Mount(m *mount.SafeFormatAndMount, source, target, fsType string, options, sensitiveMountOptions []string, _ string) error {
39+
if NeedsCredentialsOption(sensitiveMountOptions) {
40+
file, err := os.CreateTemp("/tmp/", "*.smb.credentials")
41+
if err != nil {
42+
return err
43+
}
44+
defer func() {
45+
file.Close()
46+
os.Remove(file.Name())
47+
}()
48+
49+
for _, option := range sensitiveMountOptions {
50+
if _, err := file.Write([]byte(fmt.Sprintf("%s\n", option))); err != nil {
51+
return err
52+
}
53+
}
54+
55+
sensitiveMountOptions = []string{fmt.Sprintf("credentials=%s", file.Name())}
56+
}
2957
return m.MountSensitive(source, target, fsType, options, sensitiveMountOptions)
3058
}
3159

pkg/smb/smb_common_linux_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package smb
18+
19+
import (
20+
"github.com/stretchr/testify/assert"
21+
"testing"
22+
)
23+
24+
func TestNeedsCredentialsOption(t *testing.T) {
25+
tests := []struct {
26+
options []string
27+
expectedResult bool
28+
}{
29+
{
30+
options: []string{"username=foo,password=bar"},
31+
expectedResult: false,
32+
},
33+
{
34+
options: []string{"username=foo", "password=bar"},
35+
expectedResult: false,
36+
},
37+
{
38+
options: []string{"username=foo", "password=b\"r"},
39+
expectedResult: true,
40+
},
41+
{
42+
options: []string{"username=foo", "password=b`r"},
43+
expectedResult: true,
44+
},
45+
{
46+
options: []string{"username=foo", "password=b,r"},
47+
expectedResult: true,
48+
},
49+
}
50+
51+
for _, test := range tests {
52+
assert.Equal(t, test.expectedResult, NeedsCredentialsOption(test.options))
53+
}
54+
}

0 commit comments

Comments
 (0)