Repository: aquasecurity/tracee (Go)

Issue:
Add ExportedDataSources to .so plugins
Currently our shared object plugins only support exporting signatures. Data Sources should be added as well. This can be done in the same way we use a convention for the `ExportedSignatures` symbol, we can add an `ExportedDataSources` symbol.


Discussion:
Actually I'm not sure we should invest in the shared object (.so) anymore since it simply doesn't work well (e.g. it is required to compile the .so with the same go version as tracee was compiled with). In addition to that, when tracee is compiled statically it can't load dynamic libraries (and we want to support statically compiled binary so tracee can run without any other dependency).
The only use case I can think of for having .so is to update signatures in runtime, however, this use case can also be achieved by updating the tracee binary itself. In the future, we should consider a different mechanism to update signatures, but I don't think .so will be the best solution.

> Actually I'm not sure we should invest in the shared object (.so) anymore since it simply doesn't work well (e.g. it is required to compile the .so with the same go version as tracee was compiled with). In addition to that, when tracee is compiled statically it can't load dynamic libraries (and we want to support statically compiled binary so tracee can run without any other dependency).

Thats something I thought as well and agree. We're moving towards the extensions approach, and one of the steps is to remove the .so and bring all signatures to a builtin model.
Since extensions, or any other replacement for the .so for that matter, are not on this milestone, and we have a need for externally pluggable data sources due to #3704 in this milestone, I think this is still needed, especially since it's not a very large change (data sources and signatures should be very similar in terms of exporting and registering).
When we replace the .so we can move both of these together.
> Since extensions, or any other replacement for the .so for that matter, are not on this milestone, and we have a need for externally pluggable data sources due to #3704 in this milestone, I think this is still needed,

You have a good point.



Patch:
diff --git a/cmd/tracee-rules/main.go b/cmd/tracee-rules/main.go
index 2956cf3465c4..a5cdd816bd10 100644
--- a/cmd/tracee-rules/main.go
+++ b/cmd/tracee-rules/main.go
@@ -59,7 +59,7 @@ func main() {
 				rulesDir = []string{c.String("rules-dir")}
 			}
 
-			sigs, err := signature.Find(
+			sigs, _, err := signature.Find(
 				target,
 				c.Bool("rego-partial-eval"),
 				rulesDir,
diff --git a/cmd/tracee/cmd/analyze.go b/cmd/tracee/cmd/analyze.go
index c1efbb049036..e61a03aa911b 100644
--- a/cmd/tracee/cmd/analyze.go
+++ b/cmd/tracee/cmd/analyze.go
@@ -105,7 +105,7 @@ tracee analyze --events anti_debugging events.json`,
 			signatureEvents = nil
 		}
 
-		sigs, err := signature.Find(
+		sigs, _, err := signature.Find(
 			rego.RuntimeTarget,
 			rego.PartialEval,
 			viper.GetStringSlice("signatures-dir"),
diff --git a/cmd/tracee/cmd/list.go b/cmd/tracee/cmd/list.go
index 38142fa1b535..6683f839c5c9 100644
--- a/cmd/tracee/cmd/list.go
+++ b/cmd/tracee/cmd/list.go
@@ -41,7 +41,7 @@ var listCmd = &cobra.Command{
 			os.Exit(1)
 		}
 
-		sigs, err := signature.Find(
+		sigs, _, err := signature.Find(
 			compile.TargetRego,
 			false,
 			sigsDir,
diff --git a/pkg/cmd/cobra/cobra.go b/pkg/cmd/cobra/cobra.go
index 425b9b88a41f..022272493ff0 100644
--- a/pkg/cmd/cobra/cobra.go
+++ b/pkg/cmd/cobra/cobra.go
@@ -22,7 +22,6 @@ import (
 	"github.com/aquasecurity/tracee/pkg/policy"
 	"github.com/aquasecurity/tracee/pkg/signatures/engine"
 	"github.com/aquasecurity/tracee/pkg/signatures/signature"
-	"github.com/aquasecurity/tracee/types/detect"
 )
 
 func GetTraceeRunner(c *cobra.Command, version string) (cmd.Runner, error) {
@@ -58,7 +57,7 @@ func GetTraceeRunner(c *cobra.Command, version string) (cmd.Runner, error) {
 
 	// Signature directory command line flags
 
-	sigs, err := signature.Find(
+	sigs, dataSources, err := signature.Find(
 		rego.RuntimeTarget,
 		rego.PartialEval,
 		viper.GetStringSlice("signatures-dir"),
@@ -327,7 +326,7 @@ func GetTraceeRunner(c *cobra.Command, version string) (cmd.Runner, error) {
 		// This used to be a flag, we have removed the flag from this binary to test
 		// if users do use it or not.
 		SignatureBufferSize: 1000,
-		DataSources:         []detect.DataSource{},
+		DataSources:         dataSources,
 	}
 
 	return runner, nil
diff --git a/pkg/ebpf/signature_engine.go b/pkg/ebpf/signature_engine.go
index 25331bcc1f89..d075be9ddaff 100644
--- a/pkg/ebpf/signature_engine.go
+++ b/pkg/ebpf/signature_engine.go
@@ -25,7 +25,7 @@ func (t *Tracee) engineEvents(ctx context.Context, in <-chan *trace.Event) (<-ch
 	source := engine.EventSources{Tracee: engineInput}
 
 	// Prepare built in data sources
-	t.config.EngineConfig.DataSources = t.PrepareBuiltinDataSources()
+	t.config.EngineConfig.DataSources = append(t.config.EngineConfig.DataSources, t.PrepareBuiltinDataSources()...)
 
 	sigEngine, err := engine.NewEngine(t.config.EngineConfig, source, engineOutput)
 	if err != nil {
diff --git a/pkg/signatures/signature/signature.go b/pkg/signatures/signature/signature.go
index d31f82c4a0d0..c2e1e51b2923 100644
--- a/pkg/signatures/signature/signature.go
+++ b/pkg/signatures/signature/signature.go
@@ -15,7 +15,7 @@ import (
 	"github.com/aquasecurity/tracee/types/detect"
 )
 
-func Find(target string, partialEval bool, signaturesDir []string, signatures []string, aioEnabled bool) ([]detect.Signature, error) {
+func Find(target string, partialEval bool, signaturesDir []string, signatures []string, aioEnabled bool) ([]detect.Signature, []detect.DataSource, error) {
 	if len(signaturesDir) == 0 {
 		exePath, err := os.Executable()
 		if err != nil {
@@ -24,22 +24,24 @@ func Find(target string, partialEval bool, signaturesDir []string, signatures []
 		signaturesDir = []string{filepath.Join(filepath.Dir(exePath), "signatures")}
 	}
 	var sigs []detect.Signature
+	var datasources []detect.DataSource
 
 	for _, dir := range signaturesDir {
 		if strings.TrimSpace(dir) == "" {
 			continue
 		}
 
-		gosigs, err := findGoSigs(dir)
+		gosigs, ds, err := findGoSigs(dir)
 		if err != nil {
-			return nil, err
+			return nil, nil, err
 		}
 
 		sigs = append(sigs, gosigs...)
+		datasources = append(datasources, ds...)
 
 		opasigs, err := findRegoSigs(target, partialEval, dir, aioEnabled)
 		if err != nil {
-			return nil, err
+			return nil, nil, err
 		}
 		sigs = append(sigs, opasigs...)
 	}
@@ -57,15 +59,16 @@ func Find(target string, partialEval bool, signaturesDir []string, signatures []
 			}
 		}
 	}
-	return res, nil
+	return res, datasources, nil
 }
 
-func findGoSigs(dir string) ([]detect.Signature, error) {
-	var res []detect.Signature
+func findGoSigs(dir string) ([]detect.Signature, []detect.DataSource, error) {
+	var signatures []detect.Signature
+	var datasources []detect.DataSource
 
 	if isBinaryStatic() {
 		logger.Warnw("The tracee static can't load golang signatures. Skipping ...")
-		return res, nil
+		return signatures, datasources, nil
 	}
 
 	errWD := filepath.WalkDir(dir,
@@ -84,13 +87,26 @@ func findGoSigs(dir string) ([]detect.Signature, error) {
 				logger.Errorw("Opening plugin " + path + ": " + err.Error())
 				return err
 			}
-			export, err := p.Lookup("ExportedSignatures")
+			exportSigs, err := p.Lookup("ExportedSignatures")
 			if err != nil {
 				logger.Errorw("Missing Export symbol in plugin " + d.Name())
 				return err
 			}
-			sigs := *export.(*[]detect.Signature)
-			res = append(res, sigs...)
+			sigs := *exportSigs.(*[]detect.Signature)
+
+			exportDS, err := p.Lookup("ExportedDataSources")
+			if err != nil {
+				logger.Debugw("No ExportedDataSources symbol in plugin " + d.Name())
+				// we don't return here because some plugins might not have datasources
+			}
+
+			var ds []detect.DataSource
+			if exportDS != nil {
+				ds = *exportDS.(*[]detect.DataSource)
+			}
+
+			signatures = append(signatures, sigs...)
+			datasources = append(datasources, ds...)
 			return nil
 		},
 	)
@@ -98,7 +114,7 @@ func findGoSigs(dir string) ([]detect.Signature, error) {
 		logger.Errorw("Walking dir", "error", errWD)
 	}
 
-	return res, nil
+	return signatures, datasources, nil
 }
 
 func findRegoSigs(target string, partialEval bool, dir string, aioEnabled bool) ([]detect.Signature, error) {
diff --git a/signatures/golang/export.go b/signatures/golang/export.go
index 444b5b469aeb..f3ca7c9c0654 100644
--- a/signatures/golang/export.go
+++ b/signatures/golang/export.go
@@ -36,3 +36,9 @@ var ExportedSignatures = []detect.Signature{
 	&SyscallTableHooking{},
 	&DroppedExecutable{},
 }
+
+// ExportedDataSources fulfills the goplugins contract required by the rule-engine
+// this is a list of data-sources that this plugin exports
+var ExportedDataSources = []detect.DataSource{
+	// add data-sources here
+}


Test patch:
diff --git a/pkg/signatures/signature/signature_test.go b/pkg/signatures/signature/signature_test.go
index 4cba21341dbb..979ec52a12c5 100644
--- a/pkg/signatures/signature/signature_test.go
+++ b/pkg/signatures/signature/signature_test.go
@@ -22,7 +22,7 @@ const (
 func TestFindByEventName(t *testing.T) {
 	t.Parallel()
 
-	sigs, err := Find(compile.TargetRego, false, []string{exampleRulesDir}, []string{"anti_debugging"}, false)
+	sigs, _, err := Find(compile.TargetRego, false, []string{exampleRulesDir}, []string{"anti_debugging"}, false)
 	require.NoError(t, err)
 	require.Equal(t, 1, len(sigs))
 
@@ -45,7 +45,7 @@ func TestFindByEventName(t *testing.T) {
 func TestFindByRuleID(t *testing.T) {
 	t.Parallel()
 
-	sigs, err := Find(compile.TargetRego, false, []string{exampleRulesDir}, []string{"TRC-2"}, false)
+	sigs, _, err := Find(compile.TargetRego, false, []string{exampleRulesDir}, []string{"TRC-2"}, false)
 	require.NoError(t, err)
 	require.Equal(t, 1, len(sigs))
 
diff --git a/tests/e2e-inst-signatures/export.go b/tests/e2e-inst-signatures/export.go
index 5bf1e873b009..c057e3bae3a0 100644
--- a/tests/e2e-inst-signatures/export.go
+++ b/tests/e2e-inst-signatures/export.go
@@ -14,3 +14,7 @@ var ExportedSignatures = []detect.Signature{
 	&e2eSignatureDerivation{},
 	&e2eDnsDataSource{},
 }
+
+var ExportedDataSources = []detect.DataSource{
+	// add data-sources here
+}
diff --git a/tests/e2e-net-signatures/export.go b/tests/e2e-net-signatures/export.go
index f57ea90f4c95..78295ac1eaf2 100644
--- a/tests/e2e-net-signatures/export.go
+++ b/tests/e2e-net-signatures/export.go
@@ -13,3 +13,7 @@ var ExportedSignatures = []detect.Signature{
 	&e2eDNS{},
 	&e2eHTTP{},
 }
+
+var ExportedDataSources = []detect.DataSource{
+	// add data-sources here
+}