From e0f79b9c9a266f2cbb27c385a7590d771b7b3ed2 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 17 Aug 2023 19:12:02 +0100 Subject: [PATCH] feat(aa-log): resolve all main apparmor vars in log. This also deprecate the anonymize option --- cmd/aa-log/main.go | 17 +++----- pkg/logs/loggers_test.go | 2 +- pkg/logs/logs.go | 70 ++++++++++++++++++------------- pkg/logs/logs_test.go | 91 +++++----------------------------------- 4 files changed, 57 insertions(+), 123 deletions(-) diff --git a/cmd/aa-log/main.go b/cmd/aa-log/main.go index cd6ae778..c4a13d3d 100644 --- a/cmd/aa-log/main.go +++ b/cmd/aa-log/main.go @@ -28,19 +28,17 @@ Options: -h, --help Show this help message and exit. -f, --file FILE Set a logfile or a suffix to the default log file. -s, --systemd Parse systemd logs from journalctl. - -a, --anonymize Anonymize the logs. ` // Command line options var ( - help bool - anonymize bool - path string - systemd bool + help bool + path string + systemd bool ) -func aaLog(logger string, path string, profile string, anonymize bool) error { +func aaLog(logger string, path string, profile string) error { var err error var file io.Reader @@ -56,9 +54,6 @@ func aaLog(logger string, path string, profile string, anonymize bool) error { return err } aaLogs := logs.NewApparmorLogs(file, profile) - if anonymize { - aaLogs.Anonymize() - } fmt.Print(aaLogs.String()) return nil } @@ -70,8 +65,6 @@ func init() { flag.StringVar(&path, "file", "", "Set a logfile or a suffix to the default log file.") flag.BoolVar(&systemd, "s", false, "Parse systemd logs from journalctl.") flag.BoolVar(&systemd, "systemd", false, "Parse systemd logs from journalctl.") - flag.BoolVar(&anonymize, "a", false, "Anonymize the logs.") - flag.BoolVar(&anonymize, "anonymize", false, "Anonymize the logs.") } func main() { @@ -93,7 +86,7 @@ func main() { } logfile := logs.GetLogFile(path) - err := aaLog(logger, logfile, profile, anonymize) + err := aaLog(logger, logfile, profile) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/pkg/logs/loggers_test.go b/pkg/logs/loggers_test.go index 9d58a50c..85a308d4 100644 --- a/pkg/logs/loggers_test.go +++ b/pkg/logs/loggers_test.go @@ -26,7 +26,7 @@ func TestGetJournalctlLogs(t *testing.T) { "profile": "", "label": "gsd-xsettings", "operation": "dbus_method_call", - "name": ":1.88", + "name": ":*", "mask": "receive", "bus": "session", "path": "/org/gtk/Settings", diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index 95b6552f..79bf50ee 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -8,7 +8,6 @@ import ( "bufio" "fmt" "io" - "os/user" "regexp" "strings" @@ -30,16 +29,51 @@ const ( boldYellow = "\033[1;33m" ) -// Anonymized username -const Username = "AAD" - var ( quoted bool isAppArmorLogTemplate = regexp.MustCompile(`apparmor=("DENIED"|"ALLOWED"|"AUDIT")`) - regAALogs = util.ToRegexRepl([]string{ + regCleanLogs = util.ToRegexRepl([]string{ + // Clean apparmor log file `.*apparmor="`, `apparmor="`, `(peer_|)pid=[0-9]*\s`, " ", `\x1d`, " ", + + // Resolve classic user variables + `/home/[^/]+/.cache`, `@{user_cache_dirs}`, + `/home/[^/]+/.config`, `@{user_config_dirs}`, + `/home/[^/]+/.local/share`, `@{user_share_dirs}`, + `/home/[^/]+/.local/state`, `@{user_state_dirs}`, + `/home/[^/]+/.local/bin`, `@{user_bin_dirs}`, + `/home/[^/]+/.local/lib`, `@{user_lib_dirs}`, + `/home/[^/]+/.ssh`, `@{HOME}/@{XDG_SSH_DIR}`, + `/home/[^/]+/.gnupg`, `@{HOME}/@{XDG_GPG_DIR}`, + `/home/[^/]+`, `@{HOME}`, + + // Resolve classic system variables + `/usr/lib(|32|64|exec)`, `@{lib}`, + `/usr/(|s)bin`, `@{bin}`, + `/run/`, `@{run}/`, + `user/[0-9]*/`, `user/@{uid}/`, + `/proc/`, `@{PROC}/`, + `@{PROC}/[0-9]*/`, `@{PROC}/@{pid}/`, + `@{PROC}/@{pid}/task/[0-9]*/`, `@{PROC}/@{pid}/task/@{tid}/`, + `/sys/`, `@{sys}/`, + `@{PROC}@{sys}/`, `@{PROC}/sys/`, + + // Some system glob + `pci[/0-9:.]+`, `pci[0-9]*/**/`, // PCI structure + `:1.[0-9]*`, `:*`, // dbus peer name + `@{bin}/(|ba|da)sh`, `@{bin}/{,ba,da}sh`, // collect all shell + `@{lib}/modules/[^/]+\/`, `@{lib}/modules/*/`, // strip kernel version numbers from kernel module accesses + + // Remove basic rules from abstractions/base + `(?m)^.*/etc/[^/]+so.*$`, ``, + `(?m)^.*@{lib}/[^/]+so.*$`, ``, + `(?m)^.*@{lib}/locale/.*$`, ``, + `(?m)^.*/usr/share/(locale|zoneinfo)/.*$`, ``, + `(?m)^.*/usr/share/zoneinfo/.*$`, ``, + `(?m)^.*/dev/(null|zero|full).*$`, ``, + `(?m)^.*/dev/(u|)random.*$`, ``, }) ) @@ -81,12 +115,10 @@ func NewApparmorLogs(file io.Reader, profile string) AppArmorLogs { } } - // Clean logs - for _, aa := range regAALogs { + // Clean & remove doublon in logs + for _, aa := range regCleanLogs { log = aa.Regex.ReplaceAllLiteralString(log, aa.Repl) } - - // Remove doublon in logs logs := strings.Split(log, "\n") logs = util.RemoveDuplicate(logs) @@ -117,26 +149,6 @@ func NewApparmorLogs(file io.Reader, profile string) AppArmorLogs { return aaLogs } -// Anonymize the logs before reporting -func (aaLogs AppArmorLogs) Anonymize() { - user, _ := user.Current() - keys := []string{"name", "comm"} - regAnonymizeLogs := util.ToRegexRepl([]string{ - user.Username, Username, - `/home/[^/]+`, `/home/` + Username, - `[0-9a-fA-F]*-[0-9a-fA-F]*-[0-9a-fA-F]*-[0-9a-fA-F]*-[0-9a-fA-F]*`, `b08dfa60-83e7-567a-1921-a715000001fb`, - }) - for _, log := range aaLogs { - for _, key := range keys { - if _, ok := log[key]; ok { - for _, aa := range regAnonymizeLogs { - log[key] = aa.Regex.ReplaceAllLiteralString(log[key], aa.Repl) - } - } - } - } -} - // String returns a formatted AppArmor logs string func (aaLogs AppArmorLogs) String() string { // Apparmor log states diff --git a/pkg/logs/logs_test.go b/pkg/logs/logs_test.go index a67e7bb2..b19934c7 100644 --- a/pkg/logs/logs_test.go +++ b/pkg/logs/logs_test.go @@ -29,7 +29,7 @@ var ( "apparmor": "ALLOWED", "profile": "man", "operation": "exec", - "name": "/usr/bin/preconv", + "name": "@{bin}/preconv", "target": "man_groff", "info": "no new privs", "comm": "man", @@ -55,7 +55,7 @@ var ( "interface": "org.freedesktop.DBus", "member": "AddMatch", "peer_label": "dbus-daemon", - "exe": "/usr/bin/dbus-daemon", + "exe": "@{bin}/dbus-daemon", "sauid": "102", "hostname": "?", "addr": "?", @@ -79,7 +79,7 @@ func TestAppArmorEvents(t *testing.T) { want: AppArmorLogs{ { "apparmor": "ALLOWED", - "profile": "/usr/sbin/httpd2-prefork//vhost_foo", + "profile": "@{bin}/httpd2-prefork//vhost_foo", "operation": "rename_dest", "name": "/home/www/foo.bar.in/httpdocs/apparmor/images/test/image 1.jpg", "comm": "httpd2-prefork", @@ -99,7 +99,7 @@ func TestAppArmorEvents(t *testing.T) { "apparmor": "ALLOWED", "profile": "foo bar", "operation": "file_perm", - "name": "/home/foo/.bash_history", + "name": "@{HOME}/.bash_history", "comm": "bash", "requested_mask": "rw", "denied_mask": "rw", @@ -117,7 +117,7 @@ func TestAppArmorEvents(t *testing.T) { "apparmor": "ALLOWED", "profile": "/sbin/klogd", "operation": "file_mmap", - "name": "var/run/nscd/passwd", + "name": "var@{run}/nscd/passwd", "comm": "id", "info": "Failed name lookup - disconnected path", "requested_mask": "r", @@ -144,7 +144,7 @@ func TestAppArmorEvents(t *testing.T) { "interface": "org.freedesktop.PolicyKit1.Authority", "member": "CheckAuthorization", "peer_label": "polkitd", - "exe": "/usr/bin/dbus-daemon", + "exe": "@{bin}/dbus-daemon", "sauid": "102", "hostname": "?", "addr": "?", @@ -195,7 +195,7 @@ func TestNewApparmorLogs(t *testing.T) { "apparmor": "DENIED", "profile": "dnsmasq", "operation": "open", - "name": "/proc/sys/kernel/osrelease", + "name": "@{PROC}/sys/kernel/osrelease", "comm": "dnsmasq", "requested_mask": "r", "denied_mask": "r", @@ -208,7 +208,7 @@ func TestNewApparmorLogs(t *testing.T) { "apparmor": "DENIED", "profile": "dnsmasq", "operation": "open", - "name": "/proc/1/environ", + "name": "@{PROC}/@{pid}/environ", "comm": "dnsmasq", "requested_mask": "r", "denied_mask": "r", @@ -221,7 +221,7 @@ func TestNewApparmorLogs(t *testing.T) { "apparmor": "DENIED", "profile": "dnsmasq", "operation": "open", - "name": "/proc/cmdline", + "name": "@{PROC}/cmdline", "comm": "dnsmasq", "requested_mask": "r", "denied_mask": "r", @@ -272,7 +272,7 @@ func TestAppArmorLogs_String(t *testing.T) { { name: "man", aaLogs: refMan, - want: "\033[1;32mALLOWED\033[0m \033[34mman\033[0m \033[33mexec\033[0m \033[35m/usr/bin/preconv\033[0m -> \033[35mman_groff\033[0m info=\"no new privs\" comm=man requested_mask=\033[1;31mx\033[0m denied_mask=\033[1;31mx\033[0m error=-1\n", + want: "\033[1;32mALLOWED\033[0m \033[34mman\033[0m \033[33mexec\033[0m \033[35m@{bin}/preconv\033[0m -> \033[35mman_groff\033[0m info=\"no new privs\" comm=man requested_mask=\033[1;31mx\033[0m denied_mask=\033[1;31mx\033[0m error=-1\n", }, { name: "power-profiles-daemon", @@ -288,74 +288,3 @@ func TestAppArmorLogs_String(t *testing.T) { }) } } - -func TestAppArmorLogs_Anonymize(t *testing.T) { - tests := []struct { - name string - aaLogs AppArmorLogs - want AppArmorLogs - }{ - { - name: "Anonymize Username", - aaLogs: AppArmorLogs{ - { - "apparmor": "ALLOWED", - "profile": "foo", - "operation": "file_perm", - "name": "/home/foo/.bash_history", - "comm": "bash", - "requested_mask": "rw", - "denied_mask": "rw", - "parent": "16001", - }, - }, - want: AppArmorLogs{ - { - "apparmor": "ALLOWED", - "profile": "foo", - "operation": "file_perm", - "name": "/home/AAD/.bash_history", - "comm": "bash", - "requested_mask": "rw", - "denied_mask": "rw", - "parent": "16001", - }, - }, - }, - { - name: "Anonymize UUID", - aaLogs: AppArmorLogs{ - { - "apparmor": "ALLOWED", - "profile": "drkonqi", - "operation": "file_perm", - "name": "/sys/devices/pci0000:00/0000:00:02.0/drm/card1/metrics/399d3001-97d6-4240-b065-4fb843138e17/id", - "comm": "bash", - "requested_mask": "r", - "denied_mask": "r", - "parent": "16001", - }, - }, - want: AppArmorLogs{ - { - "apparmor": "ALLOWED", - "profile": "drkonqi", - "operation": "file_perm", - "name": "/sys/devices/pci0000:00/0000:00:02.0/drm/card1/metrics/b08dfa60-83e7-567a-1921-a715000001fb/id", - "comm": "bash", - "requested_mask": "r", - "denied_mask": "r", - "parent": "16001", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.aaLogs.Anonymize() - if !reflect.DeepEqual(tt.aaLogs, tt.want) { - t.Errorf("Anonymize() = %v, want %v", tt.aaLogs, tt.want) - } - }) - } -}