Skip to content

Commit

Permalink
Merge pull request #231 from bobrik/ivan/capabilities
Browse files Browse the repository at this point in the history
Implement capability dropping and document needed capabilities
  • Loading branch information
bobrik authored Jul 25, 2023
2 parents 1f0e5c4 + 380912c commit 92f7021
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 4 deletions.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,45 @@ with your own config baked in.

See [benchmark](benchmark) directory to get an idea of how low ebpf overhead is.

## Required capabilities

While you can run `ebpf_exporter` as `root`, it is not strictly necessary.
Only the following two capabilities are necessary for normal operation:

* `CAP_BPF`: required for privileged bpf operations and for reading memory
* `CAP_PERFMON`: required to attach bpf programs to kprobes and tracepoints

If you are using `systemd`, you can use the following configuration to run
as on otherwise unprivileged dynamic user with the needed capabilities:

```ini
DynamicUser=true
AmbientCapabilities=CAP_BPF CAP_PERFMON
CapabilityBoundingSet=CAP_BPF CAP_PERFMON
```

Prior to Linux v5.8 there was no dedicated `CAP_BPF` and `CAP_PERFMON`,
but you can use `CAP_SYS_ADMIN` instead of your kernel is older.

If you pass `--capabilities.keep=none` flag to `ebpf_expoter`, then it drops
all capabilities after attaching the probes, leaving it fully unprivileged.

The following additional capabilities might be needed:

* `CAP_SYSLOG`: if you use `ksym` decoder to have access to `/proc/kallsyms`.
Note that you must keep this capability: `--capabilities.keep=cap_syslog`.
See: https://elixir.bootlin.com/linux/v6.4/source/kernel/kallsyms.c#L982
* `CAP_IPC_LOCK`: if you use `perf_event_array` for reading from the kernel.
Note that you must keep it: `--capabilities.keep=cap_perfmon,cap_ipc_lock`.
* `CAP_SYS_ADMIN`: if you want BTF information from modules.
See: https://github.com/libbpf/libbpf/blob/v1.2.0/src/libbpf.c#L8654-L8666
and https://elixir.bootlin.com/linux/v6.5-rc1/source/kernel/bpf/syscall.c#L3789
* `CAP_NET_ADMIN`: if you use net admin related programs like xdp.
See: https://elixir.bootlin.com/linux/v6.4/source/kernel/bpf/syscall.c#L3787
* `CAP_SYS_RESOURCE`: if you run an older kernel without memcg accounting for
bpf memory. Upstream Linux kernel added support for this in v5.11.
See: https://github.com/libbpf/libbpf/blob/v1.2.0/src/bpf.c#L98-L106

## Supported scenarios

Currently the only supported way of getting data out of the kernel is via maps.
Expand Down
50 changes: 50 additions & 0 deletions cmd/ebpf_exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/version"
"gopkg.in/alecthomas/kingpin.v2"
"kernel.org/pub/linux/libs/security/libcap/cap"
)

func main() {
Expand All @@ -25,6 +26,7 @@ func main() {
noLogTime := kingpin.Flag("log.no-timestamps", "Disable timestamps in log.").Bool()
listenAddress := kingpin.Flag("web.listen-address", "The address to listen on for HTTP requests (fd://0 for systemd activation).").Default(":9435").String()
metricsPath := kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
capabilities := kingpin.Flag("capabilities.keep", "Comma separated list of capabilities to keep (cap_syslog, cap_bpf, etc.), 'all' or 'none'").Default("all").String()
kingpin.Version(version.Print("ebpf_exporter"))
kingpin.HelpFlag.Short('h')
kingpin.Parse()
Expand Down Expand Up @@ -59,6 +61,11 @@ func main() {
log.Fatalf("Error attaching exporter: %s", err)
}

err = ensureCapabilities(*capabilities)
if err != nil {
log.Fatalf("Error dropping capabilities: %s", err)
}

log.Printf("Started with %d programs found in the config in %dms", len(configs), time.Since(started).Milliseconds())

err = prometheus.Register(version.NewCollector("ebpf_exporter"))
Expand Down Expand Up @@ -120,6 +127,49 @@ func listen(addr string) error {

}

func ensureCapabilities(keep string) error {
existing := cap.GetProc()
log.Printf("Started with capabilities: %q", existing)

if keep == "all" {
log.Printf("Retaining all existing capabilities")
return nil
}

ensure := cap.NewSet()

values := []cap.Value{}
if keep != "none" {
for _, name := range strings.Split(keep, ",") {
value, err := cap.FromName(name)
if err != nil {
return fmt.Errorf("error parsing capability %q: %v", name, err)
}

values = append(values, value)
}
}

err := ensure.SetFlag(cap.Permitted, true, values...)
if err != nil {
return fmt.Errorf("error setting permitted capabilities: %v", err)
}

err = ensure.SetFlag(cap.Effective, true, values...)
if err != nil {
return fmt.Errorf("error setting effective capabilities: %v", err)
}

err = ensure.SetProc()
if err != nil {
return fmt.Errorf("failed to drop capabilities: %q -> %q: %v", existing, ensure, err)
}

log.Printf("Dropped capabilities to %q", ensure)

return nil
}

func libbpfLogCallback(level int, msg string) {
levelName := "unknown"
switch level {
Expand Down
5 changes: 4 additions & 1 deletion exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ func (e *Exporter) Attach() error {
return fmt.Errorf("multiple configs with name %q", cfg.Name)
}

module, err := libbpfgo.NewModuleFromFile(cfg.BPFPath)
module, err := libbpfgo.NewModuleFromFileArgs(libbpfgo.NewModuleArgs{
BPFObjPath: cfg.BPFPath,
SkipMemlockBump: true, // Let libbpf itself decide whether it is needed
})
if err != nil {
return fmt.Errorf("error creating module from %q for config %q: %v", cfg.BPFPath, cfg.Name, err)
}
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/cloudflare/ebpf_exporter/v2
go 1.18

require (
github.com/aquasecurity/libbpfgo v0.4.9-libbpf-1.2.0
github.com/aquasecurity/libbpfgo v0.4.9-libbpf-1.2.0.0.20230724123347-7e47ce85fbec
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/elastic/go-perf v0.0.0-20191212140718-9c656876f595
github.com/iovisor/gobpf v0.2.0
Expand All @@ -12,6 +12,7 @@ require (
golang.org/x/sys v0.10.0
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.4.0
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69
)

require (
Expand All @@ -25,4 +26,5 @@ require (
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 // indirect
)
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/aquasecurity/libbpfgo v0.4.9-libbpf-1.2.0 h1:pk9L7I6wF1nTfO42+jjXhA8ozRjvtj2ZvHV/i/YC0dE=
github.com/aquasecurity/libbpfgo v0.4.9-libbpf-1.2.0/go.mod h1:UD3Mfr+JZ/ASK2VMucI/zAdEhb35LtvYXvAUdrdqE9s=
github.com/aquasecurity/libbpfgo v0.4.9-libbpf-1.2.0.0.20230724123347-7e47ce85fbec h1:3UdURQ4/Ja18MilfB9UWmheuHtgyoQItXZrc+XRyGrM=
github.com/aquasecurity/libbpfgo v0.4.9-libbpf-1.2.0.0.20230724123347-7e47ce85fbec/go.mod h1:UD3Mfr+JZ/ASK2VMucI/zAdEhb35LtvYXvAUdrdqE9s=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
Expand Down Expand Up @@ -58,3 +58,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69 h1:N0m3tKYbkRMmDobh/47ngz+AWeV7PcfXMDi8xu3Vrag=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69/go.mod h1:Tk5Ip2TuxaWGpccL7//rAsLRH6RQ/jfqTGxuN/+i/FQ=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 h1:IdrOs1ZgwGw5CI+BH6GgVVlOt+LAXoPyh7enr8lfaXs=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=

0 comments on commit 92f7021

Please sign in to comment.