feat(aa-log): better integration with journalctl & new usage page.

This commit is contained in:
Alexandre Pujol 2023-02-19 17:53:49 +00:00
parent d66a8fa082
commit 1316e0ddde
No known key found for this signature in database
GPG key ID: C5469996F0DF68EC
2 changed files with 35 additions and 24 deletions

View file

@ -1,5 +1,5 @@
// aa-log - Review AppArmor generated messages // aa-log - Review AppArmor generated messages
// Copyright (C) 2021-2022 Alexandre Pujol <alexandre@pujol.io> // Copyright (C) 2021-2023 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
package main package main
@ -20,6 +20,23 @@ import (
"strings" "strings"
) )
const usage = `aa-log [-h] [--systemd] [--dbus] [--file file] [profile]
Review AppArmor generated messages in a colorful way. Supports logs from
auditd, systemd, syslog as well as dbus session events.
It can be given an optional profile name to filter the output with.
Default logs are read from '/var/log/audit/audit.log'. Other files in
'/var/log/audit/' can easily be checked: 'aa-log -f 1' parses 'audit.log.1'
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.
`
// Command line options // Command line options
var ( var (
help bool help bool
@ -104,23 +121,21 @@ func getAuditLogs(path string) (io.Reader, error) {
} }
// getJournalctlLogs return a reader with the logs entries from Systemd // getJournalctlLogs return a reader with the logs entries from Systemd
func getJournalctlLogs(path string, user bool, useFile bool) (io.Reader, error) { func getJournalctlLogs(path string, useFile bool) (io.Reader, error) {
var logs []SystemdLog var logs []SystemdLog
var stdout bytes.Buffer var stdout bytes.Buffer
var value string var value string
if useFile { if useFile {
// content, err := os.ReadFile(filepath.Clean(path))
content, err := ioutil.ReadFile(filepath.Clean(path)) content, err := ioutil.ReadFile(filepath.Clean(path))
if err != nil { if err != nil {
return nil, err return nil, err
} }
value = string(content) value = string(content)
} else { } else {
mode := "--system" // journalctl -b -o json > systemd.log
if user { cmd := exec.Command("journalctl", "--boot", "--output=json")
mode = "--user"
}
cmd := exec.Command("journalctl", mode, "--boot", "--unit=dbus.service", "--output=json")
cmd.Stdout = &stdout cmd.Stdout = &stdout
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return nil, err return nil, err
@ -131,6 +146,7 @@ func getJournalctlLogs(path string, user bool, useFile bool) (io.Reader, error)
value = strings.Replace(value, "\n", ",\n", -1) value = strings.Replace(value, "\n", ",\n", -1)
value = strings.TrimSuffix(value, ",\n") value = strings.TrimSuffix(value, ",\n")
value = `[` + value + `]` value = `[` + value + `]`
// fmt.Printf("value: %v\n", value)
if err := json.Unmarshal([]byte(value), &logs); err != nil { if err := json.Unmarshal([]byte(value), &logs); err != nil {
return nil, err return nil, err
} }
@ -189,7 +205,7 @@ func NewApparmorLogs(file io.Reader, profile string) AppArmorLogs {
} }
} }
aa["profile"] = decodeHex(aa["profile"]) aa["profile"] = decodeHex(aa["profile"])
toDecode := []string{"profile", "name", "comm"} toDecode := []string{"name", "comm"}
for _, name := range toDecode { for _, name := range toDecode {
if value, ok := aa[name]; ok { if value, ok := aa[name]; ok {
aa[name] = decodeHex(value) aa[name] = decodeHex(value)
@ -267,7 +283,7 @@ func aaLog(logger string, path string, profile string) error {
case "auditd": case "auditd":
file, err = getAuditLogs(path) file, err = getAuditLogs(path)
case "systemd": case "systemd":
file, err = getJournalctlLogs(path, true, path != LogFile) file, err = getJournalctlLogs(path, path != LogFile)
default: default:
err = fmt.Errorf("Logger %s not supported.", logger) err = fmt.Errorf("Logger %s not supported.", logger)
} }
@ -281,21 +297,18 @@ func aaLog(logger string, path string, profile string) error {
func init() { func init() {
flag.BoolVar(&help, "h", false, "Show this help message and exit.") flag.BoolVar(&help, "h", false, "Show this help message and exit.")
flag.StringVar(&path, "f", LogFile, flag.BoolVar(&help, "help", false, "Show this help message and exit.")
"Set a log`file` or a suffix to the default log file.") flag.StringVar(&path, "f", LogFile, "Set a logfile or a suffix to the default log file.")
flag.BoolVar(&systemd, "s", false, "Parse systemd dbus logs.") flag.StringVar(&path, "file", LogFile, "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.")
} }
func main() { func main() {
flag.Usage = func() { fmt.Print(usage) }
flag.Parse() flag.Parse()
if help { if help {
fmt.Printf(`aa-log [-h] [-s] [-f file] [profile] flag.Usage()
Review AppArmor generated messages in a colorful way.
It can be given an optional profile name to filter the output with.
`)
flag.PrintDefaults()
os.Exit(0) os.Exit(0)
} }

View file

@ -94,7 +94,7 @@ func TestAppArmorEvents(t *testing.T) {
}, },
}, },
{ {
name: "dbus system", name: "dbus_system",
event: `type=USER_AVC msg=audit(1111111111.111:1111): pid=1780 uid=102 auid=4294967295 ses=4294967295 subj=? msg='apparmor="ALLOWED" operation="dbus_method_call" bus="system" path="/org/freedesktop/PolicyKit1/Authority" interface="org.freedesktop.PolicyKit1.Authority" member="CheckAuthorization" mask="send" name="org.freedesktop.PolicyKit1" pid=1794 label="snapd" peer_pid=1790 peer_label="polkitd" exe="/usr/bin/dbus-daemon" sauid=102 hostname=? addr=? terminal=?'UID="messagebus" AUID="unset" SAUID="messagebus"`, event: `type=USER_AVC msg=audit(1111111111.111:1111): pid=1780 uid=102 auid=4294967295 ses=4294967295 subj=? msg='apparmor="ALLOWED" operation="dbus_method_call" bus="system" path="/org/freedesktop/PolicyKit1/Authority" interface="org.freedesktop.PolicyKit1.Authority" member="CheckAuthorization" mask="send" name="org.freedesktop.PolicyKit1" pid=1794 label="snapd" peer_pid=1790 peer_label="polkitd" exe="/usr/bin/dbus-daemon" sauid=102 hostname=? addr=? terminal=?'UID="messagebus" AUID="unset" SAUID="messagebus"`,
want: AppArmorLogs{ want: AppArmorLogs{
{ {
@ -113,7 +113,7 @@ func TestAppArmorEvents(t *testing.T) {
}, },
}, },
{ {
name: "dbus session", name: "dbus_session",
event: `apparmor="ALLOWED" operation="dbus_bind" bus="session" name="org.freedesktop.portal.Documents" mask="bind" pid=2174 label="xdg-document-portal"`, event: `apparmor="ALLOWED" operation="dbus_bind" bus="session" name="org.freedesktop.portal.Documents" mask="bind" pid=2174 label="xdg-document-portal"`,
want: AppArmorLogs{ want: AppArmorLogs{
{ {
@ -221,13 +221,11 @@ func Test_getJournalctlLogs(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
path string path string
user bool
useFile bool useFile bool
want AppArmorLogs want AppArmorLogs
}{ }{
{ {
name: "gsd-xsettings", name: "gsd-xsettings",
user: true,
useFile: true, useFile: true,
path: "../../tests/systemd.log", path: "../../tests/systemd.log",
want: AppArmorLogs{ want: AppArmorLogs{
@ -255,7 +253,7 @@ func Test_getJournalctlLogs(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
reader, _ := getJournalctlLogs(tt.path, tt.user, tt.useFile) reader, _ := getJournalctlLogs(tt.path, tt.useFile)
if got := NewApparmorLogs(reader, tt.name); !reflect.DeepEqual(got, tt.want) { if got := NewApparmorLogs(reader, tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewApparmorLogs() = %v, want %v", got, tt.want) t.Errorf("NewApparmorLogs() = %v, want %v", got, tt.want)
} }