build: reorganise build: abi4, fallback, prebuild cli

- ABI4 by default, fallback to abi 3.
- aa-prebuild cli that can be used by other project shipping profiles.
- --file option to cli to only build one dev profile.
- add abi version filter to only & exclude directives.
This commit is contained in:
Alexandre Pujol 2024-10-02 16:22:46 +01:00
parent d6b7bef89e
commit 59ac54e2fc
No known key found for this signature in database
GPG key ID: C5469996F0DF68EC
39 changed files with 473 additions and 440 deletions

181
pkg/prebuild/cli/cli.go Normal file
View file

@ -0,0 +1,181 @@
// apparmor.d - Full set of apparmor profiles
// Copyright (C) 2023-2024 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
package cli
import (
"flag"
"fmt"
"strings"
"github.com/roddhjav/apparmor.d/pkg/logging"
"github.com/roddhjav/apparmor.d/pkg/paths"
"github.com/roddhjav/apparmor.d/pkg/prebuild"
"github.com/roddhjav/apparmor.d/pkg/prebuild/builder"
"github.com/roddhjav/apparmor.d/pkg/prebuild/directive"
"github.com/roddhjav/apparmor.d/pkg/prebuild/prepare"
"github.com/roddhjav/apparmor.d/pkg/util"
)
const (
nilABI uint = 0
usage = `aa-prebuild [-h] [--complain | --enforce] [--full] [--abi 3|4]
Prebuild apparmor.d profiles for a given distribution and apply
internal built-in directives.
Options:
-h, --help Show this help message and exit.
-c, --complain Set complain flag on all profiles.
-e, --enforce Set enforce flag on all profiles.
-a, --abi ABI Target apparmor ABI.
-f, --full Set AppArmor for full system policy.
-F, --file Only prebuild a given file.
`
)
var (
help bool
complain bool
enforce bool
full bool
abi uint
file string
)
func init() {
flag.BoolVar(&help, "h", false, "Show this help message and exit.")
flag.BoolVar(&help, "help", false, "Show this help message and exit.")
flag.BoolVar(&full, "f", false, "Set AppArmor for full system policy.")
flag.BoolVar(&full, "full", false, "Set AppArmor for full system policy.")
flag.BoolVar(&complain, "c", false, "Set complain flag on all profiles.")
flag.BoolVar(&complain, "complain", false, "Set complain flag on all profiles.")
flag.BoolVar(&enforce, "e", false, "Set enforce flag on all profiles.")
flag.BoolVar(&enforce, "enforce", false, "Set enforce flag on all profiles.")
flag.UintVar(&abi, "a", nilABI, "Target apparmor ABI.")
flag.UintVar(&abi, "abi", nilABI, "Target apparmor ABI.")
flag.StringVar(&file, "F", "", "Only prebuild a given file.")
flag.StringVar(&file, "file", "", "Only prebuild a given file.")
}
func Prebuild() {
flag.Usage = func() {
fmt.Printf("%s%s\n%s\n%s", usage,
prebuild.Help("Prepare", prepare.Tasks),
prebuild.Help("Build", builder.Builders),
directive.Usage(),
)
}
flag.Parse()
if help {
flag.Usage()
return
}
logging.Step("Building apparmor.d profiles for %s.", prebuild.Distribution)
if full {
prepare.Register("fsp")
builder.Register("fsp")
} else {
prepare.Register("systemd-early")
}
if complain {
builder.Register("complain")
} else if enforce {
builder.Register("enforce")
}
switch abi {
case 3:
prebuild.ABI = 3
builder.Register("abi3")
case 4:
prebuild.ABI = 4
for i, b := range builder.Builds {
if b.Name() == "abi3" {
builder.Builds = append(builder.Builds[:i], builder.Builds[i+1:]...)
break
}
}
case nilABI:
default:
logging.Fatal("ABI %d not supported", abi)
}
if file != "" {
sync, _ := prepare.Tasks["synchronise"].(*prepare.Synchronise)
sync.Path = file
configure, _ := prepare.Tasks["configure"].(*prepare.Configure)
configure.OneFile = true
}
if err := Prepare(); err != nil {
logging.Fatal("%s", err.Error())
}
if err := Build(); err != nil {
logging.Fatal("%s", err.Error())
}
}
func Prepare() error {
for _, task := range prepare.Prepares {
msg, err := task.Apply()
if err != nil {
return err
}
if file != "" && task.Name() == "setflags" {
continue
}
logging.Success("%s", task.Message())
logging.Indent = " "
for _, line := range msg {
if strings.Contains(line, "not found") {
logging.Warning("%s", line)
} else {
logging.Bullet("%s", line)
}
}
logging.Indent = ""
}
return nil
}
func Build() error {
files, _ := prebuild.RootApparmord.ReadDirRecursiveFiltered(nil, paths.FilterOutDirectories())
for _, file := range files {
if !file.Exist() {
continue
}
profile, err := util.ReadFile(file)
if err != nil {
return err
}
profile, err = builder.Run(file, profile)
if err != nil {
return err
}
profile, err = directive.Run(file, profile)
if err != nil {
return err
}
if err := file.WriteFile([]byte(profile)); err != nil {
return err
}
}
logging.Success("Build tasks:")
logging.Indent = " "
for _, task := range builder.Builds {
logging.Bullet("%s", task.Message())
}
logging.Indent = ""
logging.Success("Directives processed:")
logging.Indent = " "
for _, dir := range directive.Directives {
logging.Bullet("%s%s", directive.Keyword, dir.Name())
}
logging.Indent = ""
return nil
}

View file

@ -0,0 +1,110 @@
// apparmor.d - Full set of apparmor profiles
// Copyright (C) 2023-2024 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
package cli
import (
"os"
"os/exec"
"testing"
"github.com/roddhjav/apparmor.d/pkg/paths"
"github.com/roddhjav/apparmor.d/pkg/prebuild"
"github.com/roddhjav/apparmor.d/pkg/prebuild/builder"
"github.com/roddhjav/apparmor.d/pkg/prebuild/prepare"
)
func setTestBuildDirectories(name string) {
testRoot := paths.New("/tmp/tests")
prebuild.Root = testRoot.Join(name)
prebuild.RootApparmord = prebuild.Root.Join("apparmor.d")
}
func chdirGitRoot() {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
out, err := cmd.Output()
if err != nil {
panic(err)
}
root := string(out)[0 : len(out)-1]
if err := os.Chdir(root); err != nil {
panic(err)
}
}
func Test_Prebuild(t *testing.T) {
tests := []struct {
name string
wantErr bool
full bool
complain bool
enforce bool
dist string
}{
{
name: "Build for Archlinux",
wantErr: false,
full: false,
complain: true,
enforce: false,
dist: "arch",
},
{
name: "Build for Ubuntu",
wantErr: false,
full: true,
complain: false,
enforce: true,
dist: "ubuntu",
},
{
name: "Build for Debian",
wantErr: false,
full: true,
complain: false,
enforce: false,
dist: "debian",
},
{
name: "Build for OpenSUSE Tumbleweed",
wantErr: false,
full: true,
complain: true,
enforce: false,
dist: "opensuse",
},
}
chdirGitRoot()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
setTestBuildDirectories(tt.name)
prebuild.Distribution = tt.dist
prepare.Prepares = []prepare.Task{}
prepare.Register(
"synchronise", "ignore", "merge",
"configure", "setflags", "systemd-default",
)
if full {
prepare.Register("fsp")
builder.Register("fsp")
} else {
prepare.Register("systemd-early")
}
if complain {
builder.Register("complain")
} else if enforce {
builder.Register("enforce")
}
if err := Prepare(); (err != nil) != tt.wantErr {
t.Errorf("Prepare() error = %v, wantErr %v", err, tt.wantErr)
}
if err := Build(); (err != nil) != tt.wantErr {
t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}