scrub.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package log
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "sync/atomic"
  7. hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
  8. )
  9. // This package scrubs objects of potentially sensitive information to pass to logging
  10. type genMap = map[string]interface{}
  11. type scrubberFunc func(genMap) error
  12. const _scrubbedReplacement = "<scrubbed>"
  13. var (
  14. ErrUnknownType = errors.New("encoded object is of unknown type")
  15. // case sensitive keywords, so "env" is not a substring on "Environment"
  16. _scrubKeywords = [][]byte{[]byte("env"), []byte("Environment")}
  17. _scrub int32
  18. )
  19. // SetScrubbing enables scrubbing
  20. func SetScrubbing(enable bool) {
  21. v := int32(0) // cant convert from bool to int32 directly
  22. if enable {
  23. v = 1
  24. }
  25. atomic.StoreInt32(&_scrub, v)
  26. }
  27. // IsScrubbingEnabled checks if scrubbing is enabled
  28. func IsScrubbingEnabled() bool {
  29. v := atomic.LoadInt32(&_scrub)
  30. return v != 0
  31. }
  32. // ScrubProcessParameters scrubs HCS Create Process requests with config parameters of
  33. // type internal/hcs/schema2.ScrubProcessParameters (aka hcsshema.ScrubProcessParameters)
  34. func ScrubProcessParameters(s string) (string, error) {
  35. // todo: deal with v1 ProcessConfig
  36. b := []byte(s)
  37. if !IsScrubbingEnabled() || !hasKeywords(b) || !json.Valid(b) {
  38. return s, nil
  39. }
  40. pp := hcsschema.ProcessParameters{}
  41. if err := json.Unmarshal(b, &pp); err != nil {
  42. return "", err
  43. }
  44. pp.Environment = map[string]string{_scrubbedReplacement: _scrubbedReplacement}
  45. b, err := encodeBuffer(bytes.NewBuffer(b[:0]), pp)
  46. if err != nil {
  47. return "", err
  48. }
  49. return string(b), nil
  50. }
  51. // ScrubBridgeCreate scrubs requests sent over the bridge of type
  52. // internal/gcs/protocol.containerCreate wrapping an internal/hcsoci.linuxHostedSystem
  53. func ScrubBridgeCreate(b []byte) ([]byte, error) {
  54. return scrubBytes(b, scrubBridgeCreate)
  55. }
  56. func scrubBridgeCreate(m genMap) error {
  57. if !isRequestBase(m) {
  58. return ErrUnknownType
  59. }
  60. if ss, ok := m["ContainerConfig"]; ok {
  61. // ContainerConfig is a json encoded struct passed as a regular string field
  62. s, ok := ss.(string)
  63. if !ok {
  64. return ErrUnknownType
  65. }
  66. b, err := scrubBytes([]byte(s), scrubLinuxHostedSystem)
  67. if err != nil {
  68. return err
  69. }
  70. m["ContainerConfig"] = string(b)
  71. return nil
  72. }
  73. return ErrUnknownType
  74. }
  75. func scrubLinuxHostedSystem(m genMap) error {
  76. if m, ok := index(m, "OciSpecification"); ok {
  77. if _, ok := m["annotations"]; ok {
  78. m["annotations"] = map[string]string{_scrubbedReplacement: _scrubbedReplacement}
  79. }
  80. if m, ok := index(m, "process"); ok {
  81. if _, ok := m["env"]; ok {
  82. m["env"] = []string{_scrubbedReplacement}
  83. return nil
  84. }
  85. }
  86. }
  87. return ErrUnknownType
  88. }
  89. // ScrubBridgeExecProcess scrubs requests sent over the bridge of type
  90. // internal/gcs/protocol.containerExecuteProcess
  91. func ScrubBridgeExecProcess(b []byte) ([]byte, error) {
  92. return scrubBytes(b, scrubExecuteProcess)
  93. }
  94. func scrubExecuteProcess(m genMap) error {
  95. if !isRequestBase(m) {
  96. return ErrUnknownType
  97. }
  98. if m, ok := index(m, "Settings"); ok {
  99. if ss, ok := m["ProcessParameters"]; ok {
  100. // ProcessParameters is a json encoded struct passed as a regular sting field
  101. s, ok := ss.(string)
  102. if !ok {
  103. return ErrUnknownType
  104. }
  105. s, err := ScrubProcessParameters(s)
  106. if err != nil {
  107. return err
  108. }
  109. m["ProcessParameters"] = s
  110. return nil
  111. }
  112. }
  113. return ErrUnknownType
  114. }
  115. func scrubBytes(b []byte, scrub scrubberFunc) ([]byte, error) {
  116. if !IsScrubbingEnabled() || !hasKeywords(b) || !json.Valid(b) {
  117. return b, nil
  118. }
  119. m := make(genMap)
  120. if err := json.Unmarshal(b, &m); err != nil {
  121. return nil, err
  122. }
  123. // could use regexp, but if the env strings contain braces, the regexp fails
  124. // parsing into individual structs would require access to private structs
  125. if err := scrub(m); err != nil {
  126. return nil, err
  127. }
  128. b, err := encode(m)
  129. if err != nil {
  130. return nil, err
  131. }
  132. return b, nil
  133. }
  134. func isRequestBase(m genMap) bool {
  135. // neither of these are (currently) `omitempty`
  136. _, a := m["ActivityId"]
  137. _, c := m["ContainerId"]
  138. return a && c
  139. }
  140. // combination `m, ok := m[s]` and `m, ok := m.(genMap)`
  141. func index(m genMap, s string) (genMap, bool) {
  142. if m, ok := m[s]; ok {
  143. mm, ok := m.(genMap)
  144. return mm, ok
  145. }
  146. return m, false
  147. }
  148. func hasKeywords(b []byte) bool {
  149. for _, bb := range _scrubKeywords {
  150. if bytes.Contains(b, bb) {
  151. return true
  152. }
  153. }
  154. return false
  155. }