Skip to content

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

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions