From bb06ebd0f6a75a6ba55a7c022de96a91e3750d20 Mon Sep 17 00:00:00 2001 From: =?utf8?q?P=C3=A1draig=20Brady?= Date: Thu, 6 Sep 2012 16:36:50 +0100 Subject: [PATCH] support a configurable volume wiping method Add a clear_volume() method that is called from delete_method(). This is only implemented at present for the base LVM driver. This new clear_volume() method now supports these config vars: volume_clear = none, zero, shred volume_clear_size = size_in_MiB 'zero' is the default method and unchanged from previously. 'none' is used if security isn't a concern, or there is independent volume scrubbing. 'shred' is for more security conscious situations where an overwrite count is required. Currently this defaults to 3 passes. size_in_MiB can be used to limit the cleared area to the first part of the volume, which can greatly speed up the operation, and can be useful with encrypted volumes for example where overwritting the encryption keys at the start is sufficient. Fixes bug: 1022511 Change-Id: I7312678ff5d66156578501483a1b74e2a69e19a0 --- cinder/flags.py | 5 +--- cinder/tests/fake_driver.py | 4 +++ cinder/volume/drivers/lvm.py | 45 +++++++++++++++++++++++++--- etc/cinder/rootwrap.d/volume.filters | 4 +++ 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/cinder/flags.py b/cinder/flags.py index aae589a32..9e859d41a 100644 --- a/cinder/flags.py +++ b/cinder/flags.py @@ -229,9 +229,6 @@ global_opts = [ 'and deprecated.'), cfg.StrOpt('control_exchange', default='cinder', - help='AMQP exchange to connect to if using RabbitMQ or Qpid'), - cfg.BoolOpt('secure_delete', - default=True, - help='Whether to perform secure delete'), ] + help='AMQP exchange to connect to if using RabbitMQ or Qpid'), ] FLAGS.register_opts(global_opts) diff --git a/cinder/tests/fake_driver.py b/cinder/tests/fake_driver.py index 12cc5366c..10f791e77 100644 --- a/cinder/tests/fake_driver.py +++ b/cinder/tests/fake_driver.py @@ -56,8 +56,12 @@ class LoggingVolumeDriver(driver.VolumeDriver): self.log_action('create_volume', volume) def delete_volume(self, volume): + self.clear_volume(volume) self.log_action('delete_volume', volume) + def clear_volume(self, volume): + self.log_action('clear_volume', volume) + def local_path(self, volume): print "local_path not implemented" raise NotImplementedError() diff --git a/cinder/volume/drivers/lvm.py b/cinder/volume/drivers/lvm.py index 14c2b5b3b..1e0c694e5 100644 --- a/cinder/volume/drivers/lvm.py +++ b/cinder/volume/drivers/lvm.py @@ -39,6 +39,13 @@ volume_opts = [ cfg.StrOpt('volume_group', default='cinder-volumes', help='Name for the VG that will contain exported volumes'), + cfg.StrOpt('volume_clear', + default='zero', + help='Method used to wipe old volumes (valid options are: ' + 'none, zero, shred)'), + cfg.IntOpt('volume_clear_size', + default=0, + help='Size in MiB to wipe at start of old volumes. 0 => all'), cfg.IntOpt('lvm_mirrors', default=0, help='If set, create lvms with multiple mirrors. Note that ' @@ -119,10 +126,8 @@ class LVMVolumeDriver(driver.VolumeDriver): # zero out old volumes to prevent data leaking between users # TODO(ja): reclaiming space should be done lazy and low priority dev_path = self.local_path(volume) - if FLAGS.secure_delete and os.path.exists(dev_path): - LOG.info(_("Performing secure delete on volume: %s") - % volume['id']) - self._copy_volume('/dev/zero', dev_path, size_in_g) + if os.path.exists(dev_path): + self.clear_volume(volume) self._try_execute('lvremove', '-f', "%s/%s" % (FLAGS.volume_group, @@ -173,6 +178,38 @@ class LVMVolumeDriver(driver.VolumeDriver): self._delete_volume(volume, volume['size']) + def clear_volume(self, volume): + """unprovision old volumes to prevent data leaking between users.""" + + vol_path = self.local_path(volume) + size_in_g = volume.get('size') + size_in_m = FLAGS.volume_clear_size + + if not size_in_g: + return + + if FLAGS.volume_clear == 'none': + return + + LOG.info(_("Performing secure delete on volume: %s") % volume['id']) + + if FLAGS.volume_clear == 'zero': + if size_in_m == 0: + return self._copy_volume('/dev/zero', vol_path, size_in_g) + else: + clear_cmd = ['shred', '-n0', '-z', '-s%dMiB' % size_in_m] + elif FLAGS.volume_clear == 'shred': + clear_cmd = ['shred', '-n3'] + if size_in_m: + clear_cmd.append('-s%dMiB' % size_in_m) + else: + LOG.error(_("Error unrecognized volume_clear option: %s"), + FLAGS.volume_clear) + return + + clear_cmd.append(vol_path) + self._execute(*clear_cmd, run_as_root=True) + def create_snapshot(self, snapshot): """Creates a snapshot.""" orig_lv_name = "%s/%s" % (FLAGS.volume_group, snapshot['volume_name']) diff --git a/etc/cinder/rootwrap.d/volume.filters b/etc/cinder/rootwrap.d/volume.filters index 45a1e29cd..68a57b1df 100644 --- a/etc/cinder/rootwrap.d/volume.filters +++ b/etc/cinder/rootwrap.d/volume.filters @@ -28,6 +28,10 @@ lvdisplay: CommandFilter, /sbin/lvdisplay, root iscsiadm: CommandFilter, /sbin/iscsiadm, root iscsiadm_usr: CommandFilter, /usr/bin/iscsiadm, root +# cinder/volume/drivers/lvm.py: 'shred', '-n3' +# cinder/volume/drivers/lvm.py: 'shred', '-n0', '-z', '-s%dMiB' +shred: CommandFilter, /usr/bin/shred, root + #cinder/volume/.py: utils.temporary_chown(path, 0), ... chown: CommandFilter, /bin/chown, root -- 2.45.2