Merge lp:~canonical-platform-qa/qakit/appamor-utilities into lp:qakit

Proposed by Heber Parrucci
Status: Merged
Merged at revision: 200
Proposed branch: lp:~canonical-platform-qa/qakit/appamor-utilities
Merge into: lp:qakit
Diff against target: 156 lines (+146/-0)
2 files modified
qakit/apparmor/__init__.py (+18/-0)
qakit/apparmor/apparmor_utils.py (+128/-0)
To merge this branch: bzr merge lp:~canonical-platform-qa/qakit/appamor-utilities
Reviewer Review Type Date Requested Status
Santiago Baldassin (community) Approve
Review via email: mp+309374@code.launchpad.net

Commit message

adding apparmor utilities to update profiles with given rules.
update_profile: to update an apparmor profile/s
restore_profiles: to get back the original ones loaded in the system.
are_rules_present: to check if certain rules are present in a given profile.

Description of the change

Change contains utilities to update apparmor profiles with given rules.

We will need this to be able to run automated tests against snaps applications. The idea is that the tests call this in their setup before running the tests.

Utilities:
update_profile, are_rules_present, restore_profiles

To post a comment you must log in.
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Code looks good to me

review: Approve
200. By Heber Parrucci

merge from trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'qakit/apparmor'
2=== added file 'qakit/apparmor/__init__.py'
3--- qakit/apparmor/__init__.py 1970-01-01 00:00:00 +0000
4+++ qakit/apparmor/__init__.py 2016-10-26 18:00:51 +0000
5@@ -0,0 +1,18 @@
6+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
7+#
8+# Apparmor Utils
9+# Copyright (C) 2016 Canonical
10+#
11+# This program is free software: you can redistribute it and/or modify
12+# it under the terms of the GNU General Public License as published by
13+# the Free Software Foundation, either version 3 of the License, or
14+# (at your option) any later version.
15+#
16+# This program is distributed in the hope that it will be useful,
17+# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+# GNU General Public License for more details.
20+#
21+# You should have received a copy of the GNU General Public License
22+# along with this program. If not, see <http://www.gnu.org/licenses/>.
23+#
24
25=== added file 'qakit/apparmor/apparmor_utils.py'
26--- qakit/apparmor/apparmor_utils.py 1970-01-01 00:00:00 +0000
27+++ qakit/apparmor/apparmor_utils.py 2016-10-26 18:00:51 +0000
28@@ -0,0 +1,128 @@
29+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
30+#
31+# Apparmor Utils
32+# Copyright (C) 2016 Canonical
33+#
34+# This program is free software: you can redistribute it and/or modify
35+# it under the terms of the GNU General Public License as published by
36+# the Free Software Foundation, either version 3 of the License, or
37+# (at your option) any later version.
38+#
39+# This program is distributed in the hope that it will be useful,
40+# but WITHOUT ANY WARRANTY; without even the implied warranty of
41+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42+# GNU General Public License for more details.
43+#
44+# You should have received a copy of the GNU General Public License
45+# along with this program. If not, see <http://www.gnu.org/licenses/>.
46+#
47+
48+import os
49+
50+PROFILES_DIR = '/var/lib/snapd/apparmor/profiles'
51+BACKUP_DIR = '/tmp/profiles_bkp'
52+APPARMOR_PARSER_CMD = 'apparmor_parser -r'
53+
54+
55+def update_profile(rules,
56+ profiles=None,
57+ profiles_dir=PROFILES_DIR,
58+ password=None):
59+ """Update apparmor's profile/s with given rules file,
60+ it concatenates the rules to the original profile.
61+ Before updating the profile/s it does a backup of the original
62+ one so they can be restored later by calling to function:
63+ restore_profiles()
64+ :param rules: a string that represents the rules to add
65+ :param profiles: the profile name list to add
66+ the rules for.
67+ If None, all the profiles will be updated.
68+ If one profile already has the given rules,
69+ it will not be backed-up or updated.
70+ :param profiles_dir: directory path that contains apparmor profiles
71+ :param password: the password to execute the commands as superuser,
72+ if it is not provided,
73+ it will be read from the environment variable PASSWORD,
74+ if none of them are set, it will fail.
75+ """
76+ assert password or os.environ.get('PASSWORD'), \
77+ 'You either need to provide the password as ' \
78+ 'parameter or set the environment variable PASSWORD'
79+ password = password or os.environ.get('PASSWORD')
80+ found_profiles = os.listdir(profiles_dir)
81+ if profiles:
82+ found_profiles = [p for p in profiles if p in found_profiles]
83+ __ensure_backup_dir()
84+ for profile in found_profiles:
85+ if not are_rules_present(rules, profile):
86+ profile_full_path = os.path.join(profiles_dir, profile)
87+ # backup up original profile to restore later
88+ __run_with_sudo('cp %s %s' % (profile_full_path,
89+ os.path.join(BACKUP_DIR,
90+ profile)),
91+ password)
92+ original_rules = open(profile_full_path, mode='r').read()
93+ with open('/tmp/%s.tmp' % profile, 'w') as temp:
94+ # write to tmp file: original rules + new ones
95+ temp.write(original_rules.rstrip('}\n'))
96+ temp.write('\n\n')
97+ temp.write(rules)
98+
99+ # replace original profile with the updated one
100+ __run_with_sudo('mv %s %s' % ('/tmp/%s.tmp' % profile,
101+ profile_full_path), password)
102+ # refresh appamor profile
103+ __run_with_sudo('%s %s' % (APPARMOR_PARSER_CMD,
104+ profile_full_path),
105+ password)
106+
107+
108+def are_rules_present(rules, profile_name, profiles_dir=PROFILES_DIR):
109+ """Check in the apparmor profile if the given rules are present
110+ :param rules: a string that represents the rules to check
111+ :param profiles_dir: directory path that contains apparmor profiles
112+ :param profile_name: the profile name to check
113+ :return True if the rules rules are present in the given profile,
114+ False otherwise
115+ """
116+ with open(os.path.join(profiles_dir, profile_name), mode='r') as p:
117+ return p.read().find(rules) > 0
118+
119+
120+def restore_profiles(profiles=None, profiles_dir=PROFILES_DIR, password=None):
121+ """Restore the already backed-up profile/s
122+ If there are no profiles backed-up, it will do nothing
123+ :param profiles: the profiles name list to restore.
124+ If None, it will restore all the profiles present in the backup directory
125+ :param profiles_dir: directory path that contains apparmor profiles
126+ :param password: the password to execute the commands as superuser,
127+ if it is not provided,
128+ it will be read from the environment variable PASSWORD,
129+ if none of them are set, it will fail.
130+ """
131+ assert password or os.environ.get('PASSWORD'), \
132+ 'You either need to provide the password as ' \
133+ 'parameter or set the environment variable PASSWORD'
134+ password = password or os.environ.get('PASSWORD')
135+ found_profiles = os.listdir(profiles_dir)
136+ if profiles:
137+ found_profiles = [p for p in profiles if p in found_profiles]
138+ for bkp in found_profiles:
139+ if bkp in os.listdir(BACKUP_DIR):
140+ # restore profile
141+ __run_with_sudo('mv %s %s' % (os.path.join(BACKUP_DIR, bkp),
142+ os.path.join(profiles_dir, bkp)),
143+ password)
144+ # refresh appamor profile
145+ __run_with_sudo('%s %s' % (APPARMOR_PARSER_CMD,
146+ os.path.join(profiles_dir, bkp)),
147+ password)
148+
149+
150+def __ensure_backup_dir():
151+ if not os.path.exists(BACKUP_DIR):
152+ os.makedirs(BACKUP_DIR)
153+
154+
155+def __run_with_sudo(command, password):
156+ return os.system('echo %s|sudo -S %s 2>/dev/null' % (password, command))

Subscribers

People subscribed via source and target branches

to all changes: