Files
mostlymatter/app/audit.go
Doug Lauder 6a27ed4a1d MM-22785 audit server CLI (#14144)
Add auditing to server CLI.

Also:
- simplify auditing in API layer
- reduce number of AddMeta calls
- have models serialize themselves
- more consistent field naming
2020-04-08 00:52:30 -04:00

157 строки
4.9 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"fmt"
"os/user"
"github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
)
const (
RestLevelID = 240
RestContentLevelID = 241
RestPermsLevelID = 242
CLILevelID = 243
)
var (
RestLevel = audit.Level{ID: RestLevelID, Name: "audit-rest", Stacktrace: false}
RestContentLevel = audit.Level{ID: RestContentLevelID, Name: "audit-rest-content", Stacktrace: false}
RestPermsLevel = audit.Level{ID: RestPermsLevelID, Name: "audit-rest-perms", Stacktrace: false}
CLILevel = audit.Level{ID: CLILevelID, Name: "audit-cli", Stacktrace: false}
)
func (a *App) GetAudits(userId string, limit int) (model.Audits, *model.AppError) {
return a.Srv().Store.Audit().Get(userId, 0, limit)
}
func (a *App) GetAuditsPage(userId string, page int, perPage int) (model.Audits, *model.AppError) {
return a.Srv().Store.Audit().Get(userId, page*perPage, perPage)
}
// LogAuditRec logs an audit record using default CLILevel.
func (a *App) LogAuditRec(rec *audit.Record, err error) {
a.LogAuditRecWithLevel(rec, CLILevel, err)
}
// LogAuditRecWithLevel logs an audit record using specified Level.
func (a *App) LogAuditRecWithLevel(rec *audit.Record, level audit.Level, err error) {
if rec == nil {
return
}
if err != nil {
if appErr, ok := err.(*model.AppError); ok {
rec.AddMeta("err", appErr.Error())
rec.AddMeta("code", appErr.StatusCode)
} else {
rec.AddMeta("err", err)
}
rec.Fail()
}
a.Srv().Audit.LogRecord(level, *rec)
}
// MakeAuditRecord creates a audit record pre-populated with defaults.
func (a *App) MakeAuditRecord(event string, initialStatus string) *audit.Record {
var userID string
user, err := user.Current()
if err == nil {
userID = fmt.Sprintf("%s:%s", user.Uid, user.Username)
}
rec := &audit.Record{
APIPath: "",
Event: event,
Status: initialStatus,
UserID: userID,
SessionID: "",
Client: fmt.Sprintf("server %s-%s", model.BuildNumber, model.BuildHash),
IPAddress: "",
Meta: audit.Meta{audit.KeyClusterID: a.GetClusterId()},
}
rec.AddMetaTypeConverter(model.AuditModelTypeConv)
return rec
}
func (s *Server) configureAudit(adt *audit.Audit) {
adt.OnQueueFull = s.onAuditTargetQueueFull
adt.OnError = s.onAuditError
// Configure target for SysLog via TLS.
// See https://www.rsyslog.com/doc/v8-stable/tutorials/tls_cert_summary.html
if *s.Config().ExperimentalAuditSettings.SysLogEnabled {
IP := *s.Config().ExperimentalAuditSettings.SysLogIP
if IP == "" {
IP = "localhost"
}
port := *s.Config().ExperimentalAuditSettings.SysLogPort
if port <= 0 {
port = 6514
}
raddr := fmt.Sprintf("%s:%d", IP, port)
maxQSize := *s.Config().ExperimentalAuditSettings.SysLogMaxQueueSize
if maxQSize <= 0 {
maxQSize = audit.DefMaxQueueSize
}
params := &audit.SyslogParams{
Raddr: raddr,
Cert: *s.Config().ExperimentalAuditSettings.SysLogCert,
Tag: *s.Config().ExperimentalAuditSettings.SysLogTag,
Insecure: *s.Config().ExperimentalAuditSettings.SysLogInsecure,
}
filter := adt.MakeFilter(RestLevel, RestContentLevel, RestPermsLevel, CLILevel)
formatter := adt.MakeJSONFormatter()
target, err := audit.NewSyslogTLSTarget(filter, formatter, params, maxQSize)
if err != nil {
mlog.Error("cannot configure SysLogTLS audit target", mlog.Err(err))
} else {
mlog.Debug("SysLogTLS audit target connected successfully", mlog.String("raddr", raddr))
adt.AddTarget(target)
}
}
// Configure target for rotating file output
if *s.Config().ExperimentalAuditSettings.FileEnabled {
opts := audit.FileOptions{
Filename: *s.Config().ExperimentalAuditSettings.FileName,
MaxSize: *s.Config().ExperimentalAuditSettings.FileMaxSizeMB,
MaxAge: *s.Config().ExperimentalAuditSettings.FileMaxAgeDays,
MaxBackups: *s.Config().ExperimentalAuditSettings.FileMaxBackups,
Compress: *s.Config().ExperimentalAuditSettings.FileCompress,
}
maxQueueSize := *s.Config().ExperimentalAuditSettings.FileMaxQueueSize
if maxQueueSize <= 0 {
maxQueueSize = audit.DefMaxQueueSize
}
filter := adt.MakeFilter(RestLevel, RestContentLevel, RestPermsLevel, CLILevel)
formatter := adt.MakeJSONFormatter()
formatter.DisableTimestamp = false
formatter.Indent = "\n"
target, err := audit.NewFileTarget(filter, formatter, opts, maxQueueSize)
if err != nil {
mlog.Error("cannot configure File audit target", mlog.Err(err))
} else {
mlog.Debug("File audit target created successfully", mlog.String("filename", opts.Filename))
adt.AddTarget(target)
}
}
}
func (s *Server) onAuditTargetQueueFull(qname string, maxQSize int) {
mlog.Warn("Audit Queue Full", mlog.String("qname", qname), mlog.Int("maxQSize", maxQSize))
}
func (s *Server) onAuditError(err error) {
mlog.Error("Audit Error", mlog.Err(err))
}