ebpf-demo/main.go
2023-08-19 11:58:49 +08:00

112 lines
2.7 KiB
Go

package main
import (
"errors"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"syscall"
"time"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/rlimit"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go FileProtector bpf/lsm.c -- -I./bpf -O2
/*
#include <time.h>
static unsigned long long get_nsecs(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (unsigned long long)ts.tv_sec * 1000000000UL + ts.tv_nsec;
}
*/
import "C"
const ( // bpf2go cannot restore enum to corresponding names. so i do it manually
FileProtectorFileProtectStateEnabled FileProtectorFileProtectState = iota
FileProtectorFileProtectStateTick
FileProtectorFileProtectStateMax
)
func main() {
fDebug := flag.Bool("debug", false, "toggle to show full ebpf verifier error")
flag.Parse()
if err := rlimit.RemoveMemlock(); err != nil {
panic(err)
}
fileProtectorObjects := FileProtectorObjects{}
if err := LoadFileProtectorObjects(&fileProtectorObjects, &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
LogSize: ebpf.DefaultVerifierLogSize * 1024,
},
}); err != nil {
var ve *ebpf.VerifierError
if *fDebug && errors.As(err, &ve) {
// Using %+v will print the whole verifier error, not just the last
// few lines.
fmt.Printf("Verifier error: %+v\n", ve)
}
log.Panic("failed to attach lsm: ", err)
}
defer fileProtectorObjects.Close()
log.Println("lsm loaded")
lsm, err := link.AttachLSM(link.LSMOptions{
Program: fileProtectorObjects.CheckFileOpen,
})
if err != nil {
log.Panic("failed to attach lsm: ", err)
}
defer lsm.Close()
log.Println("lsm attached")
log.Println("configure maps...")
for _, path := range flag.Args() {
path, _ = filepath.Abs(path)
log.Println("-", path)
stat, err := os.Stat(path)
if err != nil {
log.Println("W: ", err)
continue
}
switch stat := stat.Sys().(type) {
case *syscall.Stat_t:
if err := fileProtectorObjects.FileProtectorMaps.Roots.Update(stat.Ino, uint8(1), ebpf.UpdateAny); err != nil {
panic(err)
}
default:
log.Printf("W: incompatible type of stat: %T", stat)
continue
}
}
// you should also protect the daemon config itself
log.Println("configuration done. enabling...")
if err := fileProtectorObjects.FileProtectorMaps.States.Update(FileProtectorFileProtectStateEnabled, uint64(1), ebpf.UpdateAny); err != nil {
panic(err)
}
ticker := time.NewTicker(time.Second * 10)
defer ticker.Stop()
log.Println("ticking...but with one step slower intentionally!")
for {
now := uint64(C.get_nsecs())
log.Println("tick:", now)
if err := fileProtectorObjects.FileProtectorMaps.States.Update(FileProtectorFileProtectStateTick, uint64(now), ebpf.UpdateAny); err != nil {
panic(err)
}
<-ticker.C
}
}