Commit 8a2d69fb authored by liming6's avatar liming6
Browse files

feature 修改策略:使用sftp日志跟踪sftp上传文件动作

parent 5b619e2e
......@@ -26,3 +26,33 @@ sshd-tool监听这个unix socket,过滤出需要的信息
- sftp登录在sshd日志里有记录,而who -u的输出是没有记录的
- who -u的输出里,可能有多个pid相同的数据,那是同一个ssh连接的多个虚拟终端,由于没有登录动作,所以sshd日志里没有对应的日志条目
## auditd日志分析
tabby上传文件成功:
```
open(at) pid: 35286, ppid: 35262, open /tmp/navicat17_premium_lite_cs_x64.exe.tabby-upload, fd: 4
-----
close pid: 35286, ppid: 35262, fd: 4
-----
link(at): paths: [map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126469 item:0 mode:0100644 name:/tmp/navicat17_premium_lite_cs_x64.exe.tabby-upload nametype:NORMAL ogid:0 ouid:0 rdev:00:00] map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126465 item:1 mode:041777 name:/tmp/ nametype:PARENT ogid:0 ouid:0 rdev:00:00] map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126469 item:2 mode:0100644 name:/tmp/navicat17_premium_lite_cs_x64.exe nametype:CREATE ogid:0 ouid:0 rdev:00:00]]
-----
unlink(at): paths: [map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126465 item:0 mode:041777 name:/tmp/ nametype:PARENT ogid:0 ouid:0 rdev:00:00] map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126469 item:1 mode:0100644 name:/tmp/navicat17_premium_lite_cs_x64.exe.tabby-upload nametype:DELETE ogid:0 ouid:0 rdev:00:00]]
-----
close pid: 35286, ppid: 35262, fd: 4
```
tabby创建文件后中断
```
open(at) pid: 35286, ppid: 35262, open /tmp/Rocky-8.10-x86_64-dvd1.iso.tabby-upload, fd: 4
-----
unlink(at): paths: [map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126465 item:0 mode:041777 name:/tmp/ nametype:PARENT ogid:0 ouid:0 rdev:00:00] map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126470 item:1 mode:0100644 name:/tmp/Rocky-8.10-x86_64-dvd1.iso.tabby-upload nametype:DELETE ogid:0 ouid:0 rdev:00:00]]
```
```
open(at) pid: 35286, ppid: 35262, open /tmp/AnolisOS-8.6-x86_64-dvd.iso.tabby-upload, fd: 5
-----
unlink(at): paths: [map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126465 item:0 mode:041777 name:/tmp/ nametype:PARENT ogid:0 ouid:0 rdev:00:00] map[cap_fe:0 cap_fi:0 cap_fp:0 cap_frootid:0 cap_fver:0 dev:fc:00 inode:8126471 item:1 mode:0100644 name:/tmp/AnolisOS-8.6-x86_64-dvd.iso.tabby-upload nametype:DELETE ogid:0 ouid:0 rdev:00:00]]
```
\ No newline at end of file
This diff is collapsed.
......@@ -13,7 +13,7 @@ import (
var (
// 需要的二进制文件
NeedExec = []string{"chattr", "lsattr", "clamscan", "clamdscan"}
NeedExec = []string{"clamscan", "clamdscan"}
ReInfectedFile = regexp.MustCompile(`(?i)^infected\s+files:\s+([0-9]+)\s*$`)
......@@ -30,6 +30,9 @@ func CheckExec() error {
return nil
}
// todo 添加对tar、tgz等压缩文件的优化
// 压缩文件大小在500MB内,解压出流,让clamdscan扫描流
// ScanFile 扫描文件,返回是否有毒,如果有毒会删除
func ScanFile(f string) (bool, error) {
finfo, err := os.Stat(f)
......
package logic
import (
"os"
"strings"
"testing"
"time"
......@@ -49,3 +50,22 @@ func TestParseInt(t *testing.T) {
}
t.Logf("%d", i)
}
func TestPids(t *testing.T) {
content, err := os.ReadFile("../../../asset/sftp.log")
if err != nil {
t.Error(err)
}
lines := strings.Split(string(content), "\n")
start := time.Now()
for _, v := range lines {
if len(v) > 0 {
_, err := ParseSftpLog(v)
if err != nil {
t.Error(err)
}
}
}
tt := time.Since(start)
t.Logf("%d ms", tt.Milliseconds())
}
......@@ -157,10 +157,10 @@ func FiltMsg() {
// 不是系统调用,跳过
continue
}
if i.Data == nil {
// Data里没有数据的跳过
continue
}
// if i.Data == nil {
// // Data里没有数据的跳过
// continue
// }
sc, have := i.Data["syscall"]
if !have {
continue
......@@ -198,18 +198,20 @@ func printEvent(e *aucoalesce.Event) {
if e == nil {
return
}
fmt.Println("-----")
switch e.Data["syscall"] {
case "open", "openat":
fmt.Printf("open(at) pid: %s, ppid: %s, open %s, fd: %s \n", e.Process.PID, e.Process.PPID, e.File.Path, e.Data["exit"])
case "close":
fmt.Printf("close pid: %s, ppid: %s, fd: %s \n", e.Process.PID, e.Process.PPID, e.Data["a0"])
case "rename", "renameat", "renameat2":
fmt.Println(json.MarshalIndent(e, "", " "))
case "link", "linkat":
fmt.Printf("link(at): paths: %v\n", e.Paths)
fmt.Printf("link(at): paths: %+v\n", e.Paths)
case "unlink", "unlinkat":
fmt.Printf("unlink(at): paths: %v\n", e.Paths)
fmt.Printf("unlink(at): paths: %+v\n", e.Paths)
case "dup2", "dup3":
fmt.Println("dup")
......
package logic
import (
"errors"
"fmt"
"log"
"net"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/samber/mo"
"github.com/shirou/gopsutil/v4/process"
)
var (
RegParsePid = regexp.MustCompile(`^\[([0-9]+)\]:\s*(.*)$`)
RegParseSession = regexp.MustCompile(`(?i)^session\s+(opened|closed)\s+for\s+local\s+user\s+(.*)\s+from\s+\[(.*)\]`)
SftpLogMap = make(map[int32]*SftpLogSet) // 全局的,用于记录sftp信息的map,key为进程id,value为日志集
SftpLogLock = sync.RWMutex{} // 保护SftpLogMap的锁
)
type GetSLA interface {
GetFfileAction() SftpLogAction
SetPid(pid int32)
GetPid() int32
}
type SftpLogAction string
const (
SLAUnknown SftpLogAction = ""
SLAOpen SftpLogAction = "open "
SLAClose SftpLogAction = "close "
SLARemove SftpLogAction = "remove name "
SLARename SftpLogAction = "rename old "
SLAForceClose SftpLogAction = "forced close "
SLAOpenSession SftpLogAction = "session opened"
SLACloseSession SftpLogAction = "session closed"
)
func parseSLA(s string, t time.Time) GetSLA {
prev := ""
if strings.HasPrefix(s, string(SLAOpen)) {
fields := strings.Fields(s)
result := SftpLogOpen{Time: t}
for i, v := range fields[1:] {
if i == 0 {
result.Path = strings.Trim(v, `"`)
continue
}
switch v {
case "flags", "mode":
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
}
if strings.HasPrefix(s, string(SLAClose)) {
fields := strings.Fields(s)
result := SftpLogClose{Time: t}
for k, v := range fields[1:] {
if k == 0 {
result.Path = strings.Trim(v, `"`)
}
switch v {
case "bytes", "read", "written":
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
}
if strings.HasPrefix(s, string(SLARemove)) {
fields := strings.Fields(s)
result := SftpLogRemove{Time: t}
result.Path = fields[2]
return &result
}
if strings.HasPrefix(s, string(SLARename)) {
fields := strings.Fields(s)
result := SftpLogRename{Time: t}
for _, v := range fields[1:] {
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
}
if strings.HasPrefix(s, string(SLAForceClose)) {
fields := strings.Fields(s)
result := SftpLogForceClose{Time: t}
for k, v := range fields[2:] {
if k == 0 {
result.Path = strings.Trim(v, `"`)
}
switch v {
case "bytes", "read", "written":
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
}
if strings.HasPrefix(s, string(SLAOpenSession)) {
result := SftpLogOpenSession{Time: t}
items := RegParseSession.FindStringSubmatch(s)
result.User = items[2]
result.From = items[3]
return &result
}
if strings.HasPrefix(s, string(SLACloseSession)) {
result := SftpLogCloseSession{Time: t}
items := RegParseSession.FindStringSubmatch(s)
result.User = items[2]
result.From = items[3]
return &result
}
return nil
}
type SftpLogOpen struct {
Path string
Flags []string
Mode mo.Option[uint32]
Time time.Time
Pid int32
}
func (slo *SftpLogOpen) GetFfileAction() SftpLogAction {
return SLAOpen
}
func (slo *SftpLogOpen) GetPid() int32 {
return slo.Pid
}
func (slo *SftpLogOpen) SetPid(pid int32) {
slo.Pid = pid
}
type SftpLogClose struct {
Path string
Read, Write mo.Option[uint64]
Time time.Time
Pid int32
}
func (s *SftpLogClose) GetFfileAction() SftpLogAction {
return SLAClose
}
func (s *SftpLogClose) GetPid() int32 {
return s.Pid
}
func (s *SftpLogClose) SetPid(pid int32) {
s.Pid = pid
}
type SftpLogRemove struct {
Path string
Time time.Time
Pid int32
}
func (slr *SftpLogRemove) GetFfileAction() SftpLogAction {
return SLARemove
}
func (s *SftpLogRemove) GetPid() int32 {
return s.Pid
}
func (s *SftpLogRemove) SetPid(pid int32) {
s.Pid = pid
}
type SftpLogRename struct {
Old, New string
Time time.Time
Pid int32
}
func (slr *SftpLogRename) GetFfileAction() SftpLogAction {
return SLARename
}
func (s *SftpLogRename) GetPid() int32 {
return s.Pid
}
func (s *SftpLogRename) SetPid(pid int32) {
s.Pid = pid
}
type SftpLogForceClose struct {
Path string
Read, Write mo.Option[uint64]
Time time.Time
Pid int32
}
func (slfc *SftpLogForceClose) GetFfileAction() SftpLogAction {
return SLAForceClose
}
func (s *SftpLogForceClose) GetPid() int32 {
return s.Pid
}
func (s *SftpLogForceClose) SetPid(pid int32) {
s.Pid = pid
}
type SftpLogOpenSession struct {
Time time.Time
User string
From string
Pid int32
}
func (slos *SftpLogOpenSession) GetFfileAction() SftpLogAction {
return SLAOpenSession
}
func (s *SftpLogOpenSession) GetPid() int32 {
return s.Pid
}
func (s *SftpLogOpenSession) SetPid(pid int32) {
s.Pid = pid
}
type SftpLogCloseSession struct {
Time time.Time
User string
From string
Pid int32
}
func (slos *SftpLogCloseSession) GetFfileAction() SftpLogAction {
return SLACloseSession
}
func (s *SftpLogCloseSession) GetPid() int32 {
return s.Pid
}
func (s *SftpLogCloseSession) SetPid(pid int32) {
s.Pid = pid
}
// SftpLogSet 存储一个sftp进程的相关日志信息
type SftpLogSet struct {
Pid int32 // 进程pid
User string // 用户名或uid
From string // 连接地址
SessionStart mo.Option[time.Time] // 对话开始时间
SessionClose mo.Option[time.Time] // 会话断开时间
OpenedFile map[string]*FileInfo // 文件日志
IsTabby mo.Option[bool] // 进程是否为tabby
IsAlive bool // 进程是否存活
Lock sync.RWMutex // 保护OpenedFile读写的锁
}
func NewSftpLogSet(pid int32, user *string, startTime *time.Time) *SftpLogSet {
result := &SftpLogSet{
Pid: pid,
User: "",
SessionStart: mo.None[time.Time](),
SessionClose: mo.None[time.Time](),
OpenedFile: make(map[string]*FileInfo),
IsTabby: mo.None[bool](),
IsAlive: true,
Lock: sync.RWMutex{},
}
if user != nil {
result.User = *user
} else {
p, err := process.NewProcess(pid)
if err == nil {
user, err := p.Username()
if err == nil {
result.User = user
}
}
}
if startTime != nil {
result.SessionStart = mo.Some(*startTime)
}
return result
}
// CheckAlive 检查进程是否存活
func (sls *SftpLogSet) CheckAlive() (bool, error) {
p, err := process.NewProcess(sls.Pid)
if err != nil {
sls.IsAlive = false
return false, err
}
if p == nil {
sls.IsAlive = false
return false, nil
}
cmd, err := p.Cmdline()
if err != nil {
sls.IsAlive = false
return false, err
}
alive := strings.Contains(cmd, "sftp-server")
sls.IsAlive = alive
return alive, nil
}
type FileInfo struct {
Path string
NameSet map[string]bool // 可用名称
Log []GetSLA // 文件操作日志
LogSet *SftpLogSet
}
func NewFileInfo(path string, ls *SftpLogSet) *FileInfo {
return &FileInfo{
Path: path,
NameSet: make(map[string]bool),
Log: make([]GetSLA, 0, 4),
LogSet: ls,
}
}
// CheckNeedScan 检查是否需要扫描文件
func (fi *FileInfo) CheckNeedScan() {
// 检查最后一个日志的种类
l := len(fi.Log)
if l == 0 {
return
}
last := fi.Log[l-1]
switch act := last.(type) {
case *SftpLogClose:
if strings.HasSuffix(act.Path, "tabby-upload") {
return
}
log.Println("scan file: ", act.Path)
have, _ := ScanFile(act.Path)
if have {
log.Println(act.Path, "有病毒")
} else {
log.Println(act.Path, "没病毒")
}
case *SftpLogRename:
if strings.HasSuffix(act.Old, "tabby-upload") {
log.Println("scan file: ", act.New)
have, _ := ScanFile(act.New)
if have {
log.Println(act.New, "有病毒")
} else {
log.Println(act.New, "没病毒")
}
}
case *SftpLogForceClose:
log.Println("scan file: ", act.Path)
have, _ := ScanFile(act.Path)
if have {
log.Println(act.Path, "有病毒")
} else {
log.Println(act.Path, "没病毒")
}
}
}
func InsertAction(action GetSLA) {
if action == nil {
return
}
pid := action.GetPid()
SftpLogLock.Lock()
defer SftpLogLock.Unlock()
switch act := action.(type) {
case *SftpLogOpenSession:
// 新建
sls := NewSftpLogSet(pid, &act.User, &act.Time)
sls.From = act.From
SftpLogMap[pid] = sls
return
case *SftpLogCloseSession:
delete(SftpLogMap, pid)
return
case *SftpLogOpen:
ls, have := SftpLogMap[pid]
if !have {
ls = NewSftpLogSet(pid, nil, &act.Time)
SftpLogMap[pid] = ls
}
istabby := strings.HasSuffix(act.Path, ".tabby-upload")
ls.IsTabby = mo.Some(istabby)
ls.Lock.Lock()
finfo, have := ls.OpenedFile[act.Path]
if !have {
finfo = NewFileInfo(act.Path, ls)
ls.OpenedFile[act.Path] = finfo
finfo.Log = append(finfo.Log, act)
} else {
// 发生重复了???
}
ls.Lock.Unlock()
return
case *SftpLogClose:
// 关闭文件
ls, have := SftpLogMap[pid]
if !have {
ls = NewSftpLogSet(pid, nil, &act.Time)
SftpLogMap[pid] = ls
}
ls.Lock.Lock()
finfo, have := ls.OpenedFile[act.Path]
if !have {
finfo = NewFileInfo(act.Path, ls)
ls.OpenedFile[act.Path] = finfo
}
finfo.Log = append(finfo.Log, act)
ls.Lock.Unlock()
finfo.CheckNeedScan()
return
case *SftpLogRemove:
// 查看是否为tabby,如果是tabby,需要检查是否为上传的文件
// 如果没有,不创建日志
ls, have := SftpLogMap[pid]
if !have {
ls = NewSftpLogSet(pid, nil, &act.Time)
SftpLogMap[pid] = ls
}
a, b := ls.IsTabby.Get()
if a && b {
// 是tabby上传的文件,需要记录日志
ls.Lock.Lock()
finfo, have := ls.OpenedFile[act.Path]
if !have {
finfo = NewFileInfo(act.Path, ls)
ls.OpenedFile[act.Path] = finfo
}
finfo.Log = append(finfo.Log, act)
ls.Lock.Unlock()
} else {
delete(ls.OpenedFile, act.Path)
}
return
case *SftpLogRename:
// 查看是否为tabby,如果是tabby,需要检查是否为上传的文件
ls, have := SftpLogMap[pid]
if !have {
ls = NewSftpLogSet(pid, nil, &act.Time)
SftpLogMap[pid] = ls
}
istabby := strings.HasSuffix(act.Old, ".tabby-upload")
ls.IsTabby = mo.Some(istabby)
if istabby {
// 是tabby上传的文件,需要记录日志
ls.Lock.Lock()
finfo, have := ls.OpenedFile[act.Old]
if !have {
finfo = NewFileInfo(act.Old, ls)
ls.OpenedFile[act.Old] = finfo
}
finfo.Log = append(finfo.Log, act)
ls.Lock.Unlock()
finfo.CheckNeedScan()
}
return
case *SftpLogForceClose:
// 是强制中断,需要检查是否为上传的文件
ls, have := SftpLogMap[pid]
if !have {
ls = NewSftpLogSet(pid, nil, &act.Time)
SftpLogMap[pid] = ls
}
ls.Lock.Lock()
finfo, have := ls.OpenedFile[act.Path]
if !have {
finfo = NewFileInfo(act.Path, ls)
ls.OpenedFile[act.Path] = finfo
}
finfo.Log = append(finfo.Log, act)
ls.Lock.Unlock()
finfo.CheckNeedScan()
return
}
}
func ParseSftpLog(s string) (GetSLA, error) {
if !strings.Contains(s, "sftp-server") {
// 不是sftp日志
return nil, nil
}
items := strings.SplitN(s, "sftp-server", 2)
if len(items) != 2 {
return nil, errors.New("parse error")
}
fs := strings.Split(strings.Trim(items[0], " "), " ")
if len(fs) != 4 {
return nil, errors.New("parse error")
}
t, err := time.Parse(time.Stamp, strings.Join(fs[:3], " "))
t = t.AddDate(time.Now().Year(), 0, 0)
if err != nil {
return nil, err
}
if !RegParsePid.MatchString(items[1]) {
return nil, errors.New("parse error")
}
fs = RegParsePid.FindStringSubmatch(items[1])
if len(fs) != 3 {
return nil, errors.New("parse error")
}
pid, err := strconv.ParseInt(fs[1], 10, 32)
if err != nil {
return nil, err
}
fta := parseSLA(fs[2], t)
if fta == nil {
return nil, nil
}
fta.SetPid(int32(pid))
return fta, nil
}
func StartSftpMonitor() {
regRemove := regexp.MustCompile(`^<\d+>(.*)$`)
os.Remove("/tmp/rsyslog.sock")
conn, err := net.ListenPacket("unixgram", "/tmp/rsyslog.sock")
if err != nil {
log.Fatal(err)
}
buffer := make([]byte, 16384)
for {
n, _, err := conn.ReadFrom(buffer)
if err != nil {
break
}
content := string(buffer[:n])
items := regRemove.FindStringSubmatch(content)
if len(items) == 0 {
continue
}
if strings.Contains(items[1], "sftp-server") {
fmt.Println(items[1])
l, err := ParseSftpLog(items[1])
if err != nil {
log.Println(err)
} else {
InsertAction(l)
}
}
}
os.Remove("/tmp/rsyslog.sock")
}
package main
import (
"fmt"
"log"
"os"
"sshd-tool/cmd/file-monitor/logic"
"time"
"github.com/elastic/go-libaudit/v2"
"github.com/elastic/go-libaudit/v2/aucoalesce"
"github.com/elastic/go-libaudit/v2/auparse"
)
type EventHandler struct{}
// import (
// "fmt"
// "log"
// "os"
// "sshd-tool/cmd/file-monitor/logic"
// "time"
func (h *EventHandler) ReassemblyComplete(msgs []*auparse.AuditMessage) {
event, err := aucoalesce.CoalesceMessages(msgs)
if err != nil {
fmt.Printf("coalesce messages error: %v", err)
}
logic.EventChan <- event
}
// "github.com/elastic/go-libaudit/v2"
// "github.com/elastic/go-libaudit/v2/aucoalesce"
// "github.com/elastic/go-libaudit/v2/auparse"
// )
func (h *EventHandler) EventsLost(count int) {
fmt.Fprintf(os.Stderr, "=== event lost: %d \n", count)
}
// type EventHandler struct{}
func main() {
cli, err := libaudit.NewMulticastAuditClient(nil)
if err != nil {
log.Fatalf("failed to create audit client: %v", err)
}
defer cli.Close()
// func (h *EventHandler) ReassemblyComplete(msgs []*auparse.AuditMessage) {
// event, err := aucoalesce.CoalesceMessages(msgs)
// if err != nil {
// fmt.Printf("coalesce messages error: %v", err)
// }
// logic.EventChan <- event
// }
handler := &EventHandler{}
rea, err := libaudit.NewReassembler(1024, time.Second*60, handler)
if err != nil {
log.Printf("%v", err)
return
}
defer rea.Close()
go func() {
ticker := time.NewTicker(time.Second * 15)
defer ticker.Stop()
for range ticker.C {
if rea.Maintain() != nil {
break
}
}
}()
// func (h *EventHandler) EventsLost(count int) {
// fmt.Fprintf(os.Stderr, "=== event lost: %d \n", count)
// }
// func main() {
// cli, err := libaudit.NewMulticastAuditClient(nil)
// if err != nil {
// log.Fatalf("failed to create audit client: %v", err)
// }
// defer cli.Close()
// handler := &EventHandler{}
// rea, err := libaudit.NewReassembler(1024, time.Second*60, handler)
// if err != nil {
// log.Printf("%v", err)
// return
// }
// defer rea.Close()
// go func() {
// ticker := time.NewTicker(time.Second * 15)
// defer ticker.Stop()
// for range ticker.C {
// if rea.Maintain() != nil {
// break
// }
// }
// }()
// go logic.FiltMsg()
for {
rawMsg, err := cli.Receive(false)
// for {
// rawMsg, err := cli.Receive(false)
// if err != nil {
// break
// }
// _ = rea.Push(rawMsg.Type, rawMsg.Data)
// }
// close(logic.EventChan)
// }
func main() {
err := logic.CheckExec()
if err != nil {
break
}
_ = rea.Push(rawMsg.Type, rawMsg.Data)
log.Fatal(err)
}
close(logic.EventChan)
logic.StartSftpMonitor()
}
......@@ -2,13 +2,8 @@
sftp:
- 对于tabby,openat开始,close,unlink、link、unlink,unlink表示写入完成
- 普通sftp,openat开始,close表示写入完成
通过解析sftp日志实现感知文件上传动作,自动扫描文件
scp:
-
对于sftp,重命名文件:
-
使用系统调用监控
package main
import (
"fmt"
"log"
"net"
)
func main() {
conn, err := net.ListenPacket("unixgram", "/tmp/rsyslog.sock")
if err != nil {
log.Fatal(err)
}
buffer := make([]byte, 16384)
for {
n, _, err := conn.ReadFrom(buffer)
if err != nil {
break
}
fmt.Println(string(buffer[:n]))
}
}
......@@ -3,18 +3,19 @@ module sshd-tool
go 1.24.9
require (
github.com/alphadose/haxmap v1.4.1
github.com/elastic/go-libaudit/v2 v2.6.2
github.com/gin-gonic/gin v1.11.0
)
require (
github.com/alphadose/haxmap v1.4.1 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/samber/mo v1.16.0 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
......@@ -43,7 +44,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/shirou/gopsutil/v4 v4.25.12
github.com/shirou/gopsutil/v4 v4.26.2
github.com/spf13/pflag v1.0.10
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
......@@ -53,7 +54,7 @@ require (
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
......
......@@ -10,8 +10,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elastic/go-libaudit/v2 v2.6.2 h1:1PM6wVBTJHJQYsKl8jfA9/Aw9pFty5uUezPiUfKtOI4=
github.com/elastic/go-libaudit/v2 v2.6.2/go.mod h1:8205nkf2oSrXFlO4H5j8/cyVMoSF3Y7jt+FjgS4ubQU=
github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4=
......@@ -66,6 +67,7 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
......@@ -74,8 +76,10 @@ github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQB
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/samber/mo v1.16.0 h1:qpEPCI63ou6wXlsNDMLE0IIN8A+devbGX/K1xdgr4b4=
github.com/samber/mo v1.16.0/go.mod h1:DlgzJ4SYhOh41nP1L9kh9rDNERuf8IqWSAs+gj2Vxag=
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
......@@ -116,6 +120,8 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
......
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