diff --git a/common/pkg/apparmor/apparmor_linux_template.go b/common/pkg/apparmor/apparmor_linux_template.go index 8f2baa72e5..cec703b8af 100644 --- a/common/pkg/apparmor/apparmor_linux_template.go +++ b/common/pkg/apparmor/apparmor_linux_template.go @@ -21,6 +21,8 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) { # Allow signals from privileged profiles and from within the same profile signal (receive) peer=unconfined, signal (send,receive) peer={{.Name}}, + # With AppArmor 5.0+, child processes get a stacked profile. + signal (send,receive) peer={{.Name}}//&*, # Allow certain signals from OCI runtimes (podman, runc and crun) signal (receive) peer={/usr/bin/,/usr/sbin/,}runc, signal (receive) peer={/usr/bin/,/usr/sbin/,}crun*, @@ -48,6 +50,8 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) { {{if ge .Version 208095}} # suppress ptrace denials when using 'ps' inside a container ptrace (trace,read) peer={{.Name}}, + # With AppArmor 5.0+, child processes get a stacked profile. + ptrace (trace,read) peer={{.Name}}//&*, {{end}} } ` diff --git a/common/pkg/apparmor/apparmor_linux_test.go b/common/pkg/apparmor/apparmor_linux_test.go index 15ea2bee53..368cf46c76 100644 --- a/common/pkg/apparmor/apparmor_linux_test.go +++ b/common/pkg/apparmor/apparmor_linux_test.go @@ -3,8 +3,11 @@ package apparmor import ( + "bytes" "os" + "strings" "testing" + "text/template" ) type versionExpected struct { @@ -130,6 +133,32 @@ func TestInstallDefault(t *testing.T) { checkLoaded(false) } +func TestStackedProfileRules(t *testing.T) { + compiled, err := template.New("apparmor_profile").Parse(defaultProfileTemplate) + if err != nil { + t.Fatalf("Failed to parse template: %v", err) + } + + p := profileData{ + Name: "test-profile", + Version: 300000, + } + + var buf bytes.Buffer + if err := compiled.Execute(&buf, p); err != nil { + t.Fatalf("Failed to execute template: %v", err) + } + + output := buf.String() + + if !strings.Contains(output, "signal (send,receive) peer=test-profile//&*,") { + t.Error("Missing stacked profile signal rule") + } + if !strings.Contains(output, "ptrace (trace,read) peer=test-profile//&*,") { + t.Error("Missing stacked profile ptrace rule") + } +} + func TestDefaultContent(t *testing.T) { if _, err := os.Stat(aapath); err != nil { t.Skip("AppArmor isn't available in this environment")