feat(aa-log): add a new apparmor profile struct
Also rewrite variables resolution to this new struct.
This commit is contained in:
parent
b2d093e125
commit
a8470dfa38
5 changed files with 379 additions and 156 deletions
|
|
@ -2,104 +2,19 @@
|
|||
// Copyright (C) 2023 Alexandre Pujol <alexandre@pujol.io>
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
// Warning: this is purposely not using a Yacc parser. Its only aim is to
|
||||
// extract variables and attachments for apparmor.d profile
|
||||
|
||||
package aa
|
||||
// AppArmorProfiles represents a full set of apparmor profiles
|
||||
type AppArmorProfiles map[string]*AppArmorProfile
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
var (
|
||||
regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`)
|
||||
regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`)
|
||||
|
||||
// Tunables
|
||||
Tunables = map[string][]string{
|
||||
"bin": {"/{usr/,}{s,}bin"},
|
||||
"lib": {"/{usr/,}lib{,exec,32,64}"},
|
||||
"multiarch": {"*-linux-gnu*"},
|
||||
"user_share_dirs": {"/home/*/.local/share"},
|
||||
"etc_ro": {"/{usr/,}etc/"},
|
||||
}
|
||||
)
|
||||
|
||||
// ApparmorProfile represents a full apparmor profile.
|
||||
// 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 writting profile, not parsing it.
|
||||
type AppArmorProfile struct {
|
||||
Variables map[string][]string
|
||||
Attachments []string
|
||||
Preamble
|
||||
Profile
|
||||
}
|
||||
|
||||
func NewAppArmorProfile() *AppArmorProfile {
|
||||
variables := make(map[string][]string)
|
||||
maps.Copy(variables, Tunables)
|
||||
return &AppArmorProfile{
|
||||
Variables: variables,
|
||||
Attachments: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseVariables extract all variables from the profile
|
||||
func (p *AppArmorProfile) ParseVariables(content string) {
|
||||
matches := regVariablesDef.FindAllStringSubmatch(content, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) > 2 {
|
||||
key := match[1]
|
||||
values := match[2]
|
||||
if _, ok := p.Variables[key]; ok {
|
||||
p.Variables[key] = append(p.Variables[key], strings.Split(values, " ")...)
|
||||
} else {
|
||||
p.Variables[key] = strings.Split(values, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve recursively resolves all variables references
|
||||
func (p *AppArmorProfile) 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 _, value := range p.Variables[varname] {
|
||||
newVar := strings.ReplaceAll(str, variable, value)
|
||||
vars = append(vars, p.resolve(newVar)...)
|
||||
}
|
||||
} else {
|
||||
vars = append(vars, str)
|
||||
}
|
||||
return vars
|
||||
}
|
||||
return []string{str}
|
||||
}
|
||||
|
||||
// ResolveAttachments resolve profile attachments defined in exec_path
|
||||
func (p *AppArmorProfile) ResolveAttachments() {
|
||||
for _, exec := range p.Variables["exec_path"] {
|
||||
p.Attachments = append(p.Attachments, p.resolve(exec)...)
|
||||
}
|
||||
}
|
||||
|
||||
// NestAttachments return a nested attachment string
|
||||
func (p *AppArmorProfile) NestAttachments() string {
|
||||
if len(p.Attachments) == 0 {
|
||||
return ""
|
||||
} else if len(p.Attachments) == 1 {
|
||||
return p.Attachments[0]
|
||||
} else {
|
||||
res := []string{}
|
||||
for _, attachment := range p.Attachments {
|
||||
if strings.HasPrefix(attachment, "/") {
|
||||
res = append(res, attachment[1:])
|
||||
} else {
|
||||
res = append(res, attachment)
|
||||
}
|
||||
}
|
||||
return "/{" + strings.Join(res, ",") + "}"
|
||||
}
|
||||
return &AppArmorProfile{}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue