Analysis of AppArmor related scripts and services (1)

  • apparmor.service

apparmor.service is a service related to apparmor. The files are located in /lib/systemd/system/ and /usr/lib/systemd/system/, and the contents are as follows:

[Unit]
Description=Load AppArmor profiles
DefaultDependencies=no
Before=sysinit.target
After=local-fs.target
After=systemd-journald-audit.socket
RequiresMountsFor=/var/cache/apparmor
AssertPathIsReadWrite=/sys/kernel/security/apparmor/.load
ConditionSecurity=apparmor
Documentation=man:apparmor(7)
Documentation=https://gitlab.com/apparmor/apparmor/wikis/home/

# Don't start this unit on the Ubuntu Live CD
ConditionPathExists=!/rofs/etc/apparmor.d

# Don't start this unit on the Debian Live CD when using overlayfs
ConditionPathExists=!/run/live/overlay/work

[Service]
Type=oneshot
ExecStart=/lib/apparmor/apparmor.systemd reload
ExecReload=/lib/apparmor/apparmor.systemd reload

# systemd maps 'restart' to 'stop; start' which means removing AppArmor confinement
# from running processes (and not being able to re-apply it later).
# Upstream systemd developers refused to implement an option that allows overriding
# this behaviour, therefore we have to make ExecStop a no-op to error out on the
# safe side.
#
# If you really want to unload all AppArmor profiles, run   aa-teardown
ExecStop=/bin/true
RemainAfterExit=yes

[Install]
WantedBy=sysinit.target

The most important of these is /lib/apparmor/apparmor.systemd.

  • apparmor.systemd

The apparmor.systemd file is located in the /lib/apparmor/ path, and the content is as follows:

#!/bin/sh
# ----------------------------------------------------------------------
#    Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of version 2 of the GNU General Public
#    License published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, contact Novell, Inc.
# ----------------------------------------------------------------------

APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions

aa_action()
{
    echo "$1"
    shift
    "$@"
    return $?
}

aa_log_warning_msg()
{
    echo "Warning: $*"
}

aa_log_failure_msg()
{
    echo "Error: $*"
}

aa_log_action_start()
{
    echo "$@"
}

aa_log_action_end()
{
    printf ""
}

aa_log_daemon_msg()
{
    echo "$@"
}

aa_log_skipped_msg()
{
    echo "Skipped: $*"
}

aa_log_end_msg()
{
    printf ""
}

# source apparmor function library
if [ -f "${APPARMOR_FUNCTIONS}" ]; then
        # shellcheck source=rc.apparmor.functions
        . "${APPARMOR_FUNCTIONS}"
else
        aa_log_failure_msg "Unable to find AppArmor initscript functions"
        exit 1
fi

case "$1" in
        start)
                if [ -x /usr/bin/systemd-detect-virt ] && \
                   systemd-detect-virt --quiet --container && \
                   ! is_container_with_internal_policy; then
                        aa_log_daemon_msg "Not starting AppArmor in container"
                        aa_log_end_msg 0
                        exit 0
                fi
                apparmor_start
                rc=$?
                ;;
        stop)
                apparmor_stop
                rc=$?
                ;;
        restart|reload|force-reload)
                if [ -x /usr/bin/systemd-detect-virt ] && \
                   systemd-detect-virt --quiet --container && \
                   ! is_container_with_internal_policy; then
                        aa_log_daemon_msg "Not starting AppArmor in container"
                        aa_log_end_msg 0
                        exit 0
                fi
                apparmor_restart
                rc=$?
                ;;
        try-restart)
                apparmor_try_restart
                rc=$?
                ;;
        kill)
                apparmor_kill
                rc=$?
                ;;
        status)
                apparmor_status
                rc=$?
                ;;
        *)
                exit 1
                ;;
esac
exit "$rc"

Take /lib/apparmor/apparmor.systemd reload in /lib/systemd/system/apparmor.service above as an example to analyze the script.

(1) Look at the first paragraph first. The code snippet is as follows:

restart|reload|force-reload)
    if [ -x /usr/bin/systemd-detect-virt ] && \
      systemd-detect-virt --quiet --container && \
      ! is_container_with_internal_policy; then
        aa_log_daemon_msg "Not starting AppArmor in container"
        aa_log_end_msg 0
        exit 0
    fi
    apparmor_restart
    rc=$?
    ;;

Regardless of whether the parameter behind /lib/apparmor/apparmor.systemd is restart, reload or force-reload, it will enter this code.

1) First check whether /usr/bin/systemd-detect-virt exists and is executable. This is a binary file that does exist on my computer, as shown below:

$ ls -l /usr/bin/systemd-detect-virt 
-rwxr-xr-x 1 root root 23416  2月15日 00:36 /usr/bin/systemd-detect-virt

Reference: systemd-detect-virt Chinese Manual - systemd Chinese Manual - Development Documentation - Wenjiang Blog

systemd-detect-virt is used to detect whether the operating environment of the system is a virtualized environment, and to further detect which virtualized environment it is, such as which virtual machine or container.

2) Next, execute systemd-detect-virt related commands for detection.

Where: the --container option only detects containers (shared kernel virtualization).

The result of running this command on the author's system is:

$ systemd-detect-virt --quiet --container
$ 

That is to say, the judgment of the if statement in the above script is false, the statement in it will not be executed, and the if statement will be skipped and executed.

3) Next, execute the sentence of apparmor_restart.

apparmor_restart is a function, in /lib/apparmor/rc.apparmor.functions, the code is as follows:

apparmor_restart() {
    if ! is_apparmor_loaded ; then
        apparmor_start
        rc=$?
        return "$rc"
    fi

    __apparmor_restart
    return $?
}

The apparmor_start function is in the same file (/lib/apparmor/rc.apparmor.functions), the code is as follows:

apparmor_start() {
    aa_log_daemon_msg "Starting AppArmor"
    if ! is_apparmor_present ; then
        aa_log_failure_msg "Starting AppArmor - failed, To enable AppArmor, ensure your kernel is configured with CONFIG_SECURITY_APPARMOR=y the    n add 'security=apparmor apparmor=1' to the kernel command line"
        aa_log_end_msg 1
        return 1
    elif ! is_apparmor_loaded ; then
        aa_log_failure_msg "Starting AppArmor - AppArmor control files aren't available under /sys/kernel/security/, please make sure securityfs     is mounted."
        aa_log_end_msg 1
        return 1
    fi

    if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
        aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
        aa_log_end_msg 1
        return 1
    fi

    # if there is anything in the profiles file don't load
    if ! read -r _ < "$SFS_MOUNTPOINT/profiles"; then
        parse_profiles load
    else
        aa_log_skipped_msg ": already loaded with profiles."
        return 0
    fi
    aa_log_end_msg 0
    return 0
}

The __apparmor_restart function is also in the same file (/lib/apparmor/rc.apparmor.functions), the code is as follows:

__apparmor_restart() {
    if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
        aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
        return 4
    fi
 
    aa_log_daemon_msg "Restarting AppArmor"

    parse_profiles reload

    rc=$?
    aa_log_end_msg "$rc"
    return "$rc"
}

The is_apparmor_loaded function is also in /lib/apparmor/rc.apparmor.functions, the code is as follows:

is_apparmor_loaded() {
    if ! is_securityfs_mounted ; then
        mount_securityfs
    fi

if [ -f "${SFS_MOUNTPOINT}/profiles" ]; then
    return 0
fi

    is_apparmor_present

    return $?
}

The is_securityfs_mounted function is also in lib/apparmor/rc.apparmor.functions, the code is as follows:

is_securityfs_mounted() {
    test -d "$SECURITYFS" -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
    return $?
}

The is_apparmor_present function is also in lib/apparmor/rc.apparmor.functions, the code is as follows:

# Test if the apparmor "module" is present.
is_apparmor_present() {
    [ -d /sys/module/apparmor ]
}

In the author's system, the /sys/module/apparmor directory exists, as shown below:

$ ls /sys/module/apparmor -ld
drwxr-xr-x 3 root root 0  5月22日 13:47 /sys/module/apparmor

Therefore, the execution result of the is_apparmor_present function is 0.

Actually execute the /lib/apparmor/apparmor.systemd reload command in /lib/systemd/system/apparmor.service, the result is as follows:

$ sudo /lib/apparmor/apparmor.systemd reload
Restarting AppArmor
Reloading AppArmor profiles 

In this way, it can be seen that the script actually takes the branch of apparmor_restart -> __apparmor_restart -> parse_profiles.

This article is too long, and the rest will continue to be analyzed in subsequent articles.

Guess you like

Origin blog.csdn.net/phmatthaus/article/details/130860501