From 8513c0dd7f89574a31f996e4c5d254eb1aa7cf64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20I=C3=B1iguez=20Goia?= Date: Thu, 18 May 2023 14:23:40 +0200 Subject: [PATCH] Added system requirements check Added flag to check system requirements. Related: #774 --- daemon/core/gzip.go | 28 +++++++++ daemon/core/system.go | 135 ++++++++++++++++++++++++++++++++++++++++++ daemon/main.go | 30 ++++++---- 3 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 daemon/core/gzip.go diff --git a/daemon/core/gzip.go b/daemon/core/gzip.go new file mode 100644 index 0000000000..34bb4194a6 --- /dev/null +++ b/daemon/core/gzip.go @@ -0,0 +1,28 @@ +package core + +import ( + "compress/gzip" + "io/ioutil" + "os" +) + +// ReadGzipFile reads a gzip to text. +func ReadGzipFile(filename string) ([]byte, error) { + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + + gz, err := gzip.NewReader(fd) + if err != nil { + return nil, err + } + defer gz.Close() + + s, err := ioutil.ReadAll(gz) + if err != nil { + return nil, err + } + return s, nil +} diff --git a/daemon/core/system.go b/daemon/core/system.go index daa27c8728..a170ddeb26 100644 --- a/daemon/core/system.go +++ b/daemon/core/system.go @@ -1,8 +1,15 @@ package core import ( + "encoding/json" + "fmt" + "io" "io/ioutil" + "os" + "regexp" "strings" + + "github.com/evilsocket/opensnitch/daemon/log" ) var ( @@ -21,3 +28,131 @@ func GetKernelVersion() string { version, _ := ioutil.ReadFile("/proc/sys/kernel/osrelease") return strings.Replace(string(version), "\n", "", -1) } + +// CheckSysRequirements checks system features we need to work properly +func CheckSysRequirements() { + type checksT struct { + RegExps []string + Reason string + } + type ReqsList struct { + Item string + Checks checksT + } + kVer := GetKernelVersion() + + log.Raw("\n\t%sChecking system requirements for kernel version %s%s\n", log.FG_WHITE+log.BG_LBLUE, kVer, log.RESET) + log.Raw("%s------------------------------------------------------------------------------%s\n\n", log.FG_WHITE+log.BG_LBLUE, log.RESET) + + confFile := fmt.Sprint("/boot/config-", kVer) + var fileContent []byte + var err error + if Exists(confFile) { + fileContent, err = os.ReadFile(confFile) + } else { + confFile = "/proc/config.gz" + fileContent, err = ReadGzipFile(confFile) + } + if err != nil { + log.Error("%s not found", confFile) + return + } + + // TODO: check loaded/configured modules (nfnetlink, nfnetlink_queue, xt_NFQUEUE, etc) + // Other items to check: + // CONFIG_NETFILTER_NETLINK + // CONFIG_NETFILTER_NETLINK_QUEUE + const reqsList = ` +[ +{ + "Item": "kprobes", + "Checks": { + "Regexps": [ + "CONFIG_KPROBES=y", + "CONFIG_KPROBES_ON_FTRACE=y", + "CONFIG_KPROBES_ON_FTRACE=y", + "CONFIG_HAVE_KPROBES=y", + "CONFIG_HAVE_KPROBES_ON_FTRACE=y", + "CONFIG_KPROBE_EVENTS=y" + ], + "Reason": " - KPROBES not fully supported by this kernel." + } +}, +{ + "Item": "uprobes", + "Checks": { + "Regexps": [ + "CONFIG_UPROBES=y", + "CONFIG_UPROBE_EVENTS=y" + ], + "Reason": " * UPROBES not supported. Common error => cannot open uprobe_events: open /sys/kernel/debug/tracing/uprobe_events" + } +}, +{ + "Item": "ftrace", + "Checks": { + "Regexps": [ + "CONFIG_FTRACE=y" + ], + "Reason": " - CONFIG_TRACE=y not set. Common error => Error while loading kprobes: invalid argument." + } +}, +{ + "Item": "syscalls", + "Checks": { + "Regexps": [ + "CONFIG_HAVE_SYSCALL_TRACEPOINTS=y", + "CONFIG_FTRACE_SYSCALLS=y" + ], + "Reason": " - CONFIG_FTRACE_SYSCALLS or CONFIG_HAVE_SYSCALL_TRACEPOINTS not set. Common error => error enabling tracepoint tracepoint/syscalls/sys_enter_execve: cannot read tracepoint id" + } +}, +{ + "Item": "nfqueue", + "Checks": { + "Regexps": [ + "CONFIG_NETFILTER_XT_TARGET_NFQUEUE=[my]" + ], + "Reason": " * NFQUEUE netfilter extension not supported by this kernel." + } +} +] +` + + reqsFullfiled := true + dec := json.NewDecoder(strings.NewReader(reqsList)) + for { + var reqs []ReqsList + if err := dec.Decode(&reqs); err == io.EOF { + break + } else if err != nil { + log.Error("%s", err) + break + } + for _, req := range reqs { + checkOk := true + for _, trex := range req.Checks.RegExps { + fmt.Printf("\tChecking => %s\n", trex) + re, err := regexp.Compile(trex) + if err != nil { + fmt.Printf("\t%s %s\n", log.Bold(log.Red("Invalid regexp =>")), log.Red(trex)) + continue + } + if re.Find(fileContent) == nil { + fmt.Printf("\t%s\n", log.Red(req.Checks.Reason)) + checkOk = false + } + } + if checkOk { + fmt.Printf("\n\t* %s\t %s\n", log.Bold(log.Green(req.Item)), log.Bold(log.Green("✔"))) + } else { + reqsFullfiled = false + fmt.Printf("\n\t* %s\t %s\n", log.Bold(log.Red(req.Item)), log.Bold(log.Red("✘"))) + } + fmt.Println() + } + } + if !reqsFullfiled { + log.Raw("\n%sWARNING:%s Your kernel doesn't support some of the features OpenSnitch needs:\nRead more: https://github.com/evilsocket/opensnitch/issues/774\n", log.FG_WHITE+log.BG_YELLOW, log.RESET) + } +} diff --git a/daemon/main.go b/daemon/main.go index fcacba16e3..a6d5424f34 100644 --- a/daemon/main.go +++ b/daemon/main.go @@ -54,18 +54,19 @@ import ( ) var ( - showVersion = false - procmonMethod = "" - logFile = "" - rulesPath = "rules" - noLiveReload = false - queueNum = 0 - repeatQueueNum int //will be set later to queueNum + 1 - workers = 16 - debug = false - warning = false - important = false - errorlog = false + showVersion = false + checkRequirements = false + procmonMethod = "" + logFile = "" + rulesPath = "rules" + noLiveReload = false + queueNum = 0 + repeatQueueNum int //will be set later to queueNum + 1 + workers = 16 + debug = false + warning = false + important = false + errorlog = false uiSocket = "" uiClient = (*ui.Client)(nil) @@ -90,6 +91,7 @@ var ( func init() { flag.BoolVar(&showVersion, "version", debug, "Show daemon version of this executable and exit.") + flag.BoolVar(&checkRequirements, "check-requirements", debug, "Check system requirements for incompatibilities.") flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)") flag.StringVar(&uiSocket, "ui-socket", uiSocket, "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md).") @@ -463,6 +465,10 @@ func main() { fmt.Println(core.Version) os.Exit(0) } + if checkRequirements { + core.CheckSysRequirements() + os.Exit(0) + } setupLogging() setupProfiling()