Compare commits
2 Commits
9970e93665
...
2167db4918
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2167db4918 | ||
|
|
18e1672114 |
|
|
@ -23,3 +23,4 @@ go.work
|
||||||
|
|
||||||
sysmonitord.code-workspace
|
sysmonitord.code-workspace
|
||||||
data/
|
data/
|
||||||
|
config.yaml
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"sysmonitord/internal/monitor/detector"
|
"sysmonitord/internal/monitor/detector"
|
||||||
"sysmonitord/internal/monitor/timer"
|
"sysmonitord/internal/monitor/timer"
|
||||||
"sysmonitord/internal/monitor/watcher"
|
"sysmonitord/internal/monitor/watcher"
|
||||||
|
"sysmonitord/internal/notifier"
|
||||||
"sysmonitord/internal/scanner/file"
|
"sysmonitord/internal/scanner/file"
|
||||||
"sysmonitord/internal/scanner/process"
|
"sysmonitord/internal/scanner/process"
|
||||||
"sysmonitord/internal/storage"
|
"sysmonitord/internal/storage"
|
||||||
|
|
@ -107,6 +108,10 @@ var StartCmd = &cobra.Command{
|
||||||
procScheduler := timer.NewScheduler(time.Duration(cfg.Scanner.Process.Interval)*time.Second, procDetector)
|
procScheduler := timer.NewScheduler(time.Duration(cfg.Scanner.Process.Interval)*time.Second, procDetector)
|
||||||
procScheduler.Start()
|
procScheduler.Start()
|
||||||
|
|
||||||
|
// ====== 启动告警管理器 ======
|
||||||
|
alerter := notifier.NewAlerter(cfg.Notification)
|
||||||
|
alerter.Start()
|
||||||
|
|
||||||
logger.Log.Info("系统监控守护服务已启动,正在监控系统变化...")
|
logger.Log.Info("系统监控守护服务已启动,正在监控系统变化...")
|
||||||
|
|
||||||
quit := make(chan os.Signal, 1)
|
quit := make(chan os.Signal, 1)
|
||||||
|
|
@ -126,6 +131,14 @@ var StartCmd = &cobra.Command{
|
||||||
fileDetector.HandleEvent(event.Path, event.Op.String())
|
fileDetector.HandleEvent(event.Path, event.Op.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test
|
||||||
|
alerter.PushAlert(notifier.AlertEvent{
|
||||||
|
Type: "File",
|
||||||
|
Path: event.Path,
|
||||||
|
Reason: event.Op.String(),
|
||||||
|
Details: "To test",
|
||||||
|
})
|
||||||
|
|
||||||
case err := <-mon.Errors():
|
case err := <-mon.Errors():
|
||||||
logger.Log.Error("文件监听错误", zap.Error(err))
|
logger.Log.Error("文件监听错误", zap.Error(err))
|
||||||
|
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -23,5 +23,7 @@ require (
|
||||||
go.uber.org/zap v1.27.1 // indirect
|
go.uber.org/zap v1.27.1 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/term v0.28.0 // indirect
|
golang.org/x/term v0.28.0 // indirect
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -50,6 +50,10 @@ golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,24 @@ type Config struct {
|
||||||
Audit AuditConfig `yaml:"audit"`
|
Audit AuditConfig `yaml:"audit"`
|
||||||
Scanner ScannerConfig `yaml:"scanner"`
|
Scanner ScannerConfig `yaml:"scanner"`
|
||||||
Storage StorageConfig `yaml:"storage"`
|
Storage StorageConfig `yaml:"storage"`
|
||||||
|
Notification NotificationConfig `yaml:"notification"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificationConfig struct {
|
||||||
|
Email EmailConfig `yaml:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailConfig struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Recipients []string `yaml:"recipients"`
|
||||||
|
SMTP SMTPConfig `yaml:"smtp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SMTPConfig struct {
|
||||||
|
Server string `yaml:"server"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package notifier
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sysmonitord/internal/config"
|
||||||
|
"sysmonitord/internal/notifier/mail"
|
||||||
|
"sysmonitord/pkg/logger"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlertEvent struct {
|
||||||
|
Type string
|
||||||
|
Path string
|
||||||
|
Reason string
|
||||||
|
Details string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Alerter struct {
|
||||||
|
cfg config.NotificationConfig
|
||||||
|
mailer *mail.Mailer
|
||||||
|
eventChan chan AlertEvent
|
||||||
|
buffer []AlertEvent
|
||||||
|
mu sync.Mutex
|
||||||
|
timer *time.Timer
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlerter(cfg config.NotificationConfig) *Alerter {
|
||||||
|
return &Alerter{
|
||||||
|
cfg: cfg,
|
||||||
|
mailer: mail.NewMailer(cfg.Email),
|
||||||
|
eventChan: make(chan AlertEvent, 100),
|
||||||
|
buffer: make([]AlertEvent, 0),
|
||||||
|
interval: 30 * time.Second, // Todo: 可配置化
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alerter) Start() {
|
||||||
|
go a.loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alerter) PushAlert(event AlertEvent) {
|
||||||
|
select {
|
||||||
|
case a.eventChan <- event:
|
||||||
|
logger.Log.Debug("[notifier] 推送告警事件", zap.String("path", event.Path), zap.String("reason", event.Reason))
|
||||||
|
default:
|
||||||
|
logger.Log.Warn("[notifier] 告警事件通道已满,丢弃告警", zap.String("path", event.Path), zap.String("reason", event.Reason))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alerter) loop() {
|
||||||
|
a.timer = time.NewTimer(a.interval)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-a.eventChan:
|
||||||
|
a.mu.Lock()
|
||||||
|
a.buffer = append(a.buffer, event)
|
||||||
|
a.mu.Unlock()
|
||||||
|
logger.Log.Debug("[notifier] 收到告警,加入待发送序列", zap.String("path", event.Path))
|
||||||
|
|
||||||
|
case <-a.timer.C:
|
||||||
|
logger.Log.Debug("[notifier] 定时检查告警事件,准备发送", zap.Int("count", len(a.buffer)))
|
||||||
|
a.mu.Lock()
|
||||||
|
if len(a.buffer) > 0 {
|
||||||
|
a.sendAlert()
|
||||||
|
a.buffer = make([]AlertEvent, 0)
|
||||||
|
}
|
||||||
|
a.mu.Unlock()
|
||||||
|
|
||||||
|
a.timer.Reset(a.interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alerter) sendAlert() {
|
||||||
|
if len(a.buffer) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := fmt.Sprintf("【Sysmonitor】新增 %d 个告警", len(a.buffer))
|
||||||
|
body := "以下是最近的告警事件:\n\n"
|
||||||
|
|
||||||
|
for _, event := range a.buffer {
|
||||||
|
body += fmt.Sprintf("- [%s] %s: %s (%s)\n", event.Type, event.Path, event.Reason, event.Details)
|
||||||
|
}
|
||||||
|
|
||||||
|
body += "\n请及时关注系统安全状况。"
|
||||||
|
|
||||||
|
if err := a.mailer.Send(subject, body); err != nil {
|
||||||
|
logger.Log.Error("[notifier] 发送告警邮件失败", zap.Error(err))
|
||||||
|
} else {
|
||||||
|
logger.Log.Debug("[notifier] 告警邮件发送成功", zap.Int("count", len(a.buffer)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sysmonitord/internal/config"
|
||||||
|
"sysmonitord/pkg/logger"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"gopkg.in/gomail.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mailer struct {
|
||||||
|
cfg config.EmailConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailer(cfg config.EmailConfig) *Mailer {
|
||||||
|
return &Mailer{cfg: cfg}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mailer) Send(subject, body string) error {
|
||||||
|
if !m.cfg.Enabled {
|
||||||
|
logger.Log.Debug("[notifier] 未启用邮件通知,跳过....")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.cfg.SMTP.Server == "" || m.cfg.SMTP.Port == 0 {
|
||||||
|
logger.Log.Error("[notifier] SMTP配置缺失",
|
||||||
|
zap.String("server", m.cfg.SMTP.Server),
|
||||||
|
zap.Int("port", m.cfg.SMTP.Port),
|
||||||
|
)
|
||||||
|
return fmt.Errorf("SMTP配置缺失")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := gomail.NewMessage()
|
||||||
|
msg.SetHeader("From", m.cfg.SMTP.Username)
|
||||||
|
msg.SetHeader("To", m.cfg.Recipients...)
|
||||||
|
msg.SetHeader("Subject", subject)
|
||||||
|
msg.SetBody("text/plain", body)
|
||||||
|
|
||||||
|
d := gomail.NewDialer(
|
||||||
|
m.cfg.SMTP.Server,
|
||||||
|
m.cfg.SMTP.Port,
|
||||||
|
m.cfg.SMTP.Username,
|
||||||
|
m.cfg.SMTP.Password,
|
||||||
|
)
|
||||||
|
|
||||||
|
if m.cfg.SMTP.Port == 465 {
|
||||||
|
d.SSL = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.DialAndSend(msg); err != nil {
|
||||||
|
logger.Log.Error("[notifier] 邮件发送失败", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Log.Info("[notifier] 邮件发送成功", zap.Strings("recipients", m.cfg.Recipients))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user