MM-22784 Advanced logging config for audit (#15076)
Adds the advanced logging config for audit. Existing support for auditing to a single file remains for E0 and E10 licenses instances, and a new config item ExperimentalAuditSettings.AdvancedLoggingConfig is added that behaves like LogSettings.AdvancedLoggingConfig. Supported destinations: - file - syslog (with out without TLS) - raw TCP socket (with out without TLS) ExperimentalAuditSettings.AdvancedLoggingConfig can contain a filespec to a config file, a database DSN, or JSON. Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Claudio Costa <cstcld91@gmail.com>
Этот коммит содержится в:
10
api4/post.go
10
api4/post.go
@@ -45,7 +45,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
post.UserId = c.App.Session().UserId
|
post.UserId = c.App.Session().UserId
|
||||||
|
|
||||||
auditRec := c.MakeAuditRecord("createPost", audit.Fail)
|
auditRec := c.MakeAuditRecord("createPost", audit.Fail)
|
||||||
defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
auditRec.AddMeta("post", post)
|
auditRec.AddMeta("post", post)
|
||||||
|
|
||||||
hasPermission := false
|
hasPermission := false
|
||||||
@@ -377,7 +377,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auditRec := c.MakeAuditRecord("deletePost", audit.Fail)
|
auditRec := c.MakeAuditRecord("deletePost", audit.Fail)
|
||||||
defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
auditRec.AddMeta("post_id", c.Params.PostId)
|
auditRec.AddMeta("post_id", c.Params.PostId)
|
||||||
|
|
||||||
post, err := c.App.GetSinglePost(c.Params.PostId)
|
post, err := c.App.GetSinglePost(c.Params.PostId)
|
||||||
@@ -537,7 +537,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auditRec := c.MakeAuditRecord("updatePost", audit.Fail)
|
auditRec := c.MakeAuditRecord("updatePost", audit.Fail)
|
||||||
defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
|
|
||||||
// The post being updated in the payload must be the same one as indicated in the URL.
|
// The post being updated in the payload must be the same one as indicated in the URL.
|
||||||
if post.Id != c.Params.PostId {
|
if post.Id != c.Params.PostId {
|
||||||
@@ -595,7 +595,7 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auditRec := c.MakeAuditRecord("patchPost", audit.Fail)
|
auditRec := c.MakeAuditRecord("patchPost", audit.Fail)
|
||||||
defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
|
|
||||||
// Updating the file_ids of a post is not a supported operation and will be ignored
|
// Updating the file_ids of a post is not a supported operation and will be ignored
|
||||||
post.FileIds = nil
|
post.FileIds = nil
|
||||||
@@ -660,7 +660,7 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinn
|
|||||||
}
|
}
|
||||||
|
|
||||||
auditRec := c.MakeAuditRecord("saveIsPinnedPost", audit.Fail)
|
auditRec := c.MakeAuditRecord("saveIsPinnedPost", audit.Fail)
|
||||||
defer c.LogAuditRecWithLevel(auditRec, app.RestContentLevel)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
|
|
||||||
if !c.App.SessionHasPermissionToChannelByPost(*c.App.Session(), c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
|
if !c.App.SessionHasPermissionToChannelByPost(*c.App.Session(), c.Params.PostId, model.PERMISSION_READ_CHANNEL) {
|
||||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||||
|
|||||||
@@ -214,10 +214,10 @@ type AppIface interface {
|
|||||||
IsUsernameTaken(name string) bool
|
IsUsernameTaken(name string) bool
|
||||||
// LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
|
// LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
|
||||||
LimitedClientConfigWithComputed() map[string]string
|
LimitedClientConfigWithComputed() map[string]string
|
||||||
// LogAuditRec logs an audit record using default CLILevel.
|
// LogAuditRec logs an audit record using default LvlAuditCLI.
|
||||||
LogAuditRec(rec *audit.Record, err error)
|
LogAuditRec(rec *audit.Record, err error)
|
||||||
// LogAuditRecWithLevel logs an audit record using specified Level.
|
// LogAuditRecWithLevel logs an audit record using specified Level.
|
||||||
LogAuditRecWithLevel(rec *audit.Record, level audit.Level, err error)
|
LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel, err error)
|
||||||
// MakeAuditRecord creates a audit record pre-populated with defaults.
|
// MakeAuditRecord creates a audit record pre-populated with defaults.
|
||||||
MakeAuditRecord(event string, initialStatus string) *audit.Record
|
MakeAuditRecord(event string, initialStatus string) *audit.Record
|
||||||
// MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one.
|
// MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one.
|
||||||
|
|||||||
94
app/audit.go
94
app/audit.go
@@ -9,7 +9,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/mattermost/mattermost-server/v5/audit"
|
"github.com/mattermost/mattermost-server/v5/audit"
|
||||||
|
"github.com/mattermost/mattermost-server/v5/config"
|
||||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||||
"github.com/mattermost/mattermost-server/v5/model"
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
"github.com/mattermost/mattermost-server/v5/store"
|
"github.com/mattermost/mattermost-server/v5/store"
|
||||||
@@ -23,10 +25,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
RestLevel = audit.Level{ID: RestLevelID, Name: "audit-rest", Stacktrace: false}
|
LevelAPI = mlog.LvlAuditAPI
|
||||||
RestContentLevel = audit.Level{ID: RestContentLevelID, Name: "audit-rest-content", Stacktrace: false}
|
LevelContent = mlog.LvlAuditContent
|
||||||
RestPermsLevel = audit.Level{ID: RestPermsLevelID, Name: "audit-rest-perms", Stacktrace: false}
|
LevelPerms = mlog.LvlAuditPerms
|
||||||
CLILevel = audit.Level{ID: CLILevelID, Name: "audit-cli", Stacktrace: false}
|
LevelCLI = mlog.LvlAuditCLI
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *App) GetAudits(userId string, limit int) (model.Audits, *model.AppError) {
|
func (a *App) GetAudits(userId string, limit int) (model.Audits, *model.AppError) {
|
||||||
@@ -57,13 +59,13 @@ func (a *App) GetAuditsPage(userId string, page int, perPage int) (model.Audits,
|
|||||||
return audits, nil
|
return audits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogAuditRec logs an audit record using default CLILevel.
|
// LogAuditRec logs an audit record using default LvlAuditCLI.
|
||||||
func (a *App) LogAuditRec(rec *audit.Record, err error) {
|
func (a *App) LogAuditRec(rec *audit.Record, err error) {
|
||||||
a.LogAuditRecWithLevel(rec, CLILevel, err)
|
a.LogAuditRecWithLevel(rec, mlog.LvlAuditCLI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogAuditRecWithLevel logs an audit record using specified Level.
|
// LogAuditRecWithLevel logs an audit record using specified Level.
|
||||||
func (a *App) LogAuditRecWithLevel(rec *audit.Record, level audit.Level, err error) {
|
func (a *App) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel, err error) {
|
||||||
if rec == nil {
|
if rec == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -102,46 +104,13 @@ func (a *App) MakeAuditRecord(event string, initialStatus string) *audit.Record
|
|||||||
return rec
|
return rec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) configureAudit(adt *audit.Audit) {
|
func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) error {
|
||||||
|
var errs error
|
||||||
|
|
||||||
adt.OnQueueFull = s.onAuditTargetQueueFull
|
adt.OnQueueFull = s.onAuditTargetQueueFull
|
||||||
adt.OnError = s.onAuditError
|
adt.OnError = s.onAuditError
|
||||||
|
|
||||||
// Configure target for SysLog via TLS.
|
// Configure target for rotating file output (E0, E10)
|
||||||
// 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
|
|
||||||
}
|
|
||||||
maxQSize := *s.Config().ExperimentalAuditSettings.SysLogMaxQueueSize
|
|
||||||
if maxQSize <= 0 {
|
|
||||||
maxQSize = audit.DefMaxQueueSize
|
|
||||||
}
|
|
||||||
|
|
||||||
params := &mlog.SyslogParams{
|
|
||||||
IP: IP,
|
|
||||||
Port: port,
|
|
||||||
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 := mlog.NewSyslogTarget(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("IP", IP), mlog.Int("Port", port))
|
|
||||||
adt.AddTarget(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure target for rotating file output
|
|
||||||
if *s.Config().ExperimentalAuditSettings.FileEnabled {
|
if *s.Config().ExperimentalAuditSettings.FileEnabled {
|
||||||
opts := audit.FileOptions{
|
opts := audit.FileOptions{
|
||||||
Filename: *s.Config().ExperimentalAuditSettings.FileName,
|
Filename: *s.Config().ExperimentalAuditSettings.FileName,
|
||||||
@@ -156,21 +125,50 @@ func (s *Server) configureAudit(adt *audit.Audit) {
|
|||||||
maxQueueSize = audit.DefMaxQueueSize
|
maxQueueSize = audit.DefMaxQueueSize
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := adt.MakeFilter(RestLevel, RestContentLevel, RestPermsLevel, CLILevel)
|
filter := adt.MakeFilter(LevelAPI, LevelContent, LevelPerms, LevelCLI)
|
||||||
formatter := adt.MakeJSONFormatter()
|
formatter := adt.MakeJSONFormatter()
|
||||||
formatter.DisableTimestamp = false
|
formatter.DisableTimestamp = false
|
||||||
target, err := audit.NewFileTarget(filter, formatter, opts, maxQueueSize)
|
target, err := audit.NewFileTarget(filter, formatter, opts, maxQueueSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mlog.Error("cannot configure File audit target", mlog.Err(err))
|
errs = multierror.Append(err)
|
||||||
} else {
|
} else {
|
||||||
mlog.Debug("File audit target created successfully", mlog.String("filename", opts.Filename))
|
mlog.Debug("File audit target created successfully", mlog.String("filename", opts.Filename))
|
||||||
adt.AddTarget(target)
|
adt.AddTarget(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advanced logging for audit requires license.
|
||||||
|
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
||||||
|
if !bAllowAdvancedLogging || dsn == "" {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
isJson := config.IsJsonMap(dsn)
|
||||||
|
cfg, err := config.NewLogConfigSrc(dsn, isJson, s.configStore)
|
||||||
|
if err != nil {
|
||||||
|
errs = multierror.Append(fmt.Errorf("invalid config for audit, %w", err))
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
if !isJson {
|
||||||
|
mlog.Debug("Loaded audit configuration", mlog.String("filename", dsn))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) onAuditTargetQueueFull(qname string, maxQSize int) {
|
for name, t := range cfg.Get() {
|
||||||
mlog.Warn("Audit Queue Full", mlog.String("qname", qname), mlog.Int("maxQSize", maxQSize))
|
if len(t.Levels) == 0 {
|
||||||
|
t.Levels = mlog.MLvlAuditAll
|
||||||
|
}
|
||||||
|
target, err := mlog.NewLogrTarget(name, t)
|
||||||
|
if err != nil {
|
||||||
|
errs = multierror.Append(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
adt.AddTarget(target)
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) onAuditTargetQueueFull(qname string, maxQSize int) bool {
|
||||||
|
mlog.Error("Audit queue full, dropping record.", mlog.String("qname", qname), mlog.Int("queueSize", maxQSize))
|
||||||
|
return true // drop it
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) onAuditError(err error) {
|
func (s *Server) onAuditError(err error) {
|
||||||
|
|||||||
@@ -405,15 +405,13 @@ func (s *Server) trackConfig() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
s.SendDiagnostic(TRACK_CONFIG_AUDIT, map[string]interface{}{
|
s.SendDiagnostic(TRACK_CONFIG_AUDIT, map[string]interface{}{
|
||||||
"syslog_enabled": *cfg.ExperimentalAuditSettings.SysLogEnabled,
|
|
||||||
"syslog_insecure": *cfg.ExperimentalAuditSettings.SysLogInsecure,
|
|
||||||
"syslog_max_queue_size": *cfg.ExperimentalAuditSettings.SysLogMaxQueueSize,
|
|
||||||
"file_enabled": *cfg.ExperimentalAuditSettings.FileEnabled,
|
"file_enabled": *cfg.ExperimentalAuditSettings.FileEnabled,
|
||||||
"file_max_size_mb": *cfg.ExperimentalAuditSettings.FileMaxSizeMB,
|
"file_max_size_mb": *cfg.ExperimentalAuditSettings.FileMaxSizeMB,
|
||||||
"file_max_age_days": *cfg.ExperimentalAuditSettings.FileMaxAgeDays,
|
"file_max_age_days": *cfg.ExperimentalAuditSettings.FileMaxAgeDays,
|
||||||
"file_max_backups": *cfg.ExperimentalAuditSettings.FileMaxBackups,
|
"file_max_backups": *cfg.ExperimentalAuditSettings.FileMaxBackups,
|
||||||
"file_compress": *cfg.ExperimentalAuditSettings.FileCompress,
|
"file_compress": *cfg.ExperimentalAuditSettings.FileCompress,
|
||||||
"file_max_queue_size": *cfg.ExperimentalAuditSettings.FileMaxQueueSize,
|
"file_max_queue_size": *cfg.ExperimentalAuditSettings.FileMaxQueueSize,
|
||||||
|
"advanced_logging_config": *cfg.ExperimentalAuditSettings.AdvancedLoggingConfig != "",
|
||||||
})
|
})
|
||||||
|
|
||||||
s.SendDiagnostic(TRACK_CONFIG_NOTIFICATION_LOG, map[string]interface{}{
|
s.SendDiagnostic(TRACK_CONFIG_NOTIFICATION_LOG, map[string]interface{}{
|
||||||
@@ -424,6 +422,7 @@ func (s *Server) trackConfig() {
|
|||||||
"file_level": *cfg.NotificationLogSettings.FileLevel,
|
"file_level": *cfg.NotificationLogSettings.FileLevel,
|
||||||
"file_json": *cfg.NotificationLogSettings.FileJson,
|
"file_json": *cfg.NotificationLogSettings.FileJson,
|
||||||
"isdefault_file_location": isDefault(*cfg.NotificationLogSettings.FileLocation, ""),
|
"isdefault_file_location": isDefault(*cfg.NotificationLogSettings.FileLocation, ""),
|
||||||
|
"advanced_logging_config": *cfg.NotificationLogSettings.AdvancedLoggingConfig != "",
|
||||||
})
|
})
|
||||||
|
|
||||||
s.SendDiagnostic(TRACK_CONFIG_PASSWORD, map[string]interface{}{
|
s.SendDiagnostic(TRACK_CONFIG_PASSWORD, map[string]interface{}{
|
||||||
|
|||||||
@@ -9945,7 +9945,7 @@ func (a *OpenTracingAppLayer) LogAuditRec(rec *audit.Record, err error) {
|
|||||||
a.app.LogAuditRec(rec, err)
|
a.app.LogAuditRec(rec, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) LogAuditRecWithLevel(rec *audit.Record, level audit.Level, err error) {
|
func (a *OpenTracingAppLayer) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel, err error) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LogAuditRecWithLevel")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LogAuditRecWithLevel")
|
||||||
|
|
||||||
|
|||||||
@@ -459,13 +459,17 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
|
|
||||||
s.ReloadConfig()
|
s.ReloadConfig()
|
||||||
|
|
||||||
|
allowAdvancedLogging := license != nil && *license.Features.AdvancedLogging
|
||||||
|
|
||||||
if s.Audit == nil {
|
if s.Audit == nil {
|
||||||
s.Audit = &audit.Audit{}
|
s.Audit = &audit.Audit{}
|
||||||
s.Audit.Init(audit.DefMaxQueueSize)
|
s.Audit.Init(audit.DefMaxQueueSize)
|
||||||
s.configureAudit(s.Audit)
|
if err := s.configureAudit(s.Audit, allowAdvancedLogging); err != nil {
|
||||||
|
mlog.Error("Error configuring audit", mlog.Err(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if license == nil || !*license.Features.AdvancedLogging {
|
if !allowAdvancedLogging {
|
||||||
timeoutCtx, cancelCtx := context.WithTimeout(context.Background(), time.Second*5)
|
timeoutCtx, cancelCtx := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
defer cancelCtx()
|
defer cancelCtx()
|
||||||
mlog.Info("Shutting down advanced logging")
|
mlog.Info("Shutting down advanced logging")
|
||||||
|
|||||||
@@ -9,17 +9,16 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/logr"
|
"github.com/mattermost/logr"
|
||||||
"github.com/mattermost/logr/format"
|
"github.com/mattermost/logr/format"
|
||||||
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Level logr.Level
|
|
||||||
|
|
||||||
type Audit struct {
|
type Audit struct {
|
||||||
lgr *logr.Logr
|
lgr *logr.Logr
|
||||||
logger logr.Logger
|
logger logr.Logger
|
||||||
|
|
||||||
// OnQueueFull is called on an attempt to add an audit record to a full queue.
|
// OnQueueFull is called on an attempt to add an audit record to a full queue.
|
||||||
// On return the calling goroutine will block until the audit record can be added.
|
// Return true to drop record, or false to block until there is room in queue.
|
||||||
OnQueueFull func(qname string, maxQueueSize int)
|
OnQueueFull func(qname string, maxQueueSize int) bool
|
||||||
|
|
||||||
// OnError is called when an error occurs while writing an audit record.
|
// OnError is called when an error occurs while writing an audit record.
|
||||||
OnError func(err error)
|
OnError func(err error)
|
||||||
@@ -35,7 +34,7 @@ func (a *Audit) Init(maxQueueSize int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MakeFilter creates a filter which only allows the specified audit levels to be output.
|
// MakeFilter creates a filter which only allows the specified audit levels to be output.
|
||||||
func (a *Audit) MakeFilter(level ...Level) *logr.CustomFilter {
|
func (a *Audit) MakeFilter(level ...mlog.LogLevel) *logr.CustomFilter {
|
||||||
filter := &logr.CustomFilter{}
|
filter := &logr.CustomFilter{}
|
||||||
for _, l := range level {
|
for _, l := range level {
|
||||||
filter.Add(logr.Level(l))
|
filter.Add(logr.Level(l))
|
||||||
@@ -56,7 +55,7 @@ func (a *Audit) MakeJSONFormatter() *format.JSON {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LogRecord emits an audit record with complete info.
|
// LogRecord emits an audit record with complete info.
|
||||||
func (a *Audit) LogRecord(level Level, rec Record) {
|
func (a *Audit) LogRecord(level mlog.LogLevel, rec Record) {
|
||||||
flds := logr.Fields{}
|
flds := logr.Fields{}
|
||||||
flds[KeyAPIPath] = rec.APIPath
|
flds[KeyAPIPath] = rec.APIPath
|
||||||
flds[KeyEvent] = rec.Event
|
flds[KeyEvent] = rec.Event
|
||||||
@@ -75,7 +74,7 @@ func (a *Audit) LogRecord(level Level, rec Record) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log emits an audit record based on minimum required info.
|
// Log emits an audit record based on minimum required info.
|
||||||
func (a *Audit) Log(level Level, path string, evt string, status string, userID string, sessionID string, meta Meta) {
|
func (a *Audit) Log(level mlog.LogLevel, path string, evt string, status string, userID string, sessionID string, meta Meta) {
|
||||||
a.LogRecord(level, Record{
|
a.LogRecord(level, Record{
|
||||||
APIPath: path,
|
APIPath: path,
|
||||||
Event: evt,
|
Event: evt,
|
||||||
@@ -101,18 +100,18 @@ func (a *Audit) Shutdown() {
|
|||||||
|
|
||||||
func (a *Audit) onQueueFull(rec *logr.LogRec, maxQueueSize int) bool {
|
func (a *Audit) onQueueFull(rec *logr.LogRec, maxQueueSize int) bool {
|
||||||
if a.OnQueueFull != nil {
|
if a.OnQueueFull != nil {
|
||||||
a.OnQueueFull("main", maxQueueSize)
|
return a.OnQueueFull("main", maxQueueSize)
|
||||||
}
|
}
|
||||||
// block until record can be added.
|
mlog.Error("Audit logging queue full, dropping record.", mlog.Int("queueSize", maxQueueSize))
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Audit) onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueueSize int) bool {
|
func (a *Audit) onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueueSize int) bool {
|
||||||
if a.OnQueueFull != nil {
|
if a.OnQueueFull != nil {
|
||||||
a.OnQueueFull(fmt.Sprintf("%v", target), maxQueueSize)
|
return a.OnQueueFull(fmt.Sprintf("%v", target), maxQueueSize)
|
||||||
}
|
}
|
||||||
// block until record can be added.
|
mlog.Error("Audit logging queue full for target, dropping record.", mlog.Any("target", target), mlog.Int("queueSize", maxQueueSize))
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Audit) onLoggerError(err error) {
|
func (a *Audit) onLoggerError(err error) {
|
||||||
|
|||||||
@@ -13,20 +13,25 @@ var (
|
|||||||
LvlDebug = LogLevel{ID: 5, Name: "debug"}
|
LvlDebug = LogLevel{ID: 5, Name: "debug"}
|
||||||
LvlTrace = LogLevel{ID: 6, Name: "trace"}
|
LvlTrace = LogLevel{ID: 6, Name: "trace"}
|
||||||
// used only by the logger
|
// used only by the logger
|
||||||
LvlLogError = LogLevel{ID: 11, Name: "logerror"}
|
LvlLogError = LogLevel{ID: 11, Name: "logerror", Stacktrace: true}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register custom (discrete) levels here...
|
// Register custom (discrete) levels here.
|
||||||
// ! ID's must not exceed 32,768 !
|
// !!!!! ID's must not exceed 32,768 !!!!!!
|
||||||
var (
|
var (
|
||||||
// used by the audit system
|
// used by the audit system
|
||||||
LvlAuditDebug = LogLevel{ID: 100, Name: "AuditDebug"}
|
LvlAuditAPI = LogLevel{ID: 100, Name: "audit-api"}
|
||||||
LvlAuditError = LogLevel{ID: 101, Name: "AuditError"}
|
LvlAuditContent = LogLevel{ID: 101, Name: "audit-content"}
|
||||||
|
LvlAuditPerms = LogLevel{ID: 102, Name: "audit-permissions"}
|
||||||
|
LvlAuditCLI = LogLevel{ID: 103, Name: "audit-cli"}
|
||||||
|
|
||||||
// used by the TCP log target
|
// used by the TCP log target
|
||||||
LvlTcpLogTarget = LogLevel{ID: 105, Name: "TcpLogTarget"}
|
LvlTcpLogTarget = LogLevel{ID: 120, Name: "TcpLogTarget"}
|
||||||
|
|
||||||
|
// add more here ...
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combinations for LogM (log multi)
|
// Combinations for LogM (log multi)
|
||||||
var (
|
var (
|
||||||
MLvlExample = []LogLevel{LvlAuditDebug, LvlDebug}
|
MLvlAuditAll = []LogLevel{LvlAuditAPI, LvlAuditContent, LvlAuditPerms, LvlAuditCLI}
|
||||||
)
|
)
|
||||||
|
|||||||
23
mlog/logr.go
23
mlog/logr.go
@@ -39,30 +39,35 @@ type LogTargetCfg map[string]*LogTarget
|
|||||||
type LogrCleanup func() error
|
type LogrCleanup func() error
|
||||||
|
|
||||||
func newLogr(targets LogTargetCfg) (*logr.Logger, error) {
|
func newLogr(targets LogTargetCfg) (*logr.Logger, error) {
|
||||||
var errs error
|
lgr := &logr.Logr{}
|
||||||
|
|
||||||
lgr := logr.Logr{}
|
|
||||||
|
|
||||||
lgr.OnExit = func(int) {}
|
lgr.OnExit = func(int) {}
|
||||||
lgr.OnPanic = func(interface{}) {}
|
lgr.OnPanic = func(interface{}) {}
|
||||||
|
|
||||||
lgr.OnLoggerError = onLoggerError
|
lgr.OnLoggerError = onLoggerError
|
||||||
lgr.OnQueueFull = onQueueFull
|
lgr.OnQueueFull = onQueueFull
|
||||||
lgr.OnTargetQueueFull = onTargetQueueFull
|
lgr.OnTargetQueueFull = onTargetQueueFull
|
||||||
|
|
||||||
|
err := logrAddTargets(lgr, targets)
|
||||||
|
logger := lgr.NewLogger()
|
||||||
|
return &logger, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func logrAddTargets(lgr *logr.Logr, targets LogTargetCfg) error {
|
||||||
|
var errs error
|
||||||
for name, t := range targets {
|
for name, t := range targets {
|
||||||
target, err := newLogrTarget(name, t)
|
target, err := NewLogrTarget(name, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = multierror.Append(err)
|
errs = multierror.Append(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lgr.AddTarget(target)
|
lgr.AddTarget(target)
|
||||||
}
|
}
|
||||||
logger := lgr.NewLogger()
|
return errs
|
||||||
return &logger, errs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogrTarget(name string, t *LogTarget) (logr.Target, error) {
|
// NewLogrTarget creates a `logr.Target` based on a target config.
|
||||||
|
// Can be used when parsing custom config files, or when programmatically adding
|
||||||
|
// built-in targets. Use `mlog.AddTarget` to add custom targets.
|
||||||
|
func NewLogrTarget(name string, t *LogTarget) (logr.Target, error) {
|
||||||
formatter, err := newFormatter(name, t.Format)
|
formatter, err := newFormatter(name, t.Format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1117,14 +1117,6 @@ func (s *LogSettings) SetDefaults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ExperimentalAuditSettings struct {
|
type ExperimentalAuditSettings struct {
|
||||||
SysLogEnabled *bool `restricted:"true"`
|
|
||||||
SysLogIP *string `restricted:"true"`
|
|
||||||
SysLogPort *int `restricted:"true"`
|
|
||||||
SysLogTag *string `restricted:"true"`
|
|
||||||
SysLogCert *string `restricted:"true"`
|
|
||||||
SysLogInsecure *bool `restricted:"true"`
|
|
||||||
SysLogMaxQueueSize *int `restricted:"true"`
|
|
||||||
|
|
||||||
FileEnabled *bool `restricted:"true"`
|
FileEnabled *bool `restricted:"true"`
|
||||||
FileName *string `restricted:"true"`
|
FileName *string `restricted:"true"`
|
||||||
FileMaxSizeMB *int `restricted:"true"`
|
FileMaxSizeMB *int `restricted:"true"`
|
||||||
@@ -1132,37 +1124,10 @@ type ExperimentalAuditSettings struct {
|
|||||||
FileMaxBackups *int `restricted:"true"`
|
FileMaxBackups *int `restricted:"true"`
|
||||||
FileCompress *bool `restricted:"true"`
|
FileCompress *bool `restricted:"true"`
|
||||||
FileMaxQueueSize *int `restricted:"true"`
|
FileMaxQueueSize *int `restricted:"true"`
|
||||||
|
AdvancedLoggingConfig *string `restricted:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ExperimentalAuditSettings) SetDefaults() {
|
func (s *ExperimentalAuditSettings) SetDefaults() {
|
||||||
if s.SysLogEnabled == nil {
|
|
||||||
s.SysLogEnabled = NewBool(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SysLogIP == nil {
|
|
||||||
s.SysLogIP = NewString("localhost")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SysLogPort == nil {
|
|
||||||
s.SysLogPort = NewInt(6514)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SysLogTag == nil {
|
|
||||||
s.SysLogTag = NewString("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SysLogCert == nil {
|
|
||||||
s.SysLogCert = NewString("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SysLogInsecure == nil {
|
|
||||||
s.SysLogInsecure = NewBool(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.SysLogMaxQueueSize == nil {
|
|
||||||
s.SysLogMaxQueueSize = NewInt(1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.FileEnabled == nil {
|
if s.FileEnabled == nil {
|
||||||
s.FileEnabled = NewBool(false)
|
s.FileEnabled = NewBool(false)
|
||||||
}
|
}
|
||||||
@@ -1190,6 +1155,10 @@ func (s *ExperimentalAuditSettings) SetDefaults() {
|
|||||||
if s.FileMaxQueueSize == nil {
|
if s.FileMaxQueueSize == nil {
|
||||||
s.FileMaxQueueSize = NewInt(1000)
|
s.FileMaxQueueSize = NewInt(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.AdvancedLoggingConfig == nil {
|
||||||
|
s.AdvancedLoggingConfig = NewString("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotificationLogSettings struct {
|
type NotificationLogSettings struct {
|
||||||
@@ -1200,6 +1169,7 @@ type NotificationLogSettings struct {
|
|||||||
FileLevel *string `restricted:"true"`
|
FileLevel *string `restricted:"true"`
|
||||||
FileJson *bool `restricted:"true"`
|
FileJson *bool `restricted:"true"`
|
||||||
FileLocation *string `restricted:"true"`
|
FileLocation *string `restricted:"true"`
|
||||||
|
AdvancedLoggingConfig *string `restricted:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NotificationLogSettings) SetDefaults() {
|
func (s *NotificationLogSettings) SetDefaults() {
|
||||||
@@ -1230,6 +1200,10 @@ func (s *NotificationLogSettings) SetDefaults() {
|
|||||||
if s.FileJson == nil {
|
if s.FileJson == nil {
|
||||||
s.FileJson = NewBool(true)
|
s.FileJson = NewBool(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.AdvancedLoggingConfig == nil {
|
||||||
|
s.AdvancedLoggingConfig = NewString("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PasswordSettings struct {
|
type PasswordSettings struct {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func GetLogSettingsFromNotificationsLogSettings(notificationLogSettings *model.N
|
|||||||
FileJson: notificationLogSettings.FileJson,
|
FileJson: notificationLogSettings.FileJson,
|
||||||
FileLevel: notificationLogSettings.FileLevel,
|
FileLevel: notificationLogSettings.FileLevel,
|
||||||
FileLocation: notificationLogSettings.FileLocation,
|
FileLocation: notificationLogSettings.FileLocation,
|
||||||
|
AdvancedLoggingConfig: notificationLogSettings.AdvancedLoggingConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,15 @@ type Context struct {
|
|||||||
siteURLHeader string
|
siteURLHeader string
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogAuditRec logs an audit record using default RestLevel.
|
// LogAuditRec logs an audit record using default LevelAPI.
|
||||||
func (c *Context) LogAuditRec(rec *audit.Record) {
|
func (c *Context) LogAuditRec(rec *audit.Record) {
|
||||||
c.LogAuditRecWithLevel(rec, app.RestLevel)
|
c.LogAuditRecWithLevel(rec, app.LevelAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogAuditRec logs an audit record using specified Level.
|
// LogAuditRec logs an audit record using specified Level.
|
||||||
func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level audit.Level) {
|
// If the context is flagged with a permissions error then `level`
|
||||||
|
// is ignored and the audit record is emitted with `LevelPerms`.
|
||||||
|
func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel) {
|
||||||
if rec == nil {
|
if rec == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,7 @@ func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level audit.Level) {
|
|||||||
rec.AddMeta("err", c.Err.Id)
|
rec.AddMeta("err", c.Err.Id)
|
||||||
rec.AddMeta("code", c.Err.StatusCode)
|
rec.AddMeta("code", c.Err.StatusCode)
|
||||||
if c.Err.Id == "api.context.permissions.app_error" {
|
if c.Err.Id == "api.context.permissions.app_error" {
|
||||||
level = app.RestPermsLevel
|
level = app.LevelPerms
|
||||||
}
|
}
|
||||||
rec.Fail()
|
rec.Fail()
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user