|
1 | 1 | package auditloganalyzer |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "fmt" |
| 5 | + "sort" |
| 6 | + "strings" |
| 7 | + "sync" |
| 8 | + |
4 | 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
5 | 10 | auditv1 "k8s.io/apiserver/pkg/apis/audit/v1" |
6 | 11 | "k8s.io/apiserver/pkg/authentication/serviceaccount" |
7 | | - "strings" |
8 | | - "sync" |
9 | 12 | ) |
10 | 13 |
|
| 14 | +type userApplyCount struct { |
| 15 | + User string |
| 16 | + Applies int |
| 17 | +} |
| 18 | + |
| 19 | +type numberOfAppliesWithExamples struct { |
| 20 | + numberOfApplies int |
| 21 | + users map[string]int |
| 22 | + firstAuditEventID string |
| 23 | + lastAuditEventID string |
| 24 | + firstOccurredAt metav1.Time |
| 25 | + lastOccurredAt metav1.Time |
| 26 | +} |
| 27 | + |
| 28 | +func (n numberOfAppliesWithExamples) toErrorString() string { |
| 29 | + appliesPerUser := []userApplyCount{} |
| 30 | + for k, v := range n.users { |
| 31 | + appliesPerUser = append(appliesPerUser, userApplyCount{k, v}) |
| 32 | + } |
| 33 | + sort.Slice(appliesPerUser, func(i, j int) bool { |
| 34 | + return appliesPerUser[i].Applies > appliesPerUser[j].Applies |
| 35 | + }) |
| 36 | + topUsernames := []string{} |
| 37 | + maxUsernames := 5 |
| 38 | + if len(appliesPerUser) < maxUsernames { |
| 39 | + maxUsernames = len(appliesPerUser) |
| 40 | + } |
| 41 | + for i := 0; i < maxUsernames; i++ { |
| 42 | + topUsernames = append(topUsernames, appliesPerUser[i].User) |
| 43 | + } |
| 44 | + |
| 45 | + return fmt.Sprintf(` |
| 46 | +Time: started %s, finished at %s |
| 47 | +Top 5 usernames: %s |
| 48 | +Audit Log IDs: %s ... %s |
| 49 | +`, |
| 50 | + n.firstOccurredAt, |
| 51 | + n.lastOccurredAt, |
| 52 | + strings.Join(topUsernames, ", "), |
| 53 | + n.firstAuditEventID, |
| 54 | + n.lastAuditEventID, |
| 55 | + ) |
| 56 | +} |
| 57 | + |
11 | 58 | type excessiveApplies struct { |
12 | 59 | lock sync.Mutex |
13 | 60 | namespacesToUserToNumberOfApplies map[string]map[string]int |
| 61 | + resourcesToNumberOfApplies map[string]numberOfAppliesWithExamples |
14 | 62 | } |
15 | 63 |
|
16 | 64 | func CheckForExcessiveApplies() *excessiveApplies { |
17 | 65 | return &excessiveApplies{ |
18 | 66 | namespacesToUserToNumberOfApplies: map[string]map[string]int{}, |
| 67 | + resourcesToNumberOfApplies: map[string]numberOfAppliesWithExamples{}, |
19 | 68 | } |
20 | 69 | } |
21 | 70 |
|
@@ -43,4 +92,36 @@ func (s *excessiveApplies) HandleAuditLogEvent(auditEvent *auditv1.Event, beginn |
43 | 92 | } |
44 | 93 | users[auditEvent.User.Username] = users[auditEvent.User.Username] + 1 |
45 | 94 | s.namespacesToUserToNumberOfApplies[nsName] = users |
| 95 | + |
| 96 | + obj := auditEvent.ObjectRef |
| 97 | + if obj == nil { |
| 98 | + return |
| 99 | + } |
| 100 | + resource := fmt.Sprintf("%s/%s", obj.Resource, obj.Name) |
| 101 | + if obj.Namespace != "" { |
| 102 | + resource = fmt.Sprintf("%s -n %s", resource, obj.Namespace) |
| 103 | + } |
| 104 | + if obj.APIGroup != "" { |
| 105 | + resource = fmt.Sprintf("%s.%s", obj.APIGroup, resource) |
| 106 | + } |
| 107 | + objApplies, ok := s.resourcesToNumberOfApplies[resource] |
| 108 | + if !ok { |
| 109 | + objApplies = numberOfAppliesWithExamples{ |
| 110 | + numberOfApplies: 0, |
| 111 | + firstAuditEventID: string(auditEvent.AuditID), |
| 112 | + lastAuditEventID: string(auditEvent.AuditID), |
| 113 | + users: map[string]int{}, |
| 114 | + firstOccurredAt: metav1.Time(auditEvent.RequestReceivedTimestamp), |
| 115 | + lastOccurredAt: metav1.Time(auditEvent.RequestReceivedTimestamp), |
| 116 | + } |
| 117 | + } |
| 118 | + objApplies.numberOfApplies += 1 |
| 119 | + objApplies.lastAuditEventID = string(auditEvent.AuditID) |
| 120 | + objApplies.lastOccurredAt = metav1.Time(auditEvent.RequestReceivedTimestamp) |
| 121 | + userApplies, ok := objApplies.users[auditEvent.User.Username] |
| 122 | + if !ok { |
| 123 | + userApplies = 0 |
| 124 | + } |
| 125 | + objApplies.users[auditEvent.User.Username] = userApplies + 1 |
| 126 | + s.resourcesToNumberOfApplies[resource] = objApplies |
46 | 127 | } |
0 commit comments