From 1183b8dd1126e3db5c93e0bdf1bc17420848bf3e Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Fri, 6 Dec 2024 20:28:34 +0800 Subject: [PATCH] feat: detect CAP_BPF (#681) * refactor: Move kernel checking to cli In order to simplify code of main.go, let us move code of kernel checking to cli. Furthermore, put kernel checking at pre-run phase of cli. * feat: Detect CAP_BPF when detect env If no capability to run bpf progs, we must check CAP_BPF asap. Without this check, there will be many noisy logs before log "error:operation not permitted", like https://github.com/gojue/ecapture/issues/678#issuecomment-2514532902. --------- Signed-off-by: Leon Hwang --- cli/cmd/env_detection.go | 84 ++++++++++++++++++++++++++++++++++++++++ cli/cmd/root.go | 22 +++++++---- main.go | 23 ----------- 3 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 cli/cmd/env_detection.go diff --git a/cli/cmd/env_detection.go b/cli/cmd/env_detection.go new file mode 100644 index 000000000..9be3efca3 --- /dev/null +++ b/cli/cmd/env_detection.go @@ -0,0 +1,84 @@ +// Copyright 2022 CFC4N . All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "errors" + "fmt" + "runtime" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/asm" + "github.com/gojue/ecapture/pkg/util/kernel" + "golang.org/x/sys/unix" +) + +func detectKernel() error { + // 系统内核版本检测 + kv, err := kernel.HostVersion() + if err != nil { + return fmt.Errorf("failed to get the host kernel version: %v", err) + } + switch runtime.GOARCH { + case "amd64": + if kv < kernel.VersionCode(4, 18, 0) { + return fmt.Errorf("the Linux/Android Kernel version %v (x86_64) is not supported. Requires a version greater than 4.18.", kv) + } + case "arm64": + if kv < kernel.VersionCode(5, 5, 0) { + return fmt.Errorf("the Linux/Android Kernel version %v (aarch64) is not supported. Requires a version greater than 5.5.", kv) + } + default: + return fmt.Errorf("unsupported CPU arch:%v", runtime.GOARCH) + } + + return nil +} +func detectBpfCap() error { + // BPF 权限检测 + prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ + Name: "uprobe_dummy", + Type: ebpf.Kprobe, + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.Return(), + }, + License: "GPL", + }) + if err != nil { + if errors.Is(err, unix.EPERM) { + return fmt.Errorf("the current user does not have CAP_BPF to load bpf programs. Please run as root or use sudo or add the --privileged=true flag for Docker.") + } + + return fmt.Errorf("failed to create bpf program: %v", err) + } + defer prog.Close() + + return nil +} + +func detectEnv() error { + // 环境检测 + + if err := detectKernel(); err != nil { + return err + } + + if err := detectBpfCap(); err != nil { + return err + } + + return nil +} diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 861f9c232..b252163b3 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -17,11 +17,6 @@ package cmd import ( "context" "fmt" - "github.com/gojue/ecapture/cli/cobrautl" - "github.com/gojue/ecapture/cli/http" - "github.com/gojue/ecapture/user/config" - "github.com/gojue/ecapture/user/module" - "github.com/rs/zerolog" "io" "net" "os" @@ -30,6 +25,11 @@ import ( "syscall" "time" + "github.com/gojue/ecapture/cli/cobrautl" + "github.com/gojue/ecapture/cli/http" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/module" + "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -69,9 +69,9 @@ var rootCmd = &cobra.Command{ Short: CliDescription, SuggestFor: []string{"ecapture"}, - Long: `eCapture(旁观者) is a tool that can capture plaintext packets + Long: `eCapture(旁观者) is a tool that can capture plaintext packets such as HTTPS and TLS without installing a CA certificate. -It can also capture bash commands, which is suitable for +It can also capture bash commands, which is suitable for security auditing scenarios, such as database auditing of mysqld, etc (disabled on Android). Support Linux(Android) X86_64 4.18/aarch64 5.5 or newer. Repository: https://github.com/gojue/ecapture @@ -88,6 +88,14 @@ docker run --rm --privileged=true --net=host -v ${HOST_PATH}:${CONTAINER_PATH} g // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { }, + + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if err := detectEnv(); err != nil { + return err + } + + return nil + }, } func usageFunc(c *cobra.Command) error { diff --git a/main.go b/main.go index 658ed534f..2f47b25f3 100644 --- a/main.go +++ b/main.go @@ -2,32 +2,9 @@ package main import ( "github.com/gojue/ecapture/cli" - "github.com/gojue/ecapture/pkg/util/kernel" _ "github.com/shuLhan/go-bindata" // add for bindata in Makefile - "log" - "runtime" ) func main() { - - // 环境检测 - // 系统内核版本检测 - kv, err := kernel.HostVersion() - if err != nil { - log.Fatal(err) - } - switch runtime.GOARCH { - case "amd64": - if kv < kernel.VersionCode(4, 18, 0) { - log.Fatalf("The Linux/Android Kernel version %v (x86_64) is not supported. Requires a version greater than 4.18.", kv) - } - case "arm64": - if kv < kernel.VersionCode(5, 5, 0) { - log.Fatalf("The Linux/Android Kernel version %v (aarch64) is not supported. Requires a version greater than 5.5.", kv) - } - default: - log.Fatalf("Unsupported CPU arch:%v. ", runtime.GOARCH) - } - cli.Start() }