Skip to content

Commit

Permalink
Implement capability dropping and document needed capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
bobrik committed Jul 19, 2023
1 parent 569076a commit ca3b87c
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 3 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,42 @@ 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_NET_ADMIN`: if you use net admin related programs like xdp.
See: https://elixir.bootlin.com/linux/v6.4/source/kernel/bpf/syscall.c#L2450
* `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 @@ -24,6 +25,7 @@ func main() {
debug := kingpin.Flag("debug", "Enable debug.").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 @@ -54,6 +56,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 @@ -115,6 +122,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: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
golang.org/x/sys v0.9.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,7 @@ 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
)

replace github.com/aquasecurity/libbpfgo => github.com/bobrik/libbpfgo v0.0.0-20230704220959-d2cc6f8a97da
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ 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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bobrik/libbpfgo v0.0.0-20230704220959-d2cc6f8a97da h1:ccRuROY275uHluzDCosqhiKacMivXro66+S54QMm6B0=
github.com/bobrik/libbpfgo v0.0.0-20230704220959-d2cc6f8a97da/go.mod h1:UD3Mfr+JZ/ASK2VMucI/zAdEhb35LtvYXvAUdrdqE9s=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
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 ca3b87c

Please sign in to comment.