Repository: falcosecurity/driverkit
Pull request #239 (2022-12-13 10:40:34)

new(builder): add bottlerocket os support.
**What type of PR is this?**

/kind feature

**Any specific area of the project related to this PR?**

/area pkg

**What this PR does / why we need it**:

Adds support for bottlerocket, using same mechanism as minikube; see kernel-crawler PR: https://github.com/falcosecurity/kernel-crawler/pull/79

**Which issue(s) this PR fixes**:

Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below.
If the PR requires additional action from users switching to the new release, prepend the string "action required:".
For example, `action required: change the API interface of the rule engine`.
-->

```release-note
new: add support for bottlerocket
```


poiana: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/falcosecurity/driverkit/pull/239#" title="Author self-approved">FedeDP</a>*

The full list of commands accepted by this bot can be found [here](https://prow.falco.org/command-help?repo=falcosecurity%2Fdriverkit).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/falcosecurity/driverkit/blob/master/OWNERS)~~ [FedeDP]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
FedeDP: Wait on https://github.com/falcosecurity/kernel-crawler/pull/79 to be merged first.
dwindsor: Still not quite sure how this works given Bottlerocket's read-only rootfs, but this is great! 🚀
FedeDP: It does indeed allow to build kmod/ebpf probe; see: https://github.com/bottlerocket-os/bottlerocket/issues/2275#issuecomment-1186315503

FedeDP: kernel-crawler PR was merged ;) 
FedeDP: We now have bottlerocket entries in kernel-crawler generated json: https://falcosecurity.github.io/kernel-crawler/?arch=x86_64&target=BottleRocket.
I think we can merge this ;) 

Patch:
diff --git a/Makefile b/Makefile
index 7583e76..7dda45f 100644
--- a/Makefile
+++ b/Makefile
@@ -132,6 +132,6 @@ ${driverkit_docgen}: ${PWD}/docgen
 
 .PHONY: docs
 docs: ${driverkit_docgen}
-	$(RM) -R docs/*
+	$(RM) -R docs/driverkit*
 	@mkdir -p docs
 	${driverkit_docgen}
diff --git a/cmd/root.go b/cmd/root.go
index cea4e16..237436e 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -144,6 +144,7 @@ func NewRootCmd() *RootCmd {
 	flags.StringVar(&rootOpts.ModuleDeviceName, "moduledevicename", rootOpts.ModuleDeviceName, "kernel module device name (the default is falco, so the device will be under /dev/falco*)")
 	flags.StringVar(&rootOpts.ModuleDriverName, "moduledrivername", rootOpts.ModuleDriverName, "kernel module driver name, i.e. the name you see when you check installed modules via lsmod")
 	flags.StringVar(&rootOpts.BuilderImage, "builderimage", rootOpts.BuilderImage, "docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.")
+	flags.StringVar(&rootOpts.BuilderImageBase, "builderimagebase", rootOpts.BuilderImageBase, "base docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.")
 	flags.StringVar(&rootOpts.GCCVersion, "gccversion", rootOpts.GCCVersion, "enforce a specific gcc version for the build")
 
 	flags.StringSliceVar(&rootOpts.KernelUrls, "kernelurls", nil, "list of kernel header urls (e.g. --kernelurls <URL1> --kernelurls <URL2> --kernelurls \"<URL3>,<URL4>\")")
diff --git a/cmd/root_options.go b/cmd/root_options.go
index 55b1b83..da9591d 100644
--- a/cmd/root_options.go
+++ b/cmd/root_options.go
@@ -33,6 +33,7 @@ type RootOptions struct {
 	Target           string   `validate:"required,target" name:"target"`
 	KernelConfigData string   `validate:"omitempty,base64" name:"kernel config data"` // fixme > tag "name" does not seem to work when used at struct level, but works when used at inner level
 	BuilderImage     string   `validate:"omitempty,imagename" name:"builder image"`
+	BuilderImageBase string   `validate:"omitempty,imagename" name:"builder base image"`
 	GCCVersion       string   `validate:"omitempty,semvertolerant" name:"gcc version"`
 	KernelUrls       []string `name:"kernel header urls"`
 	Repo             RepoOptions
@@ -115,21 +116,22 @@ func (ro *RootOptions) toBuild() *builder.Build {
 	}
 
 	build := &builder.Build{
-		TargetType:         builder.Type(ro.Target),
-		DriverVersion:      ro.DriverVersion,
-		KernelVersion:      ro.KernelVersion,
-		KernelRelease:      ro.KernelRelease,
-		Architecture:       ro.Architecture,
-		KernelConfigData:   kernelConfigData,
-		ModuleFilePath:     ro.Output.Module,
-		ProbeFilePath:      ro.Output.Probe,
-		ModuleDriverName:   ro.ModuleDriverName,
-		ModuleDeviceName:   ro.ModuleDeviceName,
-		GCCVersion:         ro.GCCVersion,
-		CustomBuilderImage: ro.BuilderImage,
-		KernelUrls:         ro.KernelUrls,
-		RepoOrg:            ro.Repo.Org,
-		RepoName:           ro.Repo.Name,
+		TargetType:             builder.Type(ro.Target),
+		DriverVersion:          ro.DriverVersion,
+		KernelVersion:          ro.KernelVersion,
+		KernelRelease:          ro.KernelRelease,
+		Architecture:           ro.Architecture,
+		KernelConfigData:       kernelConfigData,
+		ModuleFilePath:         ro.Output.Module,
+		ProbeFilePath:          ro.Output.Probe,
+		ModuleDriverName:       ro.ModuleDriverName,
+		ModuleDeviceName:       ro.ModuleDeviceName,
+		GCCVersion:             ro.GCCVersion,
+		CustomBuilderImage:     ro.BuilderImage,
+		CustomBuilderImageBase: ro.BuilderImageBase,
+		KernelUrls:             ro.KernelUrls,
+		RepoOrg:                ro.Repo.Org,
+		RepoName:               ro.Repo.Name,
 	}
 
 	// attempt the build in case it comes from an invalid config
diff --git a/docs/driverkit.md b/docs/driverkit.md
index a8d84bd..9381d6e 100644
--- a/docs/driverkit.md
+++ b/docs/driverkit.md
@@ -6,6 +6,33 @@ A command line tool to build Falco kernel modules and eBPF probes.
 driverkit
 ```
 
+### Options
+
+```
+      --architecture string       target architecture for the built driver, one of [amd64,arm64] (default "amd64")
+      --builderimage string       docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+      --builderimagebase string   base docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+  -c, --config string             config file path (default $HOME/.driverkit.yaml if exists)
+      --driverversion string      driver version as a git commit hash or as a git tag (default "master")
+      --dryrun                    do not actually perform the action
+      --gccversion string         enforce a specific gcc version for the build
+  -h, --help                      help for driverkit
+      --kernelconfigdata string   base64 encoded kernel config data: in some systems it can be found under the /boot directory, in other it is gzip compressed under /proc
+      --kernelrelease string      kernel release to build the module for, it can be found by executing 'uname -v'
+      --kernelurls strings        list of kernel header urls (e.g. --kernelurls <URL1> --kernelurls <URL2> --kernelurls "<URL3>,<URL4>")
+      --kernelversion string      kernel version to build the module for, it's the numeric value after the hash when you execute 'uname -v' (default "1")
+  -l, --loglevel string           log level (default "info")
+      --moduledevicename string   kernel module device name (the default is falco, so the device will be under /dev/falco*) (default "falco")
+      --moduledrivername string   kernel module driver name, i.e. the name you see when you check installed modules via lsmod (default "falco")
+      --output-module string      filepath where to save the resulting kernel module
+      --output-probe string       filepath where to save the resulting eBPF probe
+      --proxy string              the proxy to use to download data
+      --repo-name string          repository github name (default "libs")
+      --repo-org string           repository github organization (default "falcosecurity")
+  -t, --target string             the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+      --timeout int               timeout in seconds (default 120)
+```
+
 ### SEE ALSO
 
 * [driverkit completion](driverkit_completion.md)	 - Generates completion scripts.
diff --git a/docs/driverkit_docker.md b/docs/driverkit_docker.md
index 653d3cf..0e061d7 100644
--- a/docs/driverkit_docker.md
+++ b/docs/driverkit_docker.md
@@ -6,6 +6,33 @@ Build Falco kernel modules and eBPF probes against a docker daemon.
 driverkit docker [flags]
 ```
 
+### Options
+
+```
+      --architecture string       target architecture for the built driver, one of [amd64,arm64] (default "amd64")
+      --builderimage string       docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+      --builderimagebase string   base docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+  -c, --config string             config file path (default $HOME/.driverkit.yaml if exists)
+      --driverversion string      driver version as a git commit hash or as a git tag (default "master")
+      --dryrun                    do not actually perform the action
+      --gccversion string         enforce a specific gcc version for the build
+  -h, --help                      help for docker
+      --kernelconfigdata string   base64 encoded kernel config data: in some systems it can be found under the /boot directory, in other it is gzip compressed under /proc
+      --kernelrelease string      kernel release to build the module for, it can be found by executing 'uname -v'
+      --kernelurls strings        list of kernel header urls (e.g. --kernelurls <URL1> --kernelurls <URL2> --kernelurls "<URL3>,<URL4>")
+      --kernelversion string      kernel version to build the module for, it's the numeric value after the hash when you execute 'uname -v' (default "1")
+  -l, --loglevel string           log level (default "info")
+      --moduledevicename string   kernel module device name (the default is falco, so the device will be under /dev/falco*) (default "falco")
+      --moduledrivername string   kernel module driver name, i.e. the name you see when you check installed modules via lsmod (default "falco")
+      --output-module string      filepath where to save the resulting kernel module
+      --output-probe string       filepath where to save the resulting eBPF probe
+      --proxy string              the proxy to use to download data
+      --repo-name string          repository github name (default "libs")
+      --repo-org string           repository github organization (default "falcosecurity")
+  -t, --target string             the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+      --timeout int               timeout in seconds (default 120)
+```
+
 ### SEE ALSO
 
 * [driverkit](driverkit.md)	 - A command line tool to build Falco kernel modules and eBPF probes.
diff --git a/docs/driverkit_kubernetes-in-cluster.md b/docs/driverkit_kubernetes-in-cluster.md
index 120c6c3..c27463b 100644
--- a/docs/driverkit_kubernetes-in-cluster.md
+++ b/docs/driverkit_kubernetes-in-cluster.md
@@ -6,6 +6,36 @@ Build Falco kernel modules and eBPF probes against a Kubernetes cluster inside a
 driverkit kubernetes-in-cluster [flags]
 ```
 
+### Options
+
+```
+      --architecture string        target architecture for the built driver, one of [amd64,arm64] (default "amd64")
+      --builderimage string        docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+      --builderimagebase string    base docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+  -c, --config string              config file path (default $HOME/.driverkit.yaml if exists)
+      --driverversion string       driver version as a git commit hash or as a git tag (default "master")
+      --dryrun                     do not actually perform the action
+      --gccversion string          enforce a specific gcc version for the build
+  -h, --help                       help for kubernetes-in-cluster
+      --image-pull-secret string   ImagePullSecret
+      --kernelconfigdata string    base64 encoded kernel config data: in some systems it can be found under the /boot directory, in other it is gzip compressed under /proc
+      --kernelrelease string       kernel release to build the module for, it can be found by executing 'uname -v'
+      --kernelurls strings         list of kernel header urls (e.g. --kernelurls <URL1> --kernelurls <URL2> --kernelurls "<URL3>,<URL4>")
+      --kernelversion string       kernel version to build the module for, it's the numeric value after the hash when you execute 'uname -v' (default "1")
+  -l, --loglevel string            log level (default "info")
+      --moduledevicename string    kernel module device name (the default is falco, so the device will be under /dev/falco*) (default "falco")
+      --moduledrivername string    kernel module driver name, i.e. the name you see when you check installed modules via lsmod (default "falco")
+  -n, --namespace string           If present, the namespace scope for the pods and its config  (default "default")
+      --output-module string       filepath where to save the resulting kernel module
+      --output-probe string        filepath where to save the resulting eBPF probe
+      --proxy string               the proxy to use to download data
+      --repo-name string           repository github name (default "libs")
+      --repo-org string            repository github organization (default "falcosecurity")
+      --run-as-user int            Pods runner user
+  -t, --target string              the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+      --timeout int                timeout in seconds (default 120)
+```
+
 ### SEE ALSO
 
 * [driverkit](driverkit.md)	 - A command line tool to build Falco kernel modules and eBPF probes.
diff --git a/docs/driverkit_kubernetes.md b/docs/driverkit_kubernetes.md
index 62b2f7f..04b3df4 100644
--- a/docs/driverkit_kubernetes.md
+++ b/docs/driverkit_kubernetes.md
@@ -6,6 +6,52 @@ Build Falco kernel modules and eBPF probes against a Kubernetes cluster.
 driverkit kubernetes [flags]
 ```
 
+### Options
+
+```
+      --architecture string            target architecture for the built driver, one of [amd64,arm64] (default "amd64")
+      --as string                      username to impersonate for the operation, user could be a regular user or a service account in a namespace
+      --as-group stringArray           group to impersonate for the operation, this flag can be repeated to specify multiple groups
+      --as-uid string                  uID to impersonate for the operation
+      --builderimage string            docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+      --builderimagebase string        base docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+      --cache-dir string               default cache directory (default "$HOME/.kube/cache")
+      --certificate-authority string   path to a cert file for the certificate authority
+      --client-certificate string      path to a client certificate file for TLS
+      --client-key string              path to a client key file for TLS
+      --cluster string                 the name of the kubeconfig cluster to use
+  -c, --config string                  config file path (default $HOME/.driverkit.yaml if exists)
+      --context string                 the name of the kubeconfig context to use
+      --driverversion string           driver version as a git commit hash or as a git tag (default "master")
+      --dryrun                         do not actually perform the action
+      --gccversion string              enforce a specific gcc version for the build
+  -h, --help                           help for kubernetes
+      --image-pull-secret string       ImagePullSecret
+      --insecure-skip-tls-verify       if true, the server's certificate will not be checked for validity, this will make your HTTPS connections insecure
+      --kernelconfigdata string        base64 encoded kernel config data: in some systems it can be found under the /boot directory, in other it is gzip compressed under /proc
+      --kernelrelease string           kernel release to build the module for, it can be found by executing 'uname -v'
+      --kernelurls strings             list of kernel header urls (e.g. --kernelurls <URL1> --kernelurls <URL2> --kernelurls "<URL3>,<URL4>")
+      --kernelversion string           kernel version to build the module for, it's the numeric value after the hash when you execute 'uname -v' (default "1")
+      --kubeconfig string              path to the kubeconfig file to use for CLI requests
+  -l, --loglevel string                log level (default "info")
+      --moduledevicename string        kernel module device name (the default is falco, so the device will be under /dev/falco*) (default "falco")
+      --moduledrivername string        kernel module driver name, i.e. the name you see when you check installed modules via lsmod (default "falco")
+  -n, --namespace string               If present, the namespace scope for the pods and its config  (default "default")
+      --output-module string           filepath where to save the resulting kernel module
+      --output-probe string            filepath where to save the resulting eBPF probe
+      --proxy string                   the proxy to use to download data
+      --repo-name string               repository github name (default "libs")
+      --repo-org string                repository github organization (default "falcosecurity")
+      --request-timeout string         the length of time to wait before giving up on a single server request, non-zero values should contain a corresponding time unit (e.g, 1s, 2m, 3h), a value of zero means don't timeout requests (default "0")
+      --run-as-user int                Pods runner user
+  -s, --server string                  the address and port of the Kubernetes API server
+  -t, --target string                  the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+      --timeout int                    timeout in seconds (default 120)
+      --tls-server-name string         server name to use for server certificate validation, if it is not provided, the hostname used to contact the server is used
+      --token string                   bearer token for authentication to the API server
+      --user string                    the name of the kubeconfig user to use
+```
+
 ### SEE ALSO
 
 * [driverkit](driverkit.md)	 - A command line tool to build Falco kernel modules and eBPF probes.
diff --git a/pkg/driverbuilder/builder/bottlerocket.go b/pkg/driverbuilder/builder/bottlerocket.go
new file mode 100644
index 0000000..a6fad15
--- /dev/null
+++ b/pkg/driverbuilder/builder/bottlerocket.go
@@ -0,0 +1,30 @@
+package builder
+
+import (
+	"github.com/falcosecurity/driverkit/pkg/kernelrelease"
+)
+
+// TargetTypeBottlerocket identifies the Bottlerocket target.
+const TargetTypeBottlerocket Type = "bottlerocket"
+
+func init() {
+	BuilderByTarget[TargetTypeBottlerocket] = &bottlerocket{
+		vanilla{},
+	}
+}
+
+type bottlerocket struct {
+	vanilla
+}
+
+func (b *bottlerocket) Name() string {
+	return TargetTypeBottlerocket.String()
+}
+
+func (b *bottlerocket) TemplateData(c Config, kr kernelrelease.KernelRelease, urls []string) interface{} {
+	return vanillaTemplateData{
+		commonTemplateData: c.toTemplateData(b, kr),
+		KernelDownloadURL:  urls[0],
+		KernelLocalVersion: kr.FullExtraversion,
+	}
+}
diff --git a/pkg/driverbuilder/builder/build.go b/pkg/driverbuilder/builder/build.go
index c78474c..d6ddf0c 100644
--- a/pkg/driverbuilder/builder/build.go
+++ b/pkg/driverbuilder/builder/build.go
@@ -7,21 +7,22 @@ import (
 
 // Build contains the info about the on-going build.
 type Build struct {
-	TargetType         Type
-	KernelConfigData   string
-	KernelRelease      string
-	KernelVersion      string
-	DriverVersion      string
-	Architecture       string
-	ModuleFilePath     string
-	ProbeFilePath      string
-	ModuleDriverName   string
-	ModuleDeviceName   string
-	CustomBuilderImage string
-	KernelUrls         []string
-	GCCVersion         string
-	RepoOrg            string
-	RepoName           string
+	TargetType             Type
+	KernelConfigData       string
+	KernelRelease          string
+	KernelVersion          string
+	DriverVersion          string
+	Architecture           string
+	ModuleFilePath         string
+	ProbeFilePath          string
+	ModuleDriverName       string
+	ModuleDeviceName       string
+	CustomBuilderImage     string
+	CustomBuilderImageBase string
+	KernelUrls             []string
+	GCCVersion             string
+	RepoOrg                string
+	RepoName               string
 }
 
 func (b *Build) KernelReleaseFromBuildConfig() kernelrelease.KernelRelease {
diff --git a/pkg/driverbuilder/builder/builders.go b/pkg/driverbuilder/builder/builders.go
index 20db978..89dccf4 100644
--- a/pkg/driverbuilder/builder/builders.go
+++ b/pkg/driverbuilder/builder/builders.go
@@ -280,7 +280,14 @@ func (b *Build) GetBuilderImage() string {
 
 	// A bit complicated because we must check that
 	// "auto:tag" custom builder image was not passed
-	builderImage := BaseImage
+	var builderImage string
+
+	if len(b.CustomBuilderImageBase) != 0 {
+		builderImage = b.CustomBuilderImageBase
+	} else {
+		builderImage = BaseImage
+	}
+
 	names := strings.Split(builderImage, ":")
 	// Updated image tag if no "auto" custom builder image was passed
 	if imageTag == "" {
diff --git a/pkg/kernelrelease/kernelrelease.go b/pkg/kernelrelease/kernelrelease.go
index cb6f86e..e2f66e1 100644
--- a/pkg/kernelrelease/kernelrelease.go
+++ b/pkg/kernelrelease/kernelrelease.go
@@ -11,7 +11,7 @@ import (
 )
 
 var (
-	kernelVersionPattern = regexp.MustCompile(`(?P<fullversion>^(?P<version>0|[1-9]\d*)\.(?P<patchlevel>0|[1-9]\d*)\.(?P<sublevel>0|[1-9]\d*))(?P<fullextraversion>[-|.](?P<extraversion>0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)([\.+~](0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-_]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$`)
+	kernelVersionPattern = regexp.MustCompile(`(?P<fullversion>^(?P<version>0|[1-9]\d*)\.(?P<patchlevel>0|[1-9]\d*)\.?(?P<sublevel>0|[1-9]\d*)?)(?P<fullextraversion>[-|.](?P<extraversion>0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)([\.+~](0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-_]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$`)
 )
 
 const (
@@ -107,7 +107,11 @@ func FromString(kernelVersionStr string) KernelRelease {
 			case "patchlevel":
 				kv.Minor, err = strconv.ParseUint(match[i], 10, 64)
 			case "sublevel":
-				kv.Patch, err = strconv.ParseUint(match[i], 10, 64)
+				if len(match[i]) > 0 {
+					// We accept a missing sublevel (defaulting to 0)
+					// eg: 6.1.arch1-1
+					kv.Patch, err = strconv.ParseUint(match[i], 10, 64)
+				}
 			case "extraversion":
 				kv.Extraversion = match[i]
 			case "fullextraversion":


Test patch:
diff --git a/cmd/testdata/templates/flags.txt b/cmd/testdata/templates/flags.txt
index 415c9cb..39d3569 100644
--- a/cmd/testdata/templates/flags.txt
+++ b/cmd/testdata/templates/flags.txt
@@ -1,6 +1,7 @@
 Flags:
       --architecture string       target architecture for the built driver, one of {{ .Architectures }} (default "{{ .CurrentArch }}")
       --builderimage string       docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
+      --builderimagebase string   base docker image to be used to build the kernel module and eBPF probe. If not provided, an automatically selected image will be used.
   -c, --config string             config file path (default $HOME/.driverkit.yaml if exists)
       --driverversion string      driver version as a git commit hash or as a git tag (default "master")
       --dryrun                    do not actually perform the action