Skip to content
Snippets Groups Projects
Unverified Commit a4ea1e09 authored by Christian Boltz's avatar Christian Boltz
Browse files

Add aa-logprof test framework

... and a simple test for a single (fake) event for ping.
parent 313366fb
No related branches found
No related tags found
No related merge requests found
type=AVC msg=audit(1691930856.284:29963): apparmor="DENIED" operation="open" class="file" profile="ping" name="/proc/21622/cmdline" pid=9136 comm="cat" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
type=SYSCALL msg=audit(1691930856.284:29963): arch=c000003e syscall=257 success=no exit=-13 a0=ffffff9c a1=7ffc4539abf8 a2=0 a3=0 items=0 ppid=21622 pid=9136 auid=1000 uid=1000 gid=100 euid=1000 suid=1000 fsuid=1000 egid=100 sgid=100 fsgid=100 tty=pts4 ses=2 comm="cat" exe="/usr/bin/cat" subj=ping key=(null)
type=AVC msg=audit(1691930881.661:29975): apparmor="STATUS" operation="profile_replace" profile="apparmor_parser" name="ping" pid=10005 comm="apparmor_parser"
abi <abi/4.0>,
include <tunables/global>
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2010 Canonical Ltd.
#
# 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.
#
# ------------------------------------------------------------------
profile ping /{usr/,}bin/{,iputils-}ping {
include <abstractions/base>
include <abstractions/consoles>
include <abstractions/nameservice>
include if exists <local/bin.ping>
capability net_raw,
capability setuid,
network inet raw,
network inet6 raw,
/etc/modules.conf r,
/proc/21622/cmdline r,
/{,usr/}bin/{,iputils-}ping mrix,
}
o {"dialog": "apparmor-json-version","data": "2.12"}
o {"dialog": "info","data": "Updating AppArmor profiles in /etc/apparmor.d."}
o {"dialog": "info","data": "Reading log entries from /var/log/audit/audit.log."}
o {"dialog": "info","data": "Complain-mode changes:"}
o {"dialog": "info","data": "Enforce-mode changes:"}
o {"dialog": "promptuser","title": null,"headers": {"Profile": "ping","Path": "/proc/21622/cmdline","New Mode": "owner r","Severity": 6},"explanation": null,"options": ["owner /proc/*/cmdline r,","owner /proc/21622/cmdline r,"],"menu_items": ["(A)llow","[(D)eny]","(I)gnore","(G)lob","Glob with (E)xtension","(N)ew","Audi(t)","(O)wner permissions off","Abo(r)t","(F)inish"],"default_key": "d"}
i {"dialog":"promptuser","selected":0,"response_key":"o"}
o {"dialog": "promptuser","title": null,"headers": {"Profile": "ping","Path": "/proc/21622/cmdline","New Mode": "r","Severity": 6},"explanation": null,"options": ["/proc/*/cmdline r,","/proc/21622/cmdline r,"],"menu_items": ["(A)llow","[(D)eny]","(I)gnore","(G)lob","Glob with (E)xtension","(N)ew","Audi(t)","(O)wner permissions on","Abo(r)t","(F)inish"],"default_key": "d"}
i {"dialog":"promptuser","selected":1,"response_key":"a"}
o {"dialog": "info","data": "Adding /proc/21622/cmdline r, to profile."}
o {"dialog": "promptuser","title": "Changed Local Profiles","headers": {},"explanation": "The following local profiles were changed. Would you like to save them?","options": ["ping"],"menu_items": ["(S)ave Changes","Save Selec(t)ed Profile","[(V)iew Changes]","View Changes b/w (C)lean profiles","Abo(r)t"],"default_key": "v"}
i {"dialog":"promptuser","selected":0,"response_key":"t"}
o {"dialog": "info","data": "Writing updated profile for ping."}
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2023 Christian Boltz <apparmor@cboltz.de>
#
# 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.
#
# ------------------------------------------------------------------
import os
import shutil
import subprocess
import unittest
# import apparmor.aa as aa # see the setup_aa() call for details
from common_test import AATest, read_file, setup_all_loops # , setup_aa
class TestLogprof(AATest):
# This test expects a set of files:
# - logprof/TESTNAME.auditlog - audit.log
# - logprof/TESTNAME.jsonlog - expected aa-logprof --json input and output (gathered with json_log=1 in logprof.conf)
# - logprof/TESTNAME.PROFILE - one or more profiles in the expected state
# where TESTNAME is the name given in the first column of 'tests'
tests = (
# test name # profiles to verify
('ping', ['bin.ping']),
)
def AASetup(self):
self.createTmpdir()
# copy the local profiles to the test directory
self.profile_dir = self.tmpdir + '/profiles'
shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True)
def AATeardown(self):
self._terminate()
def _startLogprof(self, auditlog):
exe = ['python3'] # TODO: get actual interpreter (from sys.??)
# if with_coverage:
# exe = exe + ['-m', 'coverage']
exe = exe + ['../aa-logprof', '--json', '--configdir', './', '-f', auditlog, '-d', self.profile_dir]
process = subprocess.Popen(
exe,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
# stderr=subprocess.STDOUT,
env={'LANG': 'C'},
)
return process
def _terminate(self):
self.process.stdin.close()
self.process.stdout.close()
self.process.terminate()
self.process.wait(timeout=0.2)
def _run_test(self, params, expected):
auditlog = './logprof/%s.auditlog' % params
jsonlog = './logprof/%s.jsonlog' % params
jlog = read_file(jsonlog)
jlog = jlog.replace('/etc/apparmor.d', self.profile_dir)
jlog = jlog.replace('/var/log/audit/audit.log', auditlog)
jlog = jlog.strip().split('\n')
self.process = self._startLogprof(auditlog)
for line in jlog:
if line.startswith('o '): # read from stdout
output = self.process.stdout.readline().decode("utf-8").strip()
self.assertEqual(output, line[2:])
elif line.startswith('i '): # send to stdin
# expect an empty prompt line
output = self.process.stdout.readline().decode("utf-8").strip()
self.assertEqual(output, '')
# "type" answer
self.process.stdin.write(line[2:].encode("utf-8") + b"\n")
self.process.stdin.flush()
else:
raise Exception('Unknown line in json log %s: %s' % (jsonlog, line))
# give logprof some time to write the updated profile and terminate
self.process.wait(timeout=0.2)
self.assertEqual(self.process.returncode, 0)
for file in expected:
exp = read_file('./logprof/%s.%s' % (params, file))
actual = read_file(os.path.join(self.profile_dir, file))
# remove '# Last Modified:' line from updated profile
actual = actual.split('\n')
if actual[0].startswith('# Last Modified:'):
actual = actual[1:]
actual = '\n'.join(actual)
self.assertEqual(actual, exp)
# if you import apparmor.aa and call init_aa() in your tests, uncomment this
# setup_aa(aa)
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment