Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(pkg): use driverkit local build processor instead of implementing drivers build #356

Merged
merged 6 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 115 additions & 74 deletions go.mod

Large diffs are not rendered by default.

444 changes: 260 additions & 184 deletions go.sum

Large diffs are not rendered by default.

115 changes: 49 additions & 66 deletions pkg/driver/distro/distro.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package driverdistro

import (
"bufio"
"bytes"
"compress/gzip"
"errors"
"fmt"
Expand All @@ -31,7 +29,8 @@ import (
"strings"

"github.com/docker/docker/pkg/homedir"
"github.com/falcosecurity/driverkit/pkg/driverbuilder/builder"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/driverbuilder"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"gopkg.in/ini.v1"
Expand Down Expand Up @@ -130,36 +129,6 @@ func getOSReleaseDistro(kr *kernelrelease.KernelRelease) (Distro, error) {
return distro, nil
}

//nolint:gocritic // the method shall not be able to modify kr
func loadKernelHeadersFromDk(distro string, kr kernelrelease.KernelRelease) (string, func(), error) {
// Try to load kernel headers from driverkit. Don't error out if unable to.
b, err := builder.Factory(builder.Type(distro))
if err != nil {
return "", nil, nil
}

script, err := builder.KernelDownloadScript(b, nil, kr)
if err != nil {
return "", nil, err
}
script += "\necho $KERNELDIR"
out, err := exec.Command("bash", "-c", script).Output() //nolint:gosec // false positive
var path string
if err == nil {
// Scan all stdout line by line and
// store last line as KERNELDIR path.
reader := bytes.NewReader(out)
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
path = scanner.Text()
}
}
return path, func() {
_ = os.RemoveAll("/tmp/kernel-download")
_ = os.RemoveAll(path)
}, err
}

func toURL(repo, driverVer, fileName, arch string) string {
return fmt.Sprintf("%s/%s/%s/%s", repo, url.QueryEscape(driverVer), arch, fileName)
}
Expand Down Expand Up @@ -201,53 +170,67 @@ func Build(ctx context.Context,
driverType drivertype.DriverType,
driverVer string,
) (string, error) {
printer.Logger.Info("Trying to compile the requested driver")
driverFileName := toFilename(d, &kr, driverName, driverType)
destination := toLocalPath(driverVer, driverFileName, kr.Architecture.ToNonDeb())
if exist, _ := utils.FileExists(destination); exist {
return destination, ErrAlreadyPresent
destPath := toLocalPath(driverVer, driverFileName, kr.Architecture.ToNonDeb())
if exist, _ := utils.FileExists(destPath); exist {
return destPath, ErrAlreadyPresent
}

env, err := d.customizeBuild(ctx, printer, driverType, kr)
if err != nil {
return "", err
}

if env == nil {
env = make(map[string]string)
ro, err := getDKRootOptions(d, kr, driverType, driverVer, driverName, destPath)
if err != nil {
return "", err
}

// If customizeBuild did not set any KERNELDIR env variable,
// try to load kernel headers urls from driverkit.
if _, found := env[drivertype.KernelDirEnv]; !found {
printer.Logger.Debug("Trying to automatically fetch kernel headers.")
// KernelVersion needs to be fixed up; it is only used by driverkit Ubuntu builder
// and we must ensure that it is correctly set.
fixedKr := d.FixupKernel(kr)
kVerFixedKr := kr
kVerFixedKr.KernelVersion = fixedKr.KernelVersion
kernelHeadersPath, cleaner, err := loadKernelHeadersFromDk(d.String(), kVerFixedKr)
if cleaner != nil {
defer cleaner()
}
if err == nil {
printer.Logger.Debug("Downloaded and extracted kernel headers.", printer.Logger.Args("path", kernelHeadersPath))
env[drivertype.KernelDirEnv] = kernelHeadersPath
}
// Disable automatic kernel headers fetching
// if customizeBuild already retrieved kernel headers for us
// (and has set the KernelDirEnv key)
downloadHeaders := true
if _, ok := env[drivertype.KernelDirEnv]; ok {
downloadHeaders = false
}
srcPath := fmt.Sprintf("/usr/src/%s-%s", driverName, driverVer)
err = driverbuilder.NewLocalBuildProcessor(true, downloadHeaders, true, srcPath, env, 1000).Start(ro.ToBuild(printer))
return destPath, err
}

path, err := driverType.Build(ctx, printer, kr, driverName, driverVer, env)
if err != nil {
return "", err
}
// Copy the path to the expected location.
// NOTE: for kmod, this is not useful since the driver will
// be loaded directly by dkms.
printer.Logger.Info("Copying built driver to its destination.", printer.Logger.Args("src", path, "dst", destination))
f, err := os.Open(filepath.Clean(path))
//nolint:gocritic // the method shall not be able to modify kr
func getDKRootOptions(d Distro,
kr kernelrelease.KernelRelease,
driverType drivertype.DriverType,
driverVer,
driverName,
destPath string,
) (*cmd.RootOptions, error) {
ro, err := cmd.NewRootOptions()
if err != nil {
return "", err
return nil, err
}
return destination, copyDataToLocalPath(destination, f)
ro.Architecture = kr.Architecture.String()
ro.DriverVersion = driverVer
// We pass just the fixed kernelversion down to driverkit.
// it is only used by ubuntu builder,
// all the other builders do not use the kernelversion field.
fixedKr := d.FixupKernel(kr)
ro.KernelVersion = fixedKr.KernelVersion
ro.ModuleDriverName = driverName
ro.ModuleDeviceName = driverName
ro.KernelRelease = kr.String()
ro.Target = d.String()
ro.Output = driverType.ToOutput(destPath)
// This should never happen since both kmod and bpf implement ToOutput;
// the only case this can happen is if a Build is requested for modern-bpf driver type,
// But "install" cmd is smart enough to avoid that situation
// by using HasArtifacts() method.
if !ro.Output.HasOutputs() {
return nil, errors.New("build on non-artifacts driver attempted")
}
return ro, nil
}

// Download will try to download drivers for a distro trying specified repos.
Expand Down
6 changes: 1 addition & 5 deletions pkg/driver/distro/ubuntu.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ func (u *ubuntu) FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.Kerne
// from the following `uname -v` result
// `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023`
// we obtain the kernelversion`26~22.04.1`.
// NOTE: driverkernel.FetchInfo() already trims leading "#"
// and everything starting from the first whitespace,
// so that eg: we receive "26~22.04.1-Ubuntu",
// therefore we only need to drop "-Ubuntu" suffix
// Take eg: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
// Another example: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
kv := strings.TrimLeft(kr.KernelVersion, "#")
kv = strings.Split(kv, " ")[0]
kr.KernelVersion = strings.TrimSuffix(kv, "-Ubuntu")
Expand Down
49 changes: 4 additions & 45 deletions pkg/driver/type/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@ package drivertype
import (
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/docker/docker/pkg/homedir"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"k8s.io/utils/mount"

"github.com/falcosecurity/falcoctl/internal/utils"
"github.com/falcosecurity/falcoctl/pkg/output"
)

Expand Down Expand Up @@ -78,46 +75,8 @@ func (b *bpf) Supported(kr kernelrelease.KernelRelease) bool {
return kr.SupportsProbe()
}

//nolint:gocritic // the method shall not be able to modify kr
func (b *bpf) Build(ctx context.Context,
printer *output.Printer,
_ kernelrelease.KernelRelease,
driverName, driverVersion string,
env map[string]string,
) (string, error) {
// We don't fail if this fails; let's try to build a probe anyway.
_ = mountKernelDebug(printer)
srcPath := fmt.Sprintf("/usr/src/%s-%s/bpf", driverName, driverVersion)

makeCmdArgs := fmt.Sprintf(`make -C %q`, filepath.Clean(srcPath))
makeCmd := exec.CommandContext(ctx, "bash", "-c", makeCmdArgs) //nolint:gosec // false positive
// Append requested env variables to the command env
makeCmd.Env = os.Environ()
for key, val := range env {
makeCmd.Env = append(makeCmd.Env, fmt.Sprintf("%s=%s", key, val))
}
out, err := makeCmd.CombinedOutput()
if err != nil {
printer.DefaultText.Print(string(out))
}
outProbe := fmt.Sprintf("%s/probe.o", srcPath)
return outProbe, err
}

func mountKernelDebug(printer *output.Printer) error {
// Mount /sys/kernel/debug that is needed on old (pre 4.17) kernel releases,
// since these releases still did not support raw tracepoints.
// BPF_PROG_TYPE_RAW_TRACEPOINT was introduced in 4.17 indeed:
// https://github.com/torvalds/linux/commit/c4f6699dfcb8558d138fe838f741b2c10f416cf9
exists, _ := utils.FileExists("/sys/kernel/debug/tracing")
if exists {
return nil
}
printer.Logger.Info("Mounting debugfs for bpf driver.")
mounter := mount.New("/bin/mount")
err := mounter.Mount("debugfs", "/sys/kernel/debug", "debugfs", []string{"nodev"})
if err != nil {
printer.Logger.Warn("Failed to mount debugfs.", printer.Logger.Args("err", err))
func (b *bpf) ToOutput(destPath string) cmd.OutputOptions {
return cmd.OutputOptions{
Probe: destPath,
}
return err
}
98 changes: 4 additions & 94 deletions pkg/driver/type/kmod.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@ import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"

"github.com/falcosecurity/falcoctl/pkg/output"
)
Expand Down Expand Up @@ -162,96 +160,8 @@ func (k *kmod) Supported(kr kernelrelease.KernelRelease) bool {
return kr.SupportsModule()
}

func createDKMSMakeFile(gcc string) error {
file, err := os.OpenFile("/tmp/falco-dkms-make", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o777) //nolint:gosec // we need the file to be executable
if err != nil {
return err
}
defer file.Close()

_, err = fmt.Fprintln(file, "#!/usr/bin/env bash")
if err == nil {
_, err = fmt.Fprintln(file, `make CC=`+gcc+` $@`)
}
return err
}

//nolint:gocritic // the method shall not be able to modify kr
func (k *kmod) Build(ctx context.Context,
printer *output.Printer,
kr kernelrelease.KernelRelease,
driverName, driverVersion string,
env map[string]string,
) (string, error) {
// Skip dkms on UEK hosts because it will always fail
if strings.Contains(kr.String(), "uek") {
printer.Logger.Warn("Skipping because the dkms install always fail (on UEK hosts).")
return "", fmt.Errorf("unsupported on uek hosts")
}

out, err := exec.Command("which", "gcc").Output()
if err != nil {
return "", err
}
gccDir := filepath.Dir(string(out))

gccs, err := filepath.Glob(gccDir + "/gcc*")
if err != nil {
return "", err
}

for _, gcc := range gccs {
// Filter away gcc-{ar,nm,...}
// Only gcc compiler has `-print-search-dirs` option.
gccSearchArgs := fmt.Sprintf(`%s -print-search-dirs 2>&1 | grep "install:"`, gcc)
_, err = exec.Command("bash", "-c", gccSearchArgs).Output() //nolint:gosec // false positive
if err != nil {
continue
}

printer.Logger.Info("Trying to dkms install module.", printer.Logger.Args("gcc", gcc))
err = createDKMSMakeFile(gcc)
if err != nil {
printer.Logger.Warn("Could not fill /tmp/falco-dkms-make content.")
continue
}
var dkmsCmdArgs string
if kernelDir, found := env[KernelDirEnv]; found {
dkmsCmdArgs = fmt.Sprintf(`dkms install --kernelsourcedir %q --directive="MAKE='/tmp/falco-dkms-make'" -m %q -v %q -k %q --verbose`,
kernelDir, driverName, driverVersion, kr.String())
} else {
dkmsCmdArgs = fmt.Sprintf(`dkms install --directive="MAKE='/tmp/falco-dkms-make'" -m %q -v %q -k %q --verbose`,
driverName, driverVersion, kr.String())
}

// Try the build through dkms
out, err = exec.CommandContext(ctx, "bash", "-c", dkmsCmdArgs).CombinedOutput() //nolint:gosec // false positive
if err == nil {
koGlob := fmt.Sprintf("/var/lib/dkms/%s/%s/%s/%s/module/%s", driverName, driverVersion, kr.String(), kr.Architecture.ToNonDeb(), driverName)
var koFiles []string
koFiles, err = filepath.Glob(koGlob + ".*")
if err != nil || len(koFiles) == 0 {
printer.Logger.Warn("Module file not found.")
continue
}
koFile := koFiles[0]
printer.Logger.Info("Module installed in dkms.", printer.Logger.Args("file", koFile))
return koFile, nil
}
printer.DefaultText.Print(string(out))
dkmsLogFile := fmt.Sprintf("/var/lib/dkms/%s/%s/build/make.log", driverName, driverVersion)
logs, err := os.ReadFile(filepath.Clean(dkmsLogFile))
if err != nil {
printer.Logger.Warn("Running dkms build failed, couldn't find dkms log", printer.Logger.Args("file", dkmsLogFile))
} else {
printer.Logger.Warn("Running dkms build failed. Dumping dkms log.", printer.Logger.Args("file", dkmsLogFile))
logBuf := bytes.NewBuffer(logs)
scanner := bufio.NewScanner(logBuf)
for scanner.Scan() {
m := scanner.Text()
printer.DefaultText.Println(m)
}
}
func (k *kmod) ToOutput(destPath string) cmd.OutputOptions {
return cmd.OutputOptions{
Module: destPath,
}
return "", fmt.Errorf("failed to compile the module")
}
7 changes: 3 additions & 4 deletions pkg/driver/type/modernbpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/features"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"

"github.com/falcosecurity/falcoctl/pkg/output"
)
Expand Down Expand Up @@ -81,7 +81,6 @@ func (m *modernBpf) Supported(_ kernelrelease.KernelRelease) bool {
return features.HaveMapType(ebpf.RingBuf) == nil
}

//nolint:gocritic // the method shall not be able to modify kr
func (m *modernBpf) Build(_ context.Context, _ *output.Printer, _ kernelrelease.KernelRelease, _, _ string, _ map[string]string) (string, error) {
return "", nil
func (m *modernBpf) ToOutput(_ string) cmd.OutputOptions {
return cmd.OutputOptions{}
}
5 changes: 2 additions & 3 deletions pkg/driver/type/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package drivertype
import (
"fmt"

"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"

"github.com/falcosecurity/falcoctl/pkg/output"
)
Expand All @@ -37,8 +37,7 @@ type DriverType interface {
Load(printer *output.Printer, src, driverName string, fallback bool) error
Extension() string
HasArtifacts() bool
Build(ctx context.Context, printer *output.Printer, kr kernelrelease.KernelRelease,
driverName, driverVersion string, env map[string]string) (string, error)
ToOutput(destPath string) cmd.OutputOptions
Supported(kr kernelrelease.KernelRelease) bool
}

Expand Down
Loading