Skip to content

Commit 5853414

Browse files
authored
Merge pull request #4 from pohly/older-csi
support also CSI <= 0.3.0
2 parents 1628ab5 + 1e77671 commit 5853414

File tree

6 files changed

+5148
-260
lines changed

6 files changed

+5148
-260
lines changed

Gopkg.lock

+4-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protosanitizer/protosanitizer.go

+47-8
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import (
2424
"reflect"
2525
"strings"
2626

27-
"github.com/container-storage-interface/spec/lib/go/csi"
2827
"github.com/golang/protobuf/descriptor"
2928
"github.com/golang/protobuf/proto"
3029
protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor"
30+
protobufdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
3131
)
3232

3333
// StripSecrets returns a wrapper around the original CSI gRPC message
@@ -36,15 +36,27 @@ import (
3636
// Instead of the secret value(s), the string "***stripped***" is
3737
// included in the result.
3838
//
39+
// StripSecrets relies on an extension in CSI 1.0 and thus can only
40+
// be used for messages based on that or a more recent spec!
41+
//
3942
// StripSecrets itself is fast and therefore it is cheap to pass the
4043
// result to logging functions which may or may not end up serializing
4144
// the parameter depending on the current log level.
4245
func StripSecrets(msg interface{}) fmt.Stringer {
43-
return &stripSecrets{msg}
46+
return &stripSecrets{msg, isCSI1Secret}
47+
}
48+
49+
// StripSecretsCSI03 is like StripSecrets, except that it works
50+
// for messages based on CSI 0.3 and older. It does not work
51+
// for CSI 1.0, use StripSecrets for that.
52+
func StripSecretsCSI03(msg interface{}) fmt.Stringer {
53+
return &stripSecrets{msg, isCSI03Secret}
4454
}
4555

4656
type stripSecrets struct {
4757
msg interface{}
58+
59+
isSecretField func(field *protobuf.FieldDescriptorProto) bool
4860
}
4961

5062
func (s *stripSecrets) String() string {
@@ -60,7 +72,7 @@ func (s *stripSecrets) String() string {
6072
}
6173

6274
// Now remove secrets from the generic representation of the message.
63-
strip(parsed, s.msg)
75+
s.strip(parsed, s.msg)
6476

6577
// Re-encoded the stripped representation and return that.
6678
b, err = json.Marshal(parsed)
@@ -70,7 +82,7 @@ func (s *stripSecrets) String() string {
7082
return string(b)
7183
}
7284

73-
func strip(parsed interface{}, msg interface{}) {
85+
func (s *stripSecrets) strip(parsed interface{}, msg interface{}) {
7486
protobufMsg, ok := msg.(descriptor.Message)
7587
if !ok {
7688
// Not a protobuf message, so we are done.
@@ -93,8 +105,7 @@ func strip(parsed interface{}, msg interface{}) {
93105
fields := md.GetField()
94106
if fields != nil {
95107
for _, field := range fields {
96-
ex, err := proto.GetExtension(field.Options, csi.E_CsiSecret)
97-
if err == nil && ex != nil && *ex.(*bool) {
108+
if s.isSecretField(field) {
98109
// Overwrite only if already set.
99110
if _, ok := parsedFields[field.GetName()]; ok {
100111
parsedFields[field.GetName()] = "***stripped***"
@@ -126,13 +137,41 @@ func strip(parsed interface{}, msg interface{}) {
126137
if slice, ok := entry.([]interface{}); ok {
127138
// Array of values, like VolumeCapabilities in CreateVolumeRequest.
128139
for _, entry := range slice {
129-
strip(entry, i)
140+
s.strip(entry, i)
130141
}
131142
} else {
132143
// Single value.
133-
strip(entry, i)
144+
s.strip(entry, i)
134145
}
135146
}
136147
}
137148
}
138149
}
150+
151+
// isCSI1Secret uses the csi.E_CsiSecret extension from CSI 1.0 to
152+
// determine whether a field contains secrets.
153+
func isCSI1Secret(field *protobuf.FieldDescriptorProto) bool {
154+
ex, err := proto.GetExtension(field.Options, e_CsiSecret)
155+
return err == nil && ex != nil && *ex.(*bool)
156+
}
157+
158+
// Copied from the CSI 1.0 spec (https://github.com/container-storage-interface/spec/blob/37e74064635d27c8e33537c863b37ccb1182d4f8/lib/go/csi/csi.pb.go#L4520-L4527)
159+
// to avoid a package dependency that would prevent usage of this package
160+
// in repos using an older version of the spec.
161+
//
162+
// Future revision of the CSI spec must not change this extensions, otherwise
163+
// they will break filtering in binaries based on the 1.0 version of the spec.
164+
var e_CsiSecret = &proto.ExtensionDesc{
165+
ExtendedType: (*protobufdescriptor.FieldOptions)(nil),
166+
ExtensionType: (*bool)(nil),
167+
Field: 1059,
168+
Name: "csi.v1.csi_secret",
169+
Tag: "varint,1059,opt,name=csi_secret,json=csiSecret",
170+
Filename: "github.com/container-storage-interface/spec/csi.proto",
171+
}
172+
173+
// isCSI03Secret relies on the naming convention in CSI <= 0.3
174+
// to determine whether a field contains secrets.
175+
func isCSI03Secret(field *protobuf.FieldDescriptorProto) bool {
176+
return strings.HasSuffix(field.GetName(), "_secrets")
177+
}

protosanitizer/protosanitizer_test.go

+106-42
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,54 @@ import (
2020
"fmt"
2121
"testing"
2222

23-
"github.com/container-storage-interface/spec/lib/go/csi"
23+
"github.com/golang/protobuf/proto"
24+
csi03 "github.com/kubernetes-csi/csi-lib-utils/protosanitizer/test/csi03"
25+
csi "github.com/kubernetes-csi/csi-lib-utils/protosanitizer/test/csi10"
2426
"github.com/kubernetes-csi/csi-lib-utils/protosanitizer/test/csitest"
2527
"github.com/stretchr/testify/assert"
2628
)
2729

2830
func TestStripSecrets(t *testing.T) {
2931
secretName := "secret-abc"
3032
secretValue := "123"
33+
34+
// CSI 0.3.0.
35+
createVolumeCSI03 := &csi03.CreateVolumeRequest{
36+
AccessibilityRequirements: &csi03.TopologyRequirement{
37+
Requisite: []*csi03.Topology{
38+
&csi03.Topology{
39+
Segments: map[string]string{
40+
"foo": "bar",
41+
"x": "y",
42+
},
43+
},
44+
&csi03.Topology{
45+
Segments: map[string]string{
46+
"a": "b",
47+
},
48+
},
49+
},
50+
},
51+
Name: "foo",
52+
VolumeCapabilities: []*csi03.VolumeCapability{
53+
&csi03.VolumeCapability{
54+
AccessType: &csi03.VolumeCapability_Mount{
55+
Mount: &csi03.VolumeCapability_MountVolume{
56+
FsType: "ext4",
57+
},
58+
},
59+
},
60+
},
61+
CapacityRange: &csi03.CapacityRange{
62+
RequiredBytes: 1024,
63+
},
64+
ControllerCreateSecrets: map[string]string{
65+
secretName: secretValue,
66+
"secret-xyz": "987",
67+
},
68+
}
69+
70+
// Current spec.
3171
createVolume := &csi.CreateVolumeRequest{
3272
AccessibilityRequirements: &csi.TopologyRequirement{
3373
Requisite: []*csi.Topology{
@@ -63,9 +103,50 @@ func TestStripSecrets(t *testing.T) {
63103
},
64104
}
65105

66-
cases := []struct {
106+
// Revised spec with more secret fields.
107+
createVolumeFuture := &csitest.CreateVolumeRequest{
108+
CapacityRange: &csitest.CapacityRange{
109+
RequiredBytes: 1024,
110+
},
111+
MaybeSecretMap: map[int64]*csitest.VolumeCapability{
112+
1: &csitest.VolumeCapability{ArraySecret: "aaa"},
113+
2: &csitest.VolumeCapability{ArraySecret: "bbb"},
114+
},
115+
Name: "foo",
116+
NewSecretInt: 42,
117+
Seecreets: map[string]string{
118+
secretName: secretValue,
119+
"secret-xyz": "987",
120+
},
121+
VolumeCapabilities: []*csitest.VolumeCapability{
122+
&csitest.VolumeCapability{
123+
AccessType: &csitest.VolumeCapability_Mount{
124+
Mount: &csitest.VolumeCapability_MountVolume{
125+
FsType: "ext4",
126+
},
127+
},
128+
ArraySecret: "knock knock",
129+
},
130+
&csitest.VolumeCapability{
131+
ArraySecret: "Who's there?",
132+
},
133+
},
134+
VolumeContentSource: &csitest.VolumeContentSource{
135+
Type: &csitest.VolumeContentSource_Volume{
136+
Volume: &csitest.VolumeContentSource_VolumeSource{
137+
VolumeId: "abc",
138+
OneofSecretField: "hello",
139+
},
140+
},
141+
NestedSecretField: "world",
142+
},
143+
}
144+
145+
type testcase struct {
67146
original, stripped interface{}
68-
}{
147+
}
148+
149+
cases := []testcase{
69150
{nil, "null"},
70151
{1, "1"},
71152
{"hello world", `"hello world"`},
@@ -98,44 +179,9 @@ func TestStripSecrets(t *testing.T) {
98179
AccessibilityRequirements: &csi.TopologyRequirement{},
99180
}, `{"accessibility_requirements":{},"capacity_range":{"limit_bytes":1024,"required_bytes":1024},"name":"test-volume","parameters":{"param1":"param1","param2":"param2"},"secrets":"***stripped***","volume_capabilities":[{"AccessType":{"Mount":{"fs_type":"ext4","mount_flags":["flag1","flag2","flag3"]}},"access_mode":{"mode":5}}],"volume_content_source":{"Type":null}}`},
100181
{createVolume, `{"accessibility_requirements":{"requisite":[{"segments":{"foo":"bar","x":"y"}},{"segments":{"a":"b"}}]},"capacity_range":{"required_bytes":1024},"name":"foo","secrets":"***stripped***","volume_capabilities":[{"AccessType":{"Mount":{"fs_type":"ext4"}}}]}`},
182+
{createVolumeCSI03, `{"accessibility_requirements":{"requisite":[{"segments":{"foo":"bar","x":"y"}},{"segments":{"a":"b"}}]},"capacity_range":{"required_bytes":1024},"controller_create_secrets":"***stripped***","name":"foo","volume_capabilities":[{"AccessType":{"Mount":{"fs_type":"ext4"}}}]}`},
101183
{&csitest.CreateVolumeRequest{}, `{}`},
102-
{&csitest.CreateVolumeRequest{
103-
CapacityRange: &csitest.CapacityRange{
104-
RequiredBytes: 1024,
105-
},
106-
MaybeSecretMap: map[int64]*csitest.VolumeCapability{
107-
1: &csitest.VolumeCapability{ArraySecret: "aaa"},
108-
2: &csitest.VolumeCapability{ArraySecret: "bbb"},
109-
},
110-
Name: "foo",
111-
NewSecretInt: 42,
112-
Seecreets: map[string]string{
113-
secretName: secretValue,
114-
"secret-xyz": "987",
115-
},
116-
VolumeCapabilities: []*csitest.VolumeCapability{
117-
&csitest.VolumeCapability{
118-
AccessType: &csitest.VolumeCapability_Mount{
119-
Mount: &csitest.VolumeCapability_MountVolume{
120-
FsType: "ext4",
121-
},
122-
},
123-
ArraySecret: "knock knock",
124-
},
125-
&csitest.VolumeCapability{
126-
ArraySecret: "Who's there?",
127-
},
128-
},
129-
VolumeContentSource: &csitest.VolumeContentSource{
130-
Type: &csitest.VolumeContentSource_Volume{
131-
Volume: &csitest.VolumeContentSource_VolumeSource{
132-
VolumeId: "abc",
133-
OneofSecretField: "hello",
134-
},
135-
},
136-
NestedSecretField: "world",
137-
},
138-
},
184+
{createVolumeFuture,
139185
// Secrets are *not* removed from all fields yet. This will have to be fixed one way or another
140186
// before the CSI spec can start using secrets there (currently it doesn't).
141187
// The test is still useful because it shows that also complicated fields get serialized.
@@ -144,11 +190,29 @@ func TestStripSecrets(t *testing.T) {
144190
},
145191
}
146192

193+
// Message from revised spec as received by a sidecar based on the current spec.
194+
// The XXX_unrecognized field contains secrets and must not get logged.
195+
unknownFields := &csi.CreateVolumeRequest{}
196+
data, err := proto.Marshal(createVolumeFuture)
197+
if assert.NoError(t, err, "marshall future message") &&
198+
assert.NoError(t, proto.Unmarshal(data, unknownFields), "unmarshal with unknown fields") {
199+
cases = append(cases, testcase{unknownFields,
200+
`{"capacity_range":{"required_bytes":1024},"name":"foo","secrets":"***stripped***","volume_capabilities":[{"AccessType":{"Mount":{"fs_type":"ext4"}}},{"AccessType":null}],"volume_content_source":{"Type":{"Volume":{"volume_id":"abc"}}}}`,
201+
})
202+
}
203+
147204
for _, c := range cases {
148205
before := fmt.Sprint(c.original)
149-
stripped := StripSecrets(c.original)
206+
var stripped fmt.Stringer
207+
if _, ok := c.original.(*csi03.CreateVolumeRequest); ok {
208+
stripped = StripSecretsCSI03(c.original)
209+
} else {
210+
stripped = StripSecrets(c.original)
211+
}
150212
if assert.Equal(t, c.stripped, fmt.Sprintf("%s", stripped), "unexpected result for fmt s of %s", c.original) {
151-
assert.Equal(t, c.stripped, fmt.Sprintf("%v", stripped), "unexpected result for fmt v of %s", c.original)
213+
if assert.Equal(t, c.stripped, fmt.Sprintf("%v", stripped), "unexpected result for fmt v of %s", c.original) {
214+
assert.Equal(t, c.stripped, fmt.Sprintf("%+v", stripped), "unexpected result for fmt +v of %s", c.original)
215+
}
152216
}
153217
assert.Equal(t, before, fmt.Sprint(c.original), "original value modified")
154218
}

0 commit comments

Comments
 (0)