66 "github.com/expr-lang/expr/vm"
77 "github.com/panjf2000/gnet"
88 "github.com/spf13/viper"
9+ "gopkg.in/natefinch/lumberjack.v2"
10+ "log"
911 "log/slog"
1012 "os"
1113 "time"
@@ -34,11 +36,13 @@ type Response struct {
3436 MountRunningPluginVersion string `json:"mount_running_plugin_version"`
3537 MountClass string `json:"mount_class"`
3638 Data struct {
37- CreatedTime string `json:"created_time"`
38- CustomMetadata map [string ]string `json:"custom_metadata"`
39- DeletionTime string `json:"deletion_time"`
40- Destroyed bool `json:"destroyed"`
41- Version int `json:"version"`
39+ CasRequired bool `json:"cas_required"`
40+ CreatedTime string `json:"created_time"`
41+ CurrentVersion int `json:"current_version"`
42+ DeleteVersionAfter string `json:"delete_version_after"`
43+ MaxVersions int `json:"max_versions"`
44+ OldestVersion int `json:"oldest_version"`
45+ UpdatedTime string `json:"updated_time"`
4246 } `json:"data"`
4347}
4448
@@ -74,46 +78,65 @@ type CompiledRule struct {
7478 Program * vm.Program
7579}
7680
81+ type RuleGroup struct {
82+ Name string
83+ CompiledRules []CompiledRule
84+ Logger * log.Logger
85+ }
86+
87+ type RuleGroupConfig struct {
88+ Name string `mapstructure:"name"`
89+ Rules []string `mapstructure:"rules"`
90+ LogFile LogFileConfig `mapstructure:"log_file"`
91+ }
92+
93+ type LogFileConfig struct {
94+ FilePath string `mapstructure:"file_path"`
95+ MaxSize int `mapstructure:"max_size"`
96+ MaxBackups int `mapstructure:"max_backups"`
97+ MaxAge int `mapstructure:"max_age"`
98+ Compress bool `mapstructure:"compress"`
99+ }
100+
77101type AuditServer struct {
78102 * gnet.EventServer
79- logger * slog.Logger
80- compiledExpr []CompiledRule
103+ logger * slog.Logger
104+ ruleGroups []RuleGroup
81105}
82106
83107func (as * AuditServer ) React (frame []byte , c gnet.Conn ) (out []byte , action gnet.Action ) {
84- // Parse the audit log
108+ // Parse the audit log for rule evaluation
85109 var auditLog AuditLog
86110 err := json .Unmarshal (frame , & auditLog )
87111 if err != nil {
112+ // Log the error using the service logger
88113 as .logger .Error ("Error parsing audit log" , "error" , err )
89114 return nil , gnet .Close
90115 }
91116
92- // Apply the rules using expr
93- if as .shouldLog (& auditLog ) {
94- logAttrs := []any {
95- "operation" , auditLog .Request .Operation ,
96- "path" , auditLog .Request .Path ,
97- "user" , auditLog .Auth .DisplayName ,
98- "client_id" , auditLog .Request .ClientID ,
99- "remote_addr" , auditLog .Request .RemoteAddress ,
100- "time" , auditLog .Time ,
117+ // Check each rule group
118+ for _ , rg := range as .ruleGroups {
119+ if rg .shouldLog (& auditLog ) {
120+ as .logger .Info ("Matched rule group" , "group" , rg .Name )
121+ // Write the raw frame directly to the group's log file
122+ rg .Logger .Print (string (frame ))
123+ // Uncomment the following line to prevent logging to multiple groups
124+ // break
101125 }
102- as .logger .Info ("Received audit log" , logAttrs ... )
103126 }
104127
105128 return nil , gnet .Close
106129}
107- func ( as * AuditServer ) shouldLog ( auditLog * AuditLog ) bool {
108- // If no rules are defined, log all audit logs
109- if len (as . compiledExpr ) == 0 {
130+
131+ func ( rg * RuleGroup ) shouldLog ( auditLog * AuditLog ) bool {
132+ if len (rg . CompiledRules ) == 0 {
110133 return true
111134 }
112135
113- for _ , compiledRule := range as . compiledExpr {
136+ for _ , compiledRule := range rg . CompiledRules {
114137 output , err := expr .Run (compiledRule .Program , auditLog )
115138 if err != nil {
116- as . logger . Error ( "Error evaluating rule" , "error" , err )
139+ // Optionally log the error
117140 continue
118141 }
119142 if match , ok := output .(bool ); ok && match {
@@ -128,24 +151,47 @@ func New(logger *slog.Logger) *AuditServer {
128151 logger = slog .New (slog .NewJSONHandler (os .Stdout , & slog.HandlerOptions {Level : slog .LevelInfo }))
129152 }
130153
131- // Load and compile rules from configuration
132- var ruleStrings []string
133- if err := viper .UnmarshalKey ("rules " , & ruleStrings ); err != nil {
134- logger .Error ("Failed to load rules " , "error" , err )
154+ // Load rule groups from configuration
155+ var ruleGroupConfigs []RuleGroupConfig
156+ if err := viper .UnmarshalKey ("rule_groups " , & ruleGroupConfigs ); err != nil {
157+ logger .Error ("Failed to load rule groups " , "error" , err )
135158 }
136159
137- var compiledExpr []CompiledRule
138- for _ , ruleStr := range ruleStrings {
139- program , err := expr .Compile (ruleStr , expr .Env (& AuditLog {}))
140- if err != nil {
141- logger .Error ("Failed to compile rule" , "rule" , ruleStr , "error" , err )
142- continue
160+ var ruleGroups []RuleGroup
161+ for _ , rgConfig := range ruleGroupConfigs {
162+ // Compile rules
163+ var compiledRules []CompiledRule
164+ for _ , ruleStr := range rgConfig .Rules {
165+ program , err := expr .Compile (ruleStr , expr .Env (& AuditLog {}))
166+ if err != nil {
167+ logger .Error ("Failed to compile rule" , "rule" , ruleStr , "error" , err )
168+ continue
169+ }
170+ compiledRules = append (compiledRules , CompiledRule {Program : program })
171+ }
172+
173+ // Configure logger for the rule group
174+ logFileConfig := rgConfig .LogFile
175+ logFile := & lumberjack.Logger {
176+ Filename : logFileConfig .FilePath ,
177+ MaxSize : logFileConfig .MaxSize ,
178+ MaxBackups : logFileConfig .MaxBackups ,
179+ MaxAge : logFileConfig .MaxAge ,
180+ Compress : logFileConfig .Compress ,
181+ }
182+ groupLogger := log .New (logFile , "" , 0 )
183+
184+ // Create RuleGroup
185+ ruleGroup := RuleGroup {
186+ Name : rgConfig .Name ,
187+ CompiledRules : compiledRules ,
188+ Logger : groupLogger ,
143189 }
144- compiledExpr = append (compiledExpr , CompiledRule { Program : program } )
190+ ruleGroups = append (ruleGroups , ruleGroup )
145191 }
146192
147193 return & AuditServer {
148- logger : logger ,
149- compiledExpr : compiledExpr ,
194+ logger : logger ,
195+ ruleGroups : ruleGroups ,
150196 }
151197}
0 commit comments