audit.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package utils
  14. import (
  15. "bufio"
  16. "fmt"
  17. "io"
  18. "sort"
  19. "strings"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/runtime/schema"
  22. "k8s.io/apimachinery/pkg/types"
  23. auditinternal "k8s.io/apiserver/pkg/apis/audit"
  24. "k8s.io/apiserver/pkg/audit"
  25. )
  26. // AuditEvent is a simplified representation of an audit event for testing purposes
  27. type AuditEvent struct {
  28. ID types.UID
  29. Level auditinternal.Level
  30. Stage auditinternal.Stage
  31. RequestURI string
  32. Verb string
  33. Code int32
  34. User string
  35. ImpersonatedUser string
  36. ImpersonatedGroups string
  37. Resource string
  38. Namespace string
  39. RequestObject bool
  40. ResponseObject bool
  41. AuthorizeDecision string
  42. }
  43. // MissingEventsReport provides an analysis if any events are missing
  44. type MissingEventsReport struct {
  45. FirstEventChecked *auditinternal.Event
  46. LastEventChecked *auditinternal.Event
  47. NumEventsChecked int
  48. MissingEvents []AuditEvent
  49. }
  50. // String returns a human readable string representation of the report
  51. func (m *MissingEventsReport) String() string {
  52. return fmt.Sprintf(`missing %d events
  53. - first event checked: %#v
  54. - last event checked: %#v
  55. - number of events checked: %d
  56. - missing events: %#v`, len(m.MissingEvents), m.FirstEventChecked, m.LastEventChecked, m.NumEventsChecked, m.MissingEvents)
  57. }
  58. // CheckAuditLines searches the audit log for the expected audit lines.
  59. func CheckAuditLines(stream io.Reader, expected []AuditEvent, version schema.GroupVersion) (missingReport *MissingEventsReport, err error) {
  60. expectations := buildEventExpectations(expected)
  61. scanner := bufio.NewScanner(stream)
  62. missingReport = &MissingEventsReport{
  63. MissingEvents: expected,
  64. }
  65. var i int
  66. for i = 0; scanner.Scan(); i++ {
  67. line := scanner.Text()
  68. e := &auditinternal.Event{}
  69. decoder := audit.Codecs.UniversalDecoder(version)
  70. if err := runtime.DecodeInto(decoder, []byte(line), e); err != nil {
  71. return missingReport, fmt.Errorf("failed decoding buf: %s, apiVersion: %s", line, version)
  72. }
  73. if i == 0 {
  74. missingReport.FirstEventChecked = e
  75. }
  76. missingReport.LastEventChecked = e
  77. event, err := testEventFromInternal(e)
  78. if err != nil {
  79. return missingReport, err
  80. }
  81. // If the event was expected, mark it as found.
  82. if _, found := expectations[event]; found {
  83. expectations[event] = true
  84. }
  85. }
  86. if err := scanner.Err(); err != nil {
  87. return missingReport, err
  88. }
  89. missingEvents := findMissing(expectations)
  90. missingReport.MissingEvents = missingEvents
  91. missingReport.NumEventsChecked = i
  92. return missingReport, nil
  93. }
  94. // CheckAuditList searches an audit event list for the expected audit events.
  95. func CheckAuditList(el auditinternal.EventList, expected []AuditEvent) (missing []AuditEvent, err error) {
  96. expectations := buildEventExpectations(expected)
  97. for _, e := range el.Items {
  98. event, err := testEventFromInternal(&e)
  99. if err != nil {
  100. return expected, err
  101. }
  102. // If the event was expected, mark it as found.
  103. if _, found := expectations[event]; found {
  104. expectations[event] = true
  105. }
  106. }
  107. missing = findMissing(expectations)
  108. return missing, nil
  109. }
  110. // CheckForDuplicates checks a list for duplicate events
  111. func CheckForDuplicates(el auditinternal.EventList) (auditinternal.EventList, error) {
  112. // eventMap holds a map of audit events with just a nil value
  113. eventMap := map[AuditEvent]*bool{}
  114. duplicates := auditinternal.EventList{}
  115. var err error
  116. for _, e := range el.Items {
  117. event, err := testEventFromInternal(&e)
  118. if err != nil {
  119. return duplicates, err
  120. }
  121. event.ID = e.AuditID
  122. if _, ok := eventMap[event]; ok {
  123. duplicates.Items = append(duplicates.Items, e)
  124. err = fmt.Errorf("failed duplicate check")
  125. continue
  126. }
  127. eventMap[event] = nil
  128. }
  129. return duplicates, err
  130. }
  131. // buildEventExpectations creates a bool map out of a list of audit events
  132. func buildEventExpectations(expected []AuditEvent) map[AuditEvent]bool {
  133. expectations := map[AuditEvent]bool{}
  134. for _, event := range expected {
  135. expectations[event] = false
  136. }
  137. return expectations
  138. }
  139. // testEventFromInternal takes an internal audit event and returns a test event
  140. func testEventFromInternal(e *auditinternal.Event) (AuditEvent, error) {
  141. event := AuditEvent{
  142. Level: e.Level,
  143. Stage: e.Stage,
  144. RequestURI: e.RequestURI,
  145. Verb: e.Verb,
  146. User: e.User.Username,
  147. }
  148. if e.ObjectRef != nil {
  149. event.Namespace = e.ObjectRef.Namespace
  150. event.Resource = e.ObjectRef.Resource
  151. }
  152. if e.ResponseStatus != nil {
  153. event.Code = e.ResponseStatus.Code
  154. }
  155. if e.ResponseObject != nil {
  156. event.ResponseObject = true
  157. }
  158. if e.RequestObject != nil {
  159. event.RequestObject = true
  160. }
  161. if e.ImpersonatedUser != nil {
  162. event.ImpersonatedUser = e.ImpersonatedUser.Username
  163. sort.Strings(e.ImpersonatedUser.Groups)
  164. event.ImpersonatedGroups = strings.Join(e.ImpersonatedUser.Groups, ",")
  165. }
  166. event.AuthorizeDecision = e.Annotations["authorization.k8s.io/decision"]
  167. return event, nil
  168. }
  169. // findMissing checks for false values in the expectations map and returns them as a list
  170. func findMissing(expectations map[AuditEvent]bool) []AuditEvent {
  171. var missing []AuditEvent
  172. for event, found := range expectations {
  173. if !found {
  174. missing = append(missing, event)
  175. }
  176. }
  177. return missing
  178. }