summaryrefslogtreecommitdiffstats
path: root/godo.go
diff options
context:
space:
mode:
Diffstat (limited to 'godo.go')
-rw-r--r--godo.go535
1 files changed, 0 insertions, 535 deletions
diff --git a/godo.go b/godo.go
deleted file mode 100644
index 1ae336b..0000000
--- a/godo.go
+++ /dev/null
@@ -1,535 +0,0 @@
1package main
2
3import (
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
23var (
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
32type Event struct {
33 timestamp time.Time
34 pid, ppid int
35 syscall int
36 argc int
37 argv []string
38 cwd string
39}
40
41type process struct {
42 timestamp time.Time
43 pid, ppid int
44 argv []string
45 cwd string
46 rootfs string
47 children []int
48}
49
50var pids sync.Map // 古希腊掌管进程的神,int->*process
51var wg sync.WaitGroup // 掌管协程
52var rawChan chan interface{} // 从接收到整理的管道
53var cookedChan chan Event // 整理好的信息的管道
54var syscallTable [500]string //记录一下系统调用
55
56var containerdPid int
57
58func 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
123func 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
146func 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
172func 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
195func 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
208func 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
226func 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
312func 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
332func 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
446func 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
503func 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}