From 2888bdda4858abae7ce941e76d8a445fa233cc0f Mon Sep 17 00:00:00 2001 From: Thomas Labarussias Date: Wed, 22 May 2024 01:23:27 +0200 Subject: [PATCH] revamp the policy reporter output Signed-off-by: Thomas Labarussias --- .github/workflows/lint.yml | 2 +- .golangci.yml | 5 +- Makefile | 2 +- config.go | 1 + config_example.yaml | 2 +- docs/outputs/policy_report.md | 11 +- handlers.go | 16 +- outputs/policyreport.go | 455 +++++++++++++++------------------- outputs/utils.go | 9 +- types/types.go | 1 + 10 files changed, 229 insertions(+), 275 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 066161d44..87303bbe6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,5 +23,5 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1 with: - version: v1.56 + version: v1.57 args: --timeout=5m diff --git a/.golangci.yml b/.golangci.yml index 30ebede07..46ca06ed3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,8 @@ run: deadline: 5m - skip-files: - - "zz_generated.*\\.go$" +issues: + exclude-files: + - "zz_generated.*\\.go$" linters: disable-all: true enable: diff --git a/Makefile b/Makefile index b69e1c494..c16a90403 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin) GO_INSTALL = ./hack/go_install.sh # Binaries. -GOLANGCI_LINT_VER := v1.56.2 +GOLANGCI_LINT_VER := v1.57.2 GOLANGCI_LINT_BIN := golangci-lint GOLANGCI_LINT := $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER) diff --git a/config.go b/config.go index 204f0fa46..6dc8f4517 100644 --- a/config.go +++ b/config.go @@ -370,6 +370,7 @@ func getConfig() *types.Configuration { v.SetDefault("PolicyReport.Kubeconfig", "") v.SetDefault("PolicyReport.MinimumPriority", "") v.SetDefault("PolicyReport.MaxEvents", 1000) + v.SetDefault("PolicyReport.FalcoNamespace", "") v.SetDefault("PolicyReport.PruneByPriority", false) v.SetDefault("Rabbitmq.URL", "") diff --git a/config_example.yaml b/config_example.yaml index 70dbb711d..2e7e25f28 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -388,7 +388,7 @@ fission: policyreport: enabled: false # if true policyreport output is enabled kubeconfig: "~/.kube/config" # Kubeconfig file to use (only if falcosidekick is running outside the cluster) - failthreshold: 4 # events with priority above this threshold are mapped to fail in PolicyReport Summary and lower that those are mapped to warn (default=4) + falconamespace: "" # Set the namespace where Falco is running (only if falcosidekick is running outside the cluster) maxevents: 1000 # the max number of events per report(default: 1000) prunebypriority: false # if true; the events with lowest severity are pruned first, in FIFO order (default: false) diff --git a/docs/outputs/policy_report.md b/docs/outputs/policy_report.md index fc78c99bc..eef0d13b7 100644 --- a/docs/outputs/policy_report.md +++ b/docs/outputs/policy_report.md @@ -15,12 +15,12 @@ ## Configuration -| Setting | Env var | Default value | Description | +| Setting | Env var | Default value | Description | | ------------------------------ | ------------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `policyreport.enabled` | `POLICYREPORT_ENABLED` | | If true; policyreport output is **enabled** | | `policyreport.kubeconfig` | `POLICYREPORT_KUBECONFIG` | `~/.kube/config` | Kubeconfig file to use (only if falcosidekick is running outside the cluster) | +| `policyreport.falconamespace` | `POLICYREPORT_FALCONAMESPACE` | | Set the namespace where Falco is running (only if falcosidekick is running outside the cluster) | | `policyreport.maxevents` | `POLICYREPORT_MAXEVENTS` | `1000` | The max number of events that can be in a policyreport | -| `policyreport.prunebypriority` | `POLICYREPORT_PRUNEBYPRIORITY` | `false` | If true; the events with lowest severity are pruned first, in FIFO order | | `policyreport.minimumpriority` | `POLICYREPORT_MINIMUMPRIORITY` | `""` (= `debug`) | Minimum priority of event for using this output, order is `emergency,alert,critical,error,warning,notice,informational,debug or ""` | > [!NOTE] @@ -31,9 +31,9 @@ The Env var values override the settings from yaml file. ```yaml policyreport: enabled: false # if true; policyreport output is enabled - kubeconfig: "~/.kube/config" # Kubeconfig file to use (only if falcosidekick is running outside the cluster) + kubeconfig: "~/.kube/config" # kubeconfig file to use (only if falcosidekick is running outside the cluster) + falconamespace: "" # set the namespace where Falco is running (only if falcosidekick is running outside the cluster) maxevents: 1000 # the max number of events that can be in a policyreport (default: 1000) - prunebypriority: false # if true; the events with lowest severity are pruned first, in FIFO order (default: false) minimumpriority: "debug" # events with a priority above this are mapped to fail in PolicyReport Summary and lower that those are mapped to warn (default="") ``` @@ -41,6 +41,9 @@ policyreport: ### Installing Policy Report Custom Resource Definition (CRD) +> [!WARNING] +This output works only for the sources `syscalls` and `k8saudit`. + > [!WARNING] Installation of the Policy Report Custom Resource Definition (CRD) is a prerequisite for using the Policy Report output. diff --git a/handlers.go b/handlers.go index e315c5a0f..a6dc44ecf 100644 --- a/handlers.go +++ b/handlers.go @@ -18,7 +18,11 @@ import ( "github.com/google/uuid" ) -const testRule string = "Test rule" +const ( + testRule string = "Test rule" + syscalls string = "syscalls" + syscall string = "syscall" +) // mainHandler is Falco Sidekick main handler (default). func mainHandler(w http.ResponseWriter, r *http.Request) { @@ -103,7 +107,7 @@ func newFalcoPayload(payload io.Reader) (types.FalcoPayload, error) { } if falcopayload.Source == "" { - falcopayload.Source = "syscalls" + falcopayload.Source = syscalls } falcopayload.UUID = uuid.New().String() @@ -193,8 +197,6 @@ func newFalcoPayload(payload io.Reader) (types.FalcoPayload, error) { } } - fmt.Println(falcopayload.String()) - if config.Debug { log.Printf("[DEBUG] : Falco's payload : %v\n", falcopayload.String()) } @@ -383,7 +385,9 @@ func forwardEvent(falcopayload types.FalcoPayload) { go fissionClient.FissionCall(falcopayload) } if config.PolicyReport.Enabled && (falcopayload.Priority >= types.Priority(config.PolicyReport.MinimumPriority)) { - go policyReportClient.UpdateOrCreatePolicyReport(falcopayload) + if falcopayload.Source == syscalls || falcopayload.Source == syscall || falcopayload.Source == "k8saudit" { + go policyReportClient.UpdateOrCreatePolicyReport(falcopayload) + } } if config.Yandex.S3.Bucket != "" && (falcopayload.Priority >= types.Priority(config.Yandex.S3.MinimumPriority) || falcopayload.Rule == testRule) { @@ -438,7 +442,7 @@ func forwardEvent(falcopayload types.FalcoPayload) { go dynatraceClient.DynatracePost(falcopayload) } - if config.OTLP.Traces.Endpoint != "" && (falcopayload.Priority >= types.Priority(config.OTLP.Traces.MinimumPriority)) && (falcopayload.Source == "syscall" || falcopayload.Source == "syscalls") { + if config.OTLP.Traces.Endpoint != "" && (falcopayload.Priority >= types.Priority(config.OTLP.Traces.MinimumPriority)) && (falcopayload.Source == syscall || falcopayload.Source == syscalls) { go otlpClient.OTLPTracesPost(falcopayload) } } diff --git a/outputs/policyreport.go b/outputs/policyreport.go index b3cda8ac4..a2ff1f337 100644 --- a/outputs/policyreport.go +++ b/outputs/policyreport.go @@ -4,24 +4,20 @@ package outputs import ( "context" - "fmt" "log" "os" "github.com/DataDog/datadog-go/statsd" "github.com/falcosecurity/falcosidekick/types" - "github.com/google/uuid" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + errorsv1 "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/retry" wgpolicy "sigs.k8s.io/wg-policy-prototypes/policy-report/pkg/api/wgpolicyk8s.io/v1alpha2" - crdClient "sigs.k8s.io/wg-policy-prototypes/policy-report/pkg/generated/v1alpha2/clientset/versioned" + crd "sigs.k8s.io/wg-policy-prototypes/policy-report/pkg/generated/v1alpha2/clientset/versioned" ) type resource struct { @@ -30,9 +26,12 @@ type resource struct { } const ( - clusterPolicyReportBaseName = "falco-cluster-policy-report-" - policyReportBaseName = "falco-policy-report-" - policyReportSource = "Falco" + clusterPolicyReportName string = "falco-cluster-policy-report" + policyReportName string = "falco-policy-report" + policyReportSource string = "Falco" + + update string = "Update" + create string = "Create" high wgpolicy.PolicyResultSeverity = "high" low wgpolicy.PolicyResultSeverity = "low" @@ -44,31 +43,38 @@ const ( warn wgpolicy.PolicyResult = "warn" skip wgpolicy.PolicyResult = "skip" - targetNS = "ka.target.namespace" - targetResource = "ka.target.resource" - targetName = "ka.target.name" - responseName = "ka.resp.name" + k8sPodName string = "k8s.pod.name" + k8sNsName string = "k8s.ns.name" + kaTargetNS string = "ka.target.namespace" + kaTargetResource string = "ka.target.resource" + kaTargetName string = "ka.target.name" + kaRespName string = "ka.resp.name" ) var ( - minimumPriority string //nolint: unused - //slice of policy reports - policyReports = make(map[string]*wgpolicy.PolicyReport) - //cluster policy report - clusterPolicyReport *wgpolicy.ClusterPolicyReport = &wgpolicy.ClusterPolicyReport{ + defaultNamespace string = "default" + + // default policy report + defaultPolicyReport *wgpolicy.PolicyReport = &wgpolicy.PolicyReport{ ObjectMeta: metav1.ObjectMeta{ - Name: clusterPolicyReportBaseName, + Name: policyReportName, Labels: map[string]string{ - "app.kubernetes.io/created-by": "falcosidekick", + "app.kubernetes.io/managed-by": "falcosidekick", }, }, - Summary: wgpolicy.PolicyReportSummary{ - Fail: 0, - Warn: 0, + Summary: wgpolicy.PolicyReportSummary{}, + } + + // default cluster policy report + defaultClusterPolicyReport *wgpolicy.ClusterPolicyReport = &wgpolicy.ClusterPolicyReport{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterPolicyReportName, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "falcosidekick", + }, }, + Summary: wgpolicy.PolicyReportSummary{}, } - falcosidekickNamespace string - falcosidekickNamespaceUID k8stypes.UID // used resources in the k8saudit ruleset resourceMapping = map[string]resource{ @@ -79,7 +85,8 @@ var ( "namespaces": {"v1", "Namespace"}, "serviceaccounts": {"v1", "ServiceAccount"}, "daemonsets": {"apps/v1", "DaemonSet"}, - "deployments": {"apps/v1", "Deployments"}, + "deployments": {"apps/v1", "Deployment"}, + "statefulsets": {"apps/v1", "StatefulSet"}, "cronjobs": {"batch/v1", "CronJob"}, "jobs": {"batch/v1", "Job"}, "clusterroles": {"rbac.authorization.k8s.io/v1", "ClusterRole"}, @@ -90,17 +97,15 @@ var ( ) func NewPolicyReportClient(config *types.Configuration, stats *types.Statistics, promStats *types.PromStatistics, statsdClient, dogstatsdClient *statsd.Client) (*Client, error) { - clusterPolicyReport.ObjectMeta.Name += uuid.NewString()[:8] - minimumPriority = config.PolicyReport.MinimumPriority - clientConfig, err := rest.InClusterConfig() if err != nil { clientConfig, err = clientcmd.BuildConfigFromFlags("", config.PolicyReport.Kubeconfig) if err != nil { log.Printf("[ERROR] : PolicyReport - Unable to load kube config file: %v\n", err) + return nil, err } } - crdclient, err := crdClient.NewForConfig(clientConfig) + crdclient, err := crd.NewForConfig(clientConfig) if err != nil { return nil, err } @@ -109,26 +114,26 @@ func NewPolicyReportClient(config *types.Configuration, stats *types.Statistics, return nil, err } - falcosidekickNamespace = os.Getenv("NAMESPACE") - if falcosidekickNamespace == "" { - log.Println("[INFO] : PolicyReport - No env var NAMESPACE detected") - } else { - n, err := clientset.CoreV1().Namespaces().Get(context.TODO(), falcosidekickNamespace, metav1.GetOptions{}) + if config.PolicyReport.FalcoNamespace == "" { + dat, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") if err != nil { - log.Printf("[ERROR] : PolicyReport - Can't get UID of namespace %v: %v\n", falcosidekickNamespace, err) + log.Printf("[ERROR] : PolicyReport - Unable to get the Falcosidekick's namespace, '%v' used instead\n", defaultNamespace) } else { - falcosidekickNamespaceUID = n.ObjectMeta.UID + defaultNamespace = string(dat) } + } else { + defaultNamespace = config.PolicyReport.FalcoNamespace } return &Client{ - OutputType: "PolicyReport", - Config: config, - Stats: stats, - PromStats: promStats, - StatsdClient: statsdClient, - DogstatsdClient: dogstatsdClient, - Crdclient: crdclient, + OutputType: "PolicyReport", + Config: config, + Stats: stats, + PromStats: promStats, + StatsdClient: statsdClient, + DogstatsdClient: dogstatsdClient, + KubernetesClient: clientset, + Crdclient: crdclient, }, nil } @@ -136,252 +141,183 @@ func NewPolicyReportClient(config *types.Configuration, stats *types.Statistics, func (c *Client) UpdateOrCreatePolicyReport(falcopayload types.FalcoPayload) { c.Stats.PolicyReport.Add(Total, 1) - event, namespace := newResult(falcopayload) + result := newResult(falcopayload) + namespace := getNamespace(falcopayload.OutputFields) var err error if namespace != "" { - // case where the event is namespace specific - err = updatePolicyReports(c, namespace, event) + err = c.createOrUpdatePolicyReport(result, namespace) } else { - err = updateClusterPolicyReport(c, event) + err = c.createOrUpdateClusterPolicyReport(result) } if err == nil { - go c.CountMetric(Outputs, 1, []string{"output:policyreport", "status:ok"}) + go c.CountMetric(Outputs, 1, []string{"output:policyreport", "status:" + OK}) c.Stats.PolicyReport.Add(OK, 1) c.PromStats.Outputs.With(map[string]string{"destination": "policyreport", "status": OK}).Inc() } else { - go c.CountMetric(Outputs, 1, []string{"output:policyreport", "status:error"}) + go c.CountMetric(Outputs, 1, []string{"output:policyreport", "status:" + Error}) c.Stats.PolicyReport.Add(Error, 1) c.PromStats.Outputs.With(map[string]string{"destination": "policyreport", "status": Error}).Inc() } } // newResult creates a new entry for Reports -func newResult(falcopayload types.FalcoPayload) (_ *wgpolicy.PolicyReportResult, namespace string) { +func newResult(falcopayload types.FalcoPayload) *wgpolicy.PolicyReportResult { var properties = make(map[string]string) - for property, value := range falcopayload.OutputFields { - if property == targetNS || property == "k8s.ns.name" { - namespace = toString(value) // not empty for policy reports - } - properties[property] = toString(value) + for i, j := range falcopayload.OutputFields { + properties[i] = toString(j) } return &wgpolicy.PolicyReportResult{ - Policy: falcopayload.Rule, + Policy: falcopayload.Source, + Rule: falcopayload.Rule, Category: "SI - System and Information Integrity", Source: policyReportSource, - Scored: false, Timestamp: metav1.Timestamp{Seconds: int64(falcopayload.Time.Second()), Nanos: int32(falcopayload.Time.Nanosecond())}, Severity: mapSeverity(falcopayload), Result: mapResult(falcopayload), Description: falcopayload.Output, Properties: properties, - Subjects: mapResource(falcopayload, namespace), - }, namespace -} - -// check for low priority events to delete first -func checklow(result []wgpolicy.PolicyReportResult) (swapint int) { - for i, j := range result { - if j.Severity == medium || j.Severity == low || j.Severity == info { - return i - } + Subjects: getSubjects(falcopayload), } - return -1 } -// update summary for clusterpolicyreport 'report' -func updateClusterPolicyReportSummary(event *wgpolicy.PolicyReportResult) { - if event.Result == fail { - clusterPolicyReport.Summary.Fail++ - } else { - clusterPolicyReport.Summary.Warn++ - } -} +func (c *Client) createOrUpdatePolicyReport(result *wgpolicy.PolicyReportResult, namespace string) error { + action := update -// update summary for specific policyreport in 'policyReports' at index 'n' -func updatePolicyReportSummary(rep *wgpolicy.PolicyReport, event *wgpolicy.PolicyReportResult) { - if event.Result == fail { - rep.Summary.Fail++ - } else { - rep.Summary.Warn++ - } -} - -func updatePolicyReports(c *Client, namespace string, event *wgpolicy.PolicyReportResult) error { - //policyReport to be created - if policyReports[namespace] == nil { - policyReports[namespace] = &wgpolicy.PolicyReport{ - ObjectMeta: metav1.ObjectMeta{ - Name: policyReportBaseName + uuid.NewString()[:8], - Labels: map[string]string{ - "app.kubernetes.io/created-by": "falcosidekick", - }, - }, - Summary: wgpolicy.PolicyReportSummary{ - Fail: 0, - Warn: 0, - }, - } - if falcosidekickNamespace != "" && falcosidekickNamespaceUID != "" { - policyReports[namespace].ObjectMeta.OwnerReferences = []metav1.OwnerReference{ - { - APIVersion: "v1", - Kind: "Namespace", - Name: falcosidekickNamespace, - UID: falcosidekickNamespaceUID, - Controller: new(bool), - }, - } + _, err := c.KubernetesClient.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{}) + if err != nil { + if errorsv1.IsNotFound(err) { + log.Printf("[INFO] : PolicyReport - Can't find the namespace '%v', fallback to '%v'\n", namespace, defaultNamespace) + namespace = defaultNamespace + result.Subjects[0].Namespace = defaultNamespace } } - policyr := c.Crdclient.Wgpolicyk8sV1alpha2().PolicyReports(namespace) - updatePolicyReportSummary(policyReports[namespace], event) - if len(policyReports[namespace].Results) == c.Config.PolicyReport.MaxEvents { - if c.Config.PolicyReport.PruneByPriority { - pruningLogicForPolicyReports(namespace) - } else { - summaryDeletion(&policyReports[namespace].Summary, policyReports[namespace].Results[0].Result) - policyReports[namespace].Results = policyReports[namespace].Results[1:] - } - } - policyReports[namespace].Results = append(policyReports[namespace].Results, *event) - _, getErr := policyr.Get(context.Background(), policyReports[namespace].Name, metav1.GetOptions{}) - if errors.IsNotFound(getErr) { - result, err := policyr.Create(context.TODO(), policyReports[namespace], metav1.CreateOptions{}) - if err != nil { - log.Printf("[ERROR] : PolicyReport - Can't create Policy Report %v in namespace %v\n", err, namespace) + policyr, err := c.Crdclient.Wgpolicyk8sV1alpha2().PolicyReports(namespace).Get(context.Background(), policyReportName, metav1.GetOptions{}) + if err != nil { + if !errorsv1.IsNotFound(err) { return err } - log.Printf("[INFO] : PolicyReport - Create policy report %v in namespace %v\n", result.GetObjectMeta().GetName(), namespace) - - } else { - // Update existing Policy Report - retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { - result, err := policyr.Get(context.Background(), policyReports[namespace].GetName(), metav1.GetOptions{}) - if errors.IsNotFound(err) { - // This doesnt ever happen even if it is already deleted or not found - log.Printf("[ERROR] : PolicyReport - Policy Report %v not found in namespace %v\n", policyReports[namespace].GetName(), namespace) - return err - } - if err != nil { - log.Printf("[ERROR] : PolicyReport - Policy Report %v in namespace %v: %v\n", policyReports[namespace].GetName(), namespace, err) - return err - } - policyReports[namespace].SetResourceVersion(result.GetResourceVersion()) - _, updateErr := policyr.Update(context.Background(), policyReports[namespace], metav1.UpdateOptions{}) - return updateErr - }) - if retryErr != nil { - log.Printf("[ERROR] : PolicyReport - Update has failed for Policy Report %v in namespace %v: %v\n", policyReports[namespace].GetName(), namespace, retryErr) - return retryErr - } - log.Printf("[INFO] : PolicyReport - Policy Report %v in namespace %v has been updated\n", policyReports[namespace].GetName(), namespace) } - return nil -} + if policyr.Name == "" { + policyr = defaultPolicyReport + action = create + } -func updateClusterPolicyReport(c *Client, event *wgpolicy.PolicyReportResult) error { - updateClusterPolicyReportSummary(event) - //clusterpolicyreport to be created - clusterpr := c.Crdclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports() + policyr.Results = append(policyr.Results, *result) - if len(clusterPolicyReport.Results) == c.Config.PolicyReport.MaxEvents { - if c.Config.PolicyReport.PruneByPriority { - pruningLogicForClusterPolicyReport() - } else { - summaryDeletion(&clusterPolicyReport.Summary, clusterPolicyReport.Results[0].Result) - - clusterPolicyReport.Results = clusterPolicyReport.Results[1:] - } + if len(policyr.Results) > c.Config.PolicyReport.MaxEvents { + policyr.Results = policyr.Results[1:] } - clusterPolicyReport.Results = append(clusterPolicyReport.Results, *event) + policyr.Summary = getSummary(policyr.Results) - _, getErr := clusterpr.Get(context.Background(), clusterPolicyReport.Name, metav1.GetOptions{}) - if errors.IsNotFound(getErr) { - result, err := clusterpr.Create(context.TODO(), clusterPolicyReport, metav1.CreateOptions{}) + if action == create { + _, err := c.Crdclient.Wgpolicyk8sV1alpha2().PolicyReports(namespace).Create(context.Background(), policyr, metav1.CreateOptions{}) if err != nil { - log.Printf("[ERROR] : PolicyReport - %v\n", err) - return err - } - log.Printf("[INFO] : PolicyReport - Create Cluster Policy Report %v\n", result.GetObjectMeta().GetName()) - } else { - // Update existing Cluster Policy Report - retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { - result, err := clusterpr.Get(context.Background(), clusterPolicyReport.GetName(), metav1.GetOptions{}) - if errors.IsNotFound(err) { - // This doesnt ever happen even if it is already deleted or not found - log.Printf("[ERROR] : PolicyReport - Cluster Policy Report %v not found\n", clusterPolicyReport.GetName()) + if errorsv1.IsAlreadyExists(err) { + action = update + policyr, err = c.Crdclient.Wgpolicyk8sV1alpha2().PolicyReports(namespace).Get(context.Background(), policyReportName, metav1.GetOptions{}) + if err != nil { + log.Printf("[ERROR] : PolicyReport - Error with with the Policy Report %v in namespace %v: %v\n", policyReportName, namespace, err) + return err + } + _, err := c.Crdclient.Wgpolicyk8sV1alpha2().PolicyReports(namespace).Update(context.Background(), policyr, metav1.UpdateOptions{}) + if err != nil { + log.Printf("[ERROR] : PolicyReport - Can't %v the Policy Report %v in namespace %v: %v\n", action, policyReportName, namespace, err) + return err + } + } else { + log.Printf("[ERROR] : PolicyReport - Can't %v the Policy Report %v in namespace %v: %v\n", action, policyReportName, namespace, err) return err } - if err != nil { - log.Printf("[ERROR] : PolicyReport - Cluster Policy Report %v: %v\n", clusterPolicyReport.GetName(), err) - return err - } - clusterPolicyReport.SetResourceVersion(result.GetResourceVersion()) - _, updateErr := clusterpr.Update(context.Background(), clusterPolicyReport, metav1.UpdateOptions{}) - return updateErr - }) - if retryErr != nil { - log.Printf("[ERROR] : PolicyReport - Update has failed for Cluster Policy Report %v: %v\n", clusterPolicyReport.GetName(), retryErr) - return retryErr } - log.Printf("[INFO] : PolicyReport - Cluster Policy Report %v has been updated\n", clusterPolicyReport.GetName()) + log.Printf("[INFO] : PolicyReport - %v the Policy Report %v in namespace %v\n", action, policyReportName, namespace) + return nil + } else { + _, err := c.Crdclient.Wgpolicyk8sV1alpha2().PolicyReports(namespace).Update(context.Background(), policyr, metav1.UpdateOptions{}) + if err != nil { + log.Printf("[ERROR] : PolicyReport - Can't %v the Policy Report %v in namespace %v: %v\n", action, policyReportName, namespace, err) + return err + } + log.Printf("[INFO] : PolicyReport - %v the Policy Report %v in namespace %v\n", action, policyReportName, namespace) + return nil } - return nil } -func pruningLogicForPolicyReports(namespace string) { - policyReport, ok := policyReports[namespace] - if !ok { - return - } - - result := policyReport.Results[0] +func (c *Client) createOrUpdateClusterPolicyReport(result *wgpolicy.PolicyReportResult) error { + action := update - //To do for pruning for pruning one of policyreports - checklowvalue := checklow(policyReports[namespace].Results) - if checklowvalue > 0 { - result = policyReport.Results[checklowvalue] - policyReport.Results[checklowvalue] = policyReports[namespace].Results[0] + cpolicyr, err := c.Crdclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports().Get(context.Background(), clusterPolicyReportName, metav1.GetOptions{}) + if err != nil { + if !errorsv1.IsNotFound(err) { + return err + } } - if checklowvalue == -1 { - summaryDeletion(&policyReports[namespace].Summary, result.Result) - } else { - summaryDeletion(&policyReports[namespace].Summary, result.Result) + if cpolicyr == nil { + cpolicyr = defaultClusterPolicyReport + action = create } - policyReports[namespace].Results = policyReports[namespace].Results[1:] -} - -func pruningLogicForClusterPolicyReport() { - result := clusterPolicyReport.Results[0] - //To do for pruning cluster report - checklowvalue := checklow(clusterPolicyReport.Results) + cpolicyr.Results = append(cpolicyr.Results, *result) - if checklowvalue > 0 { - result = clusterPolicyReport.Results[checklowvalue] - clusterPolicyReport.Results[checklowvalue] = clusterPolicyReport.Results[0] + if len(cpolicyr.Results) > c.Config.PolicyReport.MaxEvents { + cpolicyr.Results = cpolicyr.Results[1:] } - if checklowvalue == -1 { - summaryDeletion(&clusterPolicyReport.Summary, result.Result) + + cpolicyr.Summary = getSummary(cpolicyr.Results) + + if action == create { + _, err := c.Crdclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports().Create(context.Background(), cpolicyr, metav1.CreateOptions{}) + if err != nil { + if errorsv1.IsAlreadyExists(err) { + action = update + cpolicyr, err = c.Crdclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports().Get(context.Background(), policyReportName, metav1.GetOptions{}) + if err != nil { + log.Printf("[ERROR] : PolicyReport - Error with with the Cluster Policy Report %v: %v\n", policyReportName, err) + return err + } + _, err := c.Crdclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports().Update(context.Background(), cpolicyr, metav1.UpdateOptions{}) + if err != nil { + log.Printf("[ERROR] : PolicyReport - Can't %v the Cluster Policy Report %v: %v\n", action, policyReportName, err) + return err + } + } else { + log.Printf("[ERROR] : PolicyReport - Can't %v the Cluster Policy Report %v: %v\n", action, clusterPolicyReportName, err) + return err + } + } + log.Printf("[INFO] : PolicyReport - %v Cluster the Policy Report %v\n", action, policyReportName) + return nil } else { - summaryDeletion(&clusterPolicyReport.Summary, result.Result) + _, err := c.Crdclient.Wgpolicyk8sV1alpha2().ClusterPolicyReports().Update(context.Background(), cpolicyr, metav1.UpdateOptions{}) + if err != nil { + log.Printf("[ERROR] : PolicyReport - Can't %v the Cluster Policy Report %v: %v\n", action, clusterPolicyReportName, err) + return err + } + log.Printf("[INFO] : PolicyReport - %v the ClusterPolicy Report %v\n", action, policyReportName) + return nil } - clusterPolicyReport.Results = clusterPolicyReport.Results[1:] } -func summaryDeletion(summary *wgpolicy.PolicyReportSummary, result wgpolicy.PolicyResult) { - switch result { - case fail: - summary.Fail-- - case warn: - summary.Warn-- - case skip: - summary.Skip-- +func getSummary(results []wgpolicy.PolicyReportResult) wgpolicy.PolicyReportSummary { + var summary wgpolicy.PolicyReportSummary + for _, i := range results { + switch i.Result { + case "pass": + summary.Pass++ + case "fail": + summary.Fail++ + case "warn": + summary.Warn++ + case "error": + summary.Error++ + case "skip": + summary.Skip++ + } } + return summary } func mapResult(event types.FalcoPayload) wgpolicy.PolicyResult { @@ -408,46 +344,47 @@ func mapSeverity(event types.FalcoPayload) wgpolicy.PolicyResultSeverity { } } -func mapResource(event types.FalcoPayload, ns string) []corev1.ObjectReference { - name := determineResourceName(event.OutputFields) - if name != "" { +func getSubjects(event types.FalcoPayload) []corev1.ObjectReference { + name, kind := getResourceNameKind(event.OutputFields) + if name == "" || kind == "" { return nil } - - targetResource, ok := event.OutputFields[targetResource] - if !ok { - return []corev1.ObjectReference{ - { - Namespace: ns, - Name: toString(name), - }, - } - } - - resource, ok := resourceMapping[toString(targetResource)] - if !ok { - resource.kind = toString(targetResource) - } + namespace := getNamespace(event.OutputFields) return []corev1.ObjectReference{ { - Namespace: ns, + Namespace: namespace, Name: toString(name), - Kind: resource.kind, - APIVersion: resource.apiVersion, + Kind: resourceMapping[kind].kind, + APIVersion: resourceMapping[kind].apiVersion, }, } } -func determineResourceName(outputFields map[string]interface{}) string { - name, ok := outputFields[targetName] - if ok { - return toString(name) +func getNamespace(outputFields map[string]interface{}) string { + if outputFields[k8sNsName] != nil { + return toString(outputFields[k8sNsName]) + } + if outputFields[kaTargetNS] != nil { + return toString(outputFields[kaTargetNS]) } - return toString(outputFields[responseName]) + return "" } -func toString(value interface{}) string { - return fmt.Sprintf("%v", value) +func getResourceNameKind(outputFields map[string]interface{}) (string, string) { + if outputFields[k8sPodName] != nil { + return toString(outputFields[k8sPodName]), "pods" + } + if outputFields[kaTargetResource] == nil { + return "", "" + } + if outputFields[kaTargetName] != nil { + return toString(outputFields[kaTargetName]), toString(outputFields[kaTargetResource]) + } + if outputFields[kaRespName] != nil { + return toString(outputFields[kaRespName].(string)), toString(outputFields[kaTargetResource]) + } + + return "", "" } diff --git a/outputs/utils.go b/outputs/utils.go index 2602fd574..b80b63282 100644 --- a/outputs/utils.go +++ b/outputs/utils.go @@ -2,7 +2,10 @@ package outputs -import "sort" +import ( + "fmt" + "sort" +) func getSortedStringKeys(m map[string]interface{}) []string { var keys []string @@ -17,3 +20,7 @@ func getSortedStringKeys(m map[string]interface{}) []string { sort.Strings(keys) return keys } + +func toString(value interface{}) string { + return fmt.Sprintf("%v", value) +} diff --git a/types/types.go b/types/types.go index 42cce2675..24ceb6892 100644 --- a/types/types.go +++ b/types/types.go @@ -597,6 +597,7 @@ type PolicyReportConfig struct { Enabled bool PruneByPriority bool Kubeconfig string + FalcoNamespace string MinimumPriority string MaxEvents int }