Repository: falcosecurity/driverkit
Pull request #258 (2023-03-17 15:32:18)

feat(builder-indexes): cli flag to build Images map 
feat(builder-indexes): cli flag to build Images map without scanning docker registries

**What type of PR is this?**

/kind feature

**Any specific area of the project related to this PR?**

/area cmd

/area pkg

/area docs

**What this PR does / why we need it**:

New flag to be able to set the builder.Images map with data given via cli to not search and parse Docker registries.
The flag `builderindex` is string slice, that I parse by splitting with `;` character, considering the first term as the image name, the second term as the target, and everything else as GCC versions available on the image.

I guess the format is not optimum, but I can't find other and simpler way to parse this kind of info from CLI:

Usage: --builderindex "`<image>`;`<target>`;`<gcc-version>`;`<gcc-version>`;`...`" 
Example: --builderindex "`falcosecurity/driverkit-builder`;`any`;`4.9.0`;`5.0.0`;`6.0.0`;`8.0.0`"

Multiple --buildindex can be set to insert different images


**Which issue(s) this PR fixes**:

None, context here: #256

**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 CLI flag '--builderindex' making able to set up the Images map from CLI.
  Usage: --builderindex "<image>;<target>;<gcc-versuin>;<gcc-versuin>;...".
  Example: --builderindex "falcosecurity/driverkit-builder;any;4.9.0;5.0.0;6.0.0;8.0.0"
```


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

This pull-request has been approved by: *<a href="https://github.com/falcosecurity/driverkit/pull/258#" title="Author self-approved">Lowaiz</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign leodido for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

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

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

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

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":["leodido"]} -->
FedeDP: Thanks for this PR!
I was wondering whether to use the same `builderrepo` option instead; like:
* if `builderrepo` points to a local file, we then expect it to provide a list of `"<image>;<target>;<gcc-version>,<gcc-version>,<gcc-version>\n`
* else, we expect it to be a docker repo

It is simpler; what i don't really like about having the new option (thinking twice about it) is how it plays together with `builderrepo` for example, given that they both provide the same informations.

Moreover, it would be huge to abstract everything behind an interface-like object, eg:
```go
type ImagesLister interface {
   ListImage() []Images
}

type FileImagesLister struct {
  *os.File
}

func (f *FileImagesLister) ListImage() []Images { ... }

type RepoImagesLister struct {
  repo string
}

func (f *RepoImagesLister) ListImage() []Images { ... }

RootOptions.go:
for builderrepo := range builderrepos {
  if isFile(builderrepo) {
     builderepostlister.append(newFileImagesLister(builderrepo))
 else
    builderepostlister.append(newRepoImagesLister(builderrepo))
}
```
Then basically, `images.findImage()` will receive the list of `builderepostlister`(that will actually be stored in `builder.Build`), and calls `ListImage()` on each one. 
This way, we also support using `image` subcmd with the new feat.

WDYT?
I know it is a bigger change but it will be much better IMHO!


Lowaiz: Hey,

That's a clever idea. And when changing to falctoctl later will be simpler.
FedeDP: Last round of review, i think after these ones will be addressed, we will be ready :rocket: 
FedeDP: Btw this is already good to go; if you are willing to spend a little bit more time on refining this, it's great! Otherwise i can do that myself on top of yours once it's merged :)
Sorry for the long list of "nit picks", but i feel like we are doing a really good future-proof job here!
Lowaiz: @FedeDP 
I guess, it's good now !
FedeDP: Yep @Lowaiz!
Thanks for your patience! It LGTM! :) 

Patch:
diff --git a/Example_configs.md b/Example_configs.md
index 8b7ce24..b9bddcf 100644
--- a/Example_configs.md
+++ b/Example_configs.md
@@ -79,6 +79,28 @@ output:
 driverversion: master
 ```
 
+## fedora
+
+```yaml
+kernelrelease: 5.19.16-200.fc36.x86_64
+kernelversion: 1
+target: fedora
+output:
+  module: /tmp/falco-fedora.ko
+driverversion: master
+```
+
+## oracle linux 8
+
+```yaml
+kernelrelease: 5.4.17-2011.3.2.1.el8uek.x86_64
+kernelversion: 1
+target: ol
+output:
+  module: /tmp/falco-ol8.ko
+driverversion: master
+```
+
 ## amazonlinux
 
 ```yaml
diff --git a/cmd/root.go b/cmd/root.go
index 75a56be..fee9b0d 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -144,7 +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.StringSliceVar(&rootOpts.BuilderRepos, "builderrepo", rootOpts.BuilderRepos, "list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit")
+	flags.StringSliceVar(&rootOpts.BuilderRepos, "builderrepo", rootOpts.BuilderRepos, "list of docker repositories or yaml file (absolute path) containing builder images index with the format 'images: [ { target:<target>, name:<image-name>, gcc_versions: [ <gcc-tag> ] },...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo '/path/to/my/index.yaml'.")
 	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 018f68e..66e18d6 100644
--- a/cmd/root_options.go
+++ b/cmd/root_options.go
@@ -2,13 +2,13 @@ package cmd
 
 import (
 	"fmt"
-
 	"github.com/creasty/defaults"
 	"github.com/falcosecurity/driverkit/pkg/driverbuilder/builder"
 	"github.com/falcosecurity/driverkit/pkg/kernelrelease"
 	"github.com/falcosecurity/driverkit/validate"
 	"github.com/go-playground/validator/v10"
 	logger "github.com/sirupsen/logrus"
+	"strings"
 )
 
 // OutputOptions wraps the two drivers that driverkit builds.
@@ -33,7 +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"`
-	BuilderRepos     []string `validate:"omitempty" name:"docker repositories to look for builder images"`
+	BuilderRepos     []string `default:"[\"docker.io/falcosecurity/driverkit\"]" validate:"omitempty" name:"docker repositories to look for builder images or absolute path pointing to a yaml file containing builder image index"`
 	GCCVersion       string   `validate:"omitempty,semvertolerant" name:"gcc version"`
 	KernelUrls       []string `name:"kernel header urls"`
 	Repo             RepoOptions
@@ -132,11 +132,17 @@ func (ro *RootOptions) toBuild() *builder.Build {
 		KernelUrls:       ro.KernelUrls,
 		RepoOrg:          ro.Repo.Org,
 		RepoName:         ro.Repo.Name,
+		Images:           make(builder.ImagesMap),
 	}
 
-	// Always append falcosecurity repo; Note: this is a prio first slice
-	// therefore, default falcosecurity repo has lowest prio.
-	build.BuilderRepos = append(build.BuilderRepos, "docker.io/falcosecurity/driverkit")
+	// loop over BuilderRepos to constuct the list ImagesListers based on the value of the builderRepo, if it's a local path, add FileImagesLister, otherwise add RepoImagesLister
+	for _, builderRepo := range build.BuilderRepos {
+		if strings.HasPrefix(builderRepo, "/") {
+			build.ImagesListers = append(build.ImagesListers, &builder.FileImagesLister{FilePath: builderRepo})
+		} else {
+			build.ImagesListers = append(build.ImagesListers, builder.NewRepoImagesLister(builderRepo, build))
+		}
+	}
 
 	// attempt the build in case it comes from an invalid config
 	kr := build.KernelReleaseFromBuildConfig()
diff --git a/docker/builders/builder-any-x86_64_gcc8.0.0_gcc6.0.0_gcc5.0.0_gcc4.9.0_gcc4.8.0.Dockerfile b/docker/builders/builder-any-x86_64_gcc8.0.0_gcc6.0.0_gcc5.0.0_gcc4.9.0_gcc4.8.0.Dockerfile
index c21ca2b..9de43a2 100644
--- a/docker/builders/builder-any-x86_64_gcc8.0.0_gcc6.0.0_gcc5.0.0_gcc4.9.0_gcc4.8.0.Dockerfile
+++ b/docker/builders/builder-any-x86_64_gcc8.0.0_gcc6.0.0_gcc5.0.0_gcc4.9.0_gcc4.8.0.Dockerfile
@@ -39,14 +39,16 @@ RUN apt-get update \
 	software-properties-common \
 	gpg \
 	zstd \
+	gawk \
     && rm -rf /var/lib/apt/lists/*
 
 RUN if [ "$TARGETARCH" = "amd64" ] ; then apt-get install -y --no-install-recommends libmpx2; fi
 
 # gcc 4.9 is required on x86 to build some 3.10+ kernels
 # note: on arm gcc 4.9 could not be found.
-RUN echo 'deb http://deb.debian.org/debian jessie main' >>/etc/apt/sources.list # gcc 4.9 on x86_64
-# jessie repo is unsigned therefore the APT options
+RUN echo 'deb http://dk.archive.ubuntu.com/ubuntu/ xenial main' >> /etc/apt/sources.list     && \
+	echo 'deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe' >> /etc/apt/sources.list
+# repo is unsigned therefore the APT options
 RUN if [ "$TARGETARCH" = "amd64" ] ; then apt-get -o Acquire::AllowInsecureRepositories=true -o Acquire::AllowDowngradeToInsecureRepositories=true update && apt-get -o APT::Get::AllowUnauthenticated=true install -y --no-install-recommends gcc-4.9; fi
 
 # gcc 6 is no longer included in debian stable, but we need it to
diff --git a/docs/driverkit.md b/docs/driverkit.md
index 62e11d5..1f062b8 100644
--- a/docs/driverkit.md
+++ b/docs/driverkit.md
@@ -11,7 +11,7 @@ driverkit
 ```
       --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.
-      --builderrepo strings       list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit
+      --builderrepo strings       list of docker repositories or file (absolute path) containing builder images index with the format '<image>,<target>,<gcc-version>[,<gcc-version>,...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo /path/to/my/index.txt. Index file line exmaple: yourorg/driverkit-builder;any;4.9.0;5.0.0;6.0.0;8.0.0 (default [docker.io/falcosecurity/driverkit])
   -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
@@ -29,7 +29,7 @@ driverkit
       --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,bottlerocket,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+  -t, --target string             the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,bottlerocket,centos,debian,fedora,ol,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
       --timeout int               timeout in seconds (default 120)
 ```
 
diff --git a/docs/driverkit_docker.md b/docs/driverkit_docker.md
index c1a9f8b..8526d2b 100644
--- a/docs/driverkit_docker.md
+++ b/docs/driverkit_docker.md
@@ -11,7 +11,7 @@ driverkit docker [flags]
 ```
       --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.
-      --builderrepo strings       list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit
+      --builderrepo strings       list of docker repositories or file (absolute path) containing builder images index with the format '<image>,<target>,<gcc-version>[,<gcc-version>,...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo /path/to/my/index.txt. Index file line exmaple: yourorg/driverkit-builder;any;4.9.0;5.0.0;6.0.0;8.0.0 (default [docker.io/falcosecurity/driverkit])
   -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
@@ -29,7 +29,7 @@ driverkit docker [flags]
       --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,bottlerocket,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+  -t, --target string             the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,bottlerocket,centos,debian,fedora,ol,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
       --timeout int               timeout in seconds (default 120)
 ```
 
diff --git a/docs/driverkit_images.md b/docs/driverkit_images.md
index 0860434..04accc7 100644
--- a/docs/driverkit_images.md
+++ b/docs/driverkit_images.md
@@ -11,7 +11,7 @@ driverkit images [flags]
 ```
       --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.
-      --builderrepo strings       list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit
+      --builderrepo strings       list of docker repositories or file (absolute path) containing builder images index with the format '<image>,<target>,<gcc-version>[,<gcc-version>,...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo /path/to/my/index.txt. Index file line exmaple: yourorg/driverkit-builder;any;4.9.0;5.0.0;6.0.0;8.0.0 (default [docker.io/falcosecurity/driverkit])
   -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
@@ -29,7 +29,7 @@ driverkit images [flags]
       --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,bottlerocket,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+  -t, --target string             the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,bottlerocket,centos,debian,fedora,ol,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
       --timeout int               timeout in seconds (default 120)
 ```
 
diff --git a/docs/driverkit_kubernetes-in-cluster.md b/docs/driverkit_kubernetes-in-cluster.md
index a72fc89..c28c276 100644
--- a/docs/driverkit_kubernetes-in-cluster.md
+++ b/docs/driverkit_kubernetes-in-cluster.md
@@ -11,7 +11,7 @@ driverkit kubernetes-in-cluster [flags]
 ```
       --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.
-      --builderrepo strings        list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit
+      --builderrepo strings        list of docker repositories or file (absolute path) containing builder images index with the format '<image>,<target>,<gcc-version>[,<gcc-version>,...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo /path/to/my/index.txt. Index file line exmaple: yourorg/driverkit-builder;any;4.9.0;5.0.0;6.0.0;8.0.0 (default [docker.io/falcosecurity/driverkit])
   -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
@@ -32,7 +32,7 @@ driverkit kubernetes-in-cluster [flags]
       --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,bottlerocket,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+  -t, --target string              the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,bottlerocket,centos,debian,fedora,ol,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
       --timeout int                timeout in seconds (default 120)
 ```
 
diff --git a/docs/driverkit_kubernetes.md b/docs/driverkit_kubernetes.md
index fbe3920..eb590ff 100644
--- a/docs/driverkit_kubernetes.md
+++ b/docs/driverkit_kubernetes.md
@@ -14,7 +14,7 @@ driverkit kubernetes [flags]
       --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.
-      --builderrepo strings            list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit
+      --builderrepo strings            list of docker repositories or file (absolute path) containing builder images index with the format '<image>,<target>,<gcc-version>[,<gcc-version>,...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo /path/to/my/index.txt. Index file line exmaple: yourorg/driverkit-builder;any;4.9.0;5.0.0;6.0.0;8.0.0 (default [docker.io/falcosecurity/driverkit])
       --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
@@ -45,7 +45,7 @@ driverkit kubernetes [flags]
       --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,bottlerocket,centos,debian,fedora,flatcar,minikube,opensuse,photon,redhat,rocky,ubuntu,vanilla]
+  -t, --target string                  the system to target the build for, one of [almalinux,amazonlinux,amazonlinux2,amazonlinux2022,arch,bottlerocket,centos,debian,fedora,ol,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
diff --git a/docs/index.yaml b/docs/index.yaml
new file mode 100644
index 0000000..a0b0b93
--- /dev/null
+++ b/docs/index.yaml
@@ -0,0 +1,9 @@
+images:
+  - name: docker.io/falcosecurity/driverkit-builder-any-x86_64_gcc8.0.0_gcc6.0.0_gcc5.0.0_gcc4.9.0_gcc4.8.0
+    target: any
+    gcc_versions:
+      - 8.0.0
+      - 6.0.0
+      - 5.0.0
+      - 4.9.0
+      - 4.8.0
\ No newline at end of file
diff --git a/pkg/driverbuilder/builder/build.go b/pkg/driverbuilder/builder/build.go
index 8f8981d..d025bb0 100644
--- a/pkg/driverbuilder/builder/build.go
+++ b/pkg/driverbuilder/builder/build.go
@@ -19,6 +19,7 @@ type Build struct {
 	ModuleDeviceName string
 	BuilderImage     string
 	BuilderRepos     []string
+	ImagesListers    []ImagesLister
 	KernelUrls       []string
 	GCCVersion       string
 	RepoOrg          string
diff --git a/pkg/driverbuilder/builder/image.go b/pkg/driverbuilder/builder/image.go
index 259ad38..1aa43b2 100644
--- a/pkg/driverbuilder/builder/image.go
+++ b/pkg/driverbuilder/builder/image.go
@@ -8,17 +8,41 @@ import (
 	"github.com/docker/docker/client"
 	"github.com/falcosecurity/driverkit/pkg/kernelrelease"
 	logger "github.com/sirupsen/logrus"
+	"gopkg.in/yaml.v3"
 	"log"
+	"os"
 	"regexp"
 	"strings"
 )
 
+type YAMLImage struct {
+	Target      string   `yaml:"target"`
+	GCCVersions []string `yaml:"gcc_versions"` // we expect images to internally link eg: gcc5 to gcc5.0.0
+	Name        string   `yaml:"name"`
+}
+
+type YAMLImagesList struct {
+	Images []YAMLImage `yaml:"images"`
+}
+
 type Image struct {
 	Target     Type
 	GCCVersion semver.Version // we expect images to internally link eg: gcc5 to gcc5.0.0
 	Name       string
 }
 
+type ImagesLister interface {
+	LoadImages() []Image
+}
+
+type FileImagesLister struct {
+	FilePath string
+}
+
+type RepoImagesLister struct {
+	repo string
+}
+
 type ImageKey string
 
 func (i *Image) toKey() ImageKey {
@@ -27,6 +51,8 @@ func (i *Image) toKey() ImageKey {
 
 type ImagesMap map[ImageKey]Image
 
+var repoRegs = make([]*regexp.Regexp, 0, 2)
+
 func (im ImagesMap) findImage(target Type, gccVers semver.Version) (Image, bool) {
 	targetImage := Image{
 		Target:     target,
@@ -45,78 +71,126 @@ func (im ImagesMap) findImage(target Type, gccVers semver.Version) (Image, bool)
 	return Image{}, false
 }
 
-func (b *Build) LoadImages() {
-	cli, err := client.NewClientWithOpts(client.FromEnv)
+func (f *FileImagesLister) LoadImages() []Image {
+	// loop over lines in file to print them
+	file, err := os.ReadFile(f.FilePath)
 	if err != nil {
-		log.Fatal(err)
+		logger.WithError(err).WithField("FilePath", f.FilePath).Fatal("error opening builder repo file")
+	}
+
+	var imageList YAMLImagesList
+	var res []Image
+
+	err = yaml.Unmarshal(file, &imageList)
+	if err != nil {
+		logger.WithError(err).WithField("FilePath", f.FilePath).Fatal("error unmarshalling builder repo file")
+	}
+
+	if len(imageList.Images) == 0 {
+		logger.WithField("FilePath", f.FilePath).Warning("Invalid image list file: expected at least 1 image")
 	}
 
-	// Create the proper regexes to load "any" and target-specific images for requested arch
-	arch := kernelrelease.Architecture(b.Architecture).ToNonDeb()
-	regs := make([]*regexp.Regexp, 0)
-	targetFmt := fmt.Sprintf("driverkit-builder-%s-%s(?P<gccVers>(_gcc[0-9]+.[0-9]+.[0-9]+)+)$", b.TargetType.String(), arch)
-	regs = append(regs, regexp.MustCompile(targetFmt))
-	genericFmt := fmt.Sprintf("driverkit-builder-any-%s(?P<gccVers>(_gcc[0-9]+.[0-9]+.[0-9]+)+)$", arch)
-	regs = append(regs, regexp.MustCompile(genericFmt))
-
-	b.Images = make(ImagesMap)
-	for _, repo := range b.BuilderRepos {
-		imgs, err := cli.ImageSearch(context.Background(), repo, types.ImageSearchOptions{Limit: 100})
-		if err != nil {
-			logger.Warnf("Skipping repo %s: %s\n", repo, err.Error())
-			continue
+	for _, image := range imageList.Images {
+		if len(image.GCCVersions) == 0 {
+			logger.WithField("FilePath", f.FilePath).WithField("image", image).Fatal("Invalid image list file: expected at least 1 gcc version")
 		}
-		for _, img := range imgs {
-			for regIdx, reg := range regs {
-				match := reg.FindStringSubmatch(img.Name)
-				if len(match) == 0 {
-					continue
-				}
+		for _, gcc := range image.GCCVersions {
+			buildImage := Image{
+				Name:       image.Name,
+				Target:     Type(image.Target),
+				GCCVersion: mustParseTolerant(gcc),
+			}
+			res = append(res, buildImage)
+		}
+	}
+	return res
+}
 
-				var gccVers []string
-				for i, name := range reg.SubexpNames() {
-					if i > 0 && i <= len(match) {
-						switch name {
-						case "gccVers":
-							gccVers = strings.Split(match[i], "_gcc")
-							gccVers = gccVers[1:] // remove initial whitespace
-						}
+func NewRepoImagesLister(repo string, build *Build) *RepoImagesLister {
+	if len(repoRegs) == 0 {
+		// Create the proper regexes to load "any" and target-specific images for requested arch
+		arch := kernelrelease.Architecture(build.Architecture).ToNonDeb()
+		targetFmt := fmt.Sprintf("driverkit-builder-(?P<target>%s)-%s(?P<gccVers>(_gcc[0-9]+.[0-9]+.[0-9]+)+)$", build.TargetType.String(), arch)
+		repoRegs = append(repoRegs, regexp.MustCompile(targetFmt))
+		genericFmt := fmt.Sprintf("driverkit-builder-any-%s(?P<gccVers>(_gcc[0-9]+.[0-9]+.[0-9]+)+)$", arch)
+		repoRegs = append(repoRegs, regexp.MustCompile(genericFmt))
+	}
+	return &RepoImagesLister{repo: repo}
+}
+
+func (repo *RepoImagesLister) LoadImages() []Image {
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	if err != nil {
+		log.Fatal(err)
+	}
+	imgs, err := cli.ImageSearch(context.Background(), repo.repo, types.ImageSearchOptions{Limit: 100})
+	if err != nil {
+		logger.WithField("Repository", repo.repo).WithError(err).Warnf("Skipping repo")
+		return []Image{}
+	}
+	var res []Image
+	for _, img := range imgs {
+		for _, reg := range repoRegs {
+			match := reg.FindStringSubmatch(img.Name)
+			if len(match) == 0 {
+				continue
+			}
+
+			var gccVers []string
+			target := ""
+			for i, name := range reg.SubexpNames() {
+				if i > 0 && i <= len(match) {
+					switch name {
+					case "gccVers":
+						gccVers = strings.Split(match[i], "_gcc")
+						gccVers = gccVers[1:] // remove initial whitespace
+					case "target":
+						target = match[i]
 					}
 				}
+			}
 
-				if len(gccVers) == 0 {
-					logger.Debug("Malformed image name: ", img.Name, len(match))
-					continue
-				}
+			if len(gccVers) == 0 {
+				logger.Debug("Malformed image name: ", img.Name, len(match))
+				continue
+			}
 
-				// Note: we store "any" target images as "any",
-				// instead of adding them to the target,
-				// because we always prefer specific target images,
-				// and we cannot guarantee here that any subsequent docker repos
-				// does not provide a target-specific image that offers same gcc version
-				for _, gccVer := range gccVers {
-					// If user set a fixed gcc version, only load images that provide it.
-					if b.GCCVersion != "" && b.GCCVersion != gccVer {
-						continue
-					}
-					buildImage := Image{
-						GCCVersion: mustParseTolerant(gccVer),
-						Name:       img.Name,
-					}
-					if regIdx == 0 {
-						buildImage.Target = b.TargetType
-					} else {
-						buildImage.Target = Type("any")
-					}
-					// Skip if key already exists: we have a descending prio list of docker repos!
-					if _, ok := b.Images[buildImage.toKey()]; !ok {
-						b.Images[buildImage.toKey()] = buildImage
-					}
+			// Note: we store "any" target images as "any",
+			// instead of adding them to the target,
+			// because we always prefer specific target images,
+			// and we cannot guarantee here that any subsequent docker repos
+			// does not provide a target-specific image that offers same gcc version
+			for _, gccVer := range gccVers {
+				// If user set a fixed gcc version, only load images that provide it.
+				buildImage := Image{
+					GCCVersion: mustParseTolerant(gccVer),
+					Name:       img.Name,
+				}
+				if target != "" {
+					buildImage.Target = Type(target)
+				} else {
+					buildImage.Target = Type("any")
 				}
+				res = append(res, buildImage)
+			}
+		}
+	}
+	return res
+}
+
+func (b *Build) LoadImages() {
+	for _, imagesLister := range b.ImagesListers {
+		for _, image := range imagesLister.LoadImages() {
+			if b.GCCVersion != "" && b.GCCVersion != image.GCCVersion.String() {
+				continue
+			}
+			// Skip if key already exists: we have a descending prio list of docker repos!
+			if _, ok := b.Images[image.toKey()]; !ok {
+				b.Images[image.toKey()] = image
 			}
 		}
 	}
 	if len(b.Images) == 0 {
-		log.Fatal("Could not load any builder image. Leaving.")
+		logger.Fatal("Could not load any builder image. Leaving.")
 	}
 }
diff --git a/pkg/driverbuilder/builder/oracle.go b/pkg/driverbuilder/builder/oracle.go
new file mode 100644
index 0000000..2958e3f
--- /dev/null
+++ b/pkg/driverbuilder/builder/oracle.go
@@ -0,0 +1,113 @@
+package builder
+
+import (
+	_ "embed"
+	"fmt"
+	"strings"
+
+	"github.com/falcosecurity/driverkit/pkg/kernelrelease"
+)
+
+//go:embed templates/oracle.sh
+var oracleTemplate string
+
+// TargetTypeoracle identifies the oracle target ("ol" is the ID from /etc/os-release that Oracle uses)
+const TargetTypeoracle Type = "ol"
+
+func init() {
+	BuilderByTarget[TargetTypeoracle] = &oracle{}
+}
+
+// oracle is a driverkit target.
+type oracle struct {
+}
+
+type oracleTemplateData struct {
+	commonTemplateData
+	KernelDownloadURL string
+}
+
+func (c *oracle) Name() string {
+	return TargetTypeoracle.String()
+}
+
+func (c *oracle) TemplateScript() string {
+	return oracleTemplate
+}
+
+func (c *oracle) URLs(_ Config, kr kernelrelease.KernelRelease) ([]string, error) {
+
+	// oracle FullExtraversion looks like "-2047.510.5.5.el7uek.x86_64"
+	// need to get the "el7uek" out of the middle
+	splitVersion := strings.Split(kr.FullExtraversion, ".")
+	oracleVersion := splitVersion[len(splitVersion)-2] // [ "-2047", "510", "5", "5", "el7uek","x86_64" ] want -2
+
+	// trim off the "el" and "uek" from oracleVersion
+	version := strings.Trim(strings.Trim(oracleVersion, "el"), "uek")
+
+	// sometimes Oracle 8 does "8_x" for version, only want the "8"
+	if strings.Contains(version, "_") {
+		version = strings.Split(version, "_")[0]
+	}
+
+	// list of possible UEK versions, which are used in the URL - ex: "UEKR3"
+	// may need to evolve over time if Oracle adds more
+	ueks := []string{"R3", "R4", "R5", "R6", "R7"}
+
+	// template the kernel info into all possible URL strings
+	urls := []string{
+		fmt.Sprintf( // latest (Oracle 7)
+			"http://yum.oracle.com/repo/OracleLinux/OL%s/latest/%s/getPackage/kernel-devel-%s%s.rpm",
+			version,
+			kr.Architecture.ToNonDeb(),
+			kr.Fullversion,
+			kr.FullExtraversion,
+		),
+		fmt.Sprintf( // latest + baseos (Oracle 8 + 9)
+			"http://yum.oracle.com/repo/OracleLinux/OL%s/baseos/latest/%s/getPackage/kernel-devel-%s%s.rpm",
+			version,
+			kr.Architecture.ToNonDeb(),
+			kr.Fullversion,
+			kr.FullExtraversion,
+		),
+		fmt.Sprintf( // appstream (Oracle 8 + 9)
+			"http://yum.oracle.com/repo/OracleLinux/OL%s/appstream/%s/getPackage/kernel-devel-%s%s.rpm",
+			version,
+			kr.Architecture.ToNonDeb(),
+			kr.Fullversion,
+			kr.FullExtraversion,
+		),
+		fmt.Sprintf( // MODRHCK (Oracle 7)
+			"http://yum.oracle.com/repo/OracleLinux/OL%s/MODRHCK/%s/getPackage/kernel-devel-%s%s.rpm",
+			version,
+			kr.Architecture.ToNonDeb(),
+			kr.Fullversion,
+			kr.FullExtraversion,
+		),
+	}
+
+	// add in all the UEK versions
+	for _, uekVers := range ueks {
+		urls = append(
+			urls,
+			fmt.Sprintf( // UEK versions URL
+				"http://yum.oracle.com/repo/OracleLinux/OL%s/UEK%s/%s/getPackage/kernel-uek-devel-%s%s.rpm",
+				version,
+				uekVers,
+				kr.Architecture.ToNonDeb(),
+				kr.Fullversion,
+				kr.FullExtraversion,
+			),
+		)
+	}
+
+	// return out all possible urls
+	return urls, nil
+}
+
+func (c *oracle) TemplateData(cfg Config, kr kernelrelease.KernelRelease, urls []string) interface{} {
+	return oracleTemplateData{
+		commonTemplateData: cfg.toTemplateData(c, kr),
+		KernelDownloadURL:  urls[0],
+	}
+}
diff --git a/pkg/driverbuilder/builder/templates/oracle.sh b/pkg/driverbuilder/builder/templates/oracle.sh
new file mode 100644
index 0000000..d6b0694
--- /dev/null
+++ b/pkg/driverbuilder/builder/templates/oracle.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -xeuo pipefail
+
+rm -Rf {{ .DriverBuildDir }}
+mkdir {{ .DriverBuildDir }}
+rm -Rf /tmp/module-download
+mkdir -p /tmp/module-download
+
+curl --silent -SL {{ .ModuleDownloadURL }} | tar -xzf - -C /tmp/module-download
+mv /tmp/module-download/*/driver/* {{ .DriverBuildDir }}
+
+cp /driverkit/module-Makefile {{ .DriverBuildDir }}/Makefile
+bash /driverkit/fill-driver-config.sh {{ .DriverBuildDir }}
+
+# Fetch the kernel
+mkdir /tmp/kernel-download
+cd /tmp/kernel-download
+curl --silent -o kernel-devel.rpm -SL {{ .KernelDownloadURL }}
+rpm2cpio kernel-devel.rpm | cpio --extract --make-directories
+rm -Rf /tmp/kernel
+mkdir -p /tmp/kernel
+mv usr/src/kernels/*/* /tmp/kernel
+
+{{ if .BuildModule }}
+# Build the module
+cd {{ .DriverBuildDir }}
+make CC=/usr/bin/gcc-{{ .GCCVersion }} KERNELDIR=/tmp/kernel
+mv {{ .ModuleDriverName }}.ko {{ .ModuleFullPath }}
+strip -g {{ .ModuleFullPath }}
+# Print results
+modinfo {{ .ModuleFullPath }}
+{{ end }}
+
+{{ if .BuildProbe }}
+# Build the eBPF probe
+cd {{ .DriverBuildDir }}/bpf
+make KERNELDIR=/tmp/kernel
+ls -l probe.o
+{{ end }}
\ No newline at end of file


Test patch:
diff --git a/cmd/testdata/templates/flags.txt b/cmd/testdata/templates/flags.txt
index a2f35f1..8156878 100644
--- a/cmd/testdata/templates/flags.txt
+++ b/cmd/testdata/templates/flags.txt
@@ -1,7 +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.
-      --builderrepo strings       list of docker repositories in descending priority order, used to search for builder images. Default falcosecurity/driverkit will always be enforced as lowest priority repo. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit
+      --builderrepo strings       list of docker repositories or yaml file (absolute path) containing builder images index with the format 'images: [ { target:<target>, name:<image-name>, gcc_versions: [ <gcc-tag> ] },...]', in descending priority order. Used to search for builder images. eg: --builderrepo myorg/driverkit --builderrepo falcosecurity/driverkit --builderrepo '/path/to/my/index.yaml'. (default [docker.io/falcosecurity/driverkit])
   -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