mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 14:25:52 +00:00
Merge aa-logprof/aa-genprof: Adding support for --allow-all, --output-dir and --no-abstraction
- Adding support for --output-dir in aa-logprof and aa-genprof, allowing to work on profiles without applying the modified version - Adding support for --allow-all in aa-logprof that creates non-interactively 'allow' rules for all logs - Adding support for --no-abstraction in aa-logprof and aa-genprof MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1177 Approved-by: Christian Boltz <apparmor@cboltz.de> Merged-by: Christian Boltz <apparmor@cboltz.de>
This commit is contained in:
@@ -69,6 +69,8 @@ parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
|
|||||||
parser.add_argument('-f', '--file', type=str, help=_('path to logfile'))
|
parser.add_argument('-f', '--file', type=str, help=_('path to logfile'))
|
||||||
parser.add_argument('program', type=str, help=_('name of program to profile'))
|
parser.add_argument('program', type=str, help=_('name of program to profile'))
|
||||||
parser.add_argument('-j', '--json', action="store_true", help=_('Input and Output in JSON'))
|
parser.add_argument('-j', '--json', action="store_true", help=_('Input and Output in JSON'))
|
||||||
|
parser.add_argument('--no-abstraction', action='store_true', help=_('Do not use any abstractions in profiles'))
|
||||||
|
parser.add_argument('-o', '--output-dir', type=str, help=_('Output Directory for profiles'))
|
||||||
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -78,6 +80,10 @@ apparmor.init_aa(confdir=args.configdir, profiledir=args.dir)
|
|||||||
|
|
||||||
if args.json:
|
if args.json:
|
||||||
aaui.set_json_mode(apparmor.cfg)
|
aaui.set_json_mode(apparmor.cfg)
|
||||||
|
if args.output_dir:
|
||||||
|
apparmor.check_output_dir(args.output_dir)
|
||||||
|
if args.no_abstraction:
|
||||||
|
apparmor.disable_abstractions()
|
||||||
|
|
||||||
apparmor.set_logfile(args.file)
|
apparmor.set_logfile(args.file)
|
||||||
|
|
||||||
@@ -174,7 +180,7 @@ while not done_profiling:
|
|||||||
ans, arg = q.promptUser('noexit')
|
ans, arg = q.promptUser('noexit')
|
||||||
|
|
||||||
if ans == 'CMD_SCAN':
|
if ans == 'CMD_SCAN':
|
||||||
apparmor.do_logprof_pass(logmark)
|
apparmor.do_logprof_pass(logmark, out_dir=args.output_dir)
|
||||||
else:
|
else:
|
||||||
done_profiling = True
|
done_profiling = True
|
||||||
|
|
||||||
|
@@ -28,16 +28,26 @@ parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
|
|||||||
parser.add_argument('-f', '--file', type=str, help=_('path to logfile'))
|
parser.add_argument('-f', '--file', type=str, help=_('path to logfile'))
|
||||||
parser.add_argument('-m', '--mark', type=str, help=_('mark in the log to start processing after'))
|
parser.add_argument('-m', '--mark', type=str, help=_('mark in the log to start processing after'))
|
||||||
parser.add_argument('-j', '--json', action='store_true', help=_('Input and Output in JSON'))
|
parser.add_argument('-j', '--json', action='store_true', help=_('Input and Output in JSON'))
|
||||||
|
parser.add_argument('-a', '--allow-all', action='store_true', help=_('Accept silently all rules'))
|
||||||
|
parser.add_argument('--no-abstraction', action='store_true', help=_('Do not use any abstractions in profiles'))
|
||||||
|
parser.add_argument('-o', '--output-dir', type=str, help=_('Output Directory for profiles'))
|
||||||
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--no-check-mountpoint', action='store_true', help=argparse.SUPPRESS)
|
parser.add_argument('--no-check-mountpoint', action='store_true', help=argparse.SUPPRESS)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
logmark = args.mark or ''
|
logmark = args.mark or ''
|
||||||
|
|
||||||
apparmor.init_aa(confdir=args.configdir, profiledir=args.dir)
|
apparmor.init_aa(confdir=args.configdir, profiledir=args.dir)
|
||||||
|
|
||||||
if args.json:
|
if args.json:
|
||||||
aaui.set_json_mode(apparmor.cfg)
|
aaui.set_json_mode(apparmor.cfg)
|
||||||
|
if args.allow_all:
|
||||||
|
aaui.set_allow_all_mode()
|
||||||
|
if args.no_abstraction:
|
||||||
|
apparmor.disable_abstractions()
|
||||||
|
if args.output_dir:
|
||||||
|
apparmor.check_output_dir(args.output_dir)
|
||||||
|
|
||||||
apparmor.set_logfile(args.file)
|
apparmor.set_logfile(args.file)
|
||||||
|
|
||||||
@@ -48,4 +58,4 @@ if not aa_mountpoint and not args.no_check_mountpoint:
|
|||||||
apparmor.loadincludes()
|
apparmor.loadincludes()
|
||||||
|
|
||||||
apparmor.read_profiles(True)
|
apparmor.read_profiles(True)
|
||||||
apparmor.do_logprof_pass(logmark)
|
apparmor.do_logprof_pass(logmark, out_dir=args.output_dir)
|
||||||
|
@@ -74,6 +74,8 @@ cfg = None
|
|||||||
parser = None
|
parser = None
|
||||||
profile_dir = None
|
profile_dir = None
|
||||||
extra_profile_dir = None
|
extra_profile_dir = None
|
||||||
|
|
||||||
|
use_abstractions = True
|
||||||
### end our
|
### end our
|
||||||
# To keep track of previously included profile fragments
|
# To keep track of previously included profile fragments
|
||||||
include = dict()
|
include = dict()
|
||||||
@@ -294,6 +296,11 @@ def set_enforce(filename, program):
|
|||||||
change_profile_flags(filename, program, ['complain', 'kill', 'unconfined', 'prompt','default_allow'], False) # remove conflicting and complain mode flags
|
change_profile_flags(filename, program, ['complain', 'kill', 'unconfined', 'prompt','default_allow'], False) # remove conflicting and complain mode flags
|
||||||
|
|
||||||
|
|
||||||
|
def disable_abstractions():
|
||||||
|
global use_abstractions
|
||||||
|
use_abstractions = False
|
||||||
|
|
||||||
|
|
||||||
def delete_symlink(subdir, filename):
|
def delete_symlink(subdir, filename):
|
||||||
path = filename
|
path = filename
|
||||||
link = re.sub('^%s' % profile_dir, '%s/%s' % (profile_dir, subdir), path)
|
link = re.sub('^%s' % profile_dir, '%s/%s' % (profile_dir, subdir), path)
|
||||||
@@ -338,6 +345,20 @@ def head(file):
|
|||||||
raise AppArmorException(_('Unable to read first line from %s: File Not Found') % file)
|
raise AppArmorException(_('Unable to read first line from %s: File Not Found') % file)
|
||||||
|
|
||||||
|
|
||||||
|
def check_output_dir(output_dir):
|
||||||
|
if os.path.isdir(output_dir):
|
||||||
|
return True
|
||||||
|
elif os.path.exists(output_dir):
|
||||||
|
raise AppArmorException(_("%(dir) exists and is not a directory") % {'dir': output_dir})
|
||||||
|
try:
|
||||||
|
os.mkdir(output_dir, mode=0o700)
|
||||||
|
except OSError as e:
|
||||||
|
raise AppArmorException(
|
||||||
|
_("Unable to create output directory %(dir)s\n\t%(error)s")
|
||||||
|
% {'dir': output_dir, 'error': str(e)}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_output(params):
|
def get_output(params):
|
||||||
"""Runs the program with the given args and returns the return code and stdout (as list of lines)"""
|
"""Runs the program with the given args and returns the return code and stdout (as list of lines)"""
|
||||||
try:
|
try:
|
||||||
@@ -898,6 +919,7 @@ def ask_exec(hashlog):
|
|||||||
|
|
||||||
exec_toggle = False
|
exec_toggle = False
|
||||||
q.functions.extend(build_x_functions(default, options, exec_toggle))
|
q.functions.extend(build_x_functions(default, options, exec_toggle))
|
||||||
|
q.already_have_profile = get_profile_filename_from_attachment(exec_target)
|
||||||
|
|
||||||
# ask user about the exec mode to use
|
# ask user about the exec mode to use
|
||||||
ans = ''
|
ans = ''
|
||||||
@@ -1202,7 +1224,8 @@ def ask_rule_questions(prof_events, profile_name, the_profile, r_types):
|
|||||||
newincludes = match_includes(the_profile, ruletype, rule_obj)
|
newincludes = match_includes(the_profile, ruletype, rule_obj)
|
||||||
q = aaui.PromptQuestion()
|
q = aaui.PromptQuestion()
|
||||||
if newincludes:
|
if newincludes:
|
||||||
options.extend(map(lambda inc: 'include <%s>' % inc, sorted(set(newincludes))))
|
if use_abstractions:
|
||||||
|
options.extend(map(lambda inc: 'include <%s>' % inc, sorted(set(newincludes))))
|
||||||
|
|
||||||
if ruletype == 'file' and rule_obj.path:
|
if ruletype == 'file' and rule_obj.path:
|
||||||
options += propose_file_rules(the_profile, rule_obj)
|
options += propose_file_rules(the_profile, rule_obj)
|
||||||
@@ -1270,15 +1293,20 @@ def ask_rule_questions(prof_events, profile_name, the_profile, r_types):
|
|||||||
|
|
||||||
the_profile['inc_ie'].add(IncludeRule.create_instance(selection))
|
the_profile['inc_ie'].add(IncludeRule.create_instance(selection))
|
||||||
|
|
||||||
aaui.UI_Info(_('Adding %s to profile.') % selection)
|
if aaui.UI_mode == 'allow_all':
|
||||||
|
aaui.UI_Info(_('Adding %s to profile %s.') % (selection, profile_name))
|
||||||
|
else:
|
||||||
|
aaui.UI_Info(_('Adding %s to profile.') % selection)
|
||||||
if deleted:
|
if deleted:
|
||||||
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
|
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
rule_obj = rule_obj.create_instance(selection)
|
rule_obj = rule_obj.create_instance(selection)
|
||||||
deleted = the_profile[ruletype].add(rule_obj, cleanup=True)
|
deleted = the_profile[ruletype].add(rule_obj, cleanup=True)
|
||||||
|
if aaui.UI_mode == 'allow_all':
|
||||||
aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean())
|
aaui.UI_Info(_('Adding %s to profile %s.') % (rule_obj.get_clean(), profile_name))
|
||||||
|
else:
|
||||||
|
aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean())
|
||||||
if deleted:
|
if deleted:
|
||||||
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
|
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
|
||||||
|
|
||||||
@@ -1539,7 +1567,7 @@ def set_logfile(filename):
|
|||||||
raise AppArmorException(_('%s is a directory. Please specify a file as logfile') % logfile)
|
raise AppArmorException(_('%s is a directory. Please specify a file as logfile') % logfile)
|
||||||
|
|
||||||
|
|
||||||
def do_logprof_pass(logmark=''):
|
def do_logprof_pass(logmark='', out_dir=None):
|
||||||
# set up variables for this pass
|
# set up variables for this pass
|
||||||
global active_profiles
|
global active_profiles
|
||||||
global sev_db
|
global sev_db
|
||||||
@@ -1563,10 +1591,10 @@ def do_logprof_pass(logmark=''):
|
|||||||
|
|
||||||
ask_the_questions(log_dict)
|
ask_the_questions(log_dict)
|
||||||
|
|
||||||
save_profiles()
|
save_profiles(out_dir=out_dir)
|
||||||
|
|
||||||
|
|
||||||
def save_profiles(is_mergeprof=False):
|
def save_profiles(is_mergeprof=False, out_dir=None):
|
||||||
# Ensure the changed profiles are actual active profiles
|
# Ensure the changed profiles are actual active profiles
|
||||||
for prof_name in changed.keys():
|
for prof_name in changed.keys():
|
||||||
if not aa.get(prof_name, False):
|
if not aa.get(prof_name, False):
|
||||||
@@ -1600,7 +1628,7 @@ def save_profiles(is_mergeprof=False):
|
|||||||
profile_name = options[arg]
|
profile_name = options[arg]
|
||||||
|
|
||||||
if ans == 'CMD_SAVE_SELECTED':
|
if ans == 'CMD_SAVE_SELECTED':
|
||||||
write_profile_ui_feedback(profile_name)
|
write_profile_ui_feedback(profile_name, out_dir=out_dir)
|
||||||
reload_base(profile_name)
|
reload_base(profile_name)
|
||||||
q.selected = 0 # saving the selected profile removes it from the list, therefore reset selection
|
q.selected = 0 # saving the selected profile removes it from the list, therefore reset selection
|
||||||
|
|
||||||
@@ -1626,7 +1654,7 @@ def save_profiles(is_mergeprof=False):
|
|||||||
changed.pop(options[arg])
|
changed.pop(options[arg])
|
||||||
|
|
||||||
for profile_name in sorted(changed.keys()):
|
for profile_name in sorted(changed.keys()):
|
||||||
write_profile_ui_feedback(profile_name)
|
write_profile_ui_feedback(profile_name, out_dir=out_dir)
|
||||||
reload_base(profile_name)
|
reload_base(profile_name)
|
||||||
|
|
||||||
|
|
||||||
@@ -2281,12 +2309,12 @@ def serialize_profile(profile_data, name, options):
|
|||||||
return string + '\n'
|
return string + '\n'
|
||||||
|
|
||||||
|
|
||||||
def write_profile_ui_feedback(profile, is_attachment=False):
|
def write_profile_ui_feedback(profile, is_attachment=False, out_dir=None):
|
||||||
aaui.UI_Info(_('Writing updated profile for %s.') % profile)
|
aaui.UI_Info(_('Writing updated profile for %s.') % profile)
|
||||||
write_profile(profile, is_attachment)
|
write_profile(profile, is_attachment, out_dir=out_dir)
|
||||||
|
|
||||||
|
|
||||||
def write_profile(profile, is_attachment=False):
|
def write_profile(profile, is_attachment=False, out_dir=None):
|
||||||
if aa[profile][profile].get('filename', False):
|
if aa[profile][profile].get('filename', False):
|
||||||
prof_filename = aa[profile][profile]['filename']
|
prof_filename = aa[profile][profile]['filename']
|
||||||
elif is_attachment:
|
elif is_attachment:
|
||||||
@@ -2296,8 +2324,9 @@ def write_profile(profile, is_attachment=False):
|
|||||||
|
|
||||||
serialize_options = {'METADATA': True, 'is_attachment': is_attachment}
|
serialize_options = {'METADATA': True, 'is_attachment': is_attachment}
|
||||||
profile_string = serialize_profile(split_to_merged(aa), profile, serialize_options)
|
profile_string = serialize_profile(split_to_merged(aa), profile, serialize_options)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with NamedTemporaryFile('w', suffix='~', delete=False, dir=profile_dir) as newprof:
|
with NamedTemporaryFile('w', suffix='~', delete=False, dir=out_dir or profile_dir) as newprof:
|
||||||
if os.path.exists(prof_filename):
|
if os.path.exists(prof_filename):
|
||||||
shutil.copymode(prof_filename, newprof.name)
|
shutil.copymode(prof_filename, newprof.name)
|
||||||
else:
|
else:
|
||||||
@@ -2308,7 +2337,11 @@ def write_profile(profile, is_attachment=False):
|
|||||||
except PermissionError as e:
|
except PermissionError as e:
|
||||||
raise AppArmorException(e)
|
raise AppArmorException(e)
|
||||||
|
|
||||||
os.rename(newprof.name, prof_filename)
|
if out_dir is None:
|
||||||
|
os.rename(newprof.name, prof_filename)
|
||||||
|
else:
|
||||||
|
out_filename = out_dir + "/" + prof_filename.split('/')[-1]
|
||||||
|
os.rename(newprof.name, out_filename)
|
||||||
|
|
||||||
if profile in changed:
|
if profile in changed:
|
||||||
changed.pop(profile)
|
changed.pop(profile)
|
||||||
|
@@ -75,6 +75,11 @@ def set_text_mode():
|
|||||||
UI_mode = 'text'
|
UI_mode = 'text'
|
||||||
|
|
||||||
|
|
||||||
|
def set_allow_all_mode():
|
||||||
|
global UI_mode
|
||||||
|
UI_mode = 'allow_all'
|
||||||
|
|
||||||
|
|
||||||
# reads the response on command line for json and verifies the response
|
# reads the response on command line for json and verifies the response
|
||||||
# for the dialog type
|
# for the dialog type
|
||||||
def json_response(dialog_type):
|
def json_response(dialog_type):
|
||||||
@@ -151,7 +156,10 @@ def UI_YesNo(text, default):
|
|||||||
sys.stdout.write('\n[%s] / %s\n' % (yes, no))
|
sys.stdout.write('\n[%s] / %s\n' % (yes, no))
|
||||||
else:
|
else:
|
||||||
sys.stdout.write('\n%s / [%s]\n' % (yes, no))
|
sys.stdout.write('\n%s / [%s]\n' % (yes, no))
|
||||||
ans = getkey()
|
if UI_mode == 'allow_all':
|
||||||
|
ans = nokey
|
||||||
|
else:
|
||||||
|
ans = getkey()
|
||||||
if ans:
|
if ans:
|
||||||
# Get back to english from localised answer
|
# Get back to english from localised answer
|
||||||
ans = ans.lower()
|
ans = ans.lower()
|
||||||
@@ -197,7 +205,10 @@ def UI_YesNoCancel(text, default):
|
|||||||
sys.stdout.write('\n%s / [%s] / %s\n' % (yes, no, cancel))
|
sys.stdout.write('\n%s / [%s] / %s\n' % (yes, no, cancel))
|
||||||
else:
|
else:
|
||||||
sys.stdout.write('\n%s / %s / [%s]\n' % (yes, no, cancel))
|
sys.stdout.write('\n%s / %s / [%s]\n' % (yes, no, cancel))
|
||||||
ans = getkey()
|
if UI_mode == 'allow_all':
|
||||||
|
ans = nokey
|
||||||
|
else:
|
||||||
|
ans = getkey()
|
||||||
if ans:
|
if ans:
|
||||||
# Get back to english from localised answer
|
# Get back to english from localised answer
|
||||||
ans = ans.lower()
|
ans = ans.lower()
|
||||||
@@ -377,6 +388,7 @@ class PromptQuestion:
|
|||||||
default = None
|
default = None
|
||||||
selected = None
|
selected = None
|
||||||
helptext = None
|
helptext = None
|
||||||
|
already_have_profile = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.headers = []
|
self.headers = []
|
||||||
@@ -502,6 +514,17 @@ class PromptQuestion:
|
|||||||
hm = json_response('promptuser')
|
hm = json_response('promptuser')
|
||||||
ans = hm["response_key"]
|
ans = hm["response_key"]
|
||||||
selected = hm["selected"]
|
selected = hm["selected"]
|
||||||
|
|
||||||
|
elif UI_mode == 'allow_all':
|
||||||
|
if self.already_have_profile:
|
||||||
|
expected_keys = ['CMD_px', 'CMD_ix', 'CMD_ALLOW', 'CMD_SAVE_CHANGES']
|
||||||
|
else:
|
||||||
|
expected_keys = ['CMD_ix', 'CMD_ALLOW', 'CMD_SAVE_CHANGES']
|
||||||
|
for exp in expected_keys:
|
||||||
|
if exp in functions:
|
||||||
|
ans = get_translated_hotkey(CMDS[exp])
|
||||||
|
break
|
||||||
|
|
||||||
else: # text mode
|
else: # text mode
|
||||||
sys.stdout.write(prompt + '\n')
|
sys.stdout.write(prompt + '\n')
|
||||||
ans = getkey().lower()
|
ans = getkey().lower()
|
||||||
@@ -517,6 +540,7 @@ class PromptQuestion:
|
|||||||
selected += 1
|
selected += 1
|
||||||
ans = 'XXXINVALIDXXX'
|
ans = 'XXXINVALIDXXX'
|
||||||
|
|
||||||
|
|
||||||
# elif keys.get(ans, False) == 'CMD_HELP':
|
# elif keys.get(ans, False) == 'CMD_HELP':
|
||||||
# sys.stdout.write('\n%s\n' %helptext)
|
# sys.stdout.write('\n%s\n' %helptext)
|
||||||
# ans = 'XXXINVALIDXXX'
|
# ans = 'XXXINVALIDXXX'
|
||||||
|
6
utils/test/logprof/ping.allowlog
Normal file
6
utils/test/logprof/ping.allowlog
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Updating AppArmor profiles in /etc/apparmor.d.
|
||||||
|
Reading log entries from /var/log/audit/audit.log.
|
||||||
|
Complain-mode changes:
|
||||||
|
Enforce-mode changes:
|
||||||
|
Adding owner /proc/*/cmdline r, to profile ping.
|
||||||
|
Writing updated profile for ping.
|
@@ -40,13 +40,13 @@ class TestLogprof(AATest):
|
|||||||
def AATeardown(self):
|
def AATeardown(self):
|
||||||
self._terminate()
|
self._terminate()
|
||||||
|
|
||||||
def _startLogprof(self, auditlog):
|
def _startLogprof(self, auditlog, mode):
|
||||||
exe = [sys.executable]
|
exe = [sys.executable]
|
||||||
|
|
||||||
if 'coverage' in sys.modules:
|
if 'coverage' in sys.modules:
|
||||||
exe = exe + ['-m', 'coverage', 'run', '--branch', '-p']
|
exe = exe + ['-m', 'coverage', 'run', '--branch', '-p']
|
||||||
|
|
||||||
exe = exe + ['../aa-logprof', '--json', '--configdir', './', '-f', auditlog, '-d', self.profile_dir, '--no-check-mountpoint']
|
exe = exe + ['../aa-logprof', '--' + mode, '--configdir', './', '-f', auditlog, '-d', self.profile_dir, '--no-check-mountpoint', '--output-dir', self.tmpdir]
|
||||||
|
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
exe,
|
exe,
|
||||||
@@ -76,7 +76,7 @@ class TestLogprof(AATest):
|
|||||||
jlog = jlog.replace('/var/log/audit/audit.log', auditlog)
|
jlog = jlog.replace('/var/log/audit/audit.log', auditlog)
|
||||||
jlog = jlog.strip().split('\n')
|
jlog = jlog.strip().split('\n')
|
||||||
|
|
||||||
self.process = self._startLogprof(auditlog)
|
self.process = self._startLogprof(auditlog, 'json')
|
||||||
|
|
||||||
for line in jlog:
|
for line in jlog:
|
||||||
if line.startswith('o '): # read from stdout
|
if line.startswith('o '): # read from stdout
|
||||||
@@ -101,7 +101,7 @@ class TestLogprof(AATest):
|
|||||||
|
|
||||||
for file in expected:
|
for file in expected:
|
||||||
exp = read_file('./logprof/%s.%s' % (params, file))
|
exp = read_file('./logprof/%s.%s' % (params, file))
|
||||||
actual = read_file(os.path.join(self.profile_dir, file))
|
actual = read_file(os.path.join(self.tmpdir, file))
|
||||||
|
|
||||||
# remove '# Last Modified:' line from updated profile
|
# remove '# Last Modified:' line from updated profile
|
||||||
actual = actual.split('\n')
|
actual = actual.split('\n')
|
||||||
@@ -111,6 +111,23 @@ class TestLogprof(AATest):
|
|||||||
|
|
||||||
self.assertEqual(actual, exp)
|
self.assertEqual(actual, exp)
|
||||||
|
|
||||||
|
def test_allow_all(self):
|
||||||
|
auditlog = './logprof/%s.auditlog' % 'ping'
|
||||||
|
allowlog = './logprof/%s.allowlog' % 'ping'
|
||||||
|
|
||||||
|
slog = read_file(allowlog)
|
||||||
|
slog = slog.replace('/etc/apparmor.d', self.profile_dir)
|
||||||
|
slog = slog.replace('/var/log/audit/audit.log', auditlog)
|
||||||
|
slog = slog.strip().split('\n')
|
||||||
|
|
||||||
|
self.process = self._startLogprof(auditlog, 'allow-all')
|
||||||
|
|
||||||
|
for line in slog:
|
||||||
|
output = self.process.stdout.readline().decode("utf-8").strip()
|
||||||
|
self.assertEqual(output, line)
|
||||||
|
# give logprof some time to write the updated profile and terminate
|
||||||
|
self.process.wait(timeout=0.3)
|
||||||
|
self.assertEqual(self.process.returncode, 0)
|
||||||
|
|
||||||
# if you import apparmor.aa and call init_aa() in your tests, uncomment this
|
# if you import apparmor.aa and call init_aa() in your tests, uncomment this
|
||||||
# setup_aa(aa)
|
# setup_aa(aa)
|
||||||
|
Reference in New Issue
Block a user