diff --git a/cmd/cmd.go b/cmd/cmd.go index 116b37e..1cb7d7a 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -3,6 +3,7 @@ package main import ( "encoding/binary" "fmt" + "gotproxy/common" "log" "net" "os" @@ -29,6 +30,14 @@ var rootCmd = &cobra.Command{ ProxyPort: proxyPort, } + if ok, err := common.HasPermission(); err != nil { + log.Fatal("check capabilities failed: ", err) + return + } else if !ok { + log.Fatal("gotproxy requires CAP_BPF to run. Please run gotproxy with sudo.") + return + } + ip, err := ipStrToUnit32() if err != nil { log.Fatal(err) diff --git a/common/permission.go b/common/permission.go new file mode 100644 index 0000000..a09fc64 --- /dev/null +++ b/common/permission.go @@ -0,0 +1,24 @@ +package common + +import ( + "golang.org/x/sys/unix" +) + +const ( + // capBpf 0000 0000 0000 0000 0000 0000 1000 0000 + capBpf = 1 << (unix.CAP_BPF - 32) + // capSysAdmin 0000 0000 0010 0000 0000 0000 0000 0000 + capSysAdmin = 1 << unix.CAP_SYS_ADMIN +) + +// HasPermission reference: https://man7.org/linux/man-pages/man2/capset.2.html +func HasPermission() (bool, error) { + hdr := unix.CapUserHeader{Version: unix.LINUX_CAPABILITY_VERSION_3} + var data [2]unix.CapUserData + if err := unix.Capget(&hdr, &data[0]); err != nil { + return false, err + } + // Note that the CAP_* values are bit indexes and need to be bit-shifted before ORing into the bit fields. + // Note that 64-bit capabilities use datap[0] and datap[1], whereas 32-bit capabilities use only datap[0]. + return data[1].Permitted&capBpf != 0 || data[0].Permitted&capSysAdmin != 0, nil +}