Пример #1
0
def is_encrypted(context):
    """
    Detect whether a remote system is using root disk encryption.

    :param context: A :class:`~executor.contexts.RemoteContext` object.
    :returns: :data:`True` if root disk encryption is being used,
              :data:`False` otherwise.
    """
    logger.info("Checking root disk encryption on %s ..", context)
    for entry in parse_crypttab(context=context):
        if context.test('test', '-b', entry.source_device):
            logger.verbose("Checking if %s contains root filesystem ..",
                           entry.source_device)
            listing = context.capture('lsblk', entry.source_device)
            if '/' in listing.split():
                logger.info(
                    "Yes it looks like the system is using root disk encryption."
                )
                return True
        else:
            logger.verbose("Ignoring %s because it's not a block device.",
                           entry.source_device)
    logger.info(
        "No it doesn't look like the system is using root disk encryption.")
    return False
Пример #2
0
    def cryptdisks_start_helper(self, emulated):
        """
        Test cryptdisks_start integration and emulation.

        This test requires the following line to be present in ``/etc/crypttab``::

         linux-utils /tmp/linux-utils.img /tmp/linux-utils.key discard,luks,noauto,readonly,tries=1
        """
        if not any(entry.target == TEST_TARGET_NAME and entry.source ==
                   TEST_IMAGE_FILE and entry.key_file == TEST_KEY_FILE
                   and 'luks' in entry.options for entry in parse_crypttab()):
            return self.skipTest(
                "/etc/crypttab isn't set up to test cryptdisks_start!")
        context = LocalContext()
        if emulated:
            # Disable the use of the `cryptdisks_start' program.
            context.find_program = MagicMock(return_value=[])
        # Generate the key file.
        with TemporaryKeyFile(filename=TEST_KEY_FILE):
            # Create the image file and the encrypted filesystem.
            create_image_file(filename=TEST_IMAGE_FILE,
                              size=coerce_size('10 MiB'))
            create_encrypted_filesystem(device_file=TEST_IMAGE_FILE,
                                        key_file=TEST_KEY_FILE)
            # Make sure the mapped device file doesn't exist yet.
            assert not os.path.exists(TEST_TARGET_DEVICE)
            # Unlock the encrypted filesystem using `cryptdisks_start'.
            if emulated:
                cryptdisks_start(context=context, target=TEST_TARGET_NAME)
            else:
                returncode, output = run_cli(cryptdisks_start_cli,
                                             TEST_TARGET_NAME)
                assert returncode == 0
            # Make sure the mapped device file has appeared.
            assert os.path.exists(TEST_TARGET_DEVICE)
            # Unlock the encrypted filesystem again (this should be a no-op).
            cryptdisks_start(context=context, target=TEST_TARGET_NAME)
            # Make sure the mapped device file still exists.
            assert os.path.exists(TEST_TARGET_DEVICE)
            # Lock the filesystem before we finish.
            if emulated:
                cryptdisks_stop(context=context, target=TEST_TARGET_NAME)
            else:
                returncode, output = run_cli(cryptdisks_stop_cli,
                                             TEST_TARGET_NAME)
                assert returncode == 0
            # Make sure the mapped device file has disappeared.
            assert not os.path.exists(TEST_TARGET_DEVICE)
            # Lock the filesystem again (this should be a no-op).
            cryptdisks_stop(context=context, target=TEST_TARGET_NAME)
            # Make sure the mapped device file is still gone.
            assert not os.path.exists(TEST_TARGET_DEVICE)
            # Test the error handling.
            for function in cryptdisks_start, cryptdisks_stop:
                self.assertRaises(
                    ValueError if emulated else ExternalCommandFailed,
                    function,
                    context=context,
                    target=TEST_UNKNOWN_TARGET,
                )
Пример #3
0
def cryptdisks_stop(target, context=None):
    """
    Execute cryptdisks_stop_ or emulate its functionality.

    :param target: The mapped device name (a string).
    :param context: An execution context created by :mod:`executor.contexts`
                    (coerced using :func:`.coerce_context()`).
    :raises: :exc:`~executor.ExternalCommandFailed` when a command fails,
             :exc:`~exceptions.ValueError` when no entry in `/etc/crypttab`_
             matches `target`.

    .. _cryptdisks_stop: https://manpages.debian.org/cryptdisks_stop
    """
    context = coerce_context(context)
    logger.debug("Checking if `cryptdisks_stop' program is installed ..")
    if context.find_program('cryptdisks_stop'):
        logger.debug("Using the real `cryptdisks_stop' program ..")
        context.execute('cryptdisks_stop', target, sudo=True)
    else:
        logger.debug(
            "Emulating `cryptdisks_stop' functionality (program not installed) .."
        )
        for entry in parse_crypttab(context=context):
            if entry.target == target and 'luks' in entry.options:
                logger.debug("Matched /etc/crypttab entry: %s", entry)
                if entry.is_unlocked:
                    lock_filesystem(context=context, target=target)
                else:
                    logger.debug(
                        "Encrypted filesystem is already locked, doing nothing .."
                    )
                break
        else:
            msg = "Encrypted filesystem not listed in /etc/crypttab! (%r)"
            raise ValueError(msg % target)
Пример #4
0
def cryptdisks_start(target, context=None):
    """
    Execute :man:`cryptdisks_start` or emulate its functionality.

    :param target: The mapped device name (a string).
    :param context: See :func:`.coerce_context()` for details.
    :raises: :exc:`~executor.ExternalCommandFailed` when a command fails,
             :exc:`~exceptions.ValueError` when no entry in `/etc/crypttab`_
             matches `target`.
    """
    context = coerce_context(context)
    logger.debug("Checking if `cryptdisks_start' program is installed ..")
    if context.find_program('cryptdisks_start'):
        logger.debug("Using the real `cryptdisks_start' program ..")
        context.execute('cryptdisks_start', target, sudo=True)
    else:
        logger.debug("Emulating `cryptdisks_start' functionality (program not installed) ..")
        for entry in parse_crypttab(context=context):
            if entry.target == target and 'luks' in entry.options:
                logger.debug("Matched /etc/crypttab entry: %s", entry)
                if entry.is_unlocked:
                    logger.debug("Encrypted filesystem is already unlocked, doing nothing ..")
                else:
                    unlock_filesystem(context=context,
                                      device_file=entry.source_device,
                                      key_file=entry.key_file,
                                      options=entry.options,
                                      target=entry.target)
                break
        else:
            msg = "Encrypted filesystem not listed in /etc/crypttab! (%r)"
            raise ValueError(msg % target)
def find_managed_drives(keys_directory):
    """
    Find the encrypted drives managed by `crypto-drive-manager`.

    :param keys_directory: The mount point for the virtual keys device (a string).
    :returns: A generator of :class:`~linux_utils.crypttab.EncryptedFileSystemEntry` objects.
    """
    for entry in parse_crypttab():
        if ('luks' in entry.options and entry.key_file
                and match_prefix(entry.key_file, keys_directory)):
            yield entry
Пример #6
0
 def crypttab_entry(self):
     """
     The entry in ``/etc/crypttab`` corresponding to :attr:`crypto_device`.
     The value of this property is computed automatically by parsing
     ``/etc/crypttab`` and looking for an entry whose `target` (the
     first of the four fields) matches :attr:`crypto_device`.
     When an entry is found an
     :class:`~linux_utils.crypttab.EncryptedFileSystemEntry` object is
     constructed, otherwise the result is :data:`None`.
     """
     if self.crypto_device:
         logger.debug(
             "Parsing /etc/crypttab to determine device file of encrypted filesystem %r ..",
             self.crypto_device)
         for entry in parse_crypttab(context=self.destination_context):
             if entry.target == self.crypto_device:
                 return entry
Пример #7
0
 def test_parse_crypttab(self):
     """Test the ``/etc/crypttab`` parsing."""
     source = 'UUID=36c44011-999d-4e4d-98cd-43e169b839e7'
     key_file = '/root/keys/backups.key'
     options = ['luks', 'discard', 'noauto']
     fake_entry = [TEST_UNKNOWN_TARGET, source, key_file, ','.join(options)]
     with tempfile.NamedTemporaryFile() as temporary_file:
         # Create a fake /etc/crypttab file with a valid entry.
         temporary_file.write((' '.join(fake_entry) + '\n').encode('ascii'))
         # Also add a corrupt entry to the file.
         temporary_file.write('oops!\n'.encode('ascii'))
         # Make sure the contents are on disk.
         temporary_file.flush()
         # Parse the file.
         entries = list(parse_crypttab(filename=temporary_file.name))
         # Check the results.
         assert len(entries) == 1
         assert entries[0].is_available is False
         assert entries[0].is_unlocked is False
         assert entries[0].key_file == key_file
         assert entries[0].options == options
         assert entries[0].source == source
         assert entries[0].source_device.startswith('/dev/disk/by-uuid/')
         assert entries[0].target == TEST_UNKNOWN_TARGET