From 7a58b7b77ebc347dc79a33b06bd4d56a4d15facc Mon Sep 17 00:00:00 2001 From: Thomas Labarussias Date: Fri, 26 Jan 2024 16:12:27 +0100 Subject: [PATCH] new actionner kubernetes:log Signed-off-by: Thomas Labarussias --- README.md | 11 ++++ actionners/actionners.go | 15 ++++- actionners/kubernetes/log/log.go | 109 +++++++++++++++++++++++++++++++ notifiers/smtp/smtp.go | 2 +- notifiers/smtp/templates.go | 10 +-- 5 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 actionners/kubernetes/log/log.go diff --git a/README.md b/README.md index 15d8f667..53d2fbc3 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - [`kubernetes:networkpolicy`](#kubernetesnetworkpolicy) - [`kubernetes:exec`](#kubernetesexec) - [`kubernetes:script`](#kubernetesscript) + - [`kubernetes:log`](#kuberneteslog) - [Notifiers](#notifiers) - [K8s Events](#k8s-events) - [Slack](#slack) @@ -116,6 +117,16 @@ Each `actionner` is configured with: * `k8s.pod.name` * `k8s.ns.name` +### `kubernetes:log` + +* Description: **Get logs from a pod** +* Continue: `true` +* Parameters: + * `tail_lines`: The number of lines from the end of the logs to show (default: `1000`) +* Required fields: + * `k8s.pod.name` + * `k8s.ns.name` + ## Notifiers `Notifiers` define which outputs to notify with result of actions. diff --git a/actionners/actionners.go b/actionners/actionners.go index 9388de52..6670802e 100644 --- a/actionners/actionners.go +++ b/actionners/actionners.go @@ -5,6 +5,7 @@ import ( "github.com/Issif/falco-talon/actionners/kubernetes/exec" labelize "github.com/Issif/falco-talon/actionners/kubernetes/labelize" + logActionner "github.com/Issif/falco-talon/actionners/kubernetes/log" networkpolicy "github.com/Issif/falco-talon/actionners/kubernetes/networkpolicy" "github.com/Issif/falco-talon/actionners/kubernetes/script" terminate "github.com/Issif/falco-talon/actionners/kubernetes/terminate" @@ -97,7 +98,19 @@ func GetDefaultActionners() *Actionners { }, CheckParameters: script.CheckParameters, Action: script.Script, - }) + }, + &Actionner{ + Category: "kubernetes", + Name: "log", + DefaultContinue: true, + Init: kubernetes.Init, + Checks: []checkActionner{ + kubernetes.CheckPodExist, + }, + CheckParameters: logActionner.CheckParameters, + Action: logActionner.Log, + }, + ) } return defaultActionners diff --git a/actionners/kubernetes/log/log.go b/actionners/kubernetes/log/log.go new file mode 100644 index 00000000..8d8d7418 --- /dev/null +++ b/actionners/kubernetes/log/log.go @@ -0,0 +1,109 @@ +package log + +import ( + "bytes" + "context" + "fmt" + "io" + + corev1 "k8s.io/api/core/v1" + + "github.com/Issif/falco-talon/internal/events" + kubernetes "github.com/Issif/falco-talon/internal/kubernetes/client" + "github.com/Issif/falco-talon/internal/rules" + "github.com/Issif/falco-talon/utils" +) + +var Log = func(rule *rules.Rule, action *rules.Action, event *events.Event) (utils.LogLine, error) { + pod := event.GetPodName() + namespace := event.GetNamespaceName() + + objects := map[string]string{ + "Pod": pod, + "Namespace": namespace, + } + + parameters := action.GetParameters() + tailLines := new(int64) + if parameters["tail_lines"] != nil { + *tailLines = int64(parameters["tail_lines"].(int)) + } + if *tailLines == 0 { + *tailLines = 20 + } + + command := new(string) + if parameters["command"] != nil { + *command = parameters["command"].(string) + } + + client := kubernetes.GetClient() + + p, _ := client.GetPod(pod, namespace) + containers := kubernetes.GetContainers(p) + if len(containers) == 0 { + err := fmt.Errorf("no container found") + return utils.LogLine{ + Objects: objects, + Error: err.Error(), + Status: "failure", + }, + err + } + + ctx := context.Background() + var output string + + for i, container := range containers { + logs, err := client.Clientset.CoreV1().Pods(namespace).GetLogs(pod, &corev1.PodLogOptions{ + Container: container, + TailLines: tailLines, + }).Stream(ctx) + if err != nil { + if err != nil { + if i == len(containers)-1 { + return utils.LogLine{ + Objects: objects, + Error: err.Error(), + Status: "failure", + }, err + } + continue + } + } + defer logs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, logs) + if err != nil { + return utils.LogLine{ + Objects: objects, + Status: "failure", + Error: err.Error(), + }, + err + } + + output = buf.String() + if output != "" { + break + } + } + + return utils.LogLine{ + Objects: objects, + Output: output, + Status: "success", + }, + nil +} + +var CheckParameters = func(action *rules.Action) error { + parameters := action.GetParameters() + err := utils.CheckParameters(parameters, "tail_lines", utils.IntStr, nil, false) + if err != nil { + return err + } + + return nil +} diff --git a/notifiers/smtp/smtp.go b/notifiers/smtp/smtp.go index 63d1b3ab..71f8880f 100644 --- a/notifiers/smtp/smtp.go +++ b/notifiers/smtp/smtp.go @@ -59,7 +59,7 @@ var Init = func(fields map[string]interface{}) error { var Notify = func(log utils.LogLine) error { if smtpconfig.HostPort == "" { - return errors.New("wrong config") + return errors.New("wrong host_port") } payload, err := NewPayload(log) diff --git a/notifiers/smtp/templates.go b/notifiers/smtp/templates.go index d7b474d0..63a32acd 100644 --- a/notifiers/smtp/templates.go +++ b/notifiers/smtp/templates.go @@ -34,7 +34,7 @@ var htmlTmpl = ` td{font-family:arial,helvetica,sans-serif;} - +
@@ -43,14 +43,14 @@ var htmlTmpl = `

- +
- +
{{ .Status }}{{ .Status }}
- +
@@ -97,7 +97,7 @@ var htmlTmpl = ` {{ if .Output }} - + {{ end }}
Action
Output{{ printf "%s" .Output }}{{ printf "%s" .Output }}