Skip to content

Commit 67f76c8

Browse files
committed
protosanitizer: variant of StripSecrets for CSI <= 0.3.0
The variant for the older CSI spec is relying on the naming of the secret fields to determine which ones need to be removed. As all field names in the older spec are known and fixed, that's good enough.
1 parent 34a5bd5 commit 67f76c8

File tree

3 files changed

+5066
-8
lines changed

3 files changed

+5066
-8
lines changed

protosanitizer/protosanitizer.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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,26 @@ 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, csi.E_CsiSecret)
155+
return err == nil && ex != nil && *ex.(*bool)
156+
}
157+
158+
// isCSI03Secret relies on the naming convention in CSI <= 0.3
159+
// to determine whether a field contains secrets.
160+
func isCSI03Secret(field *protobuf.FieldDescriptorProto) bool {
161+
return strings.HasSuffix(field.GetName(), "_secrets")
162+
}

protosanitizer/protosanitizer_test.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/container-storage-interface/spec/lib/go/csi"
2424
"github.com/golang/protobuf/proto"
25+
csi03 "github.com/kubernetes-csi/csi-lib-utils/protosanitizer/test/csi03"
2526
"github.com/kubernetes-csi/csi-lib-utils/protosanitizer/test/csitest"
2627
"github.com/stretchr/testify/assert"
2728
)
@@ -30,6 +31,42 @@ func TestStripSecrets(t *testing.T) {
3031
secretName := "secret-abc"
3132
secretValue := "123"
3233

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+
3370
// Current spec.
3471
createVolume := &csi.CreateVolumeRequest{
3572
AccessibilityRequirements: &csi.TopologyRequirement{
@@ -142,6 +179,7 @@ func TestStripSecrets(t *testing.T) {
142179
AccessibilityRequirements: &csi.TopologyRequirement{},
143180
}, `{"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}}`},
144181
{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"}}}]}`},
145183
{&csitest.CreateVolumeRequest{}, `{}`},
146184
{createVolumeFuture,
147185
// Secrets are *not* removed from all fields yet. This will have to be fixed one way or another
@@ -165,7 +203,12 @@ func TestStripSecrets(t *testing.T) {
165203

166204
for _, c := range cases {
167205
before := fmt.Sprint(c.original)
168-
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+
}
169212
if assert.Equal(t, c.stripped, fmt.Sprintf("%s", stripped), "unexpected result for fmt s of %s", c.original) {
170213
assert.Equal(t, c.stripped, fmt.Sprintf("%v", stripped), "unexpected result for fmt v of %s", c.original)
171214
}

0 commit comments

Comments
 (0)