Skip to content

Conversation

@AjayvirS
Copy link

@AjayvirS AjayvirS commented Oct 11, 2025

Add optional AppArmor profile to enable unprivileged user namespaces for Bubblewrap (bwrap)

This PR adds an optional AppArmor profile and the necessary Ansible tasks to install and load it on Artemis build agent hosts.

The profile enables the controlled use of unprivileged user namespaces (used by Bubblewrap, where Apparmor restricts such namespaces by default on Ubuntu 2024.04+.

This change lays the groundwork to isolate programming exercise builds inside Docker containers, by utilizing Bubblewrap to further restrict access through untrusted student code.

It does not activate or use the profile by default, and therefore introduces no behavioral or security change until Artemis containers explicitly request it.

Background

Ubuntu 23.10 + 24.04 introduced “restricted unprivileged user namespaces.”
Processes can only create useful user namespaces (with capabilities) if their AppArmor profile includes the rule userns.

To support Bubblewrap isolation, we need a host-side profile that grants this rule and can be attached per container via Docker’s --security-opt apparmor={apparmor_profile}.

Testing

  1. Deploy to a build agent host via Ansible.
  2. Verify the profile is loaded: sudo aa-status | grep docker-allow-bwrap
  3. Run a simple user-namespace test:
    docker run --rm --security-opt apparmor=apparmor_profile_bubblewrap ubuntu:24.04 \ bash -lc 'unshare -Ur true && echo OK'

Future

Future changes to Artemis (specifically BuildJobContainerService in the Artemis Codebase) will use this profile when starting build containers that rely on Bubblewrap/Phobos.
This PR prepares the infrastructure for that work.

Summary by CodeRabbit

  • New Features

    • Added an AppArmor profile to confine bubblewrap-enabled build containers, improving isolation and security.
    • Added an automatic reload trigger so the new security profile is applied immediately after deployment changes.
  • Chores

    • Ensure AppArmor utilities are installed during deployment.
    • Deployment now installs and activates the security profile as a prerequisite.

@AjayvirS AjayvirS requested a review from a team as a code owner October 11, 2025 15:11
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

Walkthrough

Adds an AppArmor profile docker-bwrap, ensures AppArmor utilities are installed, copies the profile to /etc/apparmor.d/, and adds a handler that reloads the profile via apparmor_parser; these tasks are integrated into the Artemis Docker deploy flow and notify the reload handler before further deployment steps.

Changes

Cohort / File(s) Summary
AppArmor profile addition
roles/artemis/files/apparmor_profile_bubblewrap
Adds new AppArmor profile docker-bwrap with tunables, flags, explicit capabilities, networking, signal/ptrace rules, file/mount/namespace permissions, mount propagation and bind rules, specific mount fstype allowances, and deny rules for sensitive /proc and /sys paths.
Ansible handler for AppArmor reload
roles/artemis/handlers/main.yml
Adds handler Reload apparmor profile that runs apparmor_parser -r /etc/apparmor.d/apparmor_profile_bubblewrap with become: true; placed before the existing Restart docker compose artemis handler.
Ansible deploy task updates
roles/artemis/tasks/docker_deploy_artemis.yml
Prepends tasks to install apparmor and apparmor-utils, and to copy the profile to /etc/apparmor.d/apparmor_profile_bubblewrap, notifying the reload handler; inserted before existing directory creation and deploy tasks.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Operator
  participant Ansible
  participant TargetHost
  participant AppArmor as "AppArmor Kernel/Parser"

  Operator->>Ansible: run role artemis: docker_deploy_artemis
  Ansible->>TargetHost: apt install apparmor, apparmor-utils
  Ansible->>TargetHost: copy apparmor_profile_bubblewrap -> /etc/apparmor.d/
  note right of TargetHost: copy task notifies "Reload apparmor profile"
  Ansible->>TargetHost: continue remaining deploy tasks

  rect rgba(200,230,255,0.6)
    Ansible->>TargetHost: handler: Reload apparmor profile
    TargetHost->>AppArmor: apparmor_parser -r /etc/apparmor.d/apparmor_profile_bubblewrap
    AppArmor-->>TargetHost: profile loaded/reloaded
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hopped through mounts and namespaces tight,
A tiny profile stitched for night,
Bubblewrap snug, capabilities set,
AppArmor listens, no rules unmet,
Reload and watch the containers light. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately summarizes the primary change by indicating the addition of a custom AppArmor profile to support Bubblewrap unprivileged namespace creation and is directly related to the PR objectives.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/allow-apparmor-profile-for-bubblewrap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
roles/artemis/handlers/main.yml (1)

4-7: Use fully qualified collection name for command module.

For consistency with other tasks in the codebase and to align with Ansible best practices, consider using ansible.builtin.command instead of the bare command module.

Apply this diff:

 - name: Reload apparmor profile
   become: true
-  command: apparmor_parser -r /etc/apparmor.d/apparmor_profile_bubblewrap
+  ansible.builtin.command: apparmor_parser -r /etc/apparmor.d/apparmor_profile_bubblewrap
   listen: Reload apparmor profile
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfb2535 and cae5faf.

📒 Files selected for processing (3)
  • roles/artemis/files/apparmor_profile_bubblewrap (1 hunks)
  • roles/artemis/handlers/main.yml (1 hunks)
  • roles/artemis/tasks/docker_deploy_artemis.yml (1 hunks)
🔇 Additional comments (6)
roles/artemis/tasks/docker_deploy_artemis.yml (1)

2-8: LGTM!

The task correctly installs the required AppArmor utilities with appropriate privilege escalation. The package names are correct for Ubuntu/Debian systems.

roles/artemis/files/apparmor_profile_bubblewrap (5)

1-26: LGTM!

The profile header, capabilities, networking, signals, and ptrace rules are well-structured and appropriate for container isolation. The inclusion of sys_admin capability (line 19) is correctly documented as needed for mount mediation.


28-28: Verify if file access can be more restrictive.

The file, rule grants broad file access to all files without restrictions. While this provides maximum flexibility for build containers, consider whether this could be constrained to specific paths (e.g., /home/**, /tmp/**, /workspace/**) to reduce the attack surface.

If the current broad access is intentional and required for Bubblewrap's flexibility, document this decision in a comment above line 28.


30-34: LGTM!

The namespace and mount mediation rules correctly include the userns rule, which is the key requirement from the PR objectives to enable unprivileged user namespaces for Bubblewrap on Ubuntu 23.10/24.04+.


36-66: LGTM!

The mount rules are comprehensive and well-documented. They correctly handle:

  • Staging and finalization of mounts during Bubblewrap setup
  • Various filesystem types (proc, tmpfs, devpts, mqueue, sysfs)
  • Bind and recursive bind mounts
  • Mount propagation options
  • Move and remount operations

The inline comments effectively explain the purpose of each rule.


67-73: LGTM!

The denial rules appropriately restrict access to sensitive kernel interfaces (/sys/**, /proc/sysrq-trigger, /proc/kcore), following Docker's default security posture.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
roles/artemis/handlers/main.yml (1)

4-7: Use absolute path (and quiet flag) for apparmor_parser to avoid PATH issues under sudo.

Safer on Ubuntu: use /sbin or /usr/sbin and suppress warnings.

-  command: apparmor_parser -r /etc/apparmor.d/apparmor_profile_bubblewrap
+  command: /sbin/apparmor_parser -q -r /etc/apparmor.d/apparmor_profile_bubblewrap

Please verify which path exists on your targets (/sbin vs /usr/sbin).

roles/artemis/tasks/docker_deploy_artemis.yml (1)

2-11: Gate apt-specific task to apt-based systems.

Prevents failures on non-Debian hosts.

 - name: Ensure AppArmor utilities are present
   become: true
   apt:
     name:
       - apparmor
       - apparmor-utils
     state: present
     update_cache: true
     cache_valid_time: 3600
+  when: ansible_pkg_mgr == "apt"
+  tags: [apparmor]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cae5faf and 201cf07.

📒 Files selected for processing (2)
  • roles/artemis/handlers/main.yml (1 hunks)
  • roles/artemis/tasks/docker_deploy_artemis.yml (1 hunks)
🔇 Additional comments (1)
roles/artemis/tasks/docker_deploy_artemis.yml (1)

12-20: LGTM; confirm Docker uses the profile’s declared name.

Copy/permissions/notify look correct. Ensure the profile name used with --security-opt apparmor=... matches the name declared inside the profile (not just the filename).

MarkusPaulsen
MarkusPaulsen previously approved these changes Oct 14, 2025
Copy link

@MarkusPaulsen MarkusPaulsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good. The main cut into the security layer seems to be the allowance of unprivileged user namespaces, with seems justifiable for the purpose it serves (allows Phobos to operate correctly).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
roles/artemis/tasks/docker_deploy_artemis.yml (2)

2-11: Gate apt task to Debian-family and ensure AppArmor service is active when enabled.

Avoid running apt on non-Debian systems and ensure AppArmor is started/enabled when opting in.

Apply this diff to the apt task:

 - name: Ensure AppArmor utilities are present
   become: true
   apt:
     name:
       - apparmor
       - apparmor-utils
     state: present
     update_cache: true
     cache_valid_time: 3600
+  when:
+    - artemis_enable_bwrap_apparmor_profile | default(false)
+    - ansible_facts.os_family == 'Debian'

Add this task (nearby) to enable the service:

- name: Ensure AppArmor service is enabled and running
  become: true
  when:
    - artemis_enable_bwrap_apparmor_profile | default(false)
    - ansible_facts.os_family == 'Debian'
  ansible.builtin.service:
    name: apparmor
    state: started
    enabled: true

12-20: Privilege escalation is correct; align mode and FQCN for consistency.

Good addition of become: true. Use "0644" consistently and prefer FQCN.

Apply this diff:

-  copy:
+  ansible.builtin.copy:
@@
-    mode: "644"
+    mode: "0644"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 201cf07 and 4155b8a.

📒 Files selected for processing (1)
  • roles/artemis/tasks/docker_deploy_artemis.yml (1 hunks)
🔇 Additional comments (1)
roles/artemis/tasks/docker_deploy_artemis.yml (1)

12-20: AppArmor reload handler and profile name are correct Handler “Reload apparmor profile” in roles/artemis/handlers/main.yml invokes apparmor_parser on /etc/apparmor.d/apparmor_profile_bubblewrap, and the profile is defined as docker-bwrap with no alternate names detected.

Comment on lines +2 to +20
- name: Ensure AppArmor utilities are present
become: true
apt:
name:
- apparmor
- apparmor-utils
state: present
update_cache: true
cache_valid_time: 3600

- name: Install AppArmor profile for bwrap-enabled build containers
become: true
copy:
src: apparmor_profile_bubblewrap
dest: /etc/apparmor.d/apparmor_profile_bubblewrap
owner: root
group: root
mode: "644"
notify: Reload apparmor profile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make AppArmor integration opt-in (as per PR intent).

These tasks run unconditionally. To keep the profile optional, gate both with a feature flag defaulting to false.

Apply this diff:

 - name: Ensure AppArmor utilities are present
   become: true
   apt:
     name:
       - apparmor
       - apparmor-utils
     state: present
     update_cache: true
     cache_valid_time: 3600
+  when: artemis_enable_bwrap_apparmor_profile | default(false)

 - name: Install AppArmor profile for bwrap-enabled build containers
   become: true
   copy:
     src: apparmor_profile_bubblewrap
     dest: /etc/apparmor.d/apparmor_profile_bubblewrap
     owner: root
     group: root
-    mode: "644"
+    mode: "0644"
   notify: Reload apparmor profile
+  when: artemis_enable_bwrap_apparmor_profile | default(false)

And define the variable in role defaults:

# roles/artemis/defaults/main.yml
artemis_enable_bwrap_apparmor_profile: false
🤖 Prompt for AI Agents
In roles/artemis/tasks/docker_deploy_artemis.yml around lines 2-20 the AppArmor
install and copy tasks run unconditionally; make them opt-in by adding a
conditional check using a boolean feature flag
(artemis_enable_bwrap_apparmor_profile) so both tasks only run when that
variable is true, and add the variable with default false in
roles/artemis/defaults/main.yml; ensure the tasks include the same when
condition and that the default variable is documented/defined as false in the
role defaults.

@bensofficial bensofficial requested a review from Mtze October 21, 2025 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants