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
// 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
package main
@ -20,6 +20,23 @@ import (
"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
var (
help bool
@ -104,23 +121,21 @@ func getAuditLogs(path string) (io.Reader, error) {
}
// 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 stdout bytes.Buffer
var value string
if useFile {
// content, err := os.ReadFile(filepath.Clean(path))
content, err := ioutil.ReadFile(filepath.Clean(path))
if err != nil {
return nil, err
}
value = string(content)
} else {
mode := "--system"
if user {
mode = "--user"
}
cmd := exec.Command("journalctl", mode, "--boot", "--unit=dbus.service", "--output=json")
// journalctl -b -o json > systemd.log
cmd := exec.Command("journalctl", "--boot", "--output=json")
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
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.TrimSuffix(value, ",\n")
value = `[` + value + `]`
// fmt.Printf("value: %v\n", value)
if err := json.Unmarshal([]byte(value), &logs); err != nil {
return nil, err
}
@ -189,7 +205,7 @@ func NewApparmorLogs(file io.Reader, profile string) AppArmorLogs {
}
}
aa["profile"] = decodeHex(aa["profile"])
toDecode := []string{"profile", "name", "comm"}
toDecode := []string{"name", "comm"}
for _, name := range toDecode {
if value, ok := aa[name]; ok {
aa[name] = decodeHex(value)
@ -267,7 +283,7 @@ func aaLog(logger string, path string, profile string) error {
case "auditd":
file, err = getAuditLogs(path)
case "systemd":
file, err = getJournalctlLogs(path, true, path != LogFile)
file, err = getJournalctlLogs(path, path != LogFile)
default:
err = fmt.Errorf("Logger %s not supported.", logger)
}
@ -281,21 +297,18 @@ func aaLog(logger string, path string, profile string) error {
func init() {
flag.BoolVar(&help, "h", false, "Show this help message and exit.")
flag.StringVar(&path, "f", LogFile,
"Set a log`file` or a suffix to the default log file.")
flag.BoolVar(&systemd, "s", false, "Parse systemd dbus logs.")
flag.BoolVar(&help, "help", false, "Show this help message and exit.")
flag.StringVar(&path, "f", LogFile, "Set a logfile or a suffix to the default log file.")
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() {
flag.Usage = func() { fmt.Print(usage) }
flag.Parse()
if help {
fmt.Printf(`aa-log [-h] [-s] [-f file] [profile]
Review AppArmor generated messages in a colorful way.
It can be given an optional profile name to filter the output with.
`)
flag.PrintDefaults()
flag.Usage()
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"`,
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"`,
want: AppArmorLogs{
{
@ -221,13 +221,11 @@ func Test_getJournalctlLogs(t *testing.T) {
tests := []struct {
name string
path string
user bool
useFile bool
want AppArmorLogs
}{
{
name: "gsd-xsettings",
user: true,
useFile: true,
path: "../../tests/systemd.log",
want: AppArmorLogs{
@ -255,7 +253,7 @@ func Test_getJournalctlLogs(t *testing.T) {
}
for _, tt := range tests {
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) {
t.Errorf("NewApparmorLogs() = %v, want %v", got, tt.want)
}