Browse Source

Merge "Implement privsep boilerplate in cinder."

tags/13.0.0.0b3
Zuul 11 months ago
parent
commit
e1cef0ea51

+ 32
- 0
cinder/privsep/__init__.py View File

@@ -0,0 +1,32 @@
1
+# Copyright 2016 Red Hat, Inc
2
+# Copyright 2017 Rackspace Australia
3
+# Copyright 2018 Michael Still and Aptira
4
+#
5
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+#    not use this file except in compliance with the License. You may obtain
7
+#    a copy of the License at
8
+#
9
+#         http://www.apache.org/licenses/LICENSE-2.0
10
+#
11
+#    Unless required by applicable law or agreed to in writing, software
12
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+#    License for the specific language governing permissions and limitations
15
+#    under the License.
16
+
17
+"""Setup privsep decorator."""
18
+
19
+from oslo_privsep import capabilities
20
+from oslo_privsep import priv_context
21
+
22
+sys_admin_pctxt = priv_context.PrivContext(
23
+    'cinder',
24
+    cfg_section='cinder_sys_admin',
25
+    pypath=__name__ + '.sys_admin_pctxt',
26
+    capabilities=[capabilities.CAP_CHOWN,
27
+                  capabilities.CAP_DAC_OVERRIDE,
28
+                  capabilities.CAP_DAC_READ_SEARCH,
29
+                  capabilities.CAP_FOWNER,
30
+                  capabilities.CAP_NET_ADMIN,
31
+                  capabilities.CAP_SYS_ADMIN],
32
+)

+ 35
- 0
cinder/privsep/cgroup.py View File

@@ -0,0 +1,35 @@
1
+# Copyright 2016 Red Hat, Inc
2
+# Copyright 2017 Rackspace Australia
3
+# Copyright 2018 Michael Still and Aptira
4
+#
5
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+#    not use this file except in compliance with the License. You may obtain
7
+#    a copy of the License at
8
+#
9
+#         http://www.apache.org/licenses/LICENSE-2.0
10
+#
11
+#    Unless required by applicable law or agreed to in writing, software
12
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+#    License for the specific language governing permissions and limitations
15
+#    under the License.
16
+
17
+"""
18
+Helpers for cgroup related routines.
19
+"""
20
+
21
+from oslo_concurrency import processutils
22
+
23
+import cinder.privsep
24
+
25
+
26
+@cinder.privsep.sys_admin_pctxt.entrypoint
27
+def cgroup_create(name):
28
+    processutils.execute('cgcreate', '-g', 'blkio:%s' % name)
29
+
30
+
31
+@cinder.privsep.sys_admin_pctxt.entrypoint
32
+def cgroup_limit(name, rw, dev, bps):
33
+    processutils.execute('cgset', '-r',
34
+                         'blkio.throttle.%s_bps_device=%s %d' % (rw, dev, bps),
35
+                         name)

+ 3
- 0
cinder/test.py View File

@@ -302,6 +302,9 @@ class TestCase(testtools.TestCase):
302 302
         tpool.killall()
303 303
         tpool._nthreads = 20
304 304
 
305
+        # NOTE(mikal): make sure we don't load a privsep helper accidentally
306
+        self.useFixture(cinder_fixtures.PrivsepNoHelperFixture())
307
+
305 308
     def _restore_obj_registry(self):
306 309
         objects_base.CinderObjectRegistry._registry._obj_classes = \
307 310
             self._base_test_obj_backup

+ 29
- 0
cinder/tests/fixtures.py View File

@@ -1,4 +1,6 @@
1 1
 # Copyright 2016 IBM Corp.
2
+# Copyright 2017 Rackspace Australia
3
+# Copyright 2018 Michael Still and Aptira
2 4
 #
3 5
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 6
 #    not use this file except in compliance with the License. You may obtain
@@ -21,6 +23,7 @@ import os
21 23
 import warnings
22 24
 
23 25
 import fixtures
26
+from oslo_privsep import daemon as privsep_daemon
24 27
 
25 28
 _TRUE_VALUES = ('True', 'true', '1', 'yes')
26 29
 
@@ -131,3 +134,29 @@ class WarningsFixture(fixtures.Fixture):
131 134
                     ' This key is deprecated. Please update your policy '
132 135
                     'file to use the standard policy values.')
133 136
         self.addCleanup(warnings.resetwarnings)
137
+
138
+
139
+class UnHelperfulClientChannel(privsep_daemon._ClientChannel):
140
+    def __init__(self, context):
141
+        raise Exception('You have attempted to start a privsep helper. '
142
+                        'This is not allowed in the gate, and '
143
+                        'indicates a failure to have mocked your tests.')
144
+
145
+
146
+class PrivsepNoHelperFixture(fixtures.Fixture):
147
+    """A fixture to catch failures to mock privsep's rootwrap helper.
148
+
149
+    If you fail to mock away a privsep'd method in a unit test, then
150
+    you may well end up accidentally running the privsep rootwrap
151
+    helper. This will fail in the gate, but it fails in a way which
152
+    doesn't identify which test is missing a mock. Instead, we
153
+    raise an exception so that you at least know where you've missed
154
+    something.
155
+    """
156
+
157
+    def setUp(self):
158
+        super(PrivsepNoHelperFixture, self).setUp()
159
+
160
+        self.useFixture(fixtures.MonkeyPatch(
161
+            'oslo_privsep.daemon.RootwrapClientChannel',
162
+            UnHelperfulClientChannel))

+ 21
- 32
cinder/tests/unit/test_volume_throttling.py View File

@@ -29,7 +29,9 @@ class ThrottleTestCase(test.TestCase):
29 29
             self.assertEqual([], cmd['prefix'])
30 30
 
31 31
     @mock.patch.object(utils, 'get_blkdev_major_minor')
32
-    def test_BlkioCgroup(self, mock_major_minor):
32
+    @mock.patch('cinder.privsep.cgroup.cgroup_create')
33
+    @mock.patch('cinder.privsep.cgroup.cgroup_limit')
34
+    def test_BlkioCgroup(self, mock_limit, mock_create, mock_major_minor):
33 35
 
34 36
         def fake_get_blkdev_major_minor(path):
35 37
             return {'src_volume1': "253:0", 'dst_volume1': "253:1",
@@ -37,38 +39,25 @@ class ThrottleTestCase(test.TestCase):
37 39
 
38 40
         mock_major_minor.side_effect = fake_get_blkdev_major_minor
39 41
 
40
-        self.exec_cnt = 0
42
+        throttle = throttling.BlkioCgroup(1024, 'fake_group')
43
+        with throttle.subcommand('src_volume1', 'dst_volume1') as cmd:
44
+            self.assertEqual(['cgexec', '-g', 'blkio:fake_group'],
45
+                             cmd['prefix'])
41 46
 
42
-        def fake_execute(*cmd, **kwargs):
43
-            cmd_set = ['cgset', '-r',
44
-                       'blkio.throttle.%s_bps_device=%s %d', 'fake_group']
45
-            set_order = [None,
46
-                         ('read', '253:0', 1024),
47
-                         ('write', '253:1', 1024),
48
-                         # a nested job starts; bps limit are set to the half
49
-                         ('read', '253:0', 512),
50
-                         ('read', '253:2', 512),
51
-                         ('write', '253:1', 512),
52
-                         ('write', '253:3', 512),
53
-                         # a nested job ends; bps limit is resumed
54
-                         ('read', '253:0', 1024),
55
-                         ('write', '253:1', 1024)]
56
-
57
-            if set_order[self.exec_cnt] is None:
58
-                self.assertEqual(('cgcreate', '-g', 'blkio:fake_group'), cmd)
59
-            else:
60
-                cmd_set[2] %= set_order[self.exec_cnt]
61
-                self.assertEqual(tuple(cmd_set), cmd)
62
-
63
-            self.exec_cnt += 1
64
-
65
-        with mock.patch.object(utils, 'execute', side_effect=fake_execute):
66
-            throttle = throttling.BlkioCgroup(1024, 'fake_group')
67
-            with throttle.subcommand('src_volume1', 'dst_volume1') as cmd:
47
+            # a nested job
48
+            with throttle.subcommand('src_volume2', 'dst_volume2') as cmd:
68 49
                 self.assertEqual(['cgexec', '-g', 'blkio:fake_group'],
69 50
                                  cmd['prefix'])
70 51
 
71
-                # a nested job
72
-                with throttle.subcommand('src_volume2', 'dst_volume2') as cmd:
73
-                    self.assertEqual(['cgexec', '-g', 'blkio:fake_group'],
74
-                                     cmd['prefix'])
52
+        mock_create.assert_has_calls([mock.call('fake_group')])
53
+        mock_limit.assert_has_calls([
54
+            mock.call('fake_group', 'read', '253:0', 1024),
55
+            mock.call('fake_group', 'write', '253:1', 1024),
56
+            # a nested job starts; bps limit are set to the half
57
+            mock.call('fake_group', 'read', '253:0', 512),
58
+            mock.call('fake_group', 'read', '253:2', 512),
59
+            mock.call('fake_group', 'write', '253:1', 512),
60
+            mock.call('fake_group', 'write', '253:3', 512),
61
+            # a nested job ends; bps limit is resumed
62
+            mock.call('fake_group', 'read', '253:0', 1024),
63
+            mock.call('fake_group', 'write', '253:1', 1024)])

+ 3
- 4
cinder/volume/throttling.py View File

@@ -22,6 +22,7 @@ from oslo_concurrency import processutils
22 22
 from oslo_log import log as logging
23 23
 
24 24
 from cinder import exception
25
+import cinder.privsep.cgroup
25 26
 from cinder import utils
26 27
 
27 28
 
@@ -65,8 +66,7 @@ class BlkioCgroup(Throttle):
65 66
         self.dstdevs = {}
66 67
 
67 68
         try:
68
-            utils.execute('cgcreate', '-g', 'blkio:%s' % self.cgroup,
69
-                          run_as_root=True)
69
+            cinder.privsep.cgroup.cgroup_create(self.cgroup)
70 70
         except processutils.ProcessExecutionError:
71 71
             LOG.error('Failed to create blkio cgroup \'%(name)s\'.',
72 72
                       {'name': cgroup_name})
@@ -81,8 +81,7 @@ class BlkioCgroup(Throttle):
81 81
 
82 82
     def _limit_bps(self, rw, dev, bps):
83 83
         try:
84
-            utils.execute('cgset', '-r', 'blkio.throttle.%s_bps_device=%s %d'
85
-                          % (rw, dev, bps), self.cgroup, run_as_root=True)
84
+            cinder.privsep.cgroup.cgroup_limit(self.cgroup, rw, dev, bps)
86 85
         except processutils.ProcessExecutionError:
87 86
             LOG.warning('Failed to setup blkio cgroup to throttle the '
88 87
                         'device \'%(device)s\'.', {'device': dev})

+ 4
- 2
etc/cinder/rootwrap.d/volume.filters View File

@@ -43,6 +43,10 @@ lvdisplay4: EnvFilter, env, root, LC_ALL=C, LVM_SYSTEM_DIR=, LVM_SUPPRESS_FD_WAR
43 43
 # This line ties the superuser privs with the config files, context name,
44 44
 # and (implicitly) the actual python code invoked.
45 45
 privsep-rootwrap: RegExpFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, os_brick.privileged.default, --privsep_sock_path, /tmp/.*
46
+
47
+# Privsep calls within cinder iteself
48
+privsep-rootwrap-sys_admin: RegExpFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, cinder.privsep.sys_admin_pctxt, --privsep_sock_path, /tmp/.*
49
+
46 50
 # The following and any cinder/brick/* entries should all be obsoleted
47 51
 # by privsep, and may be removed once the os-brick version requirement
48 52
 # is updated appropriately.
@@ -93,8 +97,6 @@ ionice_1: ChainingRegExpFilter, ionice, root, ionice, -c[0-3], -n[0-7]
93 97
 ionice_2: ChainingRegExpFilter, ionice, root, ionice, -c[0-3]
94 98
 
95 99
 # cinder/volume/utils.py: setup_blkio_cgroup()
96
-cgcreate: CommandFilter, cgcreate, root
97
-cgset: CommandFilter, cgset, root
98 100
 cgexec: ChainingRegExpFilter, cgexec, root, cgexec, -g, blkio:\S+
99 101
 
100 102
 # cinder/volume/driver.py

+ 14
- 0
releasenotes/notes/privsep-rocky-35bdfe70ed62a826.yaml View File

@@ -0,0 +1,14 @@
1
+---
2
+security:
3
+  - |
4
+    Privsep transitions. Cinder is transitioning from using the older style
5
+    rootwrap privilege escalation path to the new style Oslo privsep path.
6
+    This should improve performance and security of Cinder in the long term.
7
+  - |
8
+    Privsep daemons are now started by Cinder when required. These daemons can
9
+    be started via rootwrap if required. rootwrap configs therefore need to
10
+    be updated to include new privsep daemon invocations.
11
+upgrade:
12
+  - |
13
+    The following commands are no longer required to be listed in your rootwrap
14
+    configuration: cgcreate; and cgset.

Loading…
Cancel
Save