From 0a40a6422489029b9543c759866d13a5267ed6a0 Mon Sep 17 00:00:00 2001 From: Alexey Ovchinnikov <aovchinnikov@mirantis.com> Date: Wed, 17 Aug 2016 14:55:54 +0300 Subject: [PATCH] Fix for LV mounting issue in docker containers Docker containers were previously started with insufficient privileges to successfully mount a LV created on host. This change fixes this problem. Closes-Bug: 1613675 Change-Id: I63f3480ace3be70100a245570d7f3579b333e972 --- .../drivers/container/container_helper.py | 28 +++++++++++++++++-- manila/share/drivers/container/driver.py | 22 ++++++++++++++- .../share/drivers/container/storage_helper.py | 10 ------- .../container/test_container_helper.py | 4 +-- .../share/drivers/container/test_driver.py | 22 ++++++++++++++- .../drivers/container/test_storage_helper.py | 8 ------ ...ng-inside-containers-af8f84d1fab256d1.yaml | 3 ++ 7 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 releasenotes/notes/lv-mounting-inside-containers-af8f84d1fab256d1.yaml diff --git a/manila/share/drivers/container/container_helper.py b/manila/share/drivers/container/container_helper.py index eba03aaa11..80f19ec334 100644 --- a/manila/share/drivers/container/container_helper.py +++ b/manila/share/drivers/container/container_helper.py @@ -39,10 +39,32 @@ class DockerExecHelper(driver.ExecuteMixin): LOG.debug("Starting container from image %s.", image_name) # (aovchinnikov): --privileged is required for both samba and # nfs-ganesha to actually allow access to shared folders. + # + # (aovchinnikov): To actually make docker container mount a + # logical volume created after container start-up to some location + # inside it, we must share entire /dev with it. While seemingly + # dangerous it is not and moreover this is apparently the only sane + # way to do it. The reason is when a logical volume gets created + # several new things appear in /dev: a new /dev/dm-X and a symlink + # in /dev/volume_group_name pointing to /dev/dm-X. But to be able + # to interact with /dev/dm-X, it must be already present inside + # the container's /dev i.e. it must have been -v shared during + # container start-up. So we should either precreate an unknown + # number of /dev/dm-Xs (one per LV), share them all and hope + # for the best or share the entire /dev and hope for the best. + # + # The risk of allowing a container having access to entire host's + # /dev is not as big as it seems: as long as actual share providers + # are invulnerable this does not pose any extra risks. If, however, + # share providers contain vulnerabilities then the driver does not + # provide any more possibilities for an exploitation than other + # first-party drivers. + cmd = ["docker", "run", "-d", "-i", "-t", "--privileged", - "--name=%s" % name, '-v', "/tmp/shares:/shares", image_name] - result = self._inner_execute(cmd) or ['', 1] - if result[1] != '': + "-v", "/dev:/dev", "--name=%s" % name, + "-v", "/tmp/shares:/shares", image_name] + result = self._inner_execute(cmd) or ["", 1] + if result[1] != "": raise exception.ManilaException( _("Container %s has failed to start.") % name) LOG.info(_LI("A container has been successfully started! Its id is " diff --git a/manila/share/drivers/container/driver.py b/manila/share/drivers/container/driver.py index 931cb08fac..629859621d 100644 --- a/manila/share/drivers/container/driver.py +++ b/manila/share/drivers/container/driver.py @@ -126,6 +126,11 @@ class ContainerShareDriver(driver.ShareDriver, driver.ExecuteMixin): ["mkdir", "-m", "750", "/shares/%s" % share_name] ) self.storage.provide_storage(share) + lv_device = self.storage._get_lv_device(share) + self.container.execute( + server_id, + ["mount", lv_device, "/shares/%s" % share_name] + ) location = self._get_helper(share).create_share(server_id) return location @@ -136,15 +141,30 @@ class ContainerShareDriver(driver.ShareDriver, driver.ExecuteMixin): server_id = self._get_container_name(share_server["id"]) self._get_helper(share).delete_share(server_id) - self.storage.remove_storage(share) + self.container.execute( + server_id, + ["umount", "/shares/%s" % share.share_id] + ) self.container.execute( server_id, ["rm", "-fR", "/shares/%s" % share.share_id] ) + + self.storage.remove_storage(share) LOG.debug("Deletion of share %s is completed!", share.share_id) def extend_share(self, share, new_size, share_server=None): + server_id = self._get_container_name(share_server["id"]) + self.container.execute( + server_id, + ["umount", "/shares/%s" % share.share_id] + ) self.storage.extend_share(share, new_size, share_server) + lv_device = self.storage._get_lv_device(share) + self.container.execute( + server_id, + ["mount", lv_device, "/shares/%s" % share.share_id] + ) def ensure_share(self, context, share, share_server=None): pass diff --git a/manila/share/drivers/container/storage_helper.py b/manila/share/drivers/container/storage_helper.py index 2e9e658003..a787514ca4 100644 --- a/manila/share/drivers/container/storage_helper.py +++ b/manila/share/drivers/container/storage_helper.py @@ -77,24 +77,14 @@ class LVMHelper(driver.ExecuteMixin): run_as_root=True) self._execute("mkfs.ext4", self._get_lv_device(share), run_as_root=True) - self._execute("mount", self._get_lv_device(share), - self._get_lv_folder(share), run_as_root=True) - self._execute("chmod", "-R", "750", self._get_lv_folder(share), - run_as_root=True) - self._execute("chown", "nobody:nogroup", self._get_lv_folder(share), - run_as_root=True) def remove_storage(self, share): - self._execute("umount", self._get_lv_device(share), run_as_root=True) self._execute("lvremove", "-f", "--autobackup", "n", self._get_lv_device(share), run_as_root=True) def extend_share(self, share, new_size, share_server=None): lv_device = self._get_lv_device(share) - lv_folder = self._get_lv_folder(share) - self._execute("umount", lv_folder, run_as_root=True) cmd = ('lvextend', '-L', '%sG' % new_size, '-n', lv_device) self._execute(*cmd, run_as_root=True) self._execute("e2fsck", "-f", "-y", lv_device, run_as_root=True) self._execute('resize2fs', lv_device, run_as_root=True) - self._execute("mount", lv_device, lv_folder, run_as_root=True) diff --git a/manila/tests/share/drivers/container/test_container_helper.py b/manila/tests/share/drivers/container/test_container_helper.py index e7f7f281b9..1999f0ce0b 100644 --- a/manila/tests/share/drivers/container/test_container_helper.py +++ b/manila/tests/share/drivers/container/test_container_helper.py @@ -37,8 +37,8 @@ class DockerExecHelperTestCase(test.TestCase): self.mock_object(self.DockerExecHelper, "_inner_execute", mock.Mock(return_value=['fake_output', ''])) uuid.uuid1 = mock.Mock(return_value='') - expected = ['docker', 'run', '-d', '-i', '-t', '--privileged', - '--name=manila_cifs_docker_container', '-v', + expected = ['docker', 'run', '-d', '-i', '-t', '--privileged', '-v', + '/dev:/dev', '--name=manila_cifs_docker_container', '-v', '/tmp/shares:/shares', 'fake_image'] self.DockerExecHelper.start_container() diff --git a/manila/tests/share/drivers/container/test_driver.py b/manila/tests/share/drivers/container/test_driver.py index fb91434c7c..31b5365450 100644 --- a/manila/tests/share/drivers/container/test_driver.py +++ b/manila/tests/share/drivers/container/test_driver.py @@ -58,6 +58,14 @@ class ContainerShareDriverTestCase(test.TestCase): # Used only to test compatibility with share manager self.share_server = "fake_share_server" + def fake_exec_sync(self, *args, **kwargs): + kwargs['execute_arguments'].append(args) + try: + ret_val = kwargs['ret_val'] + except KeyError: + ret_val = None + return ret_val + def test__get_helper_ok(self): share = cont_fakes.fake_share(share_proto='CIFS') expected = protocol_helper.DockerCIFSHelper(None) @@ -125,9 +133,21 @@ class ContainerShareDriverTestCase(test.TestCase): def test_extend_share(self): share = cont_fakes.fake_share() + actual_arguments = [] + expected_arguments = [ + ('manila_fake_server', ['umount', '/shares/fakeshareid']), + ('manila_fake_server', + ['mount', '/dev/manila_docker_volumes/fakeshareid', + '/shares/fakeshareid']) + ] self.mock_object(self._driver.storage, "extend_share") + self._driver.container.execute = functools.partial( + self.fake_exec_sync, execute_arguments=actual_arguments, + ret_val='') - self._driver.extend_share(share, 2, 'fake-server') + self._driver.extend_share(share, 2, {'id': 'fake-server'}) + + self.assertEqual(expected_arguments, actual_arguments) def test_ensure_share(self): # Does effectively nothing by design. diff --git a/manila/tests/share/drivers/container/test_storage_helper.py b/manila/tests/share/drivers/container/test_storage_helper.py index f6cb6a64ec..02521f57fe 100644 --- a/manila/tests/share/drivers/container/test_storage_helper.py +++ b/manila/tests/share/drivers/container/test_storage_helper.py @@ -73,10 +73,6 @@ class LVMHelperTestCase(test.TestCase): ('lvcreate', '-p', 'rw', '-L', '1G', '-n', 'fakeshareid', 'manila_docker_volumes'), ('mkfs.ext4', '/dev/manila_docker_volumes/fakeshareid'), - ('mount', '/dev/manila_docker_volumes/fakeshareid', - '/tmp/shares/fakeshareid'), - ('chmod', '-R', '750', '/tmp/shares/fakeshareid'), - ('chown', 'nobody:nogroup', '/tmp/shares/fakeshareid') ] self.LVMHelper._execute = functools.partial( self.fake_exec_sync, execute_arguments=actual_arguments, @@ -89,7 +85,6 @@ class LVMHelperTestCase(test.TestCase): def test_remove_storage(self): actual_arguments = [] expected_arguments = [ - ('umount', '/dev/manila_docker_volumes/fakeshareid'), ('lvremove', '-f', '--autobackup', 'n', '/dev/manila_docker_volumes/fakeshareid') ] @@ -104,13 +99,10 @@ class LVMHelperTestCase(test.TestCase): def test_extend_share(self): actual_arguments = [] expected_arguments = [ - ('umount', '/tmp/shares/fakeshareid'), ('lvextend', '-L', 'shareG', '-n', '/dev/manila_docker_volumes/fakeshareid'), ('e2fsck', '-f', '-y', '/dev/manila_docker_volumes/fakeshareid'), ('resize2fs', '/dev/manila_docker_volumes/fakeshareid'), - ('mount', '/dev/manila_docker_volumes/fakeshareid', - '/tmp/shares/fakeshareid') ] self.LVMHelper._execute = functools.partial( self.fake_exec_sync, execute_arguments=actual_arguments, diff --git a/releasenotes/notes/lv-mounting-inside-containers-af8f84d1fab256d1.yaml b/releasenotes/notes/lv-mounting-inside-containers-af8f84d1fab256d1.yaml new file mode 100644 index 0000000000..ee0c814aff --- /dev/null +++ b/releasenotes/notes/lv-mounting-inside-containers-af8f84d1fab256d1.yaml @@ -0,0 +1,3 @@ +--- +fixes: + - Makes docker containers actually mount logical volumes.