apparmor.d/src/cmd/aa-log/main.go

158 lines
3.5 KiB
Go
Raw Normal View History

2021-11-09 23:41:12 +01:00
// aa-log - Review AppArmor generated messages
// Copyright (C) 2021 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
package main
import (
"bufio"
"fmt"
"os"
"regexp"
"strings"
)
// LogFile is the path to the file to query
const LogFile = "/var/log/audit/audit.log"
// Colors
const (
Reset = "\033[0m"
FgYellow = "\033[33m"
FgBlue = "\033[34m"
FgMagenta = "\033[35m"
BoldRed = "\033[1;31m"
BoldGreen = "\033[1;32m"
)
// AppArmorLog describes a apparmor log entry
type AppArmorLog map[string]string
// AppArmorLogs describes all apparmor log entries
type AppArmorLogs []AppArmorLog
2021-11-09 23:41:12 +01:00
func removeDuplicateLog(logs []string) []string {
list := []string{}
2021-11-21 22:57:11 +01:00
keys := map[string]interface{}{"": true}
2021-11-09 23:41:12 +01:00
for _, log := range logs {
if _, v := keys[log]; !v {
keys[log] = true
list = append(list, log)
}
}
return list
}
func NewApparmorLogs(file *os.File, profile string) AppArmorLogs {
2021-11-09 23:41:12 +01:00
log := ""
exp := fmt.Sprintf("^.*apparmor=(\"DENIED\"|\"ALLOWED\").* profile=\"%s.*\".*$", profile)
isAppArmorLog := regexp.MustCompile(exp)
// Select Apparmor logs
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if isAppArmorLog.MatchString(line) {
log += line + "\n"
}
}
// Clean logs
cleanAppArmorLogs := []*regexp.Regexp{
regexp.MustCompile(`type=AVC msg=audit(.*): `),
regexp.MustCompile(` fsuid.*`),
}
for _, clean := range cleanAppArmorLogs {
log = clean.ReplaceAllLiteralString(log, "")
}
replaceAppArmorLogs := regexp.MustCompile(`pid=.* comm`)
log = replaceAppArmorLogs.ReplaceAllLiteralString(log, "comm")
// Remove doublon in logs
logs := strings.Split(log, "\n")
logs = removeDuplicateLog(logs)
// Parse log into ApparmorLog struct
aaLogs := make(AppArmorLogs, 0)
2021-11-09 23:41:12 +01:00
for _, log := range logs {
tmp := strings.Split(log, " ")
aa := make(AppArmorLog)
for _, item := range tmp {
kv := strings.Split(item, "=")
if len(kv) >= 2 {
aa[kv[0]] = strings.Trim(kv[1], `"`)
}
2021-11-09 23:41:12 +01:00
}
aaLogs = append(aaLogs, aa)
2021-11-09 23:41:12 +01:00
}
return aaLogs
}
func (aaLogs AppArmorLogs) String() string {
res := ""
2021-11-09 23:41:12 +01:00
state := map[string]string{
"DENIED": BoldRed + "DENIED " + Reset,
"ALLOWED": BoldGreen + "ALLOWED" + Reset,
}
2021-11-21 22:57:11 +01:00
// Order of impression
keys := []string{
"profile", "operation", "name", "info", "comm", "laddr",
"lport", "faddr", "fport", "family", "sock_type", "protocol",
2021-11-21 22:57:11 +01:00
"requested_mask", "denied_mask", "signal", "peer", // "fsuid", "ouid", "FSUID", "OUID",
}
2021-11-21 22:57:11 +01:00
// Optional colors template to use
colors := map[string]string{
"profile": FgBlue,
"operation": FgYellow,
"name": FgMagenta,
"requested_mask": "requested_mask=" + BoldRed,
"denied_mask": "denied_mask=" + BoldRed,
}
2021-11-09 23:41:12 +01:00
for _, log := range aaLogs {
2021-11-21 22:57:11 +01:00
seen := map[string]bool{"apparmor": true}
res += state[log["apparmor"]]
for _, key := range keys {
if log[key] != "" {
if colors[key] != "" {
res += " " + colors[key] + log[key] + Reset
} else {
res += " " + key + "=" + log[key]
}
2021-11-21 22:57:11 +01:00
seen[key] = true
}
}
for key, value := range log {
2021-11-21 22:57:11 +01:00
if !seen[key] {
res += " " + key + "=" + value
}
}
res += "\n"
2021-11-09 23:41:12 +01:00
}
return res
}
2021-11-09 23:41:12 +01:00
func main() {
profile := ""
if len(os.Args) >= 2 {
profile = os.Args[1]
}
file, err := os.Open(LogFile)
if err != nil {
2021-11-21 22:57:11 +01:00
fmt.Println(err)
os.Exit(1)
2021-11-09 23:41:12 +01:00
}
defer func() {
if err := file.Close(); err != nil {
fmt.Println("Error closing file:", err)
2021-11-21 22:57:11 +01:00
os.Exit(1)
2021-11-09 23:41:12 +01:00
}
}()
aaLogs := NewApparmorLogs(file, profile)
fmt.Print(aaLogs.String())
2021-11-09 23:41:12 +01:00
}