diff options
Diffstat (limited to 'listener')
-rw-r--r-- | listener/audit.go | 84 | ||||
-rw-r--r-- | listener/basefunc.go | 129 | ||||
-rw-r--r-- | listener/deal.go | 269 | ||||
-rw-r--r-- | listener/global.go | 84 | ||||
-rw-r--r-- | listener/go.mod | 26 | ||||
-rw-r--r-- | listener/go.sum | 96 | ||||
-rw-r--r-- | listener/go.work | 6 | ||||
-rw-r--r-- | listener/go.work.sum | 4 | ||||
-rw-r--r-- | listener/godo.go | 176 | ||||
-rw-r--r-- | listener/mongo.go | 122 | ||||
m--------- | listener/netlink | 0 | ||||
-rw-r--r-- | listener/organize.go | 291 | ||||
-rw-r--r-- | listener/receive.go | 29 |
13 files changed, 1316 insertions, 0 deletions
diff --git a/listener/audit.go b/listener/audit.go new file mode 100644 index 0000000..ed48691 --- /dev/null +++ b/listener/audit.go | |||
@@ -0,0 +1,84 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "log" | ||
7 | "os" | ||
8 | |||
9 | "github.com/elastic/go-libaudit/v2" | ||
10 | ) | ||
11 | |||
12 | func read() error { | ||
13 | // Write netlink response to a file for further analysis or for writing | ||
14 | // tests cases. | ||
15 | var diagWriter io.Writer | ||
16 | if *diag != "" { | ||
17 | f, err := os.OpenFile(*diag, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o600) | ||
18 | if err != nil { | ||
19 | return err | ||
20 | } | ||
21 | defer f.Close() | ||
22 | diagWriter = f | ||
23 | } | ||
24 | |||
25 | log.Println("starting netlink client") | ||
26 | |||
27 | var err error | ||
28 | var client *libaudit.AuditClient | ||
29 | if *receiveOnly { | ||
30 | client, err = libaudit.NewMulticastAuditClient(diagWriter) | ||
31 | if err != nil { | ||
32 | return fmt.Errorf("failed to create receive-only audit client: %w", err) | ||
33 | } | ||
34 | defer client.Close() | ||
35 | } else { | ||
36 | client, err = libaudit.NewAuditClient(diagWriter) | ||
37 | if err != nil { | ||
38 | return fmt.Errorf("failed to create audit client: %w", err) | ||
39 | } | ||
40 | defer client.Close() | ||
41 | |||
42 | status, err := client.GetStatus() | ||
43 | if err != nil { | ||
44 | return fmt.Errorf("failed to get audit status: %w", err) | ||
45 | } | ||
46 | log.Printf("received audit status=%+v", status) | ||
47 | |||
48 | if status.Enabled == 0 { | ||
49 | log.Println("enabling auditing in the kernel") | ||
50 | if err = client.SetEnabled(true, libaudit.WaitForReply); err != nil { | ||
51 | return fmt.Errorf("failed to set enabled=true: %w", err) | ||
52 | } | ||
53 | } | ||
54 | |||
55 | if status.RateLimit != uint32(*rate) { | ||
56 | log.Printf("setting rate limit in kernel to %v", *rate) | ||
57 | if err = client.SetRateLimit(uint32(*rate), libaudit.NoWait); err != nil { | ||
58 | return fmt.Errorf("failed to set rate limit to unlimited: %w", err) | ||
59 | } | ||
60 | } | ||
61 | |||
62 | if status.BacklogLimit != uint32(*backlog) { | ||
63 | log.Printf("setting backlog limit in kernel to %v", *backlog) | ||
64 | if err = client.SetBacklogLimit(uint32(*backlog), libaudit.NoWait); err != nil { | ||
65 | return fmt.Errorf("failed to set backlog limit: %w", err) | ||
66 | } | ||
67 | } | ||
68 | |||
69 | if status.Enabled != 2 && *immutable { | ||
70 | log.Printf("setting kernel settings as immutable") | ||
71 | if err = client.SetImmutable(libaudit.NoWait); err != nil { | ||
72 | return fmt.Errorf("failed to set kernel as immutable: %w", err) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | log.Printf("sending message to kernel registering our PID (%v) as the audit daemon", os.Getpid()) | ||
77 | if err = client.SetPID(libaudit.NoWait); err != nil { | ||
78 | return fmt.Errorf("failed to set audit PID: %w", err) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | coroutine(client) | ||
83 | return nil | ||
84 | } | ||
diff --git a/listener/basefunc.go b/listener/basefunc.go new file mode 100644 index 0000000..2f39507 --- /dev/null +++ b/listener/basefunc.go | |||
@@ -0,0 +1,129 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "fmt" | ||
6 | "os" | ||
7 | "os/exec" | ||
8 | "path/filepath" | ||
9 | "strconv" | ||
10 | "strings" | ||
11 | "time" | ||
12 | ) | ||
13 | |||
14 | func figureOutSyscalls() error { | ||
15 | cmd := exec.Command("ausyscall", "--dump") | ||
16 | stdout, err := cmd.StdoutPipe() | ||
17 | if err != nil { | ||
18 | return err | ||
19 | } | ||
20 | |||
21 | if err := cmd.Start(); err != nil { | ||
22 | return err | ||
23 | } | ||
24 | |||
25 | scanner := bufio.NewScanner(stdout) | ||
26 | for i := 0; scanner.Scan(); i++ { | ||
27 | if i == 0 { | ||
28 | continue | ||
29 | } | ||
30 | line := scanner.Text() | ||
31 | parts := strings.Split(line, "\t") | ||
32 | if len(parts) != 2 { | ||
33 | return fmt.Errorf("invalid ausyscall format") | ||
34 | } | ||
35 | num, err := strconv.Atoi(parts[0]) | ||
36 | if err != nil { | ||
37 | return err | ||
38 | } | ||
39 | syscallTable[num] = parts[1] | ||
40 | } | ||
41 | |||
42 | if err := scanner.Err(); err != nil { | ||
43 | return err | ||
44 | } | ||
45 | if err := cmd.Wait(); err != nil { | ||
46 | return err | ||
47 | } | ||
48 | return nil | ||
49 | } | ||
50 | |||
51 | func getPid() (int, error) { | ||
52 | // 指定要搜索的关键词 | ||
53 | keyword := "/usr/bin/containerd" | ||
54 | |||
55 | // 获取/proc目录下的所有子目录 | ||
56 | procDir, err := filepath.Glob("/proc/*") | ||
57 | if err != nil { | ||
58 | return 0, err | ||
59 | } | ||
60 | |||
61 | // 遍历子目录,查找包含关键词的进程 | ||
62 | for _, dir := range procDir { | ||
63 | pid, err := strconv.Atoi(filepath.Base(dir)) | ||
64 | if err != nil { | ||
65 | continue // 跳过非PID的目录 | ||
66 | } | ||
67 | |||
68 | // 检查进程是否包含关键词 | ||
69 | if containsKeyword(pid, keyword) { | ||
70 | return pid, nil | ||
71 | } | ||
72 | } | ||
73 | err = fmt.Errorf("Error: no containerd process found.") | ||
74 | return 0, err | ||
75 | } | ||
76 | |||
77 | func containsKeyword(pid int, keyword string) bool { | ||
78 | // 构造完整的进程命令路径 | ||
79 | cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid) | ||
80 | |||
81 | // 打开文件 | ||
82 | file, err := os.Open(cmdPath) | ||
83 | if err != nil { | ||
84 | return false | ||
85 | } | ||
86 | defer file.Close() | ||
87 | |||
88 | // 读取文件内容 | ||
89 | scanner := bufio.NewScanner(file) | ||
90 | scanner.Split(bufio.ScanLines) | ||
91 | for scanner.Scan() { | ||
92 | line := scanner.Text() | ||
93 | if strings.Contains(line, keyword) { | ||
94 | return true | ||
95 | } | ||
96 | } | ||
97 | return false | ||
98 | } | ||
99 | |||
100 | func getTimeFromStr(timeStr string) (time.Time, error) { | ||
101 | timestampFloat, err := strconv.ParseFloat(timeStr, 64) | ||
102 | if err != nil { | ||
103 | return time.Unix(0, 0), err | ||
104 | } | ||
105 | secs := int64(timestampFloat) | ||
106 | nsecs := int64((timestampFloat - float64(secs)) * 1e9) | ||
107 | |||
108 | // 只精确到毫秒就够了 | ||
109 | t := time.Unix(secs, nsecs).Truncate(time.Millisecond) | ||
110 | return t, nil | ||
111 | } | ||
112 | |||
113 | func hexToAscii(hexString string) string { | ||
114 | bytes := []byte{} | ||
115 | for i := 0; i < len(hexString); i += 2 { | ||
116 | hexPair := hexString[i : i+2] | ||
117 | // 将十六进制数转换为十进制数 | ||
118 | decimal, err := strconv.ParseInt(hexPair, 16, 8) | ||
119 | if err != nil { | ||
120 | return "Invalid hex string" | ||
121 | } | ||
122 | char := byte(decimal) | ||
123 | bytes = append(bytes, char) | ||
124 | } | ||
125 | |||
126 | asciiString := strings.ReplaceAll(string(bytes), "\000", " ") | ||
127 | |||
128 | return asciiString | ||
129 | } | ||
diff --git a/listener/deal.go b/listener/deal.go new file mode 100644 index 0000000..8f77431 --- /dev/null +++ b/listener/deal.go | |||
@@ -0,0 +1,269 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "os" | ||
6 | "syscall" | ||
7 | "time" | ||
8 | |||
9 | "go.mongodb.org/mongo-driver/bson" | ||
10 | ) | ||
11 | |||
12 | const ( | ||
13 | dbName string = "test" | ||
14 | pidColName string = "pids" | ||
15 | fdColName string = "fds" | ||
16 | fileColName string = "files" | ||
17 | ) | ||
18 | |||
19 | var pidCol, fdCol, fileCol mongoClient | ||
20 | var err error | ||
21 | |||
22 | func deal() { | ||
23 | defer wg.Done() | ||
24 | var cooked Event | ||
25 | var ok bool | ||
26 | |||
27 | if err = pidCol.init(dbName, pidColName); err != nil { | ||
28 | fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err) | ||
29 | return | ||
30 | } | ||
31 | err = pidCol.InsertOne(Process{ | ||
32 | Ppid: 1, | ||
33 | Pid: containerdPid, | ||
34 | Cwd: "/", | ||
35 | Children: make([]int, 0), | ||
36 | Star: true, | ||
37 | }) | ||
38 | if err != nil { | ||
39 | fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err) | ||
40 | return | ||
41 | } | ||
42 | |||
43 | if err = fdCol.init(dbName, fdColName); err != nil { | ||
44 | fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err) | ||
45 | return | ||
46 | } | ||
47 | if err = fileCol.init(dbName, fileColName); err != nil { | ||
48 | fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err) | ||
49 | } | ||
50 | |||
51 | defer pidCol.Disconnect() | ||
52 | defer fdCol.Disconnect() | ||
53 | defer fileCol.Disconnect() | ||
54 | |||
55 | for { | ||
56 | cooked, ok = <-cookedChan | ||
57 | if !ok { | ||
58 | break | ||
59 | } | ||
60 | |||
61 | switch cooked.tag { | ||
62 | case NEWPID: | ||
63 | go dealNewPid(cooked) | ||
64 | case EXECVE: | ||
65 | go dealExecve(cooked) | ||
66 | case PIDEXIT: | ||
67 | go deletePid(cooked) | ||
68 | case FILEOPEN: | ||
69 | go fileOpen(cooked) | ||
70 | case FILEWRITE: | ||
71 | go fileWrite(cooked) | ||
72 | case FILECLOSE: | ||
73 | go fileClose(cooked) | ||
74 | case PIVOTROOT: | ||
75 | go pivotRoot(cooked) | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | func deletePid(cooked Event) { | ||
81 | // 在这套逻辑里,孩子是不需要收容的,因为我们根本就不看ppid来工作 | ||
82 | // 父节点那里也不需要销户 | ||
83 | // 理论上这里需要关闭所有文件描述符,但为了处理效率,留给后续流程 | ||
84 | err := pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{ | ||
85 | "$set": bson.M{ | ||
86 | "exit_timestamp": cooked.timestamp, | ||
87 | "exit_code": cooked.exit_code, | ||
88 | "exit_signal": cooked.exit_signal, | ||
89 | }, | ||
90 | }) | ||
91 | if err != nil { | ||
92 | fmt.Fprintf(os.Stderr, "Err updating: %v\n", err) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | func dealNewPid(cooked Event) { | ||
97 | // 自身是否已经记录 | ||
98 | var docRes []Process | ||
99 | err = pidCol.Finddoc(bson.M{"pid": cooked.pid}, &docRes) | ||
100 | if err != nil { | ||
101 | fmt.Fprintf(os.Stderr, "Err finding: %v\n", err) | ||
102 | return | ||
103 | } | ||
104 | |||
105 | if len(docRes) != 0 { | ||
106 | // 进程原本就存在,换言之别的消息先到了 | ||
107 | // 所有先行抵达的消息必须保留execve/children字段 | ||
108 | docRes[0].Ppid = cooked.ppid | ||
109 | docRes[0].ParentTgid = cooked.parentTgid | ||
110 | docRes[0].Pid = cooked.pid | ||
111 | docRes[0].Tgid = cooked.tgid | ||
112 | docRes[0].Cwd = cooked.cwd | ||
113 | docRes[0].Comm = cooked.comm | ||
114 | docRes[0].Args = cooked.argv | ||
115 | |||
116 | err := pidCol.ReplaceOne(bson.M{"pid": cooked.pid}, docRes[0]) | ||
117 | if err != nil { | ||
118 | fmt.Fprintf(os.Stderr, "Err replaceing: %v\n", err) | ||
119 | } | ||
120 | } else { | ||
121 | // 这进程本是新修的 | ||
122 | err := pidCol.InsertOne(Process{ | ||
123 | StartTimestamp: cooked.timestamp, | ||
124 | Ppid: cooked.ppid, | ||
125 | ParentTgid: cooked.parentTgid, | ||
126 | Pid: cooked.pid, | ||
127 | Tgid: cooked.tgid, | ||
128 | Args: cooked.argv, | ||
129 | Comm: cooked.comm, | ||
130 | Cwd: cooked.cwd, | ||
131 | Execve: make([]Exec, 0), | ||
132 | Children: make([]int, 0), | ||
133 | }) | ||
134 | if err != nil { | ||
135 | fmt.Fprintf(os.Stderr, "Err inserting: %v\n", err) | ||
136 | } | ||
137 | } | ||
138 | |||
139 | err := pidCol.UpdateOne(bson.M{"pid": cooked.ppid}, bson.M{ | ||
140 | "$push": bson.M{ | ||
141 | "children": cooked.pid, | ||
142 | }, | ||
143 | }) | ||
144 | if err != nil { | ||
145 | fmt.Fprintf(os.Stderr, "Err updating: %v\n", err) | ||
146 | } | ||
147 | } | ||
148 | |||
149 | func dealExecve(cooked Event) { | ||
150 | var docRes []Process | ||
151 | // 首先检查进程是否存在,如不存在则为之创建 | ||
152 | err = pidCol.Finddoc(bson.M{"pid": cooked.pid}, &docRes) | ||
153 | if err != nil { | ||
154 | return | ||
155 | } | ||
156 | |||
157 | if len(docRes) == 1 { | ||
158 | // 自身已在,直接记录 | ||
159 | docRes[0].Execve = append(docRes[0].Execve, Exec{ | ||
160 | Timestamp: cooked.timestamp, | ||
161 | ExecArgs: cooked.argv, | ||
162 | }) | ||
163 | |||
164 | err := pidCol.ReplaceOne(bson.M{"pid": cooked.pid}, docRes[0]) | ||
165 | if err != nil { | ||
166 | fmt.Fprintf(os.Stderr, "Err replacing: %v\n", err) | ||
167 | } | ||
168 | } else { | ||
169 | // 先fork抵达,插入 | ||
170 | process := Process{ | ||
171 | Ppid: cooked.ppid, | ||
172 | Pid: cooked.pid, | ||
173 | Execve: make([]Exec, 0), | ||
174 | Children: make([]int, 0), | ||
175 | } | ||
176 | process.Execve = append(process.Execve, Exec{ | ||
177 | Timestamp: cooked.timestamp, | ||
178 | ExecArgs: cooked.argv, | ||
179 | }) | ||
180 | |||
181 | err := pidCol.InsertOne(process) | ||
182 | if err != nil { | ||
183 | fmt.Fprintf(os.Stderr, "Err inserting: %v\n", err) | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | func fileOpen(cooked Event) { | ||
189 | // 权限检查过了,不必再查 | ||
190 | file := File{ | ||
191 | OpenTimestamp: cooked.timestamp, | ||
192 | FileName: cooked.srcPath, | ||
193 | Pid: cooked.pid, | ||
194 | Fd: cooked.exit_code, | ||
195 | Flags: cooked.syscallParam, | ||
196 | Written: make([]time.Time, 0), | ||
197 | } | ||
198 | if cooked.syscallParam[1]&syscall.O_TRUNC != 0 { | ||
199 | // 文件以清空方式打开 | ||
200 | file.Written = append(file.Written, cooked.timestamp) | ||
201 | } | ||
202 | |||
203 | err := fdCol.InsertOne(file) | ||
204 | if err != nil { | ||
205 | fmt.Fprintf(os.Stderr, "Err inserting: %v\n", err) | ||
206 | } | ||
207 | } | ||
208 | |||
209 | func fileClose(cooked Event) { | ||
210 | res, err := fdCol.FindOneAndDelete(bson.M{"pid": cooked.pid, "fd": cooked.syscallParam[0]}) | ||
211 | if err != nil { | ||
212 | return | ||
213 | } | ||
214 | res["close_timestamp"] = cooked.timestamp | ||
215 | |||
216 | err = fileCol.InsertOne(res) | ||
217 | if err != nil { | ||
218 | fmt.Fprintf(os.Stderr, "Err inserting files: %v\n", err) | ||
219 | } | ||
220 | } | ||
221 | |||
222 | func fileWrite(cooked Event) { | ||
223 | var res []File | ||
224 | err := fdCol.Finddoc(bson.M{ | ||
225 | "pid": cooked.pid, | ||
226 | "fd": cooked.syscallParam[0], | ||
227 | }, &res) | ||
228 | if err != nil { | ||
229 | fmt.Fprintf(os.Stderr, "Err closing fd %d of pid %d: %v\n", cooked.syscallParam[0], cooked.pid, err) | ||
230 | } | ||
231 | if len(res) == 0 { | ||
232 | return | ||
233 | } | ||
234 | |||
235 | err = fdCol.UpdateOne(bson.M{ | ||
236 | "pid": cooked.pid, | ||
237 | "fd": cooked.syscallParam[0], | ||
238 | }, bson.M{"$push": bson.M{"written": cooked.timestamp}}) | ||
239 | if err != nil { | ||
240 | fmt.Fprintf(os.Stderr, "Err updating: %v\n", err) | ||
241 | } | ||
242 | } | ||
243 | |||
244 | func pivotRoot(cooked Event) { | ||
245 | var docRes []Process | ||
246 | // docker的根目录信息,记录 | ||
247 | err := pidCol.Finddoc(bson.M{"pid": cooked.pid}, &docRes) | ||
248 | if err != nil { | ||
249 | fmt.Fprintf(os.Stderr, "Err finding: %v\n", err) | ||
250 | return | ||
251 | } | ||
252 | |||
253 | if len(docRes) == 0 { | ||
254 | // fork还没到,等一下 | ||
255 | pidCol.InsertOne(bson.M{ | ||
256 | "start_timestamp": cooked.timestamp, | ||
257 | "ppid": cooked.ppid, | ||
258 | "pid": cooked.pid, | ||
259 | "rootfs": "cwd", | ||
260 | }) | ||
261 | } else { | ||
262 | // 读取已有的工作目录 | ||
263 | pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{ | ||
264 | "$set": bson.M{ | ||
265 | "rootfs": docRes[0].Cwd, | ||
266 | }, | ||
267 | }) | ||
268 | } | ||
269 | } | ||
diff --git a/listener/global.go b/listener/global.go new file mode 100644 index 0000000..11b18bf --- /dev/null +++ b/listener/global.go | |||
@@ -0,0 +1,84 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "sync" | ||
5 | "time" | ||
6 | ) | ||
7 | |||
8 | type eventType int | ||
9 | |||
10 | const ( | ||
11 | NEWPID eventType = iota | ||
12 | PIDEXIT | ||
13 | EXECVE | ||
14 | FILEOPEN | ||
15 | FILECLOSE | ||
16 | FILEWRITE | ||
17 | PIVOTROOT | ||
18 | TYPENUM | ||
19 | ) | ||
20 | |||
21 | func (et eventType) String() string { | ||
22 | names := []string{"NEWPID", "PIDEXIT", "EXECVE", "FILEOPEN", "FILECLOSE", "FILEWRITE", "PIVOTROOT", "TYPENUM"} | ||
23 | if et < NEWPID || et > TYPENUM { | ||
24 | return "Unknown" | ||
25 | } | ||
26 | return names[et] | ||
27 | } | ||
28 | |||
29 | type Event struct { | ||
30 | tag eventType | ||
31 | timestamp time.Time | ||
32 | pid, tgid int | ||
33 | ppid, parentTgid int | ||
34 | syscall int | ||
35 | syscallParam [4]uint64 | ||
36 | argc int | ||
37 | argv []string | ||
38 | comm string | ||
39 | cwd string | ||
40 | exit_code int | ||
41 | exit_signal int | ||
42 | srcPath string | ||
43 | destPath string | ||
44 | } | ||
45 | |||
46 | var wg sync.WaitGroup // 掌管协程 | ||
47 | var rawChan chan interface{} // 从接收到整理的管道 | ||
48 | var cookedChan chan Event // 整理好的信息的管道 | ||
49 | var syscallTable [500]string //记录一下系统调用 | ||
50 | var containerdPid int | ||
51 | |||
52 | // 插入到数据库的结构 | ||
53 | type Exec struct { | ||
54 | Timestamp time.Time `bson:"timestamp"` | ||
55 | ExecArgs []string `bson:"execArgs"` | ||
56 | } | ||
57 | |||
58 | type Process struct { | ||
59 | Star bool `bson:"star"` | ||
60 | StartTimestamp time.Time `bson:"start_timestamp"` | ||
61 | Ppid int `bson:"ppid"` | ||
62 | ParentTgid int `bson:"parentTgid"` | ||
63 | Pid int `bson:"pid"` | ||
64 | Tgid int `bson:"tgid"` | ||
65 | Args []string `bson:"args"` | ||
66 | Comm string `bson:"comm"` | ||
67 | RootFS string `bson:"rootfs"` | ||
68 | Cwd string `bson:"cwd"` | ||
69 | Children []int `bson:"children"` | ||
70 | Execve []Exec `bson:"execve"` | ||
71 | ExitCode int `bson:"exit_code"` | ||
72 | ExitSignal int `bson:"exit_signal"` | ||
73 | ExitTimestamp time.Time `bson:"exit_timestamp"` | ||
74 | } | ||
75 | |||
76 | type File struct { | ||
77 | OpenTimestamp time.Time `bson:"timestamp"` | ||
78 | FileName string `bson:"fileName"` | ||
79 | Pid int `bson:"pid"` | ||
80 | Fd int `bson:"fd"` | ||
81 | Flags [4]uint64 `bson:"flags"` | ||
82 | Written []time.Time `bson:"written"` | ||
83 | CloseTimestamp time.Time `bson:"close_timestamp"` | ||
84 | } | ||
diff --git a/listener/go.mod b/listener/go.mod new file mode 100644 index 0000000..ed40331 --- /dev/null +++ b/listener/go.mod | |||
@@ -0,0 +1,26 @@ | |||
1 | module godo | ||
2 | |||
3 | go 1.21.5 | ||
4 | |||
5 | require ( | ||
6 | github.com/elastic/go-libaudit/v2 v2.5.0 | ||
7 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 | ||
8 | go.mongodb.org/mongo-driver v1.16.0 | ||
9 | ) | ||
10 | |||
11 | require ( | ||
12 | github.com/golang/snappy v0.0.4 // indirect | ||
13 | github.com/klauspost/compress v1.13.6 // indirect | ||
14 | github.com/montanaflynn/stats v0.7.1 // indirect | ||
15 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect | ||
16 | github.com/xdg-go/scram v1.1.2 // indirect | ||
17 | github.com/xdg-go/stringprep v1.0.4 // indirect | ||
18 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect | ||
19 | go.uber.org/atomic v1.7.0 // indirect | ||
20 | go.uber.org/multierr v1.7.0 // indirect | ||
21 | golang.org/x/crypto v0.22.0 // indirect | ||
22 | golang.org/x/sync v0.7.0 // indirect | ||
23 | golang.org/x/sys v0.19.0 // indirect | ||
24 | golang.org/x/text v0.14.0 // indirect | ||
25 | gopkg.in/yaml.v3 v3.0.1 // indirect | ||
26 | ) | ||
diff --git a/listener/go.sum b/listener/go.sum new file mode 100644 index 0000000..9164cd3 --- /dev/null +++ b/listener/go.sum | |||
@@ -0,0 +1,96 @@ | |||
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
4 | github.com/elastic/go-libaudit/v2 v2.5.0 h1:5OK919QRnGtcjVBz3n/cs5F42im1mPlVTA9TyIn2K54= | ||
5 | github.com/elastic/go-libaudit/v2 v2.5.0/go.mod h1:AjlnhinP+kKQuUJoXLVrqxBM8uyhQmkzoV6jjsCFP4Q= | ||
6 | github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4= | ||
7 | github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU= | ||
8 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= | ||
9 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||
10 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
11 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
12 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= | ||
13 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= | ||
14 | github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= | ||
15 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= | ||
16 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= | ||
17 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= | ||
18 | github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= | ||
19 | github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= | ||
20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
21 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
22 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
23 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
24 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
25 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
26 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= | ||
27 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= | ||
28 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= | ||
29 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= | ||
30 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= | ||
31 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= | ||
32 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= | ||
33 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= | ||
34 | github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||
35 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||
36 | go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4= | ||
37 | go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= | ||
38 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= | ||
39 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | ||
40 | go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= | ||
41 | go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= | ||
42 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
43 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
44 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||
45 | golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= | ||
46 | golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= | ||
47 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||
48 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||
49 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||
50 | golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= | ||
51 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||
52 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
53 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
54 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||
55 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||
56 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||
57 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
58 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
59 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
60 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= | ||
61 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||
62 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
63 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
64 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
65 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
66 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
67 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
68 | golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
69 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
70 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
71 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
72 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= | ||
73 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
74 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||
75 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||
76 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
77 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
78 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
79 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||
80 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= | ||
81 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= | ||
82 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||
83 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
84 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
85 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||
86 | golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= | ||
87 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||
88 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
89 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
90 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
91 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
92 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||
93 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
94 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
95 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
96 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
diff --git a/listener/go.work b/listener/go.work new file mode 100644 index 0000000..5b6c957 --- /dev/null +++ b/listener/go.work | |||
@@ -0,0 +1,6 @@ | |||
1 | go 1.21.5 | ||
2 | |||
3 | use ( | ||
4 | ./netlink | ||
5 | ./ | ||
6 | ) \ No newline at end of file | ||
diff --git a/listener/go.work.sum b/listener/go.work.sum new file mode 100644 index 0000000..8201e39 --- /dev/null +++ b/listener/go.work.sum | |||
@@ -0,0 +1,4 @@ | |||
1 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||
2 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= | ||
3 | golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= | ||
4 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||
diff --git a/listener/godo.go b/listener/godo.go new file mode 100644 index 0000000..efe9585 --- /dev/null +++ b/listener/godo.go | |||
@@ -0,0 +1,176 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "flag" | ||
6 | "fmt" | ||
7 | "log" | ||
8 | "netlink" | ||
9 | "os" | ||
10 | "os/exec" | ||
11 | "strings" | ||
12 | "syscall" | ||
13 | "time" | ||
14 | |||
15 | "github.com/elastic/go-libaudit/v2" | ||
16 | ) | ||
17 | |||
18 | var ( | ||
19 | fs = flag.NewFlagSet("audit", flag.ExitOnError) | ||
20 | diag = fs.String("diag", "", "dump raw information from kernel to file") | ||
21 | rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)") | ||
22 | backlog = fs.Uint("backlog", 8192, "backlog limit") | ||
23 | immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)") | ||
24 | receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+") | ||
25 | ) | ||
26 | |||
27 | func main() { | ||
28 | // 检查用户身份,并添加auditd规则,监听所有syscall | ||
29 | if os.Geteuid() != 0 { | ||
30 | fmt.Fprintf(os.Stderr, "Err: Please run me as root, %d!\n", os.Getegid()) | ||
31 | return | ||
32 | } | ||
33 | |||
34 | // 所有的系统调用号与名称的关系 | ||
35 | err := figureOutSyscalls() | ||
36 | if err != nil { | ||
37 | fmt.Fprintf(os.Stderr, "Error figuring out syscall numbers: %v\n", err) | ||
38 | } | ||
39 | |||
40 | exec.Command("auditctl", "-D").Run() | ||
41 | exec.Command("auditctl", "-b", "1000000000").Run() | ||
42 | exec.Command("auditctl", "--reset-lost").Run() | ||
43 | |||
44 | var auditCmd *exec.Cmd | ||
45 | |||
46 | pidSyscall := []string{"execve", "pivot_root"} | ||
47 | // // 设置监听规则 | ||
48 | for i := 0; i < len(pidSyscall); i++ { | ||
49 | auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i]) | ||
50 | auditCmd.Run() | ||
51 | } | ||
52 | |||
53 | // 监听文件的消息 | ||
54 | fileSyscall := []string{"open", "close", "write"} | ||
55 | // fileSyscall := []string{"open", "write", "creat", "unlink", "opendir", "mkdir", "rmdir", "chmod", "fchmod", "chown", "fchown", "lchown", "flock"} | ||
56 | for i := 0; i < len(fileSyscall); i++ { | ||
57 | auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", fileSyscall[i]) | ||
58 | auditCmd.Run() | ||
59 | } | ||
60 | |||
61 | // 查找pid | ||
62 | containerdPid, err = getPid() | ||
63 | if err != nil { | ||
64 | fmt.Fprintf(os.Stderr, "Error finding containerd: %v\n", err) | ||
65 | return | ||
66 | } | ||
67 | |||
68 | // 开始运行,解析命令行参数后监听 | ||
69 | if err := fs.Parse(os.Args[1:]); err != nil { | ||
70 | log.Fatal(err) | ||
71 | } | ||
72 | |||
73 | if err := read(); err != nil { | ||
74 | log.Fatalf("error: %v", err) | ||
75 | } | ||
76 | } | ||
77 | |||
78 | func coroutine(client *libaudit.AuditClient) { | ||
79 | // 各协程至此开始 | ||
80 | rawChan = make(chan interface{}, 65536) | ||
81 | cookedChan = make(chan Event, 65536) | ||
82 | |||
83 | wg.Add(1) | ||
84 | go procWatch() | ||
85 | |||
86 | wg.Add(1) | ||
87 | go receive(client) | ||
88 | wg.Add(1) | ||
89 | go orgnaze() | ||
90 | wg.Add(1) | ||
91 | go deal() | ||
92 | |||
93 | wg.Wait() | ||
94 | time.Sleep(2 * time.Second) | ||
95 | } | ||
96 | |||
97 | func procWatch() error { | ||
98 | ns, err := netlink.NewNetlinkSocket(syscall.NETLINK_CONNECTOR, 12345) | ||
99 | if err != nil { | ||
100 | fmt.Fprintf(os.Stderr, "Error creating socket: %v\n", err) | ||
101 | return err | ||
102 | } | ||
103 | defer ns.Close() | ||
104 | for { | ||
105 | res, err := ns.Receive(20) | ||
106 | if err != nil { | ||
107 | fmt.Fprintf(os.Stderr, "Error recv: %v\n", err) | ||
108 | continue | ||
109 | } | ||
110 | for i := 0; i < len(res); i++ { | ||
111 | procEvent := netlink.ParseProcEvent(res[i].Data) | ||
112 | switch procEvent.What { | ||
113 | case netlink.PROC_EVENT_FORK: | ||
114 | data := procEvent.Data.(netlink.ProcEventFork) | ||
115 | cooked := Event{ | ||
116 | tag: NEWPID, | ||
117 | timestamp: time.Now(), | ||
118 | pid: int(data.ChildPid), | ||
119 | tgid: int(data.ChildTgid), | ||
120 | ppid: int(data.ParentPid), | ||
121 | parentTgid: int(data.ParentTgid), | ||
122 | } | ||
123 | checkProc(&cooked) | ||
124 | cookedChan <- cooked | ||
125 | case netlink.PROC_EVENT_EXIT: | ||
126 | data := procEvent.Data.(netlink.ProcEventExit) | ||
127 | cooked := Event{ | ||
128 | tag: PIDEXIT, | ||
129 | timestamp: time.Now(), | ||
130 | pid: int(data.ProcessPid), | ||
131 | exit_code: int(data.ExitCode), | ||
132 | exit_signal: int(data.ExitSignal), | ||
133 | } | ||
134 | cookedChan <- cooked | ||
135 | default: | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | func checkProc(pCooked *Event) { | ||
142 | fileName := fmt.Sprintf("/proc/%d/task/%d/", pCooked.tgid, pCooked.pid) | ||
143 | fd, err := os.Open(fileName + "cmdline") | ||
144 | if err != nil { | ||
145 | fmt.Fprintf(os.Stderr, "Err: %v\n", err) | ||
146 | return | ||
147 | } | ||
148 | |||
149 | scanner := bufio.NewScanner(fd) | ||
150 | scanner.Split(bufio.ScanLines) | ||
151 | for scanner.Scan() { | ||
152 | line := scanner.Text() | ||
153 | pCooked.argv = append(pCooked.argv, strings.Split(line, "\x00")...) | ||
154 | } | ||
155 | pCooked.argc = len(pCooked.argv) | ||
156 | fd.Close() | ||
157 | |||
158 | fd, err = os.Open(fileName + "comm") | ||
159 | if err != nil { | ||
160 | fmt.Fprintf(os.Stderr, "Err: %v\n", err) | ||
161 | return | ||
162 | } | ||
163 | scanner = bufio.NewScanner(fd) | ||
164 | scanner.Split(bufio.ScanLines) | ||
165 | for scanner.Scan() { | ||
166 | line := scanner.Text() | ||
167 | pCooked.comm = line | ||
168 | } | ||
169 | fd.Close() | ||
170 | |||
171 | pCooked.cwd, err = os.Readlink(fileName + "cwd") | ||
172 | if err != nil { | ||
173 | fmt.Fprintf(os.Stderr, "Err: %v\n", err) | ||
174 | pCooked.cwd = "" | ||
175 | } | ||
176 | } | ||
diff --git a/listener/mongo.go b/listener/mongo.go new file mode 100644 index 0000000..a51350e --- /dev/null +++ b/listener/mongo.go | |||
@@ -0,0 +1,122 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "fmt" | ||
6 | "reflect" | ||
7 | "time" | ||
8 | |||
9 | "go.mongodb.org/mongo-driver/bson" | ||
10 | "go.mongodb.org/mongo-driver/mongo" | ||
11 | "go.mongodb.org/mongo-driver/mongo/options" | ||
12 | ) | ||
13 | |||
14 | type mongoClient struct { | ||
15 | dbName, colName string | ||
16 | client *mongo.Client | ||
17 | col *mongo.Collection | ||
18 | } | ||
19 | |||
20 | func (mc *mongoClient) init(dbName, colName string) error { | ||
21 | var err error | ||
22 | if err = mc.Connect(dbName, colName); err != nil { | ||
23 | return err | ||
24 | } | ||
25 | if err = mc.Drop(); err != nil { | ||
26 | return err | ||
27 | } | ||
28 | |||
29 | return nil | ||
30 | } | ||
31 | |||
32 | func (mc *mongoClient) Connect(dbName, colName string) error { | ||
33 | var err error | ||
34 | mc.client, err = mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017")) | ||
35 | |||
36 | if err != nil { | ||
37 | return err | ||
38 | } | ||
39 | |||
40 | ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) | ||
41 | err = mc.client.Connect(ctx) | ||
42 | if err != nil { | ||
43 | return err | ||
44 | } | ||
45 | |||
46 | mc.col = mc.client.Database(dbName).Collection(colName) | ||
47 | mc.dbName = dbName | ||
48 | mc.colName = colName | ||
49 | return nil | ||
50 | } | ||
51 | |||
52 | func (mc *mongoClient) InsertOne(document interface{}) error { | ||
53 | _, err := mc.col.InsertOne(context.Background(), document) | ||
54 | return err | ||
55 | } | ||
56 | |||
57 | func (mc *mongoClient) UpdateOne(filter, update interface{}) error { | ||
58 | _, err := mc.col.UpdateOne(context.Background(), filter, update) | ||
59 | return err | ||
60 | } | ||
61 | |||
62 | func (mc *mongoClient) UpdateMany(filter, update interface{}) error { | ||
63 | _, err := mc.col.UpdateMany(context.Background(), filter, update) | ||
64 | return err | ||
65 | } | ||
66 | |||
67 | func (mc *mongoClient) ReplaceOne(filter, new interface{}) error { | ||
68 | _, err := mc.col.ReplaceOne(context.Background(), filter, new) | ||
69 | return err | ||
70 | } | ||
71 | |||
72 | func (mc *mongoClient) Finddoc(filter bson.M, results interface{}) error { | ||
73 | sliceValue := reflect.ValueOf(results) | ||
74 | |||
75 | if sliceValue.Kind() != reflect.Ptr || sliceValue.Elem().Kind() != reflect.Slice { | ||
76 | return fmt.Errorf("Error: result argument must be pointer to slice") | ||
77 | } | ||
78 | cur, err := mc.col.Find(context.TODO(), filter) | ||
79 | if err != nil { | ||
80 | return err | ||
81 | } | ||
82 | defer cur.Close(context.TODO()) | ||
83 | |||
84 | elemType := sliceValue.Elem().Type().Elem() | ||
85 | |||
86 | sliceValue = sliceValue.Elem() | ||
87 | |||
88 | for cur.Next(context.TODO()) { | ||
89 | elem := reflect.New(elemType).Interface() | ||
90 | err := cur.Decode(elem) | ||
91 | if err != nil { | ||
92 | return err | ||
93 | } | ||
94 | sliceValue = reflect.Append(sliceValue, reflect.ValueOf(elem).Elem()) | ||
95 | } | ||
96 | |||
97 | reflect.ValueOf(results).Elem().Set(sliceValue) | ||
98 | return nil | ||
99 | } | ||
100 | |||
101 | func (mc *mongoClient) FindOneAndDelete(filter bson.M) (bson.M, error) { | ||
102 | res := mc.col.FindOneAndDelete(context.Background(), filter) | ||
103 | var result bson.M | ||
104 | err := res.Decode(&result) | ||
105 | return result, err | ||
106 | } | ||
107 | |||
108 | func (mc *mongoClient) Drop() error { | ||
109 | return mc.col.Drop(context.Background()) | ||
110 | } | ||
111 | |||
112 | func (mc *mongoClient) Disconnect() error { | ||
113 | err := mc.client.Disconnect(context.Background()) | ||
114 | if err != nil { | ||
115 | return err | ||
116 | } | ||
117 | mc.col = nil | ||
118 | mc.client = nil | ||
119 | mc.dbName = "" | ||
120 | mc.colName = "" | ||
121 | return nil | ||
122 | } | ||
diff --git a/listener/netlink b/listener/netlink new file mode 160000 | |||
Subproject e53c2724725c5991cdd9ea088c26832c5c9fcf0 | |||
diff --git a/listener/organize.go b/listener/organize.go new file mode 100644 index 0000000..0c05eb4 --- /dev/null +++ b/listener/organize.go | |||
@@ -0,0 +1,291 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "os" | ||
6 | "regexp" | ||
7 | "strconv" | ||
8 | "strings" | ||
9 | "sync" | ||
10 | "syscall" | ||
11 | |||
12 | "github.com/elastic/go-libaudit/v2" | ||
13 | "github.com/elastic/go-libaudit/v2/auparse" | ||
14 | ) | ||
15 | |||
16 | // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的 | ||
17 | var eventTable sync.Map | ||
18 | |||
19 | // 事件信息 | ||
20 | var tmp any | ||
21 | var ok bool | ||
22 | var event Event | ||
23 | var pEvent *Event | ||
24 | var eventId, argc int | ||
25 | |||
26 | // 要用的正则匹配列表 | ||
27 | var ( | ||
28 | syscallRegex = regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+)(?:.*?exit=([-+]?\d+))?.*?ppid=(\d+) pid=(\d+).*?subj=(.*?):(.*?):(.*?):(.*?) .*?$`) | ||
29 | execveRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`) | ||
30 | argsRegex = regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`) | ||
31 | pathRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): item=(\d+) name="(.*?)" .*objtype=([A-Z]+) `) | ||
32 | cwdRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`) | ||
33 | proctitleRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`) | ||
34 | eoeRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`) | ||
35 | ) | ||
36 | |||
37 | func orgnaze() { | ||
38 | defer wg.Done() | ||
39 | defer close(cookedChan) | ||
40 | // 接收信息 | ||
41 | var raw interface{} | ||
42 | var rawEvent libaudit.RawAuditMessage | ||
43 | |||
44 | for { | ||
45 | raw, ok = <-rawChan | ||
46 | if !ok { | ||
47 | break | ||
48 | } | ||
49 | rawEvent = raw.(libaudit.RawAuditMessage) | ||
50 | // fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data) | ||
51 | |||
52 | switch rawEvent.Type { | ||
53 | case auparse.AUDIT_SYSCALL: | ||
54 | syscallRaw(rawEvent) | ||
55 | case auparse.AUDIT_EXECVE: | ||
56 | execve(rawEvent) | ||
57 | case auparse.AUDIT_CWD: | ||
58 | cwd(rawEvent) | ||
59 | case auparse.AUDIT_PATH: | ||
60 | path(rawEvent) | ||
61 | case auparse.AUDIT_PROCTITLE: | ||
62 | proctitle(rawEvent) | ||
63 | case auparse.AUDIT_EOE: | ||
64 | eoe(rawEvent) | ||
65 | default: | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | func syscallRaw(rawEvent libaudit.RawAuditMessage) { | ||
71 | if !syscallRegex.Match(rawEvent.Data) { | ||
72 | return | ||
73 | } | ||
74 | |||
75 | var exit int | ||
76 | var a [4]uint64 | ||
77 | var subj [4]string | ||
78 | // 捕获基础信息 | ||
79 | match := syscallRegex.FindSubmatch(rawEvent.Data) | ||
80 | event.timestamp, _ = getTimeFromStr(string(match[1])) | ||
81 | eventId, _ = strconv.Atoi(string(match[2])) | ||
82 | event.syscall, _ = strconv.Atoi(string(match[3])) | ||
83 | if string(match[4]) == "" { | ||
84 | // exit没捕获到 | ||
85 | exit = 0 | ||
86 | } else { | ||
87 | exit, _ = strconv.Atoi(string(match[4])) | ||
88 | } | ||
89 | event.ppid, _ = strconv.Atoi(string(match[5])) | ||
90 | event.pid, _ = strconv.Atoi(string(match[6])) | ||
91 | |||
92 | // 几个subj,说不定会有用 | ||
93 | for i := 0; i < 4; i++ { | ||
94 | subj[i] = string(match[7+i]) | ||
95 | } | ||
96 | |||
97 | // 捕获参数 | ||
98 | if !argsRegex.Match(rawEvent.Data) { | ||
99 | fmt.Fprintf(os.Stderr, "Error: don't get args in syscall event!\n") | ||
100 | return | ||
101 | } | ||
102 | argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1) | ||
103 | for i := 0; i < 4; i++ { | ||
104 | a[i], _ = strconv.ParseUint(string(argsMatch[i][3]), 16, 64) | ||
105 | } | ||
106 | |||
107 | switch syscallTable[event.syscall] { | ||
108 | case "execve": | ||
109 | eventTable.Store(eventId, &Event{ | ||
110 | tag: EXECVE, | ||
111 | timestamp: event.timestamp, | ||
112 | syscall: event.syscall, | ||
113 | // exit_code: a[0], // 为啥这么写? | ||
114 | ppid: event.ppid, | ||
115 | pid: event.pid, | ||
116 | argc: 0, | ||
117 | argv: make([]string, 0), | ||
118 | cwd: "", | ||
119 | }) | ||
120 | case "open": | ||
121 | // 检查打开的权限 | ||
122 | if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 { | ||
123 | break | ||
124 | } | ||
125 | // TRUNC应该被直接标记为改变,而不是打开 | ||
126 | eventTable.Store(eventId, &Event{ | ||
127 | tag: FILEOPEN, | ||
128 | timestamp: event.timestamp, | ||
129 | syscall: event.syscall, | ||
130 | exit_code: exit, | ||
131 | ppid: event.ppid, | ||
132 | pid: event.pid, | ||
133 | argc: 0, | ||
134 | argv: make([]string, 0), | ||
135 | cwd: "", | ||
136 | syscallParam: a, | ||
137 | srcPath: "", | ||
138 | }) | ||
139 | case "write": | ||
140 | eventTable.Store(eventId, &Event{ | ||
141 | tag: FILEWRITE, | ||
142 | timestamp: event.timestamp, | ||
143 | syscall: event.syscall, | ||
144 | exit_code: exit, | ||
145 | ppid: event.ppid, | ||
146 | pid: event.pid, | ||
147 | argc: 0, | ||
148 | argv: make([]string, 0), | ||
149 | cwd: "", | ||
150 | syscallParam: a, | ||
151 | }) | ||
152 | case "close": | ||
153 | // 文件关闭 | ||
154 | eventTable.Store(eventId, &Event{ | ||
155 | tag: FILECLOSE, | ||
156 | timestamp: event.timestamp, | ||
157 | syscall: event.syscall, | ||
158 | exit_code: exit, | ||
159 | ppid: event.ppid, | ||
160 | pid: event.pid, | ||
161 | argc: 0, | ||
162 | argv: make([]string, 0), | ||
163 | cwd: "", | ||
164 | syscallParam: a, | ||
165 | }) | ||
166 | case "pivot_root": | ||
167 | if subj[2] == "container_runtime_t" { | ||
168 | eventTable.Store(eventId, &Event{ | ||
169 | tag: PIVOTROOT, | ||
170 | timestamp: event.timestamp, | ||
171 | syscall: event.syscall, | ||
172 | ppid: event.ppid, | ||
173 | pid: event.pid, | ||
174 | syscallParam: a, | ||
175 | }) | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | func execve(rawEvent libaudit.RawAuditMessage) { | ||
181 | if !execveRegex.Match(rawEvent.Data) { | ||
182 | return | ||
183 | } | ||
184 | |||
185 | match := execveRegex.FindSubmatch(rawEvent.Data) | ||
186 | eventId, _ = strconv.Atoi(string(match[1])) | ||
187 | argc, _ = strconv.Atoi(string(match[2])) | ||
188 | tmp, ok = eventTable.Load(eventId) | ||
189 | if !ok { | ||
190 | return | ||
191 | } | ||
192 | pEvent = tmp.(*Event) | ||
193 | if argsRegex.Match(rawEvent.Data) { | ||
194 | match := argsRegex.FindAllSubmatch(rawEvent.Data, -1) | ||
195 | for i := 0; i < argc; i++ { | ||
196 | if len(match[i][2]) == 0 { | ||
197 | // 代表着匹配到的是十六进制数 | ||
198 | str := hexToAscii(string(match[i][3])) | ||
199 | pEvent.argv = append(pEvent.argv, str) | ||
200 | } else { | ||
201 | pEvent.argv = append(pEvent.argv, string(match[i][2])) | ||
202 | } | ||
203 | } | ||
204 | pEvent.argc = argc | ||
205 | } | ||
206 | } | ||
207 | |||
208 | func cwd(rawEvent libaudit.RawAuditMessage) { | ||
209 | if !cwdRegex.Match(rawEvent.Data) { | ||
210 | return | ||
211 | } | ||
212 | |||
213 | match := cwdRegex.FindSubmatch(rawEvent.Data) | ||
214 | eventId, _ = strconv.Atoi(string(match[1])) | ||
215 | tmp, ok = eventTable.Load(eventId) | ||
216 | if !ok { | ||
217 | return | ||
218 | } | ||
219 | tmp.(*Event).cwd = string(match[2]) | ||
220 | } | ||
221 | |||
222 | func proctitle(rawEvent libaudit.RawAuditMessage) { | ||
223 | if !proctitleRegex.Match(rawEvent.Data) { | ||
224 | return | ||
225 | } | ||
226 | |||
227 | var cmdline string | ||
228 | match := proctitleRegex.FindSubmatch(rawEvent.Data) | ||
229 | eventId, _ = strconv.Atoi(string(match[1])) | ||
230 | tmp, ok = eventTable.Load(eventId) | ||
231 | if !ok { | ||
232 | return | ||
233 | } | ||
234 | pEvent = tmp.(*Event) | ||
235 | if pEvent.argc == 0 { | ||
236 | // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数 | ||
237 | if match[3] == nil { | ||
238 | // PROCTITLE写的是十六进制,转换为字符串 | ||
239 | cmdline = hexToAscii(string(match[4])) | ||
240 | } else { | ||
241 | cmdline = string(match[3]) | ||
242 | } | ||
243 | pEvent.argv = strings.Split(cmdline, " ") | ||
244 | pEvent.argc = len(pEvent.argv) | ||
245 | } | ||
246 | } | ||
247 | |||
248 | func eoe(rawEvent libaudit.RawAuditMessage) { | ||
249 | if !eoeRegex.Match(rawEvent.Data) { | ||
250 | return | ||
251 | } | ||
252 | |||
253 | match := eoeRegex.FindSubmatch(rawEvent.Data) | ||
254 | eventId, _ = strconv.Atoi(string(match[1])) | ||
255 | tmp, ok = eventTable.Load(eventId) | ||
256 | if !ok { | ||
257 | return | ||
258 | } | ||
259 | cooked := *(tmp.(*Event)) | ||
260 | cookedChan <- cooked | ||
261 | eventTable.Delete(eventId) // 死人别占地 | ||
262 | } | ||
263 | |||
264 | func path(rawEvent libaudit.RawAuditMessage) { | ||
265 | if !pathRegex.Match(rawEvent.Data) { | ||
266 | return | ||
267 | } | ||
268 | match := pathRegex.FindSubmatch(rawEvent.Data) | ||
269 | eventId, _ = strconv.Atoi(string(match[1])) | ||
270 | // item, _ := strconv.Atoi(string(match[2])) | ||
271 | name := string(match[3]) | ||
272 | objtype := string(match[4]) | ||
273 | |||
274 | tmp, ok = eventTable.Load(eventId) | ||
275 | if !ok { | ||
276 | return | ||
277 | } | ||
278 | pEvent = tmp.(*Event) | ||
279 | |||
280 | // 先看看是不是文件操作,再看是不是所在目录 | ||
281 | if pEvent.tag != FILEOPEN || objtype == "PARENT" { | ||
282 | return | ||
283 | } | ||
284 | |||
285 | if pEvent.cwd == "/" || name[0] == '/' { | ||
286 | pEvent.srcPath = name | ||
287 | } else { | ||
288 | pEvent.srcPath = pEvent.cwd + "/" + name | ||
289 | } | ||
290 | // ATTENTION: 这里需要做路径简化,留给过滤清洗流程吧 | ||
291 | } | ||
diff --git a/listener/receive.go b/listener/receive.go new file mode 100644 index 0000000..c0dea00 --- /dev/null +++ b/listener/receive.go | |||
@@ -0,0 +1,29 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/elastic/go-libaudit/v2" | ||
7 | "github.com/elastic/go-libaudit/v2/auparse" | ||
8 | "github.com/mohae/deepcopy" | ||
9 | ) | ||
10 | |||
11 | func receive(r *libaudit.AuditClient) error { | ||
12 | defer wg.Done() | ||
13 | defer close(rawChan) | ||
14 | for { | ||
15 | rawEvent, err := r.Receive(false) | ||
16 | if err != nil { | ||
17 | return fmt.Errorf("receive failed: %w", err) | ||
18 | } | ||
19 | |||
20 | // Messages from 1300-2999 are valid audit messages. | ||
21 | if rawEvent.Type < auparse.AUDIT_USER_AUTH || | ||
22 | rawEvent.Type > auparse.AUDIT_LAST_USER_MSG2 { | ||
23 | continue | ||
24 | } | ||
25 | |||
26 | rawEventMessage := deepcopy.Copy(*rawEvent) | ||
27 | rawChan <- rawEventMessage | ||
28 | } | ||
29 | } | ||