2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 10:07:12 +00:00

aa-notify: Support regexes in userns_special_profiles

It is now possible to use regexes to define special profiles. unpriv_.*
is used by default.

Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
This commit is contained in:
Maxime Bélair 2025-07-10 16:46:40 +02:00 committed by John Johansen
parent d8c57da6ba
commit 12e3557896
5 changed files with 18 additions and 11 deletions

View File

@ -53,7 +53,7 @@ import apparmor.update_profile as update_profile
import LibAppArmor # C-library to parse one log line import LibAppArmor # C-library to parse one log line
from apparmor.common import DebugLogger, open_file_read from apparmor.common import DebugLogger, open_file_read
from apparmor.fail import enable_aa_exception_handler 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.translations import init_translation
from apparmor.logparser import ReadLog from apparmor.logparser import ReadLog
from apparmor.gui import UsernsGUI, ErrorGUI, ShowMoreGUI, ShowMoreGUIAggregated, set_interface_theme, ProfileRules 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: else:
raw_rule = rule.get_clean() 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? # 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 rule.priority = 1
raw_rule = "priority=1 " + raw_rule raw_rule = "priority=1 " + raw_rule
if aa.is_known_rule(aa.active_profiles.profiles[ev['profile']], rule.rule_name, 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(',') userns_special_profiles = config['']['userns_special_profiles'].strip().split(',')
else: else:
# By default, unconfined and unprivileged_userns are the special profiles # 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['']: if 'ignore_denied_capability' in config['']:
ignore_denied_capability = config['']['ignore_denied_capability'].strip().split(',') ignore_denied_capability = config['']['ignore_denied_capability'].strip().split(',')

View File

@ -89,8 +89,8 @@ System-wide configuration for B<aa-notify> is done via
# Set to 'no' to disable AppArmor notifications globally # Set to 'no' to disable AppArmor notifications globally
show_notifications="yes" show_notifications="yes"
# Special profiles used to remove privileges for unconfined binaries using user namespaces. If unsure, leave as is. # 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" userns_special_profiles="unconfined,unprivileged_userns,unpriv_.*"
# Theme for aa-notify GUI. See https://ttkthemes.readthedocs.io/en/latest/themes.html for available themes. # Theme for aa-notify GUI. See https://ttkthemes.readthedocs.io/en/latest/themes.html for available themes.
interface_theme="ubuntu" interface_theme="ubuntu"

View File

@ -16,6 +16,7 @@
import os import os
import struct import struct
import sqlite3 import sqlite3
import re
from apparmor.common import AppArmorBug, DebugLogger from apparmor.common import AppArmorBug, DebugLogger
@ -135,7 +136,7 @@ def is_special_profile_userns(ev, special_profiles):
if 'comm' not in ev: if 'comm' not in ev:
return False # special profiles have a 'comm' entry 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 False # We don't use special profiles or there is already a profile defined: we don't ask to add userns
return True return True
@ -155,3 +156,7 @@ def get_event_special_type(ev, special_profiles):
else: else:
raise AppArmorBug('unexpected operation: %s' % ev['operation']) raise AppArmorBug('unexpected operation: %s' % ev['operation'])
return 'normal' return 'normal'
def set_userns_special_profile(special_profiles):
return re.compile('^({})$'.format('|'.join(special_profiles)))

View File

@ -11,8 +11,8 @@
# Set to 'no' to disable AppArmor notifications globally # Set to 'no' to disable AppArmor notifications globally
show_notifications="yes" show_notifications="yes"
# Special profiles used to remove privileges for unconfined binaries using user namespaces. If unsure, leave as is. # 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" 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. # Theme to use for aa-notify GUI themes. See https://ttkthemes.readthedocs.io/en/latest/themes.html for available themes.
interface_theme="ubuntu" interface_theme="ubuntu"

View File

@ -12,7 +12,7 @@
import unittest import unittest
from apparmor.common import AppArmorBug 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 apparmor.logparser import ReadLog
from common_test import AATest, setup_all_loops from common_test import AATest, setup_all_loops
@ -89,7 +89,7 @@ class TestGet_last_login_timestamp_wtmp(AATest):
class TestEventSpecialType(AATest): class TestEventSpecialType(AATest):
userns_special_profiles = ['unconfined', 'unprivileged_userns'] userns_special_profiles = set_userns_special_profile(['unconfined', 'unprivileged_userns', 'unpriv_.*'])
parser = ReadLog('', '', '') parser = ReadLog('', '', '')
tests = ( 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'), ('[ 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'), ('[ 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'), ('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_<var-lib-lxd>" profile="unconfined" name="system_tor" pid=18593 comm="(tor)" target="system_tor"', 'userns_change_profile'), ('[103975.623545] audit: type=1400 audit(1481284511.494:2807): apparmor="DENIED" operation="change_onexec" info="no new privs" error=-1 namespace="root//lxd-tor_<var-lib-lxd>" 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): def _run_test(self, ev, expected):