Merge lp:~thedac/charm-helpers/apparmor into lp:charm-helpers

Proposed by David Ames
Status: Merged
Merged at revision: 559
Proposed branch: lp:~thedac/charm-helpers/apparmor
Merge into: lp:charm-helpers
Diff against target: 214 lines (+177/-1)
2 files modified
charmhelpers/contrib/openstack/context.py (+91/-1)
tests/contrib/openstack/test_os_contexts.py (+86/-0)
To merge this branch: bzr merge lp:~thedac/charm-helpers/apparmor
Reviewer Review Type Date Requested Status
James Page Approve
Review via email: mp+290096@code.launchpad.net

Description of the change

Apparmor class for OpenStack charms

The class will validate aa-profile-mode config settings and either set
the profile to enforce, complain or disable mode.

To post a comment you must log in.
lp:~thedac/charm-helpers/apparmor updated
556. By David Ames

Fix call to manually_disable_aa_profile()

557. By David Ames

Move mitaka check into base AA class

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

See my inline comments. They don't alter functionality, but change an appArmorContext.ctxt to a property that is set only once regardless of whether returned in the __call__() or directly by instance.ctxt. However, it's a style thing only.

Otherwise, good to go.

lp:~thedac/charm-helpers/apparmor updated
558. By David Ames

Set self.ctxt as a property

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Just a tiny inline comment.

lp:~thedac/charm-helpers/apparmor updated
559. By David Ames

Use self() rather than self.__call__()

Revision history for this message
James Page (james-page) :
review: Needs Fixing
Revision history for this message
David Ames (thedac) wrote :

I'll address the comments.

lp:~thedac/charm-helpers/apparmor updated
560. By David Ames

Remove python3 setting, add comments for manual disable

Revision history for this message
James Page (james-page) wrote :

LGTM but does need a rebase prior to merge - please land away!

review: Approve
lp:~thedac/charm-helpers/apparmor updated
561. By David Ames

Merge upstream

Revision history for this message
David Ames (thedac) wrote :

Rebased. Merging.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/contrib/openstack/context.py'
--- charmhelpers/contrib/openstack/context.py 2016-04-01 10:00:31 +0000
+++ charmhelpers/contrib/openstack/context.py 2016-04-04 18:08:06 +0000
@@ -20,7 +20,7 @@
20import re20import re
21import time21import time
22from base64 import b64decode22from base64 import b64decode
23from subprocess import check_call23from subprocess import check_call, CalledProcessError
2424
25import six25import six
26import yaml26import yaml
@@ -45,6 +45,7 @@
45 INFO,45 INFO,
46 WARNING,46 WARNING,
47 ERROR,47 ERROR,
48 status_set,
48)49)
4950
50from charmhelpers.core.sysctl import create as sysctl_create51from charmhelpers.core.sysctl import create as sysctl_create
@@ -1491,3 +1492,92 @@
1491 """1492 """
1492 def __call__(self):1493 def __call__(self):
1493 return {'use_internal_endpoints': config('use-internal-endpoints')}1494 return {'use_internal_endpoints': config('use-internal-endpoints')}
1495
1496
1497class AppArmorContext(OSContextGenerator):
1498 """Base class for apparmor contexts."""
1499
1500 def __init__(self):
1501 self._ctxt = None
1502 self.aa_profile = None
1503 self.aa_utils_packages = ['apparmor-utils']
1504
1505 @property
1506 def ctxt(self):
1507 if self._ctxt is not None:
1508 return self._ctxt
1509 self._ctxt = self._determine_ctxt()
1510 return self._ctxt
1511
1512 def _determine_ctxt(self):
1513 """
1514 Validate aa-profile-mode settings is disable, enforce, or complain.
1515
1516 :return ctxt: Dictionary of the apparmor profile or None
1517 """
1518 if config('aa-profile-mode') in ['disable', 'enforce', 'complain']:
1519 ctxt = {'aa-profile-mode': config('aa-profile-mode')}
1520 else:
1521 ctxt = None
1522 return ctxt
1523
1524 def __call__(self):
1525 return self.ctxt
1526
1527 def install_aa_utils(self):
1528 """
1529 Install packages required for apparmor configuration.
1530 """
1531 log("Installing apparmor utils.")
1532 ensure_packages(self.aa_utils_packages)
1533
1534 def manually_disable_aa_profile(self):
1535 """
1536 Manually disable an apparmor profile.
1537
1538 If aa-profile-mode is set to disabled (default) this is required as the
1539 template has been written but apparmor is yet unaware of the profile
1540 and aa-disable aa-profile fails. Without this the profile would kick
1541 into enforce mode on the next service restart.
1542
1543 """
1544 profile_path = '/etc/apparmor.d'
1545 disable_path = '/etc/apparmor.d/disable'
1546 if not os.path.lexists(os.path.join(disable_path, self.aa_profile)):
1547 os.symlink(os.path.join(profile_path, self.aa_profile),
1548 os.path.join(disable_path, self.aa_profile))
1549
1550 def setup_aa_profile(self):
1551 """
1552 Setup an apparmor profile.
1553 The ctxt dictionary will contain the apparmor profile mode and
1554 the apparmor profile name.
1555 Makes calls out to aa-disable, aa-complain, or aa-enforce to setup
1556 the apparmor profile.
1557 """
1558 self()
1559 if not self.ctxt:
1560 log("Not enabling apparmor Profile")
1561 return
1562 self.install_aa_utils()
1563 cmd = ['aa-{}'.format(self.ctxt['aa-profile-mode'])]
1564 cmd.append(self.ctxt['aa-profile'])
1565 log("Setting up the apparmor profile for {} in {} mode."
1566 "".format(self.ctxt['aa-profile'], self.ctxt['aa-profile-mode']))
1567 try:
1568 check_call(cmd)
1569 except CalledProcessError as e:
1570 # If aa-profile-mode is set to disabled (default) manual
1571 # disabling is required as the template has been written but
1572 # apparmor is yet unaware of the profile and aa-disable aa-profile
1573 # fails. If aa-disable learns to read profile files first this can
1574 # be removed.
1575 if self.ctxt['aa-profile-mode'] == 'disable':
1576 log("Manually disabling the apparmor profile for {}."
1577 "".format(self.ctxt['aa-profile']))
1578 self.manually_disable_aa_profile()
1579 return
1580 status_set('blocked', "Apparmor profile {} failed to be set to {}."
1581 "".format(self.ctxt['aa-profile'],
1582 self.ctxt['aa-profile-mode']))
1583 raise e
14941584
=== modified file 'tests/contrib/openstack/test_os_contexts.py'
--- tests/contrib/openstack/test_os_contexts.py 2016-04-01 10:15:57 +0000
+++ tests/contrib/openstack/test_os_contexts.py 2016-04-04 18:08:06 +0000
@@ -84,6 +84,21 @@
84 return None84 return None
85 return sorted(self.relation_data[relation_id].keys())85 return sorted(self.relation_data[relation_id].keys())
8686
87
88class FakeAppArmorContext(context.AppArmorContext):
89
90 def __init__(self):
91 super(FakeAppArmorContext, self).__init__()
92 self.aa_profile = 'fake-aa-profile'
93
94 def __call__(self):
95 super(FakeAppArmorContext, self).__call__()
96 if not self.ctxt:
97 return self.ctxt
98 self._ctxt.update({'aa-profile': self.aa_profile})
99 return self.ctxt
100
101
87SHARED_DB_RELATION = {102SHARED_DB_RELATION = {
88 'db_host': 'dbserver.local',103 'db_host': 'dbserver.local',
89 'password': 'foo'104 'password': 'foo'
@@ -2892,3 +2907,74 @@
2892 config = {'use-internal-endpoints': True}2907 config = {'use-internal-endpoints': True}
2893 self.config.side_effect = fake_config(config)2908 self.config.side_effect = fake_config(config)
2894 self.assertTrue(ctxt()['use_internal_endpoints'])2909 self.assertTrue(ctxt()['use_internal_endpoints'])
2910
2911 def test_apparmor_context_call_not_valid(self):
2912 ''' Tests for the apparmor context'''
2913 mock_aa_object = context.AppArmorContext()
2914 # Test with invalid config
2915 self.config.return_value = 'NOTVALID'
2916 self.assertEquals(mock_aa_object.__call__(), None)
2917
2918 def test_apparmor_context_call_complain(self):
2919 ''' Tests for the apparmor context'''
2920 mock_aa_object = context.AppArmorContext()
2921 # Test complain mode
2922 self.config.return_value = 'complain'
2923 self.assertEquals(mock_aa_object.__call__(),
2924 {'aa-profile-mode': 'complain'})
2925
2926 def test_apparmor_context_call_enforce(self):
2927 ''' Tests for the apparmor context'''
2928 mock_aa_object = context.AppArmorContext()
2929 # Test enforce mode
2930 self.config.return_value = 'enforce'
2931 self.assertEquals(mock_aa_object.__call__(),
2932 {'aa-profile-mode': 'enforce'})
2933
2934 def test_apparmor_context_call_disable(self):
2935 ''' Tests for the apparmor context'''
2936 mock_aa_object = context.AppArmorContext()
2937 # Test complain mode
2938 self.config.return_value = 'disable'
2939 self.assertEquals(mock_aa_object.__call__(),
2940 {'aa-profile-mode': 'disable'})
2941
2942 def test_apparmor_setup_complain(self):
2943 ''' Tests for the apparmor setup'''
2944 AA = FakeAppArmorContext()
2945 AA.install_aa_utils = MagicMock()
2946 AA.manually_disable_aa_profile = MagicMock()
2947 # Test complain mode
2948 self.config.return_value = 'complain'
2949 AA.setup_aa_profile()
2950 AA.install_aa_utils.assert_called_with()
2951 self.check_call.assert_called_with(['aa-complain', 'fake-aa-profile'])
2952 self.assertFalse(AA.manually_disable_aa_profile.called)
2953
2954 def test_apparmor_setup_enforce(self):
2955 ''' Tests for the apparmor setup'''
2956 AA = FakeAppArmorContext()
2957 AA.install_aa_utils = MagicMock()
2958 AA.manually_disable_aa_profile = MagicMock()
2959 # Test enforce mode
2960 self.config.return_value = 'enforce'
2961 AA.setup_aa_profile()
2962 self.check_call.assert_called_with(['aa-enforce', 'fake-aa-profile'])
2963 self.assertFalse(AA.manually_disable_aa_profile.called)
2964
2965 def test_apparmor_setup_disable(self):
2966 ''' Tests for the apparmor setup'''
2967 AA = FakeAppArmorContext()
2968 AA.install_aa_utils = MagicMock()
2969 AA.manually_disable_aa_profile = MagicMock()
2970 # Test disable mode
2971 self.config.return_value = 'disable'
2972 AA.setup_aa_profile()
2973 self.check_call.assert_called_with(['aa-disable', 'fake-aa-profile'])
2974 self.assertFalse(AA.manually_disable_aa_profile.called)
2975 # Test failed to disable
2976 from subprocess import CalledProcessError
2977 self.check_call.side_effect = CalledProcessError(0, 0, 0)
2978 AA.setup_aa_profile()
2979 self.check_call.assert_called_with(['aa-disable', 'fake-aa-profile'])
2980 AA.manually_disable_aa_profile.assert_called_with()

Subscribers

People subscribed via source and target branches