mirror of
https://github.com/roddhjav/apparmor.d.git
synced 2024-12-24 14:06:47 +01:00
feat(aa-log): add -a option to anonymize the logs.
This commit is contained in:
parent
26bd9350f2
commit
538da05696
4 changed files with 117 additions and 7 deletions
|
@ -11,7 +11,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/roddhjav/apparmor.d/pkg/logs"
|
"github.com/roddhjav/apparmor.d/pkg/logs"
|
||||||
"github.com/roddhjav/apparmor.d/pkg/util"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `aa-log [-h] [--systemd] [--file file] [profile]
|
const usage = `aa-log [-h] [--systemd] [--file file] [profile]
|
||||||
|
@ -28,17 +28,19 @@ Options:
|
||||||
-h, --help Show this help message and exit.
|
-h, --help Show this help message and exit.
|
||||||
-f, --file FILE Set a logfile or a suffix to the default log file.
|
-f, --file FILE Set a logfile or a suffix to the default log file.
|
||||||
-s, --systemd Parse systemd logs from journalctl.
|
-s, --systemd Parse systemd logs from journalctl.
|
||||||
|
-a, --anonymize Anonymize the logs.
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
// Command line options
|
// Command line options
|
||||||
var (
|
var (
|
||||||
help bool
|
help bool
|
||||||
path string
|
anonymize bool
|
||||||
systemd bool
|
path string
|
||||||
|
systemd bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func aaLog(logger string, path string, profile string) error {
|
func aaLog(logger string, path string, profile string, anonymize bool) error {
|
||||||
var err error
|
var err error
|
||||||
var file io.Reader
|
var file io.Reader
|
||||||
|
|
||||||
|
@ -54,6 +56,9 @@ func aaLog(logger string, path string, profile string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
aaLogs := logs.NewApparmorLogs(file, profile)
|
aaLogs := logs.NewApparmorLogs(file, profile)
|
||||||
|
if anonymize {
|
||||||
|
aaLogs.Anonymize()
|
||||||
|
}
|
||||||
fmt.Print(aaLogs.String())
|
fmt.Print(aaLogs.String())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -65,6 +70,8 @@ func init() {
|
||||||
flag.StringVar(&path, "file", "", "Set a logfile or a suffix to the default log file.")
|
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, "s", false, "Parse systemd logs from journalctl.")
|
||||||
flag.BoolVar(&systemd, "systemd", 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() {
|
func main() {
|
||||||
|
@ -86,7 +93,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
logfile := logs.GetLogFile(path)
|
logfile := logs.GetLogFile(path)
|
||||||
err := aaLog(logger, logfile, profile)
|
err := aaLog(logger, logfile, profile, anonymize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -14,6 +14,7 @@ func Test_app(t *testing.T) {
|
||||||
logger string
|
logger string
|
||||||
path string
|
path string
|
||||||
profile string
|
profile string
|
||||||
|
anon bool
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -21,6 +22,7 @@ func Test_app(t *testing.T) {
|
||||||
logger: "auditd",
|
logger: "auditd",
|
||||||
path: "../../tests/audit.log",
|
path: "../../tests/audit.log",
|
||||||
profile: "",
|
profile: "",
|
||||||
|
anon: true,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -28,6 +30,7 @@ func Test_app(t *testing.T) {
|
||||||
logger: "systemd",
|
logger: "systemd",
|
||||||
path: "../../tests/systemd.log",
|
path: "../../tests/systemd.log",
|
||||||
profile: "",
|
profile: "",
|
||||||
|
anon: false,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -35,6 +38,7 @@ func Test_app(t *testing.T) {
|
||||||
logger: "auditd",
|
logger: "auditd",
|
||||||
path: "../../tests/log",
|
path: "../../tests/log",
|
||||||
profile: "",
|
profile: "",
|
||||||
|
anon: false,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -42,12 +46,13 @@ func Test_app(t *testing.T) {
|
||||||
logger: "raw",
|
logger: "raw",
|
||||||
path: "../../tests/audit.log",
|
path: "../../tests/audit.log",
|
||||||
profile: "",
|
profile: "",
|
||||||
|
anon: false,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if err := aaLog(tt.logger, tt.path, tt.profile); (err != nil) != tt.wantErr {
|
if err := aaLog(tt.logger, tt.path, tt.profile, tt.anon); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("aaLog() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("aaLog() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os/user"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -28,6 +29,9 @@ const (
|
||||||
boldYellow = "\033[1;33m"
|
boldYellow = "\033[1;33m"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Anonymized username
|
||||||
|
const Username = "AAD"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
quoted bool
|
quoted bool
|
||||||
isAppArmorLogTemplate = regexp.MustCompile(`apparmor=("DENIED"|"ALLOWED"|"AUDIT")`)
|
isAppArmorLogTemplate = regexp.MustCompile(`apparmor=("DENIED"|"ALLOWED"|"AUDIT")`)
|
||||||
|
@ -116,6 +120,29 @@ func NewApparmorLogs(file io.Reader, profile string) AppArmorLogs {
|
||||||
return aaLogs
|
return aaLogs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Anonymize the logs before reporting
|
||||||
|
func (aaLogs AppArmorLogs) Anonymize() {
|
||||||
|
user, _ := user.Current()
|
||||||
|
keys := []string{"name", "comm"}
|
||||||
|
regAnonymizeLogs := []struct {
|
||||||
|
regex *regexp.Regexp
|
||||||
|
repl string
|
||||||
|
}{
|
||||||
|
{regexp.MustCompile(user.Username), Username},
|
||||||
|
{regexp.MustCompile(`/home/[^/]+`), `/home/` + Username},
|
||||||
|
{regexp.MustCompile(`[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
|
// String returns a formatted AppArmor logs string
|
||||||
func (aaLogs AppArmorLogs) String() string {
|
func (aaLogs AppArmorLogs) String() string {
|
||||||
// Apparmor log states
|
// Apparmor log states
|
||||||
|
|
|
@ -261,3 +261,74 @@ 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue