Skip to content

Test AppArmor profiles with autopkgtest

When developing AppArmor profiles, manually exercising an application covers only the paths you think of. Most Debian and Ubuntu packages ship a dedicated test suite known as autopkgtest (DEP-8 tests). Running those tests inside a confined VM gives you broader coverage: the test suite exercises paths the upstream developers thought of including corner cases, unusual options and error recovery.

The workflow is:

  1. Boot a clean VM with your profile pre-loaded in complain mode.
  2. Run the package's test suite inside that VM.
  3. Collect the AppArmor denial log from the VM back to your host.
  4. If needed, review and refine the profile.

Preparation

Install the required tools

sudo apt-get install autopkgtest qemu-system-x86

autopkgtest drives the test runner. The qemu backend boots a disposable VM for each run.

Build a VM image

autopkgtest needs a base image to boot from. For Ubuntu, use autopkgtest-buildvm-ubuntu-cloud.

autopkgtest-buildvm-ubuntu-cloud -r questing -o ~/autopkgtest/ \
    --post-command "apt-get install -y apparmor; \
                    echo kernel.printk_ratelimit=0 > /etc/sysctl.d/99-apparmor-testing.conf; \
                    echo kernel.printk_ratelimit_burst=1000 >> /etc/sysctl.d/99-apparmor-testing.conf"

This downloads the Ubuntu cloud image, customizes it, and saves it to ~/autopkgtest/. This base image can be reused across all test runs. Building takes a few minutes.

We configure the VM for test through --post-command

  • apparmor: the userspace parser and tools (not present in the minimal cloud image).
  • sysctl rate-limit settings: written to /etc/sysctl.d/99-apparmor-testing.conf so they apply at every boot. Without them, the kernel may silently drop AppArmor log entries under load.

Set up the shared directory structure

In this tutorial we will use a 9p mount between the host and the VM to be able to share profiles and logs. We will use the following structure.

~/autopkgtest/
  profiles/     # put your profiles here; they get loaded into the VM
  logs/         # denial log lands here after the run
mkdir -p ~/autopkgtest/profiles ~/autopkgtest/logs

Place your in-progress profiles into profiles/. On the first run the directory can be empty as the VM uses whatever the package ships. After the run, updated profiles from aa-logprof go back here, and subsequent runs pick them up.

Complain mode

You can add flags=(complain) to your profile header before copying it to profiles/. This lets the test suite run to completion even if the profile is missing rules, and gives you a full picture of all accesses before you start blocking anything.

Running autopkgtest

HOST_DIR=~/autopkgtest
VM_DEST=/mnt/host
package=openvpn
cpus=4
ram=2048

autopkgtest ${package} \
  --timeout=1000 \
  --setup-commands-boot="
    sudo mkdir -p ${VM_DEST}
    sudo mount -t 9p -o trans=virtio,version=9p2000.L hostshare ${VM_DEST} || true
    sudo cp -r ${VM_DEST}/profiles/* /etc/apparmor.d/ 2>/dev/null || true
    sudo apparmor_parser -r /etc/apparmor.d/${package}
    sudo journalctl -k -f | grep --line-buffered 'audit.*apparmor=' >> ${VM_DEST}/logs/denials.log &" \
  -- qemu \
     --cpus=${cpus} \
     --ram-size=${ram} \
     --qemu-options="-virtfs local,path=${HOST_DIR},security_model=none,mount_tag=hostshare" \
     ~/autopkgtest/autopkgtest-questing-amd64.img

Autopkgtest installs the package under test automatically: there is no need to install it manually.

The boot commands:

  1. Mount ~/autopkgtest/ from the host into the VM at $VM_DEST (|| true prevents a failure if it is already mounted on a second boot).
  2. Copy any profiles from profiles/ into /etc/apparmor.d/, overriding any version shipped by the package.
  3. Reload the specific profile so it takes effect immediately.
  4. Start a background process streaming AppArmor kernel audit events to logs/denials.log.

Reviewing the results

After the run, ~/autopkgtest/logs/denials.log on your host contains all AppArmor events from inside the VM. The file may contain STATUS entries (profile load events) alongside ALLOWED and DENIED entries:

  • STATUS entries record profile load/replace events. They are normal: nothing to act on.
  • ALLOWED entries (complain mode) are the ones to review: they show what the application tried to do that the profile does not currently cover.
  • DENIED entries mean the profile blocked the application.

With aa-logprof

aa-logprof reads the log and proposes profile rules for each event. Point it at the collected file:

sudo aa-logprof -f ~/autopkgtest/logs/denials.log

For each entry, aa-logprof shows the access and proposes a rule. Review and adapt each suggestion, as some may be broader than needed. Accept, modify, or skip as appropriate, then save.

Once aa-logprof has updated the profile, copy it back into profiles/ so the next run uses the updated version:

sudo cp /etc/apparmor.d/${package} ~/autopkgtest/profiles/

By hand

If you prefer to read the log directly:

grep 'apparmor="ALLOWED"' ~/autopkgtest/logs/denials.log   # complain-mode events
grep 'apparmor="DENIED"'  ~/autopkgtest/logs/denials.log   # enforce-mode events

Each line contains the profile name, operation, resource, and permission mask. Add the missing rules to your profile and iterate.

Final validation

Once the profile looks right, switch it to enforce mode (check that the profile does not contain flags=(complain)) and re-run autopkgtest to confirm the test suite still passes cleanly without any denial.

Interactive shell for manual testing

The --shell flag tells autopkgtest to pause after each test and wait before continuing. This is useful when you want to exercise the application manually inside the VM beyond what the test suite covers:

autopkgtest ${package} --shell --setup-commands-boot="..." -- qemu ...

When the runner pauses, you will see:

Press Enter to resume running tests.

Open a second terminal and SSH into the VM (autopkgtest exposes the QEMU VM on localhost port 10022 by default):

ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 10022 ubuntu@localhost
# password: ubuntu (for Ubuntu images)

Inside the VM you can exercise the application manually, check logs in real time, or inspect AppArmor state with aa-status. Press Enter in the original terminal to resume the test run.

Running individual tests manually

autopkgtest unpacks the package source under /tmp/autopkgtest.*/build.*/src/. The DEP-8 test scripts are in debian/tests/ inside that directory:

ls /tmp/autopkgtest.*/build.*/src/debian/tests/

You can run any of them directly. For example, to run the upstream test for openvpn:

sudo /tmp/autopkgtest.*/build.*/src/debian/tests/upstream

Between runs, you can modify the profile and reload it without rebooting:

sudo vi /etc/apparmor.d/${package}
sudo apparmor_parser -r /etc/apparmor.d/${package}

Then re-run the test and watch the denial log update in real time:

tail -f ${VM_DEST}/logs/denials.log

This lets you iterate on individual rules quickly without triggering a full autopkgtest run each time.

Further reading