Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/artifact/v1alpha1/rulesfile_types.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2026 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,6 +30,8 @@ type RulesfileSpec struct {
OCIArtifact *commonv1alpha1.OCIArtifact `json:"ociArtifact,omitempty"`
// InlineRules specifies the rules as a string.
InlineRules *string `json:"inlineRules,omitempty"`
// ConfigMapRef specifies a reference to a ConfigMap containing the rules.
ConfigMapRef *commonv1alpha1.ConfigMapRef `json:"configMapRef,omitempty"`
// Priority specifies the priority of the rulesfile.\
// The higher the value, the higher the priority.
// +kubebuilder:validation:Minimum=0
Expand Down
5 changes: 5 additions & 0 deletions api/artifact/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion api/common/v1alpha1/types.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2026 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,6 +39,11 @@ const (
ConditionReconciled ConditionType = "ConditionReconciled"
)

const (
// ConfigMapRulesKey is the standard key used for rules data in ConfigMaps.
ConfigMapRulesKey = "rules.yaml"
)

// OCIArtifact defines the structure for specifying an OCI artifact reference.
// +kubebuilder:object:generate=true
type OCIArtifact struct {
Expand All @@ -65,3 +70,11 @@ type OCIPullSecret struct {
// +kubebuilder:default=password
PasswordKey string `json:"passwordKey,omitempty"`
}

// ConfigMapRef defines the structure for referencing a ConfigMap and a specific key within it.
// +kubebuilder:object:generate=true
type ConfigMapRef struct {
// Name is the name of the ConfigMap.
// +kubebuilder:validation:Required
Name string `json:"name"`
}
15 changes: 15 additions & 0 deletions api/common/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions config/crd/bases/artifact.falcosecurity.dev_rulesfiles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ spec:
spec:
description: RulesfileSpec defines the desired state of Rulesfile.
properties:
configMapRef:
description: ConfigMapRef specifies a reference to a ConfigMap containing
the rules.
properties:
name:
description: Name is the name of the ConfigMap.
type: string
required:
- name
type: object
inlineRules:
description: InlineRules specifies the rules as a string.
type: string
Expand Down
53 changes: 53 additions & 0 deletions config/samples/artifact_v1alpha1_rulesfile_configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
# Example ConfigMap containing Falco rules
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-falco-rules
namespace: default
data:
rules.yaml: |
- rule: Terminal shell in container
desc: A shell was used as the entrypoint/exec point into a container with an attached terminal.
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
and container_entrypoint
and not user_expected_terminal_shell_in_container_conditions
output: >
A shell was spawned in a container with an attached terminal (user=%user.name user_loginuid=%user.loginuid %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository)
priority: NOTICE
tags: [container, shell, mitre_execution, T1059]

- rule: Write below binary dir
desc: An attempt to write to any file below a set of binary directories.
condition: >
bin_dir and evt.dir = < and open_write
and not package_mgmt_procs
and not exe_running_docker_save
and not python_running_get_pip
and not python_running_ms_oms
and not user_known_write_below_binary_dir_activities
output: >
File below a known binary directory opened for writing (user=%user.name user_loginuid=%user.loginuid
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
priority: ERROR
tags: [filesystem, mitre_persistence, T1543]

---
# Example Rulesfile using ConfigMap reference
# Note: The ConfigMap must have a key named "rules.yaml" containing the rules content
apiVersion: artifact.falcosecurity.dev/v1alpha1
kind: Rulesfile
metadata:
labels:
app.kubernetes.io/name: falco-operator
app.kubernetes.io/managed-by: kustomize
name: rulesfile-from-configmap
namespace: default
spec:
configMapRef:
name: custom-falco-rules
priority: 60

2 changes: 0 additions & 2 deletions controllers/artifact/config/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package controller defines controllers' logic.

package config

import (
Expand Down
4 changes: 1 addition & 3 deletions controllers/artifact/config/controller_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2026 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -14,8 +14,6 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package controller defines controllers' logic.

package config

import (
Expand Down
4 changes: 1 addition & 3 deletions controllers/artifact/config/suite_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2026 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -14,8 +14,6 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package controller defines controllers' logic.

package config

import (
Expand Down
13 changes: 3 additions & 10 deletions controllers/artifact/plugin/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ type PluginsConfig struct {
}

func (pc *PluginsConfig) addConfig(plugin *artifactv1alpha1.Plugin) {
var config = PluginConfig{
config := PluginConfig{
LibraryPath: artifact.Path(plugin.Name, priority.DefaultPriority, artifact.MediumOCI, artifact.TypePlugin),
Name: plugin.Name,
}
Expand Down Expand Up @@ -358,16 +358,9 @@ func (pc *PluginsConfig) toString() (string, error) {
if err != nil {
return "", err
}

// Convert the YAML to a string.
yamlString := string(data)

return yamlString, nil
return string(data), nil
}

func (pc *PluginsConfig) isEmpty() bool {
if len(pc.Configs) == 0 && len(pc.LoadPlugins) == 0 {
return true
}
return false
return len(pc.Configs) == 0 && len(pc.LoadPlugins) == 0
}
61 changes: 59 additions & 2 deletions controllers/artifact/rulesfile/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package controller defines controllers' logic.

package rulesfile

import (
"context"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

artifactv1alpha1 "github.com/falcosecurity/falco-operator/api/artifact/v1alpha1"
"github.com/falcosecurity/falco-operator/internal/pkg/artifact"
Expand All @@ -37,6 +38,8 @@ import (
const (
// rulesfileFinalizerPrefix is the prefix for the finalizer name.
rulesfileFinalizerPrefix = "rulesfile.artifact.falcosecurity.dev/finalizer"
// configMapRefIndexField is the field used for indexing Rulesfiles by ConfigMap reference.
configMapRefIndexField = ".spec.configMapRef.name"
)

// NewRulesfileReconciler returns a new RulesfileReconciler.
Expand All @@ -47,6 +50,7 @@ func NewRulesfileReconciler(cl client.Client, scheme *runtime.Scheme, nodeName,
finalizer: common.FormatFinalizerName(rulesfileFinalizerPrefix, nodeName),
artifactManager: artifact.NewManager(cl, namespace),
nodeName: nodeName,
namespace: namespace,
}
}

Expand All @@ -57,6 +61,7 @@ type RulesfileReconciler struct {
finalizer string
artifactManager *artifact.Manager
nodeName string
namespace string
}

// Reconcile is part of the main kubernetes reconciliation loop which aims to
Expand Down Expand Up @@ -108,12 +113,59 @@ func (r *RulesfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (

// SetupWithManager sets up the controller with the Manager.
func (r *RulesfileReconciler) SetupWithManager(mgr ctrl.Manager) error {
// Create an index for Rulesfiles by ConfigMap reference for efficient lookups.
if err := mgr.GetFieldIndexer().IndexField(
context.Background(),
&artifactv1alpha1.Rulesfile{},
configMapRefIndexField,
indexRulesfileByConfigMapRef,
); err != nil {
return err
}

return ctrl.NewControllerManagedBy(mgr).
For(&artifactv1alpha1.Rulesfile{}).
Watches(
&corev1.ConfigMap{},
handler.EnqueueRequestsFromMapFunc(r.findRulesfilesForConfigMap),
).
Named("artifact-rulesfile").
Complete(r)
}

func indexRulesfileByConfigMapRef(obj client.Object) []string {
rulesfile := obj.(*artifactv1alpha1.Rulesfile)
if rulesfile.Spec.ConfigMapRef == nil {
return nil
}
return []string{rulesfile.Namespace + "/" + rulesfile.Spec.ConfigMapRef.Name}
}

// findRulesfilesForConfigMap finds all Rulesfiles that reference a given ConfigMap using the index.
func (r *RulesfileReconciler) findRulesfilesForConfigMap(ctx context.Context, configMap client.Object) []reconcile.Request {
logger := log.FromContext(ctx)
rulesfileList := &artifactv1alpha1.RulesfileList{}

// Use the index to find Rulesfiles that reference this ConfigMap
indexKey := configMap.GetNamespace() + "/" + configMap.GetName()
if err := r.List(ctx, rulesfileList, client.MatchingFields{configMapRefIndexField: indexKey}); err != nil {
logger.Error(err, "unable to list Rulesfiles by ConfigMap index")
return []reconcile.Request{}
}

requests := make([]reconcile.Request, len(rulesfileList.Items))
for i := range rulesfileList.Items {
requests[i] = reconcile.Request{
NamespacedName: client.ObjectKey{
Name: rulesfileList.Items[i].Name,
Namespace: rulesfileList.Items[i].Namespace,
},
}
}

return requests
}

// ensureFinalizer ensures the finalizer is set.
func (r *RulesfileReconciler) ensureFinalizer(ctx context.Context, rulesfile *artifactv1alpha1.Rulesfile) (bool, error) {
if !controllerutil.ContainsFinalizer(rulesfile, r.finalizer) {
Expand Down Expand Up @@ -146,5 +198,10 @@ func (r *RulesfileReconciler) ensureRulesfile(ctx context.Context, rulesfile *ar
return err
}

if err := r.artifactManager.StoreFromConfigMap(
ctx, rulesfile.Name, rulesfile.Namespace, p, rulesfile.Spec.ConfigMapRef, artifact.TypeRulesfile); err != nil {
return err
}

return nil
}
4 changes: 1 addition & 3 deletions controllers/artifact/rulesfile/controller_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2026 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -14,8 +14,6 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package controller defines controllers' logic.

package rulesfile

import (
Expand Down
4 changes: 1 addition & 3 deletions controllers/artifact/rulesfile/suite_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2026 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -14,8 +14,6 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package controller defines controllers' logic.

package rulesfile

import (
Expand Down
2 changes: 1 addition & 1 deletion controllers/falco/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func generateRole(cl client.Client, falco *instancev1alpha1.Falco) (*unstructure
{
APIGroups: []string{""},
Resources: []string{"configmaps"},
Verbs: []string{"get", "list"},
Verbs: []string{"get", "list", "watch"},
},
{
APIGroups: []string{artifactv1alpha1.GroupVersion.Group},
Expand Down
2 changes: 1 addition & 1 deletion controllers/falco/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TestGenerateRole(t *testing.T) {
assert.Contains(t, role.Rules, rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"configmaps"},
Verbs: []string{"get", "list"},
Verbs: []string{"get", "list", "watch"},
})

// Verify artifact rule
Expand Down
Loading