Skip to content

Commit 9812d55

Browse files
committed
scrub struct before log
1 parent 42115fc commit 9812d55

File tree

3 files changed

+143
-12
lines changed

3 files changed

+143
-12
lines changed

components/scrubber/scrubber.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"reflect"
1111
"regexp"
1212
"strings"
13+
"unsafe"
1314

1415
"github.com/mitchellh/reflectwalk"
1516
)
@@ -48,6 +49,16 @@ type TrustedValue interface {
4849
IsTrustedValue()
4950
}
5051

52+
type TrustedValueWrap struct {
53+
Value any
54+
}
55+
56+
func (TrustedValueWrap) IsTrustedValue() {}
57+
58+
func (t TrustedValueWrap) MarshalJSON() ([]byte, error) {
59+
return json.Marshal(t.Value)
60+
}
61+
5162
// Scrubber defines the interface for a scrubber, which can sanitise various types of data.
5263
// The scrubbing process involves removing or replacing sensitive data to prevent it from being exposed.
5364
//
@@ -86,6 +97,8 @@ type Scrubber interface {
8697
// }
8798
//
8899
Struct(val any) error
100+
101+
DeepCopyStruct(val any) any
89102
}
90103

91104
// Default is the default scrubber consumers of this package should use
@@ -189,6 +202,129 @@ func (s *scrubberImpl) Struct(val any) error {
189202
return nil
190203
}
191204

205+
func (s *scrubberImpl) deepCopyStruct(fieldName string, src reflect.Value, scrubTag string) reflect.Value {
206+
if src.Kind() == reflect.Ptr && src.IsNil() {
207+
return reflect.New(src.Type()).Elem()
208+
}
209+
210+
if src.Kind() == reflect.String {
211+
dst := reflect.New(src.Type())
212+
var (
213+
setExplicitValue bool
214+
explicitValue string
215+
)
216+
switch scrubTag {
217+
case "ignore":
218+
return dst
219+
case "hash":
220+
setExplicitValue = true
221+
explicitValue = SanitiseHash(src.String())
222+
case "redact":
223+
setExplicitValue = true
224+
explicitValue = SanitiseRedact(src.String())
225+
}
226+
227+
if setExplicitValue {
228+
dst.Elem().SetString(explicitValue)
229+
} else {
230+
sanitisatiser := s.getSanitisatiser(fieldName)
231+
if sanitisatiser != nil {
232+
dst.Elem().SetString(sanitisatiser(src.String()))
233+
} else {
234+
dst.Elem().SetString(s.Value(src.String()))
235+
}
236+
}
237+
if !dst.CanInterface() {
238+
return dst
239+
}
240+
return dst.Elem()
241+
}
242+
243+
switch src.Kind() {
244+
245+
case reflect.Struct:
246+
dst := reflect.New(src.Type())
247+
t := src.Type()
248+
249+
for i := 0; i < t.NumField(); i++ {
250+
f := t.Field(i)
251+
srcValue := src.Field(i)
252+
dstValue := dst.Elem().Field(i)
253+
254+
if !srcValue.CanInterface() {
255+
dstValue = reflect.NewAt(dstValue.Type(), unsafe.Pointer(dstValue.UnsafeAddr())).Elem()
256+
257+
if !srcValue.CanAddr() {
258+
switch {
259+
case srcValue.CanInt():
260+
dstValue.SetInt(srcValue.Int())
261+
case srcValue.CanUint():
262+
dstValue.SetUint(srcValue.Uint())
263+
case srcValue.CanFloat():
264+
dstValue.SetFloat(srcValue.Float())
265+
case srcValue.CanComplex():
266+
dstValue.SetComplex(srcValue.Complex())
267+
case srcValue.Kind() == reflect.Bool:
268+
dstValue.SetBool(srcValue.Bool())
269+
}
270+
271+
continue
272+
}
273+
274+
srcValue = reflect.NewAt(srcValue.Type(), unsafe.Pointer(srcValue.UnsafeAddr())).Elem()
275+
}
276+
277+
tagValue := f.Tag.Get("scrub")
278+
copied := s.deepCopyStruct(f.Name, srcValue, tagValue)
279+
dstValue.Set(copied)
280+
}
281+
return dst.Elem()
282+
283+
case reflect.Map:
284+
dst := reflect.MakeMap(src.Type())
285+
keys := src.MapKeys()
286+
for i := 0; i < src.Len(); i++ {
287+
mValue := src.MapIndex(keys[i])
288+
dst.SetMapIndex(keys[i], s.deepCopyStruct(keys[i].String(), mValue, ""))
289+
}
290+
return dst
291+
292+
case reflect.Slice:
293+
dst := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
294+
for i := 0; i < src.Len(); i++ {
295+
dst.Index(i).Set(s.deepCopyStruct(fieldName, src.Index(i), ""))
296+
}
297+
return dst
298+
299+
case reflect.Array:
300+
if src.Len() == 0 {
301+
return src // can not access to src.Index(0)
302+
}
303+
304+
dst := reflect.New(src.Type()).Elem()
305+
for i := 0; i < src.Len(); i++ {
306+
dst.Index(i).Set(s.deepCopyStruct(fieldName, src.Index(i), ""))
307+
}
308+
return dst
309+
310+
case reflect.Ptr:
311+
dst := reflect.New(src.Elem().Type())
312+
copied := s.deepCopyStruct(fieldName, src.Elem(), scrubTag)
313+
dst.Elem().Set(copied)
314+
return dst
315+
316+
default:
317+
dst := reflect.New(src.Type())
318+
dst.Elem().Set(src)
319+
return dst.Elem()
320+
}
321+
}
322+
323+
// Struct implements Scrubber
324+
func (s *scrubberImpl) DeepCopyStruct(val any) any {
325+
return s.deepCopyStruct("", reflect.ValueOf(val), "").Interface()
326+
}
327+
192328
func (s *scrubberImpl) scrubJsonObject(val map[string]interface{}) error {
193329
// fix https://github.com/gitpod-io/security/issues/64
194330
name, _ := val["name"].(string)

components/ws-manager-mk2/controllers/workspace_controller.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"sigs.k8s.io/controller-runtime/pkg/log"
3030

3131
wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
32-
"github.com/gitpod-io/gitpod/components/scrubber"
3332
"github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/maintenance"
3433
config "github.com/gitpod-io/gitpod/ws-manager/api/config"
3534
workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
@@ -126,20 +125,13 @@ func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
126125
r.updateMetrics(ctx, &workspace)
127126
r.emitPhaseEvents(ctx, &workspace, oldStatus)
128127

129-
var scrubbedPodStatus *corev1.PodStatus
128+
var podStatus *corev1.PodStatus
130129
if len(workspacePods.Items) > 0 {
131-
scrubbedPodStatus = workspacePods.Items[0].Status.DeepCopy()
132-
if err = scrubber.Default.Struct(scrubbedPodStatus); err != nil {
133-
log.Error(err, "failed to scrub pod status")
134-
}
135-
}
136-
scrubbedStatus := workspace.Status.DeepCopy()
137-
if err = scrubber.Default.Struct(scrubbedStatus); err != nil {
138-
log.Error(err, "failed to scrub workspace status")
130+
podStatus = &workspacePods.Items[0].Status
139131
}
140132

141133
if !equality.Semantic.DeepDerivative(oldStatus, workspace.Status) {
142-
log.Info("updating workspace status", "status", scrubbedStatus, "podStatus", scrubbedPodStatus)
134+
log.Info("updating workspace status", "status", workspace.Status, "podStatus", podStatus)
143135
}
144136

145137
err = r.Status().Update(ctx, &workspace)

components/ws-manager-mk2/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
config "github.com/gitpod-io/gitpod/ws-manager/api/config"
4444
workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
4545

46+
"github.com/gitpod-io/gitpod/components/scrubber"
4647
"github.com/gitpod-io/gitpod/ws-manager-mk2/controllers"
4748
"github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/maintenance"
4849
imgproxy "github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/proxy"
@@ -77,7 +78,9 @@ func main() {
7778
flag.Parse()
7879

7980
log.Init(ServiceName, Version, jsonLog, verbose)
80-
baseLogger := logrusr.New(log.Log)
81+
baseLogger := logrusr.New(log.Log, logrusr.WithFormatter(func(i interface{}) interface{} {
82+
return &scrubber.TrustedValueWrap{Value: scrubber.Default.DeepCopyStruct(i)}
83+
}))
8184
ctrl.SetLogger(baseLogger)
8285
// Set the logger used by k8s (e.g. client-go).
8386
klog.SetLogger(baseLogger)

0 commit comments

Comments
 (0)