Skip to content

Instantly share code, notes, and snippets.

@tomhepz
Created December 12, 2025 16:31
Show Gist options
  • Select an option

  • Save tomhepz/c629ed09f9b94c74f13417f83ecb9ef1 to your computer and use it in GitHub Desktop.

Select an option

Save tomhepz/c629ed09f9b94c74f13417f83ecb9ef1 to your computer and use it in GitHub Desktop.
Apparmor notes for Artiq FPGA compilation on new ubuntu

Useful Links

https://documentation.ubuntu.com/server/how-to/security/apparmor/

https://git.m-labs.hk/sinara-hw/assembly/src/branch/master/src/sw_sup/setup_build_pc.md#installation

https://forum.m-labs.hk/d/903-can-not-access-to-vivado-when-building-the-artiq

Refer to dodgy ways (completely dissabling Apparmor):

https://www.reddit.com/r/gamemaker/comments/1filv6k/completely_disabling_apparmor_in_ubuntu_for/

https://askubuntu.com/questions/1511854/how-to-permanently-disable-ubuntus-new-apparmor-user-namespace-creation-restric

https://askubuntu.com/questions/1536716/why-cant-i-disable-apparmor-on-24-04-1

Instructions

Below is a repeatable “Ubuntu + Nix + bwrap” setup that gets FPGA/Nix builds working without turning AppArmor off system-wide.

What Ubuntu changed: on newer Ubuntu, unprivileged processes can only create user namespaces if they’re confined and their AppArmor profile allows userns,. Otherwise the kernel forces the action into a restrictive fallback profile (unprivileged_userns) and you see errors like setpcap / uid_map denied. (Ubuntu)

Your machine has both:

  • /usr/bin/bwrap (system)
  • /nix/store/.../bin/bwrap (Nix-provided) — you confirmed at least two store copies

So you need AppArmor coverage for both bwrap locations, plus a “label” for Nix itself.


0) Confirm which binaries you’re dealing with

command -v nix
readlink -f "$(command -v nix)"

command -v bwrap
readlink -f "$(command -v bwrap)"

sudo find /nix/store -type f -path '*/bin/bwrap' -name bwrap 2>/dev/null | head -n 20

1) Install the AppArmor helper packages (profiles + tools)

sudo apt update
sudo apt install apparmor-utils apparmor-profiles apparmor-profiles-extra

2) Enable the shipped bwrap-userns-restrict profile for /usr/bin/bwrap

That profile is commonly shipped as an “extra” and may not be loaded by default. (apparmor-documentation-c38b15.gitlab.io)

# Link the shipped profile into /etc/apparmor.d (if not already)
if [ -f /usr/share/apparmor/extra-profiles/bwrap-userns-restrict ] && [ ! -e /etc/apparmor.d/bwrap-userns-restrict ]; then
  sudo ln -s /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
fi

# Load/reload it into the kernel
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict || \
sudo apparmor_parser -a /etc/apparmor.d/bwrap-userns-restrict

Important: aa-enforce expects an executable path (like /usr/bin/bwrap), not the profile file path. The Ubuntu docs show aa-enforce /path/to/bin. (Ask Ubuntu)

Optional:

sudo aa-enforce /usr/bin/bwrap

3) Add a second bwrap profile for Nix-store bwrap (/nix/store/**/bin/bwrap)

Because Nix may call its own bwrap from the store, the /usr/bin/bwrap profile won’t match it. Create this file:

sudo tee /etc/apparmor.d/bwrap-nixstore-userns-restrict >/dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>

# Nix-store bubblewrap attachment.
profile bwrap_nixstore /nix/store/**/bin/bwrap flags=(attach_disconnected) {
  allow capability,
  allow file rwlkm /{**,},
  allow network,
  allow unix,
  allow ptrace,
  allow signal,
  allow mqueue,
  allow io_uring,
  allow userns,
  allow mount,
  allow umount,
  allow pivot_root,
  allow dbus,

  # Stack a child profile (same idea as the shipped bwrap-userns-restrict).
  allow px /** -> bwrap_nixstore//&unpriv_bwrap_nixstore,

  include if exists <local/bwrap-userns-restrict>
}

profile unpriv_bwrap_nixstore flags=(attach_disconnected) {
  allow file rwlkm /{**,},
  allow network,
  allow unix,
  allow ptrace,
  allow signal,
  allow mqueue,
  allow io_uring,
  allow userns,
  allow mount,
  allow umount,
  allow pivot_root,
  allow dbus,

  allow pix /** -> &unpriv_bwrap_nixstore,

  audit deny capability,

  include if exists <local/unpriv_bwrap>
}
EOF

sudo apparmor_parser -r /etc/apparmor.d/bwrap-nixstore-userns-restrict

Why this works: Ubuntu’s model is “selectively allow userns per application via AppArmor policy” instead of a global on/off switch. (Ubuntu)


4) Label nix so it’s allowed to use user namespaces (but otherwise remains unconfined)

Don’t attach to ~/.nix-profile/... (user-writable). Attach to the immutable store path instead (/nix/store/**/bin/nix).

sudo tee /etc/apparmor.d/nix-userns >/dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>

profile nix-userns /nix/store/**/bin/nix flags=(unconfined) {
  allow userns,
  include if exists <local/nix-userns>
}
EOF

sudo apparmor_parser -r /etc/apparmor.d/nix-userns

(You generally should not run aa-enforce on this one; flags=(unconfined) is intentional.)


5) Verify it’s really the AppArmor change that fixed it

A) Check profiles are loaded

sudo aa-status | grep -E 'bwrap($|//)|unpriv_bwrap|bwrap_nixstore|unpriv_bwrap_nixstore|nix-userns'

B) Re-run a build and confirm the kernel log stopped forcing fallback

sudo journalctl -k -b | grep -E 'comm="bwrap"|unprivileged_userns|DENIED' | tail -n 80

What you want now:

  • comm="bwrap" entries should show profile="bwrap" or profile="bwrap_nixstore"
  • you should not see new profile="unprivileged_userns" DENIED lines for setpcap / uid_map

This directly demonstrates it wasn’t “magic reloading”: the process is now running under a profile that explicitly allows userns, which is exactly what Ubuntu requires. (Ubuntu)


If it ever breaks again

Run:

sudo journalctl -k -b | grep 'apparmor="DENIED"' | tail -n 50

Whatever shows up in comm="..." is the next helper binary you need to label/cover.


The blunt alternatives (not recommended, but FYI)

Ubuntu’s own docs and community answers often mention the global sysctl (kernel.apparmor_restrict_unprivileged_userns=0) as an “all apps” workaround, but it’s explicitly an all-or-nothing switch and increases attack surface. (Ubuntu)

If you want, paste the output of:

sudo aa-status | grep -E 'bwrap|bwrap_nixstore|nix-userns'

and one fresh journalctl -k -b | grep 'comm="bwrap"' | tail -n 10 and I’ll sanity-check that your system is in the “good” state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment