diff --git a/pkg/aa/apparmor.go b/pkg/aa/apparmor.go new file mode 100644 index 00000000..db0ca8b9 --- /dev/null +++ b/pkg/aa/apparmor.go @@ -0,0 +1,214 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +import ( + "bytes" + "reflect" + "slices" + "sort" + "strings" + + "github.com/arduino/go-paths-helper" +) + +// Default Apparmor magic directory: /etc/apparmor.d/. +var MagicRoot = paths.New("/etc/apparmor.d") + +// AppArmorProfileFiles represents a full set of apparmor profiles +type AppArmorProfileFiles map[string]*AppArmorProfileFile + +// AppArmorProfileFile represents a full apparmor profile file. +// Warning: close to the BNF grammar of apparmor profile but not exactly the same (yet): +// - Some rules are not supported yet (subprofile, hat...) +// - The structure is simplified as it only aims at writing profile, not parsing it. +type AppArmorProfileFile struct { + Preamble + Profiles []*Profile +} + +// Preamble section of a profile file, +type Preamble struct { + Abi []*Abi + Includes []*Include + Aliases []*Alias + Variables []*Variable +} + +func NewAppArmorProfile() *AppArmorProfileFile { + return &AppArmorProfileFile{} +} + +// String returns the formatted representation of a profile as a string +func (f *AppArmorProfileFile) String() string { + var res bytes.Buffer + err := tmplAppArmorProfile.Execute(&res, f) + if err != nil { + return err.Error() + } + return res.String() +} + +// GetDefaultProfile ensure a profile is always present in the profile file and +// return it, as a default profile. +func (f *AppArmorProfileFile) GetDefaultProfile() *Profile { + if len(f.Profiles) == 0 { + f.Profiles = append(f.Profiles, &Profile{}) + } + return f.Profiles[0] +} + +// AddRule adds a new rule to the profile from a log map +// See utils/apparmor/logparser.py for the format of the log map +func (f *AppArmorProfileFile) AddRule(log map[string]string) { + p := f.GetDefaultProfile() + + // Generate profile flags and extra rules + switch log["error"] { + case "-2": + if !slices.Contains(p.Flags, "mediate_deleted") { + p.Flags = append(p.Flags, "mediate_deleted") + } + case "-13": + if strings.Contains(log["info"], "namespace creation restricted") { + p.Rules = append(p.Rules, newUsernsFromLog(log)) + } else if strings.Contains(log["info"], "disconnected path") && !slices.Contains(p.Flags, "attach_disconnected") { + p.Flags = append(p.Flags, "attach_disconnected") + } + default: + } + + switch log["class"] { + case "cap": + p.Rules = append(p.Rules, newCapabilityFromLog(log)) + case "net": + if log["family"] == "unix" { + p.Rules = append(p.Rules, newUnixFromLog(log)) + } else { + p.Rules = append(p.Rules, newNetworkFromLog(log)) + } + case "mount": + if strings.Contains(log["flags"], "remount") { + p.Rules = append(p.Rules, newRemountFromLog(log)) + } else { + switch log["operation"] { + case "mount": + p.Rules = append(p.Rules, newMountFromLog(log)) + case "umount": + p.Rules = append(p.Rules, newUmountFromLog(log)) + case "remount": + p.Rules = append(p.Rules, newRemountFromLog(log)) + case "pivotroot": + p.Rules = append(p.Rules, newPivotRootFromLog(log)) + } + } + case "posix_mqueue", "sysv_mqueue": + p.Rules = append(p.Rules, newMqueueFromLog(log)) + case "signal": + p.Rules = append(p.Rules, newSignalFromLog(log)) + case "ptrace": + p.Rules = append(p.Rules, newPtraceFromLog(log)) + case "namespace": + p.Rules = append(p.Rules, newUsernsFromLog(log)) + case "unix": + p.Rules = append(p.Rules, newUnixFromLog(log)) + case "dbus": + p.Rules = append(p.Rules, newDbusFromLog(log)) + case "file": + if log["operation"] == "change_onexec" { + p.Rules = append(p.Rules, newChangeProfileFromLog(log)) + } else { + p.Rules = append(p.Rules, newFileFromLog(log)) + } + default: + if strings.Contains(log["operation"], "dbus") { + p.Rules = append(p.Rules, newDbusFromLog(log)) + } else if log["family"] == "unix" { + p.Rules = append(p.Rules, newUnixFromLog(log)) + } + } +} + +// Sort the rules in the profile +// Follow: https://apparmor.pujol.io/development/guidelines/#guidelines +func (f *AppArmorProfileFile) Sort() { + for _, p := range f.Profiles { + sort.Slice(p.Rules, func(i, j int) bool { + typeOfI := reflect.TypeOf(p.Rules[i]) + typeOfJ := reflect.TypeOf(p.Rules[j]) + if typeOfI != typeOfJ { + valueOfI := typeToValue(typeOfI) + valueOfJ := typeToValue(typeOfJ) + if typeOfI == reflect.TypeOf((*Include)(nil)) && p.Rules[i].(*Include).IfExists { + valueOfI = "include_if_exists" + } + if typeOfJ == reflect.TypeOf((*Include)(nil)) && p.Rules[j].(*Include).IfExists { + valueOfJ = "include_if_exists" + } + return ruleWeights[valueOfI] < ruleWeights[valueOfJ] + } + return p.Rules[i].Less(p.Rules[j]) + }) + } +} + +// MergeRules merge similar rules together. +// Steps: +// - Remove identical rules +// - Merge rule access. Eg: for same path, 'r' and 'w' becomes 'rw' +// +// Note: logs.regCleanLogs helps a lot to do a first cleaning +func (f *AppArmorProfileFile) MergeRules() { + for _, p := range f.Profiles { + for i := 0; i < len(p.Rules); i++ { + for j := i + 1; j < len(p.Rules); j++ { + typeOfI := reflect.TypeOf(p.Rules[i]) + typeOfJ := reflect.TypeOf(p.Rules[j]) + if typeOfI != typeOfJ { + continue + } + + // If rules are identical, merge them + if p.Rules[i].Equals(p.Rules[j]) { + p.Rules = append(p.Rules[:j], p.Rules[j+1:]...) + j-- + } + } + } + } +} + +// Format the profile for better readability before printing it. +// Follow: https://apparmor.pujol.io/development/guidelines/#the-file-block +func (f *AppArmorProfileFile) Format() { + const prefixOwner = " " + for _, p := range f.Profiles { + hasOwnerRule := false + for i := len(p.Rules) - 1; i > 0; i-- { + j := i - 1 + typeOfI := reflect.TypeOf(p.Rules[i]) + typeOfJ := reflect.TypeOf(p.Rules[j]) + + // File rule + if typeOfI == reflect.TypeOf((*File)(nil)) && typeOfJ == reflect.TypeOf((*File)(nil)) { + letterI := getLetterIn(fileAlphabet, p.Rules[i].(*File).Path) + letterJ := getLetterIn(fileAlphabet, p.Rules[j].(*File).Path) + + // Add prefix before rule path to align with other rule + if p.Rules[i].(*File).Owner { + hasOwnerRule = true + } else if hasOwnerRule { + p.Rules[i].(*File).Prefix = prefixOwner + } + + if letterI != letterJ { + // Add a new empty line between Files rule of different type + hasOwnerRule = false + p.Rules = append(p.Rules[:i], append([]ApparmorRule{&Rule{}}, p.Rules[i:]...)...) + } + } + } + } +} diff --git a/pkg/aa/profile_test.go b/pkg/aa/apparmor_test.go similarity index 90% rename from pkg/aa/profile_test.go rename to pkg/aa/apparmor_test.go index 9866cf89..2f89737b 100644 --- a/pkg/aa/profile_test.go +++ b/pkg/aa/apparmor_test.go @@ -31,17 +31,17 @@ func readprofile(path string) string { func TestAppArmorProfile_String(t *testing.T) { tests := []struct { name string - p *AppArmorProfile + f *AppArmorProfileFile want string }{ { name: "empty", - p: &AppArmorProfile{}, + f: &AppArmorProfileFile{}, want: ``, }, { name: "foo", - p: &AppArmorProfile{ + f: &AppArmorProfileFile{ Preamble: Preamble{ Abi: []*Abi{{IsMagic: true, Path: "abi/4.0"}}, Includes: []*Include{{IsMagic: true, Path: "tunables/global"}}, @@ -117,7 +117,7 @@ func TestAppArmorProfile_String(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := tt.p.String(); got != tt.want { + if got := tt.f.String(); got != tt.want { t.Errorf("AppArmorProfile.String() = |%v|, want |%v|", got, tt.want) } }) @@ -128,12 +128,12 @@ func TestAppArmorProfile_AddRule(t *testing.T) { tests := []struct { name string log map[string]string - want *AppArmorProfile + want *AppArmorProfileFile }{ { name: "capability", log: capability1Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{capability1}, }}, @@ -142,7 +142,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "network", log: network1Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{network1}, }}, @@ -151,7 +151,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "mount", log: mount2Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{mount2}, }}, @@ -160,7 +160,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "signal", log: signal1Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{signal1}, }}, @@ -169,7 +169,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "ptrace", log: ptrace2Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{ptrace2}, }}, @@ -178,7 +178,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "unix", log: unix1Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{unix1}, }}, @@ -187,7 +187,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "dbus", log: dbus2Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{dbus2}, }}, @@ -196,7 +196,7 @@ func TestAppArmorProfile_AddRule(t *testing.T) { { name: "file", log: file2Log, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{file2}, }}, @@ -217,12 +217,12 @@ func TestAppArmorProfile_AddRule(t *testing.T) { func TestAppArmorProfile_Sort(t *testing.T) { tests := []struct { name string - origin *AppArmorProfile - want *AppArmorProfile + origin *AppArmorProfileFile + want *AppArmorProfileFile }{ { name: "all", - origin: &AppArmorProfile{ + origin: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{ file2, network1, includeLocal1, dbus2, signal1, ptrace1, @@ -230,7 +230,7 @@ func TestAppArmorProfile_Sort(t *testing.T) { }, }}, }, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{ capability2, network1, mount2, signal1, signal2, ptrace1, @@ -254,17 +254,17 @@ func TestAppArmorProfile_Sort(t *testing.T) { func TestAppArmorProfile_MergeRules(t *testing.T) { tests := []struct { name string - origin *AppArmorProfile - want *AppArmorProfile + origin *AppArmorProfileFile + want *AppArmorProfileFile }{ { name: "all", - origin: &AppArmorProfile{ + origin: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{capability1, capability1, network1, network1, file1, file1}, }}, }, - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Profiles: []*Profile{{ Rules: []ApparmorRule{capability1, network1, file1}, }}, @@ -285,12 +285,12 @@ func TestAppArmorProfile_MergeRules(t *testing.T) { func TestAppArmorProfile_Integration(t *testing.T) { tests := []struct { name string - p *AppArmorProfile + f *AppArmorProfileFile want string }{ { name: "aa-status", - p: &AppArmorProfile{ + f: &AppArmorProfileFile{ Preamble: Preamble{ Abi: []*Abi{{IsMagic: true, Path: "abi/3.0"}}, Includes: []*Include{{IsMagic: true, Path: "tunables/global"}}, @@ -327,10 +327,10 @@ func TestAppArmorProfile_Integration(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.p.Sort() - tt.p.MergeRules() - tt.p.Format() - if got := tt.p.String(); "\n"+got != tt.want { + tt.f.Sort() + tt.f.MergeRules() + tt.f.Format() + if got := tt.f.String(); "\n"+got != tt.want { t.Errorf("AppArmorProfile = |%v|, want |%v|", "\n"+got, tt.want) } }) diff --git a/pkg/aa/profile.go b/pkg/aa/profile.go index 54f288dd..4091dc5e 100644 --- a/pkg/aa/profile.go +++ b/pkg/aa/profile.go @@ -5,46 +5,19 @@ package aa import ( - "bytes" "maps" - "reflect" "slices" - "sort" "strings" - - "github.com/arduino/go-paths-helper" ) -// Default Apparmor magic directory: /etc/apparmor.d/. -var MagicRoot = paths.New("/etc/apparmor.d") - -// AppArmorProfiles represents a full set of apparmor profiles -type AppArmorProfiles map[string]*AppArmorProfile - -// ApparmorProfile represents a full apparmor profile file. -// Warning: close to the BNF grammar of apparmor profile but not exactly the same (yet): -// - Some rules are not supported yet (subprofile, hat...) -// - The structure is simplified as it only aims at writing profile, not parsing it. -type AppArmorProfile struct { - Preamble - Profiles []*Profile -} - -// Preamble section of a profile file, -type Preamble struct { - Abi []*Abi - Includes []*Include - Aliases []*Alias - Variables []*Variable -} - -// Profile represent a single AppArmor profile. +// Profile represents a single AppArmor profile. type Profile struct { Rule Header Rules Rules } +// Header represents the header of a profile. type Header struct { Name string Attachments []string @@ -53,7 +26,11 @@ type Header struct { } func (r *Profile) Less(other any) bool { - return false // TBD + o, _ := other.(*Profile) + if r.Name != o.Name { + return r.Name < o.Name + } + return len(r.Attachments) < len(o.Attachments) } func (r *Profile) Equals(other any) bool { @@ -62,187 +39,3 @@ func (r *Profile) Equals(other any) bool { maps.Equal(r.Attributes, o.Attributes) && slices.Equal(r.Flags, o.Flags) } - -// ApparmorRule generic interface -type ApparmorRule interface { - Less(other any) bool - Equals(other any) bool -} - -type Rules []ApparmorRule - -func NewAppArmorProfile() *AppArmorProfile { - return &AppArmorProfile{} -} - -// String returns the formatted representation of a profile as a string -func (p *AppArmorProfile) String() string { - var res bytes.Buffer - err := tmplAppArmorProfile.Execute(&res, p) - if err != nil { - return err.Error() - } - return res.String() -} - -// GetDefaultProfile ensure a profile is always present in the profile file and -// return it, as a default profile. -func (p *AppArmorProfile) GetDefaultProfile() *Profile { - if len(p.Profiles) == 0 { - p.Profiles = append(p.Profiles, &Profile{}) - } - return p.Profiles[0] -} - -// AddRule adds a new rule to the profile from a log map -// See utils/apparmor/logparser.py for the format of the log map -func (profile *AppArmorProfile) AddRule(log map[string]string) { - p := profile.GetDefaultProfile() - - // Generate profile flags and extra rules - switch log["error"] { - case "-2": - if !slices.Contains(p.Flags, "mediate_deleted") { - p.Flags = append(p.Flags, "mediate_deleted") - } - case "-13": - if strings.Contains(log["info"], "namespace creation restricted") { - p.Rules = append(p.Rules, newUsernsFromLog(log)) - } else if strings.Contains(log["info"], "disconnected path") && !slices.Contains(p.Flags, "attach_disconnected") { - p.Flags = append(p.Flags, "attach_disconnected") - } - default: - } - - switch log["class"] { - case "cap": - p.Rules = append(p.Rules, newCapabilityFromLog(log)) - case "net": - if log["family"] == "unix" { - p.Rules = append(p.Rules, newUnixFromLog(log)) - } else { - p.Rules = append(p.Rules, newNetworkFromLog(log)) - } - case "mount": - if strings.Contains(log["flags"], "remount") { - p.Rules = append(p.Rules, newRemountFromLog(log)) - } else { - switch log["operation"] { - case "mount": - p.Rules = append(p.Rules, newMountFromLog(log)) - case "umount": - p.Rules = append(p.Rules, newUmountFromLog(log)) - case "remount": - p.Rules = append(p.Rules, newRemountFromLog(log)) - case "pivotroot": - p.Rules = append(p.Rules, newPivotRootFromLog(log)) - } - } - case "posix_mqueue", "sysv_mqueue": - p.Rules = append(p.Rules, newMqueueFromLog(log)) - case "signal": - p.Rules = append(p.Rules, newSignalFromLog(log)) - case "ptrace": - p.Rules = append(p.Rules, newPtraceFromLog(log)) - case "namespace": - p.Rules = append(p.Rules, newUsernsFromLog(log)) - case "unix": - p.Rules = append(p.Rules, newUnixFromLog(log)) - case "dbus": - p.Rules = append(p.Rules, newDbusFromLog(log)) - case "file": - if log["operation"] == "change_onexec" { - p.Rules = append(p.Rules, newChangeProfileFromLog(log)) - } else { - p.Rules = append(p.Rules, newFileFromLog(log)) - } - default: - if strings.Contains(log["operation"], "dbus") { - p.Rules = append(p.Rules, newDbusFromLog(log)) - } else if log["family"] == "unix" { - p.Rules = append(p.Rules, newUnixFromLog(log)) - } - } -} - -// Sort the rules in the profile -// Follow: https://apparmor.pujol.io/development/guidelines/#guidelines -func (profile *AppArmorProfile) Sort() { - for _, p := range profile.Profiles { - sort.Slice(p.Rules, func(i, j int) bool { - typeOfI := reflect.TypeOf(p.Rules[i]) - typeOfJ := reflect.TypeOf(p.Rules[j]) - if typeOfI != typeOfJ { - valueOfI := typeToValue(typeOfI) - valueOfJ := typeToValue(typeOfJ) - if typeOfI == reflect.TypeOf((*Include)(nil)) && p.Rules[i].(*Include).IfExists { - valueOfI = "include_if_exists" - } - if typeOfJ == reflect.TypeOf((*Include)(nil)) && p.Rules[j].(*Include).IfExists { - valueOfJ = "include_if_exists" - } - return ruleWeights[valueOfI] < ruleWeights[valueOfJ] - } - return p.Rules[i].Less(p.Rules[j]) - }) - } -} - -// MergeRules merge similar rules together. -// Steps: -// - Remove identical rules -// - Merge rule access. Eg: for same path, 'r' and 'w' becomes 'rw' -// -// Note: logs.regCleanLogs helps a lot to do a first cleaning -func (profile *AppArmorProfile) MergeRules() { - for _, p := range profile.Profiles { - for i := 0; i < len(p.Rules); i++ { - for j := i + 1; j < len(p.Rules); j++ { - typeOfI := reflect.TypeOf(p.Rules[i]) - typeOfJ := reflect.TypeOf(p.Rules[j]) - if typeOfI != typeOfJ { - continue - } - - // If rules are identical, merge them - if p.Rules[i].Equals(p.Rules[j]) { - p.Rules = append(p.Rules[:j], p.Rules[j+1:]...) - j-- - } - } - } - } -} - -// Format the profile for better readability before printing it. -// Follow: https://apparmor.pujol.io/development/guidelines/#the-file-block -func (profile *AppArmorProfile) Format() { - const prefixOwner = " " - for _, p := range profile.Profiles { - hasOwnerRule := false - for i := len(p.Rules) - 1; i > 0; i-- { - j := i - 1 - typeOfI := reflect.TypeOf(p.Rules[i]) - typeOfJ := reflect.TypeOf(p.Rules[j]) - - // File rule - if typeOfI == reflect.TypeOf((*File)(nil)) && typeOfJ == reflect.TypeOf((*File)(nil)) { - letterI := getLetterIn(fileAlphabet, p.Rules[i].(*File).Path) - letterJ := getLetterIn(fileAlphabet, p.Rules[j].(*File).Path) - - // Add prefix before rule path to align with other rule - if p.Rules[i].(*File).Owner { - hasOwnerRule = true - } else if hasOwnerRule { - p.Rules[i].(*File).Prefix = prefixOwner - } - - if letterI != letterJ { - // Add a new empty line between Files rule of different type - hasOwnerRule = false - p.Rules = append(p.Rules[:i], append([]ApparmorRule{&Rule{}}, p.Rules[i:]...)...) - } - } - } - } -} diff --git a/pkg/aa/rules.go b/pkg/aa/rules.go index a09aac40..dad738ba 100644 --- a/pkg/aa/rules.go +++ b/pkg/aa/rules.go @@ -9,6 +9,14 @@ import ( "strings" ) +// ApparmorRule generic interface +type ApparmorRule interface { + Less(other any) bool + Equals(other any) bool +} + +type Rules []ApparmorRule + type Rule struct { Comment string NoNewPrivs bool diff --git a/pkg/aa/templates/profile.j2 b/pkg/aa/templates/profile.j2 index 9875587c..1c4cb287 100644 --- a/pkg/aa/templates/profile.j2 +++ b/pkg/aa/templates/profile.j2 @@ -4,7 +4,7 @@ {{- define "profile" -}} - {{- if or .Name .Attachments .Attributes .Flags -}} + {{- with .Header -}} {{- "profile" -}} {{- with .Name -}} {{ " " }}{{ . }} @@ -18,9 +18,7 @@ {{- with .Flags -}} {{ " flags=(" }}{{ join . }}{{ ")" }} {{- end -}} - {{ " {" }} - {{- template "comment" . -}} - {{- "\n" -}} + {{- "{\n" -}} {{- end -}} {{- $oldtype := "" -}} @@ -296,7 +294,7 @@ {{- $oldtype = $type -}} {{- end -}} - {{- if or .Name .Attachments .Attributes .Flags -}} + {{- with .Header -}} {{- "}\n" -}} {{- end -}} diff --git a/pkg/aa/variables.go b/pkg/aa/variables.go index cd34bd1a..684564a2 100644 --- a/pkg/aa/variables.go +++ b/pkg/aa/variables.go @@ -19,8 +19,8 @@ var ( // DefaultTunables return a minimal working profile to build the profile // It should not be used when loading file from /etc/apparmor.d -func DefaultTunables() *AppArmorProfile { - return &AppArmorProfile{ +func DefaultTunables() *AppArmorProfileFile { + return &AppArmorProfileFile{ Preamble: Preamble{ Variables: []*Variable{ {Name: "bin", Values: []string{"/{,usr/}{,s}bin"}}, @@ -36,41 +36,41 @@ func DefaultTunables() *AppArmorProfile { } // ParseVariables extract all variables from the profile -func (p *AppArmorProfile) ParseVariables(content string) { +func (f *AppArmorProfileFile) ParseVariables(content string) { matches := regVariablesDef.FindAllStringSubmatch(content, -1) for _, match := range matches { if len(match) > 2 { key := match[1] values := strings.Split(match[2], " ") found := false - for idx, variable := range p.Variables { + for idx, variable := range f.Variables { if variable.Name == key { - p.Variables[idx].Values = append(p.Variables[idx].Values, values...) + f.Variables[idx].Values = append(f.Variables[idx].Values, values...) found = true break } } if !found { variable := &Variable{Name: key, Values: values} - p.Variables = append(p.Variables, variable) + f.Variables = append(f.Variables, variable) } } } } // resolve recursively resolves all variables references -func (p *AppArmorProfile) resolve(str string) []string { +func (f *AppArmorProfileFile) resolve(str string) []string { if strings.Contains(str, "@{") { vars := []string{} match := regVariablesRef.FindStringSubmatch(str) if len(match) > 1 { variable := match[0] varname := match[1] - for _, vrbl := range p.Variables { + for _, vrbl := range f.Variables { if vrbl.Name == varname { for _, value := range vrbl.Values { newVar := strings.ReplaceAll(str, variable, value) - vars = append(vars, p.resolve(newVar)...) + vars = append(vars, f.resolve(newVar)...) } } } @@ -83,8 +83,8 @@ func (p *AppArmorProfile) resolve(str string) []string { } // ResolveAttachments resolve profile attachments defined in exec_path -func (profile *AppArmorProfile) ResolveAttachments() { - p := profile.GetDefaultProfile() +func (f *AppArmorProfileFile) ResolveAttachments() { + p := f.GetDefaultProfile() for _, variable := range profile.Variables { if variable.Name == "exec_path" { @@ -100,8 +100,8 @@ func (profile *AppArmorProfile) ResolveAttachments() { } // NestAttachments return a nested attachment string -func (profile *AppArmorProfile) NestAttachments() string { - p := profile.GetDefaultProfile() +func (f *AppArmorProfileFile) NestAttachments() string { + p := f.GetDefaultProfile() if len(p.Attachments) == 0 { return "" } else if len(p.Attachments) == 1 { diff --git a/pkg/aa/variables_test.go b/pkg/aa/variables_test.go index 2232e65a..778b3380 100644 --- a/pkg/aa/variables_test.go +++ b/pkg/aa/variables_test.go @@ -16,11 +16,11 @@ import ( func TestDefaultTunables(t *testing.T) { tests := []struct { name string - want *AppArmorProfile + want *AppArmorProfileFile }{ { name: "aa", - want: &AppArmorProfile{ + want: &AppArmorProfileFile{ Preamble: Preamble{ Variables: []*Variable{ {Name: "bin", Values: []string{"/{,usr/}{,s}bin"}}, diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index 5b2ea712..f14aefd4 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -197,8 +197,8 @@ func (aaLogs AppArmorLogs) String() string { } // ParseToProfiles convert the log data into a new AppArmorProfiles -func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfiles { - profiles := make(aa.AppArmorProfiles, 0) +func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles { + profiles := make(aa.AppArmorProfileFiles, 0) for _, log := range aaLogs { name := "" if strings.Contains(log["operation"], "dbus") { @@ -208,7 +208,7 @@ func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfiles { } if _, ok := profiles[name]; !ok { - profile := &aa.AppArmorProfile{ + profile := &aa.AppArmorProfileFile{ Profiles: []*aa.Profile{{Header: aa.Header{Name: name}}}, } profile.AddRule(log) diff --git a/pkg/logs/logs_test.go b/pkg/logs/logs_test.go index e970da5f..462e8417 100644 --- a/pkg/logs/logs_test.go +++ b/pkg/logs/logs_test.go @@ -292,13 +292,13 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) { tests := []struct { name string aaLogs AppArmorLogs - want aa.AppArmorProfiles + want aa.AppArmorProfileFiles }{ { name: "", aaLogs: append(append(refKmod, refPowerProfiles...), refKmod...), - want: aa.AppArmorProfiles{ - "kmod": &aa.AppArmorProfile{ + want: aa.AppArmorProfileFiles{ + "kmod": &aa.AppArmorProfileFile{ Profiles: []*aa.Profile{{ Header: aa.Header{Name: "kmod"}, Rules: aa.Rules{ @@ -317,7 +317,7 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) { }, }}, }, - "power-profiles-daemon": &aa.AppArmorProfile{ + "power-profiles-daemon": &aa.AppArmorProfileFile{ Profiles: []*aa.Profile{{ Header: aa.Header{Name: "power-profiles-daemon"}, Rules: aa.Rules{ diff --git a/pkg/prebuild/directive/dbus.go b/pkg/prebuild/directive/dbus.go index 98c2306f..9b93ec8e 100644 --- a/pkg/prebuild/directive/dbus.go +++ b/pkg/prebuild/directive/dbus.go @@ -51,7 +51,7 @@ func setInterfaces(rules map[string]string) []string { } func (d Dbus) Apply(opt *Option, profile string) string { - var p *aa.AppArmorProfile + var p *aa.AppArmorProfileFile action := d.sanityCheck(opt) switch action { @@ -95,9 +95,9 @@ func (d Dbus) sanityCheck(opt *Option) string { return action } -func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile { +func (d Dbus) own(rules map[string]string) *aa.AppArmorProfileFile { interfaces := setInterfaces(rules) - profile := &aa.AppArmorProfile{} + profile := &aa.AppArmorProfileFile{} p := profile.GetDefaultProfile() p.Rules = append(p.Rules, &aa.Dbus{ Access: "bind", Bus: rules["bus"], Name: rules["name"], @@ -131,9 +131,9 @@ func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile { return profile } -func (d Dbus) talk(rules map[string]string) *aa.AppArmorProfile { +func (d Dbus) talk(rules map[string]string) *aa.AppArmorProfileFile { interfaces := setInterfaces(rules) - profile := &aa.AppArmorProfile{} + profile := &aa.AppArmorProfileFile{} p := profile.GetDefaultProfile() for _, iface := range interfaces { p.Rules = append(p.Rules, &aa.Dbus{ diff --git a/pkg/prebuild/directive/exec.go b/pkg/prebuild/directive/exec.go index e622d7af..9296ca96 100644 --- a/pkg/prebuild/directive/exec.go +++ b/pkg/prebuild/directive/exec.go @@ -36,7 +36,7 @@ func (d Exec) Apply(opt *Option, profileRaw string) string { delete(opt.ArgMap, t) } - profile := &aa.AppArmorProfile{} + profile := &aa.AppArmorProfileFile{} p := profile.GetDefaultProfile() for name := range opt.ArgMap { profiletoTransition := util.MustReadFile(cfg.RootApparmord.Join(name))