diff --git a/utils/aa-notify b/utils/aa-notify index 2af2f3ad0..0ecf239c4 100755 --- a/utils/aa-notify +++ b/utils/aa-notify @@ -53,7 +53,7 @@ import apparmor.update_profile as update_profile import LibAppArmor # C-library to parse one log line from apparmor.common import DebugLogger, open_file_read from apparmor.fail import enable_aa_exception_handler -from apparmor.notify import get_last_login_timestamp, get_event_special_type +from apparmor.notify import get_last_login_timestamp, get_event_special_type, set_userns_special_profile from apparmor.translations import init_translation from apparmor.logparser import ReadLog from apparmor.gui import UsernsGUI, ErrorGUI, ShowMoreGUI, ShowMoreGUIAggregated, set_interface_theme, ProfileRules @@ -563,7 +563,7 @@ def get_more_info_about_event(rl, ev, special_profiles, profile_path, header='') else: raw_rule = rule.get_clean() # TODO: This is brittle. Priority>1 might be needed. Also do we need to make the message show that we force allow? - if aa.is_known_rule(aa.active_profiles.profiles[ev['profile']], rule.rule_name, rule): + if ev['profile'] in aa.active_profiles.profiles and aa.is_known_rule(aa.active_profiles.profiles[ev['profile']], rule.rule_name, rule): rule.priority = 1 raw_rule = "priority=1 " + raw_rule if aa.is_known_rule(aa.active_profiles.profiles[ev['profile']], rule.rule_name, rule): @@ -1034,7 +1034,9 @@ def main(): userns_special_profiles = config['']['userns_special_profiles'].strip().split(',') else: # By default, unconfined and unprivileged_userns are the special profiles - userns_special_profiles = ['unconfined', 'unprivileged_userns'] + userns_special_profiles = ['unconfined', 'unprivileged_userns', 'unpriv_.*'] + # To support regexes + userns_special_profiles = set_userns_special_profile(userns_special_profiles) if 'ignore_denied_capability' in config['']: ignore_denied_capability = config['']['ignore_denied_capability'].strip().split(',') diff --git a/utils/aa-notify.pod b/utils/aa-notify.pod index fea8a25ee..df466f84c 100644 --- a/utils/aa-notify.pod +++ b/utils/aa-notify.pod @@ -89,8 +89,8 @@ System-wide configuration for B is done via # Set to 'no' to disable AppArmor notifications globally show_notifications="yes" - # Special profiles used to remove privileges for unconfined binaries using user namespaces. If unsure, leave as is. - userns_special_profiles="unconfined,unprivileged_userns" + # Special profiles used to remove privileges for unconfined binaries using user namespaces. Special profiles use Python's regular expression syntax. If unsure, leave as is. + userns_special_profiles="unconfined,unprivileged_userns,unpriv_.*" # Theme for aa-notify GUI. See https://ttkthemes.readthedocs.io/en/latest/themes.html for available themes. interface_theme="ubuntu" diff --git a/utils/apparmor/notify.py b/utils/apparmor/notify.py index 64a29df4a..a2f7a34fa 100644 --- a/utils/apparmor/notify.py +++ b/utils/apparmor/notify.py @@ -16,6 +16,7 @@ import os import struct import sqlite3 +import re from apparmor.common import AppArmorBug, DebugLogger @@ -135,7 +136,7 @@ def is_special_profile_userns(ev, special_profiles): if 'comm' not in ev: return False # special profiles have a 'comm' entry - if not special_profiles or not ev['profile'] in special_profiles: + if not special_profiles or not special_profiles.match(ev['profile']): return False # We don't use special profiles or there is already a profile defined: we don't ask to add userns return True @@ -155,3 +156,7 @@ def get_event_special_type(ev, special_profiles): else: raise AppArmorBug('unexpected operation: %s' % ev['operation']) return 'normal' + + +def set_userns_special_profile(special_profiles): + return re.compile('^({})$'.format('|'.join(special_profiles))) diff --git a/utils/notify.conf b/utils/notify.conf index a6145c643..44b048e6c 100644 --- a/utils/notify.conf +++ b/utils/notify.conf @@ -11,8 +11,8 @@ # Set to 'no' to disable AppArmor notifications globally show_notifications="yes" -# Special profiles used to remove privileges for unconfined binaries using user namespaces. If unsure, leave as is. -userns_special_profiles="unconfined,unprivileged_userns" +# Special profiles used to remove privileges for unconfined binaries using user namespaces. Special profiles use Python's regular expression syntax. If unsure, leave as is. +userns_special_profiles="unconfined,unprivileged_userns,unpriv_.*" # Theme to use for aa-notify GUI themes. See https://ttkthemes.readthedocs.io/en/latest/themes.html for available themes. interface_theme="ubuntu" diff --git a/utils/test/test-notify.py b/utils/test/test-notify.py index 2020f819e..69a94a716 100644 --- a/utils/test/test-notify.py +++ b/utils/test/test-notify.py @@ -12,7 +12,7 @@ import unittest from apparmor.common import AppArmorBug -from apparmor.notify import get_last_login_timestamp, get_last_login_timestamp_wtmp, sane_timestamp, get_event_special_type +from apparmor.notify import get_last_login_timestamp, get_last_login_timestamp_wtmp, sane_timestamp, get_event_special_type, set_userns_special_profile from apparmor.logparser import ReadLog from common_test import AATest, setup_all_loops @@ -89,7 +89,7 @@ class TestGet_last_login_timestamp_wtmp(AATest): class TestEventSpecialType(AATest): - userns_special_profiles = ['unconfined', 'unprivileged_userns'] + userns_special_profiles = set_userns_special_profile(['unconfined', 'unprivileged_userns', 'unpriv_.*']) parser = ReadLog('', '', '') tests = ( ('[ 176.385388] audit: type=1400 audit(1666891380.570:78): apparmor="DENIED" operation="userns_create" class="namespace" profile="/usr/bin/bwrap-userns-restrict" pid=1785 comm="userns_child_ex" requested="userns_create" denied="userns_create"', 'normal'), @@ -98,7 +98,7 @@ class TestEventSpecialType(AATest): ('[ 52.901383] audit: type=1400 audit(1752064882.228:82): apparmor="DENIED" operation="capable" class="cap" profile="unprivileged_userns" pid=6700 comm="electron" capability=21 capname="sys_admin"', 'userns_capable'), ('Jul 31 17:11:16 dbusdev-saucy-amd64 dbus[1692]: apparmor="DENIED" operation="dbus_bind" bus="session" name="com.apparmor.Test" mask="bind" pid=2940 profile="/tmp/apparmor-2.8.0/tests/regression/apparmor/dbus_service"', 'normal'), ('[103975.623545] audit: type=1400 audit(1481284511.494:2807): apparmor="DENIED" operation="change_onexec" info="no new privs" error=-1 namespace="root//lxd-tor_" profile="unconfined" name="system_tor" pid=18593 comm="(tor)" target="system_tor"', 'userns_change_profile'), - ('[78661.551820] audit: type=1400 audit(1752661047.170:350): apparmor="DENIED" operation="capable" class="cap" profile="unpriv_bwrap" pid=1412550 comm="node" capability=21 capname="sys_admin"', 'normal'), + ('[78661.551820] audit: type=1400 audit(1752661047.170:350): apparmor="DENIED" operation="capable" class="cap" profile="unpriv_bwrap" pid=1412550 comm="node" capability=21 capname="sys_admin"', 'userns_capable'), ) def _run_test(self, ev, expected):