feat(aa-log): improve logs cleaning and varible resolution.

This commit is contained in:
Alexandre Pujol 2024-09-26 22:25:24 +01:00
parent 83bc7d3ade
commit 00d6a664eb
No known key found for this signature in database
GPG Key ID: C5469996F0DF68EC
5 changed files with 44 additions and 35 deletions

View File

@ -17,7 +17,7 @@ import (
const usage = `aa-log [-h] [--systemd] [--file file] [--rules | --raw] [profile] const usage = `aa-log [-h] [--systemd] [--file file] [--rules | --raw] [profile]
Review AppArmor generated messages in a colorful way. Supports logs from Review AppArmor generated messages in a colorful way. It supports logs from
auditd, systemd, syslog as well as dbus session events. auditd, systemd, syslog as well as dbus session events.
It can be given an optional profile name to filter the output with. It can be given an optional profile name to filter the output with.
@ -64,7 +64,7 @@ func aaLog(logger string, path string, profile string) error {
return nil return nil
} }
aaLogs := logs.NewApparmorLogs(file, profile) aaLogs := logs.New(file, profile)
if rules { if rules {
profiles := aaLogs.ParseToProfiles() profiles := aaLogs.ParseToProfiles()
for _, p := range profiles { for _, p := range profiles {

View File

@ -76,10 +76,10 @@ func getIndentationLevel(input string) int {
return level return level
} }
func parse(kind kind, profile string) ([]aa.Rules, []string, error) { func parse(kind kind, profile string) (aa.ParaRules, []string, error) {
var raw string var raw string
paragraphs := []string{} paragraphs := []string{}
rulesByParagraph := []aa.Rules{} rulesByParagraph := aa.ParaRules{}
switch kind { switch kind {
case isTunable, isProfile: case isTunable, isProfile:
@ -110,9 +110,6 @@ func formatFile(kind kind, profile string) (string, error) {
return "", err return "", err
} }
for idx, rules := range rulesByParagraph { for idx, rules := range rulesByParagraph {
if err := rules.Validate(); err != nil {
return "", err
}
aa.IndentationLevel = getIndentationLevel(paragraphs[idx]) aa.IndentationLevel = getIndentationLevel(paragraphs[idx])
rules = rules.Merge().Sort().Format() rules = rules.Merge().Sort().Format()
profile = strings.Replace(profile, paragraphs[idx], rules.String()+"\n", -1) profile = strings.Replace(profile, paragraphs[idx], rules.String()+"\n", -1)
@ -202,8 +199,12 @@ func main() {
logging.Fatal("%s", err.Error()) logging.Fatal("%s", err.Error())
} }
err = aaFormat(files) err = aaFormat(files)
case tree: case tree:
err = aaTree() err = aaTree()
default:
flag.Usage()
} }
if err != nil { if err != nil {

View File

@ -30,7 +30,7 @@ func TestGetJournalctlLogs(t *testing.T) {
"apparmor": "ALLOWED", "apparmor": "ALLOWED",
"label": "gsd-xsettings", "label": "gsd-xsettings",
"operation": "dbus_method_call", "operation": "dbus_method_call",
"name": ":*", "name": "@{busname}",
"mask": "receive", "mask": "receive",
"bus": "session", "bus": "session",
"path": "/org/gtk/Settings", "path": "/org/gtk/Settings",
@ -50,8 +50,8 @@ func TestGetJournalctlLogs(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.useFile) reader, _ := GetJournalctlLogs(tt.path, tt.useFile)
if got := NewApparmorLogs(reader, tt.name); !reflect.DeepEqual(got, tt.want) { if got := New(reader, tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewApparmorLogs() = %v, want %v", got, tt.want) t.Errorf("New() = %v, want %v", got, tt.want)
} }
}) })
} }

View File

@ -28,11 +28,14 @@ const (
boldYellow = "\033[1;33m" boldYellow = "\033[1;33m"
) )
const (
h = `[0-9a-fA-F]`
d = `[0-9]`
)
var ( var (
quoted bool quoted bool
isAppArmorLogTemplate = regexp.MustCompile(`apparmor=("DENIED"|"ALLOWED"|"AUDIT")`) isAppArmorLogTemplate = regexp.MustCompile(`apparmor=("DENIED"|"ALLOWED"|"AUDIT")`)
_hex = `[0-9a-fA-F]`
_int = `[0-9]`
regCleanLogs = util.ToRegexRepl([]string{ regCleanLogs = util.ToRegexRepl([]string{
// Clean apparmor log file // Clean apparmor log file
`.*apparmor="`, `apparmor="`, `.*apparmor="`, `apparmor="`,
@ -61,40 +64,45 @@ var (
`/home/[^/]+/`, `@{HOME}/`, `/home/[^/]+/`, `@{HOME}/`,
// Resolve system variables // Resolve system variables
`/usr/(lib|lib32|lib64|libexec)`, `@{lib}`, `/usr/lib(32|64|exec)`, `@{lib}`,
`/usr/lib`, `@{lib}`,
`/usr/(bin|sbin)`, `@{bin}`, `/usr/(bin|sbin)`, `@{bin}`,
`x86_64-pc-linux-gnu[^/]?`, `@{multiarch}`, `(x86_64|amd64|i386|i686)`, `@{arch}`,
`@{arch}-*linux-gnu[^/]?`, `@{multiarch}`,
`/usr/etc/`, `@{etc_ro}/`, `/usr/etc/`, `@{etc_ro}/`,
`/var/run/`, `@{run}/`, `/var/run/`, `@{run}/`,
`/run/`, `@{run}/`, `/run/`, `@{run}/`,
`user/[0-9]*/`, `user/@{uid}/`, `user/[0-9]*/`, `user/@{uid}/`,
`/tmp/user/@{uid}/`, `@{tmp}/`, `/tmp/user/@{uid}/`, `@{tmp}/`,
`/proc/`, `@{PROC}/`, `/proc/`, `@{PROC}/`,
`@{PROC}/1/`, `@{PROC}/one/`, // Go does not support lookahead assertions like (?!1\b)d+, so we have to use a workaround
`@{PROC}/[0-9]*/`, `@{PROC}/@{pid}/`, `@{PROC}/[0-9]*/`, `@{PROC}/@{pid}/`,
`@{PROC}/one/`, `@{PROC}/1/`,
`@{PROC}/@{pid}/task/[0-9]*/`, `@{PROC}/@{pid}/task/@{tid}/`, `@{PROC}/@{pid}/task/[0-9]*/`, `@{PROC}/@{pid}/task/@{tid}/`,
`/sys/`, `@{sys}/`, `/sys/`, `@{sys}/`,
`@{PROC}@{sys}/`, `@{PROC}/sys/`, `@{PROC}@{sys}/`, `@{PROC}/sys/`,
`pci` + strings.Repeat(_hex, 4) + `:` + strings.Repeat(_hex, 2), `@{pci_bus}`, `pci` + strings.Repeat(h, 4) + `:` + strings.Repeat(h, 2), `@{pci_bus}`,
`@{pci_bus}/[0-9a-f:*./]*`, `@{pci}/`, `@{pci_bus}/[0-9a-f:*./]*`, `@{pci}/`,
`1000`, `@{uid}`, `1000`, `@{uid}`,
// Some system glob // Some system glob
`:1.[0-9]*`, `:*`, // dbus peer name `:not.active.yet`, `@{busname}`, // dbus unique bus name
`:1.[0-9]*`, `@{busname}`, // dbus unique bus name
`@{bin}/(|ba|da)sh`, `@{sh_path}`, // collect all shell `@{bin}/(|ba|da)sh`, `@{sh_path}`, // collect all shell
`@{lib}/modules/[^/]+\/`, `@{lib}/modules/*/`, // strip kernel version numbers from kernel module accesses `@{lib}/modules/[^/]+\/`, `@{lib}/modules/*/`, // strip kernel version numbers from kernel module accesses
// int, hex, uuid // int, hex, uuid
strings.Repeat(_hex, 8) + `[-_]` + strings.Repeat(_hex, 4) + `[-_]` + strings.Repeat(_hex, 4) + `[-_]` + strings.Repeat(_hex, 4) + `[-_]` + strings.Repeat(_hex, 12), `@{uuid}`, strings.Repeat(h, 8) + `[-_]` + strings.Repeat(h, 4) + `[-_]` + strings.Repeat(h, 4) + `[-_]` + strings.Repeat(h, 4) + `[-_]` + strings.Repeat(h, 12), `@{uuid}`,
strings.Repeat(_int, 64), `@{int64}`, strings.Repeat(d, 64), `@{int64}`,
strings.Repeat(_hex, 64), `@{hex64}`, strings.Repeat(h, 64), `@{hex64}`,
strings.Repeat(_hex, 38), `@{hex38}`, strings.Repeat(h, 38), `@{hex38}`,
strings.Repeat(_int, 32), `@{int32}`, strings.Repeat(d, 32), `@{int32}`,
strings.Repeat(_hex, 32), `@{hex32}`, strings.Repeat(h, 32), `@{hex32}`,
strings.Repeat(_int, 16), `@{int16}`, strings.Repeat(d, 16), `@{int16}`,
strings.Repeat(_hex, 16), `@{hex16}`, strings.Repeat(h, 16), `@{hex16}`,
strings.Repeat(_int, 10), `@{int10}`, strings.Repeat(d, 10), `@{int10}`,
strings.Repeat(_int, 8), `@{int8}`, strings.Repeat(d, 8), `@{int8}`,
strings.Repeat(_int, 6), `@{int6}`, strings.Repeat(d, 6), `@{int6}`,
}) })
) )
@ -117,8 +125,8 @@ func toQuote(str string) string {
return str return str
} }
// NewApparmorLogs return a new ApparmorLogs list of map from a log file // New returns a new ApparmorLogs list of map from a log file
func NewApparmorLogs(file io.Reader, profile string) AppArmorLogs { func New(file io.Reader, profile string) AppArmorLogs {
logs := GetApparmorLogs(file, profile) logs := GetApparmorLogs(file, profile)
// Parse log into ApparmorLog struct // Parse log into ApparmorLog struct

View File

@ -174,14 +174,14 @@ func TestAppArmorEvents(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) {
file := strings.NewReader(tt.event) file := strings.NewReader(tt.event)
if got := NewApparmorLogs(file, ""); !reflect.DeepEqual(got, tt.want) { if got := New(file, ""); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewApparmorLogs() = %v, want %v", got, tt.want) t.Errorf("New() = %v, want %v", got, tt.want)
} }
}) })
} }
} }
func TestNewApparmorLogs(t *testing.T) { func TestNew(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
path string path string
@ -208,7 +208,7 @@ func TestNewApparmorLogs(t *testing.T) {
"apparmor": "DENIED", "apparmor": "DENIED",
"profile": "dnsmasq", "profile": "dnsmasq",
"operation": "open", "operation": "open",
"name": "@{PROC}/@{pid}/environ", "name": "@{PROC}/1/environ",
"comm": "dnsmasq", "comm": "dnsmasq",
"requested_mask": "r", "requested_mask": "r",
"denied_mask": "r", "denied_mask": "r",
@ -251,8 +251,8 @@ func TestNewApparmorLogs(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) {
file, _ := os.Open(tt.path) file, _ := os.Open(tt.path)
if got := NewApparmorLogs(file, tt.name); !reflect.DeepEqual(got, tt.want) { if got := New(file, tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewApparmorLogs() = %v, want %v", got, tt.want) t.Errorf("New() = %v, want %v", got, tt.want)
} }
}) })
} }