diff options
author | We-unite <3205135446@qq.com> | 2024-07-19 17:02:11 +0800 |
---|---|---|
committer | We-unite <3205135446@qq.com> | 2024-07-19 17:05:29 +0800 |
commit | 7cf8e470471d30fc821a8be350dcb97dc64e5add (patch) | |
tree | e747afbc698bba079047f17674eef260a4c6733d | |
parent | 2e37dcf708be08dff963ee08cb7a9557f32e690d (diff) | |
download | godo-7cf8e470471d30fc821a8be350dcb97dc64e5add.tar.gz godo-7cf8e470471d30fc821a8be350dcb97dc64e5add.zip |
Depart the whole program into several files.
Put all the src code in only one file is to ugly, so devide it!
and mv them into src dir to keep the whole repo clear.
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | godo.go | 535 | ||||
-rw-r--r-- | src/audit.go | 84 | ||||
-rw-r--r-- | src/basefunc.go | 115 | ||||
-rw-r--r-- | src/deal.go | 97 | ||||
-rw-r--r-- | src/global.go | 10 | ||||
-rw-r--r-- | src/go.mod (renamed from go.mod) | 0 | ||||
-rw-r--r-- | src/go.sum (renamed from go.sum) | 0 | ||||
-rw-r--r-- | src/godo.go | 116 | ||||
-rw-r--r-- | src/organize.go | 124 | ||||
-rw-r--r-- | src/receive.go | 29 |
11 files changed, 580 insertions, 535 deletions
@@ -1,3 +1,8 @@ | |||
1 | .vscode/* | 1 | .vscode/* |
2 | godo | 2 | godo |
3 | 3 | ||
4 | old/* | ||
5 | !old/*.* | ||
6 | old/*.log | ||
7 | old/*.json | ||
8 | old/go.* \ No newline at end of file | ||
diff --git a/godo.go b/godo.go deleted file mode 100644 index 1ae336b..0000000 --- a/godo.go +++ /dev/null | |||
@@ -1,535 +0,0 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "flag" | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "log" | ||
9 | "os" | ||
10 | "os/exec" | ||
11 | "path/filepath" | ||
12 | "regexp" | ||
13 | "strconv" | ||
14 | "strings" | ||
15 | "sync" | ||
16 | "time" | ||
17 | |||
18 | "github.com/elastic/go-libaudit/v2" | ||
19 | "github.com/elastic/go-libaudit/v2/auparse" | ||
20 | "github.com/mohae/deepcopy" | ||
21 | ) | ||
22 | |||
23 | var ( | ||
24 | fs = flag.NewFlagSet("audit", flag.ExitOnError) | ||
25 | diag = fs.String("diag", "", "dump raw information from kernel to file") | ||
26 | rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)") | ||
27 | backlog = fs.Uint("backlog", 8192, "backlog limit") | ||
28 | immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)") | ||
29 | receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+") | ||
30 | ) | ||
31 | |||
32 | type Event struct { | ||
33 | timestamp time.Time | ||
34 | pid, ppid int | ||
35 | syscall int | ||
36 | argc int | ||
37 | argv []string | ||
38 | cwd string | ||
39 | } | ||
40 | |||
41 | type process struct { | ||
42 | timestamp time.Time | ||
43 | pid, ppid int | ||
44 | argv []string | ||
45 | cwd string | ||
46 | rootfs string | ||
47 | children []int | ||
48 | } | ||
49 | |||
50 | var pids sync.Map // 古希腊掌管进程的神,int->*process | ||
51 | var wg sync.WaitGroup // 掌管协程 | ||
52 | var rawChan chan interface{} // 从接收到整理的管道 | ||
53 | var cookedChan chan Event // 整理好的信息的管道 | ||
54 | var syscallTable [500]string //记录一下系统调用 | ||
55 | |||
56 | var containerdPid int | ||
57 | |||
58 | func main() { | ||
59 | // 检查用户身份,并添加auditd规则,监听所有syscall | ||
60 | if os.Geteuid() != 0 { | ||
61 | fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid()) | ||
62 | return | ||
63 | } | ||
64 | |||
65 | // 所有的系统调用号与名称的关系 | ||
66 | err := figureOutSyscalls() | ||
67 | if err != nil { | ||
68 | fmt.Printf("Error figuring out syscall numbers: %v\n", err) | ||
69 | } | ||
70 | |||
71 | syscall := [6]string{"fork", "vfork", "clone", "execve", "exit", "exit_group"} | ||
72 | var auditCmd *exec.Cmd | ||
73 | auditCmd = exec.Command("auditctl", "-D") // 清空所有规则 | ||
74 | auditCmd.Run() | ||
75 | // 设置监听规则 | ||
76 | for i := 0; i < len(syscall); i++ { | ||
77 | auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", syscall[i]) | ||
78 | auditCmd.Run() | ||
79 | } | ||
80 | |||
81 | // 查找pid | ||
82 | containerdPid, err = getPid() | ||
83 | if err != nil { | ||
84 | fmt.Printf("Error finding containerd: %v\n", err) | ||
85 | return | ||
86 | } | ||
87 | // 数据结构初始化 | ||
88 | // pids = make(map[int]*process) | ||
89 | // containers = make(map[string]int) | ||
90 | |||
91 | // 创世之神,1号进程 | ||
92 | // pids[1] = &process{rootfs: "/", children: make([]int, 0)} | ||
93 | // pids[1].children = append(pids[1].children, containerdPid) | ||
94 | // 1号进程还是不要在进程树上直接出现了,不然它的小儿子们都会出现 | ||
95 | |||
96 | // /usr/bin/containerd,也就是我们最关注的进程 | ||
97 | // pids[containerdPid] = &process{rootfs: "/", children: make([]int, 0)} | ||
98 | pids.Store(containerdPid, &process{ | ||
99 | ppid: 1, | ||
100 | pid: containerdPid, | ||
101 | argv: make([]string, 0), | ||
102 | cwd: "/", | ||
103 | rootfs: "/", | ||
104 | children: make([]int, 0), | ||
105 | }) | ||
106 | p, ok := pids.Load(containerdPid) | ||
107 | if !ok { | ||
108 | fmt.Printf("???\n") | ||
109 | return | ||
110 | } | ||
111 | p.(*process).argv = append(p.(*process).argv, "/usr/bin/containerd") | ||
112 | |||
113 | // 开始运行,解析命令行参数后监听 | ||
114 | if err := fs.Parse(os.Args[1:]); err != nil { | ||
115 | log.Fatal(err) | ||
116 | } | ||
117 | |||
118 | if err := read(); err != nil { | ||
119 | log.Fatalf("error: %v", err) | ||
120 | } | ||
121 | } | ||
122 | |||
123 | func figureOutSyscalls() error { | ||
124 | NRRegex := regexp.MustCompile(`#define __NR_(.*?) (\d+)$`) | ||
125 | file, err := os.Open("/usr/include/asm/unistd_64.h") | ||
126 | if err != nil { | ||
127 | return err | ||
128 | } | ||
129 | defer file.Close() | ||
130 | |||
131 | scanner := bufio.NewScanner(file) | ||
132 | for scanner.Scan() { | ||
133 | line := scanner.Text() | ||
134 | if NRRegex.MatchString(line) { | ||
135 | match := NRRegex.FindStringSubmatch(line) | ||
136 | num, err := strconv.Atoi(match[2]) | ||
137 | if err != nil { | ||
138 | return err | ||
139 | } | ||
140 | syscallTable[num] = match[1] | ||
141 | } | ||
142 | } | ||
143 | return nil | ||
144 | } | ||
145 | |||
146 | func getPid() (int, error) { | ||
147 | // 指定要搜索的关键词 | ||
148 | keyword := "/usr/bin/containerd" | ||
149 | |||
150 | // 获取/proc目录下的所有子目录 | ||
151 | procDir, err := filepath.Glob("/proc/*") | ||
152 | if err != nil { | ||
153 | return 0, err | ||
154 | } | ||
155 | |||
156 | // 遍历子目录,查找包含关键词的进程 | ||
157 | for _, dir := range procDir { | ||
158 | pid, err := strconv.Atoi(filepath.Base(dir)) | ||
159 | if err != nil { | ||
160 | continue // 跳过非PID的目录 | ||
161 | } | ||
162 | |||
163 | // 检查进程是否包含关键词 | ||
164 | if containsKeyword(pid, keyword) { | ||
165 | return pid, nil | ||
166 | } | ||
167 | } | ||
168 | err = fmt.Errorf("Error: no containerd process found.") | ||
169 | return 0, err | ||
170 | } | ||
171 | |||
172 | func containsKeyword(pid int, keyword string) bool { | ||
173 | // 构造完整的进程命令路径 | ||
174 | cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid) | ||
175 | |||
176 | // 打开文件 | ||
177 | file, err := os.Open(cmdPath) | ||
178 | if err != nil { | ||
179 | return false | ||
180 | } | ||
181 | defer file.Close() | ||
182 | |||
183 | // 读取文件内容 | ||
184 | scanner := bufio.NewScanner(file) | ||
185 | scanner.Split(bufio.ScanLines) | ||
186 | for scanner.Scan() { | ||
187 | line := scanner.Text() | ||
188 | if strings.Contains(line, keyword) { | ||
189 | return true | ||
190 | } | ||
191 | } | ||
192 | return false | ||
193 | } | ||
194 | |||
195 | func getTimeFromStr(timeStr string) (time.Time, error) { | ||
196 | timestampFloat, err := strconv.ParseFloat(timeStr, 64) | ||
197 | if err != nil { | ||
198 | return time.Unix(0, 0), err | ||
199 | } | ||
200 | secs := int64(timestampFloat) | ||
201 | nsecs := int64((timestampFloat - float64(secs)) * 1e9) | ||
202 | |||
203 | // 只精确到毫秒就够了 | ||
204 | t := time.Unix(secs, nsecs).Truncate(time.Millisecond) | ||
205 | return t, nil | ||
206 | } | ||
207 | |||
208 | func hexToAscii(hexString string) string { | ||
209 | bytes := []byte{} | ||
210 | for i := 0; i < len(hexString); i += 2 { | ||
211 | hexPair := hexString[i : i+2] | ||
212 | // 将十六进制数转换为十进制数 | ||
213 | decimal, err := strconv.ParseInt(hexPair, 16, 8) | ||
214 | if err != nil { | ||
215 | return "Invalid hex string" | ||
216 | } | ||
217 | char := byte(decimal) | ||
218 | bytes = append(bytes, char) | ||
219 | } | ||
220 | |||
221 | asciiString := strings.ReplaceAll(string(bytes), "\000", " ") | ||
222 | |||
223 | return asciiString | ||
224 | } | ||
225 | |||
226 | func read() error { | ||
227 | // Write netlink response to a file for further analysis or for writing | ||
228 | // tests cases. | ||
229 | var diagWriter io.Writer | ||
230 | if *diag != "" { | ||
231 | f, err := os.OpenFile(*diag, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o600) | ||
232 | if err != nil { | ||
233 | return err | ||
234 | } | ||
235 | defer f.Close() | ||
236 | diagWriter = f | ||
237 | } | ||
238 | |||
239 | log.Println("starting netlink client") | ||
240 | |||
241 | var err error | ||
242 | var client *libaudit.AuditClient | ||
243 | if *receiveOnly { | ||
244 | client, err = libaudit.NewMulticastAuditClient(diagWriter) | ||
245 | if err != nil { | ||
246 | return fmt.Errorf("failed to create receive-only audit client: %w", err) | ||
247 | } | ||
248 | defer client.Close() | ||
249 | } else { | ||
250 | client, err = libaudit.NewAuditClient(diagWriter) | ||
251 | if err != nil { | ||
252 | return fmt.Errorf("failed to create audit client: %w", err) | ||
253 | } | ||
254 | defer client.Close() | ||
255 | |||
256 | status, err := client.GetStatus() | ||
257 | if err != nil { | ||
258 | return fmt.Errorf("failed to get audit status: %w", err) | ||
259 | } | ||
260 | log.Printf("received audit status=%+v", status) | ||
261 | |||
262 | if status.Enabled == 0 { | ||
263 | log.Println("enabling auditing in the kernel") | ||
264 | if err = client.SetEnabled(true, libaudit.WaitForReply); err != nil { | ||
265 | return fmt.Errorf("failed to set enabled=true: %w", err) | ||
266 | } | ||
267 | } | ||
268 | |||
269 | if status.RateLimit != uint32(*rate) { | ||
270 | log.Printf("setting rate limit in kernel to %v", *rate) | ||
271 | if err = client.SetRateLimit(uint32(*rate), libaudit.NoWait); err != nil { | ||
272 | return fmt.Errorf("failed to set rate limit to unlimited: %w", err) | ||
273 | } | ||
274 | } | ||
275 | |||
276 | if status.BacklogLimit != uint32(*backlog) { | ||
277 | log.Printf("setting backlog limit in kernel to %v", *backlog) | ||
278 | if err = client.SetBacklogLimit(uint32(*backlog), libaudit.NoWait); err != nil { | ||
279 | return fmt.Errorf("failed to set backlog limit: %w", err) | ||
280 | } | ||
281 | } | ||
282 | |||
283 | if status.Enabled != 2 && *immutable { | ||
284 | log.Printf("setting kernel settings as immutable") | ||
285 | if err = client.SetImmutable(libaudit.NoWait); err != nil { | ||
286 | return fmt.Errorf("failed to set kernel as immutable: %w", err) | ||
287 | } | ||
288 | } | ||
289 | |||
290 | log.Printf("sending message to kernel registering our PID (%v) as the audit daemon", os.Getpid()) | ||
291 | if err = client.SetPID(libaudit.NoWait); err != nil { | ||
292 | return fmt.Errorf("failed to set audit PID: %w", err) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | // 各协程至此开始 | ||
297 | // return receive(client) | ||
298 | rawChan = make(chan interface{}) | ||
299 | cookedChan = make(chan Event) | ||
300 | wg.Add(1) | ||
301 | go receive(client) | ||
302 | wg.Add(1) | ||
303 | go orgnaze() | ||
304 | wg.Add(1) | ||
305 | go deal() | ||
306 | |||
307 | wg.Wait() | ||
308 | time.Sleep(2 * time.Second) | ||
309 | return nil | ||
310 | } | ||
311 | |||
312 | func receive(r *libaudit.AuditClient) error { | ||
313 | defer wg.Done() | ||
314 | defer close(rawChan) | ||
315 | for { | ||
316 | rawEvent, err := r.Receive(false) | ||
317 | if err != nil { | ||
318 | return fmt.Errorf("receive failed: %w", err) | ||
319 | } | ||
320 | |||
321 | // Messages from 1300-2999 are valid audit messages. | ||
322 | if rawEvent.Type < auparse.AUDIT_USER_AUTH || | ||
323 | rawEvent.Type > auparse.AUDIT_LAST_USER_MSG2 { | ||
324 | continue | ||
325 | } | ||
326 | |||
327 | rawEventMessage := deepcopy.Copy(*rawEvent) | ||
328 | rawChan <- rawEventMessage | ||
329 | } | ||
330 | } | ||
331 | |||
332 | func orgnaze() { | ||
333 | defer wg.Done() | ||
334 | defer close(cookedChan) | ||
335 | // 接收信息 | ||
336 | var raw interface{} | ||
337 | var ok bool | ||
338 | var rawEvent libaudit.RawAuditMessage | ||
339 | // 事件信息 | ||
340 | var eventId, argc int | ||
341 | var err [6]error | ||
342 | var event, cooked Event | ||
343 | // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的 | ||
344 | eventTable := make(map[int]*Event) | ||
345 | // 要用的正则匹配列表 | ||
346 | syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?ppid=(\d+) pid=(\d+).*?$`) | ||
347 | execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`) | ||
348 | argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`) | ||
349 | cwdRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`) | ||
350 | proctitleRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`) | ||
351 | eoeRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`) | ||
352 | for { | ||
353 | raw, ok = <-rawChan | ||
354 | if !ok { | ||
355 | break | ||
356 | } | ||
357 | rawEvent = raw.(libaudit.RawAuditMessage) | ||
358 | |||
359 | // type Event struct { | ||
360 | // timestamp time.Time | ||
361 | // pid, ppid int | ||
362 | // syscall int | ||
363 | // argc int | ||
364 | // args []string | ||
365 | // cwd string | ||
366 | // } | ||
367 | switch rawEvent.Type { | ||
368 | case auparse.AUDIT_SYSCALL: | ||
369 | if syscallRegex.Match(rawEvent.Data) { | ||
370 | match := syscallRegex.FindSubmatch(rawEvent.Data) | ||
371 | event.timestamp, err[0] = getTimeFromStr(string(match[1])) | ||
372 | eventId, err[1] = strconv.Atoi(string(match[2])) | ||
373 | event.syscall, err[2] = strconv.Atoi(string(match[3])) | ||
374 | event.ppid, err[3] = strconv.Atoi(string(match[4])) | ||
375 | event.pid, err[4] = strconv.Atoi(string(match[5])) | ||
376 | eventTable[eventId] = &Event{ | ||
377 | timestamp: event.timestamp, | ||
378 | syscall: event.syscall, | ||
379 | ppid: event.ppid, | ||
380 | pid: event.pid, | ||
381 | argc: 0, | ||
382 | argv: make([]string, 0), | ||
383 | cwd: "", | ||
384 | } | ||
385 | } | ||
386 | case auparse.AUDIT_EXECVE: | ||
387 | if execveRegex.Match(rawEvent.Data) { | ||
388 | match := execveRegex.FindSubmatch(rawEvent.Data) | ||
389 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
390 | argc, err[1] = strconv.Atoi(string(match[2])) | ||
391 | if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) { | ||
392 | match := argsRegex.FindAllSubmatch(rawEvent.Data, -1) | ||
393 | for i := 0; i < argc; i++ { | ||
394 | if len(match[i][2]) == 0 { | ||
395 | // 代表着匹配到的是十六进制数 | ||
396 | str := hexToAscii(string(match[i][3])) | ||
397 | eventTable[eventId].argv = append(eventTable[eventId].argv, str) | ||
398 | } else { | ||
399 | eventTable[eventId].argv = append(eventTable[eventId].argv, string(match[i][2])) | ||
400 | } | ||
401 | } | ||
402 | eventTable[eventId].argc = argc | ||
403 | } | ||
404 | } | ||
405 | // case auparse.AUDIT_PATH: | ||
406 | case auparse.AUDIT_CWD: | ||
407 | if cwdRegex.Match(rawEvent.Data) { | ||
408 | match := cwdRegex.FindSubmatch(rawEvent.Data) | ||
409 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
410 | eventTable[eventId].cwd = string(match[2]) | ||
411 | } | ||
412 | case auparse.AUDIT_PROCTITLE: | ||
413 | if proctitleRegex.Match(rawEvent.Data) { | ||
414 | var cmdline string | ||
415 | var pEvent *Event | ||
416 | match := proctitleRegex.FindSubmatch(rawEvent.Data) | ||
417 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
418 | pEvent = eventTable[eventId] | ||
419 | if pEvent.argc == 0 { | ||
420 | // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数 | ||
421 | if match[3] == nil { | ||
422 | // PROCTITLE写的是十六进制,转换为字符串 | ||
423 | cmdline = hexToAscii(string(match[4])) | ||
424 | } else { | ||
425 | cmdline = string(match[3]) | ||
426 | } | ||
427 | pEvent.argv = strings.Split(cmdline, " ") | ||
428 | pEvent.argc = len(eventTable[eventId].argv) | ||
429 | } | ||
430 | } | ||
431 | case auparse.AUDIT_EOE: | ||
432 | if eoeRegex.Match(rawEvent.Data) { | ||
433 | match := eoeRegex.FindSubmatch(rawEvent.Data) | ||
434 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
435 | // ATTENTION: 事件整理完毕,即刻发出,是否合理呢? | ||
436 | cooked = *eventTable[eventId] // 应当采用深拷贝吗?有待实验 | ||
437 | cookedChan <- cooked | ||
438 | delete(eventTable, eventId) //发出之后就从信息表扔掉,死人别占地 | ||
439 | } | ||
440 | default: | ||
441 | // ATTENTION: 这里也需要做防护 | ||
442 | } | ||
443 | } | ||
444 | } | ||
445 | |||
446 | func deal() { | ||
447 | defer wg.Done() | ||
448 | var cooked Event | ||
449 | var ok bool | ||
450 | for { | ||
451 | cooked, ok = <-cookedChan | ||
452 | if !ok { | ||
453 | break | ||
454 | } | ||
455 | // type Event struct { | ||
456 | // timestamp time.Time | ||
457 | // pid, ppid int | ||
458 | // syscall int | ||
459 | // argc int | ||
460 | // args []string | ||
461 | // cwd string | ||
462 | // } | ||
463 | // type process struct { | ||
464 | // timestamp time.Time | ||
465 | // pid, ppid int | ||
466 | // argv []string | ||
467 | // cwd string | ||
468 | // rootfs string | ||
469 | // children []int | ||
470 | // } | ||
471 | switch syscallTable[cooked.syscall] { | ||
472 | case "fork", "vfork", "clone": | ||
473 | ppid := cooked.ppid | ||
474 | pid := cooked.pid | ||
475 | parent, ok := pids.Load(ppid) | ||
476 | if !ok { | ||
477 | break | ||
478 | } | ||
479 | parent.(*process).children = append(parent.(*process).children, pid) | ||
480 | pids.Store(pid, &process{ | ||
481 | timestamp: cooked.timestamp, | ||
482 | pid: cooked.pid, | ||
483 | ppid: cooked.ppid, | ||
484 | argv: cooked.argv, | ||
485 | cwd: cooked.cwd, | ||
486 | children: make([]int, 0), | ||
487 | }) | ||
488 | fmt.Printf("%v syscall=%d, ppid=%d, pid=%d, cwd=\"%s\", argc=%d, ", cooked.timestamp, cooked.syscall, cooked.ppid, cooked.pid, cooked.cwd, cooked.argc) | ||
489 | for i := 0; i < cooked.argc; i++ { | ||
490 | fmt.Printf("arg[%d]=\"%s\", ", i, cooked.argv[i]) | ||
491 | } | ||
492 | fmt.Printf("\n") | ||
493 | case "exit", "exit_group": | ||
494 | _, ok := pids.Load(cooked.pid) | ||
495 | if !ok { | ||
496 | break | ||
497 | } | ||
498 | go deletePid(cooked) | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | func deletePid(cooked Event) { | ||
504 | time.Sleep(1 * time.Second) | ||
505 | Process, ok := pids.Load(cooked.pid) | ||
506 | if !ok { | ||
507 | return | ||
508 | } | ||
509 | pProcess := Process.(*process) | ||
510 | |||
511 | // 先从爹那里注销户籍 | ||
512 | parent, ok := pids.Load(pProcess.ppid) | ||
513 | if ok { | ||
514 | pParent := parent.(*process) | ||
515 | for i, child := range pParent.children { | ||
516 | if child == pProcess.pid { | ||
517 | pParent.children = append(pParent.children[:i], pParent.children[i+1:]...) | ||
518 | break | ||
519 | } | ||
520 | } | ||
521 | } | ||
522 | |||
523 | // 子进程需要收容 | ||
524 | for i := 0; i < len(pProcess.children); i++ { | ||
525 | child, ok := pids.Load(pProcess.children[i]) | ||
526 | if ok { | ||
527 | child.(*process).ppid = 1 | ||
528 | } | ||
529 | } | ||
530 | |||
531 | // 可以去死了 | ||
532 | pids.Delete(cooked.pid) | ||
533 | _, ok = pids.Load(cooked.pid) | ||
534 | fmt.Printf("%v Goodbye, %d! ok = %v\n", time.Now(), cooked.pid, ok) | ||
535 | } | ||
diff --git a/src/audit.go b/src/audit.go new file mode 100644 index 0000000..ed48691 --- /dev/null +++ b/src/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/src/basefunc.go b/src/basefunc.go new file mode 100644 index 0000000..5fff3e8 --- /dev/null +++ b/src/basefunc.go | |||
@@ -0,0 +1,115 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "fmt" | ||
6 | "os" | ||
7 | "path/filepath" | ||
8 | "regexp" | ||
9 | "strconv" | ||
10 | "strings" | ||
11 | "time" | ||
12 | ) | ||
13 | |||
14 | func figureOutSyscalls() error { | ||
15 | NRRegex := regexp.MustCompile(`#define __NR_(.*?) (\d+)$`) | ||
16 | file, err := os.Open("/usr/include/asm/unistd_64.h") | ||
17 | if err != nil { | ||
18 | return err | ||
19 | } | ||
20 | defer file.Close() | ||
21 | |||
22 | scanner := bufio.NewScanner(file) | ||
23 | for scanner.Scan() { | ||
24 | line := scanner.Text() | ||
25 | if NRRegex.MatchString(line) { | ||
26 | match := NRRegex.FindStringSubmatch(line) | ||
27 | num, err := strconv.Atoi(match[2]) | ||
28 | if err != nil { | ||
29 | return err | ||
30 | } | ||
31 | syscallTable[num] = match[1] | ||
32 | } | ||
33 | } | ||
34 | return nil | ||
35 | } | ||
36 | |||
37 | func getPid() (int, error) { | ||
38 | // 指定要搜索的关键词 | ||
39 | keyword := "/usr/bin/containerd" | ||
40 | |||
41 | // 获取/proc目录下的所有子目录 | ||
42 | procDir, err := filepath.Glob("/proc/*") | ||
43 | if err != nil { | ||
44 | return 0, err | ||
45 | } | ||
46 | |||
47 | // 遍历子目录,查找包含关键词的进程 | ||
48 | for _, dir := range procDir { | ||
49 | pid, err := strconv.Atoi(filepath.Base(dir)) | ||
50 | if err != nil { | ||
51 | continue // 跳过非PID的目录 | ||
52 | } | ||
53 | |||
54 | // 检查进程是否包含关键词 | ||
55 | if containsKeyword(pid, keyword) { | ||
56 | return pid, nil | ||
57 | } | ||
58 | } | ||
59 | err = fmt.Errorf("Error: no containerd process found.") | ||
60 | return 0, err | ||
61 | } | ||
62 | |||
63 | func containsKeyword(pid int, keyword string) bool { | ||
64 | // 构造完整的进程命令路径 | ||
65 | cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid) | ||
66 | |||
67 | // 打开文件 | ||
68 | file, err := os.Open(cmdPath) | ||
69 | if err != nil { | ||
70 | return false | ||
71 | } | ||
72 | defer file.Close() | ||
73 | |||
74 | // 读取文件内容 | ||
75 | scanner := bufio.NewScanner(file) | ||
76 | scanner.Split(bufio.ScanLines) | ||
77 | for scanner.Scan() { | ||
78 | line := scanner.Text() | ||
79 | if strings.Contains(line, keyword) { | ||
80 | return true | ||
81 | } | ||
82 | } | ||
83 | return false | ||
84 | } | ||
85 | |||
86 | func getTimeFromStr(timeStr string) (time.Time, error) { | ||
87 | timestampFloat, err := strconv.ParseFloat(timeStr, 64) | ||
88 | if err != nil { | ||
89 | return time.Unix(0, 0), err | ||
90 | } | ||
91 | secs := int64(timestampFloat) | ||
92 | nsecs := int64((timestampFloat - float64(secs)) * 1e9) | ||
93 | |||
94 | // 只精确到毫秒就够了 | ||
95 | t := time.Unix(secs, nsecs).Truncate(time.Millisecond) | ||
96 | return t, nil | ||
97 | } | ||
98 | |||
99 | func hexToAscii(hexString string) string { | ||
100 | bytes := []byte{} | ||
101 | for i := 0; i < len(hexString); i += 2 { | ||
102 | hexPair := hexString[i : i+2] | ||
103 | // 将十六进制数转换为十进制数 | ||
104 | decimal, err := strconv.ParseInt(hexPair, 16, 8) | ||
105 | if err != nil { | ||
106 | return "Invalid hex string" | ||
107 | } | ||
108 | char := byte(decimal) | ||
109 | bytes = append(bytes, char) | ||
110 | } | ||
111 | |||
112 | asciiString := strings.ReplaceAll(string(bytes), "\000", " ") | ||
113 | |||
114 | return asciiString | ||
115 | } | ||
diff --git a/src/deal.go b/src/deal.go new file mode 100644 index 0000000..fd9f788 --- /dev/null +++ b/src/deal.go | |||
@@ -0,0 +1,97 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "time" | ||
6 | ) | ||
7 | |||
8 | func deal() { | ||
9 | defer wg.Done() | ||
10 | var cooked Event | ||
11 | var ok bool | ||
12 | for { | ||
13 | cooked, ok = <-cookedChan | ||
14 | if !ok { | ||
15 | break | ||
16 | } | ||
17 | // type Event struct { | ||
18 | // timestamp time.Time | ||
19 | // pid, ppid int | ||
20 | // syscall int | ||
21 | // argc int | ||
22 | // args []string | ||
23 | // cwd string | ||
24 | // } | ||
25 | // type process struct { | ||
26 | // timestamp time.Time | ||
27 | // pid, ppid int | ||
28 | // argv []string | ||
29 | // cwd string | ||
30 | // rootfs string | ||
31 | // children []int | ||
32 | // } | ||
33 | switch syscallTable[cooked.syscall] { | ||
34 | case "fork", "vfork", "clone": | ||
35 | ppid := cooked.ppid | ||
36 | pid := cooked.pid | ||
37 | parent, ok := pids.Load(ppid) | ||
38 | if !ok { | ||
39 | break | ||
40 | } | ||
41 | parent.(*process).children = append(parent.(*process).children, pid) | ||
42 | pids.Store(pid, &process{ | ||
43 | timestamp: cooked.timestamp, | ||
44 | pid: cooked.pid, | ||
45 | ppid: cooked.ppid, | ||
46 | argv: cooked.argv, | ||
47 | cwd: cooked.cwd, | ||
48 | children: make([]int, 0), | ||
49 | }) | ||
50 | fmt.Printf("%v syscall=%d, ppid=%d, pid=%d, cwd=\"%s\", argc=%d, ", cooked.timestamp, cooked.syscall, cooked.ppid, cooked.pid, cooked.cwd, cooked.argc) | ||
51 | for i := 0; i < cooked.argc; i++ { | ||
52 | fmt.Printf("arg[%d]=\"%s\", ", i, cooked.argv[i]) | ||
53 | } | ||
54 | fmt.Printf("\n") | ||
55 | case "exit", "exit_group": | ||
56 | _, ok := pids.Load(cooked.pid) | ||
57 | if !ok { | ||
58 | break | ||
59 | } | ||
60 | go deletePid(cooked) | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | func deletePid(cooked Event) { | ||
66 | time.Sleep(1 * time.Second) | ||
67 | Process, ok := pids.Load(cooked.pid) | ||
68 | if !ok { | ||
69 | return | ||
70 | } | ||
71 | pProcess := Process.(*process) | ||
72 | |||
73 | // 先从爹那里注销户籍 | ||
74 | parent, ok := pids.Load(pProcess.ppid) | ||
75 | if ok { | ||
76 | pParent := parent.(*process) | ||
77 | for i, child := range pParent.children { | ||
78 | if child == pProcess.pid { | ||
79 | pParent.children = append(pParent.children[:i], pParent.children[i+1:]...) | ||
80 | break | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
85 | // 子进程需要收容 | ||
86 | for i := 0; i < len(pProcess.children); i++ { | ||
87 | child, ok := pids.Load(pProcess.children[i]) | ||
88 | if ok { | ||
89 | child.(*process).ppid = 1 | ||
90 | } | ||
91 | } | ||
92 | |||
93 | // 可以去死了 | ||
94 | pids.Delete(cooked.pid) | ||
95 | _, ok = pids.Load(cooked.pid) | ||
96 | fmt.Printf("%v Goodbye, %d! ok = %v\n", time.Now(), cooked.pid, ok) | ||
97 | } | ||
diff --git a/src/global.go b/src/global.go new file mode 100644 index 0000000..4e08866 --- /dev/null +++ b/src/global.go | |||
@@ -0,0 +1,10 @@ | |||
1 | package main | ||
2 | |||
3 | import "sync" | ||
4 | |||
5 | var pids sync.Map // 古希腊掌管进程的神,int->*process | ||
6 | var wg sync.WaitGroup // 掌管协程 | ||
7 | var rawChan chan interface{} // 从接收到整理的管道 | ||
8 | var cookedChan chan Event // 整理好的信息的管道 | ||
9 | var syscallTable [500]string //记录一下系统调用 | ||
10 | var containerdPid int | ||
diff --git a/src/godo.go b/src/godo.go new file mode 100644 index 0000000..6f73893 --- /dev/null +++ b/src/godo.go | |||
@@ -0,0 +1,116 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "flag" | ||
5 | "fmt" | ||
6 | "log" | ||
7 | "os" | ||
8 | "os/exec" | ||
9 | "time" | ||
10 | |||
11 | "github.com/elastic/go-libaudit/v2" | ||
12 | ) | ||
13 | |||
14 | var ( | ||
15 | fs = flag.NewFlagSet("audit", flag.ExitOnError) | ||
16 | diag = fs.String("diag", "", "dump raw information from kernel to file") | ||
17 | rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)") | ||
18 | backlog = fs.Uint("backlog", 8192, "backlog limit") | ||
19 | immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)") | ||
20 | receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+") | ||
21 | ) | ||
22 | |||
23 | type Event struct { | ||
24 | timestamp time.Time | ||
25 | pid, ppid int | ||
26 | syscall int | ||
27 | argc int | ||
28 | argv []string | ||
29 | cwd string | ||
30 | } | ||
31 | |||
32 | type process struct { | ||
33 | timestamp time.Time | ||
34 | pid, ppid int | ||
35 | argv []string | ||
36 | cwd string | ||
37 | rootfs string | ||
38 | children []int | ||
39 | } | ||
40 | |||
41 | func main() { | ||
42 | // 检查用户身份,并添加auditd规则,监听所有syscall | ||
43 | if os.Geteuid() != 0 { | ||
44 | fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid()) | ||
45 | return | ||
46 | } | ||
47 | |||
48 | // 所有的系统调用号与名称的关系 | ||
49 | err := figureOutSyscalls() | ||
50 | if err != nil { | ||
51 | fmt.Printf("Error figuring out syscall numbers: %v\n", err) | ||
52 | } | ||
53 | |||
54 | syscall := [6]string{"fork", "vfork", "clone", "execve", "exit", "exit_group"} | ||
55 | var auditCmd *exec.Cmd | ||
56 | auditCmd = exec.Command("auditctl", "-D") // 清空所有规则 | ||
57 | auditCmd.Run() | ||
58 | // 设置监听规则 | ||
59 | for i := 0; i < len(syscall); i++ { | ||
60 | auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", syscall[i]) | ||
61 | auditCmd.Run() | ||
62 | } | ||
63 | |||
64 | // 查找pid | ||
65 | containerdPid, err = getPid() | ||
66 | if err != nil { | ||
67 | fmt.Printf("Error finding containerd: %v\n", err) | ||
68 | return | ||
69 | } | ||
70 | |||
71 | // 创世之神,1号进程 | ||
72 | // pids[1] = &process{rootfs: "/", children: make([]int, 0)} | ||
73 | // pids[1].children = append(pids[1].children, containerdPid) | ||
74 | // 1号进程还是不要在进程树上直接出现了,不然它的小儿子们都会出现 | ||
75 | |||
76 | // /usr/bin/containerd,也就是我们最关注的进程 | ||
77 | // pids[containerdPid] = &process{rootfs: "/", children: make([]int, 0)} | ||
78 | pids.Store(containerdPid, &process{ | ||
79 | ppid: 1, | ||
80 | pid: containerdPid, | ||
81 | argv: make([]string, 0), | ||
82 | cwd: "/", | ||
83 | rootfs: "/", | ||
84 | children: make([]int, 0), | ||
85 | }) | ||
86 | p, ok := pids.Load(containerdPid) | ||
87 | if !ok { | ||
88 | fmt.Printf("???\n") | ||
89 | return | ||
90 | } | ||
91 | p.(*process).argv = append(p.(*process).argv, "/usr/bin/containerd") | ||
92 | |||
93 | // 开始运行,解析命令行参数后监听 | ||
94 | if err := fs.Parse(os.Args[1:]); err != nil { | ||
95 | log.Fatal(err) | ||
96 | } | ||
97 | |||
98 | if err := read(); err != nil { | ||
99 | log.Fatalf("error: %v", err) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | func coroutine(client *libaudit.AuditClient) { | ||
104 | // 各协程至此开始 | ||
105 | rawChan = make(chan interface{}) | ||
106 | cookedChan = make(chan Event) | ||
107 | wg.Add(1) | ||
108 | go receive(client) | ||
109 | wg.Add(1) | ||
110 | go orgnaze() | ||
111 | wg.Add(1) | ||
112 | go deal() | ||
113 | |||
114 | wg.Wait() | ||
115 | time.Sleep(2 * time.Second) | ||
116 | } | ||
diff --git a/src/organize.go b/src/organize.go new file mode 100644 index 0000000..025d8c0 --- /dev/null +++ b/src/organize.go | |||
@@ -0,0 +1,124 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "regexp" | ||
5 | "strconv" | ||
6 | "strings" | ||
7 | |||
8 | "github.com/elastic/go-libaudit/v2" | ||
9 | "github.com/elastic/go-libaudit/v2/auparse" | ||
10 | ) | ||
11 | |||
12 | func orgnaze() { | ||
13 | defer wg.Done() | ||
14 | defer close(cookedChan) | ||
15 | // 接收信息 | ||
16 | var raw interface{} | ||
17 | var ok bool | ||
18 | var rawEvent libaudit.RawAuditMessage | ||
19 | // 事件信息 | ||
20 | var eventId, argc int | ||
21 | var err [6]error | ||
22 | var event, cooked Event | ||
23 | // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的 | ||
24 | eventTable := make(map[int]*Event) | ||
25 | // 要用的正则匹配列表 | ||
26 | syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?ppid=(\d+) pid=(\d+).*?$`) | ||
27 | execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`) | ||
28 | argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`) | ||
29 | cwdRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`) | ||
30 | proctitleRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`) | ||
31 | eoeRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`) | ||
32 | for { | ||
33 | raw, ok = <-rawChan | ||
34 | if !ok { | ||
35 | break | ||
36 | } | ||
37 | rawEvent = raw.(libaudit.RawAuditMessage) | ||
38 | |||
39 | // type Event struct { | ||
40 | // timestamp time.Time | ||
41 | // pid, ppid int | ||
42 | // syscall int | ||
43 | // argc int | ||
44 | // args []string | ||
45 | // cwd string | ||
46 | // } | ||
47 | switch rawEvent.Type { | ||
48 | case auparse.AUDIT_SYSCALL: | ||
49 | if syscallRegex.Match(rawEvent.Data) { | ||
50 | match := syscallRegex.FindSubmatch(rawEvent.Data) | ||
51 | event.timestamp, err[0] = getTimeFromStr(string(match[1])) | ||
52 | eventId, err[1] = strconv.Atoi(string(match[2])) | ||
53 | event.syscall, err[2] = strconv.Atoi(string(match[3])) | ||
54 | event.ppid, err[3] = strconv.Atoi(string(match[4])) | ||
55 | event.pid, err[4] = strconv.Atoi(string(match[5])) | ||
56 | eventTable[eventId] = &Event{ | ||
57 | timestamp: event.timestamp, | ||
58 | syscall: event.syscall, | ||
59 | ppid: event.ppid, | ||
60 | pid: event.pid, | ||
61 | argc: 0, | ||
62 | argv: make([]string, 0), | ||
63 | cwd: "", | ||
64 | } | ||
65 | } | ||
66 | case auparse.AUDIT_EXECVE: | ||
67 | if execveRegex.Match(rawEvent.Data) { | ||
68 | match := execveRegex.FindSubmatch(rawEvent.Data) | ||
69 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
70 | argc, err[1] = strconv.Atoi(string(match[2])) | ||
71 | if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) { | ||
72 | match := argsRegex.FindAllSubmatch(rawEvent.Data, -1) | ||
73 | for i := 0; i < argc; i++ { | ||
74 | if len(match[i][2]) == 0 { | ||
75 | // 代表着匹配到的是十六进制数 | ||
76 | str := hexToAscii(string(match[i][3])) | ||
77 | eventTable[eventId].argv = append(eventTable[eventId].argv, str) | ||
78 | } else { | ||
79 | eventTable[eventId].argv = append(eventTable[eventId].argv, string(match[i][2])) | ||
80 | } | ||
81 | } | ||
82 | eventTable[eventId].argc = argc | ||
83 | } | ||
84 | } | ||
85 | // case auparse.AUDIT_PATH: | ||
86 | case auparse.AUDIT_CWD: | ||
87 | if cwdRegex.Match(rawEvent.Data) { | ||
88 | match := cwdRegex.FindSubmatch(rawEvent.Data) | ||
89 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
90 | eventTable[eventId].cwd = string(match[2]) | ||
91 | } | ||
92 | case auparse.AUDIT_PROCTITLE: | ||
93 | if proctitleRegex.Match(rawEvent.Data) { | ||
94 | var cmdline string | ||
95 | var pEvent *Event | ||
96 | match := proctitleRegex.FindSubmatch(rawEvent.Data) | ||
97 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
98 | pEvent = eventTable[eventId] | ||
99 | if pEvent.argc == 0 { | ||
100 | // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数 | ||
101 | if match[3] == nil { | ||
102 | // PROCTITLE写的是十六进制,转换为字符串 | ||
103 | cmdline = hexToAscii(string(match[4])) | ||
104 | } else { | ||
105 | cmdline = string(match[3]) | ||
106 | } | ||
107 | pEvent.argv = strings.Split(cmdline, " ") | ||
108 | pEvent.argc = len(eventTable[eventId].argv) | ||
109 | } | ||
110 | } | ||
111 | case auparse.AUDIT_EOE: | ||
112 | if eoeRegex.Match(rawEvent.Data) { | ||
113 | match := eoeRegex.FindSubmatch(rawEvent.Data) | ||
114 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
115 | // ATTENTION: 事件整理完毕,即刻发出,是否合理呢? | ||
116 | cooked = *eventTable[eventId] // 应当采用深拷贝吗?有待实验 | ||
117 | cookedChan <- cooked | ||
118 | delete(eventTable, eventId) //发出之后就从信息表扔掉,死人别占地 | ||
119 | } | ||
120 | default: | ||
121 | // ATTENTION: 这里也需要做防护 | ||
122 | } | ||
123 | } | ||
124 | } | ||
diff --git a/src/receive.go b/src/receive.go new file mode 100644 index 0000000..c0dea00 --- /dev/null +++ b/src/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 | } | ||