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 }} |
-
+
Action |
@@ -97,7 +97,7 @@ var htmlTmpl = `
{{ if .Output }}
Output |
- {{ printf "%s" .Output }} |
+ {{ printf "%s" .Output }} |
{{ end }}