audit.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. "reflect"
  19. "sort"
  20. "strings"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. "k8s.io/apimachinery/pkg/runtime/schema"
  23. "k8s.io/apimachinery/pkg/types"
  24. "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
  25. auditinternal "k8s.io/apiserver/pkg/apis/audit"
  26. "k8s.io/apiserver/pkg/audit"
  27. )
  28. // AuditEvent is a simplified representation of an audit event for testing purposes
  29. type AuditEvent struct {
  30. ID types.UID
  31. Level auditinternal.Level
  32. Stage auditinternal.Stage
  33. RequestURI string
  34. Verb string
  35. Code int32
  36. User string
  37. ImpersonatedUser string
  38. ImpersonatedGroups string
  39. Resource string
  40. Namespace string
  41. RequestObject bool
  42. ResponseObject bool
  43. AuthorizeDecision string
  44. // The Check functions in this package takes ownerships of these maps. You should
  45. // not reference these maps after calling the Check functions.
  46. AdmissionWebhookMutationAnnotations map[string]string
  47. AdmissionWebhookPatchAnnotations map[string]string
  48. }
  49. // MissingEventsReport provides an analysis if any events are missing
  50. type MissingEventsReport struct {
  51. FirstEventChecked *auditinternal.Event
  52. LastEventChecked *auditinternal.Event
  53. NumEventsChecked int
  54. MissingEvents []AuditEvent
  55. }
  56. // String returns a human readable string representation of the report
  57. func (m *MissingEventsReport) String() string {
  58. return fmt.Sprintf(`missing %d events
  59. - first event checked: %#v
  60. - last event checked: %#v
  61. - number of events checked: %d
  62. - missing events: %#v`, len(m.MissingEvents), m.FirstEventChecked, m.LastEventChecked, m.NumEventsChecked, m.MissingEvents)
  63. }
  64. // CheckAuditLines searches the audit log for the expected audit lines.
  65. func CheckAuditLines(stream io.Reader, expected []AuditEvent, version schema.GroupVersion) (missingReport *MissingEventsReport, err error) {
  66. expectations := newAuditEventTracker(expected)
  67. scanner := bufio.NewScanner(stream)
  68. missingReport = &MissingEventsReport{
  69. MissingEvents: expected,
  70. }
  71. var i int
  72. for i = 0; scanner.Scan(); i++ {
  73. line := scanner.Text()
  74. e := &auditinternal.Event{}
  75. decoder := audit.Codecs.UniversalDecoder(version)
  76. if err := runtime.DecodeInto(decoder, []byte(line), e); err != nil {
  77. return missingReport, fmt.Errorf("failed decoding buf: %s, apiVersion: %s", line, version)
  78. }
  79. if i == 0 {
  80. missingReport.FirstEventChecked = e
  81. }
  82. missingReport.LastEventChecked = e
  83. event, err := testEventFromInternal(e)
  84. if err != nil {
  85. return missingReport, err
  86. }
  87. expectations.Mark(event)
  88. }
  89. if err := scanner.Err(); err != nil {
  90. return missingReport, err
  91. }
  92. missingReport.MissingEvents = expectations.Missing()
  93. missingReport.NumEventsChecked = i
  94. return missingReport, nil
  95. }
  96. // CheckAuditList searches an audit event list for the expected audit events.
  97. func CheckAuditList(el auditinternal.EventList, expected []AuditEvent) (missing []AuditEvent, err error) {
  98. expectations := newAuditEventTracker(expected)
  99. for _, e := range el.Items {
  100. event, err := testEventFromInternal(&e)
  101. if err != nil {
  102. return expected, err
  103. }
  104. expectations.Mark(event)
  105. }
  106. return expectations.Missing(), nil
  107. }
  108. // CheckForDuplicates checks a list for duplicate events
  109. func CheckForDuplicates(el auditinternal.EventList) (auditinternal.EventList, error) {
  110. // existingEvents holds a slice of audit events that have been seen
  111. existingEvents := []AuditEvent{}
  112. duplicates := auditinternal.EventList{}
  113. for _, e := range el.Items {
  114. event, err := testEventFromInternal(&e)
  115. if err != nil {
  116. return duplicates, err
  117. }
  118. event.ID = e.AuditID
  119. for _, existing := range existingEvents {
  120. if reflect.DeepEqual(existing, event) {
  121. duplicates.Items = append(duplicates.Items, e)
  122. continue
  123. }
  124. }
  125. existingEvents = append(existingEvents, event)
  126. }
  127. var err error
  128. if len(duplicates.Items) > 0 {
  129. err = fmt.Errorf("failed duplicate check")
  130. }
  131. return duplicates, err
  132. }
  133. // testEventFromInternal takes an internal audit event and returns a test event
  134. func testEventFromInternal(e *auditinternal.Event) (AuditEvent, error) {
  135. event := AuditEvent{
  136. Level: e.Level,
  137. Stage: e.Stage,
  138. RequestURI: e.RequestURI,
  139. Verb: e.Verb,
  140. User: e.User.Username,
  141. }
  142. if e.ObjectRef != nil {
  143. event.Namespace = e.ObjectRef.Namespace
  144. event.Resource = e.ObjectRef.Resource
  145. }
  146. if e.ResponseStatus != nil {
  147. event.Code = e.ResponseStatus.Code
  148. }
  149. if e.ResponseObject != nil {
  150. event.ResponseObject = true
  151. }
  152. if e.RequestObject != nil {
  153. event.RequestObject = true
  154. }
  155. if e.ImpersonatedUser != nil {
  156. event.ImpersonatedUser = e.ImpersonatedUser.Username
  157. sort.Strings(e.ImpersonatedUser.Groups)
  158. event.ImpersonatedGroups = strings.Join(e.ImpersonatedUser.Groups, ",")
  159. }
  160. event.AuthorizeDecision = e.Annotations["authorization.k8s.io/decision"]
  161. for k, v := range e.Annotations {
  162. if strings.HasPrefix(k, mutating.PatchAuditAnnotationPrefix) {
  163. if event.AdmissionWebhookPatchAnnotations == nil {
  164. event.AdmissionWebhookPatchAnnotations = map[string]string{}
  165. }
  166. event.AdmissionWebhookPatchAnnotations[k] = v
  167. } else if strings.HasPrefix(k, mutating.MutationAuditAnnotationPrefix) {
  168. if event.AdmissionWebhookMutationAnnotations == nil {
  169. event.AdmissionWebhookMutationAnnotations = map[string]string{}
  170. }
  171. event.AdmissionWebhookMutationAnnotations[k] = v
  172. }
  173. }
  174. return event, nil
  175. }
  176. // auditEvent is a private wrapper on top of AuditEvent used by auditEventTracker
  177. type auditEvent struct {
  178. event AuditEvent
  179. found bool
  180. }
  181. // auditEventTracker keeps track of AuditEvent expectations and marks matching events as found
  182. type auditEventTracker struct {
  183. events []*auditEvent
  184. }
  185. // newAuditEventTracker creates a tracker that tracks whether expect events are found
  186. func newAuditEventTracker(expected []AuditEvent) *auditEventTracker {
  187. expectations := &auditEventTracker{events: []*auditEvent{}}
  188. for _, event := range expected {
  189. // we copy the references to the maps in event
  190. expectations.events = append(expectations.events, &auditEvent{event: event, found: false})
  191. }
  192. return expectations
  193. }
  194. // Mark marks the given event as found if it's expected
  195. func (t *auditEventTracker) Mark(event AuditEvent) {
  196. for _, e := range t.events {
  197. if reflect.DeepEqual(e.event, event) {
  198. e.found = true
  199. }
  200. }
  201. }
  202. // Missing reports events that are expected but not found
  203. func (t *auditEventTracker) Missing() []AuditEvent {
  204. var missing []AuditEvent
  205. for _, e := range t.events {
  206. if !e.found {
  207. missing = append(missing, e.event)
  208. }
  209. }
  210. return missing
  211. }