From 1a6e05e609b1ef45bdde97fcaf3944fc2824fd40 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 20 Oct 2020 11:18:45 +0200 Subject: TargetSSHLXCLVM: factor creating+mounting the snapshot Make it into its own context manager, similar to TargetSSHLVM. This is easier to follow and will allow to share more code in the future. --- lbup/targets.py | 55 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/lbup/targets.py b/lbup/targets.py index 8b90188..7f85526 100644 --- a/lbup/targets.py +++ b/lbup/targets.py @@ -396,6 +396,32 @@ class TargetSSHLXCLVM(TargetSSHLVM): return "%s{LXC:%s/%s@[%s]}{LVM:%s}" % (super().__str__(), self._lxc_containername, self._lxc_username, str(self._parent_remote), self._snapshot_size) + @contextlib.contextmanager + def _nsmount_snapshot(self, ssh, devnum, mount_path, container_pid, fstype): + """ + Return a context manager that creates a read-only LVM snapshot + for the specified LV device number and mounts it at mount_path + inside the container, then unmounts and destroys it at exit. + """ + with self._snapshot_lv(ssh, devnum) as lv_path: + # we cannot trust any binaries located inside the container, since a + # compromised container could use them to execute arbitrary code + # with real root privileges, thus nullifying the point of + # unprivileged containers) + # so we ship a special tool, 'nsmount', which has to be + # installed on the parent, to mount the snapshot into the + # container mount namespace + self._paramiko_exec_cmd(ssh, + 'nsmount m {pid} {mount_path} {lv_path} {fstype}'.format( + pid = container_pid, mount_path = mount_path, + lv_path = lv_path, fstype = fstype)) + try: + yield None + finally: + self._paramiko_exec_cmd(ssh, + 'nsmount u {pid} {mount_path}'.format( + pid = container_pid, mount_path = mount_path)) + def save(self, dry_run = False): with contextlib.ExitStack() as stack: parent = stack.enter_context(_ssh_client.SSHConnection(self._parent_remote)) @@ -433,33 +459,14 @@ class TargetSSHLXCLVM(TargetSSHLVM): self._logger.debug('Backup targets are at device %s(%s), mounted at %s', "%d:%d" % (lv_devnum >> 8, lv_devnum & 255), lv_fstype, lv_mountpoint) - snapshot_path = stack.enter_context(self._snapshot_lv(parent, lv_devnum)) + stack.enter_context(self._nsmount_snapshot(parent, lv_devnum, container_mountpoint, + container_pid, lv_fstype)) # execute the backup - - # we cannot trust any binaries located inside the container, since a - # compromised container could use them to execute arbitrary code - # with real root privileges, thus nullifying the point of - # unprivileged containers) - # so we ship a special tool, 'nsmount', which has to be - # installed on the parent, to mount the snapshot into the - # container mount namespace - self._paramiko_exec_cmd(parent, - 'nsmount m {pid} {mountpoint} {devpath} {fstype}'.format( - pid = container_pid, mountpoint = container_mountpoint, - devpath = snapshot_path, fstype = lv_fstype)) - bup_exec = ['bup', 'on', '%s@%s' % (self._remote.username, self._remote.host), '-d', container_bupdir] save_opts = ['--graft=%s=%s' % (container_mountpoint, lv_mountpoint)] reparent = (lv_mountpoint, AbsPath(container_mountpoint)) - try: - ret = self._do_save(bup_exec, dry_run, - reparent = reparent, - save_opts = save_opts, index_opts = ['--no-check-device']) - finally: - self._paramiko_exec_cmd(parent, - 'nsmount u {pid} {mountpoint}'.format( - pid = container_pid, mountpoint = container_mountpoint)) - - return ret + return self._do_save(bup_exec, dry_run, + reparent = reparent, + save_opts = save_opts, index_opts = ['--no-check-device']) -- cgit v1.2.3