def run_decrypt(overwrite, path, save_location, site_name): """Unwraps and decrypts secret documents for a site :param overwrite: if True, overwrites original files with decrypted :param path: file(s) or directory(ies) to decrypt :param save_location: if specified saves to the given path, otherwise returns list of decrypted information :param site_name: site name to process :return: decrypted data list if save_location is None :rtype: list """ decrypted_data = [] config.set_global_enc_keys(site_name) if type(path) is not list and type(path) is not tuple: path = [path] for p in path: decrypted = engine.secrets.decrypt(p, site_name=site_name) if overwrite: for file_path, data in decrypted.items(): files.write(data, file_path) elif save_location is None: for data in decrypted.values(): decrypted_data.append(data) else: for file_path, data in decrypted.items(): file_name = os.path.split(file_path)[1] file_save_location = os.path.join(save_location, file_name) files.write(data, file_save_location) return decrypted_data
def list_types(output_stream): """List type names for a given repository.""" # Create a table to output site types for a given repo type_table = PrettyTable() type_table.field_names = ['type_name'] for type_name in util.files.list_types(): type_table.add_row([type_name]) # Write table to specified output_stream msg = type_table.get_string() if output_stream: files.write(msg + "\n", output_stream) else: click.echo(msg)
def list_(output_stream): """List site names for a given repository.""" # Create a table to output site information for all sites for a given repo site_table = PrettyTable() field_names = ['site_name', 'site_type'] site_table.field_names = field_names for site_name in util.files.list_sites(): params = util.definition.load_as_params(site_name, *field_names) site_table.add_row(list(map(lambda k: params[k], field_names))) # Write table to specified output_stream msg = site_table.get_string() if output_stream: files.write(msg + "\n", output_stream) else: click.echo(msg)
def show(site_name, output_stream): data = util.definition.load_as_params(site_name) data['files'] = list(util.definition.site_files(site_name)) # Create a table to output site information for specific site site_table = PrettyTable() site_table.field_names = ['revision', 'site_name', 'site_type', 'files'] # TODO(felipemonteiro): Drop support for 'revision' once manifest # repositories have removed it altogether. if 'revision' in data.keys(): for file in data['files']: site_table.add_row( [data['revision'], data['site_name'], data['site_type'], file]) else: for file in data['files']: site_table.add_row( ["", data['site_name'], data['site_type'], file]) # Write tables to specified output_stream msg = site_table.get_string() if output_stream: files.write(msg + "\n", output_stream) else: click.echo(msg)
def test_encrypt_decrypt_using_file_path(tmpdir): # write the test data to temp file test_data = list(yaml.safe_load_all(TEST_DATA)) file_path = os.path.join(tmpdir, 'secrets_file.yaml') files.write(test_data, file_path) save_path = os.path.join(tmpdir, 'encrypted_secrets_file.yaml') # encrypt documents and validate that they were encrypted doc_mgr = PeglegSecretManagement(file_path=file_path, author='test_author') doc_mgr.encrypt_secrets(save_path) doc = doc_mgr.documents[0] assert doc.is_encrypted() assert doc.data['encrypted']['by'] == 'test_author' # decrypt documents and validate that they were decrypted doc_mgr = PeglegSecretManagement(file_path=file_path, author='test_author') doc_mgr.encrypt_secrets(save_path) # read back the encrypted file doc_mgr = PeglegSecretManagement(file_path=save_path, author='test_author') decrypted_data = doc_mgr.get_decrypted_secrets() assert test_data[0]['data'] == decrypted_data[0]['data'] assert test_data[0]['schema'] == decrypted_data[0]['schema']
def test_failed_deckhand_validation(tmpdir): # Write the test data to temp file config_data = list(yaml.safe_load_all(SITE_CONFIG_DATA)) base_config_dir = os.path.join(tmpdir, 'config_dir') config.set_site_repo(base_config_dir) config_dir = os.path.join(base_config_dir, 'site', 'test_site') config_path = os.path.join(config_dir, 'config_file.yaml') build_dir = os.path.join(tmpdir, 'build_dir') os.makedirs(config_dir) files.write(config_data, config_path) files.write( yaml.safe_load_all(SITE_DEFINITION), os.path.join(config_dir, "site-definition.yaml")) key = 'MyverYSecretEncryptionKey382803' with pytest.raises(GenesisBundleGenerateException, match=r'.*failed on deckhand validation.*'): bundle.build_genesis( build_path=build_dir, encryption_key=key, validators=False, debug=logging.ERROR, site_name="test_site")
def encrypt_secrets(self, save_path): """ Wrap and encrypt the secrets documents included in the input file, into pegleg manage secrets documents, and write the result in save_path. if save_path is the same as the source file_path the encrypted file will overwrite the source file. :param save_path: Destination path of the encrypted file :type save_path: string :param author: Identifier for the program or person who is encrypting the secrets documents :type author: string """ doc_list, encrypted_docs = self.get_encrypted_secrets() if encrypted_docs: files.write(doc_list, save_path) click.echo('Wrote encrypted data to: {}'.format(save_path)) else: LOG.debug('All documents in file: {} are either already encrypted ' 'or have cleartext storage policy. ' 'Skipping.'.format(self.file_path))
def test_write(self, temp_deployment_files): path = os.path.join(config.get_site_repo(), 'site', 'cicd', 'test_out.yaml') files.write("test text", path) with open(path, "r") as out_fi: assert out_fi.read() == "test text" files.write({"a": 1}, path) with open(path, "r") as out_fi: assert yaml.safe_load(out_fi) == {"a": 1} files.write([{"a": 1}], path) with open(path, "r") as out_fi: assert list(yaml.safe_load_all(out_fi)) == [{"a": 1}] with pytest.raises(ValueError): files.write(object(), path)
def test_no_encryption_key(tmpdir): # Write the test data to temp file config_data = list(yaml.safe_load_all(SITE_CONFIG_DATA)) base_config_dir = os.path.join(tmpdir, 'config_dir') config.set_site_repo(base_config_dir) config_dir = os.path.join(base_config_dir, 'site', 'test_site') config_path = os.path.join(config_dir, 'config_file.yaml') build_dir = os.path.join(tmpdir, 'build_dir') os.makedirs(config_dir) files.write(config_data, config_path) files.write( yaml.safe_load_all(SITE_DEFINITION), os.path.join(config_dir, "site-definition.yaml")) with pytest.raises(GenesisBundleEncryptionException, match=r'.*no encryption policy or key is specified.*'): bundle.build_genesis( build_path=build_dir, encryption_key=None, validators=False, debug=logging.ERROR, site_name="test_site")
def test_file_permissions(self, temp_deployment_files): path = os.path.join(config.get_site_repo(), 'site', 'cicd', 'test_out.yaml') files.write("test text", path) assert oct(os.stat(path).st_mode & 0o777) == EXPECTED_FILE_PERM
def generate(self, interactive=False, force_cleartext=False): """ For each passphrase entry in the passphrase catalog, generate a random passphrase string, based on a passphrase specification in the catalog. Create a pegleg managed document, wrap the generated passphrase document in the pegleg managed document, and encrypt the passphrase. Write the wrapped and encrypted document in a file at <repo_name>/site/<site_name>/secrets/passphrases/passphrase_name.yaml. :param bool interactive: If true, allow input :param bool force_cleartext: If true, don't encrypt """ for p_name in self._catalog.get_passphrase_names: # Check if this secret is present and should not be regenerated save_path = self.get_save_path(p_name) regenerable = self._catalog.is_passphrase_regenerable(p_name) if os.path.exists(save_path) and not regenerable: continue # Generate secret as it either does not exist yet or is a # regenerable secret and does exist but should be rotated. passphrase = None passphrase_type = self._catalog.get_passphrase_type(p_name) prompt = self._catalog.is_passphrase_prompt(p_name) profile = self._catalog.get_passphrase_profile(p_name) if interactive and prompt: auto_allowed = regenerable if passphrase_type == 'uuid': # nosec passphrase = self._prompt_user_passphrase_and_validate( p_name, 'UUID', self.validate_uuid, auto_allowed=auto_allowed) elif passphrase_type == 'base64': # nosec passphrase = self._prompt_user_passphrase_and_validate( p_name, 'passphrase (b64)', self.validate_base64, auto_allowed=auto_allowed) elif passphrase_type == 'passphrase': passphrase = self._prompt_user_passphrase_and_validate( p_name, 'passphrase', self.validate_passphrase, auto_allowed=auto_allowed) elif not interactive and prompt: LOG.debug('Skipping interactive input for %s', p_name) continue if not passphrase: if passphrase_type == 'uuid': # nosec passphrase = uuidutils.generate_uuid() else: passphrase = CryptoString(profile).get_crypto_string( self._catalog.get_length(p_name)) if passphrase_type == 'base64': # nosec # Take the randomly generated string and convert to a # random base64 string passphrase = passphrase.encode() passphrase = base64.b64encode(passphrase).decode() docs = list() if force_cleartext: storage_policy = passphrase_catalog.P_CLEARTEXT LOG.warning("Passphrases for {} will be " "generated in clear text.".format(p_name)) else: storage_policy = self._catalog.get_storage_policy(p_name) docs.append( self.generate_doc(KIND, p_name, storage_policy, passphrase)) if storage_policy == passphrase_catalog.P_ENCRYPTED: PeglegSecretManagement( docs=docs, generated=True, author=self._author, catalog=self._catalog).encrypt_secrets(save_path) else: files.write(docs, save_path)