Skip to content

Commit af9beda

Browse files
committed
improve the logging and allow to have different log groups based on rule matching
1 parent 545946b commit af9beda

3 files changed

Lines changed: 87 additions & 38 deletions

File tree

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func initConfig() {
109109
os.Exit(1)
110110
}
111111

112-
if !viper.IsSet("rules") || len(viper.GetStringSlice("rules")) == 0 {
112+
if !viper.IsSet("rule_groups") || len(viper.GetStringSlice("rule_groups")) == 0 {
113113
logger.Info("No rules defined in configuration; all audit logs will be printed")
114114
}
115115
}

configs/docker/docker-compose.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ services:
1414
vault-audit-filter_auditserver:
1515
image: ncode/vault-audit-filter:dev
1616
container_name: vault-audit-filter_auditserver
17+
volumes:
18+
- ./config:/config:ro
19+
- ./logs:/var/log/
1720
depends_on:
1821
- vault_primary
19-
command: "auditServer --vault.token root --vault.audit_address vault-audit-filter_auditserver:1269"
22+
command: "auditServer --config /config/config.yaml --vault.token root --vault.audit_address vault-audit-filter_auditserver:1269"
2023

2124
vault-audit-filter_setup:
2225
image: ncode/vault-audit-filter:dev

pkg/auditserver/server.go

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
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+
77101
type AuditServer struct {
78102
*gnet.EventServer
79-
logger *slog.Logger
80-
compiledExpr []CompiledRule
103+
logger *slog.Logger
104+
ruleGroups []RuleGroup
81105
}
82106

83107
func (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

Comments
 (0)