Skip to content

AppArmor profile may still break on valid signals with default AppArmor 4+ #805

@fice-t

Description

@fice-t

Related:
containers/common#1898
containers/common#2321
containers/common#2228
containers/crun#1385

Background

Since crun 1.14, crun uses AppArmor profile stacking when the container has --no-new-privileges set. However, the containers-default AppArmor profile in this repository was not updated to consider these stacked profiles.

This, combined with AppArmor 4 providing a crun profile by default, results in broken signal handling in some circumstances.

A simple illustration of how the stacked profile presents itself on Debian 13 (which now includes the upstream crun profile):

$ podman run --rm -it alpine cat /proc/self/attr/apparmor/current
containers-default-0.62.2 (enforce)

$ podman run --rm -it --security-opt no-new-privileges alpine cat /proc/self/attr/apparmor/current
containers-default-0.62.2//&crun (mixed)

Reproduction

A simple test of attempting to kill a process from a session inside the container:

$ podman run --rm -d --security-opt no-new-privileges --name test alpine sleep inf
$ podman exec -it test sh
# kill 1
sh: can't kill pid 1: Permission denied

With the audit messages:

apparmor="DENIED" operation="signal" class="signal" profile="containers-default-0.62.2" pid=17219 comm="sh" requested_mask="send" denied_mask="send" signal=term peer="containers-default-0.62.2//&crun"
apparmor="DENIED" operation="signal" class="signal" profile="containers-default-0.62.2" pid=17219 comm="sh" requested_mask="receive" denied_mask="receive" signal=term peer="containers-default-0.62.2//&crun"

Removing --security-opt no-new-privileges makes the above kill succeed.

Also, removing the upstream crun profile makes the above succeed even with no-new-privileges: sudo apparmor_parser -R /etc/apparmor.d/crun

Motivation

I encountered this after upgrading from Debian 12->13 (AppArmor 3->4) and using a Jellyfin container. With no-new-privileges enabled, using a certain Jellyfin client results in a reproducible crash with the following errors:

apparmor="DENIED" operation="signal" class="signal" profile="containers-default-0.62.2" pid=15124 comm=2E4E455420424743 requested_mask="send" denied_mask="send" signal=rtmin+2 peer="containers-default-0.62.2//&crun"
apparmor="DENIED" operation="signal" class="signal" profile="containers-default-0.62.2" pid=15124 comm=2E4E455420424743 requested_mask="receive" denied_mask="receive" signal=rtmin+2 peer="containers-default-0.62.2//&crun"
apparmor="DENIED" operation="signal" class="signal" profile="containers-default-0.62.2" pid=15124 comm=2E4E455420424743 requested_mask="send" denied_mask="send" signal=abrt peer="containers-default-0.62.2//&crun"
traps: .NET TP Worker[14848] general protection fault ip:7f79c2ecf50f sp:7f3895aefea0 error:0 in libc.so.6[2650f,7f79c2ecf000+155000]

Removing the crun AppArmor profile, as above, has seemingly eliminated the crash.

Solution

Similar to containerd/containerd#12886, I think that allowing signals from/to stacked profiles is the proper solution:

signal (send,receive) peer={{.Name}}//&*

Metadata

Metadata

Assignees

No one assigned

    Labels

    commonRelated to "common" package

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions