Exemple #1
0
    def test_rotation(self):
        # Initializing a key repository results in this many keys. We don't
        # support max_active_keys being set any lower.
        min_active_keys = 2

        # Simulate every rotation strategy up to "rotating once a week while
        # maintaining a year's worth of keys."
        for max_active_keys in range(min_active_keys, 52 + 1):
            self.config_fixture.config(group='fernet_receipts',
                                       max_active_keys=max_active_keys)

            # Ensure that resetting the key repository always results in 2
            # active keys.
            self.useFixture(
                ksfixtures.KeyRepository(self.config_fixture,
                                         'fernet_receipts',
                                         CONF.fernet_receipts.max_active_keys))

            # Validate the initial repository state.
            self.assertRepositoryState(expected_size=min_active_keys)

            # The repository should be initialized with a staged key (0) and a
            # primary key (1). The next key is just auto-incremented.
            exp_keys = [0, 1]
            next_key_number = exp_keys[-1] + 1  # keep track of next key
            self.assertEqual(exp_keys, self.keys)

            # Rotate the keys just enough times to fully populate the key
            # repository.
            key_utils = fernet_utils.FernetUtils(
                CONF.fernet_receipts.key_repository,
                CONF.fernet_receipts.max_active_keys, 'fernet_receipts')
            for rotation in range(max_active_keys - min_active_keys):
                key_utils.rotate_keys()
                self.assertRepositoryState(expected_size=rotation + 3)

                exp_keys.append(next_key_number)
                next_key_number += 1
                self.assertEqual(exp_keys, self.keys)

            # We should have a fully populated key repository now.
            self.assertEqual(max_active_keys, self.key_repository_size)

            # Rotate an additional number of times to ensure that we maintain
            # the desired number of active keys.
            key_utils = fernet_utils.FernetUtils(
                CONF.fernet_receipts.key_repository,
                CONF.fernet_receipts.max_active_keys, 'fernet_receipts')
            for rotation in range(10):
                key_utils.rotate_keys()
                self.assertRepositoryState(expected_size=max_active_keys)

                exp_keys.pop(1)
                exp_keys.append(next_key_number)
                next_key_number += 1
                self.assertEqual(exp_keys, self.keys)
    def test_rotation_disk_write_fail(self):
        # Init the key repository
        self.useFixture(
            ksfixtures.KeyRepository(
                self.config_fixture,
                'fernet_tokens',
                CONF.fernet_tokens.max_active_keys
            )
        )

        # Make sure that the init key repository contains 2 keys
        self.assertRepositoryState(expected_size=2)

        key_utils = fernet_utils.FernetUtils(
            CONF.fernet_tokens.key_repository,
            CONF.fernet_tokens.max_active_keys
        )

        # Simulate the disk full situation
        mock_open = mock.mock_open()
        file_handle = mock_open()
        file_handle.flush.side_effect = IOError('disk full')

        with mock.patch('keystone.common.fernet_utils.open', mock_open):
            self.assertRaises(IOError, key_utils.rotate_keys)

        # Assert that the key repository is unchanged
        self.assertEqual(self.key_repository_size, 2)
Exemple #3
0
    def key_repository_signature(self):
        """Create a "thumbprint" of the current key repository.

        Because key files are renamed, this produces a hash of the contents of
        the key files, ignoring their filenames.

        The resulting signature can be used, for example, to ensure that you
        have a unique set of keys after you perform a key rotation (taking a
        static set of keys, and simply shuffling them, would fail such a test).

        """
        # Load the keys into a list, keys is list of six.text_type.
        key_utils = fernet_utils.FernetUtils(
            CONF.fernet_tokens.key_repository,
            CONF.fernet_tokens.max_active_keys,
            'fernet_tokens'
        )
        keys = key_utils.load_keys()

        # Sort the list of keys by the keys themselves (they were previously
        # sorted by filename).
        keys.sort()

        # Create the thumbprint using all keys in the repository.
        signature = hashlib.sha1()
        for key in keys:
            # Need to convert key to six.binary_type for update.
            signature.update(key.encode('utf-8'))
        return signature.hexdigest()
Exemple #4
0
    def test_rotation_disk_write_fail(self):
        # Make sure that the init key repository contains 2 keys
        self.assertRepositoryState(expected_size=2)

        key_utils = fernet_utils.FernetUtils(
            CONF.fernet_tokens.key_repository,
            CONF.fernet_tokens.max_active_keys,
            'fernet_tokens'
        )

        # Simulate the disk full situation
        mock_open = mock.mock_open()
        file_handle = mock_open()
        file_handle.flush.side_effect = IOError('disk full')

        with mock.patch('keystone.common.fernet_utils.open', mock_open):
            self.assertRaises(IOError, key_utils.rotate_keys)

        # Assert that the key repository is unchanged
        self.assertEqual(self.key_repository_size, 2)

        with mock.patch('keystone.common.fernet_utils.open', mock_open):
            self.assertRaises(IOError, key_utils.rotate_keys)

        # Assert that the key repository is still unchanged, even after
        # repeated rotation attempts.
        self.assertEqual(self.key_repository_size, 2)

        # Rotate the keys normally, without any mocking, to show that the
        # system can recover.
        key_utils.rotate_keys()

        # Assert that the key repository is now expanded.
        self.assertEqual(self.key_repository_size, 3)
Exemple #5
0
    def main(cls):
        futils = fernet_utils.FernetUtils(CONF.fernet_tokens.key_repository,
                                          CONF.fernet_tokens.max_active_keys,
                                          'fernet_tokens')

        keystone_user_id, keystone_group_id = cls.get_user_group()
        if futils.validate_key_repository(requires_write=True):
            futils.rotate_keys(keystone_user_id, keystone_group_id)
Exemple #6
0
 def main(cls):
     # Check to make sure we have a repository that works...
     futils = fernet_utils.FernetUtils(CONF.credential.key_repository,
                                       credential_fernet.MAX_ACTIVE_KEYS,
                                       'credential')
     futils.validate_key_repository(requires_write=True)
     klass = cls()
     klass.migrate_credentials()
Exemple #7
0
def get_multi_fernet_keys():
    key_utils = fernet_utils.FernetUtils(CONF.credential.key_repository,
                                         MAX_ACTIVE_KEYS)
    keys = key_utils.load_keys()
    fernet_keys = [fernet.Fernet(key) for key in keys]
    crypto = fernet.MultiFernet(fernet_keys)

    return crypto, keys
Exemple #8
0
    def setUp(self):
        super(KeyRepository, self).setUp()
        directory = self.useFixture(fixtures.TempDir()).path
        self.config_fixture.config(group=self.key_group,
                                   key_repository=directory)

        fernet_utils = utils.FernetUtils(directory, self.max_active_keys)
        fernet_utils.create_key_directory()
        fernet_utils.initialize_key_repository()
Exemple #9
0
    def main(cls):
        futils = fernet_utils.FernetUtils(CONF.credential.key_repository,
                                          credential_fernet.MAX_ACTIVE_KEYS,
                                          'credential')

        keystone_user_id, keystone_group_id = cls.get_user_group()
        futils.create_key_directory(keystone_user_id, keystone_group_id)
        if futils.validate_key_repository(requires_write=True):
            futils.initialize_key_repository(keystone_user_id,
                                             keystone_group_id)
Exemple #10
0
    def main(cls):
        futils = fernet_utils.FernetUtils(CONF.credential.key_repository,
                                          credential_fernet.MAX_ACTIVE_KEYS,
                                          'credential')

        keystone_user_id, keystone_group_id = cls.get_user_group()
        if futils.validate_key_repository(requires_write=True):
            klass = cls()
            klass.validate_primary_key()
            futils.rotate_keys(keystone_user_id, keystone_group_id)
Exemple #11
0
 def test_non_numeric_files(self):
     evil_file = os.path.join(CONF.fernet_receipts.key_repository, '~1')
     with open(evil_file, 'w'):
         pass
     key_utils = fernet_utils.FernetUtils(
         CONF.fernet_receipts.key_repository,
         CONF.fernet_receipts.max_active_keys, 'fernet_receipts')
     keys = key_utils.load_keys()
     self.assertEqual(2, len(keys))
     self.assertValidFernetKeys(keys)
Exemple #12
0
 def test_empty_files(self):
     empty_file = os.path.join(CONF.fernet_tokens.key_repository, '2')
     with open(empty_file, 'w'):
         pass
     key_utils = fernet_utils.FernetUtils(
         CONF.fernet_tokens.key_repository,
         CONF.fernet_tokens.max_active_keys, 'fernet_tokens')
     keys = key_utils.load_keys()
     self.assertEqual(2, len(keys))
     self.assertValidFernetKeys(keys)
Exemple #13
0
def symptom_usability_of_credential_fernet_key_repository():
    """Credential key repository is not setup correctly.

    The credential Fernet key repository is expected to be readable by the user
    running keystone, but not world-readable, because it contains
    security sensitive secrets.
    """
    fernet_utils = utils.FernetUtils(CONF.credential.key_repository,
                                     credential_fernet.MAX_ACTIVE_KEYS)
    return ('fernet' in CONF.credential.provider
            and not fernet_utils.validate_key_repository())
Exemple #14
0
    def main(cls):
        futils = fernet_utils.FernetUtils(
            # TODO(gagehugo) Change this to CONF.token
            CONF.fernet_tokens.key_repository,
            CONF.fernet_tokens.max_active_keys,
            'fernet_tokens')

        keystone_user_id, keystone_group_id = cls.get_user_group()
        futils.create_key_directory(keystone_user_id, keystone_group_id)
        if futils.validate_key_repository(requires_write=True):
            futils.initialize_key_repository(keystone_user_id,
                                             keystone_group_id)
Exemple #15
0
def symptom_keys_in_credential_fernet_key_repository():
    """Credential key repository is empty.

    After configuring keystone to use the Fernet credential provider, you
    should use `keystone-manage credential_setup` to initially populate your
    key repository with keys, and periodically rotate your keys with
    `keystone-manage credential_rotate`.
    """
    fernet_utils = utils.FernetUtils(CONF.credential.key_repository,
                                     credential_fernet.MAX_ACTIVE_KEYS)
    return ('fernet' in CONF.credential.provider
            and not fernet_utils.load_keys())
 def test_non_numeric_files(self):
     self.useFixture(
         ksfixtures.KeyRepository(self.config_fixture, 'fernet_tokens',
                                  CONF.fernet_tokens.max_active_keys))
     evil_file = os.path.join(CONF.fernet_tokens.key_repository, '~1')
     with open(evil_file, 'w'):
         pass
     key_utils = fernet_utils.FernetUtils(
         CONF.fernet_tokens.key_repository,
         CONF.fernet_tokens.max_active_keys, 'fernet_tokens')
     keys = key_utils.load_keys()
     self.assertEqual(2, len(keys))
     self.assertValidFernetKey(keys)
Exemple #17
0
def symptom_usability_of_Fernet_key_repository():
    """Fernet key repository is not setup correctly.

    The Fernet key repository is expected to be readable by the user running
    keystone, but not world-readable, because it contains security-sensitive
    secrets.
    """
    fernet_utils = utils.FernetUtils(
        CONF.fernet_tokens.key_repository,
        CONF.fernet_tokens.max_active_keys
    )
    return (
        'fernet' in CONF.token.provider
        and not fernet_utils.validate_key_repository())
Exemple #18
0
def symptom_keys_in_Fernet_key_repository():
    """Fernet key repository is empty.

    After configuring keystone to use the Fernet token provider, you should use
    `keystone-manage fernet_setup` to initially populate your key repository
    with keys, and periodically rotate your keys with `keystone-manage
    fernet_rotate`.
    """
    fernet_utils = utils.FernetUtils(
        CONF.fernet_tokens.key_repository,
        CONF.fernet_tokens.max_active_keys
    )
    return (
        'fernet' in CONF.token.provider
        and not fernet_utils.load_keys())
Exemple #19
0
 def test_non_numeric_files(self):
     evil_file = os.path.join(CONF.fernet_tokens.key_repository, '99.bak')
     with open(evil_file, 'w'):
         pass
     key_utils = fernet_utils.FernetUtils(
         CONF.fernet_tokens.key_repository,
         CONF.fernet_tokens.max_active_keys, 'fernet_tokens')
     key_utils.rotate_keys()
     self.assertTrue(os.path.isfile(evil_file))
     keys = 0
     for x in os.listdir(CONF.fernet_tokens.key_repository):
         if x == '99.bak':
             continue
         keys += 1
     self.assertEqual(3, keys)
Exemple #20
0
 def test_rotation_empty_file(self):
     active_keys = 2
     self.assertRepositoryState(expected_size=active_keys)
     empty_file = os.path.join(CONF.fernet_tokens.key_repository, '2')
     with open(empty_file, 'w'):
         pass
     key_utils = fernet_utils.FernetUtils(
         CONF.fernet_tokens.key_repository,
         CONF.fernet_tokens.max_active_keys, 'fernet_tokens')
     # Rotate the keys to overwrite the empty file
     key_utils.rotate_keys()
     self.assertTrue(os.path.isfile(empty_file))
     keys = key_utils.load_keys()
     self.assertEqual(3, len(keys))
     self.assertTrue(os.path.getsize(empty_file) > 0)
Exemple #21
0
 def test_debug_message_not_logged_when_loading_fernet_credential_key(self):
     self.useFixture(
         ksfixtures.KeyRepository(self.config_fixture, 'credential',
                                  CONF.fernet_tokens.max_active_keys))
     logging_fixture = self.useFixture(fixtures.FakeLogger(level=log.DEBUG))
     fernet_utilities = fernet_utils.FernetUtils(
         CONF.credential.key_repository, credential_fernet.MAX_ACTIVE_KEYS)
     fernet_utilities.load_keys()
     debug_message = (
         'Loaded 2 Fernet keys from %(dir)s, but `[fernet_tokens] '
         'max_active_keys = %(max)d`; perhaps there have not been enough '
         'key rotations to reach `max_active_keys` yet?') % {
             'dir': CONF.credential.key_repository,
             'max': credential_fernet.MAX_ACTIVE_KEYS
         }
     self.assertNotIn(debug_message, logging_fixture.output)
Exemple #22
0
 def test_debug_message_logged_when_loading_fernet_token_keys(self):
     self.useFixture(
         ksfixtures.KeyRepository(self.config_fixture, 'fernet_tokens',
                                  CONF.fernet_tokens.max_active_keys))
     logging_fixture = self.useFixture(fixtures.FakeLogger(level=log.DEBUG))
     fernet_utilities = fernet_utils.FernetUtils(
         CONF.fernet_tokens.key_repository,
         CONF.fernet_tokens.max_active_keys, 'fernet_tokens')
     fernet_utilities.load_keys()
     expected_debug_message = (
         'Loaded 2 Fernet keys from %(dir)s, but `[fernet_tokens] '
         'max_active_keys = %(max)d`; perhaps there have not been enough '
         'key rotations to reach `max_active_keys` yet?') % {
             'dir': CONF.fernet_tokens.key_repository,
             'max': CONF.fernet_tokens.max_active_keys
         }
     self.assertIn(expected_debug_message, logging_fixture.output)
Exemple #23
0
    def decrypt(self, credential):
        """Attempt to decrypt a credential.

        :param credential: an encrypted credential string
        :returns: a decrypted credential
        """
        key_utils = fernet_utils.FernetUtils(CONF.credential.key_repository,
                                             MAX_ACTIVE_KEYS)
        keys = key_utils.load_keys(use_null_key=True)
        fernet_keys = [fernet.Fernet(key) for key in keys]
        crypto = fernet.MultiFernet(fernet_keys)

        try:
            if isinstance(credential, six.text_type):
                credential = credential.encode('utf-8')
            return crypto.decrypt(credential).decode('utf-8')
        except (fernet.InvalidToken, TypeError, ValueError):
            msg = _('Credential could not be decrypted. Please contact the'
                    ' administrator')
            LOG.error(msg)
            raise exception.CredentialEncryptionError(msg)
    def crypto(self):
        """Return a cryptography instance.

        You can extend this class with a custom crypto @property to provide
        your own token encoding / decoding. For example, using a different
        cryptography library (e.g. ``python-keyczar``) or to meet arbitrary
        security requirements.

        This @property just needs to return an object that implements
        ``encrypt(plaintext)`` and ``decrypt(ciphertext)``.

        """
        fernet_utils = utils.FernetUtils(CONF.fernet_tokens.key_repository,
                                         CONF.fernet_tokens.max_active_keys)
        keys = fernet_utils.load_keys()

        if not keys:
            raise exception.KeysNotFound()

        fernet_instances = [fernet.Fernet(key) for key in keys]
        return fernet.MultiFernet(fernet_instances)
Exemple #25
0
 def _get_encryption_keys(self):
     self.key_utils = fernet_utils.FernetUtils(
         CONF.credential.key_repository, MAX_ACTIVE_KEYS)
     return self.key_utils.load_keys()