Skip to content

hooks: add script to remove unneeded apparmor profiles - #201

Open
zyga wants to merge 1 commit into
canonical:masterfrom
zyga:core18-aa-trim
Open

hooks: add script to remove unneeded apparmor profiles#201
zyga wants to merge 1 commit into
canonical:masterfrom
zyga:core18-aa-trim

Conversation

@zyga

@zyga zyga commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

This speeds up boot time as we prevent many calls to apparmor_parser.

This patch is amended compared to the core26 counterpart, to provide the correct set of expected profiles (in this case, none). Those differ from base to base.

This speeds up boot time as we prevent many calls to apparmor_parser.

This patch is amended compared to the core26 counterpart, to provide the
correct set of expected profiles (in this case, none). Those differ from
base to base.
Copilot AI review requested due to automatic review settings January 28, 2026 17:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new build hook to prune AppArmor profiles from the base filesystem in order to reduce boot-time work (fewer apparmor_parser invocations).

Changes:

  • Introduces a chroot hook to remove AppArmor profile files deemed unnecessary based on whether a corresponding binary exists.
  • Adds a hook-test asserting the expected remaining AppArmor profiles for this base (none).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
hooks/800-remove-unneeded-profiles.chroot Implements the AppArmor profile cleanup during image build.
hook-tests/800-remove-unneeded-profiles.test Verifies the post-build contents of etc/apparmor.d match expectations for this base.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3 to +4
# Check profiles in /etc/apparmor.d/ and removes them if no matching binary
# exists.

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

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

Grammar: “Check profiles … and removes them” should be “Check profiles … and remove them”.

Suggested change
# Check profiles in /etc/apparmor.d/ and removes them if no matching binary
# exists.
# Check profiles in /etc/apparmor.d/ and remove them if no matching
# binary exists.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +47
for target_dir in "${SEARCH_DIRS[@]}"; do
# Check if the file exists in the target directory
if [ -e "${target_dir}/${filename}" ]; then
match_found=true
break
fi
done

# If no match was found in any directory, perform deletion
if [ "$match_found" = false ]; then

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

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

The matching logic assumes the AppArmor profile filename corresponds directly to an executable name (and checks ${target_dir}/${filename}). In practice, many profiles are not named exactly like the binary (e.g., path-encoded names) and profiles can also contain multiple profile ... {} stanzas, so this can incorrectly delete profiles even when the target executable exists. Consider deriving the attachment path(s) from the profile contents (e.g., parse profile <attachment> lines) or translating common filename encodings, and use an executability check (-x) for binaries rather than -e.

Suggested change
for target_dir in "${SEARCH_DIRS[@]}"; do
# Check if the file exists in the target directory
if [ -e "${target_dir}/${filename}" ]; then
match_found=true
break
fi
done
# If no match was found in any directory, perform deletion
if [ "$match_found" = false ]; then
# First, try to derive attachment paths from the profile contents and
# check whether any referenced executable actually exists and is executable.
if [ -s "$profile" ]; then
# Extract absolute paths from "profile ..." stanzas. This is a best-effort
# parser that looks for tokens starting with "/" on lines beginning with
# "profile". It strips surrounding quotes and "{".
mapfile -t _apparmor_attachments < <(
awk '
/^profile[[:space:]]/ {
for (i = 1; i <= NF; i++) {
if ($i ~ /^\//) {
gsub(/["{]/, "", $i);
print $i;
}
}
}
' "$profile" | sort -u
)
if [ "${#_apparmor_attachments[@]}" -gt 0 ]; then
for attachment in "${_apparmor_attachments[@]}"; do
if [ -x "$attachment" ]; then
match_found=true
break
fi
done
fi
fi
# If no executable was found via attachment paths, fall back to searching
# by filename in the configured SEARCH_DIRS.
if [ "$match_found" = false ]; then
for target_dir in "${SEARCH_DIRS[@]}"; do
# Check if an executable with this name exists in the target directory
if [ -x "${target_dir}/${filename}" ]; then
match_found=true
break
fi
done
fi
# If no match was found via attachments or filename, perform deletion
if [ "$match_found" = false ]; then

Copilot uses AI. Check for mistakes.
# If no match was found in any directory, perform deletion
if [ "$match_found" = false ]; then
echo "[DELETING] $profile"
rm "$profile"

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

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

With set -e, a transient failure of rm (e.g., file already removed, permission/FS issue) will abort the whole hook. Use a safer removal form (e.g., force and -- to terminate options) and consider emitting an error if a deletion fails unexpectedly.

Suggested change
rm "$profile"
if ! rm -f -- "$profile"; then
echo "Warning: failed to remove $profile" >&2
fi

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +9
APPARMOR_PROF_D="etc/apparmor.d"

declare -A allowed_profs

num_prof=0

for profile in "$APPARMOR_PROF_D"/*; do

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

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

This test can pass even if etc/apparmor.d is missing: in bash, an unmatched glob expands to the literal pattern and the loop effectively no-ops, leaving num_prof=0. Add an explicit assertion that the directory exists (and ideally that the glob expands, e.g. via nullglob) so the test fails when the hook/app package output is absent.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +29
printf "Apparmor profile %s is not allowed\n" "$filename"
exit 1
fi
done

if [ "$num_prof" -ne "${#allowed_profs[@]}" ]; then
# If there were more we would have failed in the loop
printf "Less number of apparmor profiles than expected\n"

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

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

Spelling/capitalization: use the product name “AppArmor” consistently in test output.

Suggested change
printf "Apparmor profile %s is not allowed\n" "$filename"
exit 1
fi
done
if [ "$num_prof" -ne "${#allowed_profs[@]}" ]; then
# If there were more we would have failed in the loop
printf "Less number of apparmor profiles than expected\n"
printf "AppArmor profile %s is not allowed\n" "$filename"
exit 1
fi
done
if [ "$num_prof" -ne "${#allowed_profs[@]}" ]; then
# If there were more we would have failed in the loop
printf "Less number of AppArmor profiles than expected\n"

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants