mirror_lxc/config/apparmor/lxc-generate-aa-rules.py
Serge Hallyn 198b363fff apparmor: auto-generate the blacklist rules
This uses the generate-apparmor-rules.py script I sent out some time
ago to auto-generate apparmor rules based on a higher level set of
block/allow rules.

Add apparmor policy testcase to make sure that some of the paths we
expect to be denied (and allowed) write access to are in fact in
effect in the final policy.

With this policy, libvirt in a container is able to start its
default network, which previously it could not.

v2: address feedback from stgraber
	  put lxc-generate-aa-rules.py into EXTRA_DIST
	  add lxc-test-apparmor, container-base and container-rules to .gitignore
	  take lxc-test-apparmor out of EXTRA_DIST
	  make lxc-generate-aa-rules.py pep8-compliant
	  don't automatically generate apparmor rules
	  This is only bc we can't be guaranteed that python3 will be
	  available.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Acked-by: Stéphane Graber <stgraber@ubuntu.com>
2014-04-01 13:49:43 -04:00

136 lines
3.4 KiB
Python
Executable File

#!/usr/bin/python3
import sys
blocks = []
#
# blocks is an array of paths under which we want to block by
# default.
#
# blocks[0] = ['path' = '/sys', 'children' = [A,B] ]
# blocks[1] = ['path' = '/proc/sys', 'children' = [ E ] ]
# A = [ 'path' = 'fs', children = [C] ]
# C = [ 'path' = 'cgroup', children = [F] ]
# B = [ 'path' = 'class', children = [D] ]
# D = [ 'path' = 'net', children = [F] ]
# E = [ 'path' = 'shm*' ]
# F = [ 'path' = '**' ]
def add_block(path):
for b in blocks:
if b['path'] == path:
# duplicate
return
blocks.append({'path': path.strip(), 'children': []})
def child_get(prev, path):
for p in prev:
if p['path'] == path:
return p
return None
def add_allow(path):
# find which block we belong to
found = None
for b in blocks:
l = len(b['path'])
if len(path) <= l:
continue
if path[0:l] == b['path']:
found = b
break
if found is None:
print("allow with no previous block at %s" % path)
sys.exit(1)
p = path[l:].strip()
while p[:1] == "/":
p = p[1:]
prev = b['children']
for s in p.split('/'):
n = {'path': s.strip(), 'children': []}
tmp = child_get(prev, n['path'])
if tmp is not None:
prev = tmp
else:
prev.append(n)
prev = n['children']
config = "config"
if len(sys.argv) > 1:
config = sys.argv[1]
with open(config) as f:
for x in f.readlines():
x.strip()
if x[:1] == '#':
continue
try:
(cmd, path) = x.split(' ')
except: # blank line
continue
if cmd == "block":
add_block(path)
elif cmd == "allow":
add_allow(path)
else:
print("Unknown command: %s" % cmd)
sys.exit(1)
denies = []
def collect_chars(children, ref, index):
r = ""
for c in children:
if index >= len(c['path']):
continue
if ref[0:index] != c['path'][0:index]:
continue
if c['path'][index] not in r:
r = r + c['path'][index]
return r
def append_deny(s):
s = "%s wklx," % s
if s not in denies:
denies.append(s)
def gen_denies(pathsofar, children):
for c in children:
for char in range(len(c['path'])):
if char == len(c['path'])-1 and c['path'][char] == '*':
continue
if char == len(c['path'])-2:
if c['path'][char:char+2] == '**':
continue
x = collect_chars(children, c['path'], char)
newdeny = "deny %s/%s[^%s]*{,/**}" % (pathsofar,
c['path'][0:char], x)
append_deny(newdeny)
if c['path'] != '**' and c['path'][len(c['path'])-1] != '*':
newdeny = "deny %s/%s?*{,/**}" % (pathsofar, c['path'])
append_deny(newdeny)
elif c['path'] != '**':
newdeny = "deny %s/%s/**" % (pathsofar, c['path'])
append_deny(newdeny)
if len(c['children']) != 0:
newpath = "%s/%s" % (pathsofar, c['path'])
gen_denies(newpath, c['children'])
for b in blocks:
gen_denies(b['path'], b['children'])
denies.sort()
genby = " # generated by: lxc-generate-aa-rules.py"
for a in sys.argv[1:]:
genby += " %s" % a
print(genby)
for d in denies:
print(" %s" % d)