From 3b0944c615f785327a24b4e382515c6f36e27558 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 28 May 2024 18:15:22 +0100 Subject: [PATCH] feat(aa): add the Kind struct to manage aa rules. --- pkg/aa/base.go | 8 ++-- pkg/aa/blocks.go | 9 ++-- pkg/aa/capability.go | 10 ++-- pkg/aa/change_profile.go | 8 ++-- pkg/aa/dbus.go | 8 ++-- pkg/aa/file.go | 32 ++++++------- pkg/aa/io_uring.go | 10 ++-- pkg/aa/mount.go | 24 +++++----- pkg/aa/mqueue.go | 10 ++-- pkg/aa/network.go | 8 ++-- pkg/aa/parse.go | 22 +++++---- pkg/aa/pivot_root.go | 6 +-- pkg/aa/preamble.go | 43 +++++++++--------- pkg/aa/profile.go | 11 +++-- pkg/aa/ptrace.go | 10 ++-- pkg/aa/resolve.go | 6 +-- pkg/aa/rlimit.go | 9 ++-- pkg/aa/rules.go | 48 +++++++++++++------- pkg/aa/signal.go | 10 ++-- pkg/aa/template.go | 96 +++++++++++++++++++-------------------- pkg/aa/templates/rules.j2 | 58 +++++++++++------------ pkg/aa/unix.go | 10 ++-- pkg/aa/userns.go | 6 +-- 23 files changed, 239 insertions(+), 223 deletions(-) diff --git a/pkg/aa/base.go b/pkg/aa/base.go index 13a67527f..8fdae72c3 100644 --- a/pkg/aa/base.go +++ b/pkg/aa/base.go @@ -24,7 +24,7 @@ func newRule(rule []string) RuleBase { idx := 0 for idx < len(rule) { - if rule[idx] == tokCOMMENT { + if rule[idx] == COMMENT.Tok() { comment = " " + strings.Join(rule[idx+1:], " ") break } @@ -85,15 +85,15 @@ func (r RuleBase) Equals(other any) bool { } func (r RuleBase) String() string { - return renderTemplate("comment", r) + return renderTemplate(r.Kind(), r) } func (r RuleBase) Constraint() constraint { return anyKind } -func (r RuleBase) Kind() string { - return "base" +func (r RuleBase) Kind() Kind { + return COMMENT } type Qualifier struct { diff --git a/pkg/aa/blocks.go b/pkg/aa/blocks.go index 8b4f3ab59..6d1079ace 100644 --- a/pkg/aa/blocks.go +++ b/pkg/aa/blocks.go @@ -5,8 +5,7 @@ package aa const ( - tokHAT = "hat" - tokCARET = "^" + HAT Kind = "hat" ) // Hat represents a single AppArmor hat. @@ -26,7 +25,7 @@ func (p *Hat) Less(other any) bool { } func (p *Hat) Equals(other any) bool { - o, _ := other.(*Profile) + o, _ := other.(*Hat) return p.Name == o.Name } @@ -38,6 +37,6 @@ func (p *Hat) Constraint() constraint { return blockKind } -func (p *Hat) Kind() string { - return tokHAT +func (p *Hat) Kind() Kind { + return HAT } diff --git a/pkg/aa/capability.go b/pkg/aa/capability.go index 33a0a6a8e..44508e0e0 100644 --- a/pkg/aa/capability.go +++ b/pkg/aa/capability.go @@ -9,10 +9,10 @@ import ( "slices" ) -const tokCAPABILITY = "capability" +const CAPABILITY Kind = "capability" func init() { - requirements[tokCAPABILITY] = requirement{ + requirements[CAPABILITY] = requirement{ "name": { "audit_control", "audit_read", "audit_write", "block_suspend", "bpf", "checkpoint_restore", "chown", "dac_override", "dac_read_search", @@ -36,7 +36,7 @@ func newCapabilityFromLog(log map[string]string) Rule { return &Capability{ RuleBase: newRuleFromLog(log), Qualifier: newQualifierFromLog(log), - Names: Must(toValues(tokCAPABILITY, "name", log["capname"])), + Names: Must(toValues(CAPABILITY, "name", log["capname"])), } } @@ -70,6 +70,6 @@ func (r *Capability) Constraint() constraint { return blockKind } -func (r *Capability) Kind() string { - return tokCAPABILITY +func (r *Capability) Kind() Kind { + return CAPABILITY } diff --git a/pkg/aa/change_profile.go b/pkg/aa/change_profile.go index be08692f9..d5cc618ce 100644 --- a/pkg/aa/change_profile.go +++ b/pkg/aa/change_profile.go @@ -6,10 +6,10 @@ package aa import "fmt" -const tokCHANGEPROFILE = "change_profile" +const CHANGEPROFILE Kind = "change_profile" func init() { - requirements[tokCHANGEPROFILE] = requirement{ + requirements[CHANGEPROFILE] = requirement{ "mode": []string{"safe", "unsafe"}, } } @@ -67,6 +67,6 @@ func (r *ChangeProfile) Constraint() constraint { return blockKind } -func (r *ChangeProfile) Kind() string { - return tokCHANGEPROFILE +func (r *ChangeProfile) Kind() Kind { + return CHANGEPROFILE } diff --git a/pkg/aa/dbus.go b/pkg/aa/dbus.go index ded2b3cb4..56edd7977 100644 --- a/pkg/aa/dbus.go +++ b/pkg/aa/dbus.go @@ -9,10 +9,10 @@ import ( "slices" ) -const tokDBUS = "dbus" +const DBUS Kind = "dbus" func init() { - requirements[tokDBUS] = requirement{ + requirements[DBUS] = requirement{ "access": []string{ "send", "receive", "bind", "eavesdrop", "r", "read", "w", "write", "rw", @@ -110,6 +110,6 @@ func (r *Dbus) Constraint() constraint { return blockKind } -func (r *Dbus) Kind() string { - return tokDBUS +func (r *Dbus) Kind() Kind { + return DBUS } diff --git a/pkg/aa/file.go b/pkg/aa/file.go index 18e2c5cbc..07ab71d83 100644 --- a/pkg/aa/file.go +++ b/pkg/aa/file.go @@ -11,14 +11,14 @@ import ( ) const ( - tokLINK = "link" - tokFILE = "file" - tokOWNER = "owner" - tokSUBSET = "subset" + LINK Kind = "link" + FILE Kind = "file" + tokOWNER = "owner" + tokSUBSET = "subset" ) func init() { - requirements[tokFILE] = requirement{ + requirements[FILE] = requirement{ "access": {"m", "r", "w", "l", "k"}, "transition": { "ix", "ux", "Ux", "px", "Px", "cx", "Cx", "pix", "Pix", "cix", @@ -40,15 +40,15 @@ func isOwner(log map[string]string) bool { // cmpFileAccess compares two access strings for file rules. // It is aimed to be used in slices.SortFunc. func cmpFileAccess(i, j string) int { - if slices.Contains(requirements[tokFILE]["access"], i) && - slices.Contains(requirements[tokFILE]["access"], j) { - return requirementsWeights[tokFILE]["access"][i] - requirementsWeights[tokFILE]["access"][j] + if slices.Contains(requirements[FILE]["access"], i) && + slices.Contains(requirements[FILE]["access"], j) { + return requirementsWeights[FILE]["access"][i] - requirementsWeights[FILE]["access"][j] } - if slices.Contains(requirements[tokFILE]["transition"], i) && - slices.Contains(requirements[tokFILE]["transition"], j) { - return requirementsWeights[tokFILE]["transition"][i] - requirementsWeights[tokFILE]["transition"][j] + if slices.Contains(requirements[FILE]["transition"], i) && + slices.Contains(requirements[FILE]["transition"], j) { + return requirementsWeights[FILE]["transition"][i] - requirementsWeights[FILE]["transition"][j] } - if slices.Contains(requirements[tokFILE]["access"], i) { + if slices.Contains(requirements[FILE]["access"], i) { return -1 } return 1 @@ -121,8 +121,8 @@ func (r *File) Constraint() constraint { return blockKind } -func (r *File) Kind() string { - return tokFILE +func (r *File) Kind() Kind { + return FILE } type Link struct { @@ -179,6 +179,6 @@ func (r *Link) Constraint() constraint { return blockKind } -func (r *Link) Kind() string { - return tokLINK +func (r *Link) Kind() Kind { + return LINK } diff --git a/pkg/aa/io_uring.go b/pkg/aa/io_uring.go index 13c1031b9..42297a1f1 100644 --- a/pkg/aa/io_uring.go +++ b/pkg/aa/io_uring.go @@ -9,10 +9,10 @@ import ( "slices" ) -const tokIOURING = "io_uring" +const IOURING Kind = "io_uring" func init() { - requirements[tokIOURING] = requirement{ + requirements[IOURING] = requirement{ "access": []string{"sqpoll", "override_creds"}, } } @@ -28,7 +28,7 @@ func newIOUringFromLog(log map[string]string) Rule { return &IOUring{ RuleBase: newRuleFromLog(log), Qualifier: newQualifierFromLog(log), - Access: Must(toAccess(tokIOURING, log["requested"])), + Access: Must(toAccess(IOURING, log["requested"])), Label: log["label"], } } @@ -64,6 +64,6 @@ func (r *IOUring) Constraint() constraint { return blockKind } -func (r *IOUring) Kind() string { - return tokIOURING +func (r *IOUring) Kind() Kind { + return IOURING } diff --git a/pkg/aa/mount.go b/pkg/aa/mount.go index 03b758ccb..e131e54cf 100644 --- a/pkg/aa/mount.go +++ b/pkg/aa/mount.go @@ -10,13 +10,13 @@ import ( ) const ( - tokMOUNT = "mount" - tokREMOUNT = "remount" - tokUMOUNT = "umount" + MOUNT Kind = "mount" + REMOUNT Kind = "remount" + UMOUNT Kind = "umount" ) func init() { - requirements[tokMOUNT] = requirement{ + requirements[MOUNT] = requirement{ "flags": { "acl", "async", "atime", "ro", "rw", "bind", "rbind", "dev", "diratime", "dirsync", "exec", "iversion", "loud", "mand", "move", @@ -38,14 +38,14 @@ func newMountConditionsFromLog(log map[string]string) MountConditions { if _, present := log["flags"]; present { return MountConditions{ FsType: log["fstype"], - Options: Must(toValues(tokMOUNT, "flags", log["flags"])), + Options: Must(toValues(MOUNT, "flags", log["flags"])), } } return MountConditions{FsType: log["fstype"]} } func (m MountConditions) Validate() error { - return validateValues(tokMOUNT, "flags", m.Options) + return validateValues(MOUNT, "flags", m.Options) } func (m MountConditions) Less(other MountConditions) bool { @@ -113,8 +113,8 @@ func (r *Mount) Constraint() constraint { return blockKind } -func (r *Mount) Kind() string { - return tokMOUNT +func (r *Mount) Kind() Kind { + return MOUNT } type Umount struct { @@ -166,8 +166,8 @@ func (r *Umount) Constraint() constraint { return blockKind } -func (r *Umount) Kind() string { - return tokUMOUNT +func (r *Umount) Kind() Kind { + return UMOUNT } type Remount struct { @@ -219,6 +219,6 @@ func (r *Remount) Constraint() constraint { return blockKind } -func (r *Remount) Kind() string { - return tokREMOUNT +func (r *Remount) Kind() Kind { + return REMOUNT } diff --git a/pkg/aa/mqueue.go b/pkg/aa/mqueue.go index 7afb11792..9fc5f2607 100644 --- a/pkg/aa/mqueue.go +++ b/pkg/aa/mqueue.go @@ -10,10 +10,10 @@ import ( "strings" ) -const tokMQUEUE = "mqueue" +const MQUEUE Kind = "mqueue" func init() { - requirements[tokMQUEUE] = requirement{ + requirements[MQUEUE] = requirement{ "access": []string{ "r", "w", "rw", "read", "write", "create", "open", "delete", "getattr", "setattr", @@ -41,7 +41,7 @@ func newMqueueFromLog(log map[string]string) Rule { return &Mqueue{ RuleBase: newRuleFromLog(log), Qualifier: newQualifierFromLog(log), - Access: Must(toAccess(tokMQUEUE, log["requested"])), + Access: Must(toAccess(MQUEUE, log["requested"])), Type: mqueueType, Label: log["label"], Name: log["name"], @@ -86,6 +86,6 @@ func (r *Mqueue) Constraint() constraint { return blockKind } -func (r *Mqueue) Kind() string { - return tokMQUEUE +func (r *Mqueue) Kind() Kind { + return MQUEUE } diff --git a/pkg/aa/network.go b/pkg/aa/network.go index edc781007..8d01b0bab 100644 --- a/pkg/aa/network.go +++ b/pkg/aa/network.go @@ -8,10 +8,10 @@ import ( "fmt" ) -const tokNETWORK = "network" +const NETWORK Kind = "network" func init() { - requirements[tokNETWORK] = requirement{ + requirements[NETWORK] = requirement{ "access": []string{ "create", "bind", "listen", "accept", "connect", "shutdown", "getattr", "setattr", "getopt", "setopt", "send", "receive", @@ -126,6 +126,6 @@ func (r *Network) Constraint() constraint { return blockKind } -func (r *Network) Kind() string { - return tokNETWORK +func (r *Network) Kind() Kind { + return NETWORK } diff --git a/pkg/aa/parse.go b/pkg/aa/parse.go index b9d65eb9a..7d949e150 100644 --- a/pkg/aa/parse.go +++ b/pkg/aa/parse.go @@ -26,12 +26,16 @@ const ( var ( newRuleMap = map[string]func([]string) (Rule, error){ - tokCOMMENT: newComment, - tokABI: newAbi, - tokALIAS: newAlias, - tokINCLUDE: newInclude, + COMMENT.Tok(): newComment, + ABI.Tok(): newAbi, + ALIAS.Tok(): newAlias, + INCLUDE.Tok(): newInclude, } + tok = map[Kind]string{ + COMMENT: "#", + VARIABLE: "@{", + } openBlocks = []rune{tokOPENPAREN, tokOPENBRACE, tokOPENBRACKET} closeBlocks = []rune{tokCLOSEPAREN, tokCLOSEBRACE, tokCLOSEBRACKET} ) @@ -53,7 +57,7 @@ func tokenize(str string) []string { blockStack := []rune{} tokens := make([]string, 0, len(str)/2) - if len(str) > 2 && str[0:2] == tokVARIABLE { + if len(str) > 2 && str[0:2] == VARIABLE.Tok() { isVariable = true } for _, r := range str { @@ -122,7 +126,7 @@ func tokenToSlice(token string) []string { func tokensStripComment(tokens []string) []string { res := []string{} for _, v := range tokens { - if v == tokCOMMENT { + if v == COMMENT.Tok() { break } res = append(res, v) @@ -147,7 +151,7 @@ func newRules(rules [][]string) (Rules, error) { return nil, err } res = append(res, r) - } else if strings.HasPrefix(rule[0], tokVARIABLE) { + } else if strings.HasPrefix(rule[0], VARIABLE.Tok()) { r, err = newVariable(rule) if err != nil { return nil, err @@ -167,7 +171,7 @@ func (f *AppArmorProfileFile) parsePreamble(input []string) error { tokenizedRules := [][]string{} for _, line := range input { - if strings.HasPrefix(line, tokCOMMENT) { + if strings.HasPrefix(line, COMMENT.Tok()) { r, err = newComment(strings.Split(line, " ")) if err != nil { return err @@ -215,7 +219,7 @@ done: switch { case tmp == "": continue - case strings.HasPrefix(tmp, tokPROFILE): + case strings.HasPrefix(tmp, PROFILE.Tok()): rawHeader = tmp break done default: diff --git a/pkg/aa/pivot_root.go b/pkg/aa/pivot_root.go index 7c3f5ab8d..178b848d5 100644 --- a/pkg/aa/pivot_root.go +++ b/pkg/aa/pivot_root.go @@ -4,7 +4,7 @@ package aa -const tokPIVOTROOT = "pivot_root" +const PIVOTROOT = "pivot_root" type PivotRoot struct { RuleBase @@ -57,6 +57,6 @@ func (r *PivotRoot) Constraint() constraint { return blockKind } -func (r *PivotRoot) Kind() string { - return tokPIVOTROOT +func (r *PivotRoot) Kind() Kind { + return PIVOTROOT } diff --git a/pkg/aa/preamble.go b/pkg/aa/preamble.go index a2b68099f..d8cb58131 100644 --- a/pkg/aa/preamble.go +++ b/pkg/aa/preamble.go @@ -11,12 +11,13 @@ import ( ) const ( - tokABI = "abi" - tokALIAS = "alias" - tokINCLUDE = "include" + ABI Kind = "abi" + ALIAS Kind = "alias" + INCLUDE Kind = "include" + VARIABLE Kind = "variable" + COMMENT Kind = "comment" + tokIFEXISTS = "if exists" - tokVARIABLE = "@{" - tokCOMMENT = "#" ) type Comment struct { @@ -42,7 +43,7 @@ func (r *Comment) Equals(other any) bool { } func (r *Comment) String() string { - return renderTemplate("comment", r) + return renderTemplate(r.Kind(), r) } func (r *Comment) IsPreamble() bool { @@ -53,8 +54,8 @@ func (r *Comment) Constraint() constraint { return anyKind } -func (r *Comment) Kind() string { - return tokCOMMENT +func (r *Comment) Kind() Kind { + return COMMENT } type Abi struct { @@ -65,7 +66,7 @@ type Abi struct { func newAbi(rule []string) (Rule, error) { var magic bool - if len(rule) > 0 && rule[0] == tokABI { + if len(rule) > 0 && rule[0] == ABI.Tok() { rule = rule[1:] } if len(rule) != 1 { @@ -113,8 +114,8 @@ func (r *Abi) Constraint() constraint { return preambleKind } -func (r *Abi) Kind() string { - return tokABI +func (r *Abi) Kind() Kind { + return ABI } type Alias struct { @@ -124,7 +125,7 @@ type Alias struct { } func newAlias(rule []string) (Rule, error) { - if len(rule) > 0 && rule[0] == tokALIAS { + if len(rule) > 0 && rule[0] == ALIAS.Tok() { rule = rule[1:] } if len(rule) != 3 { @@ -165,8 +166,8 @@ func (r *Alias) Constraint() constraint { return preambleKind } -func (r *Alias) Kind() string { - return tokALIAS +func (r *Alias) Kind() Kind { + return ALIAS } type Include struct { @@ -180,7 +181,7 @@ func newInclude(rule []string) (Rule, error) { var magic bool var ifexists bool - if len(rule) > 0 && rule[0] == tokINCLUDE { + if len(rule) > 0 && rule[0] == INCLUDE.Tok() { rule = rule[1:] } @@ -239,8 +240,8 @@ func (r *Include) Constraint() constraint { return anyKind } -func (r *Include) Kind() string { - return tokINCLUDE +func (r *Include) Kind() Kind { + return INCLUDE } type Variable struct { @@ -257,7 +258,7 @@ func newVariable(rule []string) (Rule, error) { return nil, fmt.Errorf("invalid variable format: %v", rule) } - name := strings.Trim(rule[0], tokVARIABLE+"}") + name := strings.Trim(rule[0], VARIABLE.Tok()+"}") switch rule[1] { case tokEQUAL: define = true @@ -297,13 +298,13 @@ func (r *Variable) Equals(other any) bool { } func (r *Variable) String() string { - return renderTemplate("variable", r) + return renderTemplate(r.Kind(), r) } func (r *Variable) Constraint() constraint { return preambleKind } -func (r *Variable) Kind() string { - return tokVARIABLE +func (r *Variable) Kind() Kind { + return VARIABLE } diff --git a/pkg/aa/profile.go b/pkg/aa/profile.go index 8936bbef7..55488bfb1 100644 --- a/pkg/aa/profile.go +++ b/pkg/aa/profile.go @@ -14,13 +14,14 @@ import ( ) const ( + PROFILE Kind = "profile" + tokATTRIBUTES = "xattrs" tokFLAGS = "flags" - tokPROFILE = "profile" ) func init() { - requirements[tokPROFILE] = requirement{ + requirements[PROFILE] = requirement{ tokFLAGS: { "enforce", "complain", "kill", "default_allow", "unconfined", "prompt", "audit", "mediate_deleted", "attach_disconnected", @@ -52,7 +53,7 @@ func newHeader(rule []string) (Header, error) { if rule[len(rule)-1] == "{" { rule = rule[:len(rule)-1] } - if rule[0] == tokPROFILE { + if rule[0] == PROFILE.Tok() { rule = rule[1:] } @@ -120,8 +121,8 @@ func (p *Profile) Constraint() constraint { return blockKind } -func (p *Profile) Kind() string { - return tokPROFILE +func (p *Profile) Kind() Kind { + return PROFILE } // Merge merge similar rules together. diff --git a/pkg/aa/ptrace.go b/pkg/aa/ptrace.go index d73060edf..00eca5888 100644 --- a/pkg/aa/ptrace.go +++ b/pkg/aa/ptrace.go @@ -9,10 +9,10 @@ import ( "slices" ) -const tokPTRACE = "ptrace" +const PTRACE Kind = "ptrace" func init() { - requirements[tokPTRACE] = requirement{ + requirements[PTRACE] = requirement{ "access": []string{ "r", "w", "rw", "read", "readby", "trace", "tracedby", }, @@ -30,7 +30,7 @@ func newPtraceFromLog(log map[string]string) Rule { return &Ptrace{ RuleBase: newRuleFromLog(log), Qualifier: newQualifierFromLog(log), - Access: Must(toAccess(tokPTRACE, log["requested_mask"])), + Access: Must(toAccess(PTRACE, log["requested_mask"])), Peer: log["peer"], } } @@ -67,6 +67,6 @@ func (r *Ptrace) Constraint() constraint { return blockKind } -func (r *Ptrace) Kind() string { - return tokPTRACE +func (r *Ptrace) Kind() Kind { + return PTRACE } diff --git a/pkg/aa/resolve.go b/pkg/aa/resolve.go index 9a2467462..df97c2aa5 100644 --- a/pkg/aa/resolve.go +++ b/pkg/aa/resolve.go @@ -59,7 +59,7 @@ func (f *AppArmorProfileFile) Resolve() error { } func (f *AppArmorProfileFile) resolveValues(input string) ([]string, error) { - if !strings.Contains(input, tokVARIABLE) { + if !strings.Contains(input, VARIABLE.Tok()) { return []string{input}, nil } @@ -76,7 +76,7 @@ func (f *AppArmorProfileFile) resolveValues(input string) ([]string, error) { if vrbl.Name == varname { found = true for _, v := range vrbl.Values { - if strings.Contains(v, tokVARIABLE+varname+"}") { + if strings.Contains(v, VARIABLE.Tok()+varname+"}") { return nil, fmt.Errorf("recursive variable found in: %s", varname) } newValues := strings.ReplaceAll(input, variable, v) @@ -152,7 +152,7 @@ func (f *AppArmorProfileFile) resolveInclude(include *Include) error { } // Remove all includes in iFile - iFile.Preamble = iFile.Preamble.DeleteKind(tokINCLUDE) + iFile.Preamble = iFile.Preamble.DeleteKind(INCLUDE) // Cache the included file includeCache[include] = iFile diff --git a/pkg/aa/rlimit.go b/pkg/aa/rlimit.go index d3a290490..ddb70710c 100644 --- a/pkg/aa/rlimit.go +++ b/pkg/aa/rlimit.go @@ -7,12 +7,11 @@ package aa import "fmt" const ( - tokRLIMIT = "rlimit" - tokSET = "set" + RLIMIT Kind = "rlimit" ) func init() { - requirements[tokRLIMIT] = requirement{ + requirements[RLIMIT] = requirement{ "keys": { "cpu", "fsize", "data", "stack", "core", "rss", "nofile", "ofile", "as", "nproc", "memlock", "locks", "sigpending", "msgqueue", "nice", @@ -68,6 +67,6 @@ func (r *Rlimit) Constraint() constraint { return blockKind } -func (r *Rlimit) Kind() string { - return tokRLIMIT +func (r *Rlimit) Kind() Kind { + return RLIMIT } diff --git a/pkg/aa/rules.go b/pkg/aa/rules.go index 4bd1b61ed..79db32e07 100644 --- a/pkg/aa/rules.go +++ b/pkg/aa/rules.go @@ -26,6 +26,20 @@ const ( blockKind // The rule can only be found in a profile ) +// Kind represents an AppArmor rule kind. +type Kind string + +func (k Kind) String() string { + return string(k) +} + +func (k Kind) Tok() string { + if t, ok := tok[k]; ok { + return t + } + return string(k) +} + // Rule generic interface for all AppArmor rules type Rule interface { Validate() error @@ -33,7 +47,7 @@ type Rule interface { Equals(other any) bool String() string Constraint() constraint - Kind() string + Kind() Kind } type Rules []Rule @@ -77,7 +91,7 @@ func (r Rules) Delete(i int) Rules { return append(r[:i], r[i+1:]...) } -func (r Rules) DeleteKind(kind string) Rules { +func (r Rules) DeleteKind(kind Kind) Rules { res := make(Rules, 0) for _, rule := range r { if rule.Kind() != kind { @@ -87,7 +101,7 @@ func (r Rules) DeleteKind(kind string) Rules { return res } -func (r Rules) Filter(filter string) Rules { +func (r Rules) Filter(filter Kind) Rules { res := make(Rules, 0) for _, rule := range r { if rule.Kind() != filter { @@ -128,12 +142,12 @@ func Must[T any](v T, err error) T { return v } -func validateValues(rule string, key string, values []string) error { +func validateValues(kind Kind, key string, values []string) error { for _, v := range values { if v == "" { continue } - if !slices.Contains(requirements[rule][key], v) { + if !slices.Contains(requirements[kind][key], v) { return fmt.Errorf("invalid mode '%s'", v) } } @@ -142,10 +156,10 @@ func validateValues(rule string, key string, values []string) error { // Helper function to convert a string to a slice of rule values according to // the rule requirements as defined in the requirements map. -func toValues(rule string, key string, input string) ([]string, error) { - req, ok := requirements[rule][key] +func toValues(kind Kind, key string, input string) ([]string, error) { + req, ok := requirements[kind][key] if !ok { - return nil, fmt.Errorf("unrecognized requirement '%s' for rule %s", key, rule) + return nil, fmt.Errorf("unrecognized requirement '%s' for rule %s", key, kind) } res := tokenToSlice(input) @@ -156,22 +170,22 @@ func toValues(rule string, key string, input string) ([]string, error) { } } slices.SortFunc(res, func(i, j string) int { - return requirementsWeights[rule][key][i] - requirementsWeights[rule][key][j] + return requirementsWeights[kind][key][i] - requirementsWeights[kind][key][j] }) return slices.Compact(res), nil } // Helper function to convert an access string to a slice of access according to // the rule requirements as defined in the requirements map. -func toAccess(rule string, input string) ([]string, error) { +func toAccess(kind Kind, input string) ([]string, error) { var res []string - switch rule { - case tokFILE: + switch kind { + case FILE: raw := strings.Split(input, "") trans := []string{} for _, access := range raw { - if slices.Contains(requirements[tokFILE]["access"], access) { + if slices.Contains(requirements[FILE]["access"], access) { res = append(res, access) } else { trans = append(trans, access) @@ -180,17 +194,17 @@ func toAccess(rule string, input string) ([]string, error) { transition := strings.Join(trans, "") if len(transition) > 0 { - if slices.Contains(requirements[tokFILE]["transition"], transition) { + if slices.Contains(requirements[FILE]["transition"], transition) { res = append(res, transition) } else { return nil, fmt.Errorf("unrecognized transition: %s", transition) } } - case tokFILE + "-log": + case FILE + "-log": raw := strings.Split(input, "") for _, access := range raw { - if slices.Contains(requirements[tokFILE]["access"], access) { + if slices.Contains(requirements[FILE]["access"], access) { res = append(res, access) } else if maskToAccess[access] != "" { res = append(res, maskToAccess[access]) @@ -200,7 +214,7 @@ func toAccess(rule string, input string) ([]string, error) { } default: - return toValues(rule, "access", input) + return toValues(kind, "access", input) } slices.SortFunc(res, cmpFileAccess) diff --git a/pkg/aa/signal.go b/pkg/aa/signal.go index ace95d79c..4e7ce91cd 100644 --- a/pkg/aa/signal.go +++ b/pkg/aa/signal.go @@ -9,10 +9,10 @@ import ( "slices" ) -const tokSIGNAL = "signal" +const SIGNAL Kind = "signal" func init() { - requirements[tokSIGNAL] = requirement{ + requirements[SIGNAL] = requirement{ "access": { "r", "w", "rw", "read", "write", "send", "receive", }, @@ -44,7 +44,7 @@ func newSignalFromLog(log map[string]string) Rule { return &Signal{ RuleBase: newRuleFromLog(log), Qualifier: newQualifierFromLog(log), - Access: Must(toAccess(tokSIGNAL, log["requested_mask"])), + Access: Must(toAccess(SIGNAL, log["requested_mask"])), Set: []string{log["signal"]}, Peer: log["peer"], } @@ -88,6 +88,6 @@ func (r *Signal) Constraint() constraint { return blockKind } -func (r *Signal) Kind() string { - return tokSIGNAL +func (r *Signal) Kind() Kind { + return SIGNAL } diff --git a/pkg/aa/template.go b/pkg/aa/template.go index 10d1c1616..aed36f371 100644 --- a/pkg/aa/template.go +++ b/pkg/aa/template.go @@ -25,7 +25,7 @@ var ( // The functions available in the template tmplFunctionMap = template.FuncMap{ - "typeof": typeOf, + "kindof": kindOf, "join": join, "cjoin": cjoin, "indent": indent, @@ -34,24 +34,25 @@ var ( } // The apparmor templates - tmpl = generateTemplates([]string{ + tmpl = generateTemplates([]Kind{ // Global templates "apparmor", - tokPROFILE, + PROFILE, + HAT, "rules", // Preamble templates - tokABI, - tokALIAS, - tokINCLUDE, - "variable", - "comment", + ABI, + ALIAS, + INCLUDE, + VARIABLE, + COMMENT, // Rules templates - tokALL, tokRLIMIT, tokUSERNS, tokCAPABILITY, tokNETWORK, - tokMOUNT, tokREMOUNT, tokUMOUNT, tokPIVOTROOT, tokCHANGEPROFILE, - tokMQUEUE, tokIOURING, tokUNIX, tokPTRACE, tokSIGNAL, tokDBUS, - tokFILE, tokLINK, + ALL, RLIMIT, USERNS, CAPABILITY, NETWORK, + MOUNT, REMOUNT, UMOUNT, PIVOTROOT, CHANGEPROFILE, + MQUEUE, IOURING, UNIX, PTRACE, SIGNAL, DBUS, + FILE, LINK, }) // convert apparmor requested mask to apparmor access mode @@ -64,27 +65,28 @@ var ( } // The order the apparmor rules should be sorted - ruleAlphabet = []string{ - "include", - "all", - "rlimit", - "userns", - "capability", - "network", - "mount", - "remount", - "umount", - "pivotroot", - "changeprofile", - "mqueue", - "iouring", - "signal", - "ptrace", - "unix", - "dbus", - "file", - "link", - "profile", + ruleAlphabet = []Kind{ + INCLUDE, + ALL, + RLIMIT, + USERNS, + CAPABILITY, + NETWORK, + MOUNT, + REMOUNT, + UMOUNT, + PIVOTROOT, + CHANGEPROFILE, + MQUEUE, + IOURING, + SIGNAL, + PTRACE, + UNIX, + DBUS, + FILE, + LINK, + PROFILE, + HAT, "include_if_exists", } ruleWeights = generateWeights(ruleAlphabet) @@ -117,16 +119,16 @@ var ( fileWeights = generateWeights(fileAlphabet) // The order the rule values (access, type, domains, etc) should be sorted - requirements = map[string]requirement{} - requirementsWeights map[string]map[string]map[string]int + requirements = map[Kind]requirement{} + requirementsWeights map[Kind]map[string]map[string]int ) func init() { requirementsWeights = generateRequirementsWeights(requirements) } -func generateTemplates(names []string) map[string]*template.Template { - res := make(map[string]*template.Template, len(names)) +func generateTemplates(names []Kind) map[Kind]*template.Template { + res := make(map[Kind]*template.Template, len(names)) base := template.New("").Funcs(tmplFunctionMap) base = template.Must(base.ParseFS(tmplFiles, "templates/*.j2", "templates/rule/*.j2", @@ -141,11 +143,11 @@ func generateTemplates(names []string) map[string]*template.Template { return res } -func renderTemplate(name string, data any) string { +func renderTemplate(name Kind, data any) string { var res strings.Builder template, ok := tmpl[name] if !ok { - panic("template '" + name + "' not found") + panic("template '" + name.String() + "' not found") } err := template.Execute(&res, data) if err != nil { @@ -154,16 +156,16 @@ func renderTemplate(name string, data any) string { return res.String() } -func generateWeights(alphabet []string) map[string]int { - res := make(map[string]int, len(alphabet)) +func generateWeights[T Kind | string](alphabet []T) map[T]int { + res := make(map[T]int, len(alphabet)) for i, r := range alphabet { res[r] = i } return res } -func generateRequirementsWeights(requirements map[string]requirement) map[string]map[string]map[string]int { - res := make(map[string]map[string]map[string]int, len(requirements)) +func generateRequirementsWeights(requirements map[Kind]requirement) map[Kind]map[string]map[string]int { + res := make(map[Kind]map[string]map[string]int, len(requirements)) for rule, req := range requirements { res[rule] = make(map[string]map[string]int, len(req)) for key, values := range req { @@ -207,15 +209,11 @@ func cjoin(i any) string { } } -func typeOf(i any) string { +func kindOf(i any) string { if i == nil { return "" } - return strings.TrimPrefix(reflect.TypeOf(i).String(), "*aa.") -} - -func typeToValue(i reflect.Type) string { - return strings.ToLower(strings.TrimPrefix(i.String(), "*aa.")) + return i.(Rule).Kind().String() } func setindent(i string) string { diff --git a/pkg/aa/templates/rules.j2 b/pkg/aa/templates/rules.j2 index 4b66ab38a..9a6113325 100644 --- a/pkg/aa/templates/rules.j2 +++ b/pkg/aa/templates/rules.j2 @@ -4,118 +4,118 @@ {{- define "rules" -}} - {{- $oldtype := "" -}} + {{- $oldkind := "" -}} {{- range . -}} - {{- $type := typeof . -}} - {{- if eq $type "" -}} + {{- $kind := kindof . -}} + {{- if eq $kind "" -}} {{- "\n" -}} {{- continue -}} {{- end -}} - {{- if eq $type "Comment" -}} + {{- if eq $kind "comment" -}} {{- template "comment" . -}} {{- "\n" -}} {{- continue -}} {{- end -}} - {{- if and (ne $type $oldtype) (ne $oldtype "") -}} + {{- if and (ne $kind $oldkind) (ne $oldkind "") -}} {{- "\n" -}} {{- end -}} {{- indent "" -}} - {{- if eq $type "Abi" -}} + {{- if eq $kind "abi" -}} {{- template "abi" . -}} {{- end -}} - {{- if eq $type "Alias" -}} + {{- if eq $kind "alias" -}} {{- template "alias" . -}} {{- end -}} - {{- if eq $type "Include" -}} + {{- if eq $kind "include" -}} {{- template "include" . -}} {{- end -}} - {{- if eq $type "Variable" -}} + {{- if eq $kind "variable" -}} {{- template "variable" . -}} {{- end -}} - {{- if eq $type "All" -}} + {{- if eq $kind "all" -}} {{- template "all" . -}} {{- end -}} - {{- if eq $type "Rlimit" -}} + {{- if eq $kind "rlimit" -}} {{- template "rlimit" . -}} {{- end -}} - {{- if eq $type "Userns" -}} + {{- if eq $kind "userns" -}} {{- template "userns" . -}} {{- end -}} - {{- if eq $type "Capability" -}} + {{- if eq $kind "capability" -}} {{- template "capability" . -}} {{- end -}} - {{- if eq $type "Network" -}} + {{- if eq $kind "network" -}} {{- template "network" . -}} {{- end -}} - {{- if eq $type "Mount" -}} + {{- if eq $kind "mount" -}} {{- template "mount" . -}} {{- end -}} - {{- if eq $type "Remount" -}} + {{- if eq $kind "remount" -}} {{- template "remount" . -}} {{- end -}} - {{- if eq $type "Umount" -}} + {{- if eq $kind "umount" -}} {{- template "umount" . -}} {{- end -}} - {{- if eq $type "PivotRoot" -}} + {{- if eq $kind "pivot_root" -}} {{- template "pivot_root" . -}} {{- end -}} - {{- if eq $type "ChangeProfile" -}} + {{- if eq $kind "change_profile" -}} {{- template "change_profile" . -}} {{- end -}} - {{- if eq $type "Mqueue" -}} + {{- if eq $kind "mqueue" -}} {{- template "mqueue" . -}} {{- end -}} - {{- if eq $type "IOUring" -}} + {{- if eq $kind "io_uring" -}} {{- template "io_uring" . -}} {{- end -}} - {{- if eq $type "Unix" -}} + {{- if eq $kind "unix" -}} {{- template "unix" . -}} {{- end -}} - {{- if eq $type "Ptrace" -}} + {{- if eq $kind "ptrace" -}} {{- template "ptrace" . -}} {{- end -}} - {{- if eq $type "Signal" -}} + {{- if eq $kind "signal" -}} {{- template "signal" . -}} {{- end -}} - {{- if eq $type "Dbus" -}} + {{- if eq $kind "dbus" -}} {{- template "dbus" . -}} {{- end -}} - {{- if eq $type "File" -}} + {{- if eq $kind "file" -}} {{- template "file" . -}} {{- end -}} - {{- if eq $type "Link" -}} + {{- if eq $kind "link" -}} {{- template "link" . -}} {{- end -}} - {{- if eq $type "Profile" -}} + {{- if eq $kind "profile" -}} {{- template "profile" . -}} {{- end -}} {{- "\n" -}} - {{- $oldtype = $type -}} + {{- $oldkind = $kind -}} {{- end -}} {{- end -}} diff --git a/pkg/aa/unix.go b/pkg/aa/unix.go index eefe049f9..b868459b0 100644 --- a/pkg/aa/unix.go +++ b/pkg/aa/unix.go @@ -9,10 +9,10 @@ import ( "slices" ) -const tokUNIX = "unix" +const UNIX Kind = "unix" func init() { - requirements[tokUNIX] = requirement{ + requirements[UNIX] = requirement{ "access": []string{ "create", "bind", "listen", "accept", "connect", "shutdown", "getattr", "setattr", "getopt", "setopt", "send", "receive", @@ -39,7 +39,7 @@ func newUnixFromLog(log map[string]string) Rule { return &Unix{ RuleBase: newRuleFromLog(log), Qualifier: newQualifierFromLog(log), - Access: Must(toAccess(tokUNIX, log["requested_mask"])), + Access: Must(toAccess(UNIX, log["requested_mask"])), Type: log["sock_type"], Protocol: log["protocol"], Address: log["addr"], @@ -107,6 +107,6 @@ func (r *Unix) Constraint() constraint { return blockKind } -func (r *Unix) Kind() string { - return tokUNIX +func (r *Unix) Kind() Kind { + return UNIX } diff --git a/pkg/aa/userns.go b/pkg/aa/userns.go index e84093132..4c678f3d0 100644 --- a/pkg/aa/userns.go +++ b/pkg/aa/userns.go @@ -4,7 +4,7 @@ package aa -const tokUSERNS = "userns" +const USERNS Kind = "userns" type Userns struct { RuleBase @@ -45,6 +45,6 @@ func (r *Userns) Constraint() constraint { return blockKind } -func (r *Userns) Kind() string { - return tokUSERNS +func (r *Userns) Kind() Kind { + return USERNS }