Commit 4c295cf4 authored by liming6's avatar liming6
Browse files

fix 修复文件名中有空格不能正常识别的bug

parent 1f27f718
...@@ -69,3 +69,12 @@ func TestPids(t *testing.T) { ...@@ -69,3 +69,12 @@ func TestPids(t *testing.T) {
tt := time.Since(start) tt := time.Since(start)
t.Logf("%d ms", tt.Milliseconds()) t.Logf("%d ms", tt.Milliseconds())
} }
func TestParseSftpLog(t *testing.T) {
sla, err := ParseSftpLog(`Mar 17 16:38:23 anolis sftp-server[62785]: close "/tmp/Everything-1.4.1.1030.x64-Setup. exe.tabby-upload" bytes read 0 written 1987848`)
if err != nil {
t.Error(err)
}
op := sla.(*SftpLogClose)
t.Logf("%+v", op)
}
...@@ -23,6 +23,11 @@ var ( ...@@ -23,6 +23,11 @@ var (
SftpLogMap = make(map[int32]*SftpLogSet) // 全局的,用于记录sftp信息的map,key为进程id,value为日志集 SftpLogMap = make(map[int32]*SftpLogSet) // 全局的,用于记录sftp信息的map,key为进程id,value为日志集
SftpLogLock = sync.RWMutex{} // 保护SftpLogMap的锁 SftpLogLock = sync.RWMutex{} // 保护SftpLogMap的锁
RegOpenFile = regexp.MustCompile(`(?i)^open "(.*)" flags (.*) mode (.*)$`)
RegCloseFile = regexp.MustCompile(`(?i)^close "(.*)" bytes read (\d+) written (\d+)$`)
RegRenameFile = regexp.MustCompile(`(?i)^rename old "(.*)" new "(.*)"$`)
RegForceCloseFile = regexp.MustCompile(`(?i)^forced close "(.*)" bytes read (\d+) written (\d+)$`)
) )
type GetSLA interface { type GetSLA interface {
...@@ -44,131 +49,91 @@ const ( ...@@ -44,131 +49,91 @@ const (
SLACloseSession SftpLogAction = "session closed" SLACloseSession SftpLogAction = "session closed"
) )
func parseSLA(s string, t time.Time) GetSLA { func parseSLA(s string, t time.Time) (GetSLA, error) {
prev := "" rerr := errors.New("error sftp log format")
switch { switch {
case strings.HasPrefix(s, string(SLAOpen)): case strings.HasPrefix(s, string(SLAOpen)):
fields := strings.Fields(s) fields := RegOpenFile.FindStringSubmatch(s)
if len(fields) != 4 {
return nil, rerr
}
result := SftpLogOpen{Time: t} result := SftpLogOpen{Time: t}
for i, v := range fields[1:] { result.Path = fields[1]
if i == 0 { result.Flags = strings.Split(fields[2], ",")
result.Path = strings.Trim(v, `"`) p, err := strconv.ParseUint(fields[3], 8, 32)
continue if err == nil {
} result.Mode = mo.None[uint32]()
switch v { } else {
case "flags", "mode": result.Mode = mo.Some(uint32(p))
prev = v
default:
switch prev {
case "flags":
result.Flags = strings.Split(v, ",")
case "mode":
p, err := strconv.ParseUint(v, 8, 32)
if err == nil {
result.Mode = mo.None[uint32]()
} else {
result.Mode = mo.Some(uint32(p))
}
}
}
} }
return &result return &result, nil
case strings.HasPrefix(s, string(SLAClose)): case strings.HasPrefix(s, string(SLAClose)):
fields := strings.Fields(s) fields := RegCloseFile.FindStringSubmatch(s)
if len(fields) != 4 {
return nil, rerr
}
result := SftpLogClose{Time: t} result := SftpLogClose{Time: t}
for k, v := range fields[1:] { result.Path = fields[1]
if k == 0 { read, err := strconv.ParseUint(fields[2], 10, 64)
result.Path = strings.Trim(v, `"`) if err != nil {
} result.Read = mo.None[uint64]()
switch v { } else {
case "bytes", "read", "written": result.Read = mo.Some(read)
prev = v
default:
switch prev {
case "read":
read, err := strconv.ParseUint(v, 10, 64)
if err != nil {
result.Read = mo.None[uint64]()
} else {
result.Read = mo.Some(read)
}
case "written":
read, err := strconv.ParseUint(v, 10, 64)
if err != nil {
result.Write = mo.None[uint64]()
} else {
result.Write = mo.Some(read)
}
}
}
} }
return &result write, err := strconv.ParseUint(fields[3], 10, 64)
if err != nil {
result.Write = mo.None[uint64]()
} else {
result.Write = mo.Some(write)
}
return &result, nil
case strings.HasPrefix(s, string(SLARemove)): case strings.HasPrefix(s, string(SLARemove)):
fields := strings.Fields(s)
result := SftpLogRemove{Time: t} result := SftpLogRemove{Time: t}
result.Path = fields[2] result.Path = strings.Trim(strings.Trim(s, string(SLARemove)), `"`)
return &result return &result, nil
case strings.HasPrefix(s, string(SLARename)): case strings.HasPrefix(s, string(SLARename)):
fields := strings.Fields(s) fields := RegRenameFile.FindStringSubmatch(s)
result := SftpLogRename{Time: t} if len(fields) != 3 {
for _, v := range fields[1:] { return nil, rerr
switch v {
case "old", "new":
prev = v
default:
switch prev {
case "old":
result.Old = strings.Trim(v, `"`)
case "new":
result.New = strings.Trim(v, `"`)
}
}
} }
return &result result := SftpLogRename{Time: t}
result.Old = fields[1]
result.New = fields[2]
return &result, nil
case strings.HasPrefix(s, string(SLAForceClose)): case strings.HasPrefix(s, string(SLAForceClose)):
fields := strings.Fields(s) fields := RegForceCloseFile.FindStringSubmatch(s)
if len(fields) != 4 {
return nil, rerr
}
result := SftpLogForceClose{Time: t} result := SftpLogForceClose{Time: t}
for k, v := range fields[2:] { result.Path = fields[1]
if k == 0 { read, err := strconv.ParseUint(fields[2], 10, 64)
result.Path = strings.Trim(v, `"`) if err != nil {
} result.Read = mo.None[uint64]()
switch v { } else {
case "bytes", "read", "written": result.Read = mo.Some(read)
prev = v }
default: write, err := strconv.ParseUint(fields[3], 10, 64)
switch prev { if err != nil {
case "read": result.Write = mo.None[uint64]()
read, err := strconv.ParseUint(v, 10, 64) } else {
if err != nil { result.Write = mo.Some(write)
result.Read = mo.None[uint64]()
} else {
result.Read = mo.Some(read)
}
case "written":
read, err := strconv.ParseUint(v, 10, 64)
if err != nil {
result.Write = mo.None[uint64]()
} else {
result.Write = mo.Some(read)
}
}
}
} }
return &result return &result, nil
case strings.HasPrefix(s, string(SLAOpenSession)): case strings.HasPrefix(s, string(SLAOpenSession)):
result := SftpLogOpenSession{Time: t} result := SftpLogOpenSession{Time: t}
items := RegParseSession.FindStringSubmatch(s) items := RegParseSession.FindStringSubmatch(s)
result.User = items[2] result.User = items[2]
result.From = items[3] result.From = items[3]
return &result return &result, nil
case strings.HasPrefix(s, string(SLACloseSession)): case strings.HasPrefix(s, string(SLACloseSession)):
result := SftpLogCloseSession{Time: t} result := SftpLogCloseSession{Time: t}
items := RegParseSession.FindStringSubmatch(s) items := RegParseSession.FindStringSubmatch(s)
result.User = items[2] result.User = items[2]
result.From = items[3] result.From = items[3]
return &result return &result, nil
default: default:
return nil return nil, nil
} }
} }
...@@ -588,6 +553,7 @@ func InsertAction(action GetSLA) { ...@@ -588,6 +553,7 @@ func InsertAction(action GetSLA) {
} }
} }
// todo 需要适应文件名有空格的情况
func ParseSftpLog(s string) (GetSLA, error) { func ParseSftpLog(s string) (GetSLA, error) {
if !strings.Contains(s, "sftp-server") { if !strings.Contains(s, "sftp-server") {
// 不是sftp日志 // 不是sftp日志
...@@ -617,7 +583,10 @@ func ParseSftpLog(s string) (GetSLA, error) { ...@@ -617,7 +583,10 @@ func ParseSftpLog(s string) (GetSLA, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
fta := parseSLA(fs[2], t) fta, err := parseSLA(fs[2], t)
if err != nil {
return nil, err
}
if fta == nil { if fta == nil {
return nil, nil return nil, nil
} }
...@@ -645,6 +614,7 @@ func StartSftpMonitor() { ...@@ -645,6 +614,7 @@ func StartSftpMonitor() {
continue continue
} }
if strings.Contains(items[1], "sftp-server") { if strings.Contains(items[1], "sftp-server") {
fmt.Println(items[1])
l, err := ParseSftpLog(items[1]) l, err := ParseSftpLog(items[1])
if err != nil { if err != nil {
log.Println(err) log.Println(err)
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/gofrs/flock" "github.com/gofrs/flock"
"github.com/spf13/pflag"
) )
// import ( // import (
...@@ -75,24 +76,20 @@ import ( ...@@ -75,24 +76,20 @@ import (
// } // }
var ( var (
logfile *os.File logfile *os.File
flagDebug = pflag.Bool("debug", false, "debug mode, print log to stdout, not file")
flagHelp = pflag.BoolP("help", "h", false, "show usage")
) )
func init() { func main() {
logFile, err := os.Create(fmt.Sprintf("/var/log/file-monitor.%s.log", time.Now().Format("2006-01-02_15-04-05"))) pflag.Parse()
if err == nil {
logfile = logFile
log.SetOutput(logFile)
}
}
func shutdown() { if *flagHelp {
if logfile != nil { fmt.Println("Monitor sftp and scp file uploads. After a file is uploaded, use clamdscan to scan the file. If the file contains a virus, delete it")
logfile.Close() pflag.Usage()
return
} }
}
func main() {
fileLock := flock.New("/tmp/file-monitor.lock") fileLock := flock.New("/tmp/file-monitor.lock")
locked, err := fileLock.TryLock() locked, err := fileLock.TryLock()
if err != nil || !locked { if err != nil || !locked {
...@@ -103,5 +100,15 @@ func main() { ...@@ -103,5 +100,15 @@ func main() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
if !*flagDebug {
logFile, err := os.Create(fmt.Sprintf("/var/log/file-monitor.%s.log", time.Now().Format("2006-01-02_15-04-05")))
if err == nil {
logfile = logFile
log.SetOutput(logFile)
defer logFile.Close()
}
}
logic.StartSftpMonitor() logic.StartSftpMonitor()
} }
...@@ -15,3 +15,9 @@ file-minitor是一个监控sftp和scp上传文件动作并对上传文件进行 ...@@ -15,3 +15,9 @@ file-minitor是一个监控sftp和scp上传文件动作并对上传文件进行
同样大小下,`.zip`文件扫描比较慢,建议压缩包格式为 `.tar.gz` 同样大小下,`.zip`文件扫描比较慢,建议压缩包格式为 `.tar.gz`
## todo
添加白名单:
- 用户白名单:不扫描指定用户上传的文件
- 路径白名单:不扫描上传到指定路径的文件
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment