From 94f46351c213cc439e79aa55a6a3e1edb22c6e67 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Tue, 25 Jun 2024 15:50:55 +0000 Subject: [PATCH] chore(logs): add output format option to `logs` command Signed-off-by: Navin Chandra --- cmd/log.go | 1 + log/log.go | 3 +- log/logClient.go | 13 ++- recommend/registry/registry.go | 105 +++++++++++------- sysdump/sysdump.go | 47 ++++---- ...ry.yaml => file-integrity-monitoring.yaml} | 19 +++- ...ckage-management-process-in-container.yaml | 44 +++----- .../ubuntu-18-04/system-owner-discovery.yaml | 8 +- ...4-8-apache-file-integrity-monitoring.yaml} | 19 +++- ...ckage-management-process-in-container.yaml | 44 +++----- ...ess-4-8-apache-system-owner-discovery.yaml | 8 +- 11 files changed, 165 insertions(+), 146 deletions(-) rename tests/recommend/res/out/ubuntu-18-04/{system-monitoring-deny-write-under-bin-directory.yaml => file-integrity-monitoring.yaml} (65%) rename tests/recommend/res/out/wordpress-mysql-wordpress/{wordpress-4-8-apache-system-monitoring-deny-write-under-bin-directory.yaml => wordpress-4-8-apache-file-integrity-monitoring.yaml} (64%) diff --git a/cmd/log.go b/cmd/log.go index 94e1574f..c0994c7a 100644 --- a/cmd/log.go +++ b/cmd/log.go @@ -33,6 +33,7 @@ func init() { logCmd.Flags().StringVar(&logOptions.LogPath, "logPath", "stdout", "Output location for alerts and logs, {path|stdout|none}") logCmd.Flags().StringVar(&logOptions.LogFilter, "logFilter", "policy", "Filter for what kinds of alerts and logs to receive, {policy|system|all}") logCmd.Flags().BoolVar(&logOptions.JSON, "json", false, "Flag to print alerts and logs in the JSON format") + logCmd.Flags().StringVarP(&logOptions.Output, "output", "o", "text", "Output format: text, json, or pretty-json") logCmd.Flags().StringVarP(&logOptions.Namespace, "namespace", "n", "", "k8s namespace filter") logCmd.Flags().StringVar(&logOptions.Operation, "operation", "", "Give the type of the operation (Eg:Process/File/Network)") logCmd.Flags().StringVar(&logOptions.LogType, "logType", "", "Log type you want (Eg:ContainerLog/HostLog) ") diff --git a/log/log.go b/log/log.go index 490215d9..b6167a0a 100644 --- a/log/log.go +++ b/log/log.go @@ -48,6 +48,7 @@ type Options struct { LogPath string LogFilter string JSON bool + Output string Namespace string LogType string Operation string @@ -152,7 +153,7 @@ func StartObserver(c *k8s.Client, o Options) error { return nil } - // create client + // create client logClient, err := NewClient(gRPC, o, c.K8sClientset) if err != nil { if !o.Secure && !isDialingError(err) { diff --git a/log/logClient.go b/log/logClient.go index e5235656..a6d54dc2 100644 --- a/log/logClient.go +++ b/log/logClient.go @@ -4,6 +4,7 @@ package log import ( + "bytes" "context" "encoding/json" "errors" @@ -406,8 +407,18 @@ func WatchTelemetryHelper(arr []byte, t string, o Options) { o.EventChan <- EventInfo{Data: arr, Type: t} } - if o.JSON { + if o.JSON || o.Output == "json" { str = fmt.Sprintf("%s\n", string(arr)) + } else if o.Output == "pretty-json" { + + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, arr, "", " ") + + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to prettify JSON (%s)\n", err.Error()) + } + str = fmt.Sprintf("%s\n", prettyJSON.String()) + } else { if time, ok := res["UpdatedTime"]; ok { diff --git a/recommend/registry/registry.go b/recommend/registry/registry.go index 5038bdb4..6fd9b475 100644 --- a/recommend/registry/registry.go +++ b/recommend/registry/registry.go @@ -213,63 +213,86 @@ func extractTar(tarname string, tempDir string) ([]string, []string) { }).Fatal("os create failed") } defer hacks.CloseCheckErr(f, tarname) - - tr := tar.NewReader(bufio.NewReader(f)) - for { - hdr, err := tr.Next() - if err == io.EOF { - break // End of archive - } - if err != nil { - log.WithError(err).Fatal("tar next failed") - } - - tgt, err := sanitizeArchivePath(tempDir, hdr.Name) + if isTarFile(f) { + _, err := f.Seek(0, 0) if err != nil { log.WithError(err).WithFields(log.Fields{ - "file": hdr.Name, - }).Error("ignoring file since it could not be sanitized") - continue + "tar": tarname, + }).Fatal("Failed to seek to the beginning of the file") } - - switch hdr.Typeflag { - case tar.TypeDir: - if _, err := os.Stat(tgt); err != nil { - if err := os.MkdirAll(tgt, 0750); err != nil { - log.WithError(err).WithFields(log.Fields{ - "target": tgt, - }).Fatal("tar mkdirall") - } + tr := tar.NewReader(bufio.NewReader(f)) + for { + hdr, err := tr.Next() + if err == io.EOF { + break // End of archive + } + if err != nil { + log.WithError(err).Error("tar next failed") + return nil, nil } - dl = append(dl, tgt) - case tar.TypeReg: - f, err := os.OpenFile(filepath.Clean(tgt), os.O_CREATE|os.O_RDWR, os.FileMode(hdr.Mode)) + + tgt, err := sanitizeArchivePath(tempDir, hdr.Name) if err != nil { log.WithError(err).WithFields(log.Fields{ - "target": tgt, - }).Error("tar open file") - } else { + "file": hdr.Name, + }).Error("ignoring file since it could not be sanitized") + continue + } - // copy over contents - if _, err := io.CopyN(f, tr, 2e+9 /*2GB*/); err != io.EOF { + switch hdr.Typeflag { + case tar.TypeDir: + if _, err := os.Stat(tgt); err != nil { + if err := os.MkdirAll(tgt, 0750); err != nil { + log.WithError(err).WithFields(log.Fields{ + "target": tgt, + }).Fatal("tar mkdirall") + } + } + dl = append(dl, tgt) + case tar.TypeReg: + f, err := os.OpenFile(filepath.Clean(tgt), os.O_CREATE|os.O_RDWR, os.FileMode(hdr.Mode)) + if err != nil { log.WithError(err).WithFields(log.Fields{ "target": tgt, - }).Fatal("tar io.Copy()") + }).Error("tar open file") + } else { + + // copy over contents + if _, err := io.CopyN(f, tr, 2e+9 /*2GB*/); err != io.EOF { + log.WithError(err).WithFields(log.Fields{ + "target": tgt, + }).Fatal("tar io.Copy()") + } + } + hacks.CloseCheckErr(f, tgt) + if strings.HasSuffix(tgt, "layer.tar") { + ifl, idl := extractTar(tgt, tempDir) + fl = append(fl, ifl...) + dl = append(dl, idl...) + } else if strings.HasPrefix(hdr.Name, "blobs/") { + ifl, idl := extractTar(tgt, tempDir) + fl = append(fl, ifl...) + dl = append(dl, idl...) + + } else { + fl = append(fl, tgt) } - } - hacks.CloseCheckErr(f, tgt) - if strings.HasSuffix(tgt, "layer.tar") { // deflate container image layer - ifl, idl := extractTar(tgt, tempDir) - fl = append(fl, ifl...) - dl = append(dl, idl...) - } else { - fl = append(fl, tgt) } } + } else { + log.WithFields(log.Fields{ + "file": tarname, + }).Error("Not a valid tar file") } return fl, dl } +func isTarFile(f *os.File) bool { + tr := tar.NewReader(bufio.NewReader(f)) + _, err := tr.Next() + return err == nil +} + func saveImageToTar(imageName string, cli *client.Client, tempDir string) string { imgdata, err := cli.ImageSave(context.Background(), []string{imageName}) if err != nil { diff --git a/sysdump/sysdump.go b/sysdump/sysdump.go index 67212d5f..453991bc 100644 --- a/sysdump/sysdump.go +++ b/sysdump/sysdump.go @@ -97,34 +97,39 @@ func Collect(c *k8s.Client, o Options) error { // KubeArmor Pod errs.Go(func() error { pods, err := c.K8sClientset.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{ - LabelSelector: "kubearmor-app=kubearmor", + LabelSelector: "kubearmor-app", }) + if err != nil { fmt.Printf("kubearmor pod not found. (possible if kubearmor is running in process mode)\n") return nil } + fmt.Print("Checking all pods labeled with kubearmor-app\n") for _, p := range pods.Items { - // KubeArmor Logs - fmt.Printf("getting logs from %s\n", p.Name) - - v := c.K8sClientset.CoreV1().Pods(p.Namespace).GetLogs(p.Name, &corev1.PodLogOptions{}) - s, err := v.Stream(context.Background()) - if err != nil { - fmt.Printf("failed getting logs from pod=%s err=%s\n", p.Name, err) - continue - } - defer func() { - if err := s.Close(); err != nil { - kg.Warnf("Error closing io stream %s\n", err) + // Iterate over containers in the pod + for _, container := range p.Spec.Containers { + + // KubeArmor Logs + fmt.Printf("getting logs from pod=%s container=%s\n", p.Name, container.Name) + v := c.K8sClientset.CoreV1().Pods(p.Namespace).GetLogs(p.Name, &corev1.PodLogOptions{Container: container.Name}) + s, err := v.Stream(context.Background()) + if err != nil { + fmt.Printf("failed getting logs from pod=%s err=%s\n", p.Name, err) + continue + } + defer func() { + if err := s.Close(); err != nil { + kg.Warnf("Error closing io stream %s\n", err) + } + }() + var logs bytes.Buffer + if _, err = io.Copy(&logs, s); err != nil { + return err + } + if err := writeToFile(path.Join(d, "ka-pod-"+p.Name+"-log.txt"), logs.String()); err != nil { + return err } - }() - var logs bytes.Buffer - if _, err = io.Copy(&logs, s); err != nil { - return err - } - if err := writeToFile(path.Join(d, "ka-pod-"+p.Name+"-log.txt"), logs.String()); err != nil { - return err } // KubeArmor Describe @@ -173,7 +178,7 @@ func Collect(c *k8s.Client, o Options) error { return nil }) - // AppArmor Gzip + // AppArmor GzipS errs.Go(func() error { if err := copyFromPod("/etc/apparmor.d", d, c); err != nil { return err diff --git a/tests/recommend/res/out/ubuntu-18-04/system-monitoring-deny-write-under-bin-directory.yaml b/tests/recommend/res/out/ubuntu-18-04/file-integrity-monitoring.yaml similarity index 65% rename from tests/recommend/res/out/ubuntu-18-04/system-monitoring-deny-write-under-bin-directory.yaml rename to tests/recommend/res/out/ubuntu-18-04/file-integrity-monitoring.yaml index 51db72d7..652def38 100644 --- a/tests/recommend/res/out/ubuntu-18-04/system-monitoring-deny-write-under-bin-directory.yaml +++ b/tests/recommend/res/out/ubuntu-18-04/file-integrity-monitoring.yaml @@ -1,31 +1,38 @@ apiVersion: security.kubearmor.com/v1 kind: KubeArmorPolicy metadata: - name: ubuntu-18-04-system-monitoring-deny-write-under-bin-directory + name: ubuntu-18-04-file-integrity-monitoring spec: action: Block file: matchDirectories: - - dir: /bin/ + - dir: /sbin/ readOnly: true recursive: true - - dir: /sbin/ + - dir: /usr/bin/ + readOnly: true + recursive: true + - dir: /usr/lib/ readOnly: true recursive: true - dir: /usr/sbin/ readOnly: true recursive: true - - dir: /usr/bin/ + - dir: /bin/ + readOnly: true + recursive: true + - dir: /boot/ readOnly: true recursive: true - message: Alert! An attempt to write below system binary directories denied. + message: Detected and prevented compromise to File integrity selector: matchLabels: kubearmor.io/container.name: ubuntu - severity: 5 + severity: 1 tags: - NIST - NIST_800-53_AU-2 - NIST_800-53_SI-4 - MITRE - MITRE_T1036_masquerading + - MITRE_T1565_data_manipulation diff --git a/tests/recommend/res/out/ubuntu-18-04/least-functionality-execute-package-management-process-in-container.yaml b/tests/recommend/res/out/ubuntu-18-04/least-functionality-execute-package-management-process-in-container.yaml index 45cf2cff..f8d1c49d 100644 --- a/tests/recommend/res/out/ubuntu-18-04/least-functionality-execute-package-management-process-in-container.yaml +++ b/tests/recommend/res/out/ubuntu-18-04/least-functionality-execute-package-management-process-in-container.yaml @@ -11,35 +11,17 @@ spec: severity: 5 process: matchPaths: - - path: /usr/bin/apt - - path: /usr/bin/apt-get - - path: /bin/apt-get - - path: /sbin/apk - - path: /bin/apt - - path: /usr/bin/dpkg - - path: /bin/dpkg - - path: /usr/bin/gdebi - - path: /bin/gdebi - - path: /usr/bin/make - - path: /bin/make - - path: /usr/bin/yum - - path: /bin/yum - - path: /usr/bin/rpm - - path: /bin/rpm - - path: /usr/bin/dnf - - path: /bin/dnf - - path: /usr/bin/pacman - - path: /usr/sbin/pacman - - path: /bin/pacman - - path: /sbin/pacman - - path: /usr/bin/makepkg - - path: /usr/sbin/makepkg - - path: /bin/makepkg - - path: /sbin/makepkg - - path: /usr/bin/yaourt - - path: /usr/sbin/yaourt - - path: /bin/yaourt - - path: /sbin/yaourt - - path: /usr/bin/zypper - - path: /bin/zypper + - execname: apt + - execname: apt-get + - execname: apk + - execname: dpkg + - execname: gdebi + - execname: make + - execname: yum + - execname: rpm + - execname: dnf + - execname: pacman + - execname: makepkg + - execname: yaourt + - execname: zypper action: Block \ No newline at end of file diff --git a/tests/recommend/res/out/ubuntu-18-04/system-owner-discovery.yaml b/tests/recommend/res/out/ubuntu-18-04/system-owner-discovery.yaml index 5d74dcbb..8f1b92e2 100644 --- a/tests/recommend/res/out/ubuntu-18-04/system-owner-discovery.yaml +++ b/tests/recommend/res/out/ubuntu-18-04/system-owner-discovery.yaml @@ -7,10 +7,10 @@ spec: message: System owner discovery command execution denied process: matchPaths: - - path: /usr/bin/who - - path: /usr/bin/w - - path: /usr/bin/id - - path: /usr/bin/whoami + - execname: who + - execname: w + - execname: id + - execname: whoami selector: matchLabels: kubearmor.io/container.name: ubuntu diff --git a/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-monitoring-deny-write-under-bin-directory.yaml b/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-file-integrity-monitoring.yaml similarity index 64% rename from tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-monitoring-deny-write-under-bin-directory.yaml rename to tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-file-integrity-monitoring.yaml index 982979f7..25372415 100644 --- a/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-monitoring-deny-write-under-bin-directory.yaml +++ b/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-file-integrity-monitoring.yaml @@ -1,32 +1,39 @@ apiVersion: security.kubearmor.com/v1 kind: KubeArmorPolicy metadata: - name: wordpress-wordpress-4-8-apache-system-monitoring-deny-write-under-bin-directory + name: wordpress-wordpress-4-8-apache-file-integrity-monitoring namespace: wordpress-mysql spec: action: Block file: matchDirectories: - - dir: /bin/ + - dir: /sbin/ readOnly: true recursive: true - - dir: /sbin/ + - dir: /usr/bin/ + readOnly: true + recursive: true + - dir: /usr/lib/ readOnly: true recursive: true - dir: /usr/sbin/ readOnly: true recursive: true - - dir: /usr/bin/ + - dir: /bin/ + readOnly: true + recursive: true + - dir: /boot/ readOnly: true recursive: true - message: Alert! An attempt to write below system binary directories denied. + message: Detected and prevented compromise to File integrity selector: matchLabels: app: wordpress - severity: 5 + severity: 1 tags: - NIST - NIST_800-53_AU-2 - NIST_800-53_SI-4 - MITRE - MITRE_T1036_masquerading + - MITRE_T1565_data_manipulation diff --git a/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-least-functionality-execute-package-management-process-in-container.yaml b/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-least-functionality-execute-package-management-process-in-container.yaml index 532e8a34..4a07bb2d 100644 --- a/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-least-functionality-execute-package-management-process-in-container.yaml +++ b/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-least-functionality-execute-package-management-process-in-container.yaml @@ -12,35 +12,17 @@ spec: severity: 5 process: matchPaths: - - path: /usr/bin/apt - - path: /usr/bin/apt-get - - path: /bin/apt-get - - path: /sbin/apk - - path: /bin/apt - - path: /usr/bin/dpkg - - path: /bin/dpkg - - path: /usr/bin/gdebi - - path: /bin/gdebi - - path: /usr/bin/make - - path: /bin/make - - path: /usr/bin/yum - - path: /bin/yum - - path: /usr/bin/rpm - - path: /bin/rpm - - path: /usr/bin/dnf - - path: /bin/dnf - - path: /usr/bin/pacman - - path: /usr/sbin/pacman - - path: /bin/pacman - - path: /sbin/pacman - - path: /usr/bin/makepkg - - path: /usr/sbin/makepkg - - path: /bin/makepkg - - path: /sbin/makepkg - - path: /usr/bin/yaourt - - path: /usr/sbin/yaourt - - path: /bin/yaourt - - path: /sbin/yaourt - - path: /usr/bin/zypper - - path: /bin/zypper + - execname: apt + - execname: apt-get + - execname: apk + - execname: dpkg + - execname: gdebi + - execname: make + - execname: yum + - execname: rpm + - execname: dnf + - execname: pacman + - execname: makepkg + - execname: yaourt + - execname: zypper action: Block \ No newline at end of file diff --git a/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-owner-discovery.yaml b/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-owner-discovery.yaml index 24df8a56..f508ef5a 100644 --- a/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-owner-discovery.yaml +++ b/tests/recommend/res/out/wordpress-mysql-wordpress/wordpress-4-8-apache-system-owner-discovery.yaml @@ -8,10 +8,10 @@ spec: message: System owner discovery command execution denied process: matchPaths: - - path: /usr/bin/who - - path: /usr/bin/w - - path: /usr/bin/id - - path: /usr/bin/whoami + - execname: who + - execname: w + - execname: id + - execname: whoami selector: matchLabels: app: wordpress