diff options
-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 | } | ||