Skip to content

Latest commit

 

History

History
352 lines (267 loc) · 13.5 KB

File metadata and controls

352 lines (267 loc) · 13.5 KB

kfeatures

Go Reference CI Latest release Go version License

Can my eBPF tool actually run here, and if not, exactly what needs to change?

kfeatures is a pure-Go library that answers this question.

It probes kernel capabilities at runtime and returns actionable diagnostics: not just unsupported, but why and how to fix it.

if err := kfeatures.Check(kfeatures.FeatureBPFLSM, kfeatures.FeatureBTF); err != nil {
    var fe *kfeatures.FeatureError
    if errors.As(err, &fe) {
        log.Fatalf("%s - %s", fe.Feature, fe.Reason)
        // Output: BPF LSM - CONFIG_BPF_LSM=y but 'bpf' not in active LSM list; add lsm=...,bpf to kernel boot params
    }
}

The same answers are available from the CLI for CI/CD gating (semantic exit codes), and from --mcp mode for AI agents (JSON-RPC over stdio, every subcommand exposed as an MCP tool with a discoverable input schema). See CLI.

Why not cilium/ebpf/features or bpftool?

cilium/ebpf/features answers: "Does this kernel support program type X?"

bpftool feature probe answers: "What BPF features does this kernel have?" (CLI only, not embeddable in Go)

Neither tells you whether your tool can actually run. For example, BPF LSM requires three things simultaneously: CONFIG_BPF_LSM=y in the kernel config, bpf in the active LSM boot parameter list, and the LSM program type supported by the running kernel. cilium/ebpf/features can only check the last one. bpftool can check the first and last, but not the second. Neither provides remediation guidance.

Capability cilium/ebpf/features bpftool feature probe kfeatures
BPF program type probes
BPF map type / helper probes ✓ †
BTF availability (/sys/kernel/btf/vmlinux) ✗ *
Kernel config parsing (any CONFIG_*, =y/=m)
Active LSM list (/sys/kernel/security/lsm)
BPF LSM enabled (config + boot params + program type)
IMA detection (LSM list + securityfs directory)
IMA active measurement (runtime policy detection)
Process capabilities (CAP_BPF, CAP_SYS_ADMIN, CAP_PERFMON)
Unprivileged BPF status
Mount-state gates (bpffs/tracefs/custom paths via superblock magic)
ELF requirement extraction (parse .o, derive requirements)
Composite feature validation
Actionable diagnostics (remediation steps)
Selective probing (minimize overhead) ✓ ‡ ✗ §
Pure Go, no CGO
Usable as a Go library

* bpftool checks CONFIG_DEBUG_INFO_BTF in the kernel config but does not verify /sys/kernel/btf/vmlinux exists. † Exposed in kfeatures as parameterized requirements (RequireMapType, RequireProgramHelper) consumed by Check(...). cilium/ebpf/features is per-function: callers invoke individual probe functions on demand. § bpftool feature probe runs the full probe set on every invocation.

Other Go projects (libbpfgo, Tetragon, Falco libs) have some feature detection built in, but none is a standalone reusable library. They are either CGO-dependent, tightly coupled to their parent project, or written in C/C++.

Installation

go get github.com/leodido/kfeatures

Usage

Quick check

Validate that required kernel features are available:

import (
    "errors"
    "log"

    "github.com/leodido/kfeatures"
)

if err := kfeatures.Check(kfeatures.FeatureBPFLSM, kfeatures.FeatureBTF); err != nil {
    var fe *kfeatures.FeatureError
    if errors.As(err, &fe) {
        log.Fatalf("kernel not ready: %s - %s", fe.Feature, fe.Reason)
    }
}

Mixed requirements

Combine Feature enums with parameterized workload requirements:

import (
    "github.com/cilium/ebpf"
    "github.com/cilium/ebpf/asm"
    "github.com/leodido/kfeatures"
)

err := kfeatures.Check(
    kfeatures.FeatureBTF,
    kfeatures.RequireProgramType(ebpf.XDP),
    kfeatures.RequireMapType(ebpf.Hash),
    kfeatures.RequireProgramHelper(ebpf.XDP, asm.FnMapLookupElem),
)

Custom mount paths (RequireMount)

Gate on a filesystem mounted at an arbitrary path with the expected superblock magic. Useful when bpffs lives somewhere other than /sys/fs/bpf:

import (
    "github.com/leodido/kfeatures"
    "golang.org/x/sys/unix"
)

err := kfeatures.Check(
    kfeatures.RequireMount("/run/bpf", unix.BPF_FS_MAGIC),
)

Magic constants come from golang.org/x/sys/unix (e.g. unix.BPF_FS_MAGIC, unix.TRACEFS_MAGIC, unix.CGROUP2_SUPER_MAGIC).

Reusable presets (FeatureGroup)

FeatureGroup packages a set of requirements as a single value you can pass anywhere a Requirement is accepted:

var TracingTool = kfeatures.FeatureGroup{
    kfeatures.FeatureBTF,
    kfeatures.FeatureKprobeMulti,
    kfeatures.RequireProgramType(ebpf.Kprobe),
}

if err := kfeatures.Check(TracingTool); err != nil {
    log.Fatal(err)
}

Extract requirements from a compiled object (FromELF)

Point FromELF at an eBPF .o and get back a FeatureGroup describing its program types, map types, and helper-per-program requirements (directly consumable by Check):

reqs, err := kfeatures.FromELF("./bpf/probe.o")
if err != nil {
    log.Fatal(err)
}
if err := kfeatures.Check(reqs); err != nil {
    log.Fatalf("kernel cannot run probe.o: %v", err)
}

Output is deterministic (deduplicated, stably ordered). Unknown ELF kinds fail closed.

Render remediation (Diagnose)

Check returns the diagnosis for the first failing feature. To inspect any feature against a single probe snapshot, call Diagnose directly:

sf, _ := kfeatures.Probe()
if !sf.BPFLSMEnabled.Supported {
    fmt.Println(sf.Diagnose(kfeatures.FeatureBPFLSM))
    // CONFIG_BPF_LSM=y but 'bpf' not in active LSM list; add lsm=...,bpf to kernel boot params
}

Full probe

Probe all features for diagnostics and reporting:

sf, err := kfeatures.Probe()
if err != nil {
    log.Fatal(err)
}
fmt.Println(sf)

Sample output (truncated):

Kernel: 6.1.0-generic

Program Types:
  LSM: yes
  kprobe: yes
  kprobe.multi: yes

Core:
  BTF: yes

Security Subsystems:
  BPF LSM enabled: yes
  IMA enabled: no
  IMA directory: yes
  Active LSMs: lockdown, capability, yama, apparmor, bpf

Filesystems:
  tracefs: yes
  bpffs: yes

Individual fields are typed and inspectable programmatically (see SystemFeatures).

Selective probing

Probe only what you need:

sf, err := kfeatures.ProbeWith(
    kfeatures.WithProgramTypes(ebpf.LSM, ebpf.Kprobe),
    kfeatures.WithSecuritySubsystems(),
    kfeatures.WithCapabilities(),
)

WithX options select probe scope. They do not define requirements; use Check(...) for gating.

CLI

A CLI is included for operator diagnostics, CI/CD gating, and AI-agent integration:

go install github.com/leodido/kfeatures/cmd/kfeatures@latest
kfeatures probe                                    # probe all features
kfeatures check --require bpf-lsm,btf,cap-bpf      # exit 0 if met, 1 otherwise
kfeatures probe --json                             # JSON output
kfeatures config                                   # display kernel config

CI/CD gating (semantic exit codes)

kfeatures check exits 0 when all requirements are met and 1 when any are missing. Drop it into a Helm chart pre-install hook, an init container, or a CI job. With --json the verdict is a parse-friendly object on stdout:

$ kfeatures check --require bpf-lsm,btf --json
{
  "ok": false,
  "feature": "bpf-lsm",
  "reason": "CONFIG_BPF_LSM=y but 'bpf' not in active LSM list; add lsm=...,bpf to kernel boot params"
}
$ echo $?
1

Invocation errors (missing required flag, unknown flag, invalid value, unknown subcommand) emit a structured JSON envelope on stderr and exit with a semantic code so wrappers can distinguish "the user invoked us wrong" from "the kernel is missing a feature":

Exit code Category Example
0 OK check passed
1 Runtime FeatureError, probe failure, missing kernel config
10 Input missing_required_flag: required flag not provided
11 Input invalid_flag_value: wrong type or unknown enum
12 Input unknown_flag
14 Input unknown_command
$ kfeatures check --require bogus
{"error":"invalid_flag_value","exit_code":11,"flag":"require","got":"bogus","expected":"feature","command":"kfeatures check","message":"invalid argument \"bogus\" for \"-r, --require\" flag: unknown feature: \"bogus\" (available: …)"}
$ echo $?
11

Codes follow the structcli/exitcode categories: input errors 1019 are agent-fixable, runtime errors 19 are operator-fixable.

AI agents (--jsonschema, --mcp)

kfeatures is built to be driven by LLM agents and code-generation tooling without --help scraping.

--jsonschema dumps a JSON Schema describing a command's flags. Use =tree to walk the entire subtree:

$ kfeatures check --jsonschema | jq '.title, .properties | keys'
"kfeatures check"
[
  "json",
  "require"
]

$ kfeatures --jsonschema=tree | jq 'map(.title) | map(select(test("^kfeatures( probe| check| config| version)?$")))'
[
  "kfeatures",
  "kfeatures check",
  "kfeatures config",
  "kfeatures probe",
  "kfeatures version"
]

(--jsonschema=tree walks every node, including cobra-generated help and completion leaves; filter with jq to the ones you care about.)

--mcp turns kfeatures into a Model Context Protocol server over stdio. Each runnable leaf command becomes an MCP tool whose input schema mirrors the cobra flag set; agents introspect via tools/list and invoke via tools/call:

// Claude Desktop / any MCP-aware client config
{
  "mcpServers": {
    "kfeatures": {
      "command": "kfeatures",
      "args": ["--mcp"]
    }
  }
}

Tools exposed: probe, check, config. The server stays alive across business-outcome errors (a failing check does not terminate the session), and invocation errors flow through the same structured envelope as the CLI. Pure stdlib JSON-RPC inside structcli; no extra heavy SDK dependency.

What it detects

Category Features
Program types LSM, kprobe, kprobe.multi, tracepoint, fentry
Core BTF availability (CO-RE)
Security BPF LSM enabled, IMA enabled, IMA active measurement, active LSM list
Capabilities and runtime gates CAP_BPF, CAP_SYS_ADMIN, CAP_PERFMON, unprivileged BPF disabled, BPF stats enabled
Syscalls bpf(), perf_event_open()
JIT enabled, hardened, kallsyms, memory limit, CONFIG_BPF_JIT_ALWAYS_ON
Filesystems tracefs, debugfs, securityfs, bpffs (gated tracefs/bpffs checks verify the filesystem is mounted with the expected superblock magic)
Custom mount gates any path + superblock magic via RequireMount
Namespaces initial user namespace, initial PID namespace
Parameterized workload requirements program type, map type, helper-per-program-type via requirement items
ELF-derived requirements program/map types and helper-per-program requirements via FromELF
Mitigation context Spectre v1/v2 vulnerability status
Kernel config CONFIG_BPF_LSM, CONFIG_IMA, CONFIG_DEBUG_INFO_BTF, CONFIG_FPROBE, any CONFIG_*

Stability

Pre-1.0. The public API may change between minor versions; breaking changes are called out explicitly in CHANGELOG.md. The FromELF contract (signature, determinism, fail-closed semantics) is frozen; see CONTRIBUTING.md.

Requirements

  • Linux for runtime probing/checking (uses Linux-specific syscalls and sysfs).
  • FromELF is parser-only and works on any platform.
  • Some probes require CAP_BPF or CAP_SYS_ADMIN.

Contributing

See CONTRIBUTING.md for the API model, the feature-addition checklist, and the development workflow.

License

Apache License 2.0.