From eafc8103b9d0357431af9a305ed5b47b072241a8 Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Fri, 22 Dec 2023 15:26:49 +0100 Subject: [PATCH 1/5] new(falco): add k8s-metacollector chart as a dependency Signed-off-by: Aldo Lacuku --- charts/falco/Chart.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/charts/falco/Chart.yaml b/charts/falco/Chart.yaml index 109221ba6..1ec29610c 100644 --- a/charts/falco/Chart.yaml +++ b/charts/falco/Chart.yaml @@ -22,3 +22,7 @@ dependencies: version: "0.7.11" condition: falcosidekick.enabled repository: https://falcosecurity.github.io/charts + - name: k8s-metacollector + version: 0.1.1 + repository: https://falcosecurity.github.io/charts + condition: collectors.kubernetes.enabled From 5651434f99483849f89bf7fb6884e19017a9340a Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Fri, 22 Dec 2023 15:27:55 +0100 Subject: [PATCH 2/5] new(falco): integrate k8s-metacollector and k8smeta plugin The defualt mode to get kubernetes metadata is using the k8s-metacollector and the k8smeta plugin. This commit adds the required helpers and variables to enable the k8s-metacollector by default. Signed-off-by: Aldo Lacuku --- charts/falco/templates/_helpers.tpl | 52 +++++++++++++++++++ charts/falco/templates/configmap.yaml | 1 + .../falco/templates/falcoctl-configmap.yaml | 1 + charts/falco/templates/pod-template.tpl | 10 ---- charts/falco/values.yaml | 45 ++++++++-------- 5 files changed, 77 insertions(+), 32 deletions(-) diff --git a/charts/falco/templates/_helpers.tpl b/charts/falco/templates/_helpers.tpl index c64f3fe57..b4c45a26a 100644 --- a/charts/falco/templates/_helpers.tpl +++ b/charts/falco/templates/_helpers.tpl @@ -318,4 +318,56 @@ be temporary and will stay here until we move this logic to the falcoctl tool. {{- if .Values.falcoctl.artifact.follow.env }} {{- include "falco.renderTemplate" ( dict "value" .Values.falcoctl.artifact.follow.env "context" $) | nindent 4 }} {{- end }} +{{- end -}} + + +{{/* + Build configuration for k8smeta plugin and update the relevant variables. + * The configuration that needs to be built up is the initconfig section: + init_config: + collectorPort: 0 + collectorHostname: "" + nodename: "" + The falco chart exposes this configuriotino through two variable: + * collectors.kubenetetes.collectorHostname; + * collectors.kubernetes.collectorPort; + If those two variable are not set, then we take those values from the k8smetacollector subchart. + The hostname is built using the name of the service that exposes the collector endpoints and the + port is directly taken form the service's port that exposes the gRPC endpoint. + We reuse the helpers from the k8smetacollector subchart, by passing down the variables. There is a + hardcoded values that is the chart name for the k8s-metacollector chart. + + * The falcoctl configuration is updated to allow plugin artifacts to be installed. The refs in the install + section are updated by adding the reference for the k8s meta plugin that needs to be installed. + NOTE: It seems that the named templates run during the validation process. And then again during the + render fase. In our case we are setting global variable that persist during the various phases. + We need to make the helper idempotent. +*/}} +{{- define "k8smeta.configuration" -}} +{{- if .Values.collectors.kubernetes.enabled -}} +{{- $hostname := "" -}} +{{- if .Values.collectors.kubernetes.collectorHostname -}} +{{- $hostname = .Values.collectors.kubernetes.collectorHostname -}} +{{- else -}} +{{- $collectorContext := (dict "Release" .Release "Values" (index .Values "k8s-metacollector") "Chart" (dict "Name" "k8s-metacollector")) -}} +{{- $hostname = printf "%s.%s.svc" (include "k8s-metacollector.fullname" $collectorContext) (include "k8s-metacollector.namespace" $collectorContext) -}} +{{- end -}} +{{- $hasConfig := false -}} +{{- range .Values.falco.plugins -}} +{{- if eq (get . "name") "k8smeta" -}} +{{ $hasConfig = true -}} +{{- end -}} +{{- end -}} +{{- if not $hasConfig -}} +{{- $listenPort := default (index .Values "k8s-metacollector" "service" "ports" "broker-grpc" "port") .Values.collectors.kubernetes.collectorPort -}} +{{- $listenPort = int $listenPort -}} +{{- $pluginConfig := dict "name" "k8smeta" "library_path" "libk8smeta.so" "init_config" (dict "collectorHostname" $hostname "collectorPort" $listenPort "nodename" "${FALCO_K8S_NODE_NAME}") -}} +{{- $newConfig := append .Values.falco.plugins $pluginConfig -}} +{{- $_ := set .Values.falco "plugins" ($newConfig | uniq) -}} +{{- $loadedPlugins := append .Values.falco.load_plugins "k8smeta" -}} +{{- $_ = set .Values.falco "load_plugins" ($loadedPlugins | uniq) -}} +{{- end -}} +{{- $_ := set .Values.falcoctl.config.artifact.install "refs" ((append .Values.falcoctl.config.artifact.install.refs .Values.collectors.kubernetes.pluginRef) | uniq)}} +{{- $_ = set .Values.falcoctl.config.artifact "allowedTypes" ((append .Values.falcoctl.config.artifact.allowedTypes "plugin") | uniq)}} +{{- end -}} {{- end -}} \ No newline at end of file diff --git a/charts/falco/templates/configmap.yaml b/charts/falco/templates/configmap.yaml index 387dca145..f244e5435 100644 --- a/charts/falco/templates/configmap.yaml +++ b/charts/falco/templates/configmap.yaml @@ -8,4 +8,5 @@ metadata: data: falco.yaml: |- {{- include "falco.falcosidekickConfig" . }} + {{- include "k8smeta.configuration" . -}} {{- toYaml .Values.falco | nindent 4 }} diff --git a/charts/falco/templates/falcoctl-configmap.yaml b/charts/falco/templates/falcoctl-configmap.yaml index 302fa7b1a..7b769e870 100644 --- a/charts/falco/templates/falcoctl-configmap.yaml +++ b/charts/falco/templates/falcoctl-configmap.yaml @@ -8,5 +8,6 @@ metadata: {{- include "falco.labels" . | nindent 4 }} data: falcoctl.yaml: |- + {{- include "k8smeta.configuration" . -}} {{- toYaml .Values.falcoctl.config | nindent 4 }} {{- end }} diff --git a/charts/falco/templates/pod-template.tpl b/charts/falco/templates/pod-template.tpl index 2103adeeb..38b2d465f 100644 --- a/charts/falco/templates/pod-template.tpl +++ b/charts/falco/templates/pod-template.tpl @@ -80,16 +80,6 @@ spec: - --cri - /run/crio/crio.sock {{- end }} - {{- if .kubernetes.enabled }} - - -K - - {{ .kubernetes.apiAuth }} - - -k - - {{ .kubernetes.apiUrl }} - {{- if .kubernetes.enableNodeFilter }} - - --k8s-node - - "$(FALCO_K8S_NODE_NAME)" - {{- end }} - {{- end }} - -pk {{- end }} {{- end }} diff --git a/charts/falco/values.yaml b/charts/falco/values.yaml index b6467f422..524fcf2a9 100644 --- a/charts/falco/values.yaml +++ b/charts/falco/values.yaml @@ -215,7 +215,7 @@ driver: # -- The image repository to pull from. repository: falcosecurity/falco-driver-loader # -- Overrides the image tag whose default is the chart appVersion. - tag: "" + tag: "master" # -- Extra environment variables that will be pass onto Falco driver loader init container. env: [] # -- Arguments to pass to the Falco driver loader init container. @@ -262,30 +262,31 @@ collectors: # -- The path of the CRI-O socket. socket: /run/crio/crio.sock + # -- kubernetes holds the configuration for the kubernetes collector. Starting from version 0.37.0 of Falco, the legacy + # kubernetes client has been removed. A new standalone component named k8s-metacollector and a Falco plugin have been developed + # to solve the issues that were present in the old implementation. More info here: https://github.com/falcosecurity/falco/issues/2973 kubernetes: - # -- Enable Kubernetes meta data collection via a connection to the Kubernetes API server. - # When this option is disabled, Falco falls back to the container annotations to grap the meta data. + # -- enabled specifies whether the Kubernetes metadata should be collected using the k8smeta plugin and the k8s-metacollector component. + # It will deploy the k8s-metacollector external component that fetches Kubernetes metadata and pushes them to Falco instances. + # For more info see: + # https://github.com/falcosecurity/k8s-metacollector + # https://github.com/falcosecurity/charts/tree/master/charts/k8s-metacollector + # When this option is disabled, Falco falls back to the container annotations to grab the metadata. # In such a case, only the ID, name, namespace, labels of the pod will be available. enabled: true - # -- The apiAuth value is to provide the authentication method Falco should use to connect to the Kubernetes API. - # The argument's documentation from Falco is provided here for reference: - # - # | :[:], --k8s-api-cert | :[:] - # Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. - # Each entry must specify full (absolute, or relative to the current directory) path to the respective file. - # Private key password is optional (needed only if key is password protected). - # CA certificate is optional. For all files, only PEM file format is supported. - # Specifying CA certificate only is obsoleted - when single entry is provided - # for this option, it will be interpreted as the name of a file containing bearer token. - # Note that the format of this command-line option prohibits use of files whose names contain - # ':' or '#' characters in the file name. - # -- Provide the authentication method Falco should use to connect to the Kubernetes API. - apiAuth: /var/run/secrets/kubernetes.io/serviceaccount/token - ## -- Provide the URL Falco should use to connect to the Kubernetes API. - apiUrl: "https://$(KUBERNETES_SERVICE_HOST)" - # -- If true, only the current node (on which Falco is running) will be considered when requesting metadata of pods - # to the API server. Disabling this option may have a performance penalty on large clusters. - enableNodeFilter: true + # --pluginRef is the OCI reference for the k8smeta plugin. It could be a full reference such as: + # "ghcr.io/falcosecurity/plugins/plugin/k8smeta:0.1.0". Or just name + tag: k8smeta:0.1.0. + pluginRef: "ghcr.io/falcosecurity/plugins/plugin/k8smeta:0.1.0-alpha" + # -- collectorHostname is the address of the k8s-metacollector. When not specified it will be set to match + # k8s-metacollector service. e.x: falco-k8smetacollecto.falco.svc. If for any reason you need to override + # it, make sure to set here the address of the k8s-metacollector. + # It is used by the k8smeta plugin to connect to the k8s-metacollector. + collectorHostname: "" + # -- collectorPort designates the port on which the k8s-metacollector gRPC service listens. If not specified + # the value of the port named `broker-grpc` in k8s-metacollector.service.ports is used. The default values is 45000. + # It is used by the k8smeta plugin to connect to the k8s-metacollector. + collectorPort: "" + ########################### # Extras and customization # From 57d247561a8f2e8b30c705f018186100e6e8faac Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Tue, 2 Jan 2024 11:33:13 +0100 Subject: [PATCH 3/5] update(falco): remove service account and related resources Signed-off-by: Aldo Lacuku --- charts/falco/templates/clusterrole.yaml | 43 ------------------- .../falco/templates/clusterrolebinding.yaml | 16 ------- charts/falco/templates/pod-template.tpl | 1 - charts/falco/templates/serviceaccount.yaml | 13 ------ charts/falco/values.yaml | 39 +---------------- 5 files changed, 1 insertion(+), 111 deletions(-) delete mode 100644 charts/falco/templates/clusterrole.yaml delete mode 100644 charts/falco/templates/clusterrolebinding.yaml delete mode 100644 charts/falco/templates/serviceaccount.yaml diff --git a/charts/falco/templates/clusterrole.yaml b/charts/falco/templates/clusterrole.yaml deleted file mode 100644 index 1e9e9eac3..000000000 --- a/charts/falco/templates/clusterrole.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if .Values.rbac.create }} -kind: ClusterRole -apiVersion: {{ include "rbac.apiVersion" . }} -metadata: - name: {{ include "falco.fullname" . }} - labels: - {{- include "falco.labels" . | nindent 4 }} -rules: - - apiGroups: - - extensions - - "" - resources: - - nodes - - namespaces - - pods - - replicationcontrollers - - replicasets - - services - - daemonsets - - deployments - - events - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - daemonsets - - deployments - - replicasets - - statefulsets - verbs: - - get - - list - - watch - - nonResourceURLs: - - /healthz - - /healthz/* - verbs: - - get -{{- end }} diff --git a/charts/falco/templates/clusterrolebinding.yaml b/charts/falco/templates/clusterrolebinding.yaml deleted file mode 100644 index 2a550634e..000000000 --- a/charts/falco/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if .Values.rbac.create }} -kind: ClusterRoleBinding -apiVersion: {{ include "rbac.apiVersion" . }} -metadata: - name: {{ include "falco.fullname" . }} - labels: - {{- include "falco.labels" . | nindent 4 }} -subjects: - - kind: ServiceAccount - name: {{ include "falco.serviceAccountName" . }} - namespace: {{ include "falco.namespace" . }} -roleRef: - kind: ClusterRole - name: {{ include "falco.fullname" . }} - apiGroup: rbac.authorization.k8s.io -{{- end }} diff --git a/charts/falco/templates/pod-template.tpl b/charts/falco/templates/pod-template.tpl index 38b2d465f..f493cae51 100644 --- a/charts/falco/templates/pod-template.tpl +++ b/charts/falco/templates/pod-template.tpl @@ -16,7 +16,6 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: - serviceAccountName: {{ include "falco.serviceAccountName" . }} {{- with .Values.podSecurityContext }} securityContext: {{- toYaml . | nindent 4}} diff --git a/charts/falco/templates/serviceaccount.yaml b/charts/falco/templates/serviceaccount.yaml deleted file mode 100644 index 6f7e52f1c..000000000 --- a/charts/falco/templates/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "falco.serviceAccountName" . }} - namespace: {{ include "falco.namespace" . }} - labels: - {{- include "falco.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} \ No newline at end of file diff --git a/charts/falco/values.yaml b/charts/falco/values.yaml index 524fcf2a9..4341120c0 100644 --- a/charts/falco/values.yaml +++ b/charts/falco/values.yaml @@ -23,19 +23,6 @@ fullnameOverride: "" # -- Override the deployment namespace namespaceOverride: "" -rbac: - # Create and use rbac resources when set to true. Needed to fetch k8s metadata from the api-server. - create: true - -serviceAccount: - # -- Specifies whether a service account should be created. - create: true - # -- Annotations to add to the service account. - annotations: {} - # -- The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - # -- Add additional pod annotations podAnnotations: {} @@ -215,7 +202,7 @@ driver: # -- The image repository to pull from. repository: falcosecurity/falco-driver-loader # -- Overrides the image tag whose default is the chart appVersion. - tag: "master" + tag: "" # -- Extra environment variables that will be pass onto Falco driver loader init container. env: [] # -- Arguments to pass to the Falco driver loader init container. @@ -1380,30 +1367,6 @@ falco: # Falco cloud orchestration systems integration # ################################################# - # [Stable] `metadata_download` - # - # -- When connected to an orchestrator like Kubernetes, Falco has the capability to - # collect metadata and enrich system call events with contextual data. The - # parameters mentioned here control the downloading process of this metadata. - # - # Please note that support for Mesos is deprecated, so these parameters - # currently apply only to Kubernetes. When using Falco with Kubernetes, you can - # enable this functionality by using the `-k` or `-K` command-line flag. - # - # However, it's worth mentioning that for important Kubernetes metadata fields - # such as namespace or pod name, these fields are automatically extracted from - # the container runtime, providing the necessary enrichment for common use cases - # of syscall-based threat detection. - # - # In summary, the `-k` flag is typically not required for most scenarios involving - # Kubernetes workload owner enrichment. The `-k` flag is primarily used when - # additional metadata is required beyond the standard fields, catering to more - # specific use cases, see https://falco.org/docs/reference/rules/supported-fields/#field-class-k8s. - metadata_download: - max_mb: 100 - chunk_wait_us: 1000 - watch_freq_sec: 1 - # [Stable] Guidance for Kubernetes container engine command-line args settings # # Modern cloud environments, particularly Kubernetes, heavily rely on From 993c52400a60e6af5a7c4f9080a995c7adb1759c Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Wed, 3 Jan 2024 12:32:42 +0100 Subject: [PATCH 4/5] new(falco/tests): add unit tests for k8s-metacollector integration Signed-off-by: Aldo Lacuku --- .github/workflows/test.yml | 27 ++ .../unit/k8smetacollectorDependency_test.go | 456 ++++++++++++++++++ 2 files changed, 483 insertions(+) create mode 100644 charts/falco/tests/unit/k8smetacollectorDependency_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0645d66b0..7c24f9b7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,8 @@ jobs: - name: Set up Helm uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 + with: + version: '3.10.3' - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: @@ -55,3 +57,28 @@ jobs: - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' run: ct install --config ct.yaml + + go-unit-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 + with: + version: '3.10.3' + + - name: Update repo deps + run: helm dependency update ./charts/falco + + - name: Setup Go + uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 + with: + go-version: '1.21' + check-latest: true + + - name: Falco unit tests + run: go test -cover ./charts/falco/... diff --git a/charts/falco/tests/unit/k8smetacollectorDependency_test.go b/charts/falco/tests/unit/k8smetacollectorDependency_test.go new file mode 100644 index 000000000..3d12faac2 --- /dev/null +++ b/charts/falco/tests/unit/k8smetacollectorDependency_test.go @@ -0,0 +1,456 @@ +package unit + +import ( + "encoding/json" + "fmt" + "path/filepath" + "regexp" + "slices" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/helm" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" +) + +const chartPath = "../../" + +// Using the default values we want to test that all the expected resources for the k8s-metacollector are rendered. +func TestRenderedResourcesWithDefaultValues(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + releaseName := "rendered-resources" + + // Template files that we expect to be rendered. + templateFiles := []string{ + "clusterrole.yaml", + "clusterrolebinding.yaml", + "deployment.yaml", + "service.yaml", + "serviceaccount.yaml", + } + + require.NoError(t, err) + + options := &helm.Options{} + + // Template the chart using the default values.yaml file. + output, err := helm.RenderTemplateE(t, options, helmChartPath, releaseName, nil) + require.NoError(t, err) + + // Extract all rendered files from the output. + pattern := `# Source: falco/charts/k8s-metacollector/templates/([^\n]+)` + re := regexp.MustCompile(pattern) + matches := re.FindAllStringSubmatch(output, -1) + + var renderedTemplates []string + for _, match := range matches { + // Filter out test templates. + if !strings.Contains(match[1], "test-") { + renderedTemplates = append(renderedTemplates, match[1]) + } + } + + // Assert that the rendered resources are equal tho the expected ones. + require.Equal(t, len(renderedTemplates), len(templateFiles), "should be equal") + + for _, rendered := range renderedTemplates { + require.True(t, slices.Contains(templateFiles, rendered), "template files should contain all the rendered files") + } +} + +func TestPluginConfigurationInFalcoConfig(t *testing.T) { + t.Parallel() + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + releaseName := "rendered-resources" + + testCases := []struct { + name string + values map[string]string + expected func(t *testing.T, config any) + }{ + { + "defaultValues", + nil, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(45000), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, fmt.Sprintf("%s-k8s-metacollector.default.svc", releaseName), hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + { + "overrideK8s-metacollectorNamespace", + map[string]string{ + "k8s-metacollector.namespaceOverride": "test", + }, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(45000), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, fmt.Sprintf("%s-k8s-metacollector.test.svc", releaseName), hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + { + "overrideK8s-metacollectorName", + map[string]string{ + "k8s-metacollector.fullnameOverride": "collector", + }, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(45000), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, "collector.default.svc", hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + + { + "overrideK8s-metacollectorNamespaceAndName", + map[string]string{ + "k8s-metacollector.namespaceOverride": "test", + "k8s-metacollector.fullnameOverride": "collector", + }, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(45000), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, "collector.test.svc", hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + { + "set CollectorHostname", + map[string]string{ + "collectors.kubernetes.collectorHostname": "test", + }, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(45000), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, "test", hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + + { + "set CollectorHostname and namespace name", + map[string]string{ + "collectors.kubernetes.collectorHostname": "test-with-override", + "k8s-metacollector.namespaceOverride": "test", + "k8s-metacollector.fullnameOverride": "collector", + }, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(45000), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, "test-with-override", hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + + { + "set collectorPort", + map[string]string{ + "collectors.kubernetes.collectorPort": "8888", + }, + func(t *testing.T, config any) { + plugin := config.(map[string]interface{}) + // Get init config. + initConfig, ok := plugin["init_config"] + require.True(t, ok) + initConfigMap := initConfig.(map[string]interface{}) + // Check that the collector port is correctly set. + port := initConfigMap["collectorPort"] + require.Equal(t, float64(8888), port.(float64)) + // Check that the collector nodeName is correctly set. + nodeName := initConfigMap["nodename"] + require.Equal(t, "${FALCO_K8S_NODE_NAME}", nodeName.(string)) + // Check that the collector hostname is correctly set. + hostName := initConfigMap["collectorHostname"] + require.Equal(t, fmt.Sprintf("%s-k8s-metacollector.default.svc", releaseName), hostName.(string)) + + // Check that the library path is set. + libPath := plugin["library_path"] + require.Equal(t, "libk8smeta.so", libPath) + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + options := &helm.Options{SetValues: testCase.values} + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/configmap.yaml"}) + + var cm corev1.ConfigMap + helm.UnmarshalK8SYaml(t, output, &cm) + var config map[string]interface{} + + helm.UnmarshalK8SYaml(t, cm.Data["falco.yaml"], &config) + plugins := config["plugins"] + pluginsArray := plugins.([]interface{}) + // Find the k8smeta plugin configuration. + for _, plugin := range pluginsArray { + if name, ok := plugin.(map[string]interface{})["name"]; ok && name == "k8smeta" { + testCase.expected(t, plugin) + } + } + // Check that the plugin has been added to the ones that need to be loaded. + loadplugins := config["load_plugins"] + require.True(t, slices.Contains(loadplugins.([]interface{}), "k8smeta")) + }) + } +} + +// Test that the helper does not overwrite user's configuration. +func TestPluginConfigurationUniqueEntries(t *testing.T) { + t.Parallel() + + pluginsJSON := `[ + { + "init_config": null, + "library_path": "libk8saudit.so", + "name": "k8saudit", + "open_params": "http://:9765/k8s-audit" + }, + { + "library_path": "libcloudtrail.so", + "name": "cloudtrail" + }, + { + "init_config": "", + "library_path": "libjson.so", + "name": "json" + }, + { + "init_config": { + "collectorHostname": "rendered-resources-k8s-metacollector.default.svc", + "collectorPort": 45000, + "nodename": "${FALCO_K8S_NODE_NAME}" + }, + "library_path": "libk8smeta.so", + "name": "k8smeta" + } +]` + + loadPluginsJSON := `[ + "k8smeta", + "k8saudit" +]` + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + releaseName := "rendered-resources" + + options := &helm.Options{SetJsonValues: map[string]string{ + "falco.plugins": pluginsJSON, + "falco.load_plugins": loadPluginsJSON, + }} + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/configmap.yaml"}) + + var cm corev1.ConfigMap + helm.UnmarshalK8SYaml(t, output, &cm) + var config map[string]interface{} + + helm.UnmarshalK8SYaml(t, cm.Data["falco.yaml"], &config) + plugins := config["plugins"] + + out, err := json.MarshalIndent(plugins, "", " ") + require.NoError(t, err) + require.Equal(t, pluginsJSON, string(out)) + pluginsArray := plugins.([]interface{}) + // Find the k8smeta plugin configuration. + numConfigK8smeta := 0 + for _, plugin := range pluginsArray { + if name, ok := plugin.(map[string]interface{})["name"]; ok && name == "k8smeta" { + numConfigK8smeta++ + } + } + + require.Equal(t, 1, numConfigK8smeta) + + // Check that the plugin has been added to the ones that need to be loaded. + loadplugins := config["load_plugins"] + require.Len(t, loadplugins.([]interface{}), 2) + require.True(t, slices.Contains(loadplugins.([]interface{}), "k8smeta")) +} + +// Test that the helper does not overwrite user's configuration. +func TestFalcoctlRefs(t *testing.T) { + t.Parallel() + + pluginsJSON := `[ + { + "init_config": null, + "library_path": "libk8saudit.so", + "name": "k8saudit", + "open_params": "http://:9765/k8s-audit" + }, + { + "library_path": "libcloudtrail.so", + "name": "cloudtrail" + }, + { + "init_config": "", + "library_path": "libjson.so", + "name": "json" + }, + { + "init_config": { + "collectorHostname": "rendered-resources-k8s-metacollector.default.svc", + "collectorPort": 45000, + "nodename": "${FALCO_K8S_NODE_NAME}" + }, + "library_path": "libk8smeta.so", + "name": "k8smeta" + } + ]` + + testFunc := func(t *testing.T, config any) { + // Get artifact configuration map. + configMap := config.(map[string]interface{}) + artifactConfig := (configMap["artifact"]).(map[string]interface{}) + // Test allowed types. + allowedTypes := artifactConfig["allowedTypes"] + require.Len(t, allowedTypes, 2) + require.True(t, slices.Contains(allowedTypes.([]interface{}), "plugin")) + require.True(t, slices.Contains(allowedTypes.([]interface{}), "rulesfile")) + // Test plugin reference. + refs := artifactConfig["install"].(map[string]interface{})["refs"].([]interface{}) + require.Len(t, refs, 2) + require.True(t, slices.Contains(refs, "falco-rules:2")) + require.True(t, slices.Contains(refs, "ghcr.io/falcosecurity/plugins/plugin/k8smeta:0.1.0-alpha")) + } + + testCases := []struct { + name string + valuesJSON map[string]string + expected func(t *testing.T, config any) + }{ + { + "defaultValues", + nil, + testFunc, + }, + { + "setPluginConfiguration", + map[string]string{ + "falco.plugins": pluginsJSON, + }, + testFunc, + }, + } + + helmChartPath, err := filepath.Abs(chartPath) + require.NoError(t, err) + + releaseName := "rendered-resources" + + for _, testCase := range testCases { + testCase := testCase + + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + options := &helm.Options{SetJsonValues: testCase.valuesJSON} + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/falcoctl-configmap.yaml"}) + + var cm corev1.ConfigMap + helm.UnmarshalK8SYaml(t, output, &cm) + var config map[string]interface{} + helm.UnmarshalK8SYaml(t, cm.Data["falcoctl.yaml"], &config) + testCase.expected(t, config) + }) + } +} From 93bf735b276585c8c3a5c6efb6ab9eeb7c2aa259 Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Wed, 3 Jan 2024 16:57:58 +0100 Subject: [PATCH 5/5] update(falco): bump version to make linter happy Signed-off-by: Aldo Lacuku --- charts/falco/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/falco/Chart.yaml b/charts/falco/Chart.yaml index 1ec29610c..b7c54b4b9 100644 --- a/charts/falco/Chart.yaml +++ b/charts/falco/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: falco -version: 3.8.7 +version: 3.8.8 appVersion: "0.36.2" description: Falco keywords: