def test_global_passphrase_catalog(*_): _dir = tempfile.mkdtemp() os.makedirs(os.path.join(_dir, 'cicd_site_repo'), exist_ok=True) PassphraseGenerator('cicd', _dir, 'test_author').generate() for passphrase in TEST_GLOBAL_PASSPHRASES_CATALOG['data']['passphrases']: passphrase_file_name = '{}.yaml'.format(passphrase['document_name']) passphrase_file_path = os.path.join(_dir, 'site', 'cicd', 'secrets', 'passphrases', passphrase_file_name) assert os.path.isfile(passphrase_file_path) with open(passphrase_file_path) as stream: doc = yaml.safe_load(stream) assert doc['schema'] == 'pegleg/PeglegManagedDocument/v1' assert doc['metadata']['storagePolicy'] == 'cleartext' assert 'encrypted' in doc['data'] assert doc['data']['encrypted']['by'] == 'test_author' assert 'generated' in doc['data'] assert doc['data']['generated']['by'] == 'test_author' assert 'managedDocument' in doc['data'] assert doc['data']['managedDocument']['metadata'][ 'storagePolicy'] == 'encrypted' decrypted_passphrase = encryption.decrypt( doc['data']['managedDocument']['data'], os.environ['PEGLEG_PASSPHRASE'].encode(), os.environ['PEGLEG_SALT'].encode()) if passphrase_file_name == "passphrase_from_global.yaml": assert len(decrypted_passphrase) == 24
def get_decrypted_secrets(self): """ Unwrap and decrypt all the pegleg managed documents in a secrets file, and return the result as a list of documents. The method is idempotent. If the method is called on not encrypted files, or documents inside the file, it will return the original unwrapped and unencrypted documents. """ doc_list = [] for doc in self.documents: # do not decrypt already decrypted data if doc.is_encrypted(): # Get appropriate encryption keys to use if doc.get_layer() == 'site': passphrase = config.get_passphrase() salt = config.get_salt() else: passphrase = config.get_global_passphrase() salt = config.get_global_salt() doc.set_secret( decrypt(doc.get_secret(), passphrase, salt).decode()) doc.set_decrypted() doc_list.append(doc.embedded_document) return doc_list
def test_generate_passphrases_exception(capture): unenc_data = uuid.uuid4().bytes passphrase1 = uuid.uuid4().bytes passphrase2 = uuid.uuid4().bytes salt1 = uuid.uuid4().bytes salt2 = uuid.uuid4().bytes # Generate random data and encrypt it enc_data = encryption.encrypt(unenc_data, passphrase1, salt1) # Decrypt using the wrong key to see to see the InvalidToken error with pytest.raises(fernet.InvalidToken): encryption.decrypt(enc_data, passphrase2, salt2) capture.check(('pegleg.engine.util.encryption', 'ERROR', ('Signature verification to decrypt secrets failed. ' 'Please check your provided passphrase and salt and ' 'try again.')))
def test_encrypt_and_decrypt(): data = test_utils.rand_name("this is an example of un-encrypted " "data.", "pegleg").encode() passphrase = test_utils.rand_name("passphrase1", "pegleg").encode() salt = test_utils.rand_name("salt1", "pegleg").encode() enc1 = crypt.encrypt(data, passphrase, salt) dec1 = crypt.decrypt(enc1, passphrase, salt) assert data == dec1 enc2 = crypt.encrypt(dec1, passphrase, salt) dec2 = crypt.decrypt(enc2, passphrase, salt) assert data == dec2 passphrase2 = test_utils.rand_name("passphrase2", "pegleg").encode() salt2 = test_utils.rand_name("salt2", "pegleg").encode() enc3 = crypt.encrypt(dec2, passphrase2, salt2) dec3 = crypt.decrypt(enc3, passphrase2, salt2) assert data == dec3 assert data != enc3
def test_uuid_passphrase_catalog(*_): _dir = tempfile.mkdtemp() os.makedirs(os.path.join(_dir, 'cicd_site_repo'), exist_ok=True) PassphraseGenerator('cicd', _dir, 'test_author').generate() for passphrase in TEST_TYPES_CATALOG['data']['passphrases']: passphrase_file_name = '{}.yaml'.format(passphrase['document_name']) passphrase_file_path = os.path.join(_dir, 'site', 'cicd', 'secrets', 'passphrases', passphrase_file_name) assert os.path.isfile(passphrase_file_path) with open(passphrase_file_path) as stream: doc = yaml.safe_load(stream) decrypted_passphrase = encryption.decrypt( doc['data']['managedDocument']['data'], os.environ['PEGLEG_PASSPHRASE'].encode(), os.environ['PEGLEG_SALT'].encode()) if passphrase_file_name == "uuid_passphrase_doc.yaml": assert uuid.UUID(decrypted_passphrase.decode()).version == 4
def test_generate_passphrases_with_overidden_passphrase_catalog(*_): _dir = tempfile.mkdtemp() os.makedirs(os.path.join(_dir, 'cicd_site_repo'), exist_ok=True) PassphraseGenerator('cicd', _dir, 'test_author', [TEST_OVERRIDE_PASSPHRASES_CATALOG]).generate() passphrase_dir = os.path.join(_dir, 'site', 'cicd', 'secrets', 'passphrases') assert 3 == len(os.listdir(passphrase_dir)) for passphrase in TEST_OVERRIDE_PASSPHRASES_CATALOG['data']['passphrases']: passphrase_file_name = '{}.yaml'.format(passphrase['document_name']) passphrase_file_path = os.path.join(_dir, 'site', 'cicd', 'secrets', 'passphrases', passphrase_file_name) assert os.path.isfile(passphrase_file_path) with open(passphrase_file_path) as stream: doc = yaml.safe_load(stream) assert doc['schema'] == 'pegleg/PeglegManagedDocument/v1' assert doc['metadata']['storagePolicy'] == 'cleartext' assert 'encrypted' in doc['data'] assert doc['data']['encrypted']['by'] == 'test_author' assert 'generated' in doc['data'] assert doc['data']['generated']['by'] == 'test_author' assert 'managedDocument' in doc['data'] assert doc['data']['managedDocument']['metadata'][ 'storagePolicy'] == 'encrypted' decrypted_passphrase = encryption.decrypt( doc['data']['managedDocument']['data'], os.environ['PEGLEG_PASSPHRASE'].encode(), os.environ['PEGLEG_SALT'].encode()) if passphrase_file_name == 'osh_placement_password.yaml': assert len(decrypted_passphrase) == 32 elif passphrase_file_name == 'osh_cinder_password.yaml': assert len(decrypted_passphrase) == 25 else: assert len(decrypted_passphrase) == 24
def test_profiles_catalog(*_): _dir = tempfile.mkdtemp() os.makedirs(os.path.join(_dir, 'cicd_site_repo'), exist_ok=True) PassphraseGenerator('cicd', _dir, 'test_author').generate() s_util = CryptoString() for passphrase in TEST_PROFILES_CATALOG['data']['passphrases']: passphrase_file_name = '{}.yaml'.format(passphrase['document_name']) passphrase_file_path = os.path.join(_dir, 'site', 'cicd', 'secrets', 'passphrases', passphrase_file_name) assert os.path.isfile(passphrase_file_path) with open(passphrase_file_path) as stream: doc = yaml.safe_load(stream) decrypted_passphrase = encryption.decrypt( doc['data']['managedDocument']['data'], os.environ['PEGLEG_PASSPHRASE'].encode(), os.environ['PEGLEG_SALT'].encode()).decode() assert len(decrypted_passphrase) == 24 if passphrase_file_name == "default_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is True assert s_util.has_upper(decrypted_passphrase) is True assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is True bad_symbols = any(char in '!"$%()*,./:;<>[]^_`{|}~\'' for char in decrypted_passphrase) assert not bad_symbols elif passphrase_file_name == "alphanumeric_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is True assert s_util.has_upper(decrypted_passphrase) is True assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is False elif passphrase_file_name == "alphanumeric_lower_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is True assert s_util.has_upper(decrypted_passphrase) is False assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is False elif passphrase_file_name == "alphanumeric_upper_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is False assert s_util.has_upper(decrypted_passphrase) is True assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is False elif passphrase_file_name == "all_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is True assert s_util.has_upper(decrypted_passphrase) is True assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is True elif passphrase_file_name == "hex_lower_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is True assert s_util.has_upper(decrypted_passphrase) is False assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is False bad_letters = any(char in 'ghijklmnopqrstuvwxyz' for char in decrypted_passphrase) assert not bad_letters elif passphrase_file_name == "hex_upper_passphrase.yaml": assert s_util.has_lower(decrypted_passphrase) is False assert s_util.has_upper(decrypted_passphrase) is True assert s_util.has_number(decrypted_passphrase) is True assert s_util.has_symbol(decrypted_passphrase) is False bad_letters = any(char in 'GHIJKLMNOPQRSTUVWXYZ' for char in decrypted_passphrase) assert not bad_letters
def get_global_creds(site_name): """Determine which credentials to use for global secrets. If a user desires to encrypt site secrets with one set of credentials but global secrets with a different set of credentials (in the case of multiple sites) Pegleg needs a way to handle a two-step encryption or decryption chain. This is accomplished by storing global credentials at the site level and encrypting them with site credentials. Pegleg will attempt to find both the global_salt and global_passphrase, decrypt them then use these credentials for any global encrypt/decrypt operations. If both global_salt and global_passphrase are found return both. If only one global credential is found, raise an error with the assumption the user wishes to use global credentials but does not have both. If neither are found, return the site credentials with the assumption the user wishes to encrypt the global documents with the site credentials. :param str site_name: The target site :return: Either the global, or site level - passphrase and salt """ config.set_passphrase() config.set_salt() global_passphrase = None global_salt = None docs = definition.documents_for_site(site_name) for doc in docs: if doc['schema'] == 'pegleg/PeglegManagedDocument/v1': try: name = doc['data']['managedDocument']['metadata']['name'] schema = doc['data']['managedDocument']['schema'] data = doc['data']['managedDocument']['data'] if schema == 'deckhand/Passphrase/v1': if name == 'global_passphrase': global_passphrase = encryption.decrypt( data, config.get_passphrase(), config.get_salt()) elif name == 'global_salt': global_salt = encryption.decrypt( data, config.get_passphrase(), config.get_salt()) except KeyError: continue else: try: name = doc['metadata']['name'] schema = doc['schema'] data = doc['data'] if name == 'global_passphrase': global_passphrase = data.encode() elif name == 'global_salt': global_salt = data.encode() except KeyError: continue # Break out of search if both passphrase and salt are found if global_passphrase and global_salt: return (global_passphrase, global_salt) # End of search, determine if we should use site keys or raise an error if global_passphrase or global_salt: raise exceptions.GlobalCredentialsNotFound() else: return (config.get_passphrase(), config.get_salt())