1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
summary: Ensure that batch-reloading of AppArmor profiles works, including fallback.
details: |
This test checks that apparmor profiles are re-generated on snapd startup in a batch,
and that fallback logic of running apparmor parser for each individual snap takes
over when the batch fails. This is tested by replacing /sbin/apparmor_parser with
fake apparmor_parser.fake that fails when if multiple snaps are given (more than 10)
at once, and otherwise succeeds and runs the real apparmor_parser.
The test checks system log for transient errors reported by snapd for apparmor failures
and then verifies apparmor cache to ensure expected profiles were created by fallback
logic.
systems:
- ubuntu-20.04-*
- ubuntu-24.04-*
environment:
VARIANT/changed: changed
VARIANT/unchanged: unchanged
AA_CACHE: /var/cache/apparmor/
AA_PROFILES: /var/lib/snapd/apparmor/profiles/
FAKE_LOG: /tmp/apparmor_parser.fake.log
prepare: |
if not tests.info is-reexec-in-use; then
tests.exec skip-test "The test uses apparmor from the snapd snap; not valid with no-reexec" && exit 0
fi
snap install test-snapd-content-plug test-snapd-tools
snap install --edge test-snapd-curl
cp /sbin/apparmor_parser /sbin/apparmor_parser.real
echo > "$FAKE_LOG"
restore: |
tests.exec is-skipped && exit 0
mv /sbin/apparmor_parser.real /sbin/apparmor_parser
rm -f "$FAKE_LOG"
debug: |
tests.exec is-skipped && exit 0
"$TESTSTOOLS"/journal-state get-log -a | grep apparmor_parser.fake
cat "$FAKE_LOG" || true
execute: |
tests.exec is-skipped && exit 0
systemctl stop snapd.{service,socket}
echo "Update system key"
printf '{"version":1}' > /var/lib/snapd/system-key
# snapd will execute the vendored apparmor-parser from the snapd snap
echo "Replace apparmor parser from the snapd snap with a broken one"
mount -o bind bin/apparmor_parser.fake /snap/snapd/current/usr/lib/snapd/apparmor_parser
tests.cleanup defer umount /snap/snapd/current/usr/lib/snapd/apparmor_parser
# remove all apparmor cached profiles so we can check they are recreated
rm -f $AA_CACHE/snap* $AA_CACHE/*/snap*
# "changed" variant requires source profiles on the disk to look different than the
# ones generated on snapd restart, simulate this by appending a comment to them.
# In both variants fake apparmor parser fails the same way, the difference is only
# internal to snapd and it is a slightly different execution path in apparmor backend,
# manifested in the log by "failed to batch-reload [variant] profiles.." that we match
# further down.
if [ "$VARIANT" = "changed" ]; then
for profile in "$AA_PROFILES"/snap*; do
echo "# forced profile change" >> "$profile"
done
fi
systemctl start snapd.{socket,service}
echo "Checking that apparmor profile cache has been populated"
# note, the list is not exhaustive but is enough for the test and the journal check below
expected_profiles="snap-update-ns.test-snapd-content-plug
snap-update-ns.test-snapd-curl
snap-update-ns.test-snapd-tools
snap.test-snapd-content-plug.content-plug
snap.test-snapd-curl.curl
snap.test-snapd-tools.block
snap.test-snapd-tools.cat
snap.test-snapd-tools.cmd
snap.test-snapd-tools.echo
snap.test-snapd-tools.env
snap.test-snapd-tools.fail
snap.test-snapd-tools.head
snap.test-snapd-tools.sh
snap.test-snapd-tools.success"
retry=50
while true; do
if [ $retry -le 0 ]; then
echo "Expected AppArmor profiles not found in the cache directory."
echo "Expected: $expected_profiles; Got:"
cat profiles.out
exit 1
fi
# apparmor cache is a forest on newer Ubuntu (e.g. 19.10), therefore use find
find $AA_CACHE/ -name '*test-snapd*' -exec basename {} \; | sort > profiles.out
echo "$expected_profiles" | diff profiles.out - && break
sleep 1
retry=$(( retry - 1 ))
done
# we're matching a single long log line (the invocation of apparmor_parser for batch-reload of
# multiple profiles) where the order of arguments is not deterministic, therefore it's split
# into multiple matches. We expect same profiles to always fail - we just don't know how
# snapd ordered the arguments when executing apparmor_parser.
MATCH "FAIL ON: .*/snap-update-ns.test-snapd-curl" < "$FAKE_LOG"
MATCH "FAIL ON: .*/snap.test-snapd-curl.curl" < "$FAKE_LOG"
MATCH "FAIL ON: .*/snap-update-ns.test-snapd-tools" < "$FAKE_LOG"
MATCH "FAIL ON: .*/snap.test-snapd-tools.block" < "$FAKE_LOG"
MATCH "FAIL ON: .*/snap.test-snapd-tools.cat" < "$FAKE_LOG"
# sanity: make sure we got just one "FAIL ON:" line, meaning a single failed invocation of apparmor
# parser when loading the batch.
[ "$(grep -c "FAIL ON:" < "$FAKE_LOG")" = "1" ] || echo "Got more than one failed invocation of apparmor parser when reloading multiple profiles in a batch"
|